<commit_before>// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults

/*===[ onload functions ]=============================================*/

// Highlight the flash notification area briefly.
// REQUIRES: jquery-ui.highlight
function pulse_flash() {
  $('.flash, .flash p').effect('highlight', {}, 3000)
}

$(document).ready(function() {
  pulse_flash();
})

/*===[ custom functions ]=============================================*/

/*---[ data structure conveniences ]----------------------------------*/

// Return an array of keys in +hash+. Example:
//  hash_keys({'foo': 'bar', 'baz': 'qux'}); // ['foo', 'baz']
function hash_keys(hash) {
  array = new Array();
  for (key in hash) {
    if (key) {
      array.push(key);
    }
  }
  return array;
}

// Return hash where each key is an element of the +array+. Example:
//    array_to_hash(['foo', 'bar']); // {'foo': true, 'bar': true}
function array_to_hash(array) {
  hash = new Array();
  for (i in array) {
    hash[array[i]] = true;
  }
  return hash;
}

/*---[ page_spinner ]-------------------------------------------------*/

// Display a big spinner in the middle of the page.
function page_spinner_start() {
  $(document.createElement("div")).attr('id','page_spinner').text('Working...').prependTo('body').show(100);
}

// Hide the big spinner displayed in the middle of the page by`page_spinner_start`.
function page_spinner_stop() {
  $('#page_spinner').hide(100, function(){ $(this).remove() });
}

/*---[ proposal_controls ]--------------------------------------------*/
// AJAXified controls for managing proposals.

// Bind all proposal controls, call this once for on a page using these controls.
function bind_all_proposal_controls() {
  bind_proposal_generic_control('room', null);
  bind_proposal_generic_control('transition', null);
  bind_proposal_schedule_controls();
}

// Bind AJAX controls for manipulate a proposal's values.
//
// Arguments:
// * kind: The kind of the controls, e.g., "room" or "transition". Required.
// * elements: Elements to bind. Optional, if null will bind to all appropriate controls on page.
function bind_proposal_generic_control(kind, elements) {
  if (! elements) {
    elements = $('.proposal_'+kind+'_control');
  }
  elements.removeAttr('disabled').change(function(event) {
    target = $(this);
    $e = event; $t = target; $k = kind;

    event.preventDefault();
    name = target.attr('name');
    value = target.attr('value');
    proposal_id = target.attr('x_proposal_id');
    format = 'json';
    url = '/proposals/'+proposal_id+'.'+format;

    data = { 'authenticity_token': window._token };
    data[name] = value;
    $d = data;

    $.ajax({
      'type': 'PUT',
      'url': url,
      'data': data,
      'dataType': format,
      'beforeSend': function(request) {
        target.attr('disabled', true);
        page_spinner_start();
      },
      'complete': function (XMLHttpRequest, textStatus) {
        page_spinner_stop();
        target.removeAttr('disabled');
      },
      'success': function (response, textStatus) {
        $r = response;
        html_field = '_'+kind+'_control_html';
        if (response && response[html_field]) {
          // Extact the "option" elements from the JSON repsone's HTML and update the "select" element.
          matcher = new RegExp('(<option[\\s\\S]+</option>)', 'gi').exec(response[html_field]);
          if (matcher) {
            target.html(matcher[1]);
          }
        }
      }
    });

    return false;
  });
}

function bind_proposal_schedule_controls() {
  $('.proposal_schedule_control_container select').change(function(event) {
    // Clears all time select elements if any are set to blank.
    target = $(this);

    if(target.attr('selectedIndex')==0) {
      target.parent().find('select').attr('selectedIndex',0);
    }
  }).change(function(event) {
    // Submits the schedule form on change if all three select element have values.
    if(target.parent().find('option:selected[value]').get().length == 3) {
      target = $(this);

      data = {
        'authenticity_token': window._token,
        'start_time[date]': target.parent().find('select.date').attr('value'),
        'start_time[hour]': target.parent().find('select.hour').attr('value'),
        'start_time[minute]': target.parent().find('select.minute').attr('value')
      };
      proposal_id = target.parent().attr('id').split('_').pop();
      format = 'json';
      url = '/proposals/'+proposal_id+'.'+format;

      $.ajax({
        'type': 'PUT',
        'url': url,
        'data': data,
        'dataType': format,
        'beforeSend': function(request) {
          target.parent().find('select').attr('disabled', true);
          page_spinner_start();
        },
        'complete': function (XMLHttpRequest, textStatus) {
          page_spinner_stop();
          target.parent().find('select').removeAttr('disabled');
        }
      });

      return false;
    }
  });

  $('.proposal_schedule_control_container select.hour').change(function(event){
    // If no minute value is set, set minutes to 00 when hour is changed to a non-blank value.
    target = $(this);
    if(target.attr('selectedIndex') != 0) {
      minute_select = target.parent().find('select.minute');
      if(minute_select.attr('selectedIndex') == 0) {
        minute_select.attr('selectedIndex',1).trigger('change');
      }
    }
  });
}

/*---[ proposal_mailto ]----------------------------------------------*/

// Return array of email addresses for a given +proposal_id+. Example:
//    addresses_for_manage_proposal_id(129)
function addresses_for_manage_proposal_id(proposal_id) {
  // TODO Why does this next line trigger a "identifier or string for value in attribute selector but found 'X'." where X is the +proposal_id+?
  row = $('.row-for-proposal[x_proposal_id='+proposal_id+']');
  addresses = new Array();
  row.find('.user-email').each(function() {addresses.push(this.value)});
  return addresses;
}

// Update the mailto used to contact presenters. The +is_add+ is true if
// adding, false if removing. The +addresses+ are an array of addresses to
// add or remove.
//    update_manage_proposals_mailto(true, ["bubba@smith.com", "billy.sue@smith.com"])
function update_manage_proposals_mailto(is_add, addresses) {
  element = $('.send-email-link');
  href = element.attr('href');
  prefix = 'mailto:';
  parser = new RegExp('^'+prefix+'(.+)$');
  stripper = new RegExp('bcc=', 'gi');
  matcher = parser.exec(href);
  addresses_hash = matcher ? array_to_hash(matcher[1].replace(stripper, '').split(',')) : [];
  $(addresses).each(function(){
    if (is_add) {
      addresses_hash[this] = true;
    } else {
      delete addresses_hash[this];
    }
  });
  result = prefix+(hash_keys(addresses_hash).map(function(me) {return 'bcc='+me}).join(','));
  element.attr('href', result);
  return result;
}

function bind_manage_proposals_checkboxes() {
  $('.send-email-checkbox').click(function(event) {
    target = $(this); $t = target;
    row = target.parents('.row-for-proposal');
    proposal_id = row.attr('x_proposal_id');
    proposal_addresses = addresses_for_manage_proposal_id(proposal_id);
    update_manage_proposals_mailto(target.attr('checked'), proposal_addresses);
    return true;
  });
}

/*===[ fin ]==========================================================*/

/*---[ schedule hover ]----------------------------------------------*/

$(document).ready(function() {
  $('table.schedule li.vevent').hover(
    function() {
      $(this).addClass('hover');
      box = $(this).children('.session_info');
      bottom = top + $(window).height();
      box_bottom = box.offset().top + box.outerHeight();

      if ( box_bottom > bottom ) box.css('top',-(box_bottom - bottom + 10));
      if( !$(this).hasClass('generic_item')) box.css('border-color',$(this).css('background-color'));
      },
    function() {
      $(this).removeClass('hover');
      $(this).children('.session_info').css('top',0);
    }
  )
})
<commit_msg>Fixing javascript keeping schedule information boxes on screen.<commit_after>// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults

/*===[ onload functions ]=============================================*/

// Highlight the flash notification area briefly.
// REQUIRES: jquery-ui.highlight
function pulse_flash() {
  $('.flash, .flash p').effect('highlight', {}, 3000)
}

$(document).ready(function() {
  pulse_flash();
})

/*===[ custom functions ]=============================================*/

/*---[ data structure conveniences ]----------------------------------*/

// Return an array of keys in +hash+. Example:
//  hash_keys({'foo': 'bar', 'baz': 'qux'}); // ['foo', 'baz']
function hash_keys(hash) {
  array = new Array();
  for (key in hash) {
    if (key) {
      array.push(key);
    }
  }
  return array;
}

// Return hash where each key is an element of the +array+. Example:
//    array_to_hash(['foo', 'bar']); // {'foo': true, 'bar': true}
function array_to_hash(array) {
  hash = new Array();
  for (i in array) {
    hash[array[i]] = true;
  }
  return hash;
}

/*---[ page_spinner ]-------------------------------------------------*/

// Display a big spinner in the middle of the page.
function page_spinner_start() {
  $(document.createElement("div")).attr('id','page_spinner').text('Working...').prependTo('body').show(100);
}

// Hide the big spinner displayed in the middle of the page by`page_spinner_start`.
function page_spinner_stop() {
  $('#page_spinner').hide(100, function(){ $(this).remove() });
}

/*---[ proposal_controls ]--------------------------------------------*/
// AJAXified controls for managing proposals.

// Bind all proposal controls, call this once for on a page using these controls.
function bind_all_proposal_controls() {
  bind_proposal_generic_control('room', null);
  bind_proposal_generic_control('transition', null);
  bind_proposal_schedule_controls();
}

// Bind AJAX controls for manipulate a proposal's values.
//
// Arguments:
// * kind: The kind of the controls, e.g., "room" or "transition". Required.
// * elements: Elements to bind. Optional, if null will bind to all appropriate controls on page.
function bind_proposal_generic_control(kind, elements) {
  if (! elements) {
    elements = $('.proposal_'+kind+'_control');
  }
  elements.removeAttr('disabled').change(function(event) {
    target = $(this);
    $e = event; $t = target; $k = kind;

    event.preventDefault();
    name = target.attr('name');
    value = target.attr('value');
    proposal_id = target.attr('x_proposal_id');
    format = 'json';
    url = '/proposals/'+proposal_id+'.'+format;

    data = { 'authenticity_token': window._token };
    data[name] = value;
    $d = data;

    $.ajax({
      'type': 'PUT',
      'url': url,
      'data': data,
      'dataType': format,
      'beforeSend': function(request) {
        target.attr('disabled', true);
        page_spinner_start();
      },
      'complete': function (XMLHttpRequest, textStatus) {
        page_spinner_stop();
        target.removeAttr('disabled');
      },
      'success': function (response, textStatus) {
        $r = response;
        html_field = '_'+kind+'_control_html';
        if (response && response[html_field]) {
          // Extact the "option" elements from the JSON repsone's HTML and update the "select" element.
          matcher = new RegExp('(<option[\\s\\S]+</option>)', 'gi').exec(response[html_field]);
          if (matcher) {
            target.html(matcher[1]);
          }
        }
      }
    });

    return false;
  });
}

function bind_proposal_schedule_controls() {
  $('.proposal_schedule_control_container select').change(function(event) {
    // Clears all time select elements if any are set to blank.
    target = $(this);

    if(target.attr('selectedIndex')==0) {
      target.parent().find('select').attr('selectedIndex',0);
    }
  }).change(function(event) {
    // Submits the schedule form on change if all three select element have values.
    if(target.parent().find('option:selected[value]').get().length == 3) {
      target = $(this);

      data = {
        'authenticity_token': window._token,
        'start_time[date]': target.parent().find('select.date').attr('value'),
        'start_time[hour]': target.parent().find('select.hour').attr('value'),
        'start_time[minute]': target.parent().find('select.minute').attr('value')
      };
      proposal_id = target.parent().attr('id').split('_').pop();
      format = 'json';
      url = '/proposals/'+proposal_id+'.'+format;

      $.ajax({
        'type': 'PUT',
        'url': url,
        'data': data,
        'dataType': format,
        'beforeSend': function(request) {
          target.parent().find('select').attr('disabled', true);
          page_spinner_start();
        },
        'complete': function (XMLHttpRequest, textStatus) {
          page_spinner_stop();
          target.parent().find('select').removeAttr('disabled');
        }
      });

      return false;
    }
  });

  $('.proposal_schedule_control_container select.hour').change(function(event){
    // If no minute value is set, set minutes to 00 when hour is changed to a non-blank value.
    target = $(this);
    if(target.attr('selectedIndex') != 0) {
      minute_select = target.parent().find('select.minute');
      if(minute_select.attr('selectedIndex') == 0) {
        minute_select.attr('selectedIndex',1).trigger('change');
      }
    }
  });
}

/*---[ proposal_mailto ]----------------------------------------------*/

// Return array of email addresses for a given +proposal_id+. Example:
//    addresses_for_manage_proposal_id(129)
function addresses_for_manage_proposal_id(proposal_id) {
  // TODO Why does this next line trigger a "identifier or string for value in attribute selector but found 'X'." where X is the +proposal_id+?
  row = $('.row-for-proposal[x_proposal_id='+proposal_id+']');
  addresses = new Array();
  row.find('.user-email').each(function() {addresses.push(this.value)});
  return addresses;
}

// Update the mailto used to contact presenters. The +is_add+ is true if
// adding, false if removing. The +addresses+ are an array of addresses to
// add or remove.
//    update_manage_proposals_mailto(true, ["bubba@smith.com", "billy.sue@smith.com"])
function update_manage_proposals_mailto(is_add, addresses) {
  element = $('.send-email-link');
  href = element.attr('href');
  prefix = 'mailto:';
  parser = new RegExp('^'+prefix+'(.+)$');
  stripper = new RegExp('bcc=', 'gi');
  matcher = parser.exec(href);
  addresses_hash = matcher ? array_to_hash(matcher[1].replace(stripper, '').split(',')) : [];
  $(addresses).each(function(){
    if (is_add) {
      addresses_hash[this] = true;
    } else {
      delete addresses_hash[this];
    }
  });
  result = prefix+(hash_keys(addresses_hash).map(function(me) {return 'bcc='+me}).join(','));
  element.attr('href', result);
  return result;
}

function bind_manage_proposals_checkboxes() {
  $('.send-email-checkbox').click(function(event) {
    target = $(this); $t = target;
    row = target.parents('.row-for-proposal');
    proposal_id = row.attr('x_proposal_id');
    proposal_addresses = addresses_for_manage_proposal_id(proposal_id);
    update_manage_proposals_mailto(target.attr('checked'), proposal_addresses);
    return true;
  });
}

/*===[ fin ]==========================================================*/

/*---[ schedule hover ]----------------------------------------------*/

$(document).ready(function() {
  $('table.schedule li.vevent').hover(
    function() {
      $(this).addClass('hover');
      box = $(this).children('.session_info');

      top = $(document).scrollTop();
      bottom = top + $(window).height();
      box_bottom = box.offset().top + box.outerHeight();

      if ( box_bottom > bottom ) box.css('top',-(box_bottom - bottom + 10));
      if( !$(this).hasClass('generic_item')) box.css('border-color',$(this).css('background-color'));
      },
    function() {
      $(this).removeClass('hover');
      $(this).children('.session_info').css('top',0);
    }
  )
})
<|endoftext|>


<commit_before><commit_msg>Add jQuery function to make the notice disappear<commit_after>$(document).ready(function() {
  $(".notice").not(".alert").delay(3000).slideUp('slow');
});
<|endoftext|>


<commit_before>var currentContent ="content1";
var switching = false;

var switchContent = (function(sender){
  var contentBlock = $(sender).attr("rel");
  if(!switching && contentBlock!=currentContent){
    switching = true;
    $("ul.features a").removeClass("active");
    $("#"+currentContent).fadeOut(500, function(){
      $("#"+contentBlock).fadeIn(1000, function(){
        switching = false;
      });
    });
    $("a[rel='"+contentBlock+"']").addClass("active");
    currentContent = contentBlock;
  }
});

var validateEmail = (function(address){
  var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
  !(reg.test(address) == false);
});

$(document).ready(function() {
  var header    = $('#header'),
      footer    = $('#footer'),
      login     = $('#login'),
      subscribe = $('#subscribe');

  //borrowed from jQuery easing plugin
  //http://gsgd.co.uk/sandbox/jquery.easing.php
  $.easing.easeOutQuad = function(x, t, b, c, d) {
    return -c *(t/=d)*(t-2) + b;
  };

  $(".links a", footer).mouseover(function(){
    $(this).stop();
    $(this).animate({ opacity: 1 }, 200);
  });

  $(".links a", footer).mouseout(function(){
    $(this).stop();
    $(this).animate({ opacity: 0.5 }, 200);
  });

  $('#selectAll').click(function(){
    var checked = $('#selectAll').is(':checked');
    $("td input[type='checkbox']").attr('checked', checked);
  });

  $("ul.features a,#header ul li.request a, a.toTab").each(function(){
    $(this).attr("href","javascript:;");
  });

  $("ul.features a").click(function(){
    switchContent(this);
  });

  $("ul li.login a", header).click(function(){
    if (login.hasClass('active')) {
      $(this).html("Login");
      login.fadeOut(500);
      login.removeClass('active');
    } else {
      $(this).html("Hide");
      login.fadeIn(1000);
      login.addClass('active');
      subscribe.fadeOut(250);
      subscribe.removeClass('active');
      $("ul li.subscribe a", header).html("Newsletter");
    }
  });

  $("ul li.subscribe a", header).click(function(){
    if (subscribe.hasClass('active')) {
      $(this).html("Newsletter");
      subscribe.fadeOut(500);
      subscribe.removeClass('active');
    } else {
      $(this).html('Hide');
      subscribe.fadeIn(1000);
      subscribe.addClass('active');
      login.fadeOut(250);
      login.removeClass('active');
      $("ul li.login a", header).html("Login");
    }
  });

  $("a.toTab").click(function(){
    switchContent(this);
  });

  $("ul li.request a", header).click(function(){
    switchContent(this);
    $.scrollTo($("#container"), 1500, {easing:'easeOutQuad'});
  });

  $("form.emailForm").submit(function(){
    var email      = $("#invitation_request_email").attr("value"),
        authentity = $("input[name='authenticity_token']").attr("value");
    $("span.valid").hide();
    $("span.invalid").hide();
    if (validateEmail(email)) {
      var post_data = {
        "invitation_request[email]": email,
        "authenticity_token":        authentity };
      $.post("/invitation_requests.js", post_data, function(data){
        $("span.valid").html("Thank you for your request. You will receive an e-mail very soon!");
        $("span.valid").attr("style","display:block;");
      });
    } else {
      $("span.invalid").html("Please enter a valid e-mail address.");
      $("span.invalid").attr("style","display:block;");
    }
    return false;
  });

   /*
    * Clearfield
    */
    $('.clear-field').clearField();

}); // End DOM ready
<commit_msg>make entire row clickable<commit_after>var currentContent ="content1";
var switching = false;

var switchContent = (function(sender){
  var contentBlock = $(sender).attr("rel");
  if(!switching && contentBlock!=currentContent){
    switching = true;
    $("ul.features a").removeClass("active");
    $("#"+currentContent).fadeOut(500, function(){
      $("#"+contentBlock).fadeIn(1000, function(){
        switching = false;
      });
    });
    $("a[rel='"+contentBlock+"']").addClass("active");
    currentContent = contentBlock;
  }
});

var validateEmail = (function(address){
  var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
  !(reg.test(address) == false);
});

$(document).ready(function() {
  var header    = $('#header'),
      footer    = $('#footer'),
      login     = $('#login'),
      subscribe = $('#subscribe');

  //borrowed from jQuery easing plugin
  //http://gsgd.co.uk/sandbox/jquery.easing.php
  $.easing.easeOutQuad = function(x, t, b, c, d) {
    return -c *(t/=d)*(t-2) + b;
  };

  $(".links a", footer).mouseover(function(){
    $(this).stop();
    $(this).animate({ opacity: 1 }, 200);
  });

  $(".links a", footer).mouseout(function(){
    $(this).stop();
    $(this).animate({ opacity: 0.5 }, 200);
  });

  $('#selectAll').click(function(){
    var checked = $('#selectAll').is(':checked');
    $("td input[type='checkbox']").attr('checked', checked);
  });

  $("ul.features a,#header ul li.request a, a.toTab").each(function(){
    $(this).attr("href","javascript:;");
  });

  $("ul.features a").click(function(){
    switchContent(this);
  });

  $("ul li.login a", header).click(function(){
    if (login.hasClass('active')) {
      $(this).html("Login");
      login.fadeOut(500);
      login.removeClass('active');
    } else {
      $(this).html("Hide");
      login.fadeIn(1000);
      login.addClass('active');
      subscribe.fadeOut(250);
      subscribe.removeClass('active');
      $("ul li.subscribe a", header).html("Newsletter");
    }
  });

  $("ul li.subscribe a", header).click(function(){
    if (subscribe.hasClass('active')) {
      $(this).html("Newsletter");
      subscribe.fadeOut(500);
      subscribe.removeClass('active');
    } else {
      $(this).html('Hide');
      subscribe.fadeIn(1000);
      subscribe.addClass('active');
      login.fadeOut(250);
      login.removeClass('active');
      $("ul li.login a", header).html("Login");
    }
  });

  $("a.toTab").click(function(){
    switchContent(this);
  });

  $("ul li.request a", header).click(function(){
    switchContent(this);
    $.scrollTo($("#container"), 1500, {easing:'easeOutQuad'});
  });

  $("form.emailForm").submit(function(){
    var email      = $("#invitation_request_email").attr("value"),
        authentity = $("input[name='authenticity_token']").attr("value");
    $("span.valid").hide();
    $("span.invalid").hide();
    if (validateEmail(email)) {
      var post_data = {
        "invitation_request[email]": email,
        "authenticity_token":        authentity };
      $.post("/invitation_requests.js", post_data, function(data){
        $("span.valid").html("Thank you for your request. You will receive an e-mail very soon!");
        $("span.valid").attr("style","display:block;");
      });
    } else {
      $("span.invalid").html("Please enter a valid e-mail address.");
      $("span.invalid").attr("style","display:block;");
    }
    return false;
  });

   /*
    * Clearfield
    */
  $('.clear-field').clearField();

  $('table td.project a, table.errors a').each(function(){
    var a   = $(this),
        row = a.parent('td');
    $(row).click(function(){ a.click(); });
  })

}); // End DOM ready
<|endoftext|>


<commit_before>//= require jquery
//= require jquery_ujs
//= require underscore
//= require bootstrap/dropdown
//= require bootstrap/alert
//= require angular
//= require angular-resource
//= require angular-sanitize
//= require angular-route
//= require angular-ui-router
//= require angular-ui-select2
//= require select2
//= require_tree ./client
//= require_tree ./client/views
<commit_msg>Updated to angular-rails-template<commit_after>//= require jquery
//= require jquery_ujs
//= require underscore
//= require bootstrap/dropdown
//= require bootstrap/alert
//= require angular
//= require angular-resource
//= require angular-sanitize
//= require angular-route
//= require angular-ui-router
//= require angular-ui-select2
//= require select2
//= require angular-rails-templates
//= require_tree ./client
//= require_tree ./client/views
<|endoftext|>


<commit_before>$(function(){
  $('ul.commits li ul').css("display","none")
  $('ul.commits').toggle(
  function(){
    $(this).find('li ul').show();
  },
  function(){
    $(this).find('li ul').hide();
  }
  );
});

<commit_msg>clean up jquery code<commit_after>$(function(){
  $('ul.commits li ul').css("display","none")
  $('ul.commits li h2').toggle(
  function(){
    $(this).parent().find('ul').show();
  },
  function(){
    $(this).parent().find('ul').hide();
  }
  );
});

<|endoftext|>


<commit_before>// TODO: Support cross-domain AJAX requests in IE:
// if ($.browser.msie && window.XDomainRequest) {
//     // Use Microsoft XDR
//     var xdr = new XDomainRequest();
//     xdr.open('get', url);
//     xdr.onload = function() {
//         // XDomainRequest doesn't provide responseXml, so if you need it:
//         var dom = new ActiveXObject('Microsoft.XMLDOM');
//         dom.async = false;
//         dom.loadXML(xdr.responseText);
//     };
//     xdr.send();
// } else {
//     $.ajax({...});
// }

vidibus.xss = {
  initialized: {},      // holds true for every scope that has been initialized
  fileExtension: 'xss', // use 'xss' as file extension
  loadedUrls: {},       // store urls currently loaded in each scope

  ready: function() {
    // TODO: implement real event handler
  },

  /**
   * Detects scope of script block to be executed.
   * Must be called from embedding page.
   */
  detectScope: function() {
    document.write('

');
    var $detector = $("#scopeDetector");
    var $scope = $detector.parent();
    $detector.remove();
    return $scope;
  },

  /**
   * Usage:
   *   vidibus.xss.embed('
Some HTML
', $('#scope'), 'http://host.url/');
   */
  embed: function(html, $scope, host) {
    html = this.transformPaths(html, $scope); // Transform local paths before embedding html into page!
    $scope.html(html);
    this.setUrls($scope);
    this.setActions($scope);
    this.ready();
  },

  /**
   * Calls given path for given scope. If a third parameter is set to true,
   * the location will be loaded, even if it is currently loaded.
   *
   * Usage:
   *   vidibus.xss.get('path', $('#scope') [, true]);
   *
   * If no host is provided, host of scope will be used.
   */
  get: function(path, $scope, reload) {
    // Escape query parts
    path = path.replace("?", "%3F").replace("&", "%26").replace("=", "%3D");

    var scopeId = $scope[0].id,
        params = scopeId+'='+this.getPath(path),
        keepCurrentLocation = this.initialized[scopeId] ? 0 : 1,
        location = $.param.fragment(String(document.location), params, keepCurrentLocation),
        reloadScope = {};

    this.initialized[scopeId] = true;
    window.location.href = location; // write history
    reloadScope[scopeId] = reload;
    this.loadUrl(location, reloadScope);
  },

  /**
   * Redirect to given path while forcing reloading.
   */
  redirect: function(path, $scope) {
    this.get(path, $scope, true)
  },

  /**
   * Handles callback action.
   *
   * Requires data.status:
   *   redirect, TODO: ok, error
   *
   * Accepts actions depending on status:
   *   (redirect) to: Performs redirect to location.
   */
  callback: function(data, $scope) {
    if (data.status == 'redirect') {
      this.redirect(data.to, $scope);
    }
  },

  /**
   * Sets host for given scope.
   */
  setHost: function(host, $scope) {
    $scope.attr('data-host', host);
  },

  /**
   * Returns host for given scope.
   */
  getHost: function($scope) {
    return $scope.attr('data-host');
  },

  /**
   * Turns given path into an absolute url.
   */
  getUrl: function(path, host) {
    if (path.match(/https?:\/\//)) { return path }
    return host + path;
  },

  /**
   * Returns relative path from url.
   */
  getPath: function(url) {
    return url.replace(/https?:\/\/[^\/]+/,'')
  },

  /**
   * Rewrites links to absolute urls.
   */
  setUrls: function($scope) {
    var host = this.getHost($scope);

    // Rewrite links
    $('a[href]:not([href^=http])', $scope).each(function(e) {
      var href = $(this).attr('href');
      $(this).attr('href', vidibus.xss.getUrl(href, host));
    });

    // Rewrite forms
    $('form[action]', $scope).each(function(e) {
      var action = $(this).attr('action');
      $(this).attr('href', vidibus.xss.getUrl(action, host));
    });
  },

  /**
   * Set xss actions for interactive elements.
   */
  setActions: function($scope) {
    var host = this.getHost($scope);

    // Set action for GET links
    // TODO: Allow links to be flagged as "external"
    $('a[href^='+host+']:not([data-method],[data-remote])', $scope).bind('click.xss', function(e) {
      var href = $(this).attr('href');
      vidibus.xss.get(href, $scope);
      e.preventDefault();
    });

    // Set action non-GET links
    // TODO: Remove bindings from links that match current host only: a[data-method][href^='+host+']:not([data-remote])
    $('a[data-method]:not([data-remote])').die('click').unbind('click'); // remove bindings
    $('a[data-method][href^='+host+']:not([data-remote])', $scope).click(function(e) {
      var $link = $(this),
          path = $link.attr('href'),
          url = vidibus.xss.buildUrl(path, $scope);
      $(this).callAjax(url);
      e.preventDefault();
    });

    // Set form action
    $('form[action]').die('submit').unbind('submit') // remove bindings
    $('form[action][href^='+host+']', $scope).submit(function(e) {
      var $form = $(this),
          path = $form.attr('action');
          url = vidibus.xss.buildUrl(path, $scope);
      $(this).callAjax(url);
      return false;
    });
  },

  /**
   * Modifies paths within given html string.
   * This method must be called before embedding html snippet into the page
   * to avoid loading errors from invalid relative paths.
   */
  transformPaths: function(html, $scope) {
    var match, url;
    while (match = html.match(/src="((?!http)[^"]+)"/)) {
      url = vidibus.xss.buildUrl(match[1], $scope);
      html = html.replace(match[0], 'src="'+url+'"')
    }
    return html;
  },

  /**
   * Load XSS sources from given url.
   * If url is empty, the current location will be used.
   */
  loadUrl: function(url, reload) {
    var scope, $scope, path, loaded, params = $.deparam.fragment();
    for(scope in params) {
      path = params[scope];

      // don't reload locations that have already been loaded
      loaded = this.loadedUrls[scope];
      if((!reload || !reload[scope]) && loaded && loaded == path) {
        continue;
      }

      $scope = $('#'+scope);
      if($scope[0] == undefined) {
        console.log('Scope not found: '+scope);
      } else {
        this.loadedUrls[scope] = path;
        this.loadData(path, $scope);
      }
    }
  },

  /**
   * Load relative path into given scope.
   * Transforms scope host and path into a XSS location.
   */
  loadData: function(path, $scope) {
    var url = this.buildUrl(path, $scope, true);
    $.ajax({
      url: url,
      data: [],
      dataType: 'script',
      type: 'GET'
    });
  },

  /**
   * Tranforms local paths to absolute ones.
   */
  buildUrl: function(path, $scope, uncached) {
    var parts = path.split("?");
    path = parts[0];
    params = parts[1];
    if(params) {
      params = params.split("&");
    } else {
      params = []
    }

    var host = this.getHost($scope),
      scope = $scope.attr('id'),
      url = this.getUrl(path, host)

    if (!url.match(/\.[a-z]+((\?|\#).*)?$/)) url += '.xss';
    if (url.indexOf('.xss') > -1 && params.toString().indexOf('scope='+scope) == -1) {
      params.push('scope='+scope);
    }

    // add cache buster
    if (uncached) {
      var d = new Date();
      params.push(d.getTime());
    }

    // append params
    if (params.length) {
      if (url.indexOf('?') == -1) url += '?'
      url += params.join('&');
    }

    return url;
  }
};

/**
 * Detect changes to document's location.
 *
 */
$(function($){

  // Detect changes of document.location and trigger loading.
  $(window).bind('hashchange', function(e) {
    if (vidibus.loader.complete) {
      vidibus.xss.loadUrl();
    }
    return false;
  });

  // Since the event is only triggered when the hash changes, we need
  // to trigger the event now, to handle the hash the page may have
  // loaded with.
  $(window).trigger('hashchange');
});

/**
 * Implement ajax handler.
 * This is the default handler provided in rails.js extended to accept delete method.
 */
$(function($) {

  /**
   * Extend xhr object to send credentials and force XMLHttpRequest.
   */
  extendXhr = function(xhr) {
    xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    try {
      xhr.withCredentials = "true";
    } catch(e) {
      alert('Cannot set xhr with credentials:\n'+e)
    }
  };

  /**
   * Extends xhr on beforeSend by binding to Rails' ajax:loading event.
   */
  $("body").bind('ajax:loading', function(e, xhr) {
    extendXhr(xhr);
  });

  /**
   * Try to send xhr request withCredentials.
   * Unfortunately, this has to be set after the connection has been opened.
   * If you set a beforeSend handler yourself, you have to set withCredentials by yourself.
   */
  $.ajaxSettings.beforeSend = function(xhr) {
    extendXhr(xhr);
  };
});
<commit_msg>Changed API from "host" to "selector" argument<commit_after>// TODO: Support cross-domain AJAX requests in IE:
// if ($.browser.msie && window.XDomainRequest) {
//     // Use Microsoft XDR
//     var xdr = new XDomainRequest();
//     xdr.open('get', url);
//     xdr.onload = function() {
//         // XDomainRequest doesn't provide responseXml, so if you need it:
//         var dom = new ActiveXObject('Microsoft.XMLDOM');
//         dom.async = false;
//         dom.loadXML(xdr.responseText);
//     };
//     xdr.send();
// } else {
//     $.ajax({...});
// }

vidibus.xss = {
  initialized: {},      // holds true for every scope that has been initialized
  fileExtension: 'xss', // use 'xss' as file extension
  loadedUrls: {},       // store urls currently loaded in each scope

  ready: function() {
    // TODO: implement real event handler
  },

  /**
   * Detects scope of script block to be executed.
   * Must be called from embedding page.
   */
  detectScope: function() {
    document.write('

');
    var $detector = $("#scopeDetector");
    var $scope = $detector.parent();
    $detector.remove();
    return $scope;
  },

  /**
   * Usage:
   *   vidibus.xss.embed('
Some HTML
', $('#scope') [, ".some element"]);
   */
  embed: function(html, $scope, selector) {
    html = this.transformPaths(html, $scope); // Transform local paths before embedding html into page!
    if (selector) {
      $(selector, $scope).html(html);
    } else {
      $scope.html(html);
    }
    this.setUrls($scope);
    this.setActions($scope);
    this.ready();
  },

  /**
   * Calls given path for given scope. If a third parameter is set to true,
   * the location will be loaded, even if it is currently loaded.
   *
   * Usage:
   *   vidibus.xss.get('path', $('#scope') [, true]);
   *
   * If no host is provided, host of scope will be used.
   */
  get: function(path, $scope, reload) {
    // Escape query parts
    path = path.replace("?", "%3F").replace("&", "%26").replace("=", "%3D");

    var scopeId = $scope[0].id,
        params = scopeId+'='+this.getPath(path),
        keepCurrentLocation = this.initialized[scopeId] ? 0 : 1,
        location = $.param.fragment(String(document.location), params, keepCurrentLocation),
        reloadScope = {};

    this.initialized[scopeId] = true;
    window.location.href = location; // write history
    reloadScope[scopeId] = reload;
    this.loadUrl(location, reloadScope);
  },

  /**
   * Redirect to given path while forcing reloading.
   */
  redirect: function(path, $scope) {
    this.get(path, $scope, true)
  },

  /**
   * Handles callback action.
   *
   * Requires data.status:
   *   redirect, TODO: ok, error
   *
   * Accepts actions depending on status:
   *   (redirect) to: Performs redirect to location.
   */
  callback: function(data, $scope) {
    if (data.status == 'redirect') {
      this.redirect(data.to, $scope);
    }
  },

  /**
   * Sets host for given scope.
   */
  setHost: function(host, $scope) {
    $scope.attr('data-host', host);
  },

  /**
   * Returns host for given scope.
   */
  getHost: function($scope) {
    return $scope.attr('data-host');
  },

  /**
   * Turns given path into an absolute url.
   */
  getUrl: function(path, host) {
    if (path.match(/https?:\/\//)) { return path }
    return host + path;
  },

  /**
   * Returns relative path from url.
   */
  getPath: function(url) {
    return url.replace(/https?:\/\/[^\/]+/,'')
  },

  /**
   * Rewrites links to absolute urls.
   */
  setUrls: function($scope) {
    var host = this.getHost($scope);

    // Rewrite links
    $('a[href]:not([href^=http])', $scope).each(function(e) {
      var href = $(this).attr('href');
      $(this).attr('href', vidibus.xss.getUrl(href, host));
    });

    // Rewrite forms
    $('form[action]', $scope).each(function(e) {
      var action = $(this).attr('action');
      $(this).attr('href', vidibus.xss.getUrl(action, host));
    });
  },

  /**
   * Set xss actions for interactive elements.
   */
  setActions: function($scope) {
    var host = this.getHost($scope);

    // Set action for GET links
    // TODO: Allow links to be flagged as "external"
    $('a[href^='+host+']:not([data-method],[data-remote])', $scope).bind('click.xss', function(e) {
      var href = $(this).attr('href');
      vidibus.xss.get(href, $scope);
      e.preventDefault();
    });

    // Set action non-GET links
    // TODO: Remove bindings from links that match current host only: a[data-method][href^='+host+']:not([data-remote])
    $('a[data-method]:not([data-remote])').die('click').unbind('click'); // remove bindings
    $('a[data-method][href^='+host+']:not([data-remote])', $scope).click(function(e) {
      var $link = $(this),
          path = $link.attr('href'),
          url = vidibus.xss.buildUrl(path, $scope);
      $(this).callAjax(url);
      e.preventDefault();
    });

    // Set form action
    $('form[action]').die('submit').unbind('submit') // remove bindings
    $('form[action][href^='+host+']', $scope).submit(function(e) {
      var $form = $(this),
          path = $form.attr('action');
          url = vidibus.xss.buildUrl(path, $scope);
      $(this).callAjax(url);
      return false;
    });
  },

  /**
   * Modifies paths within given html string.
   * This method must be called before embedding html snippet into the page
   * to avoid loading errors from invalid relative paths.
   */
  transformPaths: function(html, $scope) {
    var match, url;
    while (match = html.match(/src="((?!http)[^"]+)"/)) {
      url = vidibus.xss.buildUrl(match[1], $scope);
      html = html.replace(match[0], 'src="'+url+'"')
    }
    return html;
  },

  /**
   * Load XSS sources from given url.
   * If url is empty, the current location will be used.
   */
  loadUrl: function(url, reload) {
    var scope, $scope, path, loaded, params = $.deparam.fragment();
    for(scope in params) {
      path = params[scope];

      // don't reload locations that have already been loaded
      loaded = this.loadedUrls[scope];
      if((!reload || !reload[scope]) && loaded && loaded == path) {
        continue;
      }

      $scope = $('#'+scope);
      if($scope[0] == undefined) {
        console.log('Scope not found: '+scope);
      } else {
        this.loadedUrls[scope] = path;
        this.loadData(path, $scope);
      }
    }
  },

  /**
   * Load relative path into given scope.
   * Transforms scope host and path into a XSS location.
   */
  loadData: function(path, $scope) {
    var url = this.buildUrl(path, $scope, true);
    $.ajax({
      url: url,
      data: [],
      dataType: 'script',
      type: 'GET'
    });
  },

  /**
   * Tranforms local paths to absolute ones.
   */
  buildUrl: function(path, $scope, uncached) {
    var parts = path.split("?");
    path = parts[0];
    params = parts[1];
    if(params) {
      params = params.split("&");
    } else {
      params = []
    }

    var host = this.getHost($scope),
      scope = $scope.attr('id'),
      url = this.getUrl(path, host)

    if (!url.match(/\.[a-z]+((\?|\#).*)?$/)) url += '.xss';
    if (url.indexOf('.xss') > -1 && params.toString().indexOf('scope='+scope) == -1) {
      params.push('scope='+scope);
    }

    // add cache buster
    if (uncached) {
      var d = new Date();
      params.push(d.getTime());
    }

    // append params
    if (params.length) {
      if (url.indexOf('?') == -1) url += '?'
      url += params.join('&');
    }

    return url;
  }
};

/**
 * Detect changes to document's location.
 *
 */
$(function($){

  // Detect changes of document.location and trigger loading.
  $(window).bind('hashchange', function(e) {
    if (vidibus.loader.complete) {
      vidibus.xss.loadUrl();
    }
    return false;
  });

  // Since the event is only triggered when the hash changes, we need
  // to trigger the event now, to handle the hash the page may have
  // loaded with.
  $(window).trigger('hashchange');
});

/**
 * Implement ajax handler.
 * This is the default handler provided in rails.js extended to accept delete method.
 */
$(function($) {

  /**
   * Extend xhr object to send credentials and force XMLHttpRequest.
   */
  extendXhr = function(xhr) {
    xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    try {
      xhr.withCredentials = "true";
    } catch(e) {
      alert('Cannot set xhr with credentials:\n'+e)
    }
  };

  /**
   * Extends xhr on beforeSend by binding to Rails' ajax:loading event.
   */
  $("body").bind('ajax:loading', function(e, xhr) {
    extendXhr(xhr);
  });

  /**
   * Try to send xhr request withCredentials.
   * Unfortunately, this has to be set after the connection has been opened.
   * If you set a beforeSend handler yourself, you have to set withCredentials by yourself.
   */
  $.ajaxSettings.beforeSend = function(xhr) {
    extendXhr(xhr);
  };
});
<|endoftext|>


<commit_before>var add_image_handlers = function() {
  // Remove duplicate image from gallery
  $("#main-image a.large-image").click(function(event) {
    $('#main-image a.click-to-zoom').attr('rel', '');
  });

  // Remove duplicate image from gallery
  $("#main-image a.click-to-zoom").click(function(event) {
    $('#main-image a.large-image').attr('rel', '');
  });

  $("#main-image").data('selectedThumb', $('#main-image img').first().attr('src'));
  $('ul.thumbnails li').eq(0).addClass('selected');

  $('ul.thumbnails a').on('click', function(event) {
    // Append old image to gallery and remove new main-image from gallery to
    // prevent image duplication
    $('#gallery').append('');
    $('#gallery a[href="' + $(this).data("original") + '"]').remove();

    $("#main-image").data('selectedThumb', $(event.currentTarget).attr('href'));
    $("#main-image").data('selectedThumbId', $(event.currentTarget).parent().attr('id'));
    $('a.zoom-image').attr('href', $(this).data('original'));
    $(this).mouseout(function() {
      $('ul.thumbnails li').removeClass('selected');
      $(event.currentTarget).parent('li').addClass('selected');
    });
    return false;
  });
  $('ul.thumbnails li').on('mouseenter', function(event) {
    $('#main-image img').first().attr('src', $(event.currentTarget).find('a').attr('href'));
  });
  $('ul.thumbnails li').on('mouseleave', function(event) {
    $('#main-image img').first().attr('src', $("#main-image").data('selectedThumb'));
  });
};

var show_variant_images = function(variant_id) {
  $('li.vtmb').hide();
  $('li.vtmb-' + variant_id).show();
  var currentThumb = $('#' + $("#main-image").data('selectedThumbId'));
  // if currently selected thumb does not belong to current variant, nor to common images,
  // hide it and select the first available thumb instead.
  if(!currentThumb.hasClass('vtmb-' + variant_id) && !currentThumb.hasClass('tmb-all')) {
    var thumb = $($('ul.thumbnails li:visible').eq(0));
    var newImg = thumb.find('a').attr('href');
    $('ul.thumbnails li').removeClass('selected');
    thumb.addClass('selected');
    $('#main-image img').first().attr('src', newImg);
    $("#main-image").data('selectedThumb', newImg);
    $("#main-image").data('selectedThumbId', thumb.attr('id'));
  }
}

$(document).ready(function() {
    $(".fancybox").fancybox({
      'beforeClose': function() {
        $('#main-image a.click-to-zoom').attr('rel', 'gallery');
        $('#main-image a.large-image').attr('rel', 'gallery');
      }
    });
});
<commit_msg>Fix zoom button being replaced by thumbnail<commit_after>var add_image_handlers, show_variant_images, update_variant_price;

add_image_handlers = function() {
  // Remove duplicate image from gallery
  $("#main-image a.large-image").click(function(event) {
    $('#main-image a.click-to-zoom').attr('rel', '');
  });

  // Remove duplicate image from gallery
  $("#main-image a.click-to-zoom").click(function(event) {
    $('#main-image a.large-image').attr('rel', '');
  });

  ($('#main-image')).data('selectedThumb', ($('#main-image img')).attr('src'));
  ($('ul.thumbnails li')).eq(0).addClass('selected');
  ($('ul.thumbnails a')).on('click', function(event) {
    // Append old image to gallery and remove new main-image from gallery to
    // prevent image duplication
    $('#gallery').append('');
    $('#gallery a[href="' + $(this).data("original") + '"]').remove();

    ($('#main-image')).data('selectedThumb', ($(event.currentTarget)).attr('href'));
    ($('#main-image')).data('selectedThumbId', ($(event.currentTarget)).parent().attr('id'));
    ($(this)).mouseout(function() {
      ($('ul.thumbnails li')).removeClass('selected');
      return ($(event.currentTarget)).parent('li').addClass('selected');
    });
    return false;
  });

  $('ul.thumbnails li').on('mouseenter', function(event) {
    $('.click-to-zoom').attr('src', '/assets/zoom.gif');
    // $('#main-image img').first().attr('src', $(event.currentTarget).find('a').attr('href'));
  });
  $('ul.thumbnails li').on('mouseleave', function(event) {
    $('.click-to-zoom').attr('src', '/assets/zoom.gif');
    // $('#main-image img').first().attr('src', $("#main-image").data('selectedThumb'));
  });
};

show_variant_images = function(variant_id) {
  var currentThumb, newImg, thumb;
  ($('li.vtmb')).hide();
  ($('li.vtmb-' + variant_id)).show();
  currentThumb = $('#' + ($('#main-image')).data('selectedThumbId'));
  if (!currentThumb.hasClass('vtmb-' + variant_id) && !currentThumb.hasClass('tmb-all')) {
    thumb = $(($('ul.thumbnails li:visible')).eq(0));
    newImg = thumb.find('a').attr('href');
    ($('ul.thumbnails li')).removeClass('selected');
    thumb.addClass('selected');
    ($('#main-image img')).attr('src', newImg);
    ($('#main-image')).data('selectedThumb', newImg);
    return ($('#main-image')).data('selectedThumbId', thumb.attr('id'));
  }
};

$(document).ready(function() {
    add_image_handlers();
    $('.click-to-zoom').attr('src', '/assets/zoom.gif');
    $(".fancybox").fancybox({
      'beforeClose': function() {
        $('#main-image a.click-to-zoom').attr('rel', 'gallery');
        $('#main-image a.large-image').attr('rel', 'gallery');
      }
    });
});
<|endoftext|>


<commit_before>import React from "react";
import BlogItemRow from "./BlogItemRow";
import BlogEntryCreator from "./BlogEntryCreator";

export default class Body extends React.Component {
    constructor() {
        super();
        //mock-up data
        this.data = [

            {
                "id": 1,
                "title": "Title 1",
                "content": "This is the content of the blog post 1."
            },

            {
                "id": 2,
                "title": "Title 2",
                "content": "This is the content of the blog post 2."
            },

            {
                "id": 3,
                "title": "Title 3",
                "content": "This is the content of the blog post 3."
            }
        ];
    }

    parseData () {
        let pairs = [],
            data = this.data;

        for(let i in data){
            pairs.push(
                <BlogItemRow
                    key={this.data[i].id}
                    id={this.data[i].id}
                    title={this.data[i].title}
                    content={this.data[i].content}
                />
            );
        }
        return pairs;
    }

    render() {
        return (

                { this.parseData() }
                <BlogEntryCreator />

        );
    }
}
<commit_msg>[ADD] Implement Store & Actions on `Body` component<commit_after>import React from "react";
import BlogItemRow from "./BlogItemRow";
import BlogEntryCreator from "./BlogEntryCreator";
import BlogStore from "../../stores/BlogStore";
import * as BlogActions from "../../actions/BlogActions";

export default class Body extends React.Component {
    constructor() {
        super();
        this.state = {
            blogs: BlogStore.getAll()
        };
        this.reloadBlogs();
    }

    componentWillMount() {
        BlogStore.on("change", this.getBlogs.bind(this));
    }

    componentWillUnmount() {
        BlogStore.removeListener("change", this.getBlogs);
    }

    getBlogs() {
        this.setState({
            blogs: BlogStore.getAll()
        });
    }

    reloadBlogs() {
        setInterval( () => {
            console.log('fetching...');
            BlogActions.reloadBlogs();
        },5000);
    }

    render() {
        const { blogs } = this.state;
        const BlogEntries = blogs.map((entry) => {
            return  <BlogItemRow
                        key={entry.id}
                        id={entry.id}
                        title={entry.title}
                        content={entry.content}
                    />
        });

        return (

                { BlogEntries }
                <BlogEntryCreator />

        );
    }
}
<|endoftext|>


<commit_before>'use strict';
import React from 'react';
import { render } from 'react-dom';
import mapboxgl from 'mapbox-gl';
import _ from 'lodash';
import moment from 'moment';
import distance from 'turf-distance';

import { convertParamIfNeeded, circleOpacity, circleBlur,
         coloredCircleRadius, borderCircleRadius, selectCircleRadius, selectShadowCircleRadius, locationShadowCircleRadius } from '../utils/map-settings';
import { generateColorStops } from '../utils/colors';
// import  from '../utils/color-scale';

import config from '../config';
mapboxgl.accessToken = config.mapbox.token;

const MapComponent = React.createClass({
  displayName: 'MapComponent',

  propTypes: {
    measurements: React.PropTypes.array,
    highlightLoc: React.PropTypes.string,
    parameter: React.PropTypes.object,
    center: React.PropTypes.array,
    bbox: React.PropTypes.array,
    zoom: React.PropTypes.number,
    disableScrollZoom: React.PropTypes.bool,
    children: React.PropTypes.object
  },

  nearbyKm: 10,

  // The map element.
  map: null,

  popover: null,

  //
  // Start events methods
  //

  nearbyLocationClick: function (location, e) {
    e.preventDefault();
    let data = _.find(this.props.measurements, {location: location});
    this.map.panTo([data.coordinates.longitude, data.coordinates.latitude]);
    this.showPopover(data);

    // We click a nearby location we need to show the popup but also
    // select the appropriate point. However the point needs feature data.
    // From the measurement data we regenerate the feature and pass it to the
    // selectPoint function.
    // Another option would be to do a map query for features with the point
    // projected from the coordinates, but that's slower for sure.
    // Code follows anyway:
    // let projectedPoint = this.map.project([data.coordinates.longitude, data.coordinates.latitude]);
    // let features = this.map.queryRenderedFeatures(projectedPoint, { layers: ['measurements'] });
    // this.selectPoint(features[0]);
    this.selectPoint(this.generateFeature(data));
  },

  locationPageSetup: function () {
    let data = _.find(this.props.measurements, {location: this.props.highlightLoc});
    if (!data) return;
    this.showPopover(data);
    this.selectLocationPoint(this.generateFeature(data));
  },

  setupMapEvents: function () {
    // When a click event occurs near a place, open a popup at the location of
    // the feature, with description HTML from its properties.
    this.map.on('click', (e) => {
      let features = this.map.queryRenderedFeatures(e.point, { layers: ['measurements'] });

      if (!features.length) {
        // Unselect point.
        this.selectPoint(null);
        return;
      }

      // Popover.
      let data = _.find(this.props.measurements, {location: features[0].properties.location});
      this.showPopover(data);
      this.selectPoint(features[0].toJSON());
    });

    // Use the same approach as above to indicate that the symbols are clickable
    // by changing the cursor style to 'pointer'.
    this.map.on('mousemove', (e) => {
      let features = this.map.queryRenderedFeatures(e.point, { layers: ['measurements'] });
      this.map.getCanvas().style.cursor = (features.length) ? 'pointer' : '';
    });
  },

  showPopover: function (data) {
    let measurement = _.find(data.measurements, {parameter: this.props.parameter.id});

    // Find the nearby ones.
    let start = {
      'type': 'Feature',
      'geometry': {
        'type': 'Point',
        'coordinates': [
          data.coordinates.longitude,
          data.coordinates.latitude
        ]
      }
    };

    let nearby = _(_.cloneDeep(this.props.measurements))
      .map(o => {
        let end = {
          'type': 'Feature',
          'geometry': {
            'type': 'Point',
            'coordinates': [
              o.coordinates.longitude,
              o.coordinates.latitude
            ]
          }
        };
        o.distance = distance(start, end, 'kilometers');
        return o;
      })
      .sortBy('distance')
      .filter(o => o.distance !== 0 && o.distance <= this.nearbyKm)
      .take(2)
      .value();

    let popoverContent = document.createElement('div');
    render(<MapPopover
      location={data.location}
      measurement={measurement}
      parameter={this.props.parameter}
      nearbyKm={this.nearbyKm}
      nearby={nearby}
      nearbyClick={this.nearbyLocationClick} />, popoverContent);

    // Populate the popup and set its coordinates
    // based on the feature found.
    if (this.popover != null) {
      this.popover.remove();
    }

    this.popover = new mapboxgl.Popup({closeButton: false})
      .setLngLat([data.coordinates.longitude, data.coordinates.latitude])
      .setDOMContent(popoverContent)
      .addTo(this.map);
  },

  selectPoint: function (feature) {
    if (this.map.getSource('selectedPoint')) {
      this.map.getLayer('selectedPointShadow') && this.map.removeLayer('selectedPointShadow');
      this.map.getLayer('selectedPointHighlight') && this.map.removeLayer('selectedPointHighlight');
      this.map.getLayer('selectedPoint') && this.map.removeLayer('selectedPoint');
      this.map.getSource('selectedPoint') && this.map.removeSource('selectedPoint');
    }

    // Passing feature: null clears selection.
    if (feature === null) {
      return;
    }

    this.map.addSource('selectedPoint', {
      'type': 'geojson',
      'data': feature
    });
    // Add Shadow
    this.map.addLayer({
      'id': 'selectedPointShadow',
      'type': 'circle',
      'source': 'selectedPoint',
      'paint': {
        'circle-color': '#000',
        'circle-opacity': 0.3,
        'circle-radius': selectShadowCircleRadius,
        'circle-blur': 0.5,
        'circle-translate': [0.5, 0.5]
      }
    });
    // Add Highlight
    this.map.addLayer({
      'id': 'selectedPointHighlight',
      'type': 'circle',
      'source': 'selectedPoint',
      'paint': {
        'circle-color': '#fff',
        'circle-opacity': 1,
        'circle-radius': selectCircleRadius,
        'circle-blur': 0
      }
    });
    // Re-add fill by value
    this.map.addLayer({
      'id': 'selectedPoint',
      'type': 'circle',
      'source': 'selectedPoint',
      'paint': {
        'circle-color': {
          property: 'value',
          stops: generateColorStops(this.props.parameter.id)
        },
        'circle-opacity': 1,
        'circle-radius': coloredCircleRadius,
        'circle-blur': 0
      }
    });
  },

  selectLocationPoint: function (feature) {
    // Passing feature: null clears selection.
    if (feature === null) {
      return;
    }

    this.map.addSource('locationPoint', {
      'type': 'geojson',
      'data': feature
    });
    // Add Shadow
    this.map.addLayer({
      'id': 'locationPointShadow',
      'type': 'circle',
      'source': 'locationPoint',
      'paint': {
        'circle-color': '#000',
        'circle-opacity': 0.6,
        'circle-radius': locationShadowCircleRadius,
        'circle-blur': 0.5,
        'circle-translate': [0.5, 0.5]
      }
    });
    // Add Highlight
    this.map.addLayer({
      'id': 'locationPointHighlight',
      'type': 'circle',
      'source': 'locationPoint',
      'paint': {
        'circle-color': '#fff',
        'circle-opacity': 1,
        'circle-radius': selectCircleRadius,
        'circle-blur': 0
      }
    });
    // Re-add fill by value
    this.map.addLayer({
      'id': 'locationPoint',
      'type': 'circle',
      'source': 'locationPoint',
      'paint': {
        'circle-color': {
          property: 'value',
          stops: generateColorStops(this.props.parameter.id)
        },
        'circle-opacity': 1,
        'circle-radius': coloredCircleRadius,
        'circle-blur': 0
      }
    });
  },

  //
  // Start data methods
  //

  // Date control. To avoid constructing new moment objects.
  dayAgo: null,

  generateFeature: function (locMeasurement) {
    let param = _.find(locMeasurement.measurements, {parameter: this.props.parameter.id});
    // If there's no value for the active param return null.
    // Null values will be filtered out.
    if (!param) {
      return null;
    }

    let val = param ? convertParamIfNeeded(param) : -1;
    val = val < 0 ? -1 : val;

    if (this.dayAgo === null) {
      this.dayAgo = moment.utc().subtract(1, 'day').toISOString();
    }
    // Check if the value is older than 24 hours and style it differently.
    if (param && param.lastUpdated < this.dayAgo) {
      val = -1;
    }

    return {
      type: 'Feature',
      properties: {
        location: locMeasurement.location,
        // Controls which color is applied to the point.
        // Needs to be the points measurement.
        value: val
      },
      geometry: {
        type: 'Point',
        coordinates: [
          locMeasurement.coordinates.longitude,
          locMeasurement.coordinates.latitude
        ]
      }
    };
  },

  generateSourceData: function () {
    return {
      type: 'FeatureCollection',
      features: this.props.measurements.map(o => this.generateFeature(o)).filter(o => o !== null)
    };
  },

  setupMapData: function () {
    const source = this.generateSourceData();

    this.map.addSource('measurements', {
      'type': 'geojson',
      'data': source
    });

    // Layer for outline
    this.map.addLayer({
      'id': 'pointOutlines',
      'source': 'measurements',
      'type': 'circle',
      'paint': {
        'circle-color': {
          property: 'value',
          stops: generateColorStops(this.props.parameter.id, 'dark')
        },
        'circle-opacity': 1,
        'circle-radius': borderCircleRadius,
        'circle-blur': 0
      }
    });

    this.map.addLayer({
      'id': 'measurements',
      'source': 'measurements',
      'type': 'circle',
      'paint': {
        'circle-color': {
          property: 'value',
          stops: generateColorStops(this.props.parameter.id)
        },
        'circle-opacity': circleOpacity,
        'circle-radius': coloredCircleRadius,
        'circle-blur': circleBlur
      }
    });
  },

  //
  // Start life-cycle methods
  //

  componentDidUpdate: function (prevProps) {
    if (this.props.parameter.id !== prevProps.parameter.id) {
      // We need to update. Delete source + layers and setup again.
      this.map.getSource('measurements') && this.map.removeSource('measurements');
      this.map.getLayer('pointOutlines') && this.map.removeLayer('pointOutlines');
      this.map.getLayer('measurements') && this.map.removeLayer('measurements');
      this.setupMapData();
    }
  },

  componentDidMount: function () {
    if (!this.props.center && !this.props.bbox) {
      throw new Error('At least center or bbox has to be provided to MapComponent');
    }

    this.map = new mapboxgl.Map({
      container: this.refs.map,
      center: this.props.center || [0, 0],
      zoom: this.props.zoom,
      style: config.mapbox.baseStyle,
      maxBounds: [[-180, -84], [180, 84]]
    });

    if (this.props.bbox) {
      this.map.fitBounds([
        [this.props.bbox[0], this.props.bbox[1]],
        [this.props.bbox[2], this.props.bbox[3]]
      ]);
    }

    if (this.props.disableScrollZoom) {
      this.map.scrollZoom.disable();
      this.map.addControl(new mapboxgl.Navigation());
    }

    this.map.on('load', () => {
      this.setupMapData();
      this.setupMapEvents();
      // There is probably a better test for this if statement.
      if (this.props.highlightLoc) {
        this.locationPageSetup();
      }
    });
  },

  //
  // Start render methods
  //

  render: function () {
    return (

          {/* Map renders on componentDidMount. */}

          {this.props.children}

    );
  }
});

module.exports = MapComponent;

const MapPopover = React.createClass({
  displayName: 'MapPopover',

  propTypes: {
    location: React.PropTypes.string,
    measurement: React.PropTypes.object,
    parameter: React.PropTypes.object,
    nearbyKm: React.PropTypes.number,
    nearby: React.PropTypes.array,
    nearbyClick: React.PropTypes.func
  },

  renderNearby: function () {
    if (!this.props.nearby.length) {
      return
There are no locations within {this.props.nearbyKm}Km
;
    }

    return (

Showing {this.props.nearby.length} other locations within {this.props.nearbyKm}km

          {this.props.nearby.map(o => {
            let measurement = _.find(o.measurements, {parameter: this.props.parameter.id});
            return (

                {measurement
                  ? {measurement.value} {measurement.unit}
                  : {this.props.parameter.name} N/A}
                <a onClick={this.props.nearbyClick.bind(null, o.location)} href='' title={`Open ${o.location} popover`}>{o.location}

            );
          })}

    );
  },

  render: function () {
    let m = this.props.measurement;
    let reading =
{this.props.parameter.name} N/A
;
    if (m) {
      let lastUp = moment.utc(m.lastUpdated).format('YYYY/MM/DD HH:mm');
      reading =
Last reading {m.value}{m.unit} at {lastUp}
;
    }

    return (

<a href={`#/location/${encodeURIComponent(this.props.location)}`} title={`View ${this.props.location} page`}>{this.props.location}

            {reading}

              { /*
                Using `a` instead of `Link` because these are rendered outside
                the router context and `Link` needs that context to work.
              */ }

<a href={`#/compare/${encodeURIComponent(this.props.location)}`} className='button button--primary-bounded' title={`Compare ${this.props.location} with other locations`}>Compare

<a href={`#/location/${encodeURIComponent(this.props.location)}`} title={`View ${this.props.location} page`}className='button button--primary-bounded'>View More

            {this.renderNearby()}

    );
  }
});
<commit_msg>Round value to prevent line break<commit_after>'use strict';
import React from 'react';
import { render } from 'react-dom';
import mapboxgl from 'mapbox-gl';
import _ from 'lodash';
import moment from 'moment';
import distance from 'turf-distance';

import { convertParamIfNeeded, circleOpacity, circleBlur,
         coloredCircleRadius, borderCircleRadius, selectCircleRadius, selectShadowCircleRadius, locationShadowCircleRadius } from '../utils/map-settings';
import { generateColorStops } from '../utils/colors';
import { round } from '../utils/format';
// import  from '../utils/color-scale';

import config from '../config';
mapboxgl.accessToken = config.mapbox.token;

const MapComponent = React.createClass({
  displayName: 'MapComponent',

  propTypes: {
    measurements: React.PropTypes.array,
    highlightLoc: React.PropTypes.string,
    parameter: React.PropTypes.object,
    center: React.PropTypes.array,
    bbox: React.PropTypes.array,
    zoom: React.PropTypes.number,
    disableScrollZoom: React.PropTypes.bool,
    children: React.PropTypes.object
  },

  nearbyKm: 10,

  // The map element.
  map: null,

  popover: null,

  //
  // Start events methods
  //

  nearbyLocationClick: function (location, e) {
    e.preventDefault();
    let data = _.find(this.props.measurements, {location: location});
    this.map.panTo([data.coordinates.longitude, data.coordinates.latitude]);
    this.showPopover(data);

    // We click a nearby location we need to show the popup but also
    // select the appropriate point. However the point needs feature data.
    // From the measurement data we regenerate the feature and pass it to the
    // selectPoint function.
    // Another option would be to do a map query for features with the point
    // projected from the coordinates, but that's slower for sure.
    // Code follows anyway:
    // let projectedPoint = this.map.project([data.coordinates.longitude, data.coordinates.latitude]);
    // let features = this.map.queryRenderedFeatures(projectedPoint, { layers: ['measurements'] });
    // this.selectPoint(features[0]);
    this.selectPoint(this.generateFeature(data));
  },

  locationPageSetup: function () {
    let data = _.find(this.props.measurements, {location: this.props.highlightLoc});
    if (!data) return;
    this.showPopover(data);
    this.selectLocationPoint(this.generateFeature(data));
  },

  setupMapEvents: function () {
    // When a click event occurs near a place, open a popup at the location of
    // the feature, with description HTML from its properties.
    this.map.on('click', (e) => {
      let features = this.map.queryRenderedFeatures(e.point, { layers: ['measurements'] });

      if (!features.length) {
        // Unselect point.
        this.selectPoint(null);
        return;
      }

      // Popover.
      let data = _.find(this.props.measurements, {location: features[0].properties.location});
      this.showPopover(data);
      this.selectPoint(features[0].toJSON());
    });

    // Use the same approach as above to indicate that the symbols are clickable
    // by changing the cursor style to 'pointer'.
    this.map.on('mousemove', (e) => {
      let features = this.map.queryRenderedFeatures(e.point, { layers: ['measurements'] });
      this.map.getCanvas().style.cursor = (features.length) ? 'pointer' : '';
    });
  },

  showPopover: function (data) {
    let measurement = _.find(data.measurements, {parameter: this.props.parameter.id});

    // Find the nearby ones.
    let start = {
      'type': 'Feature',
      'geometry': {
        'type': 'Point',
        'coordinates': [
          data.coordinates.longitude,
          data.coordinates.latitude
        ]
      }
    };

    let nearby = _(_.cloneDeep(this.props.measurements))
      .map(o => {
        let end = {
          'type': 'Feature',
          'geometry': {
            'type': 'Point',
            'coordinates': [
              o.coordinates.longitude,
              o.coordinates.latitude
            ]
          }
        };
        o.distance = distance(start, end, 'kilometers');
        return o;
      })
      .sortBy('distance')
      .filter(o => o.distance !== 0 && o.distance <= this.nearbyKm)
      .take(2)
      .value();

    let popoverContent = document.createElement('div');
    render(<MapPopover
      location={data.location}
      measurement={measurement}
      parameter={this.props.parameter}
      nearbyKm={this.nearbyKm}
      nearby={nearby}
      nearbyClick={this.nearbyLocationClick} />, popoverContent);

    // Populate the popup and set its coordinates
    // based on the feature found.
    if (this.popover != null) {
      this.popover.remove();
    }

    this.popover = new mapboxgl.Popup({closeButton: false})
      .setLngLat([data.coordinates.longitude, data.coordinates.latitude])
      .setDOMContent(popoverContent)
      .addTo(this.map);
  },

  selectPoint: function (feature) {
    if (this.map.getSource('selectedPoint')) {
      this.map.getLayer('selectedPointShadow') && this.map.removeLayer('selectedPointShadow');
      this.map.getLayer('selectedPointHighlight') && this.map.removeLayer('selectedPointHighlight');
      this.map.getLayer('selectedPoint') && this.map.removeLayer('selectedPoint');
      this.map.getSource('selectedPoint') && this.map.removeSource('selectedPoint');
    }

    // Passing feature: null clears selection.
    if (feature === null) {
      return;
    }

    this.map.addSource('selectedPoint', {
      'type': 'geojson',
      'data': feature
    });
    // Add Shadow
    this.map.addLayer({
      'id': 'selectedPointShadow',
      'type': 'circle',
      'source': 'selectedPoint',
      'paint': {
        'circle-color': '#000',
        'circle-opacity': 0.3,
        'circle-radius': selectShadowCircleRadius,
        'circle-blur': 0.5,
        'circle-translate': [0.5, 0.5]
      }
    });
    // Add Highlight
    this.map.addLayer({
      'id': 'selectedPointHighlight',
      'type': 'circle',
      'source': 'selectedPoint',
      'paint': {
        'circle-color': '#fff',
        'circle-opacity': 1,
        'circle-radius': selectCircleRadius,
        'circle-blur': 0
      }
    });
    // Re-add fill by value
    this.map.addLayer({
      'id': 'selectedPoint',
      'type': 'circle',
      'source': 'selectedPoint',
      'paint': {
        'circle-color': {
          property: 'value',
          stops: generateColorStops(this.props.parameter.id)
        },
        'circle-opacity': 1,
        'circle-radius': coloredCircleRadius,
        'circle-blur': 0
      }
    });
  },

  selectLocationPoint: function (feature) {
    // Passing feature: null clears selection.
    if (feature === null) {
      return;
    }

    this.map.addSource('locationPoint', {
      'type': 'geojson',
      'data': feature
    });
    // Add Shadow
    this.map.addLayer({
      'id': 'locationPointShadow',
      'type': 'circle',
      'source': 'locationPoint',
      'paint': {
        'circle-color': '#000',
        'circle-opacity': 0.6,
        'circle-radius': locationShadowCircleRadius,
        'circle-blur': 0.5,
        'circle-translate': [0.5, 0.5]
      }
    });
    // Add Highlight
    this.map.addLayer({
      'id': 'locationPointHighlight',
      'type': 'circle',
      'source': 'locationPoint',
      'paint': {
        'circle-color': '#fff',
        'circle-opacity': 1,
        'circle-radius': selectCircleRadius,
        'circle-blur': 0
      }
    });
    // Re-add fill by value
    this.map.addLayer({
      'id': 'locationPoint',
      'type': 'circle',
      'source': 'locationPoint',
      'paint': {
        'circle-color': {
          property: 'value',
          stops: generateColorStops(this.props.parameter.id)
        },
        'circle-opacity': 1,
        'circle-radius': coloredCircleRadius,
        'circle-blur': 0
      }
    });
  },

  //
  // Start data methods
  //

  // Date control. To avoid constructing new moment objects.
  dayAgo: null,

  generateFeature: function (locMeasurement) {
    let param = _.find(locMeasurement.measurements, {parameter: this.props.parameter.id});
    // If there's no value for the active param return null.
    // Null values will be filtered out.
    if (!param) {
      return null;
    }

    let val = param ? convertParamIfNeeded(param) : -1;
    val = val < 0 ? -1 : val;

    if (this.dayAgo === null) {
      this.dayAgo = moment.utc().subtract(1, 'day').toISOString();
    }
    // Check if the value is older than 24 hours and style it differently.
    if (param && param.lastUpdated < this.dayAgo) {
      val = -1;
    }

    return {
      type: 'Feature',
      properties: {
        location: locMeasurement.location,
        // Controls which color is applied to the point.
        // Needs to be the points measurement.
        value: val
      },
      geometry: {
        type: 'Point',
        coordinates: [
          locMeasurement.coordinates.longitude,
          locMeasurement.coordinates.latitude
        ]
      }
    };
  },

  generateSourceData: function () {
    return {
      type: 'FeatureCollection',
      features: this.props.measurements.map(o => this.generateFeature(o)).filter(o => o !== null)
    };
  },

  setupMapData: function () {
    const source = this.generateSourceData();

    this.map.addSource('measurements', {
      'type': 'geojson',
      'data': source
    });

    // Layer for outline
    this.map.addLayer({
      'id': 'pointOutlines',
      'source': 'measurements',
      'type': 'circle',
      'paint': {
        'circle-color': {
          property: 'value',
          stops: generateColorStops(this.props.parameter.id, 'dark')
        },
        'circle-opacity': 1,
        'circle-radius': borderCircleRadius,
        'circle-blur': 0
      }
    });

    this.map.addLayer({
      'id': 'measurements',
      'source': 'measurements',
      'type': 'circle',
      'paint': {
        'circle-color': {
          property: 'value',
          stops: generateColorStops(this.props.parameter.id)
        },
        'circle-opacity': circleOpacity,
        'circle-radius': coloredCircleRadius,
        'circle-blur': circleBlur
      }
    });
  },

  //
  // Start life-cycle methods
  //

  componentDidUpdate: function (prevProps) {
    if (this.props.parameter.id !== prevProps.parameter.id) {
      // We need to update. Delete source + layers and setup again.
      this.map.getSource('measurements') && this.map.removeSource('measurements');
      this.map.getLayer('pointOutlines') && this.map.removeLayer('pointOutlines');
      this.map.getLayer('measurements') && this.map.removeLayer('measurements');
      this.setupMapData();
    }
  },

  componentDidMount: function () {
    if (!this.props.center && !this.props.bbox) {
      throw new Error('At least center or bbox has to be provided to MapComponent');
    }

    this.map = new mapboxgl.Map({
      container: this.refs.map,
      center: this.props.center || [0, 0],
      zoom: this.props.zoom,
      style: config.mapbox.baseStyle,
      maxBounds: [[-180, -84], [180, 84]]
    });

    if (this.props.bbox) {
      this.map.fitBounds([
        [this.props.bbox[0], this.props.bbox[1]],
        [this.props.bbox[2], this.props.bbox[3]]
      ]);
    }

    if (this.props.disableScrollZoom) {
      this.map.scrollZoom.disable();
      this.map.addControl(new mapboxgl.Navigation());
    }

    this.map.on('load', () => {
      this.setupMapData();
      this.setupMapEvents();
      // There is probably a better test for this if statement.
      if (this.props.highlightLoc) {
        this.locationPageSetup();
      }
    });
  },

  //
  // Start render methods
  //

  render: function () {
    return (

          {/* Map renders on componentDidMount. */}

          {this.props.children}

    );
  }
});

module.exports = MapComponent;

const MapPopover = React.createClass({
  displayName: 'MapPopover',

  propTypes: {
    location: React.PropTypes.string,
    measurement: React.PropTypes.object,
    parameter: React.PropTypes.object,
    nearbyKm: React.PropTypes.number,
    nearby: React.PropTypes.array,
    nearbyClick: React.PropTypes.func
  },

  renderNearby: function () {
    if (!this.props.nearby.length) {
      return
There are no locations within {this.props.nearbyKm}Km
;
    }

    return (

Showing {this.props.nearby.length} other locations within {this.props.nearbyKm}km

          {this.props.nearby.map(o => {
            let measurement = _.find(o.measurements, {parameter: this.props.parameter.id});
            return (

                {measurement
                  ? {round(measurement.value)} {measurement.unit}
                  : {this.props.parameter.name} N/A}
                <a onClick={this.props.nearbyClick.bind(null, o.location)} href='' title={`Open ${o.location} popover`}>{o.location}

            );
          })}

    );
  },

  render: function () {
    let m = this.props.measurement;
    let reading =
{this.props.parameter.name} N/A
;
    if (m) {
      let lastUp = moment.utc(m.lastUpdated).format('YYYY/MM/DD HH:mm');
      reading =
Last reading {round(m.value)} {m.unit} at {lastUp}
;
    }

    return (

<a href={`#/location/${encodeURIComponent(this.props.location)}`} title={`View ${this.props.location} page`}>{this.props.location}

            {reading}

              { /*
                Using `a` instead of `Link` because these are rendered outside
                the router context and `Link` needs that context to work.
              */ }

<a href={`#/compare/${encodeURIComponent(this.props.location)}`} className='button button--primary-bounded' title={`Compare ${this.props.location} with other locations`}>Compare

<a href={`#/location/${encodeURIComponent(this.props.location)}`} title={`View ${this.props.location} page`}className='button button--primary-bounded'>View More

            {this.renderNearby()}

    );
  }
});
<|endoftext|>


<commit_before>angular.module('omnibooks.auth', [])

.factory('auth', function(fireBase) {
  var loggedInUser = null; // updated when user logs in
  var loggedInOrg  = null;

  // success, failed are callbacks
  var signup = function (authInfo, success, failed) {
    // check the user-org object to see the username is available
    var userOrg = fireBase.getUserOrg();

    userOrg.$loaded().then(function () {
      var org = userOrg[authInfo.name];
      if(org){
        console.log('user already exists');
        failed('The username is already registered. Try another name.');
        return;
      }

      // if available, save the user on the database
      fireBase.createUser(authInfo, function () {
        setLoggedInInfo(authInfo);
        success();
      }, failed);
    });
  };

  // success, failed are callbacks
  var login = function (authInfo, success, failed) {
    // check the user-org object to get the org of the user
    var userOrg = fireBase.getUserOrg();

    userOrg.$loaded().then(function () {
      var org = userOrg[authInfo.name];
      if(!org){
        console.log('User not exists');
        failed('incorrect user name.');
        return;
      }

      authInfo.org = org;
      // get the email of the user to use firebase.authWithPassword
      var existingUser = fireBase.getUserInfo(org, authInfo.name);

      existingUser.$loaded().then(function () {
        authInfo.email = existingUser.userDetail.email;

        fireBase.authWithPassword(authInfo, function () {
          setLoggedInInfo(authInfo);
          success();
        }, failed);
      });
    });
  };

  var setLoggedInInfo = function (authInfo) {
    loggedInUser = fireBase.getUserInfo(authInfo.org, authInfo.name);
    loggedInOrg  = authInfo.org;
  };

  var logOut = function () {
    loggedInUser = null;
  };

  var isLoggedIn = function () {
    return !!loggedInUser;
  };

  var getUsername = function() {
    return loggedInUser;
  };

  var getOrg = function() {
    return loggedInOrg;
  };

  return {
    signup: signup,
    login: login,
    loggedInUser: loggedInUser,
    loggedInOrg: loggedInOrg,
    isLoggedIn: isLoggedIn,
    logOut: logOut,
    getUsername: getUsername,
    getOrg: getOrg
  };
});

angular.module('omnibooks')
.controller('authController', ['$scope', '$state', 'auth', 'fireBase', '$rootScope', function ($scope, $state, auth, fireBase, $rootScope) {
  $scope.orgs = ['purdue','Wellesley','Berkeley','Stanford'];
  $scope.authInfo = {org: 'purdue', name: '', email: '', password: ''};
  $scope.authInfo.org = $scope.orgs[0];
  $rootScope.loginBtnText = "Log in";
  $rootScope.loggedIn = false;

  $scope.clickSignup = function () {
    $rootScope.signupShown = true;
  };
  $scope.clickLogin = function () {
    if(auth.isLoggedIn()){
      logOut();
      return;
    }
    $rootScope.loginShown = true;
  };
  $scope.login = function () {
    auth.login($scope.authInfo, moveToMarket, showError);
  };
  $scope.signup = function () {
    auth.signup($scope.authInfo, moveToMarket, showError);
  };
  $scope.closeAuthForm = function () {
    $rootScope.signupShown = false;
    $rootScope.loginShown = false;
    hideError();
    resetUserInfo();
  };

  function moveToMarket() {
    $scope.closeAuthForm();
    $rootScope.loginBtnText = "Log out";
    $rootScope.loggedIn = true;
    $state.go("market");
  }
  function logOut() {
    auth.logOut();
    $rootScope.loginBtnText = "Log in";
    $rootScope.loggedIn = false;
    $state.go("home");
  }
  function showError(message) {
    $scope.errorMessage = message;
    $scope.error = true;
  }
  function hideError() {
    $scope.errorMessage = '';
    $scope.error = false;
  }
  function resetUserInfo() {
    $scope.authInfo = {org: 'purdue', name: '', email: '', password: ''};
  }
  $scope.closeAuthForm();

}])
.run(['$rootScope', '$state', 'auth', function ($rootScope, $state, auth) {
  $rootScope.$on('$stateChangeStart', function (event, toState) {
    if(toState.name === "home"){
      return;
    }
    if(!auth.isLoggedIn()){
      event.preventDefault();
      $state.go("home");
    }
  });
}]);
<commit_msg>fixed to "Purdue"<commit_after>angular.module('omnibooks.auth', [])

.factory('auth', function(fireBase) {
  var loggedInUser = null; // updated when user logs in
  var loggedInOrg  = null;

  // success, failed are callbacks
  var signup = function (authInfo, success, failed) {
    // check the user-org object to see the username is available
    var userOrg = fireBase.getUserOrg();

    userOrg.$loaded().then(function () {
      var org = userOrg[authInfo.name];
      if(org){
        console.log('user already exists');
        failed('The username is already registered. Try another name.');
        return;
      }

      // if available, save the user on the database
      fireBase.createUser(authInfo, function () {
        setLoggedInInfo(authInfo);
        success();
      }, failed);
    });
  };

  // success, failed are callbacks
  var login = function (authInfo, success, failed) {
    // check the user-org object to get the org of the user
    var userOrg = fireBase.getUserOrg();

    userOrg.$loaded().then(function () {
      var org = userOrg[authInfo.name];
      if(!org){
        console.log('User not exists');
        failed('incorrect user name.');
        return;
      }

      authInfo.org = org;
      // get the email of the user to use firebase.authWithPassword
      var existingUser = fireBase.getUserInfo(org, authInfo.name);

      existingUser.$loaded().then(function () {
        authInfo.email = existingUser.userDetail.email;

        fireBase.authWithPassword(authInfo, function () {
          setLoggedInInfo(authInfo);
          success();
        }, failed);
      });
    });
  };

  var setLoggedInInfo = function (authInfo) {
    loggedInUser = fireBase.getUserInfo(authInfo.org, authInfo.name);
    loggedInOrg  = authInfo.org;
  };

  var logOut = function () {
    loggedInUser = null;
  };

  var isLoggedIn = function () {
    return !!loggedInUser;
  };

  var getUsername = function() {
    return loggedInUser;
  };

  var getOrg = function() {
    return loggedInOrg;
  };

  return {
    signup: signup,
    login: login,
    loggedInUser: loggedInUser,
    loggedInOrg: loggedInOrg,
    isLoggedIn: isLoggedIn,
    logOut: logOut,
    getUsername: getUsername,
    getOrg: getOrg
  };
});

angular.module('omnibooks')
.controller('authController', ['$scope', '$state', 'auth', 'fireBase', '$rootScope', function ($scope, $state, auth, fireBase, $rootScope) {
  $scope.orgs = ['Purdue','Wellesley','Berkeley','Stanford'];
  $scope.authInfo = {org: 'Purdue', name: '', email: '', password: ''};
  $scope.authInfo.org = $scope.orgs[0];
  $rootScope.loginBtnText = "Log in";
  $rootScope.loggedIn = false;

  $scope.clickSignup = function () {
    $rootScope.signupShown = true;
  };
  $scope.clickLogin = function () {
    if(auth.isLoggedIn()){
      logOut();
      return;
    }
    $rootScope.loginShown = true;
  };
  $scope.login = function () {
    auth.login($scope.authInfo, moveToMarket, showError);
  };
  $scope.signup = function () {
    auth.signup($scope.authInfo, moveToMarket, showError);
  };
  $scope.closeAuthForm = function () {
    $rootScope.signupShown = false;
    $rootScope.loginShown = false;
    hideError();
    resetUserInfo();
  };

  function moveToMarket() {
    $scope.closeAuthForm();
    $rootScope.loginBtnText = "Log out";
    $rootScope.loggedIn = true;
    $state.go("market");
  }
  function logOut() {
    auth.logOut();
    $rootScope.loginBtnText = "Log in";
    $rootScope.loggedIn = false;
    $state.go("home");
  }
  function showError(message) {
    $scope.errorMessage = message;
    $scope.error = true;
  }
  function hideError() {
    $scope.errorMessage = '';
    $scope.error = false;
  }
  function resetUserInfo() {
    $scope.authInfo = {org: 'purdue', name: '', email: '', password: ''};
  }
  $scope.closeAuthForm();

}])
.run(['$rootScope', '$state', 'auth', function ($rootScope, $state, auth) {
  $rootScope.$on('$stateChangeStart', function (event, toState) {
    if(toState.name === "home"){
      return;
    }
    if(!auth.isLoggedIn()){
      event.preventDefault();
      $state.go("home");
    }
  });
}]);
<|endoftext|>