<commit_before>// Mozilla Firefox User Preferences

// -----------------------------------------------------------------------------
// General
// -----------------------------------------------------------------------------

// Show a blank page when Firefox starts
user_pref("browser.startup.homepage", "about:blank");
user_pref("browser.startup.page", 0);
user_pref("browser.rights.3.shown", true);
user_pref("browser.startup.homepage_override.mstone", "ignore");
user_pref("startup.homepage_welcome_url", "");
user_pref("startup.homepage_welcome_url.additional", "");
user_pref("startup.homepage_override_url", "");
user_pref("browser.laterrun.enabled", false);

// Show a blank page when opening new tab
user_pref("browser.newtabpage.enabled", false);
user_pref("browser.newtabpage.enhanced", false);
user_pref("browser.newtab.preload", false);
user_pref("browser.newtabpage.introShown", true);
user_pref("browser.newtabpage.directory.ping", "data:text/plain,");
user_pref("browser.newtabpage.directory.source", "data:text/plain,");
user_pref("browser.newtabpage.activity-stream.enabled", false); // Firefox 54+

// Always ask me where to save download files
user_pref("browser.download.useDownloadDir", false);

// Disable downloading on desktop
user_pref("browser.download.folderList", 2);

// -----------------------------------------------------------------------------
// Search
// -----------------------------------------------------------------------------

// Set default region for search engines
user_pref("browser.search.region", "JP");
user_pref("browser.search.countryCode", "JP");
user_pref("browser.search.isUS", false);

// Disable geographically specific results/search engines
user_pref("browser.search.geoSpecificDefaults", false);
user_pref("browser.search.geoSpecificDefaults.url", "");

// -----------------------------------------------------------------------------
// Content
// -----------------------------------------------------------------------------

// Set minimum font size
user_pref("font.minimum-size.ja", 10);
user_pref("font.minimum-size.ko", 10);
user_pref("font.minimum-size.zh-CN", 10);
user_pref("font.minimum-size.zh-HK", 10);
user_pref("font.minimum-size.zh-TW", 10);

// Choose preferred language for displaying pages
user_pref("intl.accept_languages", "ja, zh-tw, zh, en-us, en");

// Disable Geolocation
user_pref("geo.enabled", false);
user_pref("geo.wifi.uri", "https://127.0.0.1");
user_pref("geo.wifi.logging.enabled", false);
user_pref("geo.wifi.xhr.timeout", 1);
user_pref("geo.security.allowinsecure", false);
user_pref("browser.search.geoip.url", "");
user_pref("browser.search.geoip.timeout", 1);

// Disable WebRTC
user_pref("media.peerconnection.enabled", false);
user_pref("media.peerconnection.use_document_iceservers", false);
user_pref("media.peerconnection.video.enabled", false);
user_pref("media.peerconnection.identity.enabled", false);
user_pref("media.peerconnection.identity.timeout", 1);
user_pref("media.peerconnection.turn.disable", true);
user_pref("media.peerconnection.ice.tcp", false);
user_pref("media.navigator.video.enabled", false);

// Improve WebRTC IP leak issue
user_pref("media.peerconnection.ice.default_address_only", true);
user_pref("media.peerconnection.ice.no_host", true);

// Disable audio auto-play in non-active tabs
user_pref("media.block-autoplay-until-in-foreground", true);

// Disable link prefetching
user_pref("network.prefetch-next", false);

// Disable DNS prefetching
user_pref("network.dns.disablePrefetch", true);
user_pref("network.dns.disablePrefetchFromHTTPS", true);

// Disable opening connection to links when mouseover
user_pref("network.http.speculative-parallel-limit", 0);

// Disable Beacon
user_pref("beacon.enabled", false);

// Ignore the `ping` attribute for `<a>` elements
user_pref("browser.send_pings", false);
user_pref("browser.send_pings.require_same_host", true);

// Disable camera controlling
user_pref("camera.control.face_detection.enabled", false);

// Decrease the maximum number of popups from a single non-click event (Default is 20)
user_pref("dom.popup_maximum", 2);

// Disallow JavaScript to raise or lower windows
user_pref("dom.disable_window_flip", true);

// Disallow JavaScript to move or resize existing windows
user_pref("dom.disable_window_move_resize", true);

// Disable web notifications
user_pref("dom.webnotifications.enabled", false);
user_pref("dom.webnotifications.serviceworker.enabled", false);

// Disable SPDY (SPDY would be deprecated in 2016)
user_pref("network.http.spdy.enabled", false);
user_pref("network.http.spdy.enabled.deps", false);

// -----------------------------------------------------------------------------
// Privacy
// -----------------------------------------------------------------------------

// Request that sites not track me
user_pref("privacy.donottrackheader.enabled", true);

// Use Tracking Protection in Private Windows
user_pref("privacy.trackingprotection.enabled", true);
user_pref("privacy.trackingprotection.pbmode.enabled", true);
user_pref("privacy.trackingprotection.introCount", 20);
user_pref("privacy.trackingprotection.ui.enabled", true);

// Never accept third-party cookies
user_pref("network.cookie.cookieBehavior", 1);

// Set the maximum number of pages to be retained in the history database
user_pref("places.history.expiration.max_pages", 10000);
user_pref("places.history.expiration.transient_current_max_pages", 10000);

// -----------------------------------------------------------------------------
// Security
// -----------------------------------------------------------------------------

// Block reported attack sites
user_pref("browser.safebrowsing.malware.enabled", true);

// Block reported web forgeries
user_pref("browser.safebrowsing.enabled", true);

// Do not remember passwords for sites
user_pref("signon.rememberSignons", false);

// -----------------------------------------------------------------------------
// Advanced
// -----------------------------------------------------------------------------

// Disable Firefox Health Report
user_pref("datareporting.healthreport.uploadEnabled", false);
user_pref("datareporting.healthreport.service.enabled", false);
user_pref("datareporting.healthreport.documentServerURI", "");
user_pref("datareporting.healthreport.about.reportUrl", "data:text/plain,");
user_pref("datareporting.healthreport.about.reportUrlInified", "data:text/plain,");
user_pref("datareporting.policy.dataSubmissionEnabled", false);
user_pref("datareporting.policy.dataSubmissionEnabled.v2", false);

// Disable Crash Reporter
user_pref("breakpad.reportURL", "");
user_pref("browser.tabs.crashReporting.sendReport", false); // Added in Firefox 44
user_pref("browser.crashReports.unsubmittedCheck.enabled", false);
user_pref("browser.crashReports.unsubmittedCheck.autoSubmit", false);

// Disable Telemetry
user_pref("toolkit.telemetry.enabled", false);
user_pref("toolkit.telemetry.unified", false);
user_pref("toolkit.telemetry.unifiedIsOptIn", true);
user_pref("toolkit.telemetry.archive.enabled", false);
user_pref("toolkit.telemetry.cachedClientID", "");
user_pref("toolkit.telemetry.server", "");

// Disable Telemetry Experiments
user_pref("experiments.enabled", false);
user_pref("experiments.supported", false);
user_pref("experiments.activeExperiment", false);
user_pref("experiments.manifest.uri", "");
user_pref("network.allow-experiments", false);

// Enable OCSP stapling
user_pref("security.ssl.enable_ocsp_stapling", true);

// Query OCSP responder servers to confirm current validity of certificates
user_pref("security.OCSP.enabled", 1);

// -----------------------------------------------------------------------------
// Add-ons and Plugins
// -----------------------------------------------------------------------------

// Disable enforced add-on signing since Firefox 43
user_pref("xpinstall.signatures.required", false);

// Hide the "Get Add-ons" pane in the Add-ons page
user_pref("extensions.getAddons.showPane", false);

// Disable extension discovery
user_pref("extensions.webservice.discoverURL", "http://127.0.0.1");

// Lock down allowed extension directories
user_pref("extensions.enabledScopes", 1);

// Clear localStorage and UUID when a WebExtension is uninstalled
user_pref("extensions.webextensions.keepStorageOnUninstall", false);
user_pref("extensions.webextensions.keepUuidOnUninstall", false);

// Ask me to activate plugins by default
user_pref("plugin.default.state", 1);
user_pref("plugin.defaultXpi.state", 1);

// Enable click-to-play and set the session permission
user_pref("plugins.click_to_play", true);
user_pref("plugin.sessionPermissionNow.intervalInMinutes", 0);

// Click to play Adobe Flash
user_pref("plugin.state.flash", 1);

// Disable all GMP (Gecko Media Plugins)
user_pref("media.gmp-provider.enabled", false);
user_pref("media.gmp.trial-create.enabled", false);

// Disable widevine CDM
user_pref("media.gmp-widevinecdm.visible", false);
user_pref("media.gmp-widevinecdm.enabled", false);
user_pref("media.gmp-widevinecdm.autoupdate", false);

// Disable all DRM content
user_pref("media.eme.enabled", false);
user_pref("browser.eme.ui.enabled", false);

// Disable OpenH264 Video Codec by Cisco
user_pref("media.gmp-gmpopenh264.enabled", false);
user_pref("media.gmp-gmpopenh264.autoupdate", false);
user_pref("media.gmp-manager.url", "data:text/plain,");

// -----------------------------------------------------------------------------
// Others
// -----------------------------------------------------------------------------

// Disable Heartbeat
user_pref("browser.selfsupport.enabled", false);
user_pref("browser.selfsupport.url", "");

// Disable Pocket
user_pref("extensions.pocket.enabled", false);
user_pref("extensions.pocket.api", "");
user_pref("extensions.pocket.site", "");
user_pref("extensions.pocket.oAuthConsumerKey", "");

// Disable Reader View
user_pref("reader.parse-on-load.enabled", false);

// Disable capturing page thumbnails
user_pref("browser.pagethumbnails.capturing_disabled", true);

// Disable Taskbar Jump List in Windows 7 and above
user_pref("browser.taskbar.lists.frequent.enabled", false);
user_pref("browser.taskbar.lists.recent.enabled", false);

// Disable "Slow startup" warnings
user_pref("browser.slowStartup.notificationDisabled", true);
user_pref("browser.slowStartup.maxSamples", 0);
user_pref("browser.slowStartup.samples", 0);

// Disable `about:config` warnings
user_pref("general.warnOnAboutConfig", false);

// Skip the installation delay
user_pref("security.dialog_enable_delay", 0);

// Disable closing browser with last tab
user_pref("browser.tabs.closeWindowWithLastTab", false);

// Disable UI animation
user_pref("browser.fullscreen.animate", false);
user_pref("browser.tabs.animate", false);

// Disable site specific zoom
user_pref("browser.zoom.siteSpecific", false);

// Disable search suggestions in URL bar
user_pref("browser.urlbar.suggest.searches", false);
user_pref("browser.urlbar.userMadeSearchSuggestionsChoice", true);

// Disable preloading top websites in URL bar (Firefox 54+)
user_pref("browser.urlbar.usepreloadedtopurls.enabled", false);

// Display the raw Punycode in URL bar.
user_pref("network.IDN_show_punycode", true);

// Disable WebIDE to prevent remote debugging and addon downloads
user_pref("devtools.webide.enabled", false);
user_pref("devtools.webide.autoinstallADBHelper", false);
user_pref("devtools.webide.autoinstallFxdtAdapters", false);
user_pref("devtools.debugger.remote-enabled", false);

// Disable UI tour
user_pref("browser.uitour.enabled", false);
user_pref("browser.uitour.url", "");

// Prevent links from launching Windows Store (Windows 8+)
user_pref("network.protocol-handler.external.ms-windows-store", false);

// Disable Microsoft Family Safety cert (Winodws 8+)
user_pref("security.family_safety.mode", 0);

// Set the cache directory location
// user_pref("browser.cache.disk.parent_directory", "");
<commit_msg>Disable Firefox system add-ons<commit_after>// Mozilla Firefox User Preferences

// -----------------------------------------------------------------------------
// General
// -----------------------------------------------------------------------------

// Show a blank page when Firefox starts
user_pref("browser.startup.homepage", "about:blank");
user_pref("browser.startup.page", 0);
user_pref("browser.rights.3.shown", true);
user_pref("browser.startup.homepage_override.mstone", "ignore");
user_pref("startup.homepage_welcome_url", "");
user_pref("startup.homepage_welcome_url.additional", "");
user_pref("startup.homepage_override_url", "");
user_pref("browser.laterrun.enabled", false);

// Show a blank page when opening new tab
user_pref("browser.newtabpage.enabled", false);
user_pref("browser.newtabpage.enhanced", false);
user_pref("browser.newtab.preload", false);
user_pref("browser.newtabpage.introShown", true);
user_pref("browser.newtabpage.directory.ping", "data:text/plain,");
user_pref("browser.newtabpage.directory.source", "data:text/plain,");
user_pref("browser.newtabpage.activity-stream.enabled", false); // Firefox 54+

// Always ask me where to save download files
user_pref("browser.download.useDownloadDir", false);

// Disable downloading on desktop
user_pref("browser.download.folderList", 2);

// -----------------------------------------------------------------------------
// Search
// -----------------------------------------------------------------------------

// Set default region for search engines
user_pref("browser.search.region", "JP");
user_pref("browser.search.countryCode", "JP");
user_pref("browser.search.isUS", false);

// Disable geographically specific results/search engines
user_pref("browser.search.geoSpecificDefaults", false);
user_pref("browser.search.geoSpecificDefaults.url", "");

// -----------------------------------------------------------------------------
// Content
// -----------------------------------------------------------------------------

// Set minimum font size
user_pref("font.minimum-size.ja", 10);
user_pref("font.minimum-size.ko", 10);
user_pref("font.minimum-size.zh-CN", 10);
user_pref("font.minimum-size.zh-HK", 10);
user_pref("font.minimum-size.zh-TW", 10);

// Choose preferred language for displaying pages
user_pref("intl.accept_languages", "ja, zh-tw, zh, en-us, en");

// Disable Geolocation
user_pref("geo.enabled", false);
user_pref("geo.wifi.uri", "https://127.0.0.1");
user_pref("geo.wifi.logging.enabled", false);
user_pref("geo.wifi.xhr.timeout", 1);
user_pref("geo.security.allowinsecure", false);
user_pref("browser.search.geoip.url", "");
user_pref("browser.search.geoip.timeout", 1);

// Disable WebRTC
user_pref("media.peerconnection.enabled", false);
user_pref("media.peerconnection.use_document_iceservers", false);
user_pref("media.peerconnection.video.enabled", false);
user_pref("media.peerconnection.identity.enabled", false);
user_pref("media.peerconnection.identity.timeout", 1);
user_pref("media.peerconnection.turn.disable", true);
user_pref("media.peerconnection.ice.tcp", false);
user_pref("media.navigator.video.enabled", false);

// Improve WebRTC IP leak issue
user_pref("media.peerconnection.ice.default_address_only", true);
user_pref("media.peerconnection.ice.no_host", true);

// Disable audio auto-play in non-active tabs
user_pref("media.block-autoplay-until-in-foreground", true);

// Disable link prefetching
user_pref("network.prefetch-next", false);

// Disable DNS prefetching
user_pref("network.dns.disablePrefetch", true);
user_pref("network.dns.disablePrefetchFromHTTPS", true);

// Disable opening connection to links when mouseover
user_pref("network.http.speculative-parallel-limit", 0);

// Disable Beacon
user_pref("beacon.enabled", false);

// Ignore the `ping` attribute for `<a>` elements
user_pref("browser.send_pings", false);
user_pref("browser.send_pings.require_same_host", true);

// Disable camera controlling
user_pref("camera.control.face_detection.enabled", false);

// Decrease the maximum number of popups from a single non-click event (Default is 20)
user_pref("dom.popup_maximum", 2);

// Disallow JavaScript to raise or lower windows
user_pref("dom.disable_window_flip", true);

// Disallow JavaScript to move or resize existing windows
user_pref("dom.disable_window_move_resize", true);

// Disable web notifications
user_pref("dom.webnotifications.enabled", false);
user_pref("dom.webnotifications.serviceworker.enabled", false);

// Disable Flyweb
user_pref("dom.flyweb.enabled", false);

// Disable SPDY (SPDY would be deprecated in 2016)
user_pref("network.http.spdy.enabled", false);
user_pref("network.http.spdy.enabled.deps", false);

// -----------------------------------------------------------------------------
// Privacy
// -----------------------------------------------------------------------------

// Request that sites not track me
user_pref("privacy.donottrackheader.enabled", true);

// Use Tracking Protection in Private Windows
user_pref("privacy.trackingprotection.enabled", true);
user_pref("privacy.trackingprotection.pbmode.enabled", true);
user_pref("privacy.trackingprotection.introCount", 20);
user_pref("privacy.trackingprotection.ui.enabled", true);

// Never accept third-party cookies
user_pref("network.cookie.cookieBehavior", 1);

// Set the maximum number of pages to be retained in the history database
user_pref("places.history.expiration.max_pages", 10000);
user_pref("places.history.expiration.transient_current_max_pages", 10000);

// -----------------------------------------------------------------------------
// Security
// -----------------------------------------------------------------------------

// Block reported attack sites
user_pref("browser.safebrowsing.malware.enabled", true);

// Block reported web forgeries
user_pref("browser.safebrowsing.enabled", true);

// Do not remember passwords for sites
user_pref("signon.rememberSignons", false);

// -----------------------------------------------------------------------------
// Advanced
// -----------------------------------------------------------------------------

// Disable Firefox Health Report
user_pref("datareporting.healthreport.uploadEnabled", false);
user_pref("datareporting.healthreport.service.enabled", false);
user_pref("datareporting.healthreport.documentServerURI", "");
user_pref("datareporting.healthreport.about.reportUrl", "data:text/plain,");
user_pref("datareporting.healthreport.about.reportUrlInified", "data:text/plain,");
user_pref("datareporting.policy.dataSubmissionEnabled", false);
user_pref("datareporting.policy.dataSubmissionEnabled.v2", false);

// Disable Crash Reporter
user_pref("breakpad.reportURL", "");
user_pref("browser.tabs.crashReporting.sendReport", false); // Added in Firefox 44
user_pref("browser.crashReports.unsubmittedCheck.enabled", false);
user_pref("browser.crashReports.unsubmittedCheck.autoSubmit", false);

// Disable Telemetry
user_pref("toolkit.telemetry.enabled", false);
user_pref("toolkit.telemetry.unified", false);
user_pref("toolkit.telemetry.unifiedIsOptIn", true);
user_pref("toolkit.telemetry.archive.enabled", false);
user_pref("toolkit.telemetry.cachedClientID", "");
user_pref("toolkit.telemetry.server", "");

// Disable Telemetry Experiments
user_pref("experiments.enabled", false);
user_pref("experiments.supported", false);
user_pref("experiments.activeExperiment", false);
user_pref("experiments.manifest.uri", "");
user_pref("network.allow-experiments", false);

// Enable OCSP stapling
user_pref("security.ssl.enable_ocsp_stapling", true);

// Query OCSP responder servers to confirm current validity of certificates
user_pref("security.OCSP.enabled", 1);

// -----------------------------------------------------------------------------
// Add-ons and Plugins
// -----------------------------------------------------------------------------

// Disable enforced add-on signing since Firefox 43
user_pref("xpinstall.signatures.required", false);

// Hide the "Get Add-ons" pane in the Add-ons page
user_pref("extensions.getAddons.showPane", false);

// Disable extension discovery
user_pref("extensions.webservice.discoverURL", "http://127.0.0.1");

// Lock down allowed extension directories
user_pref("extensions.enabledScopes", 1);

// Clear localStorage and UUID when a WebExtension is uninstalled
user_pref("extensions.webextensions.keepStorageOnUninstall", false);
user_pref("extensions.webextensions.keepUuidOnUninstall", false);

// Ask me to activate plugins by default
user_pref("plugin.default.state", 1);
user_pref("plugin.defaultXpi.state", 1);

// Enable click-to-play and set the session permission
user_pref("plugins.click_to_play", true);
user_pref("plugin.sessionPermissionNow.intervalInMinutes", 0);

// Click to play Adobe Flash
user_pref("plugin.state.flash", 1);

// Disable all GMP (Gecko Media Plugins)
user_pref("media.gmp-provider.enabled", false);
user_pref("media.gmp.trial-create.enabled", false);

// Disable widevine CDM
user_pref("media.gmp-widevinecdm.visible", false);
user_pref("media.gmp-widevinecdm.enabled", false);
user_pref("media.gmp-widevinecdm.autoupdate", false);

// Disable all DRM content
user_pref("media.eme.enabled", false);
user_pref("browser.eme.ui.enabled", false);

// Disable OpenH264 Video Codec by Cisco
user_pref("media.gmp-gmpopenh264.enabled", false);
user_pref("media.gmp-gmpopenh264.autoupdate", false);
user_pref("media.gmp-manager.url", "data:text/plain,");

// -----------------------------------------------------------------------------
// Others
// -----------------------------------------------------------------------------

// Disable Heartbeat
user_pref("browser.selfsupport.enabled", false);
user_pref("browser.selfsupport.url", "");

// Disable Shield (Telemetry system)
user_pref("extensions.shield-recipe-client.enabled", false);
user_pref("extensions.shield-recipe-client.api_url", "");

// Disable Pocket
user_pref("extensions.pocket.enabled", false);
user_pref("extensions.pocket.api", "");
user_pref("extensions.pocket.site", "");
user_pref("extensions.pocket.oAuthConsumerKey", "");

// Disable Reader View
user_pref("reader.parse-on-load.enabled", false);

// Disable capturing page thumbnails
user_pref("browser.pagethumbnails.capturing_disabled", true);

// Disable Taskbar Jump List in Windows 7 and above
user_pref("browser.taskbar.lists.frequent.enabled", false);
user_pref("browser.taskbar.lists.recent.enabled", false);

// Disable "Slow startup" warnings
user_pref("browser.slowStartup.notificationDisabled", true);
user_pref("browser.slowStartup.maxSamples", 0);
user_pref("browser.slowStartup.samples", 0);

// Disable `about:config` warnings
user_pref("general.warnOnAboutConfig", false);

// Skip the installation delay
user_pref("security.dialog_enable_delay", 0);

// Disable closing browser with last tab
user_pref("browser.tabs.closeWindowWithLastTab", false);

// Disable UI animation
user_pref("browser.fullscreen.animate", false);
user_pref("browser.tabs.animate", false);

// Disable site specific zoom
user_pref("browser.zoom.siteSpecific", false);

// Disable search suggestions in URL bar
user_pref("browser.urlbar.suggest.searches", false);
user_pref("browser.urlbar.userMadeSearchSuggestionsChoice", true);

// Disable preloading top websites in URL bar (Firefox 54+)
user_pref("browser.urlbar.usepreloadedtopurls.enabled", false);

// Display the raw Punycode in URL bar.
user_pref("network.IDN_show_punycode", true);

// Disable WebIDE to prevent remote debugging and addon downloads
user_pref("devtools.webide.enabled", false);
user_pref("devtools.webide.autoinstallADBHelper", false);
user_pref("devtools.webide.autoinstallFxdtAdapters", false);
user_pref("devtools.debugger.remote-enabled", false);

// Disable UI tour
user_pref("browser.uitour.enabled", false);
user_pref("browser.uitour.url", "");

// Prevent links from launching Windows Store (Windows 8+)
user_pref("network.protocol-handler.external.ms-windows-store", false);

// Disable Microsoft Family Safety cert (Winodws 8+)
user_pref("security.family_safety.mode", 0);

// Set the cache directory location
// user_pref("browser.cache.disk.parent_directory", "");
<|endoftext|>


<commit_before>jQuery(document).ready(function() {

    var windowWidth = $(window).width();
    $(window).on('load resize', function(){
        updateReadingBar();
        updateActiveMenuItem();

        windowWidth = $(window).width();

        if(windowWidth > 998){
            if($('.chapter-links').length){
                $('.chapter-links').css('display','inline-block');
                if($('.chapter-links').hasClass('open')){
                    $('.chapter-links').removeClass('open');
                    $('.fa').toggleClass('fa-chevron-down fa-chevron-up');
                }
            }
        }
        if(windowWidth < 998){
            if($('.chapter-links').length && (!($('.chapter-links').hasClass('open')))){
                $('.chapter-links').css('display', 'none');
            }
        }
    });

    if($('.feature-image-overlay').length){
        $('.feature-image-overlay').css('opacity', '0');
    }

    $('#mobile-chapter-menu').click(function(e) {
        $('.chapter-links').slideToggle().toggleClass('open');
        $('.fa').toggleClass('fa-chevron-down fa-chevron-up');
    });

    if($('.story .block-RelatedItems').length){
        $('.story').before( '

' );
        setRelatedMenu();
        $('.related-content-menu').click(function(e){
            $('.story .block-RelatedItems').slideToggle();
            $('.story .block-RelatedItems').closest('.container-clear-none').addClass('open');
            $('.related-content-menu i').toggleClass('fa-close fa-bars');
        });
    }

    function setRelatedMenu(){
        if($('.related-content-menu').length){
            var fromTop = $('.related-content-menu').offset().top - $(window).scrollTop() - 200;
            //console.log($(window).scrollTop() + ' ' + fromTop);
            var windowPosition = $(window).scrollTop();
            if(fromTop <= 0){
                $('.related-content-menu').addClass('fixed');
                $('.story .container-clear-none').addClass('fixed');
            }
            if(windowPosition < 550){
                $('.related-content-menu').removeClass('fixed');
                $('.story .container-clear-none').removeClass('fixed');
            }
        }
    }

    function unsetRelatedMenu(){
        if($('footer .block-RelatedItems').length){
            var fromBottom = $('footer').offset().top - $(window).scrollTop();
            if(fromBottom < 350){
                $('.related-content-menu').removeClass('fixed');
                $('.story .container-clear-none').removeClass('fixed');
            }
        }
    }

    function updateReadingBar() {
        var s = $(window).scrollTop(); // Current scroll mark
        ap = $('#article-page').last();
        s = s - ap.position().top; // Start counting at the top of the article
        d = ap.height(); // The end of the article page element
        c = $(window).height(); // Height of the element
        scrollPercent = (s / (d-c)) * 100;
        if (scrollPercent > 100) scrollPercent = 100;
        else if (scrollPercent < 0) scrollPercent = 0;
        $('.readingbar').width(scrollPercent + '%');
    }

    /*** update selected chapter in header menu on scroll */
    function updateActiveMenuItem() {
       // Get container scroll position
       var fromTop = $(this).scrollTop() + chapterMenuHeight;

       // Get id of current scroll item
       var cur = scrollItems.map(function(){
         if ($(this).offset().top < fromTop)
           return this;
       });
       // Get the id of the current element
       cur = cur[cur.length-1];
       var id = cur && cur.length ? cur[0].id : "";
       // Set/remove active class
       menuItems
         .parent().removeClass("active")
         .end().filter("[href='#"+id+"']").parent().addClass("active");
    }

    // Keeping track of position in article to update active page in menu
    var chapterMenu = $(".chapter-links ul"),
        chapterMenuHeight = chapterMenu.outerHeight(),
        // All list items
        menuItems = chapterMenu.find("a"),
        // Anchors corresponding to menu items
        scrollItems = menuItems.map(function(){
          var item = $($(this).attr("href"));
          if (item.length) { return item; }
        });

    // Bind click handler to menu items to scroll down to chapter on page
    menuItems.click(function(e){
      var href = $(this).attr("href"),
          offsetTop = href === "#" ? 0 : $(href).offset().top-chapterMenuHeight+1;
      $('html, body').stop().animate({
          scrollTop: offsetTop
      }, 300);
      e.preventDefault();
    });

// Bind to scroll
    $(window).scroll(function(){
       updateReadingBar();
       updateActiveMenuItem();
       setRelatedMenu();
       unsetRelatedMenu();
    });

});
<commit_msg>add close related items menu on bodyclick<commit_after>jQuery(document).ready(function() {

    var windowWidth = $(window).width();
    $(window).on('load resize', function(){
        updateReadingBar();
        updateActiveMenuItem();

        windowWidth = $(window).width();

        if(windowWidth > 998){
            if($('.chapter-links').length){
                $('.chapter-links').css('display','inline-block');
                if($('.chapter-links').hasClass('open')){
                    $('.chapter-links').removeClass('open');
                    $('.fa').toggleClass('fa-chevron-down fa-chevron-up');
                }
            }
        }
        if(windowWidth < 998){
            if($('.chapter-links').length && (!($('.chapter-links').hasClass('open')))){
                $('.chapter-links').css('display', 'none');
            }
        }
    });

    if($('.feature-image-overlay').length){
        $('.feature-image-overlay').css('opacity', '0');
    }

    $('#mobile-chapter-menu').click(function(e) {
        $('.chapter-links').slideToggle().toggleClass('open');
        $('.fa').toggleClass('fa-chevron-down fa-chevron-up');
    });

    if($('.story .block-RelatedItems').length){
        $('.story').before( '

' );
        setRelatedMenu();
        $('.related-content-menu').click(function(e){
            $('.story .block-RelatedItems').slideToggle();
            $('.story .block-RelatedItems').closest('.container-clear-none').addClass('open');
            $('.related-content-menu i').toggleClass('fa-close fa-bars');
        });
    }

    function setRelatedMenu(){
        if($('.related-content-menu').length){
            var fromTop = $('.related-content-menu').offset().top - $(window).scrollTop() - 200;
            //console.log($(window).scrollTop() + ' ' + fromTop);
            var windowPosition = $(window).scrollTop();
            if(fromTop <= 0){
                $('.related-content-menu').addClass('fixed');
                $('.story .container-clear-none').addClass('fixed');
            }
            if(windowPosition < 550){
                $('.related-content-menu').removeClass('fixed');
                $('.story .container-clear-none').removeClass('fixed');
            }
        }
    }

    function unsetRelatedMenu(){
        if($('footer .block-RelatedItems').length){
            var fromBottom = $('footer').offset().top - $(window).scrollTop();
            if(fromBottom < 350){
                $('.related-content-menu').removeClass('fixed');
                $('.story .container-clear-none').removeClass('fixed');
            }
        }
    }

    function updateReadingBar() {
        var s = $(window).scrollTop(); // Current scroll mark
        ap = $('#article-page').last();
        s = s - ap.position().top; // Start counting at the top of the article
        d = ap.height(); // The end of the article page element
        c = $(window).height(); // Height of the element
        scrollPercent = (s / (d-c)) * 100;
        if (scrollPercent > 100) scrollPercent = 100;
        else if (scrollPercent < 0) scrollPercent = 0;
        $('.readingbar').width(scrollPercent + '%');
    }

    /*** update selected chapter in header menu on scroll */
    function updateActiveMenuItem() {
       // Get container scroll position
       var fromTop = $(this).scrollTop() + chapterMenuHeight;

       // Get id of current scroll item
       var cur = scrollItems.map(function(){
         if ($(this).offset().top < fromTop)
           return this;
       });
       // Get the id of the current element
       cur = cur[cur.length-1];
       var id = cur && cur.length ? cur[0].id : "";
       // Set/remove active class
       menuItems
         .parent().removeClass("active")
         .end().filter("[href='#"+id+"']").parent().addClass("active");
    }

    // Keeping track of position in article to update active page in menu
    var chapterMenu = $(".chapter-links ul"),
        chapterMenuHeight = chapterMenu.outerHeight(),
        // All list items
        menuItems = chapterMenu.find("a"),
        // Anchors corresponding to menu items
        scrollItems = menuItems.map(function(){
          var item = $($(this).attr("href"));
          if (item.length) { return item; }
        });

    // Bind click handler to menu items to scroll down to chapter on page
    menuItems.click(function(e){
      var href = $(this).attr("href"),
          offsetTop = href === "#" ? 0 : $(href).offset().top-chapterMenuHeight+1;
      $('html, body').stop().animate({
          scrollTop: offsetTop
      }, 300);
      e.preventDefault();
    });

// Bind to scroll
    $(window).scroll(function(){
       updateReadingBar();
       updateActiveMenuItem();
       setRelatedMenu();
       unsetRelatedMenu();
    });

//close related items menu if body is clicked
    $(window).click(function(e) {
        if(!$(e.target).closest('.story .block-RelatedItems').length && !$(e.target).closest('.related-content-menu').length) {
            if($('.story .block-RelatedItems').is(":visible")) {
                $('.story .block-RelatedItems').slideToggle();
                $('.related-content-menu i').toggleClass('fa-close fa-bars');

            }
        }
    });

});
<|endoftext|>


<commit_before>$(document).ready(function() {

  // Fetch and render user reservations
  $.getJSON('/reservations.json', function(data) {
    $('div#reservation-loading').hide();
    data.forEach(function(reservation) {
      renderReservation(reservation);
    });
  });

  // Add booking widget functionality
  $('#booking-widget-container button').click(function() {
    var arrivalDate = $('#form--arrival-date').val();
    var nights = $('#form--nights').val();
    var guests = $('#form--guests').val();
    addBooking(arrivalDate, nights, guests);
    return false;
  });

  // Periodically check for unconfirmed bookings
  setInterval(checkUnconfirmedBookings, 5000);
});

// Go over unconfirmed bookings, and verify their status against the server.
var checkUnconfirmedBookings = function() {
  $('.reservation-card--unconfirmed').each(function() {
    $.getJSON('/reservation-details.json', {id: $(this).data('id')}, function(data) {
      updateReservationDisplay(data);
    });
  });
};

// Adds a booking as pending to the DOM, and try to contact server to book it.
var addBooking = function(arrivalDate, nights, guests) {
  if (!arrivalDate || !nights || !guests) {
    return;
  }
  var reservationDetails = {
    id: Date.now().toString().substring(3,11),
    arrivalDate:  arrivalDate,
    nights:       nights,
    guests:       guests,
    status:       'Awaiting confirmation'
  };
  renderReservation(reservationDetails);
  $.getJSON('/make-reservation', reservationDetails, function(data) {
    updateReservationDisplay(data);
  });
};

/* ************************************************************ */
/* The code below this point is used to render to the DOM. It   */
/* completely ignores common sense principles like separation   */
/* of concerns as a trade off for readability.                  */
/* You can ignore it, or you can send angry tweets about it to  */
/* @TalAter                                                     */
/* ************************************************************ */

// Renders a reservation card and adds it to the DOM.
var renderReservation = function(reservation) {
  var newReservation = $(
    '
'+
      ''+
      '
'+
        '
'+
          'Gotham Imperial Hotel'+
          '
1 Imperial Plaza, Gotham.
'+
          '
Check-in: '+reservation['arrivalDate']+'.
'+
          '
'+reservation['nights']+' nights. '+reservation['guests']+' guests.
'+
        '
'+
        '
'+
          '
Total price
'+
          '
'+(reservation['price'] ? '§'+reservation['price']+'.99' : '?')+'
'+
        '
'+
      '
'+
      '
'+
        'Modify booking details'+
        '
'+reservation['status']+'
'+
      '
'+
      '
'+
        'Order number: '+reservation['id']+''+
        'Booked on: '+(reservation['bookedOn'] ? reservation['bookedOn'] : 'n/a')+''+
      '
'+
    '
'
  );
  $('#reservations-container').append(newReservation);
  if (reservation['status'] !== 'Confirmed') {
    newReservation.addClass('reservation-card--unconfirmed');
  }

  // Adds an event listener to the modify reservation button.
  $('#reservation-'+reservation['id']+' a').click(function() {
    var possibleResponses = ['Orders are non-negotiable!', 'Not open to discussion!'];
    $(this).text(possibleResponses[Math.floor(Math.random()*possibleResponses.length)]);
    $(this).addClass('reservation-action--error');
    return false;
  });

};

var updateReservationDisplay = function(reservation) {
  var reservationNode = $('#reservation-'+reservation.id);
  $('.reservation-bookedOn', reservationNode).text(reservation.bookedOn);
  $('.reservation-price strong', reservationNode).text('§'+reservation.price+'.99');
  $('.reservation-status', reservationNode).text(reservation.status);
  $('.arrivalDate span', reservationNode).text(reservation.arrivalDate);
  if (reservation['status'] !== 'Confirmed') {
    reservationNode.addClass('reservation-card--unconfirmed');
  } else {
    reservationNode.removeClass('reservation-card--unconfirmed');
  }

};
<commit_msg>Moved new booking id generation and input verification to form submission<commit_after>$(document).ready(function() {

  // Fetch and render user reservations
  $.getJSON('/reservations.json', function(data) {
    $('div#reservation-loading').hide();
    data.forEach(function(reservation) {
      renderReservation(reservation);
    });
  });

  // Add booking widget functionality
  $('#booking-widget-container button').click(function() {
    var arrivalDate = $('#form--arrival-date').val();
    var nights = $('#form--nights').val();
    var guests = $('#form--guests').val();
    var id = Date.now().toString().substring(3,11);
    if (!arrivalDate || !nights || !guests) {
      return;
    }
    addBooking(id, arrivalDate, nights, guests);
    return false;
  });

  // Periodically check for unconfirmed bookings
  setInterval(checkUnconfirmedBookings, 5000);
});

// Go over unconfirmed bookings, and verify their status against the server.
var checkUnconfirmedBookings = function() {
  $('.reservation-card--unconfirmed').each(function() {
    $.getJSON('/reservation-details.json', {id: $(this).data('id')}, function(data) {
      updateReservationDisplay(data);
    });
  });
};

// Adds a booking as pending to the DOM, and try to contact server to book it.
var addBooking = function(id, arrivalDate, nights, guests) {
  var reservationDetails = {
    id:           id,
    arrivalDate:  arrivalDate,
    nights:       nights,
    guests:       guests,
    status:       'Awaiting confirmation'
  };
  renderReservation(reservationDetails);
  $.getJSON('/make-reservation', reservationDetails, function(data) {
    updateReservationDisplay(data);
  });
};

/* ************************************************************ */
/* The code below this point is used to render to the DOM. It   */
/* completely ignores common sense principles like separation   */
/* of concerns as a trade off for readability.                  */
/* You can ignore it, or you can send angry tweets about it to  */
/* @TalAter                                                     */
/* ************************************************************ */

// Renders a reservation card and adds it to the DOM.
var renderReservation = function(reservation) {
  var newReservation = $(
    '
'+
      ''+
      '
'+
        '
'+
          'Gotham Imperial Hotel'+
          '
1 Imperial Plaza, Gotham.
'+
          '
Check-in: '+reservation['arrivalDate']+'.
'+
          '
'+reservation['nights']+' nights. '+reservation['guests']+' guests.
'+
        '
'+
        '
'+
          '
Total price
'+
          '
'+(reservation['price'] ? '§'+reservation['price']+'.99' : '?')+'
'+
        '
'+
      '
'+
      '
'+
        'Modify booking details'+
        '
'+reservation['status']+'
'+
      '
'+
      '
'+
        'Order number: '+reservation['id']+''+
        'Booked on: '+(reservation['bookedOn'] ? reservation['bookedOn'] : 'n/a')+''+
      '
'+
    '
'
  );
  $('#reservations-container').append(newReservation);
  if (reservation['status'] !== 'Confirmed') {
    newReservation.addClass('reservation-card--unconfirmed');
  }

  // Adds an event listener to the modify reservation button.
  $('#reservation-'+reservation['id']+' a').click(function() {
    var possibleResponses = ['Orders are non-negotiable!', 'Not open to discussion!'];
    $(this).text(possibleResponses[Math.floor(Math.random()*possibleResponses.length)]);
    $(this).addClass('reservation-action--error');
    return false;
  });

};

var updateReservationDisplay = function(reservation) {
  var reservationNode = $('#reservation-'+reservation.id);
  $('.reservation-bookedOn', reservationNode).text(reservation.bookedOn);
  $('.reservation-price strong', reservationNode).text('§'+reservation.price+'.99');
  $('.reservation-status', reservationNode).text(reservation.status);
  $('.arrivalDate span', reservationNode).text(reservation.arrivalDate);
  if (reservation['status'] !== 'Confirmed') {
    reservationNode.addClass('reservation-card--unconfirmed');
  } else {
    reservationNode.removeClass('reservation-card--unconfirmed');
  }

};
<|endoftext|>


<commit_before>/* global bundle, $, d3plus */

var d3 = require("d3");

var COLOR_TABLE = [
  "#C8D2D9", "#97ABB8", "#FFBEB9", "#FFD1CE"
];

/* This is a poor-man's estimate, given the fact that measuring the
 * actual size of the characters (accounting for kerning) will involve
 * significant DOM mutation. */
var INDIVIDUAL_CHARACTER_SIZE = 10;

/**
 * drawBarForDataPoint
 */
function drawBarForDataPoint(svg, dataPoint, index, length, offsetAmount, dataMax) {
  var computeTextY = function computeTextY() {
    var chartHeight = this.parentNode.parentNode.clientHeight;
    var barSpacing = 10;
    var textHeight = 15;
    return (((chartHeight - (barSpacing * length)) /
            length) / 2) + (textHeight / 2);
  };
  var computeBarWidth = function computeBarWidth() {
    var parentWidth = (this.parentNode.parentNode.clientWidth -
                       offsetAmount);
    var x = d3.scaleLinear().domain([0, dataMax])
                            .range([0, parentWidth]);
    return x(dataPoint.value);
  };
  var g = svg.append("g")
             .attr("transform", function computeBarY() {
               var chartHeight = this.parentNode.clientHeight;
               var barSpacing = 10;
               var barHeight = ((chartHeight - (barSpacing *
                                                length)) /
                                length);

               var y = (index * barSpacing) + (index * barHeight);
               return "translate(0.0, " + y + ")";
             });
  g.append("rect")
   .attr("height", function computeBarHeight() {
     var chartHeight = this.parentNode.parentNode.clientHeight;
     var barSpacing = 10;
     return ((chartHeight - (barSpacing * length)) /
             length);
   })
   .transition()
   .delay(index * 50)
   .duration(750)
   .ease(d3.easeElastic.period(0.4))
   .attr("width", computeBarWidth)
   .attr("rx", "15")
   .attr("ry", "15")
   .attr("x", String(offsetAmount))
   .attr("fill", COLOR_TABLE[index % COLOR_TABLE.length]);
  g.append("text")
   .attr("fill", "#000000")
   .text(dataPoint.name)
   .attr("y", computeTextY)
   .attr("x", 10);
  g.append("text")
   .attr("fill", "#ffffff")
   .text(dataPoint.value)
   .attr("y", computeTextY)
   .transition()
   .delay(index * 50)
   .duration(750)
   .ease(d3.easeElastic.period(0.4))
   .attr("x", function computeNumberX() {
     var barWidth = computeBarWidth.call(this);
     var label = String(dataPoint.value);
     var labelSize = label.length * INDIVIDUAL_CHARACTER_SIZE;
     return (barWidth - labelSize) + offsetAmount;
   });
}

/**
 * drawBarChart
 *
 * Draws a bar chart using data on the given node.
 *
 * @data {array} - The data to use to draw the chart
 * @chartContainer {object} - The containing element for this chart
 * @returns {object} - The completed node
 */
function drawBarChart(data, chartContainer) {
  /* The individual points of data, not including labels */
  var dataPoints = data.map(function onEachDataPoint(w) {
    return w.value;
  });

  var svg = d3.select("div.chart")
              .append("svg")
              .attr("class", "svgchart")
              .attr("width", function computeWidth() {
                return this.parentNode.clientWidth * 0.80;
              })
              .attr("height", function computeHeight() {
                return this.parentNode.clientHeight * 0.80;
              })
              .attr("style", function computeTransform() {
                var x = this.parentNode.clientWidth * 0.10;
                var y = this.parentNode.clientHeight * 0.10;
                return ["transform: translate(", x, "px,", y, "px", ")"].join("");
              });

  /* Estimated sizes of the labels and the bars if no scaling was applied -
   * use this to determine whether an offset should be applied to the chart
   * based on the width of the smallest bar and its corresponding label. If
   * a bar's label size exceeds the size of the bar, we need to make
   * at least enough room on the left hand side for the bar label */
  var offsetAmount = 0;
  var postprocessedData = data.map(function onEachDataPoint(d) {
    var availableParentWidth = chartContainer.clientWidth * 0.80;
    var x = d3.scaleLinear().domain([0, d3.max(dataPoints)])
                            .range([0, availableParentWidth]);
    var label = 10 + (INDIVIDUAL_CHARACTER_SIZE * d.name.length);

    if (offsetAmount < label) {
      offsetAmount = label;
    }

    return {
      name: d.name,
      value: d.value
    };
  });

  var dataMax = d3.max(dataPoints);
  postprocessedData.forEach(function forEachDataPoint(dataPoint, index) {
    drawBarForDataPoint(svg, dataPoint, index, postprocessedData.length, offsetAmount, dataMax);
  });

  return svg;
}

/**
 * drawLineChart
 *
 * Draws a line chart using hte data on the given node.
 *
 * @svg {object} - An svg node to draw the bar chart on
 * @data {array} = The data to use to draw the chart
 * @returns {object} - The completed node
 */
function drawLineChart(data) {
  var dataset = data.map(function onEachDataPoint(d) {
    return {
      year: Number(d.name),
      retractions: d.value,
      name: "Retractions"
    };
  });

  return new d3plus.viz()
                   .container(".chart")
                   .data(dataset)
                   .type("line")
                   .id("name")
                   .text("name")
                   .y("retractions")
                   .x("year")
                   .draw();
}

var DrawChartDispatch = {
  countryRetraction: drawBarChart,
  authorRetraction: drawBarChart,
  topicRetraction: drawBarChart,
  retractionsOverTime: drawLineChart
};

/**
 * updateGraph
 *
 * Update the graph with some new data and remove the old one.
 *
 * @newData: New tuples of data to update the graph with
 */
function updateGraph(newData, name) {
  var chartContainer = document.getElementsByClassName("chart")[0];

  /* Drop any existing SVG elements */
  Array.prototype.forEach.call(document.getElementsByClassName("chart"),
                               function dropInnerHTMLOf(element) {
                                 /* Drop everything inside this chart */
                                 element.innerHTML = "";  // eslint-disable-line no-param-reassign
                               });

  DrawChartDispatch[name](newData, chartContainer);
}

/**
 * postGraphUpdateRequest
 *
 * Post a request to the server to get a new graph.
 *
 * @name The chart name to fetch.
 * @filter A filter string
 # @filterType What to filter on
 */
function postGraphUpdateRequest(name, filterString, filterType) {
  if (!name) {
    $("#incorrectInputAlert").html("You need to specify a chart type to display");
    $("#incorrectInputAlert").attr("style", "");
    return;
  }

  if (filterString && (!filterType || filterType === "none")) {
    $("#incorrectInputAlert").html("You need to specify what to search on");
    $("#incorrectInputAlert").attr("style", "");
    return;
  }

  $("#incorrectInputAlert").attr("style", "visibility: hidden;");

  if (name) {
    $.ajax({
      url: "/get_bar_chart",
      data: {
        name: name,
        filterString: filterString || null,
        filterType: filterType || null
      },
      success: function onXHRSuccess(data) {
        updateGraph(data.data, name);
      }
    });
  }
}

/**
 * listenForUserInteraction
 *
 * For a dropdown menu id and a it's contents' id, listen for
 * click events and then post an update request to fetch a graph.
 */
function listenForUserInteraction(dropdownSelector, menuSelector) {
  $(menuSelector + " li a").click(function onDropdownClick(event) {
    var button = $(dropdownSelector);
    var parentDiv = $(".open");

    button.html(this.text + " ");
    button.attr("selection", $(this).attr("data-selection-id"));
    parentDiv.removeClass("open");
    event.preventDefault();

    postGraphUpdateRequest($("#chartSelectionDropdown").attr("selection"),
                           $("#chartFilterInput").val(),
                           $("#filterSelectionDropdown").attr("selection"));
    return false;
  });
}

document.addEventListener("DOMContentLoaded", function onDOMLoaded() {
  listenForUserInteraction("#chartSelectionDropdown", "#chartSelectionMenu");
  listenForUserInteraction("#filterSelectionDropdown",
                           "#filterSelectionMenu");
  $("#chartFilterInput").keypress(function onKeyPress(event) {
    var keyCode = event.keyCode || event.which;
    if (keyCode === 13) {
      /* User pressed enter, respond accordingly */
      postGraphUpdateRequest($("#chartSelectionDropdown").attr("selection"),
                             $("#chartFilterInput").val(),
                             $("#filterSelectionDropdown").attr("selection"));
    }
  });
});
<commit_msg>retraction: Disable warning about capitalised new<commit_after>/* global bundle, $, d3plus */

var d3 = require("d3");

var COLOR_TABLE = [
  "#C8D2D9", "#97ABB8", "#FFBEB9", "#FFD1CE"
];

/* This is a poor-man's estimate, given the fact that measuring the
 * actual size of the characters (accounting for kerning) will involve
 * significant DOM mutation. */
var INDIVIDUAL_CHARACTER_SIZE = 10;

/**
 * drawBarForDataPoint
 */
function drawBarForDataPoint(svg, dataPoint, index, length, offsetAmount, dataMax) {
  var computeTextY = function computeTextY() {
    var chartHeight = this.parentNode.parentNode.clientHeight;
    var barSpacing = 10;
    var textHeight = 15;
    return (((chartHeight - (barSpacing * length)) /
            length) / 2) + (textHeight / 2);
  };
  var computeBarWidth = function computeBarWidth() {
    var parentWidth = (this.parentNode.parentNode.clientWidth -
                       offsetAmount);
    var x = d3.scaleLinear().domain([0, dataMax])
                            .range([0, parentWidth]);
    return x(dataPoint.value);
  };
  var g = svg.append("g")
             .attr("transform", function computeBarY() {
               var chartHeight = this.parentNode.clientHeight;
               var barSpacing = 10;
               var barHeight = ((chartHeight - (barSpacing *
                                                length)) /
                                length);

               var y = (index * barSpacing) + (index * barHeight);
               return "translate(0.0, " + y + ")";
             });
  g.append("rect")
   .attr("height", function computeBarHeight() {
     var chartHeight = this.parentNode.parentNode.clientHeight;
     var barSpacing = 10;
     return ((chartHeight - (barSpacing * length)) /
             length);
   })
   .transition()
   .delay(index * 50)
   .duration(750)
   .ease(d3.easeElastic.period(0.4))
   .attr("width", computeBarWidth)
   .attr("rx", "15")
   .attr("ry", "15")
   .attr("x", String(offsetAmount))
   .attr("fill", COLOR_TABLE[index % COLOR_TABLE.length]);
  g.append("text")
   .attr("fill", "#000000")
   .text(dataPoint.name)
   .attr("y", computeTextY)
   .attr("x", 10);
  g.append("text")
   .attr("fill", "#ffffff")
   .text(dataPoint.value)
   .attr("y", computeTextY)
   .transition()
   .delay(index * 50)
   .duration(750)
   .ease(d3.easeElastic.period(0.4))
   .attr("x", function computeNumberX() {
     var barWidth = computeBarWidth.call(this);
     var label = String(dataPoint.value);
     var labelSize = label.length * INDIVIDUAL_CHARACTER_SIZE;
     return (barWidth - labelSize) + offsetAmount;
   });
}

/**
 * drawBarChart
 *
 * Draws a bar chart using data on the given node.
 *
 * @data {array} - The data to use to draw the chart
 * @chartContainer {object} - The containing element for this chart
 * @returns {object} - The completed node
 */
function drawBarChart(data, chartContainer) {
  /* The individual points of data, not including labels */
  var dataPoints = data.map(function onEachDataPoint(w) {
    return w.value;
  });

  var svg = d3.select("div.chart")
              .append("svg")
              .attr("class", "svgchart")
              .attr("width", function computeWidth() {
                return this.parentNode.clientWidth * 0.80;
              })
              .attr("height", function computeHeight() {
                return this.parentNode.clientHeight * 0.80;
              })
              .attr("style", function computeTransform() {
                var x = this.parentNode.clientWidth * 0.10;
                var y = this.parentNode.clientHeight * 0.10;
                return ["transform: translate(", x, "px,", y, "px", ")"].join("");
              });

  /* Estimated sizes of the labels and the bars if no scaling was applied -
   * use this to determine whether an offset should be applied to the chart
   * based on the width of the smallest bar and its corresponding label. If
   * a bar's label size exceeds the size of the bar, we need to make
   * at least enough room on the left hand side for the bar label */
  var offsetAmount = 0;
  var postprocessedData = data.map(function onEachDataPoint(d) {
    var availableParentWidth = chartContainer.clientWidth * 0.80;
    var x = d3.scaleLinear().domain([0, d3.max(dataPoints)])
                            .range([0, availableParentWidth]);
    var label = 10 + (INDIVIDUAL_CHARACTER_SIZE * d.name.length);

    if (offsetAmount < label) {
      offsetAmount = label;
    }

    return {
      name: d.name,
      value: d.value
    };
  });

  var dataMax = d3.max(dataPoints);
  postprocessedData.forEach(function forEachDataPoint(dataPoint, index) {
    drawBarForDataPoint(svg, dataPoint, index, postprocessedData.length, offsetAmount, dataMax);
  });

  return svg;
}

/**
 * drawLineChart
 *
 * Draws a line chart using hte data on the given node.
 *
 * @svg {object} - An svg node to draw the bar chart on
 * @data {array} = The data to use to draw the chart
 * @returns {object} - The completed node
 */
function drawLineChart(data) {
  var dataset = data.map(function onEachDataPoint(d) {
    return {
      year: Number(d.name),
      retractions: d.value,
      name: "Retractions"
    };
  });

  return new d3plus.viz()  // eslint-disable-line new-cap
                   .container(".chart")
                   .data(dataset)
                   .type("line")
                   .id("name")
                   .text("name")
                   .y("retractions")
                   .x("year")
                   .draw();
}

var DrawChartDispatch = {
  countryRetraction: drawBarChart,
  authorRetraction: drawBarChart,
  topicRetraction: drawBarChart,
  retractionsOverTime: drawLineChart
};

/**
 * updateGraph
 *
 * Update the graph with some new data and remove the old one.
 *
 * @newData: New tuples of data to update the graph with
 */
function updateGraph(newData, name) {
  var chartContainer = document.getElementsByClassName("chart")[0];

  /* Drop any existing SVG elements */
  Array.prototype.forEach.call(document.getElementsByClassName("chart"),
                               function dropInnerHTMLOf(element) {
                                 /* Drop everything inside this chart */
                                 element.innerHTML = "";  // eslint-disable-line no-param-reassign
                               });

  DrawChartDispatch[name](newData, chartContainer);
}

/**
 * postGraphUpdateRequest
 *
 * Post a request to the server to get a new graph.
 *
 * @name The chart name to fetch.
 * @filter A filter string
 # @filterType What to filter on
 */
function postGraphUpdateRequest(name, filterString, filterType) {
  if (!name) {
    $("#incorrectInputAlert").html("You need to specify a chart type to display");
    $("#incorrectInputAlert").attr("style", "");
    return;
  }

  if (filterString && (!filterType || filterType === "none")) {
    $("#incorrectInputAlert").html("You need to specify what to search on");
    $("#incorrectInputAlert").attr("style", "");
    return;
  }

  $("#incorrectInputAlert").attr("style", "visibility: hidden;");

  if (name) {
    $.ajax({
      url: "/get_bar_chart",
      data: {
        name: name,
        filterString: filterString || null,
        filterType: filterType || null
      },
      success: function onXHRSuccess(data) {
        updateGraph(data.data, name);
      }
    });
  }
}

/**
 * listenForUserInteraction
 *
 * For a dropdown menu id and a it's contents' id, listen for
 * click events and then post an update request to fetch a graph.
 */
function listenForUserInteraction(dropdownSelector, menuSelector) {
  $(menuSelector + " li a").click(function onDropdownClick(event) {
    var button = $(dropdownSelector);
    var parentDiv = $(".open");

    button.html(this.text + " ");
    button.attr("selection", $(this).attr("data-selection-id"));
    parentDiv.removeClass("open");
    event.preventDefault();

    postGraphUpdateRequest($("#chartSelectionDropdown").attr("selection"),
                           $("#chartFilterInput").val(),
                           $("#filterSelectionDropdown").attr("selection"));
    return false;
  });
}

document.addEventListener("DOMContentLoaded", function onDOMLoaded() {
  listenForUserInteraction("#chartSelectionDropdown", "#chartSelectionMenu");
  listenForUserInteraction("#filterSelectionDropdown",
                           "#filterSelectionMenu");
  $("#chartFilterInput").keypress(function onKeyPress(event) {
    var keyCode = event.keyCode || event.which;
    if (keyCode === 13) {
      /* User pressed enter, respond accordingly */
      postGraphUpdateRequest($("#chartSelectionDropdown").attr("selection"),
                             $("#chartFilterInput").val(),
                             $("#filterSelectionDropdown").attr("selection"));
    }
  });
});
<|endoftext|>


<commit_before>var System = require('./System.js');
var Skybox = require('./Skybox.js');

var ExoViz = module.exports = function() {
  console.log('Initializing system');

  // Make animate always execute in our scope
  this.animate = this.animate.bind(this);

  // Camera
  this.camera = new THREE.PerspectiveCamera(45, document.body.clientWidth / document.body.clientHeight, 1, 10000);
  this.camera.position.z = 1500;

  // Controls
  this.controls = new THREE.TrackballControls(this.camera);

  this.controls.rotateSpeed = 1.0;
  this.controls.zoomSpeed = 1.2;
  this.controls.panSpeed = 0.8;

  this.controls.noZoom = false;
  this.controls.noPan = false;

  this.controls.staticMoving = true;
  this.controls.dynamicDampingFactor = 0.3;

  // Adds functions in conjuction with mouse click:
  //  A = rotate, S = zoom, D = pan
  this.controls.keys = [65, 83, 68];

  // TODO: Why do you need to do this? Shouldn't it happen in the render loop?
  var self = this;
  this.controls.addEventListener('change', function() {
    self.render();
  });

  // Scene
  this.scene = new THREE.Scene();

  // Renderer
  // Create a WebGLRenderer
  this.renderer = new THREE.WebGLRenderer({antialias: true});
  this.renderer.setClearColor(0x000000, 1.0);
  this.renderer.clear();

  // Add to DOM
  this.$container = $("#container"); //grab DOM element
  this.$container.append(this.renderer.domElement);

  // Set size
  this.setSize(container.offsetWidth, container.offsetHeight);

  // Create new system
  this.system = new System({
    starName: 'HAT-P-1',
    scene: this.scene,
    loaded: this.addData.bind(this)
  });

  // Create skybox
  this.skybox = new Skybox({
    scene: this.scene
  });

  $(window).on('resize', this.setSize.bind(this));

  // Start animating
  this.animate(0);
};

ExoViz.prototype.setSystem = function(starName) {
  this.system.fetch(starName);
};

ExoViz.prototype.setSize = function(width, height) {
  var width = this.width = this.$container.innerWidth();
  var height = this.height = this.$container.innerHeight();

  // Make crisp on retina displays by using devicePixelRation
  var ratio = window.devicePixelRatio || 1;
  this.renderer.setSize(width*ratio, height*ratio);

  this.renderer.setSize(width, height);

  if (this.camera) {
    this.camera.aspect = width/height;

    this.camera.updateProjectionMatrix();
  }
};

ExoViz.prototype.render = function() {
  // this.cubeCamera.rotation.copy(this.camera.rotation); // ties skybox camera to regular camera
  // this.renderer.render(this.cubeScene, this.cubeCamera);
  this.renderer.render(this.scene, this.camera);
};

ExoViz.prototype.animate = function(time) {
  this.controls.update();

  this.system.update(time);

  this.render();

  requestAnimationFrame(this.animate);
};

module.exports = ExoViz;
<commit_msg>removed duplicate line without retina display support<commit_after>var System = require('./System.js');
var Skybox = require('./Skybox.js');

var ExoViz = module.exports = function() {
  console.log('Initializing system');

  // Make animate always execute in our scope
  this.animate = this.animate.bind(this);

  // Camera
  this.camera = new THREE.PerspectiveCamera(45, document.body.clientWidth / document.body.clientHeight, 1, 10000);
  this.camera.position.z = 1500;

  // Controls
  this.controls = new THREE.TrackballControls(this.camera);

  this.controls.rotateSpeed = 1.0;
  this.controls.zoomSpeed = 1.2;
  this.controls.panSpeed = 0.8;

  this.controls.noZoom = false;
  this.controls.noPan = false;

  this.controls.staticMoving = true;
  this.controls.dynamicDampingFactor = 0.3;

  // Adds functions in conjuction with mouse click:
  //  A = rotate, S = zoom, D = pan
  this.controls.keys = [65, 83, 68];

  // TODO: Why do you need to do this? Shouldn't it happen in the render loop?
  var self = this;
  this.controls.addEventListener('change', function() {
    self.render();
  });

  // Scene
  this.scene = new THREE.Scene();

  // Renderer
  // Create a WebGLRenderer
  this.renderer = new THREE.WebGLRenderer({antialias: true});
  this.renderer.setClearColor(0x000000, 1.0);
  this.renderer.clear();

  // Add to DOM
  this.$container = $("#container"); //grab DOM element
  this.$container.append(this.renderer.domElement);

  // Set size
  this.setSize(container.offsetWidth, container.offsetHeight);

  // Create new system
  this.system = new System({
    starName: $('#starlist').val(),
    scene: this.scene,
    //loaded: this.addData.bind(this) //loads data into DOM
  });

  // Create skybox
  this.skybox = new Skybox({
    scene: this.scene
  });

  $(window).on('resize', this.setSize.bind(this));

  // Start animating
  this.animate(0);
};

ExoViz.prototype.setSystem = function(starName) {
  this.system.fetch(starName);
};

ExoViz.prototype.setSize = function(width, height) {
  var width = this.width = this.$container.innerWidth();
  var height = this.height = this.$container.innerHeight();

  // Make crisp on retina displays by using devicePixelRation
  var ratio = window.devicePixelRatio || 1;
  this.renderer.setSize(width*ratio, height*ratio);

  if (this.camera) {
    this.camera.aspect = width/height;

    this.camera.updateProjectionMatrix();
  }
};

ExoViz.prototype.render = function() {
  // this.cubeCamera.rotation.copy(this.camera.rotation); // ties skybox camera to regular camera
  // this.renderer.render(this.cubeScene, this.cubeCamera);
  this.renderer.render(this.scene, this.camera);
};

ExoViz.prototype.animate = function(time) {
  this.controls.update();

  this.system.update(time);

  this.render();

  requestAnimationFrame(this.animate);
};

module.exports = ExoViz;
<|endoftext|>


<commit_before>
var process = {};

var logger = require('Logger');
process.stdout = { write: logger.log };

process.platform = 'GoogleAppsScript';
process.pid = 0;

process.env = {};
process.env.NODE_DEBUG = false;

module.exports = process;
<commit_msg>Wrap Logger.log in function for convert as process.stdout.write.<commit_after>
var process = {};

var logger = require('Logger');
process.stdout = { write: function (data) {
    logger.log(data);
    }
};

process.platform = 'GoogleAppsScript';
process.pid = 0;

process.env = {};
process.env.NODE_DEBUG = false;

Error.captureStackTrace = function (THIS, stackStartFunction) {
    console.log('Error.captureStackTrace called');
};

module.exports = process;
<|endoftext|>


<commit_before>
var socket;
start();

function start() {
	socket = io.connect();
	socket.on('connect', (data) => {

	});

	socket.on('reconnect', (data) => {
		var name = $('#currentStatus').text('');
		if (name && name.length > 0) {
			login();
		}
	});

	socket.on('disconnect', (data) => {
		$('#currentStatus').text('Disconnected from server; Trying to reconnect');
	});

	//handling video stream

	socket.on("videoStream", function (data) {
	 $("#videoStream").attr('src','data:image/jpeg;base64, ' + data);
	});

}

function login() {
	var name = $('#userName').val();
	console.log(` >>> web client >>> ${name}`);

	//login
	socket.emit('login', {
		'connectedType': 'controller',
		'name': name
	}, (data) => {
		console.log(' >>> web client >>> login >>> received login back');
		$('#currentStatus').text('Connected!');
	});
};

//function for moving the robot
function move(direction) {
	console.log(` >>> web client >>> move ${direction}`);
	socket.emit('moveRobot',
		{ 'direction': direction },
		(data) => {
			console.log(` >>> web client >>> response move robot ${data}`);
		});
}

function startStream() {

	socket.emit('startClientVideoStream', null, (data) => {
	console.log(' >>> web client >>> start video stream');
	});
}

function stopStream() {
	console.log(' >>> web client >>> stop video stream');
	socket.emit('stopClientVideoStream', null, (data) => {
	console.log(' >>> web client >>> stop video stream');
	});
}<commit_msg>added timeout for direction buttons<commit_after>
var socket;
var directionActive = [];
var directionTimeout = 1000;

start();

function start() {
	for (var i = 0; i < 4; i++) {
		directionActive[0] = false;
	}
	socket = io.connect();
	socket.on('connect', (data) => {
		console.log('connected!');
	});

	socket.on('reconnect', (data) => {
		var name = $('#currentStatus').text('');
		if (name && name.length > 0) {
			login();
		}
	});

	socket.on('disconnect', (data) => {
		$('#currentStatus').text('Disconnected from server; Trying to reconnect');
	});

	//handling video stream

	socket.on("videoStream", function (data) {
		$("#videoStream").attr('src', 'data:image/jpeg;base64, ' + data);
	});

}

function login() {
	var name = $('#userName').val();
	console.log(` >>> web client >>> ${name}`);

	//login
	socket.emit('login', {
		'connectedType': 'controller',
		'name': name
	}, (data) => {
		console.log(' >>> web client >>> login >>> received login back');
		$('#currentStatus').text('Connected!');
	});
};

//function for moving the robot
function move(direction) {

	var dir;
	switch (direction) {
		case 'left':
			dir = 0;
			break;
		case 'right':
			dir = 1;
			break;
		case 'up':
			dir = 2;
			break;
		case 'down':
			dir = 3;
			break;
	}

	if (directionActive[dir] === true) {
		return;
	} else {
		directionActive[dir] = true;
		setTimeout(() => {
			directionActive[dir] = false;
		}, directionTimeout);
	}

	console.log(` >>> web client >>> move ${direction}`);
	socket.emit('moveRobot',
		{ 'direction': direction },
		(data) => {
			console.log(` >>> web client >>> response move robot ${data}`);
		});
}

function startStream() {

	socket.emit('startClientVideoStream', null, (data) => {
		console.log(' >>> web client >>> start video stream');
	});
}

function stopStream() {
	console.log(' >>> web client >>> stop video stream');
	socket.emit('stopClientVideoStream', null, (data) => {
		console.log(' >>> web client >>> stop video stream');
	});
}<|endoftext|>


<commit_before>/*
 * debugger.io: An interactive web scripting sandbox
 *
 * popup.js: template-able popup windows
 */

define(function(require) {
  'use strict';

  var $      = require('jquery');
  var _      = require('underscore');
  var bus    = require('bus');
  var config = require('config');
  var utils  = require('utils');

  var cdn       = require('cdn');
  var dom       = require('dom');
  var flash     = require('flash');
  var keys      = require('keys');
  var locales   = require('locales');
  var panels    = require('panels');
  var templates = require('templates');
  var themes    = require('themes');

  // ---

  var popups = utils.module('popups');
  var popupEl = '#popup';
  var popupKeyHander;
  var currentView = null;

  /**
   * Base popup Model
   *
   * @param {Boolean} small - mini style popup if true, full-sized if false
   * @param {String} title - Popup heading
   */
  popups.Popup = Backbone.Model.extend({
    defaults: { small: false, title: null },

    // array of promises to resolve before model is ready
    pre_ready: [],

    /**
     * Add remote data to model
     *
     * @param {Object} opts - { key: uri }, add data from uri to key on model
     */
    remote_data: function(opts) {
      var that = this;

      var opts_p = _.map(opts, function(uri, key) {
        return $.get(uri).then(function(data) {
          return that.set(key, data);
        }, function(err) {
          flash.message_bad('@server_error', '@server_error_msg');
        });
      });

      this.pre_ready.push($.when.apply(null, opts_p));
    }
  });

  /**
   * Base popup View
   */
  popups.PopupView = Backbone.View.extend({
    el: popupEl,

    initialize: function(options) {
      var that = this;
      _.extend(this, _.pick(options, 'template', 'post_render'));
      $.when.apply(null, this.model.pre_ready)
        .done(this.render.bind(this))
        .fail(this.destroy.bind(this));

      popupKeyHander =
        keys.register_handler({ key: 'esc' }, function(e) { that.destroy(); });
    },

    events: {
      'submit form': function(e) {
        e.preventDefault();
        this.trigger('submit');
      },

      'mousedown': function(e) {
        // only destroy the popup if the background area is clicked
        this.bg_mousedown = $(e.target).is(popupEl);
      },

      'mouseup': function(e) {
        if ($(e.target).is(popupEl) && this.bg_mousedown) { this.destroy(); }
      },

      // destroy a popup when the cancel button is pressed
      'click .popup-cancel': function(e) { e.preventDefault(); this.destroy(); }
    },

    destroy: function() {
      var d = $.Deferred();

      var that = this;
      keys.unregister_handler(popupKeyHander);

      this.$el.removeData('type');

      popups.hide().always(function() {
        dom.destroy_view(that);
        currentView = null;
        d.resolve(true);
      });

      return d.promise();
    },

    render: function() {
      currentView = this;

      var that = this;
      var data = this.model.toJSON();

      var popup_p = templates.get('popup', this);
      var content_p = templates.get(this.template, this);
      var title_p = locales.string(data.title);

      return $.when(popup_p, content_p, title_p)
      .then(function(popup_fn, content_fn, title) {
        var that = _.first(utils.ensure_array(this));

        var contentHtml = content_fn({
          data: data,
          config: _.clone(config)
        });

        var popupHtml = popup_fn({
          small: !!data.small,
          title: title,
          content: contentHtml,
          name: _.str.sprintf('%s-outer', that.template)
        });

        // remove any existing popups first
        popups.hide().done(function() {
          that.$el.html(popupHtml);
          that.$el.data('type', that.template);

          that.$el.find('select').chosen({
            disable_search: true,
            inherit_select_classes: true
          });

          popups.show().done(function() {
            if (_.isFunction(that.post_transition)) { that.post_transition(); }
          });

          that.$el.find('input:not([type=hidden])').first().focus();

          if (_.isFunction(that.post_render)) { that.post_render(); }
        });

        return that.trigger('render');

      }).fail(function(err) {
        var that = _.first(utils.ensure_array(this));
        var msg = _.str.sprintf('Error rendering "%s": %s', that.template, err);
        popups.console.error(msg);
      });
    }
  });

  /**
   * Login popup
   */
  popups.LoginPopup = popups.Popup.extend({
    defaults: { small: true, title: 'login' }
  });

  popups.LoginPopupView = popups.PopupView.extend({
    template: 'popup-login',

    initialize: function(options) {
      this.events = _.extend({}, this.events, this._events);
      this.constructor.__super__.initialize.apply(this, arguments);
    },

    _events: {
      'submit #login_form': function(e) {
        var that = this;

        utils.submit_form($(e.target)).done(function(username) {
          bus.trigger('user:login', username);
          that.destroy();
        }).fail(function() {
          that.show_invalid_login(); // invalid credentials
        });
      }
    },

    post_render: function() {
      dom.cache(this, this.$el, { 'by_name': ['username', 'password'] });
    },

    show_invalid_login: function() {
      this.$el.find('input[name="password"]').select();
      flash.message_bad('@invalid_creds', '@invalid_creds_msg');
    }
  });

  /**
   * Sign Up popup
   */
  popups.SignupPopup = popups.Popup.extend({
    defaults: { small: true, title: 'create_account' }
  });

  popups.SignupPopupView = popups.PopupView.extend({
    template: 'popup-signup',

    initialize: function(options) {
      this.events = _.extend({}, this.events, this._events);
      this.constructor.__super__.initialize.apply(this, arguments);
    },

    _events: {
      'submit #signup_form': function(e) {
        var that = this;

        if (this.$username.val().length < 3) {
          return flash.message_bad('@invalid_username', '@invalid_username_msg');
        }

        if (this.$email.is(':invalid') || !this.$email.val()) {
          return flash.message_bad('@invalid_email');
        }

        if (this.$password.val() !== this.$confirm.val()) {
          return flash.message_bad('@password_mismatch');
        }

        if (this.$password.val().length < 8) {
          return flash.message_bad('@invalid_password', '@invalid_password_msg');
        }

        utils.submit_form($(e.target)).done(function(username) {
          bus.trigger('user:login', username);
          that.destroy();
        }).fail(function(xhr) {
          flash.locale_message_bad(xhr.responseJSON);
       });
      }
    },

    post_render: function() {
      dom.cache(this, this.$el, {
        'by_name': ['username', 'email', 'password', 'confirm']
      });
    }
  });

  /**
   * User settings popup
   */
  popups.UserSettingsPopup = popups.Popup.extend({
    defaults: { small: true, title: 'user_settings' },

    initialize: function() {
      var that = this;

      this.remote_data({
        'locales': '/api/resource/locales'
      });

      var themes_p = locales.strings(themes.get_themes())
      .then(function(themes) {
        return that.set('themes', themes);
      });

      var layouts_p = locales.strings(panels.get_layouts())
      .then(function(layouts) {
        return that.set('layouts', layouts);
      });

      this.pre_ready.push(themes_p);
      this.pre_ready.push(layouts_p);

      this.set('cdns', cdn.get_cdns());
    }
  });

  popups.UserSettingsPopupView = popups.PopupView.extend({
    template: 'popup-user-settings',

    initialize: function(options) {
      var that = this;

      this.events = _.extend({}, this.events, this._events);
      this.constructor.__super__.initialize.apply(this, arguments);
    },

    _events: {
      'change select[name="locale"]': function(e) {
        config.locale = $(e.target).val();
      },

      'change select[name="theme"]': function(e) {
        config.theme = $(e.target).val();
      },

      'change select[name="layout"]': function(e) {
        config.layout = $(e.target).val();
      },

      'change select[name="cdn"]': function(e) {
        config.cdn = $(e.target).val();
      },

      'submit #user_settings_form': function(e) {
        this.destroy();
      }
    },

    post_render: function() {
      dom.cache(this, this.$el, {
        'by_name': ['locales',]
      });
    }
  });

  /**
   * User prompt
   */
  popups.InputPopup = popups.Popup.extend({
    defaults: { small: true, title: 'input' }
  });

  popups.InputPopupView = popups.PopupView.extend({
    template: 'popup-input',

    initialize: function(options) {
      var that = this;

      this.events = _.extend({}, this.events, this._events);
      this.extras = utils.ensure_array(this.model.get('extras'));

      var placeholder_p = _.map(this.extras, function(extra) {
        return locales.string(extra.placeholder).then(function(placeholder) {
          extra.placeholder = placeholder;
        });
      });

      $.when.apply(null, placeholder_p).done(function() {
        that.constructor.__super__.initialize.apply(that, arguments);
      });
    },

    _events: {
      'submit #input_form': function(e) { this.destroy(); }
    },

    post_render: function() {
      var that = this;

      _.each(this.extras, function(extra) {

        var $input = that.$el.find(_.str.sprintf("input[name='%s']", extra.name));

        $input.on('input input-filter', function(e, no_copy) {
          var $this = $(this);
          var filtering = (e.type === 'input-filter');

          // mark manually updated inputs
          if ($this.is(':focus')) { $this.data('touched', true); }

          if (_.isFunction(extra.filter)) {
            var start = this.selectionStart, end = this.selectionEnd;

            var val = extra.filter($this.val());
            $this.val(val);

            if (!filtering) { this.setSelectionRange(start, end); }
          }

          if (extra.copy_to) {
            var $dest = that.$el.find(_.str.sprintf("input[name='%s']", extra.copy_to));
            if (!$dest.val()) { $dest.data('touched', false); }
            if (!$dest.data('touched') && (!no_copy || !$dest.val())) {
              $dest.val($input.val()).trigger('input-filter');
            }
          }
        }).trigger('input-filter', true); // filter existing values right away
      });
    }
  });

  /**
   * Build a popup and show it right away
   *
   * @param {String} name - name of the popup template to use
   * @param {String} title - locale key to use as popup title
   * @param {Object} [extras] - optional data to pass to template
   * @return {Promise} to resolve to a map of the submitted form
   */
  popups.popup = function(name, title, extras) {
    var d = $.Deferred();

    var modelName = _.str.sprintf('%sPopup', _.str.capitalize(_.str.camelize(name)));
    var viewName = _.str.sprintf('%sView', modelName);

    var ModelConstructor = popups[modelName];
    var ViewConstructor = popups[viewName];

    if (!ModelConstructor || !ViewConstructor) {
      popups.console.error('popups.%s / popups.%s do not exist', modelName, viewName);
      return utils.reject();
    }

    var model = new ModelConstructor();
    if (title) { model.set('title', title); }
    model.set('extras', extras);

    var view = new ViewConstructor({ model: model });

    view.on('destroy', d.reject);

    view.on('submit', function() {
      var $form = view.$el.find('form');

      var map = _.chain($form.serialize().split('&')).map(function(token) {
        return _.map(token.split('='), function(str) {
          return decodeURIComponent(str.replace(/\+/g, ' '));
        });
      }).object().value();

      _.each(_.keys(map), function(prop) {
        if (_.first(prop) === '_') { delete map[prop]; }
      });

      d.resolve(map);
    });

    return d.promise();
  };

  /**
   * Show the currently assigned popup
   *
   * @return {Promise} resolves after showing, or rejects on failure
   */
  popups.show = function() {
    var $popup = $(popupEl);
    if (!$popup.length || $popup.is(':empty')) {
      return utils.reject();
    }

    $popup.removeClass('nopointer');
    $popup.css({ 'display': 'block' });

    var $inner = $popup.find('.popup');
    var $wrap = $popup.find('.popup-wrap');
    var $bar = $popup.find('.popup-actionbar');
    var offset = $bar.offset().top - ($wrap.offset().top + $wrap.outerHeight());
    $wrap.css('margin-bottom', _.str.sprintf('-%spx', offset));

    return dom.multi_transition([
      { el: $popup, args: { 'opacity': 1 } },
      { el: $inner, args: { 'margin-top': '1em' } }
    ]);
  };

  /**
   * Hide the currently visible popup
   *
   * @return {Promise} resolves after hiding, or rejects on failure
   */
  popups.hide = function() {
    var d = $.Deferred();

    var $popup = $(popupEl);
    if (!$popup.length) {
      return utils.reject();
    }

    // popup is already hidden, don't wait to resolve
    if ($popup.css('opacity') === '0') {
      return utils.resolve();
    }

    $popup.addClass('nopointer');

    var $inner = $popup.find('.popup');

    return dom.multi_transition([
      { el: $popup, args: { 'opacity': 0 } },
      { el: $inner, args: { 'margin-top': 0 } }
    ]).then(function() {
      $popup.css({ 'display': 'none' });
    });
  };

  /**
   * Destroy the currently visible popup(s)
   *
   * @param {String} [type]
   * @return {Promise}
   */
  popups.destroy = function(type) {
    if (!(currentView instanceof popups.PopupView)) {
      return utils.resolve(true);
    }

    if (!type || (type === currentView.$el.data('type'))) {
      return currentView.destroy();
    }
  };

  /**
   * User prompt using input popup
   *
   * @param {String} title - locale key to use as popup title
   * @param {Object|Array} - inputs, or an array of inputs
   * @return {Promise}
   */
  popups.prompt = function(title, inputs) {
    return popups.popup('input', title, utils.ensure_array(inputs));
  };

  popups.type = function() {
    return $(popupEl).data('type');
  };

  return popups;
});
<commit_msg>Fix popup form submission<commit_after>/*
 * debugger.io: An interactive web scripting sandbox
 *
 * popup.js: template-able popup windows
 */

define(function(require) {
  'use strict';

  var $      = require('jquery');
  var _      = require('underscore');
  var bus    = require('bus');
  var config = require('config');
  var utils  = require('utils');

  var cdn       = require('cdn');
  var dom       = require('dom');
  var flash     = require('flash');
  var keys      = require('keys');
  var locales   = require('locales');
  var panels    = require('panels');
  var templates = require('templates');
  var themes    = require('themes');

  // ---

  var popups = utils.module('popups');
  var popupEl = '#popup';
  var popupKeyHander;
  var currentView = null;

  /**
   * Base popup Model
   *
   * @param {Boolean} small - mini style popup if true, full-sized if false
   * @param {String} title - Popup heading
   */
  popups.Popup = Backbone.Model.extend({
    defaults: { small: false, title: null },

    // array of promises to resolve before model is ready
    pre_ready: [],

    /**
     * Add remote data to model
     *
     * @param {Object} opts - { key: uri }, add data from uri to key on model
     */
    remote_data: function(opts) {
      var that = this;

      var opts_p = _.map(opts, function(uri, key) {
        return $.get(uri).then(function(data) {
          return that.set(key, data);
        }, function(err) {
          flash.message_bad('@server_error', '@server_error_msg');
        });
      });

      this.pre_ready.push($.when.apply(null, opts_p));
    }
  });

  /**
   * Base popup View
   */
  popups.PopupView = Backbone.View.extend({
    el: popupEl,

    initialize: function(options) {
      var that = this;
      _.extend(this, _.pick(options, 'template', 'post_render'));
      $.when.apply(null, this.model.pre_ready)
        .done(this.render.bind(this))
        .fail(this.destroy.bind(this));

      popupKeyHander =
        keys.register_handler({ key: 'esc' }, function(e) { that.destroy(); });
    },

    events: {
      'submit form': function(e) {
        e.preventDefault();
        this.trigger('submit', e.target);
      },

      'mousedown': function(e) {
        // only destroy the popup if the background area is clicked
        this.bg_mousedown = $(e.target).is(popupEl);
      },

      'mouseup': function(e) {
        if ($(e.target).is(popupEl) && this.bg_mousedown) { this.destroy(); }
      },

      // destroy a popup when the cancel button is pressed
      'click .popup-cancel': function(e) {
        e.preventDefault();
        this.destroy();
      }
    },

    destroy: function() {
      var d = $.Deferred();

      var that = this;
      keys.unregister_handler(popupKeyHander);

      this.$el.removeData('type');

      popups.hide().always(function() {
        dom.destroy_view(that);
        currentView = null;
        d.resolve(true);
      });

      return d.promise();
    },

    render: function() {
      currentView = this;

      var that = this;
      var data = this.model.toJSON();

      var popup_p = templates.get('popup', this);
      var content_p = templates.get(this.template, this);
      var title_p = locales.string(data.title);

      return $.when(popup_p, content_p, title_p)
      .then(function(popup_fn, content_fn, title) {
        var that = _.first(utils.ensure_array(this));

        var contentHtml = content_fn({
          data: data,
          config: _.clone(config)
        });

        var popupHtml = popup_fn({
          small: !!data.small,
          title: title,
          content: contentHtml,
          name: _.str.sprintf('%s-outer', that.template)
        });

        // remove any existing popups first
        popups.hide().done(function() {
          that.$el.html(popupHtml);
          that.$el.data('type', that.template);

          that.$el.find('select').chosen({
            disable_search: true,
            inherit_select_classes: true
          });

          popups.show().done(function() {
            if (_.isFunction(that.post_transition)) { that.post_transition(); }
          });

          that.$el.find('input:not([type=hidden])').first().focus();

          if (_.isFunction(that.post_render)) { that.post_render(); }
        });

        return that.trigger('render');

      }).fail(function(err) {
        var that = _.first(utils.ensure_array(this));
        var msg = _.str.sprintf('Error rendering "%s": %s', that.template, err);
        popups.console.error(msg);
      });
    }
  });

  /**
   * Login popup
   */
  popups.LoginPopup = popups.Popup.extend({
    defaults: { small: true, title: 'login' }
  });

  popups.LoginPopupView = popups.PopupView.extend({
    template: 'popup-login',

    initialize: function(options) {
      this.events = _.extend({}, this.events, this._events);
      this.constructor.__super__.initialize.apply(this, arguments);

      this.on('submit', function(form) {
        var that = this;
        var $form = $(form);

        utils.submit_form($form).done(function(username) {
          bus.trigger('user:login', username);
          that.destroy();
        }).fail(function() {
          that.show_invalid_login(); // invalid credentials
        });
      });
    },

    post_render: function() {
      dom.cache(this, this.$el, { 'by_name': ['username', 'password'] });
    },

    show_invalid_login: function() {
      this.$el.find('input[name="password"]').select();
      flash.message_bad('@invalid_creds', '@invalid_creds_msg');
    }
  });

  /**
   * Sign Up popup
   */
  popups.SignupPopup = popups.Popup.extend({
    defaults: { small: true, title: 'create_account' }
  });

  popups.SignupPopupView = popups.PopupView.extend({
    template: 'popup-signup',

    initialize: function(options) {
      this.events = _.extend({}, this.events, this._events);
      this.constructor.__super__.initialize.apply(this, arguments);

      this.on('submit', function(form) {
        var that = this;
        var $form = $(form);
        console.log('signup');

        if (this.$username.val().length < 3) {
          return flash.message_bad('@invalid_username', '@invalid_username_msg');
        }

        if (this.$email.is(':invalid') || !this.$email.val()) {
          return flash.message_bad('@invalid_email');
        }

        if (this.$password.val() !== this.$confirm.val()) {
          return flash.message_bad('@password_mismatch');
        }

        if (this.$password.val().length < 8) {
          return flash.message_bad('@invalid_password', '@invalid_password_msg');
        }

        utils.submit_form($form).done(function(username) {
          bus.trigger('user:login', username);
          that.destroy();
        }).fail(function(xhr) {
          flash.locale_message_bad(xhr.responseJSON);
       });
      });
    },

    post_render: function() {
      dom.cache(this, this.$el, {
        'by_name': ['username', 'email', 'password', 'confirm']
      });
    }
  });

  /**
   * User settings popup
   */
  popups.UserSettingsPopup = popups.Popup.extend({
    defaults: { small: true, title: 'user_settings' },

    initialize: function() {
      var that = this;

      this.remote_data({
        'locales': '/api/resource/locales'
      });

      var themes_p = locales.strings(themes.get_themes())
      .then(function(themes) {
        return that.set('themes', themes);
      });

      var layouts_p = locales.strings(panels.get_layouts())
      .then(function(layouts) {
        return that.set('layouts', layouts);
      });

      this.pre_ready.push(themes_p);
      this.pre_ready.push(layouts_p);

      this.set('cdns', cdn.get_cdns());
    }
  });

  popups.UserSettingsPopupView = popups.PopupView.extend({
    template: 'popup-user-settings',

    initialize: function(options) {
      var that = this;

      this.events = _.extend({}, this.events, this._events);
      this.constructor.__super__.initialize.apply(this, arguments);

      this.on('submit', function(form) {
        this.destroy();
      });
    },

    _events: {
      'change select[name="locale"]': function(e) {
        config.locale = $(e.target).val();
      },

      'change select[name="theme"]': function(e) {
        config.theme = $(e.target).val();
      },

      'change select[name="layout"]': function(e) {
        config.layout = $(e.target).val();
      },

      'change select[name="cdn"]': function(e) {
        config.cdn = $(e.target).val();
      }
    },

    post_render: function() {
      dom.cache(this, this.$el, {
        'by_name': ['locales',]
      });
    }
  });

  /**
   * User prompt
   */
  popups.InputPopup = popups.Popup.extend({
    defaults: { small: true, title: 'input' }
  });

  popups.InputPopupView = popups.PopupView.extend({
    template: 'popup-input',

    initialize: function(options) {
      var that = this;

      this.events = _.extend({}, this.events, this._events);
      this.extras = utils.ensure_array(this.model.get('extras'));

      var placeholder_p = _.map(this.extras, function(extra) {
        return locales.string(extra.placeholder).then(function(placeholder) {
          extra.placeholder = placeholder;
        });
      });

      $.when.apply(null, placeholder_p).done(function() {
        that.constructor.__super__.initialize.apply(that, arguments);
      });

      this.on('submit', function(form) {
        this.destroy();
      });
    },

    post_render: function() {
      var that = this;

      _.each(this.extras, function(extra) {

        var $input = that.$el.find(_.str.sprintf("input[name='%s']", extra.name));

        $input.on('input input-filter', function(e, no_copy) {
          var $this = $(this);
          var filtering = (e.type === 'input-filter');

          // mark manually updated inputs
          if ($this.is(':focus')) { $this.data('touched', true); }

          if (_.isFunction(extra.filter)) {
            var start = this.selectionStart, end = this.selectionEnd;

            var val = extra.filter($this.val());
            $this.val(val);

            if (!filtering) { this.setSelectionRange(start, end); }
          }

          if (extra.copy_to) {
            var $dest = that.$el.find(_.str.sprintf("input[name='%s']", extra.copy_to));
            if (!$dest.val()) { $dest.data('touched', false); }
            if (!$dest.data('touched') && (!no_copy || !$dest.val())) {
              $dest.val($input.val()).trigger('input-filter');
            }
          }
        }).trigger('input-filter', true); // filter existing values right away
      });
    }
  });

  /**
   * Build a popup and show it right away
   *
   * @param {String} name - name of the popup template to use
   * @param {String} title - locale key to use as popup title
   * @param {Object} [extras] - optional data to pass to template
   * @return {Promise} to resolve to a map of the submitted form
   */
  popups.popup = function(name, title, extras) {
    var d = $.Deferred();

    var modelName = _.str.sprintf('%sPopup', _.str.capitalize(_.str.camelize(name)));
    var viewName = _.str.sprintf('%sView', modelName);

    var ModelConstructor = popups[modelName];
    var ViewConstructor = popups[viewName];

    if (!ModelConstructor || !ViewConstructor) {
      popups.console.error('popups.%s / popups.%s do not exist', modelName, viewName);
      return utils.reject();
    }

    var model = new ModelConstructor();
    if (title) { model.set('title', title); }
    model.set('extras', extras);

    var view = new ViewConstructor({ model: model });

    view.on('destroy', d.reject);

    view.on('submit', function(form) {
      var $form = $(form);

      var map = _.chain($form.serialize().split('&')).map(function(token) {
        return _.map(token.split('='), function(str) {
          return decodeURIComponent(str.replace(/\+/g, ' '));
        });
      }).object().value();

      _.each(_.keys(map), function(prop) {
        if (_.first(prop) === '_') { delete map[prop]; }
      });

      d.resolve(map);
    });

    return d.promise();
  };

  /**
   * Show the currently assigned popup
   *
   * @return {Promise} resolves after showing, or rejects on failure
   */
  popups.show = function() {
    var $popup = $(popupEl);
    if (!$popup.length || $popup.is(':empty')) {
      return utils.reject();
    }

    $popup.removeClass('nopointer');
    $popup.css({ 'display': 'block' });

    var $inner = $popup.find('.popup');
    var $wrap = $popup.find('.popup-wrap');
    var $bar = $popup.find('.popup-actionbar');
    var offset = $bar.offset().top - ($wrap.offset().top + $wrap.outerHeight());
    $wrap.css('margin-bottom', _.str.sprintf('-%spx', offset));

    return dom.multi_transition([
      { el: $popup, args: { 'opacity': 1 } },
      { el: $inner, args: { 'margin-top': '1em' } }
    ]);
  };

  /**
   * Hide the currently visible popup
   *
   * @return {Promise} resolves after hiding, or rejects on failure
   */
  popups.hide = function() {
    var d = $.Deferred();

    var $popup = $(popupEl);
    if (!$popup.length) {
      return utils.reject();
    }

    // popup is already hidden, don't wait to resolve
    if ($popup.css('opacity') === '0') {
      return utils.resolve();
    }

    $popup.addClass('nopointer');

    var $inner = $popup.find('.popup');

    return dom.multi_transition([
      { el: $popup, args: { 'opacity': 0 } },
      { el: $inner, args: { 'margin-top': 0 } }
    ]).then(function() {
      $popup.css({ 'display': 'none' });
    });
  };

  /**
   * Destroy the currently visible popup(s)
   *
   * @param {String} [type]
   * @return {Promise}
   */
  popups.destroy = function(type) {
    if (!(currentView instanceof popups.PopupView)) {
      return utils.resolve(true);
    }

    if (!type || (type === currentView.$el.data('type'))) {
      return currentView.destroy();
    }
  };

  /**
   * User prompt using input popup
   *
   * @param {String} title - locale key to use as popup title
   * @param {Object|Array} - inputs, or an array of inputs
   * @return {Promise}
   */
  popups.prompt = function(title, inputs) {
    return popups.popup('input', title, utils.ensure_array(inputs));
  };

  popups.type = function() {
    return $(popupEl).data('type');
  };

  return popups;
});
<|endoftext|>