<commit_before>'use strict';

// TODO: Implement site-wide search functionality.

define([
    "rsvp",
    "foundation",
    "app/router",
    "app/honest/responsive_video",
    "angular",
    "angular-resource",
    "angular-animate",
    "app/controllers",
    "app/directives",
    "app/services",
    "app/filters",
    "fastclick",
    "app/honest/dropdown",
    "app/honest/answer_reveal",
    "angulartics",
    "angulartics-ga",
    ], function() {

    window.Promise = RSVP.Promise;
    window.Promise.defer = RSVP.defer;

	var rv = require("app/honest/responsive_video");

	// Declare app level module which depends on filters, and services
	angular.module('isaac', [
        'isaac.router',
		'isaac.filters',
		'isaac.services',
		'isaac.directives',
		'isaac.controllers',
        'angulartics',
        'angulartics.google.analytics',
        'ngAnimate',
	])

	.config(['$locationProvider', 'apiProvider', '$httpProvider', function($locationProvider, apiProvider, $httpProvider) {

        // Send session cookies with the API requests.
        $httpProvider.defaults.withCredentials = true;

        // Only use html5 mode if we are on a real server, which should respect .htaccess
		$locationProvider.html5Mode(document.location.hostname != "localhost").hashPrefix("!");

        // Here we configure the api provider with the server running the API. Don't need to do this if we want to use the same server as the static content.
        if (document.location.hostname == "localhost")
            apiProvider.server("https://dev.isaacphysics.org");

	}])

	.run(['$rootScope', 'api', '$state', function($rootScope, api, $state) {

        $rootScope.globalFlags = {
            siteSearchOpen: false,
            isLoading: false,
            noSearch: false
        };

        $rootScope.$state = $state;

        $rootScope.$on("$stateChangeStart", function() {
            $rootScope.globalFlags.isLoading = true;
        });

        $rootScope.$on("$stateChangeSuccess", function() {
            $rootScope.globalFlags.isLoading = false;
            $rootScope.globalFlags.displayLoadingMessage = false;
            // TODO: find a better way to hide the search
            $rootScope.globalFlags.noSearch = false;

            $(document).scrollTop(0);
        })

        $rootScope.$on("$stateChangeError", function() {
            $rootScope.globalFlags.isLoading = false;
        })

        $rootScope.$on("$includeContentLoaded", function() {

            // Make all videos responsive
            rv.updateAll();

            $(document).foundation({
                // Queries for retina images for data interchange
                interchange:
                {
                    named_queries :
                    {
                        small_retina :  'only screen and (min-width: 1px) and (-webkit-min-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 1px) and (min--moz-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 1px) and (-o-min-device-pixel-ratio: 2/1),'+
                                        'only screen and (min-width: 1px) and (min-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 1px) and (min-resolution: 192dpi),'+
                                        'only screen and (min-width: 1px) and (min-resolution: 2dppx)',
                        medium_retina : 'only screen and (min-width: 641px) and (-webkit-min-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 641px) and (min--moz-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 641px) and (-o-min-device-pixel-ratio: 2/1),'+
                                        'only screen and (min-width: 641px) and (min-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 641px) and (min-resolution: 192dpi),'+
                                        'only screen and (min-width: 641px) and (min-resolution: 2dppx)',
                        large_retina :  'only screen and (min-width: 1024px) and (-webkit-min-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 1024px) and (min--moz-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 1024px) and (-o-min-device-pixel-ratio: 2/1),'+
                                        'only screen and (min-width: 1024px) and (min-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 1024px) and (min-resolution: 192dpi),'+
                                        'only screen and (min-width: 1024px) and (min-resolution: 2dppx)',
                        medium_large :  'only screen and (min-width: 54.115em)'
                    }
                }
            });

            $(document).foundation('interchange', 'reflow');

            // Global jQuery
            $(function()
            {
                //$.cookie("test", 1);
                // Mobile detection based on presence of mobile header
                /**
                 * Are we on a mobile? (Safe on any browser without needing media queries via JS)
                 * @returns {Boolean}
                 */
                $.ru_IsMobile = function()
                {
                    return ($(".ru-mobile-header").css('display') !== 'none');
                };

                // Fix ups for iOS
                if((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)))
                {
                    $('.accordion.ru_accordion dd a.ru_accordion_titlebar .ru_accordion_title').addClass('iphone');
                    $('.ru-answer-orbit .ru-answer-orbit-content p').addClass('iphone');
                }

                // Fix up for Firefox scroll to
                if (navigator.userAgent.match(/firefox/i))
                {
                    $('html').css({overflow:'hidden', height:'100%'});
                    $('body').css({overflow:'auto', height:'100%'});
                }

                // Safari - accordion titles
                if (navigator.userAgent.search("Safari") >= 0 && navigator.userAgent.search("Chrome") < 0)
                {
                    $('.accordion.ru_accordion dd a.ru_accordion_titlebar .ru_accordion_title').addClass('safari');
                }

                // Fix up for custom check box 2nd label
                $('.ru-drop-big-label,.ru-drop-mid-label,span.ru-drop-check~label').each(function()
                {
                    var $drop = $(this).prev('.ru-drop-check');
                    var id = $('input', $drop).attr('id');
                    $(this).attr('for', id);
                }).css('user-select','none');

                // Set tab indexes for some things
                // Header nav
                $('.ru-desktop-nav-item').attr('tabindex', 0).bind('keydown', function(e)
                {
                    // Follow link for tab on top level nav
                    if(e.which === 13)
                    {
                        var $link = $('a', $(this));
                        if(!$link.hasClass('active'))
                        {
                            window.location.href = $link.attr('href');
                        }
                    }
                });
                $('.ru-desktop-nav-item .active').parent().attr('tabindex', null);
                // Footer social icons
                $("[class*='ru-social-icon-']").attr('tabindex',0).bind('keydown', function(e)
                {
                    // Follow link for tab on top level nav
                    if(e.which === 13)
                    {
                        window.location.href = $(this).attr('href');
                    }
                });
                // Custom tick boxes
                $('span.ru-drop-check').each(function()
                {
                    // Add tab index
                    var span = $(this);
                    span.attr('tabindex', 0);
                    // Blur span on click
                    $('input', span).click(function()
                    {
                        $(this).parent().blur();
                    });
                    // Enter on checkbox
                    span.bind('keyup', function(e)
                    {
                        if(e.which === 13)
                        {
                            $('input', span).click();
                        }
                    });
                });

                 // Fast click
                FastClick.attach(document.body);

                // Mobile login drop down
	            $("#mobile-login").off("click");
                $("#mobile-login").click(function(e)
                {
                    e.preventDefault();
                    $("#mobile-login-form").ruDropDownToggle(this);
                });

                // Mobile search drop down
	            $("#mobile-search").off("click");
                $("#mobile-search").click(function(e)
                {
                    e.preventDefault();
                    $("#mobile-search-form").ruDropDownToggle(this);
                });

                // Resize slider on tab change (copes with resize when slider tab not visible)
                var sliderResize = function()
                {
                    $(".bxslider").each(function()
                    {
                        var slider = $(this).data('slider');
                        slider.reloadSlider();
                        var orbit = $(this).closest('.ru-answer-orbit');
                        var item = parseInt($('.ru-answer-orbit-bullets>.active', orbit).attr('data-target'));
                        slider.goToSlide(item);
                    });
                    // Also - remove iphone override on p carousel text as it is not needed after a change of tab
    //                if((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)))
    //                {
    //                    $('.ru-answer-orbit .ru-answer-orbit-content p').removeClass('iphone');
    //                }
                };
                var cookie = {
                    create: function(name,value,days) {
                        if (days) {
                            var date = new Date();
                                date.setTime(date.getTime()+(days*24*60*60*1000));
                            var expires = "; expires="+date.toGMTString();
                        }
                        else var expires = "";
                        document.cookie = name+"="+value+expires+"; path=/";
                    },
                    read: function(name) {
                        var nameEQ = name + "=";
                        var ca = document.cookie.split(';');
                        for(var i=0;i < ca.length;i++) {
                            var c = ca[i];
                            while (c.charAt(0)==' ') c = c.substring(1,c.length);
                            if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
                        }
                        return null;
                    }
                }
                var x = cookie.read('cookiesAccepted');
                if (!x) {
                    $(".cookies-message").show();
                } else {
                    $(".cookies-message").remove();
                }
                $(document).on('close.cookies-accepted.fndtn.alert-box', function(event) {
                    cookie.create('cookiesAccepted',1,720);
                });

                // Force resize of vidoes on tab change and accordion change
                $(document).foundation(
                {
                    tab:{
                        callback : function (tab)
                        {
                            rv.forceResize();
                            sliderResize();
                            // Determine location of tabs content and then pause any child videos
                            var obj = $($('a', tab).attr('href')).parent();
                            rv.pauseVideos(obj);
                        }
                    }
                });

                // Toggle hide / show of share links
                $(".ru_share").click(function()
                {
                    if($(".ru_share_link").width() === 258)
                    {
                        $(".ru_share_link").animate({width:0}, {duration:400});
                    }
                    else
                    {
                        $(".ru_share_link").animate({width:260}, {duration:400});
                    }
                });

                // Image zoom
                $('.ru-expand div').click(function(e)
                {
                    e.preventDefault();

                    // Invoke browser full screen
                    function requestFullScreen(element)
                    {
                        // Supports most browsers and their versions.
                        var requested = false;
                        var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullScreen;

                        if (requestMethod)
                        {
                            // Native full screen.
                            requestMethod.call(element);
                            requested = true;
                        }
                        else if (typeof window.ActiveXObject !== "undefined")
                        {
                            // Older IE.
                            var wscript = new ActiveXObject("WScript.Shell");
                            if (wscript !== null)
                            {
                                wscript.SendKeys("{F11}");
                                requested = true;
                            }
                        }
                        return requested;
                    }

                    // Get target img element
                    var elem = $(this).parent().find('img');
                    var url = elem.attr('src');
                    // Mobile - follow link
                    if($.ru_IsMobile())
                    {
                        window.location.href = url;
                    }
                    // Desktop - full screen mode, else revert to opening link
                    else
                    {
                        if(!requestFullScreen(elem.get(0)))
                        {
                            window.location.href = url;
                        }
                    }
                });

            });

        });

	}]);

	/////////////////////////////////////
	// Bootstrap AngularJS
	/////////////////////////////////////

    if (document.location.hash == "#_=_") {

        // This is necessary because of Facebook's stupid hackery. See http://stackoverflow.com/a/7297873

        if (document.location.hostname == "localhost")
            document.location.hash = "#!";
        else
            document.location.hash = "";

    }

	var root = $("html");
	angular.bootstrap(root, ['isaac']);

});
<commit_msg>Cookie function commented.<commit_after>'use strict';

// TODO: Implement site-wide search functionality.

define([
    "rsvp",
    "foundation",
    "app/router",
    "app/honest/responsive_video",
    "angular",
    "angular-resource",
    "angular-animate",
    "app/controllers",
    "app/directives",
    "app/services",
    "app/filters",
    "fastclick",
    "app/honest/dropdown",
    "app/honest/answer_reveal",
    "angulartics",
    "angulartics-ga",
    ], function() {

    window.Promise = RSVP.Promise;
    window.Promise.defer = RSVP.defer;

	var rv = require("app/honest/responsive_video");

	// Declare app level module which depends on filters, and services
	angular.module('isaac', [
        'isaac.router',
		'isaac.filters',
		'isaac.services',
		'isaac.directives',
		'isaac.controllers',
        'angulartics',
        'angulartics.google.analytics',
        'ngAnimate',
	])

	.config(['$locationProvider', 'apiProvider', '$httpProvider', function($locationProvider, apiProvider, $httpProvider) {

        // Send session cookies with the API requests.
        $httpProvider.defaults.withCredentials = true;

        // Only use html5 mode if we are on a real server, which should respect .htaccess
		$locationProvider.html5Mode(document.location.hostname != "localhost").hashPrefix("!");

        // Here we configure the api provider with the server running the API. Don't need to do this if we want to use the same server as the static content.
        if (document.location.hostname == "localhost")
            apiProvider.server("https://dev.isaacphysics.org");

	}])

	.run(['$rootScope', 'api', '$state', function($rootScope, api, $state) {

        $rootScope.globalFlags = {
            siteSearchOpen: false,
            isLoading: false,
            noSearch: false
        };

        $rootScope.$state = $state;

        $rootScope.$on("$stateChangeStart", function() {
            $rootScope.globalFlags.isLoading = true;
        });

        $rootScope.$on("$stateChangeSuccess", function() {
            $rootScope.globalFlags.isLoading = false;
            $rootScope.globalFlags.displayLoadingMessage = false;
            // TODO: find a better way to hide the search
            $rootScope.globalFlags.noSearch = false;

            $(document).scrollTop(0);
        })

        $rootScope.$on("$stateChangeError", function() {
            $rootScope.globalFlags.isLoading = false;
        })

        $rootScope.$on("$includeContentLoaded", function() {

            // Make all videos responsive
            rv.updateAll();

            $(document).foundation({
                // Queries for retina images for data interchange
                interchange:
                {
                    named_queries :
                    {
                        small_retina :  'only screen and (min-width: 1px) and (-webkit-min-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 1px) and (min--moz-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 1px) and (-o-min-device-pixel-ratio: 2/1),'+
                                        'only screen and (min-width: 1px) and (min-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 1px) and (min-resolution: 192dpi),'+
                                        'only screen and (min-width: 1px) and (min-resolution: 2dppx)',
                        medium_retina : 'only screen and (min-width: 641px) and (-webkit-min-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 641px) and (min--moz-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 641px) and (-o-min-device-pixel-ratio: 2/1),'+
                                        'only screen and (min-width: 641px) and (min-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 641px) and (min-resolution: 192dpi),'+
                                        'only screen and (min-width: 641px) and (min-resolution: 2dppx)',
                        large_retina :  'only screen and (min-width: 1024px) and (-webkit-min-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 1024px) and (min--moz-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 1024px) and (-o-min-device-pixel-ratio: 2/1),'+
                                        'only screen and (min-width: 1024px) and (min-device-pixel-ratio: 2),'+
                                        'only screen and (min-width: 1024px) and (min-resolution: 192dpi),'+
                                        'only screen and (min-width: 1024px) and (min-resolution: 2dppx)',
                        medium_large :  'only screen and (min-width: 54.115em)'
                    }
                }
            });

            $(document).foundation('interchange', 'reflow');

            // Global jQuery
            $(function()
            {
                //$.cookie("test", 1);
                // Mobile detection based on presence of mobile header
                /**
                 * Are we on a mobile? (Safe on any browser without needing media queries via JS)
                 * @returns {Boolean}
                 */
                $.ru_IsMobile = function()
                {
                    return ($(".ru-mobile-header").css('display') !== 'none');
                };

                // Fix ups for iOS
                if((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)))
                {
                    $('.accordion.ru_accordion dd a.ru_accordion_titlebar .ru_accordion_title').addClass('iphone');
                    $('.ru-answer-orbit .ru-answer-orbit-content p').addClass('iphone');
                }

                // Fix up for Firefox scroll to
                if (navigator.userAgent.match(/firefox/i))
                {
                    $('html').css({overflow:'hidden', height:'100%'});
                    $('body').css({overflow:'auto', height:'100%'});
                }

                // Safari - accordion titles
                if (navigator.userAgent.search("Safari") >= 0 && navigator.userAgent.search("Chrome") < 0)
                {
                    $('.accordion.ru_accordion dd a.ru_accordion_titlebar .ru_accordion_title').addClass('safari');
                }

                // Fix up for custom check box 2nd label
                $('.ru-drop-big-label,.ru-drop-mid-label,span.ru-drop-check~label').each(function()
                {
                    var $drop = $(this).prev('.ru-drop-check');
                    var id = $('input', $drop).attr('id');
                    $(this).attr('for', id);
                }).css('user-select','none');

                // Set tab indexes for some things
                // Header nav
                $('.ru-desktop-nav-item').attr('tabindex', 0).bind('keydown', function(e)
                {
                    // Follow link for tab on top level nav
                    if(e.which === 13)
                    {
                        var $link = $('a', $(this));
                        if(!$link.hasClass('active'))
                        {
                            window.location.href = $link.attr('href');
                        }
                    }
                });
                $('.ru-desktop-nav-item .active').parent().attr('tabindex', null);
                // Footer social icons
                $("[class*='ru-social-icon-']").attr('tabindex',0).bind('keydown', function(e)
                {
                    // Follow link for tab on top level nav
                    if(e.which === 13)
                    {
                        window.location.href = $(this).attr('href');
                    }
                });
                // Custom tick boxes
                $('span.ru-drop-check').each(function()
                {
                    // Add tab index
                    var span = $(this);
                    span.attr('tabindex', 0);
                    // Blur span on click
                    $('input', span).click(function()
                    {
                        $(this).parent().blur();
                    });
                    // Enter on checkbox
                    span.bind('keyup', function(e)
                    {
                        if(e.which === 13)
                        {
                            $('input', span).click();
                        }
                    });
                });

                 // Fast click
                FastClick.attach(document.body);

                // Mobile login drop down
	            $("#mobile-login").off("click");
                $("#mobile-login").click(function(e)
                {
                    e.preventDefault();
                    $("#mobile-login-form").ruDropDownToggle(this);
                });

                // Mobile search drop down
	            $("#mobile-search").off("click");
                $("#mobile-search").click(function(e)
                {
                    e.preventDefault();
                    $("#mobile-search-form").ruDropDownToggle(this);
                });

                // Resize slider on tab change (copes with resize when slider tab not visible)
                var sliderResize = function()
                {
                    $(".bxslider").each(function()
                    {
                        var slider = $(this).data('slider');
                        slider.reloadSlider();
                        var orbit = $(this).closest('.ru-answer-orbit');
                        var item = parseInt($('.ru-answer-orbit-bullets>.active', orbit).attr('data-target'));
                        slider.goToSlide(item);
                    });
                    // Also - remove iphone override on p carousel text as it is not needed after a change of tab
    //                if((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)))
    //                {
    //                    $('.ru-answer-orbit .ru-answer-orbit-content p').removeClass('iphone');
    //                }
                };
                var cookie = {
                    create: function(name, value, days) {
                        // Only do time calculation if a day has been passed in
                        if (days) {
                            var date = new Date();
                            var maxCookiesExpiry = days*24*60*60*1000;
                            // convert day to a Unix timestamp
                                date.setTime(date.getTime()+maxCookiesExpiry);
                            // formate date ready to be passed to the DOM
                            var expires = "; expires="+date.toGMTString();
                        }
                        else var expires = "";
                        // Build cookie and send to DOM
                        document.cookie = name+"="+value+expires+"; path=/";
                    },
                    read: function(name) {
                        var nameEQ = name + "=";
                        // Create array containing all cookies
                        var cookieArray = document.cookie.split(';');

                        // Loop through array of cookies checking each one
                        for(var i=0; i < cookieArray.length; i++) {
                            var cookie = cookieArray[i];

                            // Check to see first character is a space
                            while (cookie.charAt(0) == ' ') {
                                // Strip any subsequent spaces until the first character is not a space
                                cookie = cookie.substring(1, cookie.length);
                            }

                            if (cookie.indexOf(nameEQ) == 0) {
                                // Hurrah this is the cookie we wanted, now to return just the name
                                return cookie.substring(nameEQ.length,cookie.length);
                            }
                        }
                        return null;
                    }
                }

                if (!cookie.read('cookiesAccepted')) {
                    // If cookies haven't been accepted show cookie message
                    $(".cookies-message").show();
                } else {
                    // If cookies have been accepted remove the cookie message from the DOM
                    $(".cookies-message").remove();
                }

                // Set cookie on click without overriding Foundations close function
                $(document).on('close.cookies-accepted.fndtn.alert-box', function(event) {
                    cookie.create('cookiesAccepted',1,720);
                });

                // Force resize of vidoes on tab change and accordion change
                $(document).foundation(
                {
                    tab:{
                        callback : function (tab)
                        {
                            rv.forceResize();
                            sliderResize();
                            // Determine location of tabs content and then pause any child videos
                            var obj = $($('a', tab).attr('href')).parent();
                            rv.pauseVideos(obj);
                        }
                    }
                });

                // Toggle hide / show of share links
                $(".ru_share").click(function()
                {
                    if($(".ru_share_link").width() === 258)
                    {
                        $(".ru_share_link").animate({width:0}, {duration:400});
                    }
                    else
                    {
                        $(".ru_share_link").animate({width:260}, {duration:400});
                    }
                });

                // Image zoom
                $('.ru-expand div').click(function(e)
                {
                    e.preventDefault();

                    // Invoke browser full screen
                    function requestFullScreen(element)
                    {
                        // Supports most browsers and their versions.
                        var requested = false;
                        var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullScreen;

                        if (requestMethod)
                        {
                            // Native full screen.
                            requestMethod.call(element);
                            requested = true;
                        }
                        else if (typeof window.ActiveXObject !== "undefined")
                        {
                            // Older IE.
                            var wscript = new ActiveXObject("WScript.Shell");
                            if (wscript !== null)
                            {
                                wscript.SendKeys("{F11}");
                                requested = true;
                            }
                        }
                        return requested;
                    }

                    // Get target img element
                    var elem = $(this).parent().find('img');
                    var url = elem.attr('src');
                    // Mobile - follow link
                    if($.ru_IsMobile())
                    {
                        window.location.href = url;
                    }
                    // Desktop - full screen mode, else revert to opening link
                    else
                    {
                        if(!requestFullScreen(elem.get(0)))
                        {
                            window.location.href = url;
                        }
                    }
                });

            });

        });

	}]);

	/////////////////////////////////////
	// Bootstrap AngularJS
	/////////////////////////////////////

    if (document.location.hash == "#_=_") {

        // This is necessary because of Facebook's stupid hackery. See http://stackoverflow.com/a/7297873

        if (document.location.hostname == "localhost")
            document.location.hash = "#!";
        else
            document.location.hash = "";

    }

	var root = $("html");
	angular.bootstrap(root, ['isaac']);

});
<|endoftext|>


<commit_before>import { Service } from 'components/fxos-mvc/dist/mvc';

const USER_NAME = 'web';
const APP_NAME = 'foxbox-client';

export default class Hue extends Service {
  constructor(settings) {
    this.settings = settings;
    super();
  }

  connectToBridge() {
    return this.getBridgeAddress()
      .then(this.getUserId.bind(this));
  }

  /**
   * Use N-UPnP to discover the IP address of a Philips Hue bridge connected
   * to the local network.
   */
  getBridgeAddress() {
    return new Promise((resolve, reject) => {
      if (this.settings.bridgeAddress) {
        // @todo Try to ping it to see if it's still connected.
        return resolve(this.settings.bridgeAddress);
      }

      loadJSON('GET', 'https://www.meethue.com/api/nupnp')
        .then(response => {
          if (!response) {
            return reject(`Cannot reach the broker server. Make sure you're connected to the internet.`);
          }

          if (!response.length) {
            return reject('No bridge found. Please connect a bridge and try again.');
          }

          // @todo Manage the case where several bridges are connected.
          this.settings.bridgeAddress = response[0].internalipaddress;
          return resolve(this.settings.bridgeAddress);
        });
    });
  }

  getUserId() {
    let retriesNumber = 10;
    let messageShown = false;

    return new Promise((resolve, reject) => {
      if (this.settings.userId) {
        // @todo Check that this user ID is still working/authorised.
        return resolve(this.settings.userId);
      }

      let userInterval = setInterval(() => {
        loadJSON('POST', `http://${this.settings.bridgeAddress}/api`,
          `{"devicetype":"${APP_NAME}#${USER_NAME}"}`)
          .then(response => {
            console.log(response);

            if (response[0] && response[0].success) {
              this.settings.userId = response[0].success.username;
              clearInterval(userInterval);
              this._dispatchEvent('dismiss-message');
              return resolve(this.settings.userId);
            }

            if (response[0] && response[0].error.type === 101 && !messageShown) {
              this._dispatchEvent('message', {
                title: 'Please link',
                body: 'Please press the link button on your Philips bridge.'
              });
              messageShown = true;
            }

            if (retriesNumber === 0) {
              clearInterval(userInterval);
              this._dispatchEvent('message', {
                title: 'Error',
                body: 'The link button was not pressed on time.'
              });
              return reject('The link button was not pressed on time.');
            }

            retriesNumber--;
          });
      }, 1000);
    });
  }

  getLights() {
    return new Promise((resolve, reject) => {
      return loadJSON('GET', `http://${this.settings.bridgeAddress}/api/${this.settings.userId}`)
        .then(response => {
          if (!response.lights) {
            return reject('lights property is missing.');
          }

          resolve(toArray(response.lights));
        });
    });
  }

  changeLightState(id, states) {
    return new Promise((resolve, reject) => {
      return loadJSON('PUT', `http://${this.settings.bridgeAddress}/api/${this.settings.userId}/lights/${id}/state`,
        JSON.stringify(states))
        .then(response => {
          if (response[0] && response[0].error) {
            return reject(response[0].error.description);
          }

          resolve(response);
        });
    });
  }
}

function loadJSON(method, url, content = '') {
  return new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest();
    xhr.open(method, url, true);
    xhr.responseType = 'json';
    xhr.timeout = 3000;
    xhr.overrideMimeType('application/json');
    xhr.setRequestHeader('Accept', 'application/json,text/javascript,*/*;q=0.01');
    xhr.addEventListener('load', () => {
      if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
        resolve(xhr.response);
      } else {
        reject('Could not complete the operation.');
      }
    });
    xhr.addEventListener('error', reject);
    xhr.send(content);
  });
}

function toArray(object) {
  return Object.keys(object).map(k => object[k]);
}
<commit_msg>Minor editing to hue.js<commit_after>import { Service } from 'components/fxos-mvc/dist/mvc';

const USER_NAME = 'web';
const APP_NAME = 'foxbox-client';

let loadJSON = function(method, url, content = '') {
  return new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest();
    xhr.open(method, url, true);
    xhr.responseType = 'json';
    xhr.timeout = 3000;
    xhr.overrideMimeType('application/json');
    xhr.setRequestHeader('Accept', 'application/json,text/javascript,*/*;q=0.01');
    xhr.addEventListener('load', () => {
      if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
        resolve(xhr.response);
      } else {
        reject('Could not complete the operation.');
      }
    });
    xhr.addEventListener('error', reject);
    xhr.send(JSON.stringify(content));
  });
};

let toArray = function(object) {
  return Object.keys(object).map(k => object[k]);
};

export default class Hue extends Service {
  constructor(settings) {
    this.settings = settings;
    super();
  }

  connectToBridge() {
    return this.getBridgeAddress()
      .then(this.getUserId.bind(this));
  }

  /**
   * Use N-UPnP to discover the IP address of a Philips Hue bridge connected
   * to the local network.
   */
  getBridgeAddress() {
    return new Promise((resolve, reject) => {
      if (this.settings.bridgeAddress) {
        // @todo Try to ping it to see if it's still connected.
        return resolve(this.settings.bridgeAddress);
      }

      loadJSON('GET', 'https://www.meethue.com/api/nupnp')
        .then(response => {
          if (!response) {
            return reject(`Cannot reach the broker server. Make sure you're connected to the internet.`);
          }

          if (!response.length) {
            return reject('No bridge found. Please connect a bridge and try again.');
          }

          // @todo Manage the case where several bridges are connected.
          this.settings.bridgeAddress = response[0].internalipaddress;
          return resolve(this.settings.bridgeAddress);
        });
    });
  }

  getUserId() {
    let retriesNumber = 10;
    let messageShown = false;

    return new Promise((resolve, reject) => {
      if (this.settings.userId) {
        // @todo Check that this user ID is still working/authorised.
        return resolve(this.settings.userId);
      }

      let userInterval = setInterval(() => {
        loadJSON('POST', `http://${this.settings.bridgeAddress}/api`, {
          devicetype: `${APP_NAME}#${USER_NAME}`
        })
          .then(response => {
            console.log(response);

            if (response[0] && response[0].success) {
              this.settings.userId = response[0].success.username;
              clearInterval(userInterval);
              this._dispatchEvent('dismiss-message');
              return resolve(this.settings.userId);
            }

            if (response[0] && response[0].error.type === 101 && !messageShown) {
              this._dispatchEvent('message', {
                title: 'Please link',
                body: 'Please press the link button on your Philips bridge.'
              });
              messageShown = true;
            }

            if (retriesNumber === 0) {
              clearInterval(userInterval);
              this._dispatchEvent('message', {
                title: 'Error',
                body: 'The link button was not pressed on time.'
              });
              return reject('The link button was not pressed on time.');
            }

            retriesNumber--;
          });
      }, 1000);
    });
  }

  getLights() {
    return new Promise((resolve, reject) => {
      return loadJSON('GET', `http://${this.settings.bridgeAddress}/api/${this.settings.userId}`)
        .then(response => {
          if (!response.lights) {
            return reject('lights property is missing.');
          }

          resolve(toArray(response.lights));
        });
    });
  }

  changeLightState(id, states) {
    return new Promise((resolve, reject) => {
      return loadJSON('PUT', `http://${this.settings.bridgeAddress}/api/${this.settings.userId}/lights/${id}/state`,
        states)
        .then(response => {
          if (response[0] && response[0].error) {
            return reject(response[0].error.description);
          }

          resolve(response);
        });
    });
  }
}
<|endoftext|>


<commit_before>/* globals Models Status Random */

Models = { // eslint-disable-line
  project: function () {
    return {
      name: '',
      industry: '',
      createdAt: '',
      description: '',
      owner: '',
      contributors: [],
      commands: [],
      notes: [],
      droneLog: [],
      files: []
    }
  },

  host: function () {
    return {
      projectId: '',
      longIpv4Addr: 0,
      ipv4: '',
      mac: '',
      hostnames: [],
      os: '',
      notes: [],
      statusMessage: '',
      tags: [],
      status: Status.grey,
      lastModifiedBy: '',
      isFlagged: false,
      files: []
    }
  },

  authInterface: function () {
    return {
      projectId: '',
      isMultifactor: true,
      kind: '',
      url: '',
      description: ''
    }
  },

  netblock: function () {
    return {
      projectId: '',
      asn: '',
      asnCountryCode: '',
      asnCidr: '',
      asnDate: '',
      asnRegistry: '',
      cidr: '',
      abuseEmails: '',
      miscEmails: '',
      techEmails: '',
      name: '',
      address: '',
      city: '',
      state: '',
      country: '',
      postalCode: '',
      created: '',
      updated: '',
      description: '',
      handle: ''
    }
  },

  os: function () {
    return {
      tool: '',
      fingerprint: 'Unknown',
      weight: 0
    }
  },

  service: function () {
    return {
      projectId: '',
      hostId: '',
      port: 0,
      protocol: 'tcp',
      service: 'Unknown',
      product: 'Unknown',
      status: Status.grey,
      isFlagged: false,
      lastModifiedBy: '',
      notes: [],
      files: []
    }
  },

  issue: function () {
    return {
      projectId: '',
      title: '',
      cvss: 0,
      rating: '',
      isConfirmed: false,
      description: '',
      evidence: '',
      solution: '',
      hosts: [],
      pluginIds: [{
        tool: 'Manual',
        id: Random.id()
      }],
      cves: [],
      references: [],
      identifiedBy: [{
        tool: 'Manual'
      }],
      notes: [],
      isFlagged: false,
      status: Status.grey,
      lastModifiedBy: '',
      files: []
    }
  },

  issueHost: function () {
    return {
      ipv4: '',
      port: 0,
      protocol: ''
    }
  },

  issueReference: function () {
    return {
      link: '',
      name: ''
    }
  },

  person: function () {
    return {
      projectId: '',
      principalName: '',
      samAccountName: '',
      distinguishedName: '',
      firstName: '',
      middleName: '',
      lastName: '',
      displayName: '',
      department: '',
      description: '',
      address: '',
      emails: [],
      phones: [],
      references: [],
      groups: [],
      lastLogon: '',
      lastLogoff: '',
      loggedIn: []
    }
  },

  personReference: function () {
    return {
      description: '',
      username: '',
      link: ''
    }
  },

  note: function () {
    return {
      title: '',
      content: '',
      lastModifiedBy: ''
    }
  },

  credential: function () {
    return {
      username: '',
      password: '',
      format: '',
      hash: '',
      host: '',
      service: ''
    }
  },

  webDirectory: function () {
    return {
      projectId: '',
      hostId: '',
      path: '',
      port: 0,
      responseCode: '',
      lastModifiedBy: '',
      isFlagged: false
    }
  },

  file: function () {
    return {
      filename: '',
      url: ''
    }
  }
}
<commit_msg>Change default OS value to {}, rather than '' when adding a new host.<commit_after>/* globals Models Status Random */

Models = { // eslint-disable-line
  project: function () {
    return {
      name: '',
      industry: '',
      createdAt: '',
      description: '',
      owner: '',
      contributors: [],
      commands: [],
      notes: [],
      droneLog: [],
      files: []
    }
  },

  host: function () {
    return {
      projectId: '',
      longIpv4Addr: 0,
      ipv4: '',
      mac: '',
      hostnames: [],
      os: {},
      notes: [],
      statusMessage: '',
      tags: [],
      status: Status.grey,
      lastModifiedBy: '',
      isFlagged: false,
      files: []
    }
  },

  authInterface: function () {
    return {
      projectId: '',
      isMultifactor: true,
      kind: '',
      url: '',
      description: ''
    }
  },

  netblock: function () {
    return {
      projectId: '',
      asn: '',
      asnCountryCode: '',
      asnCidr: '',
      asnDate: '',
      asnRegistry: '',
      cidr: '',
      abuseEmails: '',
      miscEmails: '',
      techEmails: '',
      name: '',
      address: '',
      city: '',
      state: '',
      country: '',
      postalCode: '',
      created: '',
      updated: '',
      description: '',
      handle: ''
    }
  },

  os: function () {
    return {
      tool: '',
      fingerprint: 'Unknown',
      weight: 0
    }
  },

  service: function () {
    return {
      projectId: '',
      hostId: '',
      port: 0,
      protocol: 'tcp',
      service: 'Unknown',
      product: 'Unknown',
      status: Status.grey,
      isFlagged: false,
      lastModifiedBy: '',
      notes: [],
      files: []
    }
  },

  issue: function () {
    return {
      projectId: '',
      title: '',
      cvss: 0,
      rating: '',
      isConfirmed: false,
      description: '',
      evidence: '',
      solution: '',
      hosts: [],
      pluginIds: [{
        tool: 'Manual',
        id: Random.id()
      }],
      cves: [],
      references: [],
      identifiedBy: [{
        tool: 'Manual'
      }],
      notes: [],
      isFlagged: false,
      status: Status.grey,
      lastModifiedBy: '',
      files: []
    }
  },

  issueHost: function () {
    return {
      ipv4: '',
      port: 0,
      protocol: ''
    }
  },

  issueReference: function () {
    return {
      link: '',
      name: ''
    }
  },

  person: function () {
    return {
      projectId: '',
      principalName: '',
      samAccountName: '',
      distinguishedName: '',
      firstName: '',
      middleName: '',
      lastName: '',
      displayName: '',
      department: '',
      description: '',
      address: '',
      emails: [],
      phones: [],
      references: [],
      groups: [],
      lastLogon: '',
      lastLogoff: '',
      loggedIn: []
    }
  },

  personReference: function () {
    return {
      description: '',
      username: '',
      link: ''
    }
  },

  note: function () {
    return {
      title: '',
      content: '',
      lastModifiedBy: ''
    }
  },

  credential: function () {
    return {
      username: '',
      password: '',
      format: '',
      hash: '',
      host: '',
      service: ''
    }
  },

  webDirectory: function () {
    return {
      projectId: '',
      hostId: '',
      path: '',
      port: 0,
      responseCode: '',
      lastModifiedBy: '',
      isFlagged: false
    }
  },

  file: function () {
    return {
      filename: '',
      url: ''
    }
  }
}
<|endoftext|>


<commit_before>import ButtonGroup from '../../components/button-group'
import Code from '../../components/code'
import Layout from '../../components/layout'
import Link from '../../components/link'
import Title from '../../components/title'

export default () => (
  <Layout title="Wilderness motionPath function">
    <Title>
      The motionPath function
    </Title>

      Documentation coming soon.

    <ButtonGroup>
      <Link href="/api/the-plainshapeobject-function" button>
        Next up: The plainShapeObject function
      </Link>
    </ButtonGroup>
  </Layout>
)
<commit_msg>add motionPath api documentation<commit_after>import ButtonGroup from '../../components/button-group'
import Code from '../../components/code'
import Layout from '../../components/layout'
import Link from '../../components/link'
import Subtitle from '../../components/subtitle'
import Title from '../../components/title'

export default () => (
  <Layout title="Wilderness motionPath function">
    <Title>
      The motionPath function
    </Title>

      Creates a <Link href="/advanced-features/motion-path">motion path</Link>
      {' '} <Link href="/advanced-features/force">Force</Link> when passed a
      {' '} <Link href="/api/definitions#plain-shape-object">Plain Shape
      Object</Link>.

    {`
motionPath(plainShapeObject)
    `}

    <Subtitle id="arguments">
      Arguments
    </Subtitle>

        plainShapeObject

      The Plain Shape Object creates a motion path relative to the Shape it
      is applied to. For this reason it should almost always start from the
      point {`{ x: 0, y: 0 }`}.

      This Plain Shape Object can take three properties that have special
      significance in relation to a motion path.

        accuracy (optional) is a number, in
        degrees, that Wilderness determines a curve "straight enough" to be
        considered a straight line. This will determine how accurately a {' '}
        <Link href="/api/definitions#shape">Shape</Link> follows a motion path.
        However, the more accurate, the more calculations required, and
        therefore the worse the performance.

        easing (optional) is a string that
        maps to a function defined in {' '}
        <Link href="https://github.com/chenglou/tween-functions">Tween
        Functions</Link>, or a custom easing function. This is the easing with
        which the Shape travels the motion path.

        rotate (optional) is a boolean or a
        number that determines if the Shape should rotate along with the
        motion path. If set to a number, this is the angle to which the Shape
        is offset against the motion path's current angle.

    <Subtitle id="return-value">
      Return value
    </Subtitle>

      A Force function.

    <ButtonGroup>
      <Link href="/api/the-plainshapeobject-function" button>
        Next up: The plainShapeObject function
      </Link>
    </ButtonGroup>
  </Layout>
)
<|endoftext|>


<commit_before>'use strict';
angular.module('pdouah.controllers', [])
//Main controller
.controller('PdoMainCtrl', function($scope, $state) {

    $scope.goToReport = function() {
        $state.go('pdo.report');
    };
    $scope.goToHistory = function() {
        $state.go('pdo.history');
    };
})
//PDO Report Ctrl
.controller('PdoReportCtrl', ['Schools', 'Programs', 'Pdo', 'configService', '$scope', '$ionicPopup', '$q', '$location', '$ionicScrollDelegate',

    function(Schools, Programs, Pdo, configService, $scope, $ionicPopup, $q, $location, $ionicScrollDelegate) {
        var prom_programs_loaded = $q.defer();
        $scope.schools = [];
        $scope.courses = [];
        $scope.programs = [];
        $scope.pdo = {};

        Schools.query().then(function(data) {
            $scope.schools = data.schools;
        }).
        catch (function(reason) {
            console.error(reason);
        });
        $scope.schoolChanged = function(newSchool) {
            //Actualizo el model de las asignaturas
            Schools.load_programs(newSchool).then(function(data) {
                $scope.programs = data.programs;
                if (!$scope.$$phase) {
                    $scope.$apply(); //this triggers a $digest
                }
            }).
            catch (function(reason) {
                console.error(reason);
            });

        };
        $scope.programChanged = function(newProgram) {
            if (typeof newProgram === 'undefined') {
                $scope.courses = [];
                return;
            }
            Programs.load_courses(newProgram).then(function(data) {
                $scope.courses = data.courses;
                if (!$scope.$$phase) {
                    $scope.$apply(); //this triggers a $digest
                }
            }).
            catch (function(reason) {
                console.error(reason);
            });

        };
        $scope.courseChanged = function(newCourse) {
            console.log(newCourse);
        };
        $scope.edit = function() {
            $('.personal-data-edit').show();
            $('.personal-data.edit').hide();
        }
        // A confirm dialog
        $scope.send = function() {
            var confirmPopup = $ionicPopup.confirm({
                title: 'Enviar P.do',
                template: '¿Estás seguro que quieres enviar el P.do tal y como está??'
            });
            confirmPopup.then(function(res) {
                if (res) {
                    Pdo.sendPdo($scope.pdo).then(function(data) {
                        if (!configService.get('personalData')) {
                            var confirmPopupSaveData = $ionicPopup.confirm({
                                title: 'P.do enviado!',
                                template: '¿Quieres guardar tus datos para un proximo pdo?'
                            });
                            confirmPopupSaveData.then(function(res) {
                                if (res) {
                                    var personalData = {};
                                    personalData.name = $scope.pdo.name;
                                    personalData.surname = $scope.pdo.surname;
                                    personalData.phone = $scope.pdo.phone;
                                    personalData.program_id = $scope.pdo.program._id;
                                    personalData.school_id = $scope.pdo.school._id;
                                    personalData.num_id = $scope.pdo.num_id;
                                    personalData.email = $scope.pdo.email;
                                    configService.put('personalData', personalData);
                                } else {
                                    console.log('You are not sure');
                                }
                            });
                        }
                        configService.push('pdoStore', data.pdo);
                    }, function(reason) {
                        console.log('Razon:', reason);
                    });
                } else {
                    console.log('You are not sure');
                }
            });
        };
        if (configService.get('personalData')) {
            var loadSavedData = $ionicPopup.confirm({
                title: 'Datos personales detectados!',
                template: '¿Quieres cargar tus datos guardados?'
            });
            loadSavedData.then(function(res) {
                if (res) {
                    var personalData = configService.get('personalData');
                    if (personalData) {
                        $scope.pdo.name = personalData.name;
                        $scope.pdo.surname = personalData.surname;
                        $scope.pdo.phone = personalData.phone;
                        $scope.pdo.num_id = personalData.num_id;
                        $scope.pdo.email = personalData.email;
                        if (personalData.school_id) {
                            Schools.findById(personalData.school_id).then(function(school) {
                                $scope.pdo.school = school;

                                if (personalData.program_id) {
                                    //Si hay un dato de programa guardado
                                    Schools.load_programs($scope.pdo.school).then(function(data) {
                                        $scope.programs = data.programs;
                                        Programs.findById($scope.pdo.school.schoolname, personalData.program_id).then(function(program) {
                                            $scope.pdo.program = program;
                                            $location.hash('pdo-course');
                                            $ionicScrollDelegate.anchorScroll();

                                        }).
                                        catch (function(reason) {
                                            console.error('No encontrado:', reason);
                                        });
                                    }).
                                    catch (function(reason) {
                                        console.error(reason);
                                    });
                                } else {
                                    $location.hash('pdo-program');
                                    $ionicScrollDelegate.anchorScroll();
                                }
                            }).
                            catch (function(reason) {
                                console.error('No encontrada:', reason);
                            });
                        }
                        $('.personal-data-edit').hide();
                        if (!personalData.school_id) {
                            $('.personal-data-edit.school').show();
                        }
                        if (!personalData.program_id) {
                            $('.personal-data-edit.program').show();
                        }

                    }

                } else {
                    console.log('You are not sure');
                }
            });
        }

    }
])
//History Ctrl
.controller('PdoHistoryCtrl', function($scope) {})
//Basic Ctrl
.controller('PdoBasicCtrl', function($scope) {});<commit_msg>- Incluido que se oculte el boton de editar cuando se carga por primera vez<commit_after>'use strict';
angular.module('pdouah.controllers', [])
//Main controller
.controller('PdoMainCtrl', function($scope, $state) {

    $scope.goToReport = function() {
        $state.go('pdo.report');
    };
    $scope.goToHistory = function() {
        $state.go('pdo.history');
    };
})
//PDO Report Ctrl
.controller('PdoReportCtrl', ['Schools', 'Programs', 'Pdo', 'configService', '$scope', '$ionicPopup', '$q',

    function(Schools, Programs, Pdo, configService, $scope, $ionicPopup, $q) {
        var prom_programs_loaded = $q.defer();
        $scope.schools = [];
        $scope.courses = [];
        $scope.programs = [];
        $scope.pdo = {};

        Schools.query().then(function(data) {
            $scope.schools = data.schools;
        }).
        catch (function(reason) {
            console.error(reason);
        });
        $scope.schoolChanged = function(newSchool) {
            //Actualizo el model de las asignaturas
            Schools.load_programs(newSchool).then(function(data) {
                $scope.programs = data.programs;
                if (!$scope.$$phase) {
                    $scope.$apply(); //this triggers a $digest
                }
            }).
            catch (function(reason) {
                console.error(reason);
            });

        };
        $scope.programChanged = function(newProgram) {
            if (typeof newProgram === 'undefined') {
                $scope.courses = [];
                return;
            }
            Programs.load_courses(newProgram).then(function(data) {
                $scope.courses = data.courses;
                if (!$scope.$$phase) {
                    $scope.$apply(); //this triggers a $digest
                }
            }).
            catch (function(reason) {
                console.error(reason);
            });

        };
        $scope.courseChanged = function(newCourse) {
            console.log(newCourse);
        };
        $scope.edit = function() {
            $('.personal-data-edit').show();
            $('.personal-data.edit').hide();
        }
        // A confirm dialog
        $scope.send = function() {
            var confirmPopup = $ionicPopup.confirm({
                title: 'Enviar P.do',
                template: '¿Estás seguro que quieres enviar el P.do tal y como está??'
            });
            confirmPopup.then(function(res) {
                if (res) {
                    Pdo.sendPdo($scope.pdo).then(function(data) {
                        if (!configService.get('personalData')) {
                            var confirmPopupSaveData = $ionicPopup.confirm({
                                title: 'P.do enviado!',
                                template: '¿Quieres guardar tus datos para un proximo pdo?'
                            });
                            confirmPopupSaveData.then(function(res) {
                                if (res) {
                                    var personalData = {};
                                    personalData.name = $scope.pdo.name;
                                    personalData.surname = $scope.pdo.surname;
                                    personalData.phone = $scope.pdo.phone;
                                    personalData.program_id = $scope.pdo.program._id;
                                    personalData.school_id = $scope.pdo.school._id;
                                    personalData.num_id = $scope.pdo.num_id;
                                    personalData.email = $scope.pdo.email;
                                    configService.put('personalData', personalData);
                                } else {
                                    console.log('You are not sure');
                                }
                            });
                        }
                        configService.push('pdoStore', data.pdo);
                    }, function(reason) {
                        console.log('Razon:', reason);
                    });
                } else {
                    console.log('You are not sure');
                }
            });
        };
        if (configService.get('personalData')) {
            var loadSavedData = $ionicPopup.confirm({
                title: 'Datos personales detectados!',
                template: '¿Quieres cargar tus datos guardados?'
            });
            loadSavedData.then(function(res) {
                if (res) {
                    var personalData = configService.get('personalData');
                    if (personalData) {
                        $scope.pdo.name = personalData.name;
                        $scope.pdo.surname = personalData.surname;
                        $scope.pdo.phone = personalData.phone;
                        $scope.pdo.num_id = personalData.num_id;
                        $scope.pdo.email = personalData.email;
                        if (personalData.school_id) {
                            Schools.findById(personalData.school_id).then(function(school) {
                                $scope.pdo.school = school;

                                if (personalData.program_id) {
                                    //Si hay un dato de programa guardado
                                    Schools.load_programs($scope.pdo.school).then(function(data) {
                                        $scope.programs = data.programs;
                                        Programs.findById($scope.pdo.school.schoolname, personalData.program_id).then(function(program) {
                                            $scope.pdo.program = program;

                                        }).
                                        catch (function(reason) {
                                            console.error('No encontrado:', reason);
                                        });
                                    }).
                                    catch (function(reason) {
                                        console.error(reason);
                                    });
                                } else {
                                }
                            }).
                            catch (function(reason) {
                                console.error('No encontrada:', reason);
                            });
                        }
                        $('.personal-data-edit').hide();
                        if (!personalData.school_id) {
                            $('.personal-data-edit.school').show();
                        }
                        if (!personalData.program_id) {
                            $('.personal-data-edit.program').show();
                        }

                    }

                } else {
                    console.log('You are not sure');
                }
            });
        }else{
            $scope.edit();
        }

    }
])
//History Ctrl
.controller('PdoHistoryCtrl', function($scope) {})
//Basic Ctrl
.controller('PdoBasicCtrl', function($scope) {});<|endoftext|>


<commit_before>"use strict";

const path = require('path');
const Promise = require('bluebird');
const sqlite3 = require('sqlite3');
const mkdirp = require('mkdirp');
const {forEach} = require('lodash/collection');
const {range} = require('lodash/util');

const DB_TABLE_NAME = 'CACHE';

/**
 * Creates a key/value wrapper around a persistent SQL store.
 *
 * Changes from `set` or `remove` are synchronously made in memory but
 * are not persisted until the `persistChanges` method is called. This
 * is a workaround for sqlite's blocking writes, which prevent any reads
 * from occurring until the write has finished
 */
class PersistentCache {
  constructor(filename, {createDatabaseConnection}={}) {
    if (createDatabaseConnection) {
      this.createDatabaseConnection = createDatabaseConnection;
    } else {
      if (!filename) {
        throw new Error('A `filename` option must be provided');
      }
      this.createDatabaseConnection = () => {
        return createSqlite3Database(filename);
      };
    }

    this.connection = this.createDatabaseConnection();

    // An in-memory cache that helps avoid some of the overhead involved with
    // the file system. Note that the memory cache only stores the serialized
    // state of the entries, so there is still a deserialization cost incurred
    // for gets
    this.memoryCache = new MemoryCache();

    // Mutable maps (k => v) of the changes that should be persisted
    this.pendingInserts = Object.create(null);
    this.pendingDeletes = Object.create(null);
  }
  /**
   * Any writes to SQLite will block concurrent reads, so we defer
   * all write and delete operations until this method is called
   */
  persistChanges() {
    return this.connection
      .then(db => {
        const _pendingInserts = this.pendingInserts;
        this.pendingInserts = Object.create(null);
        const insertParams = [];
        forEach(_pendingInserts, (value, key) => {
          if (value !== undefined) {
            insertParams.push(key, value);
          }
        });

        const _pendingDeletes = this.pendingDeletes;
        this.pendingDeletes = Object.create(null);
        const deleteParams = [];
        forEach(_pendingDeletes, (value, key) => {
          if (value !== undefined) {
            deleteParams.push(key);
          }
        });

        if (!insertParams.length && !deleteParams.length) {
          return Promise.resolve();
        }

        let insertStatement;
        if (insertParams.length) {
          const insertTokens = range(insertParams.length / 2).map(() => '(?, ?)');
          insertStatement = `
            INSERT OR REPLACE INTO ${DB_TABLE_NAME} (key, value)
            VALUES ${insertTokens.join(',')};
          `;
        }

        let deleteStatement;
        if (deleteParams.length) {
          const deleteTokens = range(deleteParams.length).map(() => 'key = ?');
          deleteStatement = `DELETE FROM ${DB_TABLE_NAME} WHERE ${deleteTokens.join(' OR ')};`;
        }

        // Flush the changes to disk
        return new Promise((res, rej) => {
          function applyInserts(cb) {
            db.run(insertStatement, insertParams, err => {
              if (err) return rej(err);
              cb();
            });
          }

          function applyDeletes(cb) {
            db.run(deleteStatement, deleteParams, err => {
              if (err) return rej(err);
              cb();
            });
          }

          if (insertStatement && deleteStatement) {
            applyInserts(() => applyDeletes(res));
          } else if (insertStatement) {
            applyInserts(res);
          } else if (deleteStatement) {
            applyDeletes(res);
          }
        });
      });
  }
  /**
   * Given a key, returns a promise resolving to either an associated value
   * or null.
   */
  get(key) {
    if (this.memoryCache.has(key)) {
      const inMemoryValue = this.memoryCache.get(key);
      if (inMemoryValue) {
        return this._deserializeData(inMemoryValue);
      }
      // Handle cases where we've already hit the file system and there
      // is no data available
      return Promise.resolve(null);
    }

    return this.connection
      .then(db => {
        return new Promise((res, rej) => {
          db.get(
            `SELECT value FROM ${DB_TABLE_NAME} WHERE key = ?`,
            [key],
            (err, data) => {
              if (err) return rej(err);
              if (!data) return res(null);
              res(data.value);
            }
          );
        });
      })
      .then(data => {
        this.memoryCache.set(key, data);
        if (data) {
          return this._deserializeData(data);
        }
        return null;
      });
  }
  /**
   * Associates a key/value combination in both memory and persistent stores.
   */
  set(key, value) {
    // Note: serializing large JSON structures can block the event loop. We could defer,
    // in an attempt to avoid blocking the event loop, but that opens up a potential
    // world of pain if the objects were ever mutated
    const json = JSON.stringify(value);
    this.memoryCache.set(key, json);
    this._schedulePersistentWrite(key, json);
  }
  /**
   * Removes any value associated with the provided key.
   */
  remove(key) {
    this.memoryCache.remove(key);
    this._schedulePersistentDelete(key);
  }
  closeDatabaseConnection() {
    return this.connection
      .then(db => {
        return new Promise((res, rej) => {
          db.close(err => {
            if (err) return rej(err);
            return res();
          });
        });
      });
  }
  _schedulePersistentWrite(key, value) {
    if (this.pendingDeletes[key]) {
      this.pendingDeletes[key] = undefined;
    }
    return this.pendingInserts[key] = value;
  }
  _schedulePersistentDelete(key) {
    if (this.pendingInserts[key]) {
      this.pendingInserts[key] = undefined;
    }
    return this.pendingDeletes[key] = true;
  }
  _deserializeData(json) {
    let data;
    try {
      data = JSON.parse(json);
    } catch(err) {
      err.message = `Error deserializing cached data - ${err.message}`;
      return Promise.reject(err);
    }
    return Promise.resolve(data);
  }
}

/**
 * Simple k/v store that operates in memory
 *
 * @returns {Object}
 */
class MemoryCache {
  constructor() {
    this.cache = Object.create(null);
  }
  has(key) {
    return this.cache[key] !== undefined;
  }
  get(key) {
    return this.cache[key] || null;
  }
  set(key, value) {
    this.cache[key] = value;
  }
  remove(key) {
    this.cache[key] = null;
  }
}

function createSqlite3Database(filename) {
  return new Promise((res, rej) => {
    const directory = path.dirname(filename);
    mkdirp(directory, err => {
      if (err) return rej(err);

      const db = new sqlite3.Database(filename, (err) => {
        if (err) return rej(err);

        /*
         TODO: textual keys allegedly have poor selection performance

         Possible solutions:
         - A more contextual method with multiple fields: path, mtime, etc?
         - Use int ids for more performant selections?
           Would require a key => id map to be maintained, which means more moving parts
         */
        db.run(
          `
            CREATE TABLE IF NOT EXISTS ${DB_TABLE_NAME} (
              key TEXT PRIMARY KEY NOT NULL,
              value TEXT
            );
          `,
          (err) => {
            if (err) return rej(err);
            res(db);
          }
        );
      });
    });
  });
}

module.exports = {
  PersistentCache,
  MemoryCache,
  createSqlite3Database
};<commit_msg>Replaced the persistent cache's memory store with a simple object<commit_after>"use strict";

const path = require('path');
const Promise = require('bluebird');
const sqlite3 = require('sqlite3');
const mkdirp = require('mkdirp');
const {forEach} = require('lodash/collection');
const {range} = require('lodash/util');

const DB_TABLE_NAME = 'CACHE';

/**
 * Creates a key/value wrapper around a persistent SQL store.
 *
 * Changes from `set` or `remove` are synchronously made in memory but
 * are not persisted until the `persistChanges` method is called. This
 * is a workaround for sqlite's blocking writes, which prevent any reads
 * from occurring until the write has finished
 */
class PersistentCache {
  constructor(filename, {createDatabaseConnection}={}) {
    if (createDatabaseConnection) {
      this.createDatabaseConnection = createDatabaseConnection;
    } else {
      if (!filename) {
        throw new Error('A `filename` option must be provided');
      }
      this.createDatabaseConnection = () => {
        return createSqlite3Database(filename);
      };
    }

    this.connection = this.createDatabaseConnection();

    // An in-memory cache that helps avoid some of the overhead involved with
    // the file system. Note that the memory cache only stores the serialized
    // state of the entries, so there is still a deserialization cost incurred
    // for gets
    this.memoryCache = Object.create(null);

    // Mutable maps (k => v) of the changes that should be persisted
    this.pendingInserts = Object.create(null);
    this.pendingDeletes = Object.create(null);
  }
  /**
   * Any writes to SQLite will block concurrent reads, so we defer
   * all write and delete operations until this method is called
   */
  persistChanges() {
    return this.connection
      .then(db => {
        const _pendingInserts = this.pendingInserts;
        this.pendingInserts = Object.create(null);
        const insertParams = [];
        forEach(_pendingInserts, (value, key) => {
          if (value !== undefined) {
            insertParams.push(key, value);
          }
        });

        const _pendingDeletes = this.pendingDeletes;
        this.pendingDeletes = Object.create(null);
        const deleteParams = [];
        forEach(_pendingDeletes, (value, key) => {
          if (value !== undefined) {
            deleteParams.push(key);
          }
        });

        if (!insertParams.length && !deleteParams.length) {
          return Promise.resolve();
        }

        let insertStatement;
        if (insertParams.length) {
          const insertTokens = range(insertParams.length / 2).map(() => '(?, ?)');
          insertStatement = `
            INSERT OR REPLACE INTO ${DB_TABLE_NAME} (key, value)
            VALUES ${insertTokens.join(',')};
          `;
        }

        let deleteStatement;
        if (deleteParams.length) {
          const deleteTokens = range(deleteParams.length).map(() => 'key = ?');
          deleteStatement = `DELETE FROM ${DB_TABLE_NAME} WHERE ${deleteTokens.join(' OR ')};`;
        }

        // Flush the changes to disk
        return new Promise((res, rej) => {
          function applyInserts(cb) {
            db.run(insertStatement, insertParams, err => {
              if (err) return rej(err);
              cb();
            });
          }

          function applyDeletes(cb) {
            db.run(deleteStatement, deleteParams, err => {
              if (err) return rej(err);
              cb();
            });
          }

          if (insertStatement && deleteStatement) {
            applyInserts(() => applyDeletes(res));
          } else if (insertStatement) {
            applyInserts(res);
          } else if (deleteStatement) {
            applyDeletes(res);
          }
        });
      });
  }
  /**
   * Given a key, returns a promise resolving to either an associated value
   * or null.
   */
  get(key) {
    if (key in this.memoryCache) {
      const inMemoryValue = this.memoryCache[key];
      if (inMemoryValue) {
        return this._deserializeData(inMemoryValue);
      }
      // Handle cases where we've already hit the file system and there
      // is no data available
      return Promise.resolve(null);
    }

    return this.connection
      .then(db => {
        return new Promise((res, rej) => {
          db.get(
            `SELECT value FROM ${DB_TABLE_NAME} WHERE key = ?`,
            [key],
            (err, data) => {
              if (err) return rej(err);
              if (!data) return res(null);
              res(data.value);
            }
          );
        });
      })
      .then(data => {
        this.memoryCache[key] = data;
        if (data) {
          return this._deserializeData(data);
        }
        return null;
      });
  }
  /**
   * Associates a key/value combination in both memory and persistent stores.
   */
  set(key, value) {
    // Note: serializing large JSON structures can block the event loop. We could defer,
    // in an attempt to avoid blocking the event loop, but that opens up a potential
    // world of pain if the objects were ever mutated
    const json = JSON.stringify(value);
    this.memoryCache[key] = json;
    this._schedulePersistentWrite(key, json);
  }
  /**
   * Removes any value associated with the provided key.
   */
  remove(key) {
    this.memoryCache[key] = null;
    this._schedulePersistentDelete(key);
  }
  closeDatabaseConnection() {
    return this.connection
      .then(db => {
        return new Promise((res, rej) => {
          db.close(err => {
            if (err) return rej(err);
            return res();
          });
        });
      });
  }
  _schedulePersistentWrite(key, value) {
    if (this.pendingDeletes[key]) {
      this.pendingDeletes[key] = undefined;
    }
    return this.pendingInserts[key] = value;
  }
  _schedulePersistentDelete(key) {
    if (this.pendingInserts[key]) {
      this.pendingInserts[key] = undefined;
    }
    return this.pendingDeletes[key] = true;
  }
  _deserializeData(json) {
    let data;
    try {
      data = JSON.parse(json);
    } catch(err) {
      err.message = `Error deserializing cached data - ${err.message}`;
      return Promise.reject(err);
    }
    return Promise.resolve(data);
  }
}

function createSqlite3Database(filename) {
  return new Promise((res, rej) => {
    const directory = path.dirname(filename);
    mkdirp(directory, err => {
      if (err) return rej(err);

      const db = new sqlite3.Database(filename, (err) => {
        if (err) return rej(err);

        /*
         TODO: textual keys allegedly have poor selection performance

         Possible solutions:
         - A more contextual method with multiple fields: path, mtime, etc?
         - Use int ids for more performant selections?
           Would require a key => id map to be maintained, which means more moving parts
         */
        db.run(
          `
            CREATE TABLE IF NOT EXISTS ${DB_TABLE_NAME} (
              key TEXT PRIMARY KEY NOT NULL,
              value TEXT
            );
          `,
          (err) => {
            if (err) return rej(err);
            res(db);
          }
        );
      });
    });
  });
}

module.exports = {
  PersistentCache,
  createSqlite3Database
};<|endoftext|>


<commit_before>$(function() {

  // toggle all checkbox
  $('[data-toggle="checkbox"]').change(function () {
    $('input[type=checkbox]').prop('checked', this.checked);
  });

  // Open job caption form when caption is clicked
  // ----------------------------------------------

  $(".caption").button({
    text: false,
  }).click(function( event ) {
    var job_id = $(this).attr('data-value');
    $.getJSON(
      '/edit_job.json',
      {'job_id': job_id},
      function(json) {
        if (json) {
          form = $('#caption-form');

          // Set the title
          //form.find('h4').text('Edit Caption');
          $.each(json, function(k, v) {
            // Set the value for each field from the returned json
            form.find('input[name="' + k + '"]').attr('value', v);
          });

          form.modal('show');
        }
      }
    );
  });

  // Open job labels form when labels is clicked
  // --------------------------------------------

  $(".labels").button({
    text: false,
  }).click(function( event ) {
    var job_id = $(this).attr('data-value');
    $.getJSON(
      '/edit_job.json',
      {'job_id': job_id},
      function(json) {
        if (json) {
          form = $('#labels-form');

          // Set the title
          //form.find('h4').text('Edit labels');
          $.each(json, function(k, v) {
            // Set the value for each field from the returned json
            form.find('input[name="' + k + '"]').attr('value', v);
          });

          form.modal('show');
        }
      }
    );
  });

  // Select status
  // --------------

  var selectStatus = function() {
    default_location = '/monitor/';
    var location = $('#status-select option:selected').val();
    if (location){
      window.location = location;
    } else {
      window.location = default_location;
    }
  };

  $('#status-select').change( selectStatus );

  // Select limit
  // -------------

  var selectLimit = function() {
    default_location = '/monitor/';
    var location = $('#limit-select option:selected').val();
    if (location){
      window.location = location;
    } else {
      window.location = default_location;
    }
  };

  $('#limit-select').change( selectLimit );

  // monitor auto-update
  // -------------------

  var timerId = 0;

  // update job table with current values
  var updateJobs = function() {
    $.getJSON(
      '/active_jobs.json',
      {},
      function(json) {

        // count running jobs
        var numActiveJobs = $(".fa-gear").length;
        //console.log("active jobs in table = " + numActiveJobs + ", num jobs = " + json.length);
        if (numActiveJobs != json.length) {
          // TODO: this can also be done with jquery
          location.reload();
        }
        else {
          $.each(json, function(index, job) {
            var iconClass = ''
            if (job.status == 'ProcessStarted') {
              iconClass = "fa fa-gear fa-spin text-muted";
            }
            else if (job.status == 'ProcessSucceeded') {
              iconClass = "fa fa-check-circle text-success";
            }
            else if (job.status == 'ProcessFailed') {
              iconClass = "fa fa-times-circle text-danger";
            }
            else if (job.status == 'ProcessPaused') {
              iconClass = "fa fa-pause text-muted";
            }
            else {
              iconClass = "fa fa-fw fa-clock-o pulse text-muted";
            }

            $("#status-"+job.identifier).attr('class', iconClass);
            $("#status-"+job.identifier).attr('title', job.status);
            $("#progress-"+job.identifier).attr('style', "width: "+job.progress+"%");
            $("#progress-"+job.identifier).text(job.progress+"%");
            $("#duration-"+job.identifier).text(job.duration);
          });
        }
      }
    );
  };

  // refresh job list each 3 secs ...
  timerId = setInterval(function() {
    updateJobs();
  }, 3000);

});
<commit_msg>set auto-update to 30 secs<commit_after>$(function() {

  // toggle all checkbox
  $('[data-toggle="checkbox"]').change(function () {
    $('input[type=checkbox]').prop('checked', this.checked);
  });

  // Open job caption form when caption is clicked
  // ----------------------------------------------

  $(".caption").button({
    text: false,
  }).click(function( event ) {
    var job_id = $(this).attr('data-value');
    $.getJSON(
      '/edit_job.json',
      {'job_id': job_id},
      function(json) {
        if (json) {
          form = $('#caption-form');

          // Set the title
          //form.find('h4').text('Edit Caption');
          $.each(json, function(k, v) {
            // Set the value for each field from the returned json
            form.find('input[name="' + k + '"]').attr('value', v);
          });

          form.modal('show');
        }
      }
    );
  });

  // Open job labels form when labels is clicked
  // --------------------------------------------

  $(".labels").button({
    text: false,
  }).click(function( event ) {
    var job_id = $(this).attr('data-value');
    $.getJSON(
      '/edit_job.json',
      {'job_id': job_id},
      function(json) {
        if (json) {
          form = $('#labels-form');

          // Set the title
          //form.find('h4').text('Edit labels');
          $.each(json, function(k, v) {
            // Set the value for each field from the returned json
            form.find('input[name="' + k + '"]').attr('value', v);
          });

          form.modal('show');
        }
      }
    );
  });

  // Select status
  // --------------

  var selectStatus = function() {
    default_location = '/monitor/';
    var location = $('#status-select option:selected').val();
    if (location){
      window.location = location;
    } else {
      window.location = default_location;
    }
  };

  $('#status-select').change( selectStatus );

  // Select limit
  // -------------

  var selectLimit = function() {
    default_location = '/monitor/';
    var location = $('#limit-select option:selected').val();
    if (location){
      window.location = location;
    } else {
      window.location = default_location;
    }
  };

  $('#limit-select').change( selectLimit );

  // monitor auto-update
  // -------------------

  var timerId = 0;

  // update job table with current values
  var updateJobs = function() {
    $.getJSON(
      '/active_jobs.json',
      {},
      function(json) {

        // count running jobs
        var numActiveJobs = $(".fa-gear").length;
        //console.log("active jobs in table = " + numActiveJobs + ", num jobs = " + json.length);
        if (numActiveJobs != json.length) {
          // TODO: this can also be done with jquery
          location.reload();
        }
        else {
          $.each(json, function(index, job) {
            var iconClass = ''
            if (job.status == 'ProcessStarted') {
              iconClass = "fa fa-gear fa-spin text-muted";
            }
            else if (job.status == 'ProcessSucceeded') {
              iconClass = "fa fa-check-circle text-success";
            }
            else if (job.status == 'ProcessFailed') {
              iconClass = "fa fa-times-circle text-danger";
            }
            else if (job.status == 'ProcessPaused') {
              iconClass = "fa fa-pause text-muted";
            }
            else {
              iconClass = "fa fa-fw fa-clock-o pulse text-muted";
            }

            $("#status-"+job.identifier).attr('class', iconClass);
            $("#status-"+job.identifier).attr('title', job.status);
            $("#progress-"+job.identifier).attr('style', "width: "+job.progress+"%");
            $("#progress-"+job.identifier).text(job.progress+"%");
            $("#duration-"+job.identifier).text(job.duration);
          });
        }
      }
    );
  };

  // refresh job list each 30 secs ...
  timerId = setInterval(function() {
    updateJobs();
}, 30000);

});
<|endoftext|>


<commit_before>// PLAYER NAMES //////////////////////////////////////////////////////
// Player names are cached in local storage forever. There is no GUI
// element from within the total conversion to clean them, but you
// can run localStorage.clean() to reset it.

// retrieves player name by GUID. If the name is not yet available, it
// will be added to a global list of GUIDs that need to be resolved.
// The resolve method is not called automatically.
window.getPlayerName = function(guid) {
  if(localStorage[guid]) return localStorage[guid];
  // only add to queue if it isn’t already
  if(playersToResolve.indexOf(guid) === -1 && playersInResolving.indexOf(guid) === -1) {
    console.log('resolving player guid=' + guid);
    playersToResolve.push(guid);
  }
  return '{'+guid.slice(0, 12)+'}';
}

window._playerNameToGuidCache = {};

window.playerNameToGuid = function(playerName) {
  var cachedGuid = window._playerNameToGuidCache[playerName];
  if (cachedGuid !== undefined) return cachedGuid;

  var guid = null;
  $.each(Object.keys(localStorage), function(ind,key) {
    if(playerName === localStorage[key]) {
      guid = key;
      return false;  //break from $.each
    }
  });

  window._playerNameToGuidCache[playerName] = guid;
  return guid;
}

// resolves all player GUIDs that have been added to the list. Reruns
// renderPortalDetails when finished, so that then-unresolved names
// get replaced by their correct versions.
window.resolvePlayerNames = function() {
  if(window.playersToResolve.length === 0) return;
  var p = window.playersToResolve;
  var d = {guids: p};
  playersInResolving = window.playersInResolving.concat(p);
  playersToResolve = [];
  postAjax('getPlayersByGuids', d, function(dat) {
    $.each(dat.result, function(ind, player) {
      window.setPlayerName(player.guid, player.nickname);
      // remove from array
      window.playersInResolving.splice(window.playersInResolving.indexOf(player.guid), 1);
    });
    if(window.selectedPortal)
      window.renderPortalDetails(window.selectedPortal);
  },
  function() {
    // append failed resolves to the list again
    console.warn('resolving player guids failed: ' + p.join(', '));
    window.playersToResolve.concat(p);
  });
}

window.setPlayerName = function(guid, nick) {
  if($.trim(('' + nick)).slice(0, 5) === '{"L":' && !window.alertFor37WasShown) {
    window.alertFor37WasShown = true;
    alert('You have run into bug #37. Please help me solve it!\nCopy and paste this text and post it here:\nhttps://github.com/breunigs/ingress-intel-total-conversion/issues/37\nIf copy & pasting doesn’t work, make a screenshot instead.\n\n\n' + window.debug.printStackTrace() + '\n\n\n' + JSON.stringify(nick));
  }
  localStorage[guid] = nick;
}

window.loadPlayerNamesForPortal = function(portal_details) {
  if(map.getZoom() < PRECACHE_PLAYER_NAMES_ZOOM) return;
  var e = portal_details;

  if(e.captured && e.captured.capturingPlayerId)
    getPlayerName(e.captured.capturingPlayerId);

  if(!e.resonatorArray || !e.resonatorArray.resonators) return;

  $.each(e.resonatorArray.resonators, function(ind, reso) {
    if(reso) getPlayerName(reso.ownerGuid);
  });
}
<commit_msg>don't resolve more than 15 names in one request - this is approximately the limit imposed by the server if more are to be resolved, multiple requests are made. this does increase the odds of hitting the request rate limit, but in that case we'll get http failed requests and the names will be pushed back onto the pending resolve list for next time<commit_after>// PLAYER NAMES //////////////////////////////////////////////////////
// Player names are cached in local storage forever. There is no GUI
// element from within the total conversion to clean them, but you
// can run localStorage.clean() to reset it.

// retrieves player name by GUID. If the name is not yet available, it
// will be added to a global list of GUIDs that need to be resolved.
// The resolve method is not called automatically.
window.getPlayerName = function(guid) {
  if(localStorage[guid]) return localStorage[guid];
  // only add to queue if it isn’t already
  if(playersToResolve.indexOf(guid) === -1 && playersInResolving.indexOf(guid) === -1) {
    playersToResolve.push(guid);
  }
  return '{'+guid.slice(0, 12)+'}';
}

window._playerNameToGuidCache = {};

window.playerNameToGuid = function(playerName) {
  var cachedGuid = window._playerNameToGuidCache[playerName];
  if (cachedGuid !== undefined) return cachedGuid;

  var guid = null;
  $.each(Object.keys(localStorage), function(ind,key) {
    if(playerName === localStorage[key]) {
      guid = key;
      return false;  //break from $.each
    }
  });

  window._playerNameToGuidCache[playerName] = guid;
  return guid;
}

// resolves all player GUIDs that have been added to the list. Reruns
// renderPortalDetails when finished, so that then-unresolved names
// get replaced by their correct versions.
window.resolvePlayerNames = function() {
  if(window.playersToResolve.length === 0) return;

  //limit per request. stock site is never more than 13 (8 res, 4 mods, owner)
  //testing shows 15 works and 20 fails
  var MAX_RESOLVE_PLAYERS_PER_REQUEST = 15;

  var p = window.playersToResolve.slice(0,MAX_RESOLVE_PLAYERS_PER_REQUEST);
  window.playersToResolve = playersToResolve.slice(MAX_RESOLVE_PLAYERS_PER_REQUEST);

  var d = {guids: p};
  window.playersInResolving = window.playersInResolving.concat(p);

  postAjax('getPlayersByGuids', d, function(dat) {
    if(dat.result) {
      $.each(dat.result, function(ind, player) {
        window.setPlayerName(player.guid, player.nickname);
        // remove from array
        window.playersInResolving.splice(window.playersInResolving.indexOf(player.guid), 1);
      });
    } else {
      //no 'result' - a successful http request, but the returned result was an error of some kind
      console.warn('getplayers problem - no result in response: '+dat);

      //likely to be some kind of 'bad request' (e.g. too many names at once, or otherwise badly formatted data.
      //therefore, not a good idea to automatically retry by adding back to the playersToResolve list
    }

    //TODO: have an event triggered for this instead of hard-coded single function call
    if(window.selectedPortal)
      window.renderPortalDetails(window.selectedPortal);

    //if more to do, run again
    if(window.playersToResolve.length>0) resolvePlayerNames();
  },
  function() {
    // append failed resolves to the list again
    console.warn('resolving player guids failed: ' + p.join(', '));
    window.playersToResolve.concat(p);
  });
}

window.setPlayerName = function(guid, nick) {
  if($.trim(('' + nick)).slice(0, 5) === '{"L":' && !window.alertFor37WasShown) {
    window.alertFor37WasShown = true;
    alert('You have run into bug #37. Please help me solve it!\nCopy and paste this text and post it here:\nhttps://github.com/breunigs/ingress-intel-total-conversion/issues/37\nIf copy & pasting doesn’t work, make a screenshot instead.\n\n\n' + window.debug.printStackTrace() + '\n\n\n' + JSON.stringify(nick));
  }
  localStorage[guid] = nick;
}

window.loadPlayerNamesForPortal = function(portal_details) {
  if(map.getZoom() < PRECACHE_PLAYER_NAMES_ZOOM) return;
  var e = portal_details;

  if(e.captured && e.captured.capturingPlayerId)
    getPlayerName(e.captured.capturingPlayerId);

  if(!e.resonatorArray || !e.resonatorArray.resonators) return;

  $.each(e.resonatorArray.resonators, function(ind, reso) {
    if(reso) getPlayerName(reso.ownerGuid);
  });
}
<|endoftext|>