<commit_before>// This Javascript supporting implementing the per project form used
// for showing and editing information about a project.

var criterionCategoryValue = {};
var criteriaMetUrlRequired = {};
var criterionFuture = {};
var MIN_SHOULD_LENGTH = 5;

// Global - name of criterion we last selected as 'met'.
// Don't hide this criterion (yet), so that users can enter a justification.
var globalLastSelectedMet = '';
var globalHideMetnaCriteria = false;

// Do a polyfill for datalist if it's not already supported
// (e.g., Safari fails to support polyfill at the time of this writing).
// See https://github.com/thgreasi/datalist-polyfill/blob/master/README.md
function polyfillDatalist() {
  var nativedatalist = !!('list' in document.createElement('input')) &&
      !!(document.createElement('datalist') && window.HTMLDataListElement);
  if (!nativedatalist) {
    $('input[list]').each(function() {
      var availableTags = $('#' + $(this).attr('list')).find('option').map(
        function() {
          return this.value;
        }).get();
      $(this).autocomplete({ source: availableTags });
    });
  }
}

// Note: This regex needs to be logically the same as the one used in
// the server-side badge calculation, or it may confuse some users.
// See app/models/project.rb function "contains_url?".
function containsURL(justification) {
  if (!justification) {
    return false;
  } else {
    return !!justification.match(/https?:\/\/[^ ]{5}/);
  }
}

// Determine result for a given criterion, which is one of
// passing, barely, failing, or question.
// The result calculation here must match the equivalent routine
// implemented on the server to prevent confusion.
function criterionResult(criterion) {
  var criterionStatus = '#project_' + criterion + '_status';
  var justification = $('#project_' + criterion + '_justification').val();
  if (!justification) {
    justification = '';
  }
  if ($(criterionStatus + '_na').is(':checked')) {
    return 'passing';
  } else if ($(criterionStatus + '_met').is(':checked')) {
    if (criteriaMetUrlRequired[criterion] && !containsURL(justification)) {
      // Odd case: met is claimed, but we're still missing information.
      return 'question';
    } else {
      return 'passing';
    }
  } else if (criterionCategoryValue[criterion] === 'SHOULD' &&
             $(criterionStatus + '_unmet').is(':checked') &&
             justification.length >= MIN_SHOULD_LENGTH) {
    return 'barely';
  } else if (criterionCategoryValue[criterion] === 'SUGGESTED' &&
            !($(criterionStatus + '_').is(':checked'))) {
    return 'barely';
  } else if ($(criterionStatus + '_').is(':checked')) {
    return 'question';
  } else {
    return 'failing';
  }
}

// This must match the criteria implemented in Ruby to prevent confusion.
function isEnough(criterion) {
  var result = criterionResult(criterion);
  return (result === 'passing' || result === 'barely');
}

function resetProgressBar() {
  var total = 0;
  var enough = 0;
  var percentage;
  var percentAsString;
  $.each(criterionCategoryValue, function(key, value) {
    if (!criterionFuture[key]) { // Only include non-future values
      total++;
      if (isEnough(key)) {
        enough++;
      }
    }
  });
  percentage = enough / total;
  percentAsString = Math.round(percentage * 100).toString() + '%';
  $('#badge-progress').attr('aria-valuenow', percentage).
                      text(percentAsString).css('width', percentAsString);
}

function resetCriterionResult(criterion) {
  var result = criterionResult(criterion);
  if (result === 'passing') {
    $('#' + criterion + '_enough').
        attr('src', $('#result_symbol_check_img').attr('src')).
        attr('width', 40).attr('height', 40).
        attr('alt', 'Enough for a badge!');
  } else if (result === 'barely') {
    $('#' + criterion + '_enough').
        attr('src', $('#result_symbol_dash').attr('src')).
        attr('width', 40).attr('height', 40).
        attr('alt', 'Not enough for a badge.');
  } else if (result === 'question') {
    $('#' + criterion + '_enough').
        attr('src', $('#result_symbol_question').attr('src')).
        attr('width', 40).attr('height', 40).
        attr('alt', 'Not enough for a badge.');
  } else {
    $('#' + criterion + '_enough').
        attr('src', $('#result_symbol_x_img').attr('src')).
        attr('width', 40).attr('height', 40).
        attr('alt', 'Not enough for a badge.');
  }
}

function changedJustificationText(criteria) {
  var criteriaJust = '#project_' + criteria + '_justification';
  var criteriaStatus = '#project_' + criteria + '_status';
  if ($(criteriaStatus + '_unmet').is(':checked') &&
       (criterionCategoryValue[criteria] === 'SHOULD') &&
       ($(criteriaJust).val().length < MIN_SHOULD_LENGTH)) {
    $(criteriaJust).addClass('required-data');
  } else if ($(criteriaStatus + '_met').is(':checked') &&
           criteriaMetUrlRequired[criteria] &&
           !containsURL($(criteriaJust).val())) {
    $(criteriaJust).addClass('required-data');
  } else {
    $(criteriaJust).removeClass('required-data');
  }
  resetCriterionResult(criteria);
  resetProgressBar();
}

// Do we have any text in this field region?  Handle the variations.
function hasFieldTextInside(e) {
  var i;
  i = e.find('input[type="text"]');
  if (i && i.val()) {
    return true;
  }
  i = e.find('textarea');
  if (i && i.val()) {
    return true;
  }
  i = e.find('.discussion-markdown');
  if (i && i.text()) {
    return true;
  }
  return false;
}

// If we should, hide the criteria that are "Met" or N/A and are enough.
// Do NOT hide 'met' criteria that aren't enough (e.g., missing required URL),
// and do NOT hide the last-selected-met criterion (so users can enter/edit
// justification text).
function hideMetNA() {
  $.each(criterionCategoryValue, function(key, value) {
    if (globalHideMetnaCriteria && key !== globalLastSelectedMet &&
        ($('#project_' + key + '_status_met').is(':checked') ||
         $('#project_' + key + '_status_na').is(':checked')) &&
        isEnough(key)) {
      $('#' + key).addClass('hidden');
    } else {
      $('#' + key).removeClass('hidden');
    }
  });
  $('.hidable-text-entry').each(function() {
    if (globalHideMetnaCriteria && hasFieldTextInside($(this))) {
      $(this).addClass('hidden');
    } else {
      $(this).removeClass('hidden');
    }
  });
}

function updateCriteriaDisplay(criteria) {
  var criteriaJust = '#project_' + criteria + '_justification';
  var criteriaStatus = '#project_' + criteria + '_status';
  var justificationElement = document.getElementById('project_' +
                           criteria + '_justification');
  var justificationValue = '';
  if (justificationElement) {
    justificationValue = justificationElement.value;
  }
  if ($(criteriaStatus + '_met').is(':checked')) {
    var criteriaMetPlaceholder = criteria + '_met_placeholder';
    $(criteriaJust).
       attr('placeholder',
         $('#' + criteriaMetPlaceholder).html().trim());
    if (document.getElementById(criteria + '_met_suppress')) {
      $(criteriaJust).hide('fast');
    } else {
      $(criteriaJust).show('fast');
    }
  } else if ($(criteriaStatus + '_unmet').is(':checked')) {
    var criteriaUnmetPlaceholder = criteria + '_unmet_placeholder';
    $(criteriaJust).
       attr('placeholder',
         $('#' + criteriaUnmetPlaceholder).html().trim());
    if (document.getElementById(criteria + '_unmet_suppress')) {
      $(criteriaJust).hide('fast');
    } else {
      $(criteriaJust).show('fast');
    }
  } else if ($(criteriaStatus + '_na').is(':checked')) {
    var criteriaNaPlaceholder = criteria + '_na_placeholder';
    $(criteriaJust).
       attr('placeholder',
         $('#' + criteriaNaPlaceholder).html().trim());
    if (document.getElementById(criteria + '_na_suppress')) {
      $(criteriaJust).hide('fast');
    } else {
      $(criteriaJust).show('fast');
    }
  } else if ($(criteriaStatus + '_').is(':checked')) {
    $(criteriaJust).attr('placeholder', 'Please explain');
    $(criteriaJust).hide('fast');
  }
  // If there's old justification text, force showing it even if it
  // no longer makes sense (so they can fix it or change their mind).
  if (justificationValue.length > 0) {
    $(criteriaJust).show('fast');
  }
  if (globalHideMetnaCriteria) {
    // If we're hiding met criteria, walk through and hide them.
    // We don't need to keep running this if we are NOT hiding them,
    // which is the normal case.
    hideMetNA();
  }
  changedJustificationText(criteria);
}

function changeCriterion(criterion) {
  var criterionStatus = '#project_' + criterion + '_status';
  if ($(criterionStatus + '_met').is(':checked')) {
    globalLastSelectedMet = criterion;
  }
  updateCriteriaDisplay(criterion);
}

function ToggleHideMet(e) {
  globalHideMetnaCriteria = !globalHideMetnaCriteria;
  // Note that button text shows what WILL happen on click, so it
  // shows the REVERSED state (not the current state).
  if (globalHideMetnaCriteria) {
    $('#toggle-hide-metna-criteria')
      .addClass('active').html('Show met and N/A criteria');
  } else {
    $('#toggle-hide-metna-criteria')
      .removeClass('active').html('Hide met or N/A criteria');
  }
  hideMetNA();
}

function setupProjectField(criteria) {
  updateCriteriaDisplay(criteria);
  $('input[name="project[' + criteria + '_status]"]').click(
      function() {
        changeCriterion(criteria);
      });
  $('input[name="project[' + criteria + '_justification]"]').blur(
      function() {
        updateCriteriaDisplay(criteria);
      });
  $('#project_' + criteria + '_justification').on('input',
      function() {
        changedJustificationText(criteria);
      });
  $('#project_' + criteria + '_justification').on('keyup',
      function() {
        changedJustificationText(criteria);
      });
}

function ToggleDetailsDisplay(e) {
  var detailsTextID = e.target.id.
                        replace('_details_toggler', '_details_text');
  $('#' + detailsTextID).toggle('fast',
    function() {
      var buttonText;
      if ($('#' + detailsTextID).is(':hidden')) {
        buttonText = 'Show details';
      } else {
        buttonText = 'Hide details';
      }
      $('#' + e.target.id).html(buttonText);
    });
}

// Create mappings from criteria name to category and met_url_required.
// Eventually replace with just accessing classes directly via Javascript.
function SetupCriteriaStructures() {
  $('.status-chooser').each(
    function(index) {
      var criterionName = $(this).find('.criterion-name').text();
      var res = $(this).find('.criterion-met-url-required').text();
      var val = 'true' === res;
      criteriaMetUrlRequired[criterionName] = val;
      criterionCategoryValue[criterionName] =
        $(this).find('.criterion-category').text();
      criterionFuture[criterionName] =
        $(this).find('.criterion-future').text() === 'true';
    }
  );
}

// Setup display as soon as page is ready
$(document).ready(function() {
  // By default, hide details.  We do the hiding in Javascript, so
  // those who disable Javascript will still see the text
  // (they'll have no way to later reveal it).
  $('.details-text').hide('fast');
  $('.details-toggler').html('Show details');
  $('.details-toggler').click(ToggleDetailsDisplay);

  $('#show-all-details').click(function(e) {
    $('.details-text').show('fast');
    $('.details-toggler').html('Hide details');
  });
  $('#hide-all-details').click(function(e) {
    $('.details-text').hide('fast');
    $('.details-toggler').html('Show details');
  });

  // Force these values on page reload
  globalLastSelectedMet = '';
  globalHideMetnaCriteria = false;
  $('#toggle-hide-metna-criteria').click(function(e) {
    ToggleHideMet(e);
  });

  $('[data-toggle="tooltip"]').tooltip(); // Enable bootstrap tooltips
  $('textarea').autosize();

  // A form element with class onchange-submit automatically submits its
  // form whenever it is changed.
  $('.onchange-submit').change(function() {
    $(this).parents('form').submit();
  });

  if ($('#project_entry_form').length) {

    SetupCriteriaStructures();

    // Implement "press this button to make all crypto N/A"
    $('#all_crypto_na').click(function(e) {
      $.each(criterionCategoryValue, function(key, value) {
        if ((/^crypto/).test(key)) {
          $('#project_' + key + '_status_na').prop('checked', true);
        }
        updateCriteriaDisplay(key);
      });
      resetProgressBar();
    });

    // Use "imagesloaded" to wait for image load before displaying them
    imagesLoaded(document).on('always', function(instance) {
      // Set up the interactive displays of "enough".
      $.each(criterionCategoryValue, function(key, value) {
        setupProjectField(key);
      });
      resetProgressBar();
    });
  }

  // Polyfill datalist (for Safari users)
  polyfillDatalist();
});
<commit_msg>Remove now-extraneous call to autoresize<commit_after>// This Javascript supporting implementing the per project form used
// for showing and editing information about a project.

var criterionCategoryValue = {};
var criteriaMetUrlRequired = {};
var criterionFuture = {};
var MIN_SHOULD_LENGTH = 5;

// Global - name of criterion we last selected as 'met'.
// Don't hide this criterion (yet), so that users can enter a justification.
var globalLastSelectedMet = '';
var globalHideMetnaCriteria = false;

// Do a polyfill for datalist if it's not already supported
// (e.g., Safari fails to support polyfill at the time of this writing).
// See https://github.com/thgreasi/datalist-polyfill/blob/master/README.md
function polyfillDatalist() {
  var nativedatalist = !!('list' in document.createElement('input')) &&
      !!(document.createElement('datalist') && window.HTMLDataListElement);
  if (!nativedatalist) {
    $('input[list]').each(function() {
      var availableTags = $('#' + $(this).attr('list')).find('option').map(
        function() {
          return this.value;
        }).get();
      $(this).autocomplete({ source: availableTags });
    });
  }
}

// Note: This regex needs to be logically the same as the one used in
// the server-side badge calculation, or it may confuse some users.
// See app/models/project.rb function "contains_url?".
function containsURL(justification) {
  if (!justification) {
    return false;
  } else {
    return !!justification.match(/https?:\/\/[^ ]{5}/);
  }
}

// Determine result for a given criterion, which is one of
// passing, barely, failing, or question.
// The result calculation here must match the equivalent routine
// implemented on the server to prevent confusion.
function criterionResult(criterion) {
  var criterionStatus = '#project_' + criterion + '_status';
  var justification = $('#project_' + criterion + '_justification').val();
  if (!justification) {
    justification = '';
  }
  if ($(criterionStatus + '_na').is(':checked')) {
    return 'passing';
  } else if ($(criterionStatus + '_met').is(':checked')) {
    if (criteriaMetUrlRequired[criterion] && !containsURL(justification)) {
      // Odd case: met is claimed, but we're still missing information.
      return 'question';
    } else {
      return 'passing';
    }
  } else if (criterionCategoryValue[criterion] === 'SHOULD' &&
             $(criterionStatus + '_unmet').is(':checked') &&
             justification.length >= MIN_SHOULD_LENGTH) {
    return 'barely';
  } else if (criterionCategoryValue[criterion] === 'SUGGESTED' &&
            !($(criterionStatus + '_').is(':checked'))) {
    return 'barely';
  } else if ($(criterionStatus + '_').is(':checked')) {
    return 'question';
  } else {
    return 'failing';
  }
}

// This must match the criteria implemented in Ruby to prevent confusion.
function isEnough(criterion) {
  var result = criterionResult(criterion);
  return (result === 'passing' || result === 'barely');
}

function resetProgressBar() {
  var total = 0;
  var enough = 0;
  var percentage;
  var percentAsString;
  $.each(criterionCategoryValue, function(key, value) {
    if (!criterionFuture[key]) { // Only include non-future values
      total++;
      if (isEnough(key)) {
        enough++;
      }
    }
  });
  percentage = enough / total;
  percentAsString = Math.round(percentage * 100).toString() + '%';
  $('#badge-progress').attr('aria-valuenow', percentage).
                      text(percentAsString).css('width', percentAsString);
}

function resetCriterionResult(criterion) {
  var result = criterionResult(criterion);
  if (result === 'passing') {
    $('#' + criterion + '_enough').
        attr('src', $('#result_symbol_check_img').attr('src')).
        attr('width', 40).attr('height', 40).
        attr('alt', 'Enough for a badge!');
  } else if (result === 'barely') {
    $('#' + criterion + '_enough').
        attr('src', $('#result_symbol_dash').attr('src')).
        attr('width', 40).attr('height', 40).
        attr('alt', 'Not enough for a badge.');
  } else if (result === 'question') {
    $('#' + criterion + '_enough').
        attr('src', $('#result_symbol_question').attr('src')).
        attr('width', 40).attr('height', 40).
        attr('alt', 'Not enough for a badge.');
  } else {
    $('#' + criterion + '_enough').
        attr('src', $('#result_symbol_x_img').attr('src')).
        attr('width', 40).attr('height', 40).
        attr('alt', 'Not enough for a badge.');
  }
}

function changedJustificationText(criteria) {
  var criteriaJust = '#project_' + criteria + '_justification';
  var criteriaStatus = '#project_' + criteria + '_status';
  if ($(criteriaStatus + '_unmet').is(':checked') &&
       (criterionCategoryValue[criteria] === 'SHOULD') &&
       ($(criteriaJust).val().length < MIN_SHOULD_LENGTH)) {
    $(criteriaJust).addClass('required-data');
  } else if ($(criteriaStatus + '_met').is(':checked') &&
           criteriaMetUrlRequired[criteria] &&
           !containsURL($(criteriaJust).val())) {
    $(criteriaJust).addClass('required-data');
  } else {
    $(criteriaJust).removeClass('required-data');
  }
  resetCriterionResult(criteria);
  resetProgressBar();
}

// Do we have any text in this field region?  Handle the variations.
function hasFieldTextInside(e) {
  var i;
  i = e.find('input[type="text"]');
  if (i && i.val()) {
    return true;
  }
  i = e.find('textarea');
  if (i && i.val()) {
    return true;
  }
  i = e.find('.discussion-markdown');
  if (i && i.text()) {
    return true;
  }
  return false;
}

// If we should, hide the criteria that are "Met" or N/A and are enough.
// Do NOT hide 'met' criteria that aren't enough (e.g., missing required URL),
// and do NOT hide the last-selected-met criterion (so users can enter/edit
// justification text).
function hideMetNA() {
  $.each(criterionCategoryValue, function(key, value) {
    if (globalHideMetnaCriteria && key !== globalLastSelectedMet &&
        ($('#project_' + key + '_status_met').is(':checked') ||
         $('#project_' + key + '_status_na').is(':checked')) &&
        isEnough(key)) {
      $('#' + key).addClass('hidden');
    } else {
      $('#' + key).removeClass('hidden');
    }
  });
  $('.hidable-text-entry').each(function() {
    if (globalHideMetnaCriteria && hasFieldTextInside($(this))) {
      $(this).addClass('hidden');
    } else {
      $(this).removeClass('hidden');
    }
  });
}

function updateCriteriaDisplay(criteria) {
  var criteriaJust = '#project_' + criteria + '_justification';
  var criteriaStatus = '#project_' + criteria + '_status';
  var justificationElement = document.getElementById('project_' +
                           criteria + '_justification');
  var justificationValue = '';
  if (justificationElement) {
    justificationValue = justificationElement.value;
  }
  if ($(criteriaStatus + '_met').is(':checked')) {
    var criteriaMetPlaceholder = criteria + '_met_placeholder';
    $(criteriaJust).
       attr('placeholder',
         $('#' + criteriaMetPlaceholder).html().trim());
    if (document.getElementById(criteria + '_met_suppress')) {
      $(criteriaJust).hide('fast');
    } else {
      $(criteriaJust).show('fast');
    }
  } else if ($(criteriaStatus + '_unmet').is(':checked')) {
    var criteriaUnmetPlaceholder = criteria + '_unmet_placeholder';
    $(criteriaJust).
       attr('placeholder',
         $('#' + criteriaUnmetPlaceholder).html().trim());
    if (document.getElementById(criteria + '_unmet_suppress')) {
      $(criteriaJust).hide('fast');
    } else {
      $(criteriaJust).show('fast');
    }
  } else if ($(criteriaStatus + '_na').is(':checked')) {
    var criteriaNaPlaceholder = criteria + '_na_placeholder';
    $(criteriaJust).
       attr('placeholder',
         $('#' + criteriaNaPlaceholder).html().trim());
    if (document.getElementById(criteria + '_na_suppress')) {
      $(criteriaJust).hide('fast');
    } else {
      $(criteriaJust).show('fast');
    }
  } else if ($(criteriaStatus + '_').is(':checked')) {
    $(criteriaJust).attr('placeholder', 'Please explain');
    $(criteriaJust).hide('fast');
  }
  // If there's old justification text, force showing it even if it
  // no longer makes sense (so they can fix it or change their mind).
  if (justificationValue.length > 0) {
    $(criteriaJust).show('fast');
  }
  if (globalHideMetnaCriteria) {
    // If we're hiding met criteria, walk through and hide them.
    // We don't need to keep running this if we are NOT hiding them,
    // which is the normal case.
    hideMetNA();
  }
  changedJustificationText(criteria);
}

function changeCriterion(criterion) {
  var criterionStatus = '#project_' + criterion + '_status';
  if ($(criterionStatus + '_met').is(':checked')) {
    globalLastSelectedMet = criterion;
  }
  updateCriteriaDisplay(criterion);
}

function ToggleHideMet(e) {
  globalHideMetnaCriteria = !globalHideMetnaCriteria;
  // Note that button text shows what WILL happen on click, so it
  // shows the REVERSED state (not the current state).
  if (globalHideMetnaCriteria) {
    $('#toggle-hide-metna-criteria')
      .addClass('active').html('Show met and N/A criteria');
  } else {
    $('#toggle-hide-metna-criteria')
      .removeClass('active').html('Hide met or N/A criteria');
  }
  hideMetNA();
}

function setupProjectField(criteria) {
  updateCriteriaDisplay(criteria);
  $('input[name="project[' + criteria + '_status]"]').click(
      function() {
        changeCriterion(criteria);
      });
  $('input[name="project[' + criteria + '_justification]"]').blur(
      function() {
        updateCriteriaDisplay(criteria);
      });
  $('#project_' + criteria + '_justification').on('input',
      function() {
        changedJustificationText(criteria);
      });
  $('#project_' + criteria + '_justification').on('keyup',
      function() {
        changedJustificationText(criteria);
      });
}

function ToggleDetailsDisplay(e) {
  var detailsTextID = e.target.id.
                        replace('_details_toggler', '_details_text');
  $('#' + detailsTextID).toggle('fast',
    function() {
      var buttonText;
      if ($('#' + detailsTextID).is(':hidden')) {
        buttonText = 'Show details';
      } else {
        buttonText = 'Hide details';
      }
      $('#' + e.target.id).html(buttonText);
    });
}

// Create mappings from criteria name to category and met_url_required.
// Eventually replace with just accessing classes directly via Javascript.
function SetupCriteriaStructures() {
  $('.status-chooser').each(
    function(index) {
      var criterionName = $(this).find('.criterion-name').text();
      var res = $(this).find('.criterion-met-url-required').text();
      var val = 'true' === res;
      criteriaMetUrlRequired[criterionName] = val;
      criterionCategoryValue[criterionName] =
        $(this).find('.criterion-category').text();
      criterionFuture[criterionName] =
        $(this).find('.criterion-future').text() === 'true';
    }
  );
}

// Setup display as soon as page is ready
$(document).ready(function() {
  // By default, hide details.  We do the hiding in Javascript, so
  // those who disable Javascript will still see the text
  // (they'll have no way to later reveal it).
  $('.details-text').hide('fast');
  $('.details-toggler').html('Show details');
  $('.details-toggler').click(ToggleDetailsDisplay);

  $('#show-all-details').click(function(e) {
    $('.details-text').show('fast');
    $('.details-toggler').html('Hide details');
  });
  $('#hide-all-details').click(function(e) {
    $('.details-text').hide('fast');
    $('.details-toggler').html('Show details');
  });

  // Force these values on page reload
  globalLastSelectedMet = '';
  globalHideMetnaCriteria = false;
  $('#toggle-hide-metna-criteria').click(function(e) {
    ToggleHideMet(e);
  });

  $('[data-toggle="tooltip"]').tooltip(); // Enable bootstrap tooltips

  // A form element with class onchange-submit automatically submits its
  // form whenever it is changed.
  $('.onchange-submit').change(function() {
    $(this).parents('form').submit();
  });

  if ($('#project_entry_form').length) {

    SetupCriteriaStructures();

    // Implement "press this button to make all crypto N/A"
    $('#all_crypto_na').click(function(e) {
      $.each(criterionCategoryValue, function(key, value) {
        if ((/^crypto/).test(key)) {
          $('#project_' + key + '_status_na').prop('checked', true);
        }
        updateCriteriaDisplay(key);
      });
      resetProgressBar();
    });

    // Use "imagesloaded" to wait for image load before displaying them
    imagesLoaded(document).on('always', function(instance) {
      // Set up the interactive displays of "enough".
      $.each(criterionCategoryValue, function(key, value) {
        setupProjectField(key);
      });
      resetProgressBar();
    });
  }

  // Polyfill datalist (for Safari users)
  polyfillDatalist();
});
<|endoftext|>


<commit_before><commit_msg>Remove errand JS debugger statement<commit_after><|endoftext|>


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

  angular
    .module('app')
    .controller('SamplesTabController', SamplesTabController);

  SamplesTabController.$inject = ['$ionicModal', '$ionicPopup', '$log', '$scope', '$state', 'DataModelsFactory',
    'FormFactory', 'HelpersFactory', 'ProjectFactory'];

  function SamplesTabController($ionicModal, $ionicPopup, $log, $scope, $state, DataModelsFactory, FormFactory,
                                HelpersFactory, ProjectFactory) {
    var vm = this;
    var vmParent = $scope.vm;
    vmParent.loadTab($state);     // Need to load current state into parent

    var isDelete;

    vm.addSample = addSample;
    vm.basicFormModal = {};
    vm.deleteSample = deleteSample;
    vm.editSample = editSample;
    vm.modalTitle = '';
    vm.submit = submit;

    activate();

    /**
     * Private Functions
     */

    function activate() {
      $log.log('Samples:', vmParent.spot.properties.samples);
      vmParent.survey = DataModelsFactory.getDataModel('sample').survey;
      vmParent.choices = DataModelsFactory.getDataModel('sample').choices;
      checkProperties();
      createModal();
    }

    function checkProperties() {
      _.each(vmParent.spot.properties.samples, function (sample) {
        if (!sample.label) sample.label = createDefaultLabel(sample);
        if (!sample.id) sample.id = HelpersFactory.getNewId();
      });
    }

    function createDefaultLabel(sampleToLabel) {
      return sampleToLabel.sample_id_name || 'sample';
    }

    function createModal() {
      $ionicModal.fromTemplateUrl('app/spot/basic-form-modal.html', {
        'scope': $scope,
        'animation': 'slide-in-up',
        'focusFirstInput': true,
        'backdropClickToClose': false
      }).then(function (modal) {
        vm.basicFormModal = modal;
      });

      // Cleanup the modal when we're done with it!
      $scope.$on('$destroy', function () {
        vm.basicFormModal.remove();
      });
    }

    function handleSampleNumber() {
      var found = _.find(vmParent.spot.properties.samples, function (sample) {
        return sample.id === vmParent.data.id;
      });
      if (!found) ProjectFactory.incrementSampleNumber();
    }

    /**
     * Public Functions
     */

    function addSample() {
      vmParent.survey = DataModelsFactory.getDataModel('sample').survey;
      vmParent.choices = DataModelsFactory.getDataModel('sample').choices;
      vmParent.data = {};
      vmParent.data.id = HelpersFactory.getNewId();
      var number = ProjectFactory.getSampleNumber() || '';
      var prefix = ProjectFactory.getSamplePrefix() || '';
      if (number !== '' || prefix !== '') vmParent.data.sample_id_name = prefix + number.toString();
      vm.modalTitle = 'Add a Sample';
      vm.basicFormModal.show();
    }

    function deleteSample(sampleToDelete) {
      isDelete = true;
      var confirmPopup = $ionicPopup.confirm({
        'title': 'Delete Sample',
        'template': 'Are you sure you want to delete the Sample ' + sampleToDelete.label + '?'
      });
      confirmPopup.then(function (res) {
        if (res) {
          vmParent.spot.properties.samples = _.reject(vmParent.spot.properties.samples, function (sample) {
            return sample.id === sampleToDelete.id;
          });
          if (vmParent.spot.properties.samples.length === 0) delete vmParent.spot.properties.samples;
        }
        isDelete = false;
      });
    }

    function editSample(sampleToEdit) {
      if (!isDelete) {
        vmParent.data = angular.fromJson(angular.toJson(sampleToEdit));  // Copy value, not reference
        vm.modalTitle = 'Edit Sample';
        vm.basicFormModal.show();
      }
    }

    function submit() {
      if (_.size(vmParent.data) <= 1) {
        // If not more than id field no data has been entered so don't continue, just close modal
        vmParent.data = {};
        vm.basicFormModal.hide();
        vmParent.survey = undefined;
        vmParent.choices = undefined;
        return;
      }
      if (!vmParent.data.label) vmParent.data.label = createDefaultLabel(vmParent.data);
      if (FormFactory.validate(vmParent.survey, vmParent.data)) {
        if (!vmParent.spot.properties.samples) vmParent.spot.properties.samples = [];
        handleSampleNumber();
        vmParent.spot.properties.samples = _.reject(vmParent.spot.properties.samples, function (sample) {
          return sample.id === vmParent.data.id;
        });
        vmParent.spot.properties.samples.push(vmParent.data);
        vmParent.data = {};
        vm.basicFormModal.hide();
        vmParent.survey = undefined;
        vmParent.choices = undefined;
      }
    }
  }
}());
<commit_msg>fixed sample form not showing on edit sample<commit_after>(function () {
  'use strict';

  angular
    .module('app')
    .controller('SamplesTabController', SamplesTabController);

  SamplesTabController.$inject = ['$ionicModal', '$ionicPopup', '$log', '$scope', '$state', 'DataModelsFactory',
    'FormFactory', 'HelpersFactory', 'ProjectFactory'];

  function SamplesTabController($ionicModal, $ionicPopup, $log, $scope, $state, DataModelsFactory, FormFactory,
                                HelpersFactory, ProjectFactory) {
    var vm = this;
    var vmParent = $scope.vm;
    vmParent.loadTab($state);     // Need to load current state into parent

    var isDelete;

    vm.addSample = addSample;
    vm.basicFormModal = {};
    vm.deleteSample = deleteSample;
    vm.editSample = editSample;
    vm.modalTitle = '';
    vm.submit = submit;

    activate();

    /**
     * Private Functions
     */

    function activate() {
      $log.log('Samples:', vmParent.spot.properties.samples);
      vmParent.survey = DataModelsFactory.getDataModel('sample').survey;
      vmParent.choices = DataModelsFactory.getDataModel('sample').choices;
      checkProperties();
      createModal();
    }

    function checkProperties() {
      _.each(vmParent.spot.properties.samples, function (sample) {
        if (!sample.label) sample.label = createDefaultLabel(sample);
        if (!sample.id) sample.id = HelpersFactory.getNewId();
      });
    }

    function createDefaultLabel(sampleToLabel) {
      return sampleToLabel.sample_id_name || 'sample';
    }

    function createModal() {
      $ionicModal.fromTemplateUrl('app/spot/basic-form-modal.html', {
        'scope': $scope,
        'animation': 'slide-in-up',
        'focusFirstInput': true,
        'backdropClickToClose': false
      }).then(function (modal) {
        vm.basicFormModal = modal;
      });

      // Cleanup the modal when we're done with it!
      $scope.$on('$destroy', function () {
        vm.basicFormModal.remove();
      });
    }

    function handleSampleNumber() {
      var found = _.find(vmParent.spot.properties.samples, function (sample) {
        return sample.id === vmParent.data.id;
      });
      if (!found) ProjectFactory.incrementSampleNumber();
    }

    /**
     * Public Functions
     */

    function addSample() {
      vmParent.survey = DataModelsFactory.getDataModel('sample').survey;
      vmParent.choices = DataModelsFactory.getDataModel('sample').choices;
      vmParent.data = {};
      vmParent.data.id = HelpersFactory.getNewId();
      var number = ProjectFactory.getSampleNumber() || '';
      var prefix = ProjectFactory.getSamplePrefix() || '';
      if (number !== '' || prefix !== '') vmParent.data.sample_id_name = prefix + number.toString();
      vm.modalTitle = 'Add a Sample';
      vm.basicFormModal.show();
    }

    function deleteSample(sampleToDelete) {
      isDelete = true;
      var confirmPopup = $ionicPopup.confirm({
        'title': 'Delete Sample',
        'template': 'Are you sure you want to delete the Sample ' + sampleToDelete.label + '?'
      });
      confirmPopup.then(function (res) {
        if (res) {
          vmParent.spot.properties.samples = _.reject(vmParent.spot.properties.samples, function (sample) {
            return sample.id === sampleToDelete.id;
          });
          if (vmParent.spot.properties.samples.length === 0) delete vmParent.spot.properties.samples;
        }
        isDelete = false;
      });
    }

    function editSample(sampleToEdit) {
      if (!isDelete) {
        vmParent.data = angular.fromJson(angular.toJson(sampleToEdit));  // Copy value, not reference
        vmParent.survey = DataModelsFactory.getDataModel('sample').survey;
        vmParent.choices = DataModelsFactory.getDataModel('sample').choices;
        vm.modalTitle = 'Edit Sample';
        vm.basicFormModal.show();
      }
    }

    function submit() {
      if (_.size(vmParent.data) <= 1) {
        // If not more than id field no data has been entered so don't continue, just close modal
        vmParent.data = {};
        vm.basicFormModal.hide();
        vmParent.survey = undefined;
        vmParent.choices = undefined;
        return;
      }
      if (!vmParent.data.label) vmParent.data.label = createDefaultLabel(vmParent.data);
      if (FormFactory.validate(vmParent.survey, vmParent.data)) {
        if (!vmParent.spot.properties.samples) vmParent.spot.properties.samples = [];
        handleSampleNumber();
        vmParent.spot.properties.samples = _.reject(vmParent.spot.properties.samples, function (sample) {
          return sample.id === vmParent.data.id;
        });
        vmParent.spot.properties.samples.push(vmParent.data);
        vmParent.data = {};
        vm.basicFormModal.hide();
        vmParent.survey = undefined;
        vmParent.choices = undefined;
      }
    }
  }
}());
<|endoftext|>


<commit_before>/* Popup PhoneGap Plugin - JavaScript side of the bridge to StatBarPlugin.java
*
 * @author Ally Ogilvie
 * @copyright WizCorp Inc. [ Incorporated Wizards ] 2011
 * @file JavaScript StatBarPlugin for PhoneGap
 *
*/

/*

============= example code ====================

function shouldRotateToOrientation (orientation)
{
    if (deviceIsReady == true) {
        switch (orientation) {
            case 0:
            // portrait normal
            window.nativeSpinner.rotate(1);
            return true;
            break;

            case 90:
            // landscape left
            window.nativeSpinner.rotate(3);
            return true;

            break;

            case -90:
            // landscape right
            window.nativeSpinner.rotate(4);
            return true;

            break;

            case 180:
            // portrait upside down
            return false;

            break
        }
    }

}

function onDeviceReady()
{
    deviceIsReady = true;
}

*/

var nativeSpinner = {

    create: function(s,f,a) {
        if(typeof(a) == "undefined") {
			return PhoneGap.exec(s, f, 'SpinnerPlugin', 'create', []);
		} else {
			return PhoneGap.exec(s, f, 'SpinnerPlugin', 'create', [a]);
		}

    },

    show: function(s,f,a) {

        if(typeof(a) == "undefined") {

            return PhoneGap.exec(s, f, 'SpinnerPlugin', 'show', []);
		} else {
            return PhoneGap.exec(s, f, 'SpinnerPlugin', 'show', [a]);
        }

    },

    hide: function(s,f) {

        return PhoneGap.exec(null, null, 'SpinnerPlugin', 'hide', []);

    },

    rotate: function(orientation) {
        PhoneGap.exec(null, null, 'SpinnerPlugin', 'rotate', [orientation]);
    }

};
<commit_msg>updated JS file for Cordova<commit_after>/* Spinner/Loader Cordova Plugin - JavaScript side of the bridge to iOS
*
* @author Ally Ogilvie
* @copyright WizCorp Inc. [ Incorporated Wizards ] 2012
* @file JavaScript SpinnerPlugin for Cordova
*
*/

/*

============= example code ====================

function shouldRotateToOrientation (orientation)
{
if (deviceIsReady == true) {
switch (orientation) {
case 0:
// portrait normal
window.nativeSpinner.rotate(1);
return true;
break;
case 90:
// landscape left
window.nativeSpinner.rotate(3);
return true;
break;
case -90:
// landscape right
window.nativeSpinner.rotate(4);
return true;
break;
case 180:
// portrait upside down
return false;
break
}
}
}

function onDeviceReady()
{
deviceIsReady = true;
}
*/

var nativeSpinner = {

    create: function(s,f,a) {
        if(typeof(a) == "undefined") {
return cordova.exec(s, f, 'SpinnerPlugin', 'create', []);
} else {
return cordova.exec(s, f, 'SpinnerPlugin', 'create', [a]);
}

    },

    show: function(s,f,a) {

        if(typeof(a) == "undefined") {

            return cordova.exec(s, f, 'SpinnerPlugin', 'show', []);
} else {
            return cordova.exec(s, f, 'SpinnerPlugin', 'show', [a]);
        }

    },

    hide: function(s,f) {

        return cordova.exec(null, null, 'SpinnerPlugin', 'hide', []);

    },

    rotate: function(orientation) {
        cordova.exec(null, null, 'SpinnerPlugin', 'rotate', [orientation]);
    }

};<|endoftext|>


<commit_before>/** @jsx jsx */
import { jsx } from "theme-ui"
import React, { Fragment } from "react"
import { Link, useStaticQuery, graphql } from "gatsby"
import { Helmet } from "react-helmet"
import url from "url"
import Img from "gatsby-image"
import qs from "qs"

import { mediaQueries } from "gatsby-design-tokens/dist/theme-gatsbyjs-org"
import Layout from "../components/layout"
import Modal from "../components/modal"
import ShareMenu from "../components/share-menu"
import Button from "../components/button"
import Screenshot from "../views/shared/screenshot"

import MdArrowUpward from "react-icons/lib/md/arrow-upward"
import MdLink from "react-icons/lib/md/link"
import FeaturedIcon from "../assets/icons/featured-sites-icons"
import GithubIcon from "react-icons/lib/go/mark-github"

const gutter = 6
const gutterDesktop = 8

const styles = {
  link: {
    color: `link.color`,
    textDecoration: `none`,
  },
  prevNextLink: {
    color: `lilac`,
    fontFamily: `heading`,
    px: [6, null, null, 0],
  },
  prevNextLinkSiteTitle: {
    color: `link.color`,
    fontWeight: `bold`,
  },
  prevNextImage: {
    borderRadius: 1,
    boxShadow: `overlay`,
  },
}

const cleanUrl = mainUrl => {
  const parsed = url.parse(mainUrl)
  let path = parsed.pathname
  if (path[path.length - 1] === `/`) path = path.slice(0, path.length - 1)
  return parsed.hostname + path
}

const Featured = () => (
  <div
    sx={{
      color: `textMuted`,
      display: `flex`,
      fontWeight: `bold`,
      mr: 4,
    }}
  >
     t.space[5],
        m: 0,
        mr: 2,
        width: t => t.space[5],
      }}
    >
      <FeaturedIcon />

    {` `}
    Featured

)

const SourceLink = ({ ...props }) => (
  <a
    {...props}
    sx={{
      "&&": {
        border: 0,
      },
      display: `flex`,
      alignItems: `center`,
      mr: 3,
      color: `link.color`,
      width: `100%`,
    }}
  >
    <GithubIcon sx={{ fontSize: 3, mr: 2, color: `link.color` }} />
    Source

)

function usePrevAndNextSite(item) {
  const { allSitesYaml } = useStaticQuery(graphql`
    query {
      allSitesYaml(
        filter: {
          featured: { eq: true }
          main_url: { ne: null }
          fields: { hasScreenshot: { eq: true } }
        }
      ) {
        nodes {
          id
          url
          title
          fields {
            slug
          }
          childScreenshot {
            screenshotFile {
              childImageSharp {
                resize(width: 200, height: 200) {
                  src
                }
              }
            }
          }
        }
      }
    }
  `)

  const sites = allSitesYaml.nodes
  const currentIndex = sites.findIndex(node => node.fields.slug === item)
  const nextSite = sites[(currentIndex + 1) % sites.length]
  const previousSite =
    sites[currentIndex === 0 ? sites.length - 1 : currentIndex - 1]
  return { nextSite, previousSite }
}

/**
 * @returns {string} - the URI that should be navigated to when the showcase details modal is closed
 */
function getExitLocation(filters = {}) {
  if (Object.keys(filters).length) {
    const queryString = qs.stringify({ filters })
    return `/showcase/?${queryString}`
  } else {
    return `/showcase/`
  }
}

function ShowcaseModal({ children, location }) {
  const { previousSite, nextSite } = usePrevAndNextSite(location.pathname)
  const { filters } = location.state || {}
  return (
    <Modal
      modalBackgroundPath={getExitLocation(filters)}
      next={nextSite.fields.slug}
      previous={previousSite.fields.slug}
      filters={filters}
      modalNextLink={
        <>

          <div
            sx={{
              ...styles.prevNextLink,
              [mediaQueries.md]: {
                position: `absolute`,
                top: 240,
                width: 300,
                transform: `translateX(-80px) rotate(90deg)`,
              },
            }}
          >
            <MdArrowUpward
              sx={{
                transform: `rotate(90deg)`,
                [mediaQueries.md]: {
                  transform: `none`,
                },
              }}
            />

 Next Site in Showcase

{nextSite.title}

        </>
      }
      modalPreviousLink={
        <>

          <div
            sx={{
              ...styles.prevNextLink,
              [mediaQueries.md]: {
                textAlign: `right`,
                position: `absolute`,
                top: 240,
                width: 300,
                transform: `translateX(-80px) rotate(-90deg)`,
              },
            }}
          >
            <MdArrowUpward
              sx={{
                transform: `rotate(-90deg)`,
                [mediaQueries.md]: {
                  transform: `none`,
                },
              }}
            />

 Previous Site in Showcase

{previousSite.title}

        </>
      }
    >
      {children}
    </Modal>
  )
}

const ShowcaseDetails = ({ location, site, isModal, categories }) => {
  const screenshotFile = site.childScreenshot.screenshotFile.childImageSharp

  const PageLayout = isModal ? ShowcaseModal : Layout
  return (
    <PageLayout location={location}>
      <div
        sx={{
          display: `flex`,
          flexDirection: `column`,
          maxWidth: isModal ? false : 1080,
          margin: isModal ? false : `0 auto`,
          width: `100%`,
          order: 1,
        }}
      >
        <div css={{ width: `100%` }}>
          <Helmet titleTemplate="%s | GatsbyJS">
            <title>{`${site.title}: Showcase`}</title>
            <meta
              property="og:image"
              content={`https://www.gatsbyjs.org${screenshotFile.resize.src}`}
            />
            <meta
              name="twitter:image"
              content={`https://www.gatsbyjs.org${screenshotFile.resize.src}`}
            />
            <meta name="twitter:card" content="summary_large_image" />
            <meta
              name="og:title"
              value={`${site.title}: Showcase | GatsbyJS`}
            />
            <meta
              property="og:image:width"
              content={screenshotFile.resize.width}
            />
            <meta
              property="og:image:height"
              content={screenshotFile.resize.height}
            />
            <meta
              property="og:description"
              content={site.description || site.main_url}
            />
            <meta
              name="twitter:description"
              content={site.description || site.main_url}
            />
          </Helmet>

{site.title}

              {cleanUrl(site.main_url)}

            {site.built_by && (
              <span sx={{ color: `textMuted` }}>
                /
                Built by {` `}
                {site.built_by_url ? (

                    {site.built_by}

                ) : (
                  site.built_by
                )}

            )}

          <div
            sx={{
              alignItems: `center`,
              display: `flex`,
              fontFamily: `header`,
              mx: gutter,
              py: 4,
              [mediaQueries.lg]: {
                mx: gutterDesktop,
              },
            }}
          >
            {site.featured && <Featured />}
            {site.source_url && <SourceLink href={site.source_url} />}
            <div
              sx={{
                alignSelf: `center`,
                ml: `auto`,
              }}
            >
              <div
                css={{
                  display: `flex`,
                  position: `relative`,
                  zIndex: 1,
                }}
              >
                <Button
                  icon={<MdLink />}
                  overrideCSS={{ mr: 2 }}
                  tag="href"
                  to={site.main_url}
                >
                  Visit site
                </Button>
                <ShareMenu
                  image={`https://www.gatsbyjs.org${screenshotFile.resize.src}`}
                  title={site.title}
                  url={site.main_url}
                />

          <Screenshot
            alt={`Screenshot of ${site.title}`}
            boxShadow={!isModal}
            imageSharp={screenshotFile.fluid}
            key={site.id}
          />

{site.description}

            <div sx={{ display: `flex` }}>
              <div sx={{ color: `textMuted`, pr: 5 }}>Categories

                {categories.map((c, i) => (
                  <Fragment key={c}>
                    <Link
                      to={`/showcase/?${qs.stringify({ filters: [c] })}`}
                      state={{ isModal: true }}
                      sx={styles.link}
                    >
                      {c}
                    </Link>
                    {i === categories.length - 1 ? `` : `, `}
                  </Fragment>
                ))}

    </PageLayout>
  )
}

export default ShowcaseDetails
<commit_msg>fix(www): Fix Gatsby Showcase Details View (#21448)<commit_after>/** @jsx jsx */
import { jsx } from "theme-ui"
import React, { Fragment } from "react"
import { Link, useStaticQuery, graphql } from "gatsby"
import { Helmet } from "react-helmet"
import url from "url"
import Img from "gatsby-image"
import qs from "qs"

import { mediaQueries } from "gatsby-design-tokens/dist/theme-gatsbyjs-org"
import Layout from "../components/layout"
import Modal from "../components/modal"
import ShareMenu from "../components/share-menu"
import Button from "../components/button"
import Screenshot from "../views/shared/screenshot"

import MdArrowUpward from "react-icons/lib/md/arrow-upward"
import MdLink from "react-icons/lib/md/link"
import FeaturedIcon from "../assets/icons/featured-sites-icons"
import GithubIcon from "react-icons/lib/go/mark-github"

const gutter = 6
const gutterDesktop = 8

const styles = {
  link: {
    color: `link.color`,
    textDecoration: `none`,
  },
  prevNextLink: {
    color: `lilac`,
    fontFamily: `heading`,
    px: [6, null, null, 0],
  },
  prevNextLinkSiteTitle: {
    color: `link.color`,
    fontWeight: `bold`,
  },
  prevNextImage: {
    borderRadius: 1,
    boxShadow: `overlay`,
  },
}

const cleanUrl = mainUrl => {
  const parsed = url.parse(mainUrl)
  let path = parsed.pathname
  if (path[path.length - 1] === `/`) path = path.slice(0, path.length - 1)
  return parsed.hostname + path
}

const Featured = () => (
  <div
    sx={{
      color: `textMuted`,
      display: `flex`,
      fontWeight: `bold`,
      mr: 4,
    }}
  >
     t.space[5],
        m: 0,
        mr: 2,
        width: t => t.space[5],
      }}
    >
      <FeaturedIcon />

    {` `}
    Featured

)

const SourceLink = ({ ...props }) => (
  <a
    {...props}
    sx={{
      "&&": {
        border: 0,
      },
      display: `flex`,
      alignItems: `center`,
      mr: 3,
      color: `link.color`,
      width: `100%`,
    }}
  >
    <GithubIcon sx={{ fontSize: 3, mr: 2, color: `link.color` }} />
    Source

)

function usePrevAndNextSite(item) {
  const { allSitesYaml } = useStaticQuery(graphql`
    query {
      allSitesYaml(
        filter: {
          main_url: { ne: null }
          fields: { hasScreenshot: { eq: true } }
        }
      ) {
        nodes {
          id
          url
          title
          fields {
            slug
          }
          childScreenshot {
            screenshotFile {
              childImageSharp {
                resize(width: 200, height: 200) {
                  src
                }
              }
            }
          }
        }
      }
    }
  `)

  const sites = allSitesYaml.nodes
  const currentIndex = sites.findIndex(node => node.fields.slug === item)
  const nextSite = sites[(currentIndex + 1) % sites.length]
  const previousSite =
    sites[currentIndex === 0 ? sites.length - 1 : currentIndex - 1]
  return { nextSite, previousSite }
}

/**
 * @returns {string} - the URI that should be navigated to when the showcase details modal is closed
 */
function getExitLocation(filters = {}) {
  if (Object.keys(filters).length) {
    const queryString = qs.stringify({ filters })
    return `/showcase/?${queryString}`
  } else {
    return `/showcase/`
  }
}

function ShowcaseModal({ children, location }) {
  const { previousSite, nextSite } = usePrevAndNextSite(location.pathname)
  const { filters } = location.state || {}
  return (
    <Modal
      modalBackgroundPath={getExitLocation(filters)}
      next={nextSite.fields.slug}
      previous={previousSite.fields.slug}
      filters={filters}
      modalNextLink={
        <>

          <div
            sx={{
              ...styles.prevNextLink,
              [mediaQueries.md]: {
                position: `absolute`,
                top: 240,
                width: 300,
                transform: `translateX(-80px) rotate(90deg)`,
              },
            }}
          >
            <MdArrowUpward
              sx={{
                transform: `rotate(90deg)`,
                [mediaQueries.md]: {
                  transform: `none`,
                },
              }}
            />

 Next Site in Showcase

{nextSite.title}

        </>
      }
      modalPreviousLink={
        <>

          <div
            sx={{
              ...styles.prevNextLink,
              [mediaQueries.md]: {
                textAlign: `right`,
                position: `absolute`,
                top: 240,
                width: 300,
                transform: `translateX(-80px) rotate(-90deg)`,
              },
            }}
          >
            <MdArrowUpward
              sx={{
                transform: `rotate(-90deg)`,
                [mediaQueries.md]: {
                  transform: `none`,
                },
              }}
            />

 Previous Site in Showcase

{previousSite.title}

        </>
      }
    >
      {children}
    </Modal>
  )
}

const ShowcaseDetails = ({ location, site, isModal, categories }) => {
  const screenshotFile = site.childScreenshot.screenshotFile.childImageSharp

  const PageLayout = isModal ? ShowcaseModal : Layout
  return (
    <PageLayout location={location}>
      <div
        sx={{
          display: `flex`,
          flexDirection: `column`,
          maxWidth: isModal ? false : 1080,
          margin: isModal ? false : `0 auto`,
          width: `100%`,
          order: 1,
        }}
      >
        <div css={{ width: `100%` }}>
          <Helmet titleTemplate="%s | GatsbyJS">
            <title>{`${site.title}: Showcase`}</title>
            <meta
              property="og:image"
              content={`https://www.gatsbyjs.org${screenshotFile.resize.src}`}
            />
            <meta
              name="twitter:image"
              content={`https://www.gatsbyjs.org${screenshotFile.resize.src}`}
            />
            <meta name="twitter:card" content="summary_large_image" />
            <meta
              name="og:title"
              value={`${site.title}: Showcase | GatsbyJS`}
            />
            <meta
              property="og:image:width"
              content={screenshotFile.resize.width}
            />
            <meta
              property="og:image:height"
              content={screenshotFile.resize.height}
            />
            <meta
              property="og:description"
              content={site.description || site.main_url}
            />
            <meta
              name="twitter:description"
              content={site.description || site.main_url}
            />
          </Helmet>

{site.title}

              {cleanUrl(site.main_url)}

            {site.built_by && (
              <span sx={{ color: `textMuted` }}>
                /
                Built by {` `}
                {site.built_by_url ? (

                    {site.built_by}

                ) : (
                  site.built_by
                )}

            )}

          <div
            sx={{
              alignItems: `center`,
              display: `flex`,
              fontFamily: `header`,
              mx: gutter,
              py: 4,
              [mediaQueries.lg]: {
                mx: gutterDesktop,
              },
            }}
          >
            {site.featured && <Featured />}
            {site.source_url && <SourceLink href={site.source_url} />}
            <div
              sx={{
                alignSelf: `center`,
                ml: `auto`,
              }}
            >
              <div
                css={{
                  display: `flex`,
                  position: `relative`,
                  zIndex: 1,
                }}
              >
                <Button
                  icon={<MdLink />}
                  overrideCSS={{ mr: 2 }}
                  tag="href"
                  to={site.main_url}
                >
                  Visit site
                </Button>
                <ShareMenu
                  image={`https://www.gatsbyjs.org${screenshotFile.resize.src}`}
                  title={site.title}
                  url={site.main_url}
                />

          <Screenshot
            alt={`Screenshot of ${site.title}`}
            boxShadow={!isModal}
            imageSharp={screenshotFile.fluid}
            key={site.id}
          />

{site.description}

            <div sx={{ display: `flex` }}>
              <div sx={{ color: `textMuted`, pr: 5 }}>Categories

                {categories.map((c, i) => (
                  <Fragment key={c}>
                    <Link
                      to={`/showcase/?${qs.stringify({ filters: [c] })}`}
                      state={{ isModal: true }}
                      sx={styles.link}
                    >
                      {c}
                    </Link>
                    {i === categories.length - 1 ? `` : `, `}
                  </Fragment>
                ))}

    </PageLayout>
  )
}

export default ShowcaseDetails
<|endoftext|>


<commit_before>function runtime_1(element) {
    return {
        handler: function(handler_name) {
            var usage = $(element).data('usage');
            return "/" + usage + "/" + handler_name;
        },
        prep_xml_http_request: function(xhr) {
            xhr.setRequestHeader('X-CSRFToken', $.cookie('csrftoken'));
        }
    }
}
<commit_msg>Fix the name of handler_url in the runtime<commit_after>function runtime_1(element) {
    return {
        handler_url: function(handler_name) {
            var usage = $(element).data('usage');
            return "/" + usage + "/" + handler_name;
        },
        prep_xml_http_request: function(xhr) {
            xhr.setRequestHeader('X-CSRFToken', $.cookie('csrftoken'));
        }
    }
}
<|endoftext|>


<commit_before>import { LeoZhekov, flurreN } from 'CONTRIBUTORS';
import { change, date } from 'common/changelog';

export default [
  change(date(2020, 12, 23), 'Updated spells and talents for SL', [flurreN]),
  change(date(2020, 10, 30), 'Updated the deprecated StatisticBox elements with the new Statistic ones.', [LeoZhekov]),
];
<commit_msg>Changelog<commit_after>import { LeoZhekov, flurreN } from 'CONTRIBUTORS';
import { change, date } from 'common/changelog';

export default [
  change(date(2020, 12, 24), 'Updated spells and talents for SL', [flurreN]),
  change(date(2020, 12, 23), 'Updated spells and talents for SL', [flurreN]),
  change(date(2020, 10, 30), 'Updated the deprecated StatisticBox elements with the new Statistic ones.', [LeoZhekov]),
];
<|endoftext|>


<commit_before>var React = require('react');
var types = React.PropTypes;
import {mergeProps} from 'pui-react-helpers';

var DividerProps = {
  propTypes: {
    inverse: types.bool,
    size: types.oneOf(['large'])
  }
};

/**
 * @component Divider
 * @description Draws horizontal lines between different content groupings.
 *
 * @property size {String} If set to `large`, makes the line thicker
 *
 * @example ```js
 * var Divider = require('pui-react-dividers').Divider;
 * var MyComponent = React.createClass({
 *   render() {
 *     return (
 *       <p>Some Content</p>
 *       <Divider/>
 *       <p>Other Content</p>
 *       <Divider size="large"/>
 *       <p>Other other content</p>
 *     );
 *   }
 * });
 * ```
 *
 */
var Divider = React.createClass({
  propTypes: {
    inverse: types.bool,
    size: types.oneOf(['large'])
  },

  render() {
    var {inverse, size, ...others} = this.props;
    var dividerClass =
      {
        'divider-1': inverse && size !== 'large',
        'divider-2': inverse && size === 'large',
        'divider-alternate-1': !inverse && size !== 'large',
        'divider-alternate-2': !inverse && size === 'large'
      };

    const props = mergeProps(others, {className: dividerClass});

    return
;
  }
});

function defDivider(props) {
  return React.createClass({
    mixins: [DividerProps],
    render() {
      return <Divider {...props} {...this.props} />;
    }
  });
}

module.exports = {
  Divider,

  /**
   * @component InverseDivider
   * @description A Divider for use on dark backgrounds.
   *
   * @property size {String} If set to `large`, makes the line thicker
   *
   * @example ```js
   * var InverseDivider = require('pui-react-dividers').InverseDivider;
   * var MyComponent = React.createClass({
   *   render() {
   *     return (
   *       <p>Some Content</p>
   *       <InverseDivider/>
   *       <p>Other Content</p>
   *       <InverseDivider size="large"/>
   *       <p>Other other content</p>
   *     );
   *   }
   * });
   * ```
   *
   */
  InverseDivider: defDivider({inverse: true})
};

/*doc
---
title: Dividers
name: divider_react
categories:
 - react_base_dividers
 - react_all
---

npm install pui-react-dividers --save

Require the subcomponents:

```
var Divider = require('pui-react-dividers').Divider;
var InverseDivider = require('pui-react-dividers').InverseDivider;
```

Dividers draw horizontal lines between different content groupings.

```react_example_table
<Divider />

<Divider size="large" />
```

On a dark background, use these inverse dividers

```react_inverse_example_table
<div>
  I am some content
  <InverseDivider />
  Me too
</div>

<div>
  Here's some stuff above the divider
  <InverseDivider size='large' />
  Here's some stuff below the divider
</div>
```
*/
<commit_msg>chore(docs): update divider docs<commit_after>var React = require('react');
var types = React.PropTypes;
import {mergeProps} from 'pui-react-helpers';

var DividerProps = {
  propTypes: {
    inverse: types.bool,
    size: types.oneOf(['large'])
  }
};

/**
 * @component Divider
 * @description Draws horizontal lines between different content groupings.
 *
 * @property size {String} If set to `large`, makes the line thicker
 *
 * @example ```js
 * var Divider = require('pui-react-dividers').Divider;
 * var MyComponent = React.createClass({
 *   render() {
 *     return (
 *       <p>Some Content</p>
 *       <Divider/>
 *       <p>Other Content</p>
 *       <Divider size="large"/>
 *       <p>Other other content</p>
 *     );
 *   }
 * });
 * ```
 *
 */
var Divider = React.createClass({
  propTypes: {
    inverse: types.bool,
    size: types.oneOf(['large'])
  },

  render() {
    var {inverse, size, ...others} = this.props;
    var dividerClass =
      {
        'divider-1': inverse && size !== 'large',
        'divider-2': inverse && size === 'large',
        'divider-alternate-1': !inverse && size !== 'large',
        'divider-alternate-2': !inverse && size === 'large'
      };

    const props = mergeProps(others, {className: dividerClass});

    return
;
  }
});

function defDivider(props) {
  return React.createClass({
    mixins: [DividerProps],
    render() {
      return <Divider {...props} {...this.props} />;
    }
  });
}

module.exports = {
  Divider,

  /**
   * @component InverseDivider
   * @description A Divider for use on dark backgrounds.
   *
   * @property size {String} If set to `large`, makes the line thicker
   *
   * @example ```js
   * var InverseDivider = require('pui-react-dividers').InverseDivider;
   * var MyComponent = React.createClass({
   *   render() {
   *     return (
   *       <p>Some Content</p>
   *       <InverseDivider/>
   *       <p>Other Content</p>
   *       <InverseDivider size="large"/>
   *       <p>Other other content</p>
   *     );
   *   }
   * });
   * ```
   *
   */
  InverseDivider: defDivider({inverse: true})
};

/*doc
---
title: Dividers
name: divider_react
categories:
 - react_base_dividers
 - react_all
---

npm install pui-react-dividers --save

Require the subcomponents:

```
var Divider = require('pui-react-dividers').Divider;
var InverseDivider = require('pui-react-dividers').InverseDivider;
```

Dividers draw horizontal lines between different content groupings.

```react_example_table
<Divider />

<Divider size="large" />
```

On a dark background, use these inverse dividers

```react_inverse_example_table
<div className="type-dark-11">
  I am some content
  <InverseDivider />
  Me too
</div>

<div className="type-dark-11">
  Here's some stuff above the divider
  <InverseDivider size='large' />
  Here's some stuff below the divider
</div>
```
*/
<|endoftext|>


<commit_before>/**
 * @author Mat Groves http://matgroves.com/ @Doormat23
 */

PIXI.glContexts = []; // this is where we store the webGL contexts for easy access.
PIXI.instances = [];

/**
 * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer
 * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs.
 * So no need for Sprite Batches or Sprite Clouds.
 * Don't forget to add the view to your DOM or you will not see anything :)
 *
 * @class WebGLRenderer
 * @constructor
 * @param game {Phaser.Game} A reference to the Phaser Game instance
 */
PIXI.WebGLRenderer = function(game) {

    /**
    * @property {Phaser.Game} game - A reference to the Phaser Game instance.
    */
    this.game = game;

    if (!PIXI.defaultRenderer)
    {
        PIXI.defaultRenderer = this;
    }

    /**
     * @property type
     * @type Number
     */
    this.type = PIXI.WEBGL_RENDERER;

    /**
     * The resolution of the renderer
     *
     * @property resolution
     * @type Number
     * @default 1
     */
    this.resolution = game.resolution;

    /**
     * Whether the render view is transparent
     *
     * @property transparent
     * @type Boolean
     */
    this.transparent = game.transparent;

    /**
     * Whether the render view should be resized automatically
     *
     * @property autoResize
     * @type Boolean
     */
    this.autoResize = false;

    /**
     * The value of the preserveDrawingBuffer flag affects whether or not the contents of the stencil buffer is retained after rendering.
     *
     * @property preserveDrawingBuffer
     * @type Boolean
     */
    this.preserveDrawingBuffer = game.preserveDrawingBuffer;

    /**
     * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true:
     * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0).
     * If the Stage is transparent, Pixi will clear to the target Stage's background color.
     * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set.
     *
     * @property clearBeforeRender
     * @type Boolean
     * @default
     */
    this.clearBeforeRender = game.clearBeforeRender;

    /**
     * The width of the canvas view
     *
     * @property width
     * @type Number
     */
    this.width = game.width;

    /**
     * The height of the canvas view
     *
     * @property height
     * @type Number
     */
    this.height = game.height;

    /**
     * The canvas element that everything is drawn to
     *
     * @property view
     * @type HTMLCanvasElement
     */
    this.view = game.canvas;

    /**
     * @property _contextOptions
     * @type Object
     * @private
     */
    this._contextOptions = {
        alpha: this.transparent,
        antialias: game.antialias,
        premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
        stencil: true,
        preserveDrawingBuffer: this.preserveDrawingBuffer
    };

    /**
     * @property projection
     * @type Point
     */
    this.projection = new PIXI.Point();

    /**
     * @property offset
     * @type Point
     */
    this.offset = new PIXI.Point();

    // time to create the render managers! each one focuses on managing a state in webGL

    /**
     * Deals with managing the shader programs and their attribs
     * @property shaderManager
     * @type WebGLShaderManager
     */
    this.shaderManager = new PIXI.WebGLShaderManager();

    /**
     * Manages the rendering of sprites
     * @property spriteBatch
     * @type WebGLSpriteBatch
     */
    this.spriteBatch = new PIXI.WebGLSpriteBatch();

    /**
     * Manages the masks using the stencil buffer
     * @property maskManager
     * @type WebGLMaskManager
     */
    this.maskManager = new PIXI.WebGLMaskManager();

    /**
     * Manages the filters
     * @property filterManager
     * @type WebGLFilterManager
     */
    this.filterManager = new PIXI.WebGLFilterManager();

    /**
     * Manages the stencil buffer
     * @property stencilManager
     * @type WebGLStencilManager
     */
    this.stencilManager = new PIXI.WebGLStencilManager();

    /**
     * Manages the blendModes
     * @property blendModeManager
     * @type WebGLBlendModeManager
     */
    this.blendModeManager = new PIXI.WebGLBlendModeManager();

    /**
     * @property renderSession
     * @type Object
     */
    this.renderSession = {};

    //  Needed?
    this.renderSession.game = this.game;
    this.renderSession.gl = this.gl;
    this.renderSession.drawCount = 0;
    this.renderSession.shaderManager = this.shaderManager;
    this.renderSession.maskManager = this.maskManager;
    this.renderSession.filterManager = this.filterManager;
    this.renderSession.blendModeManager = this.blendModeManager;
    this.renderSession.spriteBatch = this.spriteBatch;
    this.renderSession.stencilManager = this.stencilManager;
    this.renderSession.renderer = this;
    this.renderSession.resolution = this.resolution;

    // time init the context..
    this.initContext();

    // map some webGL blend modes..
    this.mapBlendModes();

};

// constructor
PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer;

/**
* @method initContext
*/
PIXI.WebGLRenderer.prototype.initContext = function()
{
    var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions);

    this.gl = gl;

    if (!gl) {
        // fail, not able to get a context
        throw new Error('This browser does not support webGL. Try using the canvas renderer');
    }

    this.glContextId = gl.id = PIXI.WebGLRenderer.glContextId++;

    PIXI.glContexts[this.glContextId] = gl;

    PIXI.instances[this.glContextId] = this;

    // set up the default pixi settings..
    gl.disable(gl.DEPTH_TEST);
    gl.disable(gl.CULL_FACE);
    gl.enable(gl.BLEND);

    // need to set the context for all the managers...
    this.shaderManager.setContext(gl);
    this.spriteBatch.setContext(gl);
    this.maskManager.setContext(gl);
    this.filterManager.setContext(gl);
    this.blendModeManager.setContext(gl);
    this.stencilManager.setContext(gl);

    this.renderSession.gl = this.gl;

    // now resize and we are good to go!
    this.resize(this.width, this.height);
};

/**
 * Renders the stage to its webGL view
 *
 * @method render
 * @param stage {Stage} the Stage element to be rendered
 */
PIXI.WebGLRenderer.prototype.render = function(stage)
{
    // no point rendering if our context has been blown up!
    if (this.contextLost)
    {
        return;
    }

    // update the scene graph
    stage.updateTransform();

    var gl = this.gl;

    // -- Does this need to be set every frame? -- //
    gl.viewport(0, 0, this.width, this.height);

    // make sure we are bound to the main frame buffer
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

    if (this.game.clearBeforeRender)
    {
        gl.clearColor(stage._bgColor.r, stage._bgColor.g, stage._bgColor.b, stage._bgColor.a);

        gl.clear(gl.COLOR_BUFFER_BIT);
    }

    if (this.game.fd.on)
    {
        this.game.fd.record();
    }

    this.renderDisplayObject(stage, this.projection);
};

/**
 * Renders a Display Object.
 *
 * @method renderDisplayObject
 * @param displayObject {DisplayObject} The DisplayObject to render
 * @param projection {Point} The projection
 * @param buffer {Array} a standard WebGL buffer
 */
PIXI.WebGLRenderer.prototype.renderDisplayObject = function(displayObject, projection, buffer, matrix)
{
    this.renderSession.blendModeManager.setBlendMode(PIXI.blendModes.NORMAL);

    // reset the render session data..
    this.renderSession.drawCount = 0;

    // make sure to flip the Y if using a render texture..
    this.renderSession.flipY = buffer ? -1 : 1;

    // set the default projection
    this.renderSession.projection = projection;

    //set the default offset
    this.renderSession.offset = this.offset;

    // start the sprite batch
    this.spriteBatch.begin(this.renderSession);

    // start the filter manager
    this.filterManager.begin(this.renderSession, buffer);

    // render the scene!
    displayObject._renderWebGL(this.renderSession, matrix);

    // finish the sprite batch
    this.spriteBatch.end();
};

/**
 * Resizes the webGL view to the specified width and height.
 *
 * @method resize
 * @param width {Number} the new width of the webGL view
 * @param height {Number} the new height of the webGL view
 */
PIXI.WebGLRenderer.prototype.resize = function(width, height)
{
    this.width = width * this.resolution;
    this.height = height * this.resolution;

    this.view.width = this.width;
    this.view.height = this.height;

    if (this.autoResize) {
        this.view.style.width = this.width / this.resolution + 'px';
        this.view.style.height = this.height / this.resolution + 'px';
    }

    this.gl.viewport(0, 0, this.width, this.height);

    this.projection.x =  this.width / 2 / this.resolution;
    this.projection.y =  -this.height / 2 / this.resolution;
};

/**
 * Updates and Creates a WebGL texture for the renderers context.
 *
 * @method updateTexture
 * @param texture {Texture} the texture to update
 * @return {boolean} True if the texture was successfully bound, otherwise false.
 */
PIXI.WebGLRenderer.prototype.updateTexture = function(texture)
{
    if (!texture.hasLoaded)
    {
        return false;
    }

    var gl = this.gl;

    if (!texture._glTextures[gl.id])
    {
        texture._glTextures[gl.id] = gl.createTexture();
    }

    gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]);

    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultipliedAlpha);

    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source);

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);

    if (texture.mipmap && PIXI.isPowerOfTwo(texture.width, texture.height))
    {
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST);
        gl.generateMipmap(gl.TEXTURE_2D);
    }
    else
    {
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
    }

    if (!texture._powerOf2)
    {
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    }
    else
    {
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
    }

    texture._dirty[gl.id] = false;

    // return texture._glTextures[gl.id];
    return true;

};

/**
 * Removes everything from the renderer (event listeners, spritebatch, etc...)
 *
 * @method destroy
 */
PIXI.WebGLRenderer.prototype.destroy = function()
{
    PIXI.glContexts[this.glContextId] = null;

    this.projection = null;
    this.offset = null;

    this.shaderManager.destroy();
    this.spriteBatch.destroy();
    this.maskManager.destroy();
    this.filterManager.destroy();

    this.shaderManager = null;
    this.spriteBatch = null;
    this.maskManager = null;
    this.filterManager = null;

    this.gl = null;
    this.renderSession = null;

    PIXI.CanvasPool.remove(this);

    PIXI.instances[this.glContextId] = null;

    PIXI.WebGLRenderer.glContextId--;
};

/**
 * Maps Pixi blend modes to WebGL blend modes.
 *
 * @method mapBlendModes
 */
PIXI.WebGLRenderer.prototype.mapBlendModes = function()
{
    var gl = this.gl;

    if (!PIXI.blendModesWebGL)
    {
        var b = [];
        var modes = PIXI.blendModes;

        b[modes.NORMAL]        = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.ADD]           = [gl.SRC_ALPHA, gl.DST_ALPHA];
        b[modes.MULTIPLY]      = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA];
        b[modes.SCREEN]        = [gl.SRC_ALPHA, gl.ONE];
        b[modes.OVERLAY]       = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.DARKEN]        = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.LIGHTEN]       = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.COLOR_DODGE]   = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.COLOR_BURN]    = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.HARD_LIGHT]    = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.SOFT_LIGHT]    = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.DIFFERENCE]    = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.EXCLUSION]     = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.HUE]           = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.SATURATION]    = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.COLOR]         = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.LUMINOSITY]    = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];

        PIXI.blendModesWebGL = b;
    }
};

PIXI.WebGLRenderer.glContextId = 0;
<commit_msg>Added fd to the renderSession to stop the FrameDebugger breaking in TilingSprite under WebGL.<commit_after>/**
 * @author Mat Groves http://matgroves.com/ @Doormat23
 */

PIXI.glContexts = []; // this is where we store the webGL contexts for easy access.
PIXI.instances = [];

/**
 * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer
 * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs.
 * So no need for Sprite Batches or Sprite Clouds.
 * Don't forget to add the view to your DOM or you will not see anything :)
 *
 * @class WebGLRenderer
 * @constructor
 * @param game {Phaser.Game} A reference to the Phaser Game instance
 */
PIXI.WebGLRenderer = function(game) {

    /**
    * @property {Phaser.Game} game - A reference to the Phaser Game instance.
    */
    this.game = game;

    if (!PIXI.defaultRenderer)
    {
        PIXI.defaultRenderer = this;
    }

    /**
     * @property type
     * @type Number
     */
    this.type = PIXI.WEBGL_RENDERER;

    /**
     * The resolution of the renderer
     *
     * @property resolution
     * @type Number
     * @default 1
     */
    this.resolution = game.resolution;

    /**
     * Whether the render view is transparent
     *
     * @property transparent
     * @type Boolean
     */
    this.transparent = game.transparent;

    /**
     * Whether the render view should be resized automatically
     *
     * @property autoResize
     * @type Boolean
     */
    this.autoResize = false;

    /**
     * The value of the preserveDrawingBuffer flag affects whether or not the contents of the stencil buffer is retained after rendering.
     *
     * @property preserveDrawingBuffer
     * @type Boolean
     */
    this.preserveDrawingBuffer = game.preserveDrawingBuffer;

    /**
     * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true:
     * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0).
     * If the Stage is transparent, Pixi will clear to the target Stage's background color.
     * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set.
     *
     * @property clearBeforeRender
     * @type Boolean
     * @default
     */
    this.clearBeforeRender = game.clearBeforeRender;

    /**
     * The width of the canvas view
     *
     * @property width
     * @type Number
     */
    this.width = game.width;

    /**
     * The height of the canvas view
     *
     * @property height
     * @type Number
     */
    this.height = game.height;

    /**
     * The canvas element that everything is drawn to
     *
     * @property view
     * @type HTMLCanvasElement
     */
    this.view = game.canvas;

    /**
     * @property _contextOptions
     * @type Object
     * @private
     */
    this._contextOptions = {
        alpha: this.transparent,
        antialias: game.antialias,
        premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
        stencil: true,
        preserveDrawingBuffer: this.preserveDrawingBuffer
    };

    /**
     * @property projection
     * @type Point
     */
    this.projection = new PIXI.Point();

    /**
     * @property offset
     * @type Point
     */
    this.offset = new PIXI.Point();

    // time to create the render managers! each one focuses on managing a state in webGL

    /**
     * Deals with managing the shader programs and their attribs
     * @property shaderManager
     * @type WebGLShaderManager
     */
    this.shaderManager = new PIXI.WebGLShaderManager();

    /**
     * Manages the rendering of sprites
     * @property spriteBatch
     * @type WebGLSpriteBatch
     */
    this.spriteBatch = new PIXI.WebGLSpriteBatch();

    /**
     * Manages the masks using the stencil buffer
     * @property maskManager
     * @type WebGLMaskManager
     */
    this.maskManager = new PIXI.WebGLMaskManager();

    /**
     * Manages the filters
     * @property filterManager
     * @type WebGLFilterManager
     */
    this.filterManager = new PIXI.WebGLFilterManager();

    /**
     * Manages the stencil buffer
     * @property stencilManager
     * @type WebGLStencilManager
     */
    this.stencilManager = new PIXI.WebGLStencilManager();

    /**
     * Manages the blendModes
     * @property blendModeManager
     * @type WebGLBlendModeManager
     */
    this.blendModeManager = new PIXI.WebGLBlendModeManager();

    /**
     * @property renderSession
     * @type Object
     */
    this.renderSession = {};

    //  Needed?
    this.renderSession.game = this.game;
    this.renderSession.fd = this.game.fd;
    this.renderSession.gl = this.gl;
    this.renderSession.drawCount = 0;
    this.renderSession.shaderManager = this.shaderManager;
    this.renderSession.maskManager = this.maskManager;
    this.renderSession.filterManager = this.filterManager;
    this.renderSession.blendModeManager = this.blendModeManager;
    this.renderSession.spriteBatch = this.spriteBatch;
    this.renderSession.stencilManager = this.stencilManager;
    this.renderSession.renderer = this;
    this.renderSession.resolution = this.resolution;

    // time init the context..
    this.initContext();

    // map some webGL blend modes..
    this.mapBlendModes();

};

// constructor
PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer;

/**
* @method initContext
*/
PIXI.WebGLRenderer.prototype.initContext = function()
{
    var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions);

    this.gl = gl;

    if (!gl) {
        // fail, not able to get a context
        throw new Error('This browser does not support webGL. Try using the canvas renderer');
    }

    this.glContextId = gl.id = PIXI.WebGLRenderer.glContextId++;

    PIXI.glContexts[this.glContextId] = gl;

    PIXI.instances[this.glContextId] = this;

    // set up the default pixi settings..
    gl.disable(gl.DEPTH_TEST);
    gl.disable(gl.CULL_FACE);
    gl.enable(gl.BLEND);

    // need to set the context for all the managers...
    this.shaderManager.setContext(gl);
    this.spriteBatch.setContext(gl);
    this.maskManager.setContext(gl);
    this.filterManager.setContext(gl);
    this.blendModeManager.setContext(gl);
    this.stencilManager.setContext(gl);

    this.renderSession.gl = this.gl;

    // now resize and we are good to go!
    this.resize(this.width, this.height);
};

/**
 * Renders the stage to its webGL view
 *
 * @method render
 * @param stage {Stage} the Stage element to be rendered
 */
PIXI.WebGLRenderer.prototype.render = function(stage)
{
    // no point rendering if our context has been blown up!
    if (this.contextLost)
    {
        return;
    }

    // update the scene graph
    stage.updateTransform();

    var gl = this.gl;

    // -- Does this need to be set every frame? -- //
    gl.viewport(0, 0, this.width, this.height);

    // make sure we are bound to the main frame buffer
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

    if (this.game.clearBeforeRender)
    {
        gl.clearColor(stage._bgColor.r, stage._bgColor.g, stage._bgColor.b, stage._bgColor.a);

        gl.clear(gl.COLOR_BUFFER_BIT);
    }

    if (this.game.fd.on)
    {
        this.game.fd.record();
    }

    this.renderDisplayObject(stage, this.projection);
};

/**
 * Renders a Display Object.
 *
 * @method renderDisplayObject
 * @param displayObject {DisplayObject} The DisplayObject to render
 * @param projection {Point} The projection
 * @param buffer {Array} a standard WebGL buffer
 */
PIXI.WebGLRenderer.prototype.renderDisplayObject = function(displayObject, projection, buffer, matrix)
{
    this.renderSession.blendModeManager.setBlendMode(PIXI.blendModes.NORMAL);

    // reset the render session data..
    this.renderSession.drawCount = 0;

    // make sure to flip the Y if using a render texture..
    this.renderSession.flipY = buffer ? -1 : 1;

    // set the default projection
    this.renderSession.projection = projection;

    //set the default offset
    this.renderSession.offset = this.offset;

    // start the sprite batch
    this.spriteBatch.begin(this.renderSession);

    // start the filter manager
    this.filterManager.begin(this.renderSession, buffer);

    // render the scene!
    displayObject._renderWebGL(this.renderSession, matrix);

    // finish the sprite batch
    this.spriteBatch.end();
};

/**
 * Resizes the webGL view to the specified width and height.
 *
 * @method resize
 * @param width {Number} the new width of the webGL view
 * @param height {Number} the new height of the webGL view
 */
PIXI.WebGLRenderer.prototype.resize = function(width, height)
{
    this.width = width * this.resolution;
    this.height = height * this.resolution;

    this.view.width = this.width;
    this.view.height = this.height;

    if (this.autoResize) {
        this.view.style.width = this.width / this.resolution + 'px';
        this.view.style.height = this.height / this.resolution + 'px';
    }

    this.gl.viewport(0, 0, this.width, this.height);

    this.projection.x =  this.width / 2 / this.resolution;
    this.projection.y =  -this.height / 2 / this.resolution;
};

/**
 * Updates and Creates a WebGL texture for the renderers context.
 *
 * @method updateTexture
 * @param texture {Texture} the texture to update
 * @return {boolean} True if the texture was successfully bound, otherwise false.
 */
PIXI.WebGLRenderer.prototype.updateTexture = function(texture)
{
    if (!texture.hasLoaded)
    {
        return false;
    }

    var gl = this.gl;

    if (!texture._glTextures[gl.id])
    {
        texture._glTextures[gl.id] = gl.createTexture();
    }

    gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]);

    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultipliedAlpha);

    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source);

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);

    if (texture.mipmap && PIXI.isPowerOfTwo(texture.width, texture.height))
    {
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST);
        gl.generateMipmap(gl.TEXTURE_2D);
    }
    else
    {
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
    }

    if (!texture._powerOf2)
    {
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    }
    else
    {
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
    }

    texture._dirty[gl.id] = false;

    // return texture._glTextures[gl.id];
    return true;

};

/**
 * Removes everything from the renderer (event listeners, spritebatch, etc...)
 *
 * @method destroy
 */
PIXI.WebGLRenderer.prototype.destroy = function()
{
    PIXI.glContexts[this.glContextId] = null;

    this.projection = null;
    this.offset = null;

    this.shaderManager.destroy();
    this.spriteBatch.destroy();
    this.maskManager.destroy();
    this.filterManager.destroy();

    this.shaderManager = null;
    this.spriteBatch = null;
    this.maskManager = null;
    this.filterManager = null;

    this.gl = null;
    this.renderSession = null;

    PIXI.CanvasPool.remove(this);

    PIXI.instances[this.glContextId] = null;

    PIXI.WebGLRenderer.glContextId--;
};

/**
 * Maps Pixi blend modes to WebGL blend modes.
 *
 * @method mapBlendModes
 */
PIXI.WebGLRenderer.prototype.mapBlendModes = function()
{
    var gl = this.gl;

    if (!PIXI.blendModesWebGL)
    {
        var b = [];
        var modes = PIXI.blendModes;

        b[modes.NORMAL]        = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.ADD]           = [gl.SRC_ALPHA, gl.DST_ALPHA];
        b[modes.MULTIPLY]      = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA];
        b[modes.SCREEN]        = [gl.SRC_ALPHA, gl.ONE];
        b[modes.OVERLAY]       = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.DARKEN]        = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.LIGHTEN]       = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.COLOR_DODGE]   = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.COLOR_BURN]    = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.HARD_LIGHT]    = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.SOFT_LIGHT]    = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.DIFFERENCE]    = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.EXCLUSION]     = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.HUE]           = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.SATURATION]    = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.COLOR]         = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];
        b[modes.LUMINOSITY]    = [gl.ONE,       gl.ONE_MINUS_SRC_ALPHA];

        PIXI.blendModesWebGL = b;
    }
};

PIXI.WebGLRenderer.glContextId = 0;
<|endoftext|>


<commit_before>//alert("hello world!");

function hack_zhihu_like(answer_id, xsrf)
{

  param = {
    method:"vote_up",
    params:"{\"answer_id\":\"" + answer_id + "\"}",
    _xsrf:xsrf
  };

  backfunc = function(){
    console.log("like back");
  };
  $.post("/node/AnswerVoteBarV2",param, backfunc);

}

function hack_zhihu_get_xsrf()
{
  var rf = $("input[name=_xsrf]").val();
  console.log("xsrf="+rf);
  return rf;
}

function hack_zhihu_get_all_answer_ids()
{
  anslist = $(".zm-item-answer");
  console.log(anslist.length)
  var ret = [];
  for (var i = 0, l = anslist.length; i < l; i++) {
    var aid = anslist[i].getAttribute("data-aid");
    ret.push(aid);
    console.log(aid);
  }
  return ret;
}

function hack_zhihu_likeall()
{
  var xsrf = hack_zhihu_get_xsrf();
  var aids = hack_zhihu_get_all_answer_ids();
  console.log("aid list size="+aids.length);
  for (var i = 0; i < aids.length; i++) {
    hack_zhihu_like(aids[i], xsrf);
  }
}

chrome.runtime.onMessage.addListener(function(request, sender, callback) {
  console.log(request);
  hack_zhihu_likeall();
});

// automatic like all!
hack_zhihu_likeall();
<commit_msg>add zhihu feed auto vote up<commit_after>//alert("hello world!");

function hack_zhihu_like(answer_id, xsrf)
{

  param = {
    method:"vote_up",
    params:"{\"answer_id\":\"" + answer_id + "\"}",
    _xsrf:xsrf
  };

  backfunc = function(){
    console.log("like back");
  };
  $.post("/node/AnswerVoteBarV2",param, backfunc);

}

function hack_zhihu_get_xsrf()
{
  var rf = $("input[name=_xsrf]").val();
  console.log("xsrf="+rf);
  return rf;
}

// 在feed界面
function hack_zhihu_get_all_answer_ids_b()
{
  anslist = $(".entry-body");
  console.log(anslist.length)
  var ret = [];
  for (var i = 0; i < anslist.length; i++) {
    var aid = anslist[i].getAttribute("data-aid");
    ret.push(aid);
  }
  return ret;
}

// 在具体问题界面
function hack_zhihu_get_all_answer_ids_a()
{
  anslist = $(".zm-item-answer");
  console.log(anslist.length)
  var ret = [];
  for (var i = 0, l = anslist.length; i < l; i++) {
    var aid = anslist[i].getAttribute("data-aid");
    ret.push(aid);
    console.log(aid);
  }
  return ret;
}

function hack_zhihu_get_all_answer_ids()
{
    var ret = hack_zhihu_get_all_answer_ids_a();
    if (ret.length == 0) {
      ret = hack_zhihu_get_all_answer_ids_b();
    }

    console.log("length of aids=" + ret.length);
    return ret;
}

function hack_zhihu_likeall()
{
  var xsrf = hack_zhihu_get_xsrf();
  var aids = hack_zhihu_get_all_answer_ids();
  console.log("aid list size="+aids.length);
  for (var i = 0; i < aids.length; i++) {
    hack_zhihu_like(aids[i], xsrf);
  }
}

chrome.runtime.onMessage.addListener(function(request, sender, callback) {
  console.log(request);
  // hack_zhihu_likeall();
});

hack_zhihu_likeall();
setInterval(hack_zhihu_likeall, 30000);
// automatic like all!
// hack_zhihu_likeall();
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc directive
 * @name ng.directive:a
 * @restrict E
 *
 * @description
 * Modifies the default behavior of html A tag, so that the default action is prevented when href
 * attribute is empty.
 *
 * The reasoning for this change is to allow easy creation of action links with `ngClick` directive
 * without changing the location or causing page reloads, e.g.:
 * Save
 */
var htmlAnchorDirective = valueFn({
  restrict: 'E',
  compile: function(element, attr) {
    // turn link into a link in IE
    // but only if it doesn't have name attribute, in which case it's an anchor
    if (!attr.href) {
      attr.$set('href', '');
    }

    return function(scope, element) {
      element.bind('click', function(event){
        // if we have no href url, then don't navigate anywhere.
        if (!element.attr('href')) {
          event.preventDefault();
          return false; // Needed for opera
        }
      });
    }
  }
});
<commit_msg>revert: fix(a): prevent Opera from incorrectly navigating on link click<commit_after>'use strict';

/**
 * @ngdoc directive
 * @name ng.directive:a
 * @restrict E
 *
 * @description
 * Modifies the default behavior of html A tag, so that the default action is prevented when href
 * attribute is empty.
 *
 * The reasoning for this change is to allow easy creation of action links with `ngClick` directive
 * without changing the location or causing page reloads, e.g.:
 * Save
 */
var htmlAnchorDirective = valueFn({
  restrict: 'E',
  compile: function(element, attr) {
    // turn link into a link in IE
    // but only if it doesn't have name attribute, in which case it's an anchor
    if (!attr.href) {
      attr.$set('href', '');
    }

    return function(scope, element) {
      element.bind('click', function(event){
        // if we have no href url, then don't navigate anywhere.
        if (!element.attr('href')) {
          event.preventDefault();
        }
      });
    }
  }
});
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc directive
 * @name ng.directive:a
 * @restrict E
 *
 * @description
 * Modifies the default behavior of html A tag, so that the default action is prevented when href
 * attribute is empty.
 *
 * The reasoning for this change is to allow easy creation of action links with `ngClick` directive
 * without changing the location or causing page reloads, e.g.:
 * Save
 */
var htmlAnchorDirective = valueFn({
  restrict: 'E',
  compile: function(element, attr) {
    // turn link into a link in IE
    // but only if it doesn't have name attribute, in which case it's an anchor
    if (!attr.href) {
      attr.$set('href', '');
    }

    return function(scope, element) {
      element.bind('click', function(event){
        // if we have no href url, then don't navigate anywhere.
        if (!element.attr('href')) {
          event.preventDefault();
        }
      });
    }
  }
});
<commit_msg>fix(a): prevent Opera from incorrectly navigating on link click<commit_after>'use strict';

/**
 * @ngdoc directive
 * @name ng.directive:a
 * @restrict E
 *
 * @description
 * Modifies the default behavior of html A tag, so that the default action is prevented when href
 * attribute is empty.
 *
 * The reasoning for this change is to allow easy creation of action links with `ngClick` directive
 * without changing the location or causing page reloads, e.g.:
 * Save
 */
var htmlAnchorDirective = valueFn({
  restrict: 'E',
  compile: function(element, attr) {
    // turn link into a link in IE
    // but only if it doesn't have name attribute, in which case it's an anchor
    if (!attr.href) {
      attr.$set('href', '');
    }

    return function(scope, element) {
      element.bind('click', function(event){
        // if we have no href url, then don't navigate anywhere.
        if (!element.attr('href')) {
          event.preventDefault();
          return false; // Needed for opera
        }
      });
    }
  }
});
<|endoftext|>