<commit_before>/*global NMTdata,window*/
/***
 * Provides data and methods for serving ads.
 * @author: Duane.Jennings@niit-mediatech.com
 * @version: 201310022226:443263
 *
 */
var NMTdata = NMTdata || {};

    NMTdata.ads = (function () {
        // REQUIRES: NMTdata.data module to be loaded prior to this module.
        // ads related methods and properties.
        if(!window.console){
            console={};
            console.log = function(){};
        }
        console.log("NMTdata.ads init");

        var data = NMTdata.data,
        adunitPrefixDomainMappings = new Array(),
        adunitPrefixURLMappings = new Array(),
        adunitPathMappings = new Array(),
        adunitURLMappings = new Array(),
        cccPathMappings = new Array(),
        cccURLMappings = new Array(),
        i = 0,
        mmo_ccc = '',
        mmo_console = '',
        pathlength = 0,
        maxAdunitPathLength = 3,  // limit the adunit path elements.
        dfp_adunit = '', // example: /news/local
		dfp_ccc = ''; // customTargeting value

////////////////////////////////////////////////////////////////////////
/***
 * mapping_version: 201310041000:443265
 */
dfp_adunit_prefix = '/11365842/savannahnow.com',
        adunitPrefixDomainMappings = [
                                      // These mappings will do a contains match against domain host.
                                      // MBU custom mappings
                                      {'autos\.coastalautos\.com': '/11365842/savannahnow.com/autos'},
                                      {'classifieds\.savannahnow\.com': '/11365842/savannahnow.com/classifieds'},
                                      {'class\.savannahnow\.com': '/11365842/savannahnow.com/classifieds'},
                                      {'events\.savannahnow\.com': '/11365842/savannahnow.com/events'},
                                      {'giftguide\.': '/11365842/giftguide.savannahnow.com'},
                                      {'physicianguide\.': '/11365842/savannahnow.com/physicianguide'},
                                      {'^m\.savannahnow\.com': '/11365842/m.savannahnow.com'},
                                      {'^m\.pfsav': '/11365842/m.savannahnow.com'},
                                      {'jobs\.savannahnow\.com': '/11365842/savannahnow.com/jobs'},
                                      {'savannahhomesforsale\.com': '/11365842/savannahhomesforsale.com'},
                                      {'savannahmagazine\.com': '/11365842/savannahmagazine.com'},
                                      {'spotted\.savannahnow\.com': '/11365842/savannahnow.com/spotted'},
                                      {'rentals\.savannahnow\.com': '/11365842/savannahnow.com/rentals'},
                                      {'legacy\.com': '/11365842/savannahnow.com/obits'}
                              ];
        adunitURLMappings = [
                             // MBU custom mappings
					{'rentals\.savannahnow\.com': ''},
					{'events\.savannahnow\.com': ''}
                             // Common mappings
                      ];
        adunitPathMappings = [
                              // MBU custom mappings
                              {'^\/births$': '/lifestyle/births'},
                              {'^\/fast-physicianguide.php': '/physicianguide'},
                              // Common mappings
                              {'^\/homepageSMN$': '/homepage'},
                              {'^\/$': '/homepage'}
                      ];
        cccURLMappings = [
                          // MBU custom mappings
                          // Common mappings
                   ];
        cccPathMappings = [
                           // MBU custom mappings
                           // Common mappings
                           {'^\/homepageSMN$': 'homepage'},
                           {'^\/$': 'homepage'}
                   ];
////////////////////////////////////////////////////////////////////////

        // Process domain mapping for dfp_adunit_prefix
        dfp_adunit_prefix = data.processMapping(adunitPrefixDomainMappings, location.host, dfp_adunit_prefix);

        // Process URL mapping for dfp_adunit_prefix
        dfp_adunit_prefix = data.processMapping(adunitPrefixURLMappings, document.URL, dfp_adunit_prefix);

        // build dfp_adunit default value
        // Limit adunit to 3 path elements.
        pathlength = (data.pathnames.length > maxAdunitPathLength) ? maxAdunitPathLength : data.pathnames.length;
        for (i = 0; i < pathlength; i++) {
            if (data.pathnames[i] !== '') {
                dfp_adunit += '/' + data.pathnames[i];
            }
        }

        // Process Path mappings for dfp_adunit
        dfp_adunit = data.processMapping(adunitPathMappings, window.location.pathname, dfp_adunit);
        // Process URL mappings for dfp_adunit
        dfp_adunit = data.processMapping(adunitURLMappings, document.URL, dfp_adunit);

        // dfp_ccc default value
        // Paths that have trailing slash will leave the last path element empty.
        // Let's check for this case and go back one more for the ccc value.
        if (data.pathnames.length > 1 && data.pathnames[data.pathnames.length-1] == '') {
            dfp_ccc = data.pathnames[data.pathnames.length - 2];
        } else {
            dfp_ccc = data.pathnames[data.pathnames.length - 1];
        }

        // Process Path mappings for dfp_ccc
        dfp_ccc = data.processMapping(cccPathMappings, window.location.pathname, dfp_ccc);
        // Process URL mappings for dfp_ccc
        dfp_ccc = data.processMapping(cccURLMappings, document.URL, dfp_ccc);

        // override dfp_ccc with query_string mmo_ccc
        mmo_ccc = data.getQueryParam('mmo_ccc');
        if (mmo_ccc !== undefined) { dfp_ccc = mmo_ccc; }

        // Google DFP has 40 character limit on targeting values.
        dfp_ccc = dfp_ccc.slice(0,40);

        // output debug information to console
        mmo_console = data.getQueryParam('google_console');
        if (mmo_console !== undefined) {
            console.log("NMTdata.ads.dfp_adunit_prefix: "+dfp_adunit_prefix);
            console.log("NMTdata.ads.dfp_adunit: "+dfp_adunit);
            console.log("DFP AdUnit: "+dfp_adunit_prefix+dfp_adunit);
            console.log("NMTdata.ads.dfp_ccc: "+dfp_ccc);
        }

        // output debug information to console
        mmo_console = data.getQueryParam('nmt_console');
        if (mmo_console !== undefined) {
            document.write("
NMTdata.ads.dfp_adunit_prefix: "+dfp_adunit_prefix);
            document.write("
NMTdata.ads.dfp_adunit: "+dfp_adunit);
            document.write("
DFP AdUnit: "+dfp_adunit_prefix+dfp_adunit);
            document.write("
NMTdata.ads.dfp_ccc: "+data.escapeHtml(dfp_ccc));
        }

        return { // return object
            dfp_nmt_mapping_version: '201310041000:443265',
            dfp_nmt_ads_version: '201310022226:443263',
            dfp_adunit_prefix: dfp_adunit_prefix,
            dfp_adunit: dfp_adunit,
            dfp_ccc: data.escapeHtml(dfp_ccc)
        };

    }());

<commit_msg>Added dosavannah.com mapping<commit_after>/*global NMTdata,window*/
/***
 * Provides data and methods for serving ads.
 * @author: Duane.Jennings@niit-mediatech.com
 * @version: 201310022226:443263
 *
 */
var NMTdata = NMTdata || {};

    NMTdata.ads = (function () {
        // REQUIRES: NMTdata.data module to be loaded prior to this module.
        // ads related methods and properties.
        if(!window.console){
            console={};
            console.log = function(){};
        }
        console.log("NMTdata.ads init");

        var data = NMTdata.data,
        adunitPrefixDomainMappings = new Array(),
        adunitPrefixURLMappings = new Array(),
        adunitPathMappings = new Array(),
        adunitURLMappings = new Array(),
        cccPathMappings = new Array(),
        cccURLMappings = new Array(),
        i = 0,
        mmo_ccc = '',
        mmo_console = '',
        pathlength = 0,
        maxAdunitPathLength = 3,  // limit the adunit path elements.
        dfp_adunit = '', // example: /news/local
		dfp_ccc = ''; // customTargeting value

////////////////////////////////////////////////////////////////////////
/***
 * mapping_version: 201310041128:443265
 */
dfp_adunit_prefix = '/11365842/savannahnow.com',
        adunitPrefixDomainMappings = [
                                      // These mappings will do a contains match against domain host.
                                      // MBU custom mappings
                                      {'autos\.coastalautos\.com': '/11365842/savannahnow.com/autos'},
                                      {'classifieds\.savannahnow\.com': '/11365842/savannahnow.com/classifieds'},
                                      {'class\.savannahnow\.com': '/11365842/savannahnow.com/classifieds'},
                                      {'dosavannah\.com': '/11365842/savannahnow.com/entertainment'},
                                      {'events\.savannahnow\.com': '/11365842/savannahnow.com/events'},
                                      {'giftguide\.': '/11365842/giftguide.savannahnow.com'},
                                      {'physicianguide\.': '/11365842/savannahnow.com/physicianguide'},
                                      {'^m\.savannahnow\.com': '/11365842/m.savannahnow.com'},
                                      {'^m\.pfsav': '/11365842/m.savannahnow.com'},
                                      {'jobs\.savannahnow\.com': '/11365842/savannahnow.com/jobs'},
                                      {'savannahhomesforsale\.com': '/11365842/savannahhomesforsale.com'},
                                      {'savannahmagazine\.com': '/11365842/savannahmagazine.com'},
                                      {'spotted\.savannahnow\.com': '/11365842/savannahnow.com/spotted'},
                                      {'rentals\.savannahnow\.com': '/11365842/savannahnow.com/rentals'},
                                      {'legacy\.com': '/11365842/savannahnow.com/obits'}
                              ];
        adunitURLMappings = [
                             // MBU custom mappings
					{'rentals\.savannahnow\.com': ''},
					{'events\.savannahnow\.com': ''}
                             // Common mappings
                      ];
        adunitPathMappings = [
                              // MBU custom mappings
                              {'^\/births$': '/lifestyle/births'},
                              {'^\/fast-physicianguide.php': '/physicianguide'},
                              // Common mappings
                              {'^\/homepageSMN$': '/homepage'},
                              {'^\/$': '/homepage'}
                      ];
        cccURLMappings = [
                          // MBU custom mappings
                          // Common mappings
                   ];
        cccPathMappings = [
                           // MBU custom mappings
                           // Common mappings
                           {'^\/homepageSMN$': 'homepage'},
                           {'^\/$': 'homepage'}
                   ];
////////////////////////////////////////////////////////////////////////

        // Process domain mapping for dfp_adunit_prefix
        dfp_adunit_prefix = data.processMapping(adunitPrefixDomainMappings, location.host, dfp_adunit_prefix);

        // Process URL mapping for dfp_adunit_prefix
        dfp_adunit_prefix = data.processMapping(adunitPrefixURLMappings, document.URL, dfp_adunit_prefix);

        // build dfp_adunit default value
        // Limit adunit to 3 path elements.
        pathlength = (data.pathnames.length > maxAdunitPathLength) ? maxAdunitPathLength : data.pathnames.length;
        for (i = 0; i < pathlength; i++) {
            if (data.pathnames[i] !== '') {
                dfp_adunit += '/' + data.pathnames[i];
            }
        }

        // Process Path mappings for dfp_adunit
        dfp_adunit = data.processMapping(adunitPathMappings, window.location.pathname, dfp_adunit);
        // Process URL mappings for dfp_adunit
        dfp_adunit = data.processMapping(adunitURLMappings, document.URL, dfp_adunit);

        // dfp_ccc default value
        // Paths that have trailing slash will leave the last path element empty.
        // Let's check for this case and go back one more for the ccc value.
        if (data.pathnames.length > 1 && data.pathnames[data.pathnames.length-1] == '') {
            dfp_ccc = data.pathnames[data.pathnames.length - 2];
        } else {
            dfp_ccc = data.pathnames[data.pathnames.length - 1];
        }

        // Process Path mappings for dfp_ccc
        dfp_ccc = data.processMapping(cccPathMappings, window.location.pathname, dfp_ccc);
        // Process URL mappings for dfp_ccc
        dfp_ccc = data.processMapping(cccURLMappings, document.URL, dfp_ccc);

        // override dfp_ccc with query_string mmo_ccc
        mmo_ccc = data.getQueryParam('mmo_ccc');
        if (mmo_ccc !== undefined) { dfp_ccc = mmo_ccc; }

        // Google DFP has 40 character limit on targeting values.
        dfp_ccc = dfp_ccc.slice(0,40);

        // output debug information to console
        mmo_console = data.getQueryParam('google_console');
        if (mmo_console !== undefined) {
            console.log("NMTdata.ads.dfp_adunit_prefix: "+dfp_adunit_prefix);
            console.log("NMTdata.ads.dfp_adunit: "+dfp_adunit);
            console.log("DFP AdUnit: "+dfp_adunit_prefix+dfp_adunit);
            console.log("NMTdata.ads.dfp_ccc: "+dfp_ccc);
        }

        // output debug information to console
        mmo_console = data.getQueryParam('nmt_console');
        if (mmo_console !== undefined) {
            document.write("
NMTdata.ads.dfp_adunit_prefix: "+dfp_adunit_prefix);
            document.write("
NMTdata.ads.dfp_adunit: "+dfp_adunit);
            document.write("
DFP AdUnit: "+dfp_adunit_prefix+dfp_adunit);
            document.write("
NMTdata.ads.dfp_ccc: "+data.escapeHtml(dfp_ccc));
        }

        return { // return object
            dfp_nmt_mapping_version: '201310041128:443265',
            dfp_nmt_ads_version: '201310022226:443263',
            dfp_adunit_prefix: dfp_adunit_prefix,
            dfp_adunit: dfp_adunit,
            dfp_ccc: data.escapeHtml(dfp_ccc)
        };

    }());

<|endoftext|>


<commit_before>(function() {
    'use strict';

    angular.module('brewApp.controllers')
        .controller('AddRecipeCtrl', ['$scope', 'AppState', 'BrewMaster', 'notifications', 'logger', 'UserStore', addRecipeController]);

    function addRecipeController($scope, AppState, BrewMaster, notifications, logger, UserStore) {
        /* jshint validthis: true */
        var self = this, appState = AppState.area('AddRecipe');

        self.INGREDIENT_TYPE = { HOPS: 'hops', MALT: 'malt', YEAST: 'yeast' };

        self.isIngredientsCollapsed = true;

        self.recipe = appState.recipe || {};

        self.ingredients = appState.ingredients || [];

        BrewMaster.getDefinitions().then(function(definitions) {
            self.units = definitions.units.volume.map(function(def) { return def.name; });

            if (BrewMaster.hasIngredient(definitions, 'yeast'))
                self.yeasts = definitions.ingredient.yeast.map(function(def) { return def.name; });
        });

        //Trying to do this generically but it kinda sucks.
        self.addIngredient = function(type, ingredient) {
            (self.ingredients[type] = self.ingredients[type] || []).push(ingredient);

            //This is pretty brittle.
            self.current[type] = null;
        };

        self.deleteIngredient = function(type, ingredient) {
            var index = self.ingredients[type]
                .map(function(i) { return i.name; })
                .indexOf(ingredient.name);

            self.ingredients[type].splice(index, 1);
        };

        self.isIngredientValid = function(ingredient) {
            return !(ingredient == null || ingredient.name == null || ingredient.name.trim() == '' ||
                (ingredient.volume != null && ingredient.units == null));
        };

        self.submit = function(isValid) {
            if (isValid) {
                self.recipe.ingredients = self.recipe.ingredients || [];

                for (var key in self.INGREDIENT_TYPE) {
                    if (!self.INGREDIENT_TYPE.hasOwnProperty(key)) continue;

                    var type = self.INGREDIENT_TYPE[key];

                    if (self.ingredients[type] && self.ingredients[type].length > 0)
                        for (var i = 0; i < self.ingredients[type].length; i++) {
                            var ingredient = self.ingredients[type][i];
                            ingredient.type = type;

                            self.recipe.ingredients.push(ingredient);
                        }
                }

                //Need a spinner on this?
                UserStore.saveRecipe(self.recipe).then(function(data) {
                    notifications.success('Recipe ' + self.recipe.name + ' saved.');
                    logger.info('Recipe ' + data.recipeId + ' saved.');

                    self.clear();
                });
            }
        };

        self.clear = function() {
            self.recipeForm.$setPristine();
            self.recipe = {};
            self.ingredients = [];

            appState.remove('recipe');
            appState.remove('ingredients');
        };

        self.ingredientsCollapse = function() {
            self.isIngredientsCollapsed = !self.isIngredientsCollapsed;
        };

        $scope.$on('$destroy', function() {
            //Only persist recipe if it exists and isn't empty.
            if (self.recipe && !angular.equals({}, self.recipe)) {
                appState.recipe = self.recipe;
                appState.ingredients = self.ingredients;
            }
        });
    }
})();<commit_msg>Ingredients expanded by default.<commit_after>(function() {
    'use strict';

    angular.module('brewApp.controllers')
        .controller('AddRecipeCtrl', ['$scope', 'AppState', 'BrewMaster', 'notifications', 'logger', 'UserStore', addRecipeController]);

    function addRecipeController($scope, AppState, BrewMaster, notifications, logger, UserStore) {
        /* jshint validthis: true */
        var self = this, appState = AppState.area('AddRecipe');

        self.INGREDIENT_TYPE = { HOPS: 'hops', MALT: 'malt', YEAST: 'yeast' };

        self.isIngredientsCollapsed = false;

        self.recipe = appState.recipe || {};

        self.ingredients = appState.ingredients || [];

        BrewMaster.getDefinitions().then(function(definitions) {
            self.units = definitions.units.volume.map(function(def) { return def.name; });

            if (BrewMaster.hasIngredient(definitions, 'yeast'))
                self.yeasts = definitions.ingredient.yeast.map(function(def) { return def.name; });
        });

        //Trying to do this generically but it kinda sucks.
        self.addIngredient = function(type, ingredient) {
            (self.ingredients[type] = self.ingredients[type] || []).push(ingredient);

            //This is pretty brittle.
            self.current[type] = null;
        };

        self.deleteIngredient = function(type, ingredient) {
            var index = self.ingredients[type]
                .map(function(i) { return i.name; })
                .indexOf(ingredient.name);

            self.ingredients[type].splice(index, 1);
        };

        self.isIngredientValid = function(ingredient) {
            return !(ingredient == null || ingredient.name == null || ingredient.name.trim() == '' ||
                (ingredient.volume != null && ingredient.units == null));
        };

        self.submit = function(isValid) {
            if (isValid) {
                self.recipe.ingredients = self.recipe.ingredients || [];

                for (var key in self.INGREDIENT_TYPE) {
                    if (!self.INGREDIENT_TYPE.hasOwnProperty(key)) continue;

                    var type = self.INGREDIENT_TYPE[key];

                    if (self.ingredients[type] && self.ingredients[type].length > 0)
                        for (var i = 0; i < self.ingredients[type].length; i++) {
                            var ingredient = self.ingredients[type][i];
                            ingredient.type = type;

                            self.recipe.ingredients.push(ingredient);
                        }
                }

                //Need a spinner on this?
                UserStore.saveRecipe(self.recipe).then(function(data) {
                    notifications.success('Recipe ' + self.recipe.name + ' saved.');
                    logger.info('Recipe ' + data.recipeId + ' saved.');

                    self.clear();
                });
            }
        };

        self.clear = function() {
            self.recipeForm.$setPristine();
            self.recipe = {};
            self.ingredients = [];

            appState.remove('recipe');
            appState.remove('ingredients');
        };

        self.ingredientsCollapse = function() {
            self.isIngredientsCollapsed = !self.isIngredientsCollapsed;
        };

        $scope.$on('$destroy', function() {
            //Only persist recipe if it exists and isn't empty.
            if (self.recipe && !angular.equals({}, self.recipe)) {
                appState.recipe = self.recipe;
                appState.ingredients = self.ingredients;
            }
        });
    }
})();
<|endoftext|>


<commit_before>
(function($) {
  $(window).load(function() {
    // generate buttons hidden
    $('.heading-first .logo').parent().append('Menü');
    $('#main-nav').prepend('close');
    $('#mobilemenu-icon').hide();
    $('#mobilemenu-close').hide();

    // initialize main-menu
    // setMainMenuActiveState();

    if (!$('body').hasClass('sevensome')) {
      $('#mobilemenu-icon').show();
      $('#mobilemenu-close').show();
    }

    $('#mobilemenu-icon,  #mobilemenu-close').click(function(e) {
      var mainMenu = $('#main-nav');
      var mainMenuWidth = mainMenu.innerWidth();
      $('ul ul', mainMenu).hide();
      if (mainMenu.is(':visible')) {
        $('body').animate({right: '0px'}, 300, function() {
          mainMenu.hide();
          $('body').removeClass('mobilemenu-open');
          // setMainMenuActiveState();
          // $('#mobilemenu-close').hide();
        });
      } else {
        // mainMenu.css({right: '-50000px'});
        setMainMenuMinHeight($('body').innerHeight());
        mainMenu.css({right: '-' + mainMenuWidth + 'px'}).show();
        $('body').animate({right: mainMenuWidth + 'px'}, 300, function() {
          $('body').addClass('mobilemenu-open');
          // setMainMenuActiveState();
        });
      }
      $(this).blur();
      e.preventDefault();
      return false;
    });

    // if the link clicked has a ul.menu sibling (i.e. a submenu)
    // we want to show the submenu
    $('html').on('click', '.mobilemenu-open #main-nav li a', function(e) {
      var ul = $(this).siblings('ul');
      var holder = $(this).siblings('.drop-holder');
      if (holder.length > 0) {
        ul = $('.drop-content > ul', holder);
      }
      if (ul.length > 0) {
        if (ul.is(':visible')) {
          ul.hide();
        } else {
          ul.show();
        }
        e.preventDefault();
        e.stopPropagation();
        return false;
      }
    });

    // reset the visibility when we hit the breakpoint 'desktop'
    $(window).resize(function() {
      var _mobilemenu = $.mobilemenu || {};

      var mainMenu = $('#main-nav');
      var mobileMenuClose = $('#mobilemenu-close');
      var mobileMenuIcon = $('#mobilemenu-icon');

      // % on padding will change the main-nav size --> recalc
      if($('body').hasClass('mobilemenu-open')) {
        var mainMenuWidth = mainMenu.innerWidth();
        $('body').css({right: mainMenuWidth + 'px'});
        mainMenu.css({right: '-' + mainMenuWidth + 'px'});
        setMainMenuMinHeight($('body').innerHeight());
      }

      // we switch from mobile/tablet to desktop
      if (!_mobilemenu.breakpoint_passed && $('body').hasClass('sevensome')) {
        $('body').css({right: '0px'});
        mainMenu.show();
        mobileMenuClose.hide();
        mobileMenuIcon.hide();
        mainMenu.css({minHeight: '0px'});
        $('body').removeClass('mobilemenu-open');
        // reset the display, i.e. remove the element style
        // otherwise the dropdown css in header.css will not work properly
        // after the mobilemenu was used
        $('ul ul', mainMenu).css('display', '');
        $('.drop-holder', mainMenu).css('display', '');

        _mobilemenu.breakpoint_passed = true;
      }
      // we switch from desktop to tablet/mobile
      if (_mobilemenu.breakpoint_passed && !$('body').hasClass('sevensome')) {
        mainMenu.hide();
        mobileMenuClose.show();
        mobileMenuIcon.show();
        $('body').css({right: '0px'});
        $('body').removeClass('mobilemenu-open');
        $('ul ul', mainMenu).show();
        _mobilemenu.breakpoint_passed = false;
      }

      $.mobilemenu = _mobilemenu;
    });

    function setMainMenuActiveState() {
      var mobileMenu = $('#main-nav');
      var mainMenu = $('#main-nav');
      var mainMenuIcon = $('#mobilemenu-icon');
      if(mobileMenu.is(':visible')) {
        mainMenuIcon.addClass('active');
      } else {
        mainMenuIcon.removeClass('active');
      }
    }

    function setMainMenuMinHeight(pixel) {
      var mainMenu = $('#main-nav');
      var paddingTop = parseInt(mainMenu.css('padding-top'), 10);
      var paddingBottom = parseInt(mainMenu.css('padding-bottom'), 10);
      mainMenu.css({minHeight: (pixel - paddingTop - paddingBottom) + 'px', maxHeight: (pixel - paddingTop - paddingBottom) + 'px', overflow: 'auto'});
    }
  });
})(jQuery);
<commit_msg>fix menu title, fix mobile menu initialization<commit_after>
(function($) {
  $(window).load(function() {
    // generate buttons hidden
    $('.heading-first .logo').parent().append('Menu');
    $('#main-nav').prepend('close');
    $('#mobilemenu-icon').hide();
    $('#mobilemenu-close').hide();

    // initialize main-menu
    // setMainMenuActiveState();

    if (!$('body').hasClass('sevensome')) {
      $('#mobilemenu-icon').show();
      $('#mobilemenu-close').show();
    }

    $('#mobilemenu-icon,  #mobilemenu-close').click(function(e) {
      var mainMenu = $('#main-nav');
      var mainMenuWidth = mainMenu.innerWidth();
      $('ul ul', mainMenu).hide();
      if (mainMenu.is(':visible')) {
        $('body').animate({right: '0px'}, 300, function() {
          mainMenu.hide();
          $('body').removeClass('mobilemenu-open');
          // setMainMenuActiveState();
          // $('#mobilemenu-close').hide();
        });
      } else {
        // mainMenu.css({right: '-50000px'});
        setMainMenuMinHeight($('body').innerHeight());
        mainMenu.css({right: '-' + mainMenuWidth + 'px'}).show();
        $('body').animate({right: mainMenuWidth + 'px'}, 300, function() {
          $('body').addClass('mobilemenu-open');
          // setMainMenuActiveState();
        });
      }
      $(this).blur();
      e.preventDefault();
      return false;
    });

    // if the link clicked has a ul.menu sibling (i.e. a submenu)
    // we want to show the submenu
    $('html').on('click', '.mobilemenu-open #main-nav li a', function(e) {
      var ul = $(this).siblings('ul');
      var holder = $(this).siblings('.drop-holder');
      if (holder.length > 0) {
        ul = $('.drop-content > ul', holder);
      }
      if (ul.length > 0) {
        if (ul.is(':visible')) {
          ul.hide();
        } else {
          ul.show();
        }
        e.preventDefault();
        e.stopPropagation();
        return false;
      }
    });

    // reset the visibility when we hit the breakpoint 'desktop'
    $(window).resize(function() {
      var _mobilemenu = $.mobilemenu || {};

      var mainMenu = $('#main-nav');
      var mobileMenuClose = $('#mobilemenu-close');
      var mobileMenuIcon = $('#mobilemenu-icon');

      // % on padding will change the main-nav size --> recalc
      if($('body').hasClass('mobilemenu-open')) {
        var mainMenuWidth = mainMenu.innerWidth();
        $('body').css({right: mainMenuWidth + 'px'});
        mainMenu.css({right: '-' + mainMenuWidth + 'px'});
        setMainMenuMinHeight($('body').innerHeight());
      }

      // make sure icon and close button are visible when on mobile
      if (!_mobilemenu.breakpoint_passed && !$('body').hasClass('sevensome')) {
        mobileMenuIcon.show();
        mobileMenuClose.show();
      }
      // we switch from mobile/tablet to desktop
      if (!_mobilemenu.breakpoint_passed && $('body').hasClass('sevensome')) {
        $('body').css({right: '0px'});
        mainMenu.show();
        mobileMenuClose.hide();
        mobileMenuIcon.hide();
        mainMenu.css({minHeight: '0px'});
        $('body').removeClass('mobilemenu-open');
        // reset the display, i.e. remove the element style
        // otherwise the dropdown css in header.css will not work properly
        // after the mobilemenu was used
        $('ul ul', mainMenu).css('display', '');
        $('.drop-holder', mainMenu).css('display', '');

        _mobilemenu.breakpoint_passed = true;
      }
      // we switch from desktop to tablet/mobile
      if (_mobilemenu.breakpoint_passed && !$('body').hasClass('sevensome')) {
        mainMenu.hide();
        mobileMenuClose.show();
        mobileMenuIcon.show();
        $('body').css({right: '0px'});
        $('body').removeClass('mobilemenu-open');
        $('ul ul', mainMenu).show();
        _mobilemenu.breakpoint_passed = false;
      }

      $.mobilemenu = _mobilemenu;
    });

    function setMainMenuActiveState() {
      var mobileMenu = $('#main-nav');
      var mainMenu = $('#main-nav');
      var mainMenuIcon = $('#mobilemenu-icon');
      if(mobileMenu.is(':visible')) {
        mainMenuIcon.addClass('active');
      } else {
        mainMenuIcon.removeClass('active');
      }
    }

    function setMainMenuMinHeight(pixel) {
      var mainMenu = $('#main-nav');
      var paddingTop = parseInt(mainMenu.css('padding-top'), 10);
      var paddingBottom = parseInt(mainMenu.css('padding-bottom'), 10);
      mainMenu.css({minHeight: (pixel - paddingTop - paddingBottom) + 'px', maxHeight: (pixel - paddingTop - paddingBottom) + 'px', overflow: 'auto'});
    }
  });
})(jQuery);
<|endoftext|>


<commit_before>cdb.admin.overlays.Text = cdb.core.View.extend({

  className: "text overlay",

  template_name: 'table/views/overlays/text',

  events: {

    "mouseup":           "_onMouseUp",
    "mousedown":         "_onMouseDown",

    "mouseenter .text":  "_onMouseEnter",
    "mouseleave .text":  "_onMouseLeave",

    "click .close":      "_close",
    "click .content":    "_click",
    "dblclick .content": "_dblClick",

    "keyup .text":       "_onKeyUp",
    "click .text":       "_killEvent",
    "paste .text":       "_onPaste"

  },

  initialize: function() {

    _.bindAll(this, "_click", "_close", "_dblClick", "_onChangeMode", "_onKeyDown", "_putOnTop");

    // Bind ESC key
    $(document).bind('keydown', this._onKeyDown);

    this.template = this.getTemplate(this.template_name);

    this._setupModels();

  },

  _killEvent: function(e) {

    e && e.preventDefault();
    e && e.stopPropagation();

  },

  // Setup the internal and custom model
  _setupModels: function() {

    var extra = this.model.get("extra");

    this.model.set({ text: extra.text }, { silent: true });

    // Binding
    this.model.bind('change:text',    this._setText,    this);
    this.model.bind('change:style',   this._applyStyle, this);
    this.model.bind('change:display', this._onChangeDisplay, this);
    this.model.bind('change:extra',   this._onChangeExtra, this);

    // Internal model to store the editing state
    this.editModel = new cdb.core.Model({ mode: "" });
    this.editModel.bind('change:mode', this._onChangeMode, this);

    this.add_related_model(this.editModel);

  },

  // Element events
  _onKeyUp: function(e) {

    var self = this;

    var temp      = "";
    var domString = "";

    if (this.timeout)       clearTimeout(this.timeout);
    if (this.keyUpTimeout)  clearTimeout(this.keyUpTimeout);
    if (this.savingTimeout) clearTimeout(this.savingTimeout);

    var value = this.$text.html();

    if (cdb.Utils.stripHTML(value) == "") {

      this.keyUpTimeout = setTimeout(function() {

        self.model.set({ text: "" }, { silent: true });
        self._close();

      }, 600);

    } else {

      this.model.set({ text: value }, { silent: true });

      if (!this.$el.hasClass("hover") && this.$text.text()) {
        this.savingTimeout = setTimeout(function() {

          self._disableEditingMode();

        }, 500);
      }
    }

  },

  _dblClick: function(e) {

    this._killEvent(e);

  },

  _click: function(e) {

    this._killEvent(e);

    this._putOnTop();

    var isLink = e.target.hasAttribute("href");

    if (!isLink) {
      cdb.god.trigger("closeDialogs");
    }

  },

  _onKeyDown: function(e) {

    if (e.keyCode === 27) {
      this.editModel.set("mode", "");
    }

  },

  _onPaste: function(e) {

    var self = this;

    setTimeout(function() {

      var text = cdb.Utils.stripHTML(self.model.get("text"));
      self.model.set("text", text)

    }, 200);

  },

  _onMouseUp: function(e) {

    // Prevents entering in the edit mode when clicking the edit button
    if (!$(e.target).parents(".overlay_text").length && !$(e.target).hasClass("overlay_text")) {
      this._savePosition(false);
      return;
    }

    cdb.god.trigger("closeDialogs");

    this._savePosition(true);

  },

  _savePosition: function(editable) {

    var extra = this.model.get("extra");

    var x     = this.model.get("x");
    var y     = this.model.get("y");

    var oldX  = this.$el.position().left;
    var oldY  = this.$el.position().top;

    var portraitDirection  = extra.portraitDirection;
    var landscapeDirection = extra.landscapeDirection;

    if (y == 0 && portraitDirection == "bottom") oldY = y;
    if (x == 0 && landscapeDirection == "right") oldX = x;

    if (editable) {
      // If we didn't move the overlay
      if (oldX === x && y === oldY || x == 0 && landscapeDirection == "right" && y === oldY || y == 0 && portraitDirection == "bottom" && oldX === x) {

        this.dropdown.hide();
        this.editModel.set("mode", "editable");

        return;

      }
    }

    var x = this.$el.position().left;
    var y = this.$el.position().top;

    var r = $(".cartodb-map").width()  - x;
    var b = $(".cartodb-map").height() - y;

    // Default positions
    extra.portraitDirection  = "top";
    extra.landscapeDirection = "left";

    var pLeft = (x + (this.$el.width()/2)) / $(".cartodb-map").width() * 100;
    var pTop  = (y + (this.$el.height()/2)) / $(".cartodb-map").height() * 100;

    var landscapeDominantSide = x <= r ? "left" : "right";
    var portraitDominantSide =  y <= b ? "top" : "bottom";

    //console.log("pLeft: " + pLeft, "ptop: " + pTop, "landscapeDominantSide" + landscapeDominantSide, "portraitDominantSide" + portraitDominantSide);

    //console.log("x: " + x, + "y: " + y, "right" + r, "bottom" + b)

    extra.landscapeDominantSide = landscapeDominantSide;
    extra.portraitDominantSide  = portraitDominantSide;

    extra.pTop  = pTop;
    extra.pLeft = pLeft;

    extra.r = r;
    extra.b = b;

    extra.width = this.$el.width();

    //console.log(extra.width)

    // Let's see where did we put our overlay…
    var rightSticked  = x + this.$el.width()  >= $(".cartodb-map").width();
    var bottomSticked = y + this.$el.height() >= $(".cartodb-map").height();

    if (bottomSticked) { // we sticked the overlay to the bottom of the screen

      y = 0;

      this.$el.css({ bottom: 0, top: "auto" });

      extra.portraitDirection = "bottom";

    }

    if (rightSticked) { // we sticked the overlay to the right side

      x = 0;

      this.$el.css({ right: 0, left: "auto" });

      extra.landscapeDirection = "right";

    }

    this.model.set({ extra: extra }, { silent: true});
    this.model.set({ x: x, y: y });

    this.model.save();

  },

  _onMouseDown: function() { },

  _onMouseEnter: function() {

    this.$el.addClass("hover");
    if (this.editModel.get("mode") == "editable") {
      if (this.timeout) clearTimeout(this.timeout);
    }

  },

  _onMouseLeave: function() {
    this.$el.removeClass("hover");

    var self = this;

    if (this.editModel.get("mode") == "editable") {

      this.timeout = setTimeout(function() {

        self.editModel.set("mode", "");

      }, 250);
    }

  },

  show: function(animated) {

    this.$el.show();

    if (true) this.$el.addClass('animated bounceIn');

  },

  hide: function(callback) {

    var self = this;

    this.$el
    .removeClass('animated bounceIn')
    .addClass('animated bounceOut');

    callback && callback();

    // Give it some time to complete the animation
    setTimeout(function() {
      self.clean();
    }, 550);

  },

  _close: function(e) {

    this._killEvent(e);

    var self = this;

    this.dropdown.hide();
    this.dropdown.clean();

    this.hide(function() {
      self.trigger("remove", self);
    });

  },

  _onChangeDisplay: function() {

    var display = this.model.get("display");

    if (display) {
      this.$el.removeClass("disabled");
      this.show();
    } else {
      this.$el.addClass("disabled");
      this.$el.hide();
    }

  },

  _onChangeExtra: function() {

    var extra  = this.model.get("extra");
    extra.text = this.model.get("text");

    this.model.set({ extra: extra }, { silent: true });

  },

  /*
   * Applies style to the content of the widget
   */

  _applyStyle: function() {

    var style      = this.model.get("style");

    var boxColor   = style["box-color"];
    var boxOpacity = style["box-opacity"];
    var boxWidth   = style["box-width"];
    var fontFamily = style["font-family-name"];

    this.$text.css(style);
    this.$text.css("font-size", style["font-size"] + "px");

    var rgbaCol = 'rgba(' + parseInt(boxColor.slice(-6,-4),16)
    + ',' + parseInt(boxColor.slice(-4,-2),16)
    + ',' + parseInt(boxColor.slice(-2),16)
    +', ' + boxOpacity + ' )';

    this.$el.css("background-color", rgbaCol);
    this.$el.css("max-width", boxWidth);

    var fontFamilyClass = "";

    if      (fontFamily  == "Droid Sans") fontFamilyClass = "droid";
    else if (fontFamily  == "Vollkorn")   fontFamilyClass = "vollkorn";
    else if (fontFamily  == "Open Sans")  fontFamilyClass = "open_sans";
    else if (fontFamily  == "Roboto")     fontFamilyClass = "roboto";

    this.$el.css("width", "auto");

    this.$el
    .removeClass("droid")
    .removeClass("vollkorn")
    .removeClass("roboto")
    .removeClass("open_sans");

    this.$el.addClass(fontFamilyClass);

  },

  _onChangeMode: function() {

    var mode = this.editModel.get("mode");

    if (mode == "editable") {

      this._enableEditingMode();

    } else {

      this._disableEditingMode();

    }

  },

  _enableEditingMode: function() {

    var text = this.model.get("text");

    this.$el
    .addClass("editable")
    .addClass("disabled");

    this.$text.attr("contenteditable", true).focus();

    this.$el.css("width", "auto");

    var self = this;

    setTimeout(function() {
      self.$text.html(text);
      self.$el.css("max-width", self.$el.width() + 20);
      self.$el.css("width", self.$el.width() + 20);
    }, 100)

  },

  _disableEditingMode: function() {

    var text = this._transformToMarkdown(this.model.get("text"));

    this.editModel.set("mode", "");

    if (text) {

      this.$text.html(text);

      this.$el
      .removeClass("editable")
      .removeClass("disabled");

      this.$text.attr("contenteditable", false);
      this.$el.css("width", "auto");

      var self = this;

      setTimeout(function() {

        var width = self.$el.width();
        var extra = self.model.get("extra");

        extra.width = width;
        self.model.set({ extra: extra }, { silent: true});
        self.model.save();

        self.$el.css("width", width);

      }, 400);

      //this.model.save();

    }

  },

  _setText: function() {

    var text          = this.model.get("text");
    var rendered_text = this._transformToMarkdown(text);

    var extra = this.model.get("extra");

    extra.text          = text;
    extra.rendered_text = rendered_text

    this.model.set({ extra: extra }, { silent: true });

    if (rendered_text) this.$text.html(rendered_text);

  },

  _transformToMarkdown: function(text) {

    text = markdown.toHTML(text)

    text = text.replace(/</g, "<");
    text = text.replace(/>/g, ">");
    text = text.replace(/<p>/g, "");
    text = text.replace(/&nbsp;/g, " ");
    text = text.replace(/<\/p>/g, "");

    return text;

  },

  _addDropdown: function() {

    var device            = this.model.get("device");
    var horizontal_offset = 26;
    var vertical_offset   = 24;

    if (device === "mobile") {
      vertical_offset -= $("header").height() + 12;
    }

    this.dropdown = new cdb.admin.WidgetPropertiesDropdown({
      tick: "left",
      target: this.$el.find(".edit"),
      model: this.model,
      offset_mode: device === 'mobile' ? "offset" : "position",
      horizontal_position: "left",
      horizontal_offset: horizontal_offset,
      vertical_offset: vertical_offset,
      template_base: 'table/views/overlays/properties_dropdown',
      form_data: [{
        name: 'Text Align',
        form: {
          'text-align':      { type: 'text_align', value: 'left' },
        }
      }, {
        name: 'Text Style',
        form: {
          'font-size':  { type: 'simple_number', value: 12, min: 5, max: 50, inc: 2 },
          'color':      { type: 'color', value: '#FFF' },
        }

      }, {
        name: 'Font',
        form: {
          'font-family-name': {
            type: 'select',
            value: "Helvetica",
            extra: ["Helvetica", "Droid Sans", "Vollkorn", "Roboto", "Open Sans"]
          }
        }
      }, {

        name: 'Box Style',
        form: {
          'box-color':  { type: 'color', value: '#000' },
          'box-opacity':  { type: 'simple_opacity', value: .7, min:0, max:1, inc: .1 },
        }

      }, {
        name: 'Max Width',
        form: {
          'box-width':  { type: 'simple_number', value: 300, min: 50, max: 2000, inc: 10 },
        }

      }]
    });

    this._bindDropdown();

    var self = this;

    this.dropdown.on("saved", function() {
      self.dropdown.move(true);
    });

    if (this.model.get("device") == "mobile") {
      $(".map").append(this.dropdown.render().el);
    } else {
      this.$el.append(this.dropdown.render().el);
    }

  },

  _bindDropdown: function() {

    this.dropdown.bind("onDropdownShown", function() {
      this.$el.addClass("open");

      this._putOnTop();

    }, this);

    this.dropdown.bind("onDropdownHidden", function() {
      this.$el.removeClass("open");
    }, this);

    cdb.god.bind("closeDialogs", this.dropdown.hide, this.dropdown);

  },

  _putOnTop: function() {

    $(".overlay").css("z-index", 999);
    this.$el.css("z-index", 2001);

  },

  _place: function() {

    var landscapeDirection = this.model.get("extra").landscapeDirection;
    var portraitDirection  = this.model.get("extra").portraitDirection;

    if (portraitDirection == 'bottom') {

      this.$el.offset({
        top: this.model.get("y")
      });

      this.$el.css({
        top: "auto",
        bottom: this.model.get("y")
      });

    } else {
      this.$el.offset({
        top: this.model.get("y")
      });
    }

    if (landscapeDirection == 'right') {

      this.$el.offset({
        left: this.model.get("x")
      });

      this.$el.css({
        left: "auto",
        right: this.model.get("x")
      });

    } else {
      this.$el.offset({
        left: this.model.get("x")
      });
    }

  },

  render: function() {

    this._place();

    this.$el.append(this.template(this.model.attributes));

    this.$text = this.$el.find(".content div.text");

    var text = this._transformToMarkdown(this.model.get("text"));

    this.$text.html(text);

    this._applyStyle();
    this._onChangeExtra();
    this._addDropdown();

    this.$el.addClass(this.model.get("device"));

    return this;

  }

});
<commit_msg>fixes a bug that prevented moving some overlays<commit_after>cdb.admin.overlays.Text = cdb.core.View.extend({

  className: "text overlay",

  template_name: 'table/views/overlays/text',

  events: {

    "mouseup":           "_onMouseUp",
    "mousedown":         "_onMouseDown",

    "mouseenter .text":  "_onMouseEnter",
    "mouseleave .text":  "_onMouseLeave",

    "click .close":      "_close",
    "click .content":    "_click",
    "dblclick .content": "_dblClick",

    "keyup .text":       "_onKeyUp",
    "click .text":       "_killEvent",
    "paste .text":       "_onPaste"

  },

  initialize: function() {

    _.bindAll(this, "_click", "_close", "_dblClick", "_onChangeMode", "_onKeyDown", "_putOnTop");

    // Bind ESC key
    $(document).bind('keydown', this._onKeyDown);

    this.template = this.getTemplate(this.template_name);

    this._setupModels();

  },

  _killEvent: function(e) {

    e && e.preventDefault();
    e && e.stopPropagation();

  },

  // Setup the internal and custom model
  _setupModels: function() {

    var extra = this.model.get("extra");

    this.model.set({ text: extra.text }, { silent: true });

    // Binding
    this.model.bind('change:text',    this._setText,    this);
    this.model.bind('change:style',   this._applyStyle, this);
    this.model.bind('change:display', this._onChangeDisplay, this);
    this.model.bind('change:extra',   this._onChangeExtra, this);

    // Internal model to store the editing state
    this.editModel = new cdb.core.Model({ mode: "" });
    this.editModel.bind('change:mode', this._onChangeMode, this);

    this.add_related_model(this.editModel);

  },

  // Element events
  _onKeyUp: function(e) {

    var self = this;

    var temp      = "";
    var domString = "";

    if (this.timeout)       clearTimeout(this.timeout);
    if (this.keyUpTimeout)  clearTimeout(this.keyUpTimeout);
    if (this.savingTimeout) clearTimeout(this.savingTimeout);

    var value = this.$text.html();

    if (cdb.Utils.stripHTML(value) == "") {

      this.keyUpTimeout = setTimeout(function() {

        self.model.set({ text: "" }, { silent: true });
        self._close();

      }, 600);

    } else {

      this.model.set({ text: value }, { silent: true });

      if (!this.$el.hasClass("hover") && this.$text.text()) {
        this.savingTimeout = setTimeout(function() {

          self._disableEditingMode();

        }, 500);
      }
    }

  },

  _dblClick: function(e) {

    this._killEvent(e);

  },

  _click: function(e) {

    this._killEvent(e);

    this._putOnTop();

    var isLink = e.target.hasAttribute("href");

    if (!isLink) {
      cdb.god.trigger("closeDialogs");
    }

  },

  _onKeyDown: function(e) {

    if (e.keyCode === 27) {
      this.editModel.set("mode", "");
    }

  },

  _onPaste: function(e) {

    var self = this;

    setTimeout(function() {

      var text = cdb.Utils.stripHTML(self.model.get("text"));
      self.model.set("text", text)

    }, 200);

  },

  _onMouseUp: function(e) {

    // Prevents entering in the edit mode when clicking the edit button
    if (!$(e.target).parents(".overlay_text").length && !$(e.target).hasClass("overlay_text")) {
      this._savePosition(false);
      return;
    }

    cdb.god.trigger("closeDialogs");

    this._savePosition(true);

  },

  _savePosition: function(editable) {

    var extra = this.model.get("extra");

    var x     = this.model.get("x");
    var y     = this.model.get("y");

    var oldX  = this.$el.position().left;
    var oldY  = this.$el.position().top;

    var portraitDirection  = extra.portraitDirection;
    var landscapeDirection = extra.landscapeDirection;

    if (y == 0 && portraitDirection == "bottom") oldY = y;
    if (x == 0 && landscapeDirection == "right") oldX = x;

    if (editable) {
      // If we didn't move the overlay
      if (oldX === x && y === oldY || x == 0 && landscapeDirection == "right" && y === oldY || y == 0 && portraitDirection == "bottom" && oldX === x) {

        this.dropdown.hide();
        this.editModel.set("mode", "editable");

        return;

      }
    }

    var x = this.$el.position().left;
    var y = this.$el.position().top;

    var r = $(".cartodb-map").width()  - x;
    var b = $(".cartodb-map").height() - y;

    // Default positions
    extra.portraitDirection  = "top";
    extra.landscapeDirection = "left";

    var pLeft = (x + (this.$el.width()/2)) / $(".cartodb-map").width() * 100;
    var pTop  = (y + (this.$el.height()/2)) / $(".cartodb-map").height() * 100;

    var landscapeDominantSide = x <= r ? "left" : "right";
    var portraitDominantSide =  y <= b ? "top" : "bottom";

    //console.log("pLeft: " + pLeft, "ptop: " + pTop, "landscapeDominantSide" + landscapeDominantSide, "portraitDominantSide" + portraitDominantSide);

    //console.log("x: " + x, + "y: " + y, "right" + r, "bottom" + b)

    extra.landscapeDominantSide = landscapeDominantSide;
    extra.portraitDominantSide  = portraitDominantSide;

    extra.pTop  = pTop;
    extra.pLeft = pLeft;

    extra.r = r;
    extra.b = b;

    extra.width = this.$el.width();

    //console.log(extra.width)

    // Let's see where did we put our overlay…
    var rightSticked  = x + this.$el.width()  >= $(".cartodb-map").width();
    var bottomSticked = y + this.$el.height() >= $(".cartodb-map").height();

    if (bottomSticked) { // we sticked the overlay to the bottom of the screen

      y = 0;

      this.$el.css({ bottom: 0, top: "auto" });

      extra.portraitDirection = "bottom";

    }

    if (rightSticked) { // we sticked the overlay to the right side

      x = 0;

      this.$el.css({ right: 0, left: "auto" });

      extra.landscapeDirection = "right";

    }

    this.model.set({ extra: extra }, { silent: true});
    this.model.set({ x: x, y: y });

    this.model.save();

  },

  _onMouseDown: function() { },

  _onMouseEnter: function() {

    this.$el.addClass("hover");
    if (this.editModel.get("mode") == "editable") {
      if (this.timeout) clearTimeout(this.timeout);
    }

  },

  _onMouseLeave: function() {
    this.$el.removeClass("hover");

    var self = this;

    if (this.editModel.get("mode") == "editable") {

      this.timeout = setTimeout(function() {

        self.editModel.set("mode", "");

      }, 250);
    }

  },

  show: function(animated) {

    this.$el.show();

    if (true) this.$el.addClass('animated bounceIn');

  },

  hide: function(callback) {

    var self = this;

    this.$el
    .removeClass('animated bounceIn')
    .addClass('animated bounceOut');

    callback && callback();

    // Give it some time to complete the animation
    setTimeout(function() {
      self.clean();
    }, 550);

  },

  _close: function(e) {

    this._killEvent(e);

    var self = this;

    this.dropdown.hide();
    this.dropdown.clean();

    this.hide(function() {
      self.trigger("remove", self);
    });

  },

  _onChangeDisplay: function() {

    var display = this.model.get("display");

    if (display) {
      this.show();
    } else {
      this.$el.hide();
    }

  },

  _onChangeExtra: function() {

    var extra  = this.model.get("extra");
    extra.text = this.model.get("text");

    this.model.set({ extra: extra }, { silent: true });

  },

  /*
   * Applies style to the content of the widget
   */

  _applyStyle: function() {

    var style      = this.model.get("style");

    var boxColor   = style["box-color"];
    var boxOpacity = style["box-opacity"];
    var boxWidth   = style["box-width"];
    var fontFamily = style["font-family-name"];

    this.$text.css(style);
    this.$text.css("font-size", style["font-size"] + "px");

    var rgbaCol = 'rgba(' + parseInt(boxColor.slice(-6,-4),16)
    + ',' + parseInt(boxColor.slice(-4,-2),16)
    + ',' + parseInt(boxColor.slice(-2),16)
    +', ' + boxOpacity + ' )';

    this.$el.css("background-color", rgbaCol);
    this.$el.css("max-width", boxWidth);

    var fontFamilyClass = "";

    if      (fontFamily  == "Droid Sans") fontFamilyClass = "droid";
    else if (fontFamily  == "Vollkorn")   fontFamilyClass = "vollkorn";
    else if (fontFamily  == "Open Sans")  fontFamilyClass = "open_sans";
    else if (fontFamily  == "Roboto")     fontFamilyClass = "roboto";

    this.$el.css("width", "auto");

    this.$el
    .removeClass("droid")
    .removeClass("vollkorn")
    .removeClass("roboto")
    .removeClass("open_sans");

    this.$el.addClass(fontFamilyClass);

  },

  _onChangeMode: function() {

    var mode = this.editModel.get("mode");

    if (mode == "editable") {

      this._enableEditingMode();

    } else {

      this._disableEditingMode();

    }

  },

  _enableEditingMode: function() {

    var text = this.model.get("text");

    this.$el
    .addClass("editable")
    .addClass("disabled");

    this.$text.attr("contenteditable", true).focus();

    this.$el.css("width", "auto");

    var self = this;

    setTimeout(function() {
      self.$text.html(text);
      self.$el.css("max-width", self.$el.width() + 20);
      self.$el.css("width", self.$el.width() + 20);
    }, 100)

  },

  _disableEditingMode: function() {

    var text = this._transformToMarkdown(this.model.get("text"));

    this.editModel.set("mode", "");

    if (text) {

      this.$text.html(text);

      this.$el
      .removeClass("editable")
      .removeClass("disabled");

      this.$text.attr("contenteditable", false);
      this.$el.css("width", "auto");

      var self = this;

      setTimeout(function() {

        var width = self.$el.width();
        var extra = self.model.get("extra");

        extra.width = width;
        self.model.set({ extra: extra }, { silent: true});
        self.model.save();

        self.$el.css("width", width);

      }, 400);

      //this.model.save();

    }

  },

  _setText: function() {

    var text          = this.model.get("text");
    var rendered_text = this._transformToMarkdown(text);

    var extra = this.model.get("extra");

    extra.text          = text;
    extra.rendered_text = rendered_text

    this.model.set({ extra: extra }, { silent: true });

    if (rendered_text) this.$text.html(rendered_text);

  },

  _transformToMarkdown: function(text) {

    text = markdown.toHTML(text)

    text = text.replace(/</g, "<");
    text = text.replace(/>/g, ">");
    text = text.replace(/<p>/g, "");
    text = text.replace(/&nbsp;/g, " ");
    text = text.replace(/<\/p>/g, "");

    return text;

  },

  _addDropdown: function() {

    var device            = this.model.get("device");
    var horizontal_offset = 26;
    var vertical_offset   = 24;

    if (device === "mobile") {
      vertical_offset -= $("header").height() + 12;
    }

    this.dropdown = new cdb.admin.WidgetPropertiesDropdown({
      tick: "left",
      target: this.$el.find(".edit"),
      model: this.model,
      offset_mode: device === 'mobile' ? "offset" : "position",
      horizontal_position: "left",
      horizontal_offset: horizontal_offset,
      vertical_offset: vertical_offset,
      template_base: 'table/views/overlays/properties_dropdown',
      form_data: [{
        name: 'Text Align',
        form: {
          'text-align':      { type: 'text_align', value: 'left' },
        }
      }, {
        name: 'Text Style',
        form: {
          'font-size':  { type: 'simple_number', value: 12, min: 5, max: 50, inc: 2 },
          'color':      { type: 'color', value: '#FFF' },
        }

      }, {
        name: 'Font',
        form: {
          'font-family-name': {
            type: 'select',
            value: "Helvetica",
            extra: ["Helvetica", "Droid Sans", "Vollkorn", "Roboto", "Open Sans"]
          }
        }
      }, {

        name: 'Box Style',
        form: {
          'box-color':  { type: 'color', value: '#000' },
          'box-opacity':  { type: 'simple_opacity', value: .7, min:0, max:1, inc: .1 },
        }

      }, {
        name: 'Max Width',
        form: {
          'box-width':  { type: 'simple_number', value: 300, min: 50, max: 2000, inc: 10 },
        }

      }]
    });

    this._bindDropdown();

    var self = this;

    this.dropdown.on("saved", function() {
      self.dropdown.move(true);
    });

    if (this.model.get("device") == "mobile") {
      $(".map").append(this.dropdown.render().el);
    } else {
      this.$el.append(this.dropdown.render().el);
    }

  },

  _bindDropdown: function() {

    this.dropdown.bind("onDropdownShown", function() {
      this.$el.addClass("open");

      this._putOnTop();

    }, this);

    this.dropdown.bind("onDropdownHidden", function() {
      this.$el.removeClass("open");
    }, this);

    cdb.god.bind("closeDialogs", this.dropdown.hide, this.dropdown);

  },

  _putOnTop: function() {

    $(".overlay").css("z-index", 999);
    this.$el.css("z-index", 2001);

  },

  _place: function() {

    var landscapeDirection = this.model.get("extra").landscapeDirection;
    var portraitDirection  = this.model.get("extra").portraitDirection;

    if (portraitDirection == 'bottom') {

      this.$el.offset({
        top: this.model.get("y")
      });

      this.$el.css({
        top: "auto",
        bottom: this.model.get("y")
      });

    } else {
      this.$el.offset({
        top: this.model.get("y")
      });
    }

    if (landscapeDirection == 'right') {

      this.$el.offset({
        left: this.model.get("x")
      });

      this.$el.css({
        left: "auto",
        right: this.model.get("x")
      });

    } else {
      this.$el.offset({
        left: this.model.get("x")
      });
    }

  },

  render: function() {

    this._place();

    this.$el.append(this.template(this.model.attributes));

    this.$text = this.$el.find(".content div.text");

    var text = this._transformToMarkdown(this.model.get("text"));

    this.$text.html(text);

    this._applyStyle();
    this._onChangeExtra();
    this._addDropdown();

    this.$el.addClass(this.model.get("device"));

    return this;

  }

});
<|endoftext|>


<commit_before>const moment = require('moment');

const queries = require('../db/queries');
const { db, logger } = require('../utils');

const modelAttributes = ['id', 'performanceId', 'spotId', 'firstNameNumber', 'secondNameNumber', 'firstComments', 'secondComments', 'winnerId', 'pending', 'needsApproval'];
const alternateVersusRegular = result =>
  (!result.userOneAlternate && result.userTwoAlternate) ||
  (result.userOneAlternate && !result.userTwoAlternate);
const alternateVersusAlternate = result =>
  result.userOneAlternate && result.userTwoAlternate;
const regularVersusRegular = result =>
  !result.userOneAlternate && !result.userTwoAlternate;

/*
  We need a special sorting for results when we're switching user spots based on results
  - Regular VS Alternate
  - Alternate VS Alternate
  - Regular VS Regular
  This grossness does that
*/
const resultsSort = (a, b) => { // eslint-disable-line consistent-return, array-callback-return
  if (alternateVersusRegular(a) && alternateVersusRegular(b)) return 0;
  if (alternateVersusRegular(a) && alternateVersusAlternate(b)) return -1;
  if (alternateVersusRegular(a) && regularVersusRegular(b)) return -1;
  if (alternateVersusAlternate(a) && alternateVersusRegular(b)) return 1;
  if (alternateVersusAlternate(a) && alternateVersusAlternate(b)) return 0;
  if (alternateVersusAlternate(a) && regularVersusRegular(b)) return -1;
  if (regularVersusRegular(a) && alternateVersusRegular(b)) return 1;
  if (regularVersusRegular(a) && alternateVersusAlternate(b)) return 1;
  if (regularVersusRegular(a) && regularVersusRegular(b)) return 0;
};

class Result {

  constructor(id, fC, fNN, nA, opponentName, pending, perfDate, perfName, perfId, sC, sNN, spotId, uOA, uTA, winnerId) {
    this._id = id;
    this._firstComments = fC;
    this._firstNameNumber = fNN;
    this._needsApproval = nA;
    this._opponentName = opponentName;
    this._pending = pending;
    this._performanceDate = new Date(perfDate);
    this._performanceName = perfName;
    this._performanceId = perfId;
    this._secondComments = sC;
    this._secondNameNumber = sNN;
    this._spotId = spotId;
    this._userOneAlternate = uOA;
    this._userTwoAlternate = uTA;
    this._winnerId = winnerId;
  }

  static get attributes() {
    return modelAttributes;
  }

  static get idName() {
    return 'id';
  }

  static get tableName() {
    return 'results';
  }

  static approve(ids) {
    const client = db.createClient();
    const sql = 'UPDATE results SET needsApproval = FALSE, pending = FALSE WHERE id = ANY($1) RETURNING performanceId';
    let performanceId;

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const query = client.query(sql, [ids]);

      query.on('row', ({ performanceid }) => {
        performanceId = performanceid;
      });

      query.on('end', () => {
        client.end();
        resolve(performanceId);
      });
      query.on('error', (err) => {
        reject(err);
        client.end();
      });
    });
  }

  static checkAllDoneForPerformance(id) {
    const client = db.createClient();
    const sql = 'SELECT count(*) FROM results WHERE performanceid = $1 AND needsApproval';
    let count;

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const query = client.query(sql, [id]);

      query.on('row', result => {
        count = parseInt(result.count, 10);
      });
      query.on('end', () => {
        client.end();
        resolve(count === 0);
      });
      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  static createWithClient(attributes, client) {
    const { sql, values } = db.queryBuilder(Result, attributes);

    return new Promise((resolve, reject) => {
      const query = client.query(sql, values);

      query.on('end', resolve);
      query.on('error', reject);
    });
  }

  static findAllForApproval(user) {
    const client = db.createClient();
    const sql = queries.resultsForApproval;
    const results = [];

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const query = client.query(sql, [user.instrument, user.part]);

      query.on('row', (result) => results.push(resultForAdminFromDBRow(result)));

      query.on('end', () => {
        client.end();
        resolve(results);
      });

      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  static findAllForEval(nameNumber, row) {
    const client = db.createClient();
    const sql = queries.resultsForEval;
    const results = [];

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const query = client.query(sql, [nameNumber, row]);

      query.on('row', (result) => results.push(resultForEvaluationFromDBRow(result)));

      query.on('end', () => {
        client.end();
        resolve(results);
      });

      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  static findAllForPerformance(performanceId) {
    const client = db.createClient();
    const sql = queries.resultsForPerformance;
    const results = [];

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const query = client.query(sql, [performanceId]);

      query.on('row', (result) => results.push(resultForAdminFromDBRow(result)));

      query.on('end', () => {
        client.end();
        resolve(results);
      });

      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  static findAllRawForPerformance(performanceId) {
    const client = db.createClient();
    const sql = 'SELECT * FROM results WHERE performanceId = $1';
    const results = [];

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', reject);

      const query = client.query(sql, [performanceId]);

      query.on('row', (result) => results.push(resultForAdminFromDBRow(result)));

      query.on('end', () => {
        client.end();
        resolve(results);
      });

      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  static findAllForUser(nameNumber) {
    const client = db.createClient();
    const sql = queries.resultsForUser;
    const results = [];

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const query = client.query(sql, [nameNumber]);

      query.on('row', ({ comments, opponentname, performdate, performanceid, name, spotid, winnerid }) => {
        console.log(comments, opponentname, performdate, performanceid, name, spotid, nameNumber, winnerid);
        results.push(new ResultForUser(comments, opponentname, performdate, performanceid, name, spotid, nameNumber, winnerid));
      });

      query.on('end', () => {
        client.end();
        resolve(results);
      });

      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  static switchSpotsForPerformance(id) {
    const client = db.createClient();
    const resultsSql = queries.resultsForPerformance;
    const switchSql = 'SELECT switch_spots_based_on_results($1)';
    const oneUserSql = 'SELECT switch_spots_based_on_results_one_user($1)';
    const results = [];
    const onePersonResultIds = [];

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => {
        logger.errorLog('Results.switchSpotsForPerformance', err);
        reject(err);
      });

      const resultsQuery = client.query(resultsSql, [id]);

      resultsQuery.on('row', resultRow => results.push(instanceFromDBRow(resultRow)));
      resultsQuery.on('end', () => {
        const filteredResults = results.filter(({ secondNameNumber }, i) => {
          if (!secondNameNumber) {
            onePersonResultIds.push(results[i].id);
          }
          return secondNameNumber;
        });
        const filteredResultsIds = filteredResults.sort(resultsSort).map(({ id: resultId }) => resultId);

        const switchQuery = client.query(switchSql, [filteredResultsIds]);

        switchQuery.on('end', () => {
          const oneUserQuery = client.query(oneUserSql, [onePersonResultIds]);

          oneUserQuery.on('end', () => {
            client.end();
            resolve();
          });
          oneUserQuery.on('error', err => {
            client.end();
            logger.errorLog('Results.switchSpotsForPerformance: oneUserQuery', err);
            reject(err);
          });
        });

        switchQuery.on('error', err => {
          client.end();
          logger.errorLog('Results.switchSpotsForPerformance: switchQuery', err);
          reject(err);
        });
      });

      resultsQuery.on('error', err => {
        logger.errorLog('Results.switchSpotsForPerformance', err);
        client.end();
        reject(err);
      });
    });
  }

  static update(attributes) {
    const client = db.createClient();

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const id = attributes.id;

      if (typeof id === 'undefined') {
        reject(new Error('No id provided with attributes'));
      }

      delete attributes.id;
      const { sql, values } = db.queryBuilder(Result, attributes, { statement: 'UPDATE', id });
      const query = client.query(sql, values);

      query.on('end', () => resolve());
      query.on('error', (err) => reject(err));
    });
  }

  static updateForTestsOnly(firstNameNumber, comments1, comments2, winnerId) {
    const client = db.createClient();
    const sql = 'UPDATE results SET firstComments = $1, secondComments = $2, winnerId = $3 WHERE firstNameNumber = $4';

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', reject);

      const query = client.query(sql, [comments1, comments2 || null, winnerId, firstNameNumber]);

      query.on('end', () => {
        client.end();
        resolve();
      });

      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  get id() {
    return this._id;
  }

  get firstComments() {
    return this._firstComments;
  }

  get firstNameNumber() {
    return this._firstNameNumber;
  }

  get needsApproval() {
    return this._needsApproval;
  }

  get opponentName() {
    return this._opponentName;
  }

  get pending() {
    return this._pending;
  }

  get performanceId() {
    return this._performanceId;
  }

  get secondComments() {
    return this._secondComments;
  }

  get secondNameNumber() {
    return this._secondNameNumber;
  }

  get spotId() {
    return this._spotId;
  }

  get userOneAlternate() {
    return this._userOneAlternate;
  }

  get userTwoAlternate() {
    return this._userTwoAlternate;
  }

  // This function is only for results returned by findForUser
  toJSONForUser(nameNumber) {
    return {
      comments: this._firstComments,
      opponentName: this._opponentName,
      performanceDate: moment(this._performanceDate).format('MMM D, YYYY'),
      performanceName: this._performanceName,
      spotId: this._spotId,
      winner: this._winnerId === nameNumber
    };
  }
}

class ResultForAdmin {

  constructor(id, fC, fN, fNN, pending, perfId, perfName, sC, sN, sNN, spotId, winner) {
    this._firstComments = fC;
    this._firstName = fN;
    this._firstNameNumber = fNN;
    this._id = id;
    this._pending = pending;
    this._performanceId = perfId;
    this._performanceName = perfName;
    this._secondComments = sC;
    this._secondName = sN;
    this._secondNameNumber = sNN;
    this._spotId = spotId;
    this._winner = winner;
  }

  get firstComments() {
    return this._firstComments;
  }

  get firstName() {
    return this._firstName;
  }

  get firstNameNumber() {
    return this._firstNameNumber;
  }

  get id() {
    return this._id;
  }

  get pending() {
    return this._pending;
  }

  get performanceId() {
    return this._performanceId;
  }

  get performanceName() {
    return this._performanceName;
  }

  get secondComments() {
    return this._secondComments;
  }

  get secondName() {
    return this._secondName;
  }

  get secondNameNumber() {
    return this._secondNameNumber;
  }

  get spotId() {
    return this._spotId;
  }

  get winner() {
    return this._winner;
  }

}

class ResultForEvaluation {

  constructor(firstName, firstNameNumber, id, secondName, secondNameNumber, spotId) {
    this._firstName = firstName;
    this._firstNameNumber = firstNameNumber;
    this._id = id;
    this._secondName = secondName;
    this._secondNameNumber = secondNameNumber;
    this._spotId = spotId;
  }

  get firstName() {
    return this._firstName;
  }

  get firstNameNumber() {
    return this._firstNameNumber;
  }

  get resultId() {
    return this._id;
  }

  get secondName() {
    return this._secondName;
  }

  get secondNameNumber() {
    return this._secondNameNumber;
  }

  get spotId() {
    return this._spotId;
  }

}

class ResultForUser {

  constructor(comments, opponentName, performanceDate, performanceId, performanceName, spotId, userNameNumber, winner) {
    this._comments = comments;
    this._opponentName = opponentName;
    this._performanceDate = moment(performanceDate).format('MMMM D, YYYY');
    this._performanceId = performanceId;
    this._performanceName = performanceName;
    this._spotId = spotId;
    this._winner = winner === userNameNumber;
  }

  get comments() {
    return this._comments;
  }

  get opponentName() {
    return this._opponentName;
  }

  get performanceDate() {
    return this._performanceDate;
  }

  get performanceId() {
    return this._performanceId;
  }

  get performanceName() {
    return this._performanceName;
  }

  get spotId() {
    return this._spotId;
  }

  get winner() {
    return this._winner;
  }

}

const resultForAdminFromDBRow = ({
  resultid,
  firstcomments,
  nameone,
  firstnamenumber,
  pending,
  performanceid,
  performancename,
  secondcomments,
  nametwo,
  secondnamenumber,
  spotid,
  winnerid
}) => new ResultForAdmin(
  resultid,
  firstcomments,
  nameone,
  firstnamenumber,
  pending,
  performanceid,
  performancename,
  secondcomments,
  nametwo,
  secondnamenumber,
  spotid,
  winnerid
);

const resultForEvaluationFromDBRow = ({
  nameone,
  firstnamenumber,
  resultid,
  nametwo,
  secondnamenumber,
  spotid
}) => new ResultForEvaluation(nameone, firstnamenumber, resultid, nametwo, secondnamenumber, spotid);

const instanceFromDBRow = ({
  id,
  firstcomments,
  firstnamenumber,
  needsapproval,
  opponentname,
  pending,
  performdate,
  name,
  performanceid,
  secondcomments,
  secondnamenumber,
  spotid,
  useronealternate,
  usertwoalternate,
  winnerid
}) => new Result(
  id,
  firstcomments,
  firstnamenumber,
  needsapproval,
  opponentname,
  pending,
  performdate,
  name,
  performanceid,
  secondcomments,
  secondnamenumber,
  spotid,
  useronealternate,
  usertwoalternate,
  winnerid
);

module.exports = Result;
<commit_msg>ResultForAdmin.winner is the name of the winner<commit_after>const moment = require('moment');

const queries = require('../db/queries');
const { db, logger } = require('../utils');

const modelAttributes = ['id', 'performanceId', 'spotId', 'firstNameNumber', 'secondNameNumber', 'firstComments', 'secondComments', 'winnerId', 'pending', 'needsApproval'];
const alternateVersusRegular = result =>
  (!result.userOneAlternate && result.userTwoAlternate) ||
  (result.userOneAlternate && !result.userTwoAlternate);
const alternateVersusAlternate = result =>
  result.userOneAlternate && result.userTwoAlternate;
const regularVersusRegular = result =>
  !result.userOneAlternate && !result.userTwoAlternate;

/*
  We need a special sorting for results when we're switching user spots based on results
  - Regular VS Alternate
  - Alternate VS Alternate
  - Regular VS Regular
  This grossness does that
*/
const resultsSort = (a, b) => { // eslint-disable-line consistent-return, array-callback-return
  if (alternateVersusRegular(a) && alternateVersusRegular(b)) return 0;
  if (alternateVersusRegular(a) && alternateVersusAlternate(b)) return -1;
  if (alternateVersusRegular(a) && regularVersusRegular(b)) return -1;
  if (alternateVersusAlternate(a) && alternateVersusRegular(b)) return 1;
  if (alternateVersusAlternate(a) && alternateVersusAlternate(b)) return 0;
  if (alternateVersusAlternate(a) && regularVersusRegular(b)) return -1;
  if (regularVersusRegular(a) && alternateVersusRegular(b)) return 1;
  if (regularVersusRegular(a) && alternateVersusAlternate(b)) return 1;
  if (regularVersusRegular(a) && regularVersusRegular(b)) return 0;
};

class Result {

  constructor(id, fC, fNN, nA, opponentName, pending, perfDate, perfName, perfId, sC, sNN, spotId, uOA, uTA, winnerId) {
    this._id = id;
    this._firstComments = fC;
    this._firstNameNumber = fNN;
    this._needsApproval = nA;
    this._opponentName = opponentName;
    this._pending = pending;
    this._performanceDate = new Date(perfDate);
    this._performanceName = perfName;
    this._performanceId = perfId;
    this._secondComments = sC;
    this._secondNameNumber = sNN;
    this._spotId = spotId;
    this._userOneAlternate = uOA;
    this._userTwoAlternate = uTA;
    this._winnerId = winnerId;
  }

  static get attributes() {
    return modelAttributes;
  }

  static get idName() {
    return 'id';
  }

  static get tableName() {
    return 'results';
  }

  static approve(ids) {
    const client = db.createClient();
    const sql = 'UPDATE results SET needsApproval = FALSE, pending = FALSE WHERE id = ANY($1) RETURNING performanceId';
    let performanceId;

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const query = client.query(sql, [ids]);

      query.on('row', ({ performanceid }) => {
        performanceId = performanceid;
      });

      query.on('end', () => {
        client.end();
        resolve(performanceId);
      });
      query.on('error', (err) => {
        reject(err);
        client.end();
      });
    });
  }

  static checkAllDoneForPerformance(id) {
    const client = db.createClient();
    const sql = 'SELECT count(*) FROM results WHERE performanceid = $1 AND needsApproval';
    let count;

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const query = client.query(sql, [id]);

      query.on('row', result => {
        count = parseInt(result.count, 10);
      });
      query.on('end', () => {
        client.end();
        resolve(count === 0);
      });
      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  static createWithClient(attributes, client) {
    const { sql, values } = db.queryBuilder(Result, attributes);

    return new Promise((resolve, reject) => {
      const query = client.query(sql, values);

      query.on('end', resolve);
      query.on('error', reject);
    });
  }

  static findAllForApproval(user) {
    const client = db.createClient();
    const sql = queries.resultsForApproval;
    const results = [];

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const query = client.query(sql, [user.instrument, user.part]);

      query.on('row', (result) => results.push(resultForAdminFromDBRow(result)));

      query.on('end', () => {
        client.end();
        resolve(results);
      });

      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  static findAllForEval(nameNumber, row) {
    const client = db.createClient();
    const sql = queries.resultsForEval;
    const results = [];

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const query = client.query(sql, [nameNumber, row]);

      query.on('row', (result) => results.push(resultForEvaluationFromDBRow(result)));

      query.on('end', () => {
        client.end();
        resolve(results);
      });

      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  static findAllForPerformance(performanceId) {
    const client = db.createClient();
    const sql = queries.resultsForPerformance;
    const results = [];

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const query = client.query(sql, [performanceId]);

      query.on('row', (result) => results.push(resultForAdminFromDBRow(result)));

      query.on('end', () => {
        client.end();
        resolve(results);
      });

      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  static findAllRawForPerformance(performanceId) {
    const client = db.createClient();
    const sql = 'SELECT * FROM results WHERE performanceId = $1';
    const results = [];

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', reject);

      const query = client.query(sql, [performanceId]);

      query.on('row', (result) => results.push(resultForAdminFromDBRow(result)));

      query.on('end', () => {
        client.end();
        resolve(results);
      });

      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  static findAllForUser(nameNumber) {
    const client = db.createClient();
    const sql = queries.resultsForUser;
    const results = [];

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const query = client.query(sql, [nameNumber]);

      query.on('row', ({ comments, opponentname, performdate, performanceid, name, spotid, winnerid }) => {
        console.log(comments, opponentname, performdate, performanceid, name, spotid, nameNumber, winnerid);
        results.push(new ResultForUser(comments, opponentname, performdate, performanceid, name, spotid, nameNumber, winnerid));
      });

      query.on('end', () => {
        client.end();
        resolve(results);
      });

      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  static switchSpotsForPerformance(id) {
    const client = db.createClient();
    const resultsSql = queries.resultsForPerformance;
    const switchSql = 'SELECT switch_spots_based_on_results($1)';
    const oneUserSql = 'SELECT switch_spots_based_on_results_one_user($1)';
    const results = [];
    const onePersonResultIds = [];

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => {
        logger.errorLog('Results.switchSpotsForPerformance', err);
        reject(err);
      });

      const resultsQuery = client.query(resultsSql, [id]);

      resultsQuery.on('row', resultRow => results.push(instanceFromDBRow(resultRow)));
      resultsQuery.on('end', () => {
        const filteredResults = results.filter(({ secondNameNumber }, i) => {
          if (!secondNameNumber) {
            onePersonResultIds.push(results[i].id);
          }
          return secondNameNumber;
        });
        const filteredResultsIds = filteredResults.sort(resultsSort).map(({ id: resultId }) => resultId);

        const switchQuery = client.query(switchSql, [filteredResultsIds]);

        switchQuery.on('end', () => {
          const oneUserQuery = client.query(oneUserSql, [onePersonResultIds]);

          oneUserQuery.on('end', () => {
            client.end();
            resolve();
          });
          oneUserQuery.on('error', err => {
            client.end();
            logger.errorLog('Results.switchSpotsForPerformance: oneUserQuery', err);
            reject(err);
          });
        });

        switchQuery.on('error', err => {
          client.end();
          logger.errorLog('Results.switchSpotsForPerformance: switchQuery', err);
          reject(err);
        });
      });

      resultsQuery.on('error', err => {
        logger.errorLog('Results.switchSpotsForPerformance', err);
        client.end();
        reject(err);
      });
    });
  }

  static update(attributes) {
    const client = db.createClient();

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', (err) => reject(err));

      const id = attributes.id;

      if (typeof id === 'undefined') {
        reject(new Error('No id provided with attributes'));
      }

      delete attributes.id;
      const { sql, values } = db.queryBuilder(Result, attributes, { statement: 'UPDATE', id });
      const query = client.query(sql, values);

      query.on('end', () => resolve());
      query.on('error', (err) => reject(err));
    });
  }

  static updateForTestsOnly(firstNameNumber, comments1, comments2, winnerId) {
    const client = db.createClient();
    const sql = 'UPDATE results SET firstComments = $1, secondComments = $2, winnerId = $3 WHERE firstNameNumber = $4';

    return new Promise((resolve, reject) => {
      client.connect();
      client.on('error', reject);

      const query = client.query(sql, [comments1, comments2 || null, winnerId, firstNameNumber]);

      query.on('end', () => {
        client.end();
        resolve();
      });

      query.on('error', (err) => {
        client.end();
        reject(err);
      });
    });
  }

  get id() {
    return this._id;
  }

  get firstComments() {
    return this._firstComments;
  }

  get firstNameNumber() {
    return this._firstNameNumber;
  }

  get needsApproval() {
    return this._needsApproval;
  }

  get opponentName() {
    return this._opponentName;
  }

  get pending() {
    return this._pending;
  }

  get performanceId() {
    return this._performanceId;
  }

  get secondComments() {
    return this._secondComments;
  }

  get secondNameNumber() {
    return this._secondNameNumber;
  }

  get spotId() {
    return this._spotId;
  }

  get userOneAlternate() {
    return this._userOneAlternate;
  }

  get userTwoAlternate() {
    return this._userTwoAlternate;
  }

  // This function is only for results returned by findForUser
  toJSONForUser(nameNumber) {
    return {
      comments: this._firstComments,
      opponentName: this._opponentName,
      performanceDate: moment(this._performanceDate).format('MMM D, YYYY'),
      performanceName: this._performanceName,
      spotId: this._spotId,
      winner: this._winnerId === nameNumber
    };
  }
}

class ResultForAdmin {

  constructor(id, fC, fN, fNN, pending, perfId, perfName, sC, sN, sNN, spotId, winnerId) {
    this._firstComments = fC;
    this._firstName = fN;
    this._firstNameNumber = fNN;
    this._id = id;
    this._pending = pending;
    this._performanceId = perfId;
    this._performanceName = perfName;
    this._secondComments = sC;
    this._secondName = sN;
    this._secondNameNumber = sNN;
    this._spotId = spotId;
    this._winnerId = winnerId;
  }

  get firstComments() {
    return this._firstComments;
  }

  get firstName() {
    return this._firstName;
  }

  get firstNameNumber() {
    return this._firstNameNumber;
  }

  get id() {
    return this._id;
  }

  get pending() {
    return this._pending;
  }

  get performanceId() {
    return this._performanceId;
  }

  get performanceName() {
    return this._performanceName;
  }

  get secondComments() {
    return this._secondComments;
  }

  get secondName() {
    return this._secondName;
  }

  get secondNameNumber() {
    return this._secondNameNumber;
  }

  get spotId() {
    return this._spotId;
  }

  get winner() {
    return this._winnerId === this._firstNameNumber ? this._firstName : this._secondName;
  }

}

class ResultForEvaluation {

  constructor(firstName, firstNameNumber, id, secondName, secondNameNumber, spotId) {
    this._firstName = firstName;
    this._firstNameNumber = firstNameNumber;
    this._id = id;
    this._secondName = secondName;
    this._secondNameNumber = secondNameNumber;
    this._spotId = spotId;
  }

  get firstName() {
    return this._firstName;
  }

  get firstNameNumber() {
    return this._firstNameNumber;
  }

  get resultId() {
    return this._id;
  }

  get secondName() {
    return this._secondName;
  }

  get secondNameNumber() {
    return this._secondNameNumber;
  }

  get spotId() {
    return this._spotId;
  }

}

class ResultForUser {

  constructor(comments, opponentName, performanceDate, performanceId, performanceName, spotId, userNameNumber, winner) {
    this._comments = comments;
    this._opponentName = opponentName;
    this._performanceDate = moment(performanceDate).format('MMMM D, YYYY');
    this._performanceId = performanceId;
    this._performanceName = performanceName;
    this._spotId = spotId;
    this._winner = winner === userNameNumber;
  }

  get comments() {
    return this._comments;
  }

  get opponentName() {
    return this._opponentName;
  }

  get performanceDate() {
    return this._performanceDate;
  }

  get performanceId() {
    return this._performanceId;
  }

  get performanceName() {
    return this._performanceName;
  }

  get spotId() {
    return this._spotId;
  }

  get winner() {
    return this._winner;
  }

}

const resultForAdminFromDBRow = ({
  resultid,
  firstcomments,
  nameone,
  firstnamenumber,
  pending,
  performanceid,
  performancename,
  secondcomments,
  nametwo,
  secondnamenumber,
  spotid,
  winnerid
}) => new ResultForAdmin(
  resultid,
  firstcomments,
  nameone,
  firstnamenumber,
  pending,
  performanceid,
  performancename,
  secondcomments,
  nametwo,
  secondnamenumber,
  spotid,
  winnerid
);

const resultForEvaluationFromDBRow = ({
  nameone,
  firstnamenumber,
  resultid,
  nametwo,
  secondnamenumber,
  spotid
}) => new ResultForEvaluation(nameone, firstnamenumber, resultid, nametwo, secondnamenumber, spotid);

const instanceFromDBRow = ({
  id,
  firstcomments,
  firstnamenumber,
  needsapproval,
  opponentname,
  pending,
  performdate,
  name,
  performanceid,
  secondcomments,
  secondnamenumber,
  spotid,
  useronealternate,
  usertwoalternate,
  winnerid
}) => new Result(
  id,
  firstcomments,
  firstnamenumber,
  needsapproval,
  opponentname,
  pending,
  performdate,
  name,
  performanceid,
  secondcomments,
  secondnamenumber,
  spotid,
  useronealternate,
  usertwoalternate,
  winnerid
);

module.exports = Result;
<|endoftext|>