<commit_before>$(document).ready(function () {
  //Cached elements
  var $mainNav = $('#main-nav');
  var $navLogo = $mainNav.children('.logo');

  var $sections = $('.section');
  var $sectionIntro = $sections.filter('[data-section="home"]');
  var $sectionWork = $sections.filter('[data-section="work"]');
  var $sectionSkills = $sections.filter('[data-section="skills"]');
  var $sectionContact = $sections.filter('[data-section="contact"]');

  var $introParticles = $('#intro-particles');
  var $introMiddle = $sectionIntro.children('.middle');
  var $introLogo = $introMiddle.children('.logo');

  var $skillsParticles = $('#skills-particles');

  var $contactForm = $sectionContact.find('form');
  var $contactEmail = $contactForm.find('#contact-email');
  var $contactMessage = $contactForm.find('#contact-message');

  //Constants that aren't elements...
  var contactMessageMin = parseInt($contactMessage.attr('minlength'));
  var contactMessageMax = parseInt($contactMessage.attr('maxlength'));

  //State tracking
  var initialLoad = true;
  var prevY = null;
  var prevW = null;
  var scrollPoints = {};
  var scrollInElements = [];
  var previousSection = '';
  var currentSection = '';
  var preventScrollSectionChange = true;
  var preventScrollSectionAnim = false;
  var contactSending = false;

  /**********************************
   * Methods
   **********************************/

  function getScrollPos() {
    return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
  }

  //adapted from https://stackoverflow.com/questions/21474678/scrolltop-animation-without-jquery
  function scrollTo(to, duration, cb) {
    var cosParameter = (getScrollPos() - to) / 2,
      scrollCount = 0,
      oldTimestamp  = window.performance.now();

    function step(newTimestamp) {
      scrollCount += Math.PI / (duration / (newTimestamp - oldTimestamp));

      if (scrollCount >= Math.PI) {
        window.scrollTo(0, to); //ensures we actually end up at the right place, not slightly off it

        if ($.isFunction(cb)) {
          cb();
        }

        return;
      }

      window.scrollTo(0, Math.round(to + cosParameter + cosParameter * Math.cos(scrollCount)));
      oldTimestamp = newTimestamp;
      window.requestAnimationFrame(step);
    }
    window.requestAnimationFrame(step);
  }

  function gotoPage(page) {
    history.replaceState({page: page}, '', page === 'home' ? '/' : page);
    $(window).trigger('popstate');
  }

  function getSectionElement(sectionName) {
    return $sections.filter('[data-section="'+sectionName+'"]');
  }

  function activateNavItem() {
    var $item = $mainNav.find('.page-links a[href="/'+ currentSection +'"]');
    $mainNav.find('.page-links a').not($item).removeClass('active');
    $item.addClass('active');
  }

  function inViewport($el) {
    var elH = $el.height(),
      H   = window.innerHeight,
      r   = $el[0].getBoundingClientRect(), t=r.top, b=r.bottom;
    return Math.max(0, t>0? Math.min(elH, H-t) : (b<H?b:H));
  }

  function registerScrollInEvent($el, handler, offset) {
    scrollInElements.push({
      pos: 99999999,
      offset: offset ? offset : 0,
      $el: $el,
      handler: handler
    });
  }

  function afterPopScroll() {
    preventScrollSectionChange = false;
  }

  function ContactValidationException(message) {
    this.message = message;
  }

  function validateContactEmail() {
    var email = $contactEmail.val();

    if (email.length < 3) {
      throw new ContactValidationException();
    }
  }

  function validateContactMessage() {
    var message = $contactMessage.val();

    if (message.length < contactMessageMin) {
      throw new ContactValidationException('Your message is a tad too short! Please enter at least ' +
                                            contactMessageMin + ' characters.');
    }

    if (message.length > contactMessageMax) {
      throw new ContactValidationException('Your message is a bit too long! Please keep it under ' +
                                            contactMessageMin + ' characters.');
    }
  }

  function toggleErrorTip(mode, $el, text) {
    var $parent = $el.parent();

    if (mode === 'show') {
      $parent.removeClass('valid').addClass('error');
      $parent.find('.error-tip').text(text);
    } else if (mode === 'hide') {
      $parent.removeClass('error');
    }
  }

  /**********************************
   * Events
   **********************************/

  $(window).scroll(function() {
    var currentY = getScrollPos();
    //Only update on vertical scroll change
    if (prevY !== currentY) {
      var viewHeight = document.documentElement.clientHeight;

      if (!preventScrollSectionChange && !initialLoad) {
        //Change url if a new section has the majority of screen height
        $sections.each(function (i, el) {
          var $el = $(el);
          var section = $el.data('section');
          if (inViewport($el) > viewHeight / 2 && currentSection !== section) {
            preventScrollSectionAnim = true;
            gotoPage(section);
          }
        });
      }

      //Toggle a dark bg on nav
      if (currentY >= scrollPoints.navBg && !$mainNav.hasClass('has-bg')) {
        $mainNav.addClass('has-bg');
      } else if (currentY < scrollPoints.navBg && $mainNav.hasClass('has-bg')) {
        $mainNav.removeClass('has-bg');
      }

      //Show logo in nav if we scrolled passed the one in intro
      if (currentY >= scrollPoints.navLogo && !$mainNav.hasClass('has-logo')) {
        $navLogo.attr('aria-hidden', false);
        $mainNav.addClass('has-logo');
      } else if (currentY < scrollPoints.navLogo && $mainNav.hasClass('has-logo')) {
        $navLogo.attr('aria-hidden', true);
        $mainNav.removeClass('has-logo');
      }

      //Don't need to animate intro particles if we can't see them!
      if (introParticles) {
        var visible = currentY < scrollPoints.introParticles;
        if (!introParticles.active && visible) {
          particlesJS.resume(introParticles);
        } else if (introParticles.active && !visible) {
          particlesJS.pause(introParticles);
        }
      }

      //Same with the skills particles
      if (skillsParticles) {
        var visible = currentY + viewHeight > scrollPoints.skillsParticlesTop &&
                      currentY < scrollPoints.skillsParticlesBottom;
        if (!skillsParticles.active && visible) {
          particlesJS.resume(skillsParticles);
        } else if (skillsParticles.active && !visible) {
          particlesJS.pause(skillsParticles);
        }
      }

      //Handle elements that have scrolled into view
      var s = scrollInElements.length;
      while (s--) {
        var scrollIn = scrollInElements[s];
        if ((currentY + viewHeight) > scrollIn.pos) {
          scrollIn.handler(scrollIn.$el);
          scrollInElements.splice(scrollInElements.indexOf(scrollIn), 1);
        }
      }

      prevY = currentY;
    }
  });

  $(window).resize(function() {
    var currentW = document.documentElement.clientWidth;
    //Only update on width change
    if (prevW !== currentW) {
      console.log('Resize fired', $skillsParticles.offset().top);

      //Update scroll detection points
      scrollPoints.navBg = $introMiddle.offset().top;
      scrollPoints.navLogo = $introLogo.offset().top + $introLogo.height();
      scrollPoints.introParticles = $introParticles.offset().top + $introParticles.height();
      scrollPoints.skillsParticlesTop = $skillsParticles.offset().top;
      scrollPoints.skillsParticlesBottom = scrollPoints.skillsParticlesTop + $skillsParticles.height();

      $.each(scrollInElements, function(key, scrollIn) {
        scrollIn.pos = scrollIn.$el.offset().top + scrollIn.offset;
      });

      prevW = currentW;
    }
  });

  $('a[href^="/"]').not('[href*="."]').not('[target="_blank"]').click(function() {
    gotoPage($(this).attr('href').substring(1));
    return false;
  });

  $(window).on('popstate', function() {
    var page = history.state && history.state.page ? history.state.page : location.pathname.substring(1);
    previousSection = currentSection;
    currentSection = page !== '' ? page : 'home';

    var $target = getSectionElement(currentSection);
    activateNavItem();

    if (!preventScrollSectionAnim) {
      if ($target.length > 0) {
        preventScrollSectionChange = true;

        if (initialLoad) {
          window.scrollTop = $target.offset().top;
          afterPopScroll();

        } else {
          var scrollAnimComplete = false;
          scrollTo($target.offset().top, 250, function() {
            if (!scrollAnimComplete) {
              scrollAnimComplete = true;
              afterPopScroll();
            }
          });
        }
      }
    }

    preventScrollSectionAnim = false;
  });

  //Contact form stuff

  $contactEmail.on('blur keypress', function(e) {
    var $parent = $contactEmail.parent();
    if (e.type === 'keypress' && !$parent.hasClass('error')) {
      return true;
    }

    try {
      validateContactEmail();
      toggleErrorTip('hide', $contactEmail);
      $parent.addClass('valid');
    } catch (e) {
      if (e instanceof ContactValidationException) {
        toggleErrorTip('show', $contactEmail, 'Please enter a valid email address.');
      }
    }
  });

  $contactMessage.on('blur keypress', function(e) {
    var $parent = $contactMessage.parent();
    if (e.type === 'keypress' && !$parent.hasClass('error')) {
      return true;
    }

    try {
      validateContactMessage();
      toggleErrorTip('hide', $contactMessage);
      $parent.addClass('valid');
    } catch (e) {
      if (e instanceof ContactValidationException) {
        toggleErrorTip('show', $contactMessage, e.message);
      }
    }
  });

  $contactForm.find('.alert').on('click', '.close', function() {
    $(this).closest('.alert').removeClass('active');
    $contactForm.removeClass('has-alert');
  });

  $sectionContact.find('form').on('submit', function(e) {
    e.preventDefault();
    var errors = false;

    try {
      validateContactEmail();
      toggleErrorTip('hide', $contactEmail);
    } catch (e) {
      if (e instanceof ContactValidationException) {
        errors = true;
        toggleErrorTip('show', $contactEmail, 'Please enter a valid email address.');
      }
    }

    try {
      validateContactMessage();
      toggleErrorTip('hide', $contactMessage);
    } catch (e) {
      if (e instanceof ContactValidationException) {
        errors = true;
        toggleErrorTip('show', $contactMessage, e.message);
      }
    }

    if (errors || contactSending) {
      return false;
    }

    //submit form via ajax
    contactSending = true;
    $contactForm.find('button[type="submit"]').attr('disabled', 'disabled').addClass('loading');

    $.ajax({
      type: 'POST',
      url: $contactForm.attr('action'),
      dataType: 'json',
      data: {
        ajax: true,
        name: $contactForm.find('#contact-name').val(),
        email: $contactEmail.val(),
        message: $contactMessage.val(),
        url: $contactForm.find('#contact-url').val()
      },
      complete: function() {
        //Ensure the form always unlocks regardless of error state
        $contactForm.find('button[type="submit"]').removeAttr('disabled').removeClass('loading');
        $contactForm.children('fieldset').removeClass('valid error');
        contactSending = false;
      },
      success: function(res) {
        if (res.error) {
          //Something broke on the server welp
          $contactForm.addClass('has-alert');
          $contactForm.find('.send-error').addClass('active')
            .find('.error-code').text(res.code ? '(Err: ' + res.code + ')' : '');

        } else if (res.errors) {
          //User got validation errors (bypassed the html/js rules?)
          $.each(res.errors, function (type, message) {
            if (type === 'email') {
              toggleErrorTip('show', $contactEmail, message);
            } else if (type === 'message') {
              toggleErrorTip('show', $contactMessage, message);
            }
          });

        } else if (res.success) {
          //Message actually went through!
          $contactForm.get(0).reset();
          $contactForm.addClass('has-alert');
          $contactForm.find('.send-success').addClass('active');
        }
      },
      error: function() {
        //Something broke on the server welp
        $contactForm.addClass('has-alert');
        $contactForm.find('.send-error').addClass('active')
          .find('.error-code').text('(Err: 99)');
      }
    });
  });

  /**********************************
   * Scroll-in visibility effects
   **********************************/

  $sections.not($sectionIntro).find('header .title').each(function(i, el) {
    registerScrollInEvent($(el), function($el) {
      $el.addClass('active');
    }, 100);
  });

  $sectionWork.find('.showcase .item .logo').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    }, 100);
  });

  $sectionWork.find('.showcase .item .info').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    }, 100);
  });

  $sectionSkills.find('article > p, .resume').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    });
  });

  $sectionSkills.find('article > ul li').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    });
  });

  $sectionContact.find('article > p, form button[type="submit"]').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    });
  });

  $sectionContact.find('form > fieldset').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    }, ($(el).height() / 4) * -1);
  });

  $sectionContact.find('.social a').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    });
  });

  /**********************************
   * Init
   **********************************/

  var introParticles = particlesJS('intro-particles', window.introParticlesConf);
  var skillsParticles = particlesJS('skills-particles', window.skillsParticlesConf);

  //ghetto do-it-yourself ladda.js
  $('.dh-btn.loading-enabled').each(function(i, el) {
    var $el = $(el);
    var html = $el.html();
    $el.empty();
    $el.append('' + html + '')
      .append('');
  });

  $sectionIntro.addClass('active');

  $(window).trigger('resize');
  $(window).trigger('popstate');
  $(window).trigger('scroll');

  setTimeout(function() {
    prevW = null;
    prevY = null;
    $(window).trigger('resize');
    $(window).trigger('scroll');
  }, 500);

  initialLoad = false;

  window.dhDebug = function() {
    console.log(scrollPoints);
  };
});<commit_msg>Ok that wasn't it so here's another wild stab in the dark<commit_after>$(document).ready(function () {
  //Cached elements
  var $mainNav = $('#main-nav');
  var $navLogo = $mainNav.children('.logo');

  var $sections = $('.section');
  var $sectionIntro = $sections.filter('[data-section="home"]');
  var $sectionWork = $sections.filter('[data-section="work"]');
  var $sectionSkills = $sections.filter('[data-section="skills"]');
  var $sectionContact = $sections.filter('[data-section="contact"]');

  var $introParticles = $('#intro-particles');
  var $introMiddle = $sectionIntro.children('.middle');
  var $introLogo = $introMiddle.children('.logo');

  var $skillsParticles = $('#skills-particles');

  var $contactForm = $sectionContact.find('form');
  var $contactEmail = $contactForm.find('#contact-email');
  var $contactMessage = $contactForm.find('#contact-message');

  //Constants that aren't elements...
  var contactMessageMin = parseInt($contactMessage.attr('minlength'));
  var contactMessageMax = parseInt($contactMessage.attr('maxlength'));

  //State tracking
  var initialLoad = true;
  var prevY = null;
  var prevW = null;
  var scrollPoints = {};
  var scrollInElements = [];
  var previousSection = '';
  var currentSection = '';
  var preventScrollSectionChange = true;
  var preventScrollSectionAnim = false;
  var contactSending = false;

  /**********************************
   * Methods
   **********************************/

  function getScrollPos() {
    return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
  }

  //adapted from https://stackoverflow.com/questions/21474678/scrolltop-animation-without-jquery
  function scrollTo(to, duration, cb) {
    var cosParameter = (getScrollPos() - to) / 2,
      scrollCount = 0,
      oldTimestamp  = window.performance.now();

    function step(newTimestamp) {
      scrollCount += Math.PI / (duration / (newTimestamp - oldTimestamp));

      if (scrollCount >= Math.PI) {
        window.scrollTo(0, to); //ensures we actually end up at the right place, not slightly off it

        if ($.isFunction(cb)) {
          cb();
        }

        return;
      }

      window.scrollTo(0, Math.round(to + cosParameter + cosParameter * Math.cos(scrollCount)));
      oldTimestamp = newTimestamp;
      window.requestAnimationFrame(step);
    }
    window.requestAnimationFrame(step);
  }

  function gotoPage(page) {
    history.replaceState({page: page}, '', page === 'home' ? '/' : page);
    $(window).trigger('popstate');
  }

  function getSectionElement(sectionName) {
    return $sections.filter('[data-section="'+sectionName+'"]');
  }

  function activateNavItem() {
    var $item = $mainNav.find('.page-links a[href="/'+ currentSection +'"]');
    $mainNav.find('.page-links a').not($item).removeClass('active');
    $item.addClass('active');
  }

  function inViewport($el) {
    var elH = $el.height(),
      H   = window.innerHeight,
      r   = $el[0].getBoundingClientRect(), t=r.top, b=r.bottom;
    return Math.max(0, t>0? Math.min(elH, H-t) : (b<H?b:H));
  }

  function registerScrollInEvent($el, handler, offset) {
    scrollInElements.push({
      pos: 99999999,
      offset: offset ? offset : 0,
      $el: $el,
      handler: handler
    });
  }

  function afterPopScroll() {
    preventScrollSectionChange = false;
  }

  function ContactValidationException(message) {
    this.message = message;
  }

  function validateContactEmail() {
    var email = $contactEmail.val();

    if (email.length < 3) {
      throw new ContactValidationException();
    }
  }

  function validateContactMessage() {
    var message = $contactMessage.val();

    if (message.length < contactMessageMin) {
      throw new ContactValidationException('Your message is a tad too short! Please enter at least ' +
                                            contactMessageMin + ' characters.');
    }

    if (message.length > contactMessageMax) {
      throw new ContactValidationException('Your message is a bit too long! Please keep it under ' +
                                            contactMessageMin + ' characters.');
    }
  }

  function toggleErrorTip(mode, $el, text) {
    var $parent = $el.parent();

    if (mode === 'show') {
      $parent.removeClass('valid').addClass('error');
      $parent.find('.error-tip').text(text);
    } else if (mode === 'hide') {
      $parent.removeClass('error');
    }
  }

  /**********************************
   * Events
   **********************************/

  $(window).scroll(function() {
    var currentY = getScrollPos();
    //Only update on vertical scroll change
    if (prevY !== currentY) {
      var viewHeight = document.documentElement.clientHeight;

      if (!preventScrollSectionChange && !initialLoad) {
        //Change url if a new section has the majority of screen height
        $sections.each(function (i, el) {
          var $el = $(el);
          var section = $el.data('section');
          if (inViewport($el) > viewHeight / 2 && currentSection !== section) {
            preventScrollSectionAnim = true;
            gotoPage(section);
          }
        });
      }

      //Toggle a dark bg on nav
      if (currentY >= scrollPoints.navBg && !$mainNav.hasClass('has-bg')) {
        $mainNav.addClass('has-bg');
      } else if (currentY < scrollPoints.navBg && $mainNav.hasClass('has-bg')) {
        $mainNav.removeClass('has-bg');
      }

      //Show logo in nav if we scrolled passed the one in intro
      if (currentY >= scrollPoints.navLogo && !$mainNav.hasClass('has-logo')) {
        $navLogo.attr('aria-hidden', false);
        $mainNav.addClass('has-logo');
      } else if (currentY < scrollPoints.navLogo && $mainNav.hasClass('has-logo')) {
        $navLogo.attr('aria-hidden', true);
        $mainNav.removeClass('has-logo');
      }

      //Don't need to animate intro particles if we can't see them!
      if (introParticles) {
        var visible = currentY < scrollPoints.introParticles;
        if (!introParticles.active && visible) {
          particlesJS.resume(introParticles);
        } else if (introParticles.active && !visible) {
          particlesJS.pause(introParticles);
        }
      }

      //Same with the skills particles
      if (skillsParticles) {
        var visible = currentY + viewHeight > scrollPoints.skillsParticlesTop &&
                      currentY < scrollPoints.skillsParticlesBottom;
        if (!skillsParticles.active && visible) {
          particlesJS.resume(skillsParticles);
        } else if (skillsParticles.active && !visible) {
          particlesJS.pause(skillsParticles);
        }
      }

      //Handle elements that have scrolled into view
      var s = scrollInElements.length;
      while (s--) {
        var scrollIn = scrollInElements[s];
        if ((currentY + viewHeight) > scrollIn.pos) {
          scrollIn.handler(scrollIn.$el);
          scrollInElements.splice(scrollInElements.indexOf(scrollIn), 1);
        }
      }

      prevY = currentY;
    }
  });

  $(window).resize(function() {
    var currentW = document.documentElement.clientWidth;
    //Only update on width change
    if (prevW !== currentW) {
      console.log('Resize fired', $skillsParticles.offset().top);

      //Update scroll detection points
      scrollPoints.navBg = $introMiddle.offset().top;
      scrollPoints.navLogo = $introLogo.offset().top + $introLogo.height();
      scrollPoints.introParticles = $introParticles.offset().top + $introParticles.height();
      scrollPoints.skillsParticlesTop = $skillsParticles.offset().top;
      scrollPoints.skillsParticlesBottom = scrollPoints.skillsParticlesTop + $skillsParticles.height();

      $.each(scrollInElements, function(key, scrollIn) {
        scrollIn.pos = scrollIn.$el.offset().top + scrollIn.offset;
      });

      prevW = currentW;
    }
  });

  $('a[href^="/"]').not('[href*="."]').not('[target="_blank"]').click(function() {
    gotoPage($(this).attr('href').substring(1));
    return false;
  });

  $(window).on('popstate', function() {
    var page = history.state && history.state.page ? history.state.page : location.pathname.substring(1);
    previousSection = currentSection;
    currentSection = page !== '' ? page : 'home';

    var $target = getSectionElement(currentSection);
    activateNavItem();

    if (!preventScrollSectionAnim) {
      if ($target.length > 0) {
        preventScrollSectionChange = true;

        if (initialLoad) {
          window.scrollTop = $target.offset().top;
          afterPopScroll();

        } else {
          var scrollAnimComplete = false;
          scrollTo($target.offset().top, 250, function() {
            if (!scrollAnimComplete) {
              scrollAnimComplete = true;
              afterPopScroll();
            }
          });
        }
      }
    }

    preventScrollSectionAnim = false;
  });

  //Contact form stuff

  $contactEmail.on('blur keypress', function(e) {
    var $parent = $contactEmail.parent();
    if (e.type === 'keypress' && !$parent.hasClass('error')) {
      return true;
    }

    try {
      validateContactEmail();
      toggleErrorTip('hide', $contactEmail);
      $parent.addClass('valid');
    } catch (e) {
      if (e instanceof ContactValidationException) {
        toggleErrorTip('show', $contactEmail, 'Please enter a valid email address.');
      }
    }
  });

  $contactMessage.on('blur keypress', function(e) {
    var $parent = $contactMessage.parent();
    if (e.type === 'keypress' && !$parent.hasClass('error')) {
      return true;
    }

    try {
      validateContactMessage();
      toggleErrorTip('hide', $contactMessage);
      $parent.addClass('valid');
    } catch (e) {
      if (e instanceof ContactValidationException) {
        toggleErrorTip('show', $contactMessage, e.message);
      }
    }
  });

  $contactForm.find('.alert').on('click', '.close', function() {
    $(this).closest('.alert').removeClass('active');
    $contactForm.removeClass('has-alert');
  });

  $sectionContact.find('form').on('submit', function(e) {
    e.preventDefault();
    var errors = false;

    try {
      validateContactEmail();
      toggleErrorTip('hide', $contactEmail);
    } catch (e) {
      if (e instanceof ContactValidationException) {
        errors = true;
        toggleErrorTip('show', $contactEmail, 'Please enter a valid email address.');
      }
    }

    try {
      validateContactMessage();
      toggleErrorTip('hide', $contactMessage);
    } catch (e) {
      if (e instanceof ContactValidationException) {
        errors = true;
        toggleErrorTip('show', $contactMessage, e.message);
      }
    }

    if (errors || contactSending) {
      return false;
    }

    //submit form via ajax
    contactSending = true;
    $contactForm.find('button[type="submit"]').attr('disabled', 'disabled').addClass('loading');

    $.ajax({
      type: 'POST',
      url: $contactForm.attr('action'),
      dataType: 'json',
      data: {
        ajax: true,
        name: $contactForm.find('#contact-name').val(),
        email: $contactEmail.val(),
        message: $contactMessage.val(),
        url: $contactForm.find('#contact-url').val()
      },
      complete: function() {
        //Ensure the form always unlocks regardless of error state
        $contactForm.find('button[type="submit"]').removeAttr('disabled').removeClass('loading');
        $contactForm.children('fieldset').removeClass('valid error');
        contactSending = false;
      },
      success: function(res) {
        if (res.error) {
          //Something broke on the server welp
          $contactForm.addClass('has-alert');
          $contactForm.find('.send-error').addClass('active')
            .find('.error-code').text(res.code ? '(Err: ' + res.code + ')' : '');

        } else if (res.errors) {
          //User got validation errors (bypassed the html/js rules?)
          $.each(res.errors, function (type, message) {
            if (type === 'email') {
              toggleErrorTip('show', $contactEmail, message);
            } else if (type === 'message') {
              toggleErrorTip('show', $contactMessage, message);
            }
          });

        } else if (res.success) {
          //Message actually went through!
          $contactForm.get(0).reset();
          $contactForm.addClass('has-alert');
          $contactForm.find('.send-success').addClass('active');
        }
      },
      error: function() {
        //Something broke on the server welp
        $contactForm.addClass('has-alert');
        $contactForm.find('.send-error').addClass('active')
          .find('.error-code').text('(Err: 99)');
      }
    });
  });

  /**********************************
   * Scroll-in visibility effects
   **********************************/

  $sections.not($sectionIntro).find('header .title').each(function(i, el) {
    registerScrollInEvent($(el), function($el) {
      $el.addClass('active');
    }, 100);
  });

  $sectionWork.find('.showcase .item .logo').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    }, 100);
  });

  $sectionWork.find('.showcase .item .info').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    }, 100);
  });

  $sectionSkills.find('article > p, .resume').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    });
  });

  $sectionSkills.find('article > ul li').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    });
  });

  $sectionContact.find('article > p, form button[type="submit"]').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    });
  });

  $sectionContact.find('form > fieldset').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    }, ($(el).height() / 4) * -1);
  });

  $sectionContact.find('.social a').each(function(i, el) {
    registerScrollInEvent($(el), function ($el) {
      $el.addClass('active');
    });
  });

  /**********************************
   * Init
   **********************************/

  var introParticles = particlesJS('intro-particles', window.introParticlesConf);
  var skillsParticles = particlesJS('skills-particles', window.skillsParticlesConf);

  //ghetto do-it-yourself ladda.js
  $('.dh-btn.loading-enabled').each(function(i, el) {
    var $el = $(el);
    var html = $el.html();
    $el.empty();
    $el.append('' + html + '')
      .append('');
  });

  $sectionIntro.addClass('active');

  $(window).trigger('resize');
  $(window).trigger('popstate');
  $(window).trigger('scroll');

  initialLoad = false;

  window.dhDebug = function() {
    console.log(scrollPoints);
  };
});

$(window).load(function() {
    prevW = null;
    prevY = null;
    $(window).trigger('resize');
    $(window).trigger('scroll');
});<|endoftext|>


<commit_before>'use strict';

const CommandType = require('./CommandType');

/**
 * Interpreter.. you guessed it, interprets command input
 */
class CommandParser {
  /**
   * Parse a given string to find the resulting command/arguments
   * @param {GameState} state
   * @param {string} data
   * @param {Player} player
   * @return {{
   *   type: CommandType,
   *   command: Command,
   *   skill: Skill,
   *   channel: Channel,
   *   args: string,
   *   originalCommand: string
   * }}
   */
  static parse(state, data, player) {
    data = data.trim();

    const parts = data.split(' ');

    const command = parts.shift().replace(/[^a-z]/i, '').toLowerCase();
    if (!command.length) {
      throw new InvalidCommandError();
    }

    const args = parts.join(' ');

    // Kludge so that 'l' alone will always force a look,
    // instead of mixing it up with lock or list.
    // TODO: replace this a priority list
    if (command === 'l') {
      return {
        type: CommandType.COMMAND,
        command: state.CommandManager.get('look'),
        args: args
      };
    }

    // Same with 'i' and inventory.
    if (command === 'i') {
      return {
        type: CommandType.COMMAND,
        command: state.CommandManager.get('inventory'),
        args: args
      };
    }

    // check for direction shortcuts
    const directions = {
      'n':  'north',
      'e':  'east',
      's':  'south',
      'w':  'west',
      'u':  'up',
      'd':  'down',

      'ne': 'northeast',
      'se': 'southeast',
      'nw': 'northwest',
      'sw': 'southwest',
    };

    const moveCommand = state.Config.get("moveCommand");
    if (command in directions) {
      const direction = directions[command];
      return {
        type: CommandType.COMMAND,
        command: state.CommandManager.get(moveCommand),
        args: direction,
        // Send the equivalent of bash $0 so the command, when executed,
        // can tell which of its aliases was used
        originalCommand: direction
      };
    }

    // See if the command is an exit and the player can go that way
    if (player.canGo(command)) {
      return {
        type: CommandType.COMMAND,
        command: state.CommandManager.get(moveCommand),
        args: command,
        originalCommand: command
      };
    }

    // see if they matched exactly a command
    if (state.CommandManager.get(command)) {
      return {
        type: CommandType.COMMAND,
        command: state.CommandManager.get(command),
        args,
        originalCommand: command
      };
    }

    // see if they typed at least the beginning of a command and try to match
    let found = state.CommandManager.find(command, /* returnAlias: */ true);
    if (found) {
      return {
        type: CommandType.COMMAND,
        command: found.command,
        args,
        originalCommand: found.alias
      };
    }

    // finally check channels
    found = state.ChannelManager.find(command);
    if (found) {
      return {
        type: CommandType.CHANNEL,
        channel: found,
        args
      };
    }

    // finally check channels
    found = state.SkillManager.find(command);
    if (found) {
      return {
        type: CommandType.SKILL,
        skill: found,
        args
      };
    }

    throw new InvalidCommandError();
  }

  /**
   * Parse "get 2.foo bar"
   * @param {string}   search    2.foo
   * @param {Iterable} list      Where to look for the item
   * @param {boolean}  returnKey If `list` is a Map, true to return the KV tuple instead of just the entry
   * @return {*} Boolean on error otherwise an entry from the list
   */
  static parseDot(search, list, returnKey = false) {
    if (!list) {
      return null;
    }

    const parts = search.split('.');
    let findNth = 1;
    let keyword = null;
    if (parts.length > 2) {
      return null;
    }

    if (parts.length === 1) {
      keyword = parts[0];
    } else {
      findNth = parseInt(parts[0], 10);
      keyword = parts[1];
    }

    let encountered = 0;
    for (let entity of list) {
      let key, entry;
      if (list instanceof Map) {
        [key, entry] = entity;
      } else {
        entry = entity;
      }

      if (!('keywords' in entry) && !('name' in entry)) {
        throw new Error('Items in list have no keywords or name');
      }

      // prioritize keywords over item/player names
      if (entry.keywords && (entry.keywords.includes(keyword) || entry.uuid === keyword)) {
        encountered++;
        if (encountered === findNth) {
          return returnKey ? [key, entry] : entry;
        }
        // if the keyword matched skip to next loop so we don't double increment
        // the encountered counter
        continue;
      }

      if (entry.name && entry.name.toLowerCase().includes(keyword)) {
        encountered++;
        if (encountered === findNth) {
          return returnKey ? [key, entry] : entry;
        }
      }
    }

    return false;
  }
}
exports.CommandParser = CommandParser;

/**
 * Used when the player enters a bad command
 * @extends Error
 */
class InvalidCommandError extends Error {}
/**
 * Used when the player tries a command they don't have access to
 * @extends Error
 */
class RestrictedCommandError extends Error {}
exports.InvalidCommandError = InvalidCommandError;
exports.RestrictedCommandError = RestrictedCommandError;
<commit_msg>Removing [a-z] filter from command (#262)<commit_after>'use strict';

const CommandType = require('./CommandType');

/**
 * Interpreter.. you guessed it, interprets command input
 */
class CommandParser {
  /**
   * Parse a given string to find the resulting command/arguments
   * @param {GameState} state
   * @param {string} data
   * @param {Player} player
   * @return {{
   *   type: CommandType,
   *   command: Command,
   *   skill: Skill,
   *   channel: Channel,
   *   args: string,
   *   originalCommand: string
   * }}
   */
  static parse(state, data, player) {
    data = data.trim();

    const parts = data.split(' ');

    const command = parts.shift().toLowerCase();
    if (!command.length) {
      throw new InvalidCommandError();
    }

    const args = parts.join(' ');

    // Kludge so that 'l' alone will always force a look,
    // instead of mixing it up with lock or list.
    // TODO: replace this a priority list
    if (command === 'l') {
      return {
        type: CommandType.COMMAND,
        command: state.CommandManager.get('look'),
        args: args
      };
    }

    // Same with 'i' and inventory.
    if (command === 'i') {
      return {
        type: CommandType.COMMAND,
        command: state.CommandManager.get('inventory'),
        args: args
      };
    }

    // check for direction shortcuts
    const directions = {
      'n':  'north',
      'e':  'east',
      's':  'south',
      'w':  'west',
      'u':  'up',
      'd':  'down',

      'ne': 'northeast',
      'se': 'southeast',
      'nw': 'northwest',
      'sw': 'southwest',
    };

    const moveCommand = state.Config.get("moveCommand");
    if (command in directions) {
      const direction = directions[command];
      return {
        type: CommandType.COMMAND,
        command: state.CommandManager.get(moveCommand),
        args: direction,
        // Send the equivalent of bash $0 so the command, when executed,
        // can tell which of its aliases was used
        originalCommand: direction
      };
    }

    // See if the command is an exit and the player can go that way
    if (player.canGo(command)) {
      return {
        type: CommandType.COMMAND,
        command: state.CommandManager.get(moveCommand),
        args: command,
        originalCommand: command
      };
    }

    // see if they matched exactly a command
    if (state.CommandManager.get(command)) {
      return {
        type: CommandType.COMMAND,
        command: state.CommandManager.get(command),
        args,
        originalCommand: command
      };
    }

    // see if they typed at least the beginning of a command and try to match
    let found = state.CommandManager.find(command, /* returnAlias: */ true);
    if (found) {
      return {
        type: CommandType.COMMAND,
        command: found.command,
        args,
        originalCommand: found.alias
      };
    }

    // finally check channels
    found = state.ChannelManager.find(command);
    if (found) {
      return {
        type: CommandType.CHANNEL,
        channel: found,
        args
      };
    }

    // finally check channels
    found = state.SkillManager.find(command);
    if (found) {
      return {
        type: CommandType.SKILL,
        skill: found,
        args
      };
    }

    throw new InvalidCommandError();
  }

  /**
   * Parse "get 2.foo bar"
   * @param {string}   search    2.foo
   * @param {Iterable} list      Where to look for the item
   * @param {boolean}  returnKey If `list` is a Map, true to return the KV tuple instead of just the entry
   * @return {*} Boolean on error otherwise an entry from the list
   */
  static parseDot(search, list, returnKey = false) {
    if (!list) {
      return null;
    }

    const parts = search.split('.');
    let findNth = 1;
    let keyword = null;
    if (parts.length > 2) {
      return null;
    }

    if (parts.length === 1) {
      keyword = parts[0];
    } else {
      findNth = parseInt(parts[0], 10);
      keyword = parts[1];
    }

    let encountered = 0;
    for (let entity of list) {
      let key, entry;
      if (list instanceof Map) {
        [key, entry] = entity;
      } else {
        entry = entity;
      }

      if (!('keywords' in entry) && !('name' in entry)) {
        throw new Error('Items in list have no keywords or name');
      }

      // prioritize keywords over item/player names
      if (entry.keywords && (entry.keywords.includes(keyword) || entry.uuid === keyword)) {
        encountered++;
        if (encountered === findNth) {
          return returnKey ? [key, entry] : entry;
        }
        // if the keyword matched skip to next loop so we don't double increment
        // the encountered counter
        continue;
      }

      if (entry.name && entry.name.toLowerCase().includes(keyword)) {
        encountered++;
        if (encountered === findNth) {
          return returnKey ? [key, entry] : entry;
        }
      }
    }

    return false;
  }
}
exports.CommandParser = CommandParser;

/**
 * Used when the player enters a bad command
 * @extends Error
 */
class InvalidCommandError extends Error {}
/**
 * Used when the player tries a command they don't have access to
 * @extends Error
 */
class RestrictedCommandError extends Error {}
exports.InvalidCommandError = InvalidCommandError;
exports.RestrictedCommandError = RestrictedCommandError;
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////////
// make converts objects which can be meaningfully interpreted as
// Exp values to the appropriate Exp values, giving us some poor-man
// static polymorphism
Shade.make = function(exp)
{
    var t = typeOf(exp);
    if (t === 'boolean' ||
        t === 'number') {
        return Shade.constant(exp);
    } else if (t === 'array') {
        return Shade.seq(exp);
    }
    t = constant_type(exp);
    if (t === 'vector' || t === 'matrix') {
        return Shade.constant(exp);
    } else if (exp._shade_type === 'attribute_buffer') {
        return Shade.attribute_from_buffer(exp);
    } else if (exp._shade_type === 'render_buffer') {
        return Shade.sampler2D_from_texture(exp.texture);
    } else if (exp._shade_type === 'texture') {
        return Shade.sampler2D_from_texture(exp);
    }
    return exp;
};

<commit_msg>Shade.make disallows undefined<commit_after>//////////////////////////////////////////////////////////////////////////////
// make converts objects which can be meaningfully interpreted as
// Exp values to the appropriate Exp values, giving us some poor-man
// static polymorphism
Shade.make = function(exp)
{
    var t = typeOf(exp);
    if (t === 'undefined') {
        throw "Shade.make does not support undefined";
    }
    if (t === 'boolean' || t === 'number') {
        return Shade.constant(exp);
    } else if (t === 'array') {
        return Shade.seq(exp);
    }
    t = constant_type(exp);
    if (t === 'vector' || t === 'matrix') {
        return Shade.constant(exp);
    } else if (exp._shade_type === 'attribute_buffer') {
        return Shade.attribute_from_buffer(exp);
    } else if (exp._shade_type === 'render_buffer') {
        return Shade.sampler2D_from_texture(exp.texture);
    } else if (exp._shade_type === 'texture') {
        return Shade.sampler2D_from_texture(exp);
    }
    return exp;
};

<|endoftext|>


<commit_before>Config = {}
Config.loadScripts = function(){
  var labellingFilePath = '/config/locales/default.js'

  $(document.body).append("<script src='" + labellingFilePath + "'></script>")
  $(document.body).append("<script src='/js/app/app.js\'></script>")
}<commit_msg>Removes escapted quote in js configDefault.<commit_after>Config = {}
Config.loadScripts = function(){
  var labellingFilePath = '/config/locales/default.js'

  $(document.body).append("<script src='" + labellingFilePath + "'></script>")
  $(document.body).append("<script src='/js/app/app.js'></script>")
}
<|endoftext|>


<commit_before>var strftime = require('strftime');

/*::
import * as moment from "moment"

declare module "moment" {
  interface Moment {
    _d: Date
    origValueString: string
  }
}
declare global {
  interface ViewHelpers {
    TIMESTAMPTZ_OID: number
    TIMESTAMP_OID: number
    formatCellFromSelect: (value: any, field: any) => string
    formatCell: (value: any, format: string, dataType: string) => string
    relatedRowsIcon: (rel: any, columnName: string, value: string) => string
    truncate: (str: string, length: number) => string
    tag_options: (options: any) => string
    link_to: (text: string, url: string, options: any) => string
    icon: (name: string, title: string) => string
    column_type_label: (column: any, short: string) => string
    betterDateTime: (date: moment.Moment) => string
    betterDateTimeZ: (date: moment.Moment) => string
    editDateFormat: (value: any, format: string) => string
    betterDate: (value: Date) => string
    timeFormat: (date: string) => string
    execTime: (time: number) => string
    formatJson: (value: any) => string
    formatArray: (value: any, format: string) => string
    getIndexType: (indexSql: string) => string
    escapeHTML: (unsafe: string) => string
    shorterTypeName: (typeName: string) => string
    relType: (type: string) => string
    tableGrantsDesc: (permissions: string) => string
    formatBytes: (size: number) => string
  }
}
*/

var GRANTS_ABBR = {
  r: 'SELECT',
  w: 'UPDATE',
  a: 'INSERT',
  d: 'DELETE',
  D: 'TRUNCATE',
  R: 'RULE',
  x: 'REFERENCES',
  t: 'TRIGGER',
  X: 'EXECUTE',
  U: 'USAGE',
  C: 'CREATE',
  c: 'CONNECT',
  T: 'TEMPORARY'
};

var helpers = global.ViewHelpers = {

  TIMESTAMPTZ_OID: 1184,
  TIMESTAMP_OID: 1114,

  formatCellFromSelect(value, field) {
    var format = field.udt_name;
    if (field.dataTypeID == this.TIMESTAMP_OID) {
      format = 'timestamp';
    }
    if (field.dataTypeID == this.TIMESTAMPTZ_OID) {
      format = 'timestamptz';
    }

    return this.formatCell(value, format, field.data_type);
  },

  formatCell: function (value, format, dataType) {
    if (value === null) {
      return 'NULL';
    }
    if (typeof value == 'string') {
      value = this.escapeHTML(value);
    }

    var formated = value;
    if (!formated) return formated;

    switch (format) {
      case 'hstore': case 'text':
        formated = '' + value + '';
        break;
      case 'xml':
        formated = '' + value + '';
        break;
      case 'varchar':
        if (typeof value == 'string' && value.length > 20) {
          formated = '' + value + '';
        }
        break;
      case 'timestamp':
        formated = this.betterDateTime(value);
        break;
      case 'timestamptz':
        formated = this.betterDateTimeZ(value);
        break;
      case 'date':
        formated = this.betterDate(value);
        break;
      case 'jsonb': case 'json':
        formated = this.formatJson(value);
        break;
      case 'bytea':
        var str = value.toString('ascii', 0, 100);
        formated = str;
        //console.log(value);
        //formated = value.length > 100 ? value.substr(0, 100) : value;
        break;
      case 'interval':
        formated = `${value.toPostgres()}`
        break;
    }

    if (dataType == 'ARRAY' && Array.isArray(value)) {
      formated = this.formatArray(value, format);
    }

    return formated;
  },

  relatedRowsIcon(rel, columnName, value) {
    if (rel) {
      var escapedValue = typeof value == 'string' ? value.replace(/('|")/g, "\\$1") : value;
      var execAttr = `viewForeign('${rel.foreign_table_schema}', '${rel.foreign_table_name}', '${rel.foreign_column_name}', '${escapedValue}')`;
      return `<a class="foreign" exec="${execAttr}"></span>`;
    }
  },

  truncate: function(str, length) {
    if (typeof str != 'string') str = '' + str;
    if (!length) length = 100;
    if (str.length > length) {
      return str.substr(0, length - 3) + '...';
    } else {
      return str;
    }
  },

  tag_options: function (options) {
    var attrs = [];
    Object.keys(options).forEach((key) => {
      attrs.push(key + '="' + options[key] + '"');
    });
    return attrs.join(" ");
  },

  link_to: function (text, url, options) {
    options = options || {}
    options.href = url;
    return "' + text + '';
  },

  icon: function(name, title, height = 20, width = 20) {
    title = title === undefined ? name.replace(/[-_]/g, ' ') : title;
    return `<img src="./public/icons/${name}.png" width="${width}" height="${height}" class="app-icon" title="${title}"/>`;
  },

  column_type_label: function (column, short) {
    // still not sure why it happens
    if (!column) {
      return 'varchar';
    }

    var baseName = column.udt_name || column.data_type;
    if (column.data_type == 'ARRAY') {
      baseName = baseName.replace(/^_/, '') + "[]";
    }
    return short ? this.shorterTypeName(baseName) : baseName;
  },

  betterDateTime: function (date) {
    var dateSec = date._d.getTime() / 1000.0;
    var nowSec = Math.round(Date.now() / 1000);
    var todayStart = nowSec - nowSec % 86400;
    var todayEnd = todayStart + 86400;
    var currentYear = new Date().getFullYear();

    var attrs = date.origValueString ? ` title="${date.origValueString}"` : '';
    var formatted = null;

    if (dateSec > todayStart && dateSec < todayEnd) {
      formatted = "Today, " + date.format("HH:mm:ss");
    } else if (currentYear == date._d.getFullYear()) {
      formatted = date.format("MMM DD HH:mm:ss");
    } else {
      formatted = date.format("MMM DD YYYY HH:mm:ss");
    }

    return `<time${attrs}>${formatted}</time>`;
  },

  betterDateTimeZ: function (date) {
    // date._d - is a date with substracted timezone offset
    var dateSec = date._d.getTime() / 1000.0;
    var nowSec = Math.round(Date.now() / 1000);
    var todayStart = nowSec - nowSec % 86400;
    var todayEnd = todayStart + 86400;
    var currentYear = new Date().getFullYear();

    var attrs = date.origValueString ? ` title="${date.origValueString}"` : '';
    var formatted = null;

    if (dateSec > todayStart && dateSec < todayEnd) {
      formatted = "Today, " + date.format("HH:mm:ss Z").replace(/:00$/, '');
    } else if (currentYear == date._d.getFullYear()) {
      formatted = date.format("MMM DD HH:mm:ss Z").replace(/:00$/, '');
    } else {
      formatted = date.format("MMM DD YYYY HH:mm:ss Z").replace(/:00$/, '');
    }

    return `<time${attrs}>${formatted}</time>`;
  },

  editDateFormat: function (value, format) {
    if (value.origValueString) {
      return value.origValueString;
    } else {
      if (format == "date") {
        return strftime('%F', value);
      } else if (format == "timetz") {
        return value;
      } else {
        return strftime('%F %T.%L', value);
      }
    }
  },

  // 1999-01-08
  betterDate: function (date) {
    //var date = new Date(Date.parse(value));
    return '<time>' + strftime('%Y-%m-%d', date) + '</time>';
  },

  timeFormat: function (date) {
    return strftime('%H:%M:%S', date);
  },

  execTime: function (time) {
    if (time >= 1000) {
      return "" + (time / 1000) + " sec";
    } else {
      return "" + time + " ms";
    }
  },

  formatJson: function (value) {
    var json;
    var wrongJson = false;
    if (typeof value == 'string') {
      //json = value;
      if (value.startsWith('{') && value.endsWith('}') || value.startsWith('[') && value.endsWith(']')) {
        try {
          value = JSON.parse(value);
          wrongJson = true;
        } catch (e) {}
      }
    }
    json = JSON.stringify(value, null, 4);
    json = this.escapeHTML(json);
    return `<span class="text ${wrongJson ? 'wrong-json' : ''}" ${wrongJson ? 'title="JSON value saved as string"' : ''}>${json}</span>`;
  },

  formatArray: function (value, format) {
    var fomrmatted = value.map((element) => {
      if (Array.isArray(element)) {
        return this.formatArray(element, format);
      } else {
        return this.formatCell(element, format);
      }
    });

    return '{' + fomrmatted.join(',') + '}';
  },

  getIndexType: function (indexSql) {
    var regM = indexSql.match(/USING ([^\s]+)\s/i);
    return regM ? regM[1] : undefined;
  },

  escapeHTML: function(unsafe) {
    if (unsafe.match(/[<>]/)) {
      var result = unsafe
        .replace(/&/g, "&")
        .replace(/</g, "<")
        .replace(/>/g, ">")
        .replace(/"/g, """)
        .replace(/'/g, "'");
      //console.log('unsafe', unsafe, result);
      return result;
    } else {
      return unsafe;
    }
  },

  shorterTypeName(typeName) {
    if (typeName == undefined) typeName = '';
    return typeName
      .replace(/character varying/, 'varchar')
      .replace(/timestamp without time zone/, 'timestamp')
      .replace(/timestamp with time zone/, 'timestampz')
      .replace(/time without time zone/, 'time')
      .replace(/time with time zone/, 'timez');
  },

  relType(type) {
    // r = ordinary table, i = index, S = sequence, t = TOAST table, v = view, m = materialized view, c = composite type, f = foreign table, p = partitioned table
    if (type == 'r') return 'table';
    if (type == 'i') return 'index';
    if (type == 'S') return 'sequence';
    if (type == 't') return 'TOAST table';
    if (type == 'v') return 'view';
    if (type == 'm') return 'mat. view';
    if (type == 'c') return 'composite type';
    if (type == 'f') return 'foreign table';
    if (type == 'p') return 'partitioned table';
    return type;
  },

  tableGrantsDesc(permissions) {
    var allPerms = 'arwdDxt';
    if (permissions == 'arwdDxt') {
      return "Full Access";
    } else if (permissions.length > 4) {
      var actions = [];
      allPerms.split('').forEach(letter => {
        if (!permissions.includes(letter)) {
          actions.push(GRANTS_ABBR[letter] || letter);
        }
      });
      return `All except ${actions.join(", ")}`;
    } else {
      var actions = [];
      permissions.split('').forEach(letter => {
        actions.push(GRANTS_ABBR[letter] || letter);
      });
      return actions.join(", ");
    }
  },

  formatBytes(bytes) {
    if (bytes < 1024) {
      return bytes + " Bytes";
    } else if (bytes < 1048576) {
      return (bytes / 1024).toFixed(3).replace(/\.?0+$/, '') + " KB";
    } else if (bytes < 1073741824) {
      return (bytes / 1048576).toFixed(3).replace(/\.?0+$/, '') + " MB";
    } else {
      return (bytes / 1073741824).toFixed(3).replace(/\.?0+$/, '') + " GB";
    }
  }
};

module.exports = helpers;<commit_msg>Cut long strings to 500 chars when shown in table<commit_after>var strftime = require('strftime');

/*::
import * as moment from "moment"

declare module "moment" {
  interface Moment {
    _d: Date
    origValueString: string
  }
}
declare global {
  interface ViewHelpers {
    TIMESTAMPTZ_OID: number
    TIMESTAMP_OID: number
    formatCellFromSelect: (value: any, field: any) => string
    formatCell: (value: any, format: string, dataType: string) => string
    relatedRowsIcon: (rel: any, columnName: string, value: string) => string
    truncate: (str: string, length: number) => string
    tag_options: (options: any) => string
    link_to: (text: string, url: string, options: any) => string
    icon: (name: string, title: string) => string
    column_type_label: (column: any, short: string) => string
    betterDateTime: (date: moment.Moment) => string
    betterDateTimeZ: (date: moment.Moment) => string
    editDateFormat: (value: any, format: string) => string
    betterDate: (value: Date) => string
    timeFormat: (date: string) => string
    execTime: (time: number) => string
    formatJson: (value: any) => string
    formatArray: (value: any, format: string) => string
    getIndexType: (indexSql: string) => string
    escapeHTML: (unsafe: string) => string
    shorterTypeName: (typeName: string) => string
    relType: (type: string) => string
    tableGrantsDesc: (permissions: string) => string
    formatBytes: (size: number) => string
  }
}
*/

var GRANTS_ABBR = {
  r: 'SELECT',
  w: 'UPDATE',
  a: 'INSERT',
  d: 'DELETE',
  D: 'TRUNCATE',
  R: 'RULE',
  x: 'REFERENCES',
  t: 'TRIGGER',
  X: 'EXECUTE',
  U: 'USAGE',
  C: 'CREATE',
  c: 'CONNECT',
  T: 'TEMPORARY'
};

var TRUNCATE_LONG_TEXT = 500;

var helpers = global.ViewHelpers = {

  TIMESTAMPTZ_OID: 1184,
  TIMESTAMP_OID: 1114,

  formatCellFromSelect(value, field) {
    var format = field.udt_name || field.format;
    if (field.dataTypeID == this.TIMESTAMP_OID) {
      format = 'timestamp';
    }
    if (field.dataTypeID == this.TIMESTAMPTZ_OID) {
      format = 'timestamptz';
    }

    return this.formatCell(value, format, field.data_type);
  },

  formatCell: function (value, format, dataType) {
    if (value === null) {
      return 'NULL';
    }
    if (typeof value == 'string') {
      //value = this.escapeHTML(value);
    }

    var formated = value;
    if (!formated) return formated;

    switch (format) {
      case 'hstore': case 'text': case 'tsvector':
        var shorterValue = value.length > TRUNCATE_LONG_TEXT ? value.slice(0, TRUNCATE_LONG_TEXT) + '...' : value;
        formated = '' + shorterValue + '';
        break;
      case 'xml':
        formated = '' + value + '';
        break;
      case 'varchar':
        if (typeof value == 'string' && value.length > 20) {
          var shorterValue = value.length > TRUNCATE_LONG_TEXT ? value.slice(0, TRUNCATE_LONG_TEXT) + '...' : value;
          formated = '' + shorterValue + '';
        }
        break;
      case 'timestamp':
        formated = this.betterDateTime(value);
        break;
      case 'timestamptz':
        formated = this.betterDateTimeZ(value);
        break;
      case 'date':
        formated = this.betterDate(value);
        break;
      case 'jsonb': case 'json':
        formated = this.formatJson(value);
        break;
      case 'bytea':
        var str = value.toString('ascii', 0, 100);
        formated = str;
        //console.log(value);
        //formated = value.length > 100 ? value.substr(0, 100) : value;
        break;
      case 'interval':
        formated = `${value.toPostgres()}`
        break;
    }

    if (dataType == 'ARRAY' && Array.isArray(value)) {
      formated = this.formatArray(value, format);
    }

    return formated;
  },

  relatedRowsIcon(rel, columnName, value) {
    if (rel) {
      var escapedValue = typeof value == 'string' ? value.replace(/('|")/g, "\\$1") : value;
      var execAttr = `viewForeign('${rel.foreign_table_schema}', '${rel.foreign_table_name}', '${rel.foreign_column_name}', '${escapedValue}')`;
      return `<a class="foreign" exec="${execAttr}"></span>`;
    }
  },

  truncate: function(str, length) {
    if (typeof str != 'string') str = '' + str;
    if (!length) length = 100;
    if (str.length > length) {
      return str.substr(0, length - 3) + '...';
    } else {
      return str;
    }
  },

  tag_options: function (options) {
    var attrs = [];
    Object.keys(options).forEach((key) => {
      attrs.push(key + '="' + options[key] + '"');
    });
    return attrs.join(" ");
  },

  link_to: function (text, url, options) {
    options = options || {}
    options.href = url;
    return "' + text + '';
  },

  icon: function(name, title, height = 20, width = 20) {
    title = title === undefined ? name.replace(/[-_]/g, ' ') : title;
    return `<img src="./public/icons/${name}.png" width="${width}" height="${height}" class="app-icon" title="${title}"/>`;
  },

  column_type_label: function (column, short) {
    // still not sure why it happens
    if (!column) {
      return 'varchar';
    }

    var baseName = column.udt_name || column.data_type;
    if (column.data_type == 'ARRAY') {
      baseName = baseName.replace(/^_/, '') + "[]";
    }
    return short ? this.shorterTypeName(baseName) : baseName;
  },

  betterDateTime: function (date) {
    var dateSec = date._d.getTime() / 1000.0;
    var nowSec = Math.round(Date.now() / 1000);
    var todayStart = nowSec - nowSec % 86400;
    var todayEnd = todayStart + 86400;
    var currentYear = new Date().getFullYear();

    var attrs = date.origValueString ? ` title="${date.origValueString}"` : '';
    var formatted = null;

    if (dateSec > todayStart && dateSec < todayEnd) {
      formatted = "Today, " + date.format("HH:mm:ss");
    } else if (currentYear == date._d.getFullYear()) {
      formatted = date.format("MMM DD HH:mm:ss");
    } else {
      formatted = date.format("MMM DD YYYY HH:mm:ss");
    }

    return `<time${attrs}>${formatted}</time>`;
  },

  betterDateTimeZ: function (date) {
    // date._d - is a date with substracted timezone offset
    var dateSec = date._d.getTime() / 1000.0;
    var nowSec = Math.round(Date.now() / 1000);
    var todayStart = nowSec - nowSec % 86400;
    var todayEnd = todayStart + 86400;
    var currentYear = new Date().getFullYear();

    var attrs = date.origValueString ? ` title="${date.origValueString}"` : '';
    var formatted = null;

    if (dateSec > todayStart && dateSec < todayEnd) {
      formatted = "Today, " + date.format("HH:mm:ss Z").replace(/:00$/, '');
    } else if (currentYear == date._d.getFullYear()) {
      formatted = date.format("MMM DD HH:mm:ss Z").replace(/:00$/, '');
    } else {
      formatted = date.format("MMM DD YYYY HH:mm:ss Z").replace(/:00$/, '');
    }

    return `<time${attrs}>${formatted}</time>`;
  },

  editDateFormat: function (value, format) {
    if (value.origValueString) {
      return value.origValueString;
    } else {
      if (format == "date") {
        return strftime('%F', value);
      } else if (format == "timetz") {
        return value;
      } else {
        return strftime('%F %T.%L', value);
      }
    }
  },

  // 1999-01-08
  betterDate: function (date) {
    //var date = new Date(Date.parse(value));
    return '<time>' + strftime('%Y-%m-%d', date) + '</time>';
  },

  timeFormat: function (date) {
    return strftime('%H:%M:%S', date);
  },

  execTime: function (time) {
    if (time >= 1000) {
      return "" + (time / 1000) + " sec";
    } else {
      return "" + time + " ms";
    }
  },

  formatJson: function (value) {
    var json;
    var wrongJson = false;
    if (typeof value == 'string') {
      //json = value;
      if (value.startsWith('{') && value.endsWith('}') || value.startsWith('[') && value.endsWith(']')) {
        try {
          value = JSON.parse(value);
          wrongJson = true;
        } catch (e) {}
      }
    }
    json = JSON.stringify(value, null, 4);
    json = this.escapeHTML(json);
    return `<span class="text ${wrongJson ? 'wrong-json' : ''}" ${wrongJson ? 'title="JSON value saved as string"' : ''}>${json}</span>`;
  },

  formatArray: function (value, format) {
    var fomrmatted = value.map((element) => {
      if (Array.isArray(element)) {
        return this.formatArray(element, format);
      } else {
        return this.formatCell(element, format);
      }
    });

    return '{' + fomrmatted.join(',') + '}';
  },

  getIndexType: function (indexSql) {
    var regM = indexSql.match(/USING ([^\s]+)\s/i);
    return regM ? regM[1] : undefined;
  },

  escapeHTML: function(unsafe) {
    if (unsafe.match(/[<>]/)) {
      var result = unsafe
        .replace(/&/g, "&")
        .replace(/</g, "<")
        .replace(/>/g, ">")
        .replace(/"/g, """)
        .replace(/'/g, "'");
      //console.log('unsafe', unsafe, result);
      return result;
    } else {
      return unsafe;
    }
  },

  shorterTypeName(typeName) {
    if (typeName == undefined) typeName = '';
    return typeName
      .replace(/character varying/, 'varchar')
      .replace(/timestamp without time zone/, 'timestamp')
      .replace(/timestamp with time zone/, 'timestampz')
      .replace(/time without time zone/, 'time')
      .replace(/time with time zone/, 'timez');
  },

  relType(type) {
    // r = ordinary table, i = index, S = sequence, t = TOAST table, v = view, m = materialized view, c = composite type, f = foreign table, p = partitioned table
    if (type == 'r') return 'table';
    if (type == 'i') return 'index';
    if (type == 'S') return 'sequence';
    if (type == 't') return 'TOAST table';
    if (type == 'v') return 'view';
    if (type == 'm') return 'mat. view';
    if (type == 'c') return 'composite type';
    if (type == 'f') return 'foreign table';
    if (type == 'p') return 'partitioned table';
    return type;
  },

  tableGrantsDesc(permissions) {
    var allPerms = 'arwdDxt';
    if (permissions == 'arwdDxt') {
      return "Full Access";
    } else if (permissions.length > 4) {
      var actions = [];
      allPerms.split('').forEach(letter => {
        if (!permissions.includes(letter)) {
          actions.push(GRANTS_ABBR[letter] || letter);
        }
      });
      return `All except ${actions.join(", ")}`;
    } else {
      var actions = [];
      permissions.split('').forEach(letter => {
        actions.push(GRANTS_ABBR[letter] || letter);
      });
      return actions.join(", ");
    }
  },

  formatBytes(bytes) {
    if (bytes < 1024) {
      return bytes + " Bytes";
    } else if (bytes < 1048576) {
      return (bytes / 1024).toFixed(3).replace(/\.?0+$/, '') + " KB";
    } else if (bytes < 1073741824) {
      return (bytes / 1048576).toFixed(3).replace(/\.?0+$/, '') + " MB";
    } else {
      return (bytes / 1073741824).toFixed(3).replace(/\.?0+$/, '') + " GB";
    }
  }
};

module.exports = helpers;<|endoftext|>


<commit_before>'use strict';
/*var app = */
(function() {
    var elements = {
	result: document.getElementById('result'),
        totalram:  document.getElementById('totalram'),
        cores: document.getElementById('cores'),
        architecture: document.getElementById('architecture'),
        otherram: document.getElementById('otherram')
    };
    var values = {
        totalram: elements.totalram.value,
        cores: elements.cores.value,
        architecture: elements.architecture.value,
        otherram: elements.otherram.value
    };
    function numberWithCommas(x) {
        //http://stackoverflow.com/a/2901298/632117
        return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    }
    var calculateMemory = function() {
        //everything in MB
        var oneGb = 1000; //MB

        // "hard coded" options
        var architecture = values.architecture;
        var totalram = parseInt(values.totalram) * oneGb;
        var corenumber = parseInt(values.cores);
        var otherApps = parseInt(values.otherram);

        //thread stack sizes in MB
        var threadStackSizes = { 'x86': 1, 'x64': 2, 'IA64': 4 };

        var threadStackSize = threadStackSizes[architecture];

        var osReservedPart = totalram < (20 * oneGb) ? 0.2 : 0.125;
        var forOS = totalram * osReservedPart;
        var coretemp = corenumber > 4 ? 0 : corenumber;
        var sqlThreads = 256 + (coretemp - 4) * 8;
        var temp = corenumber / 4;

        var maxMemory = (totalram - forOS - otherApps - (sqlThreads * threadStackSize) - (1024 * Math.ceil(temp)));
        return Math.ceil(maxMemory);
    };
    var handleChangeEvent = function(e) {
        var id = e.target.id;
        values[id] = elements[id].value;
        elements.result.innerText = numberWithCommas(calculateMemory());
    };
    elements.architecture.addEventListener('change', handleChangeEvent);
    elements.totalram.addEventListener('change', handleChangeEvent);
    elements.cores.addEventListener('change', handleChangeEvent);
    elements.otherram.addEventListener('change', handleChangeEvent);

    elements.result.innerText = numberWithCommas(calculateMemory());
})();
/*

 */
<commit_msg>corrected indentation according to editorconfig<commit_after>'use strict';
/*var app = */
(function() {
  var elements = {
    result: document.getElementById('result'),
    totalram:  document.getElementById('totalram'),
    cores: document.getElementById('cores'),
    architecture: document.getElementById('architecture'),
    otherram: document.getElementById('otherram')
  };
  var values = {
    totalram: elements.totalram.value,
    cores: elements.cores.value,
    architecture: elements.architecture.value,
    otherram: elements.otherram.value
  };
  function numberWithCommas(x) {
    //http://stackoverflow.com/a/2901298/632117
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  }
  var calculateMemory = function() {
    //everything in MB
    var oneGb = 1000; //MB

    // "hard coded" options
    var architecture = values.architecture;
    var totalram = parseInt(values.totalram) * oneGb;
    var corenumber = parseInt(values.cores);
    var otherApps = parseInt(values.otherram);

    //thread stack sizes in MB
    var threadStackSizes = { 'x86': 1, 'x64': 2, 'IA64': 4 };

    var threadStackSize = threadStackSizes[architecture];

    var osReservedPart = totalram < (20 * oneGb) ? 0.2 : 0.125;
    var forOS = totalram * osReservedPart;
    var coretemp = corenumber > 4 ? 0 : corenumber;
    var sqlThreads = 256 + (coretemp - 4) * 8;
    var temp = corenumber / 4;

    var maxMemory = (totalram - forOS - otherApps - (sqlThreads * threadStackSize) - (1024 * Math.ceil(temp)));
    return Math.ceil(maxMemory);
  };

  var handleChangeEvent = function(e) {
    var id = e.target.id;
    values[id] = elements[id].value;
    elements.result.innerText = numberWithCommas(calculateMemory());
  };

  elements.architecture.addEventListener('change', handleChangeEvent);
  elements.totalram.addEventListener('change', handleChangeEvent);
  elements.cores.addEventListener('change', handleChangeEvent);
  elements.otherram.addEventListener('change', handleChangeEvent);

  elements.result.innerText = numberWithCommas(calculateMemory());
})();
/*

 */
<|endoftext|>


<commit_before>var GitHubApi = require('github');
var fs = require('fs');
var marked = require('marked');
var async = require('async');

var issues = function (session, callback) {

	var keywords = [];
	var nodes = [];
	var links = [];
	var repos = [];
	var nodesByNumber = {};
	var msg;

	var github = new GitHubApi({
		version: '3.0.0',
	});

	github.authenticate({
		type: "oauth",
		token: session.oauth
	});

	fs.readFile(__dirname+"/keywords.json", 'utf8', function (err, data) {
		if (err) {
			return console.log(err);
		}
		keywords = (JSON.parse(data).keywords);
		keywords.push("n/a");

		if (session.source == 1) {
			msg = {
				user: session.uid,
				type: 'all',
				page: 1,
				per_page: 100
			};
		} else if (session.source == 2){
			msg = {
				org: session.sname,
				page: 1,
				per_page: 100
			};
		} else {
			msg = {
				user: session.sname,
				page: 1,
				per_page: 100
			};
		}

		getRepos();

		function getRepos(){
			if (session.source == 2){
				github.repos.getFromOrg(msg, function (err, res) {
					repos = repos.concat(res);

					if (res.length < 100){
						callGetIssues(repos, session.sname);
					} else {
						msg.page++;
						getRepos();
					}
				});
			} else {
				github.repos.getFromUser(msg, function (err, res) {
					repos = repos.concat(res);

					if (res.length < 100){
						if (session.source == 1){
							callGetIssues(repos, session.uid);
						} else {
							callGetIssues(repos, session.sname);
						}
					} else {
						msg.page++;
						getRepos();
					}
				});
			}
		}

		function callGetIssues(repoList, user){
			var filtered = repoList.filter(function (repo) {
				return repo.has_issues;
			});
			repos = repoList.map(function (repo) {
				return repo.name;
			});

			var repos_length = filtered.length;
			async.each(filtered, function (repo, done) {
				getIssues(user, github, repo.name, 1, done);
			}, function (err) {
				if (err){
					callback(err);
				} else {
					writeData(user);
				}
			});
		}
	});

	function getIssues(user,github,name,pageN,done){
		github.issues.repoIssues({
			user: user,
			repo: name,
			page: pageN,
			per_page: 100
			//state: "open"
		}, function(err, res) {
			//conole.log(res.length);
			if (err) {
				console.log('Error!' + err);
				done(err);
			}
			res.forEach( function(value) {
				// copy over just the values we want

				var node = {
					id: '' + value.id,
					number: value.number,
					title: value.title,
					body: marked(value.body),
					naked_body: value.body,
					assignee: value.assignee ? value.assignee.login : "none",
					milestone: value.milestone ? value.milestone.title : "none",
					repo: name,
					url: value.html_url
				};
				nodes.push(node);
				nodesByNumber[user + "/" +node.repo + '#' + node.number] = node;
			});
			if (res.length === 0){
				done();
			} else {
				getIssues(user,github, name, pageN+1, done);
			}
		});
	}

	function writeData(user){
		var linkPattern = /[a-z]* ?[&a-z-_/]*#\d+/gi;
		var keywordRegEx = new RegExp(keywords.join('|')); //regex for keywords
		nodes.forEach(function (node) {
			var matches = node.body.match(linkPattern);
			if (matches && matches.length > 0) {
				matches.forEach(function (match) {
					var type = match.match(keywordRegEx);
					if(type === null) type = "n/a";
					var id = match.match(/[&a-z-_/]+#\d+/i);
					var linkedTo = null;
					if (id && id.length >0) {
						if (match.indexOf('&') == -1){
							linkedTo = nodesByNumber[id];
						}
					} else {
						id = match.match(/#\d+/);

						if (id && id.length >0) {
							linkedTo = nodesByNumber[user +"/" + node.repo + id[0].replace(/ /, "")];
						}
					}
					if (linkedTo) {
						links.push({
							source: node,
							target: linkedTo,
							type: type
						});
					}
				});
			}
		});
		var graphData = {
			keywords: keywords,
			repos: repos.sort(),
			nodes: nodes,
			links: links
		};
		//console.log(JSON.stringify(graphData, null, 2));
		callback(null, graphData);
	}
};

module.exports.getIssues = issues;<commit_msg>fix bug when user is not owner of collaborative repo<commit_after>var GitHubApi = require('github');
var fs = require('fs');
var marked = require('marked');
var async = require('async');

var issues = function (session, callback) {

	var keywords = [];
	var nodes = [];
	var links = [];
	var repos = [];
	var nodesByNumber = {};
	var msg;

	var github = new GitHubApi({
		version: '3.0.0',
	});
	github.authenticate({
		type: "oauth",
		token: session.oauth
	});

	fs.readFile(__dirname+"/keywords.json", 'utf8', function (err, data) {
		if (err) {
			return console.log(err);
		}
		keywords = (JSON.parse(data).keywords);
		keywords.push("n/a");

		if (session.source == 1) {
			msg = {
				user: session.uid,
				type: 'all',
				page: 1,
				per_page: 100
			};
		} else if (session.source == 2){
			msg = {
				org: session.sname,
				page: 1,
				per_page: 100
			};
		} else {
			msg = {
				user: session.sname,
				page: 1,
				per_page: 100
			};
		}

		getRepos();

		function getRepos(){
			if (session.source == 2){
				github.repos.getFromOrg(msg, function (err, res) {
					repos = repos.concat(res);

					if (res.length < 100){
						callGetIssues(repos, session.sname);
					} else {
						msg.page++;
						getRepos();
					}
				});
			} else {
				github.repos.getFromUser(msg, function (err, res) {
					repos = repos.concat(res);

					if (res.length < 100){
						if (session.source == 1){
							callGetIssues(repos, session.uid);
						} else {
							callGetIssues(repos, session.sname);
						}
					} else {
						msg.page++;
						getRepos();
					}
				});
			}
		}

		function callGetIssues(repoList, user){
			var filtered = repoList.filter(function (repo) {
				return repo.has_issues;
			});
			repos = repoList.map(function (repo) {
				return repo.name;
			});

			var repos_length = filtered.length;
			async.each(filtered, function (repo, done) {
				getIssues(repo.owner.login, github, repo.name, 1, done);
			}, function (err) {
				if (err){
					callback(err);
				} else {
					writeData(user);
				}
			});
		}
	});

	function getIssues(user,github,name,pageN,done){
		github.issues.repoIssues({
			user: user,
			repo: name,
			page: pageN,
			per_page: 100
			//state: "open"
		}, function(err, res) {

			if (res) console.log("Issue Length" + res.length + name);
			if (err) {
				console.log('Error pulling from '+name+': ' + err);
				done(err);
			} else {
				res.forEach( function(value) {
					// copy over just the values we want

					var node = {
						id: '' + value.id,
						number: value.number,
						title: value.title,
						body: value.body ? marked(value.body) : "",
						naked_body: value.body,
						assignee: value.assignee ? value.assignee.login : "none",
						milestone: value.milestone ? value.milestone.title : "none",
						repo: name,
						url: value.html_url
					};
					nodes.push(node);
					nodesByNumber[user + "/" +node.repo + '#' + node.number] = node;
				});
				if (res.length === 0){
					done();
				} else {
					getIssues(user,github, name, pageN+1, done);
				}
			}

		});
	}

	function writeData(user){
		var linkPattern = /[a-z]* ?[&a-z-_/]*#\d+/gi;
		var keywordRegEx = new RegExp(keywords.join('|')); //regex for keywords
		nodes.forEach(function (node) {
			var matches = node.body.match(linkPattern);
			if (matches && matches.length > 0) {
				matches.forEach(function (match) {
					var type = match.match(keywordRegEx);
					if(type === null) type = "n/a";
					var id = match.match(/[&a-z-_/]+#\d+/i);
					var linkedTo = null;
					if (id && id.length >0) {
						if (match.indexOf('&') == -1){
							linkedTo = nodesByNumber[id];
						}
					} else {
						id = match.match(/#\d+/);

						if (id && id.length >0) {
							linkedTo = nodesByNumber[user +"/" + node.repo + id[0].replace(/ /, "")];
						}
					}
					if (linkedTo) {
						links.push({
							source: node,
							target: linkedTo,
							type: type
						});
					}
				});
			}
		});
		var graphData = {
			keywords: keywords,
			repos: repos.sort(),
			nodes: nodes,
			links: links
		};
		//console.log(JSON.stringify(graphData, null, 2));
		callback(null, graphData);
	}
};

module.exports.getIssues = issues;<|endoftext|>


<commit_before>import faker from 'faker';

let vouchers = [];

function generateVoucher(index = Math.round(Math.random() * Number.MAX_SAFE_INTEGER)) {
  const isPaperVoucher = Math.random() < 0.5;
  const faceValue = Math.round((Math.random() + 1) * 10000);
  const askingPrice = Math.round(faceValue * (1.5 - Math.random()));

  return {
    brand_image_url: `${faker.image.imageUrl(40, 40)}?${index}`,
    brand_name: faker.company.companyName(),
    serial_number: Number.parseInt(faker.finance.account(), 10),
    cvv: isPaperVoucher ? null : Number.parseInt(faker.finance.account(), 10),
    face_value: faceValue,
    asking_price: askingPrice,
    discount: Math.round((100 * (faceValue - askingPrice)) / faceValue),
    seller: faker.name.findName(),
    created: +faker.date.past(),
    id: faker.random.uuid(),
    notes: faker.lorem.sentence(),
    currency: ['GBP', 'US', 'EUR'][Math.floor(Math.random() * 2.99)],
    paper_voucher: isPaperVoucher,
    bulk_id: faker.random.uuid(),
    invoice_number: faker.random.uuid(),
    order_number: index,
    status: [null, 'Active', 'Decline'][Math.floor(Math.random() * 2.99)],
  };
}

function generateVouchers(n = 10, current = 0) {
  return n <= 0 ? [] : generateVouchers(n - 1, current + 1).concat(generateVoucher(current));
}

export default {
  get() {
    if (vouchers.length === 0) {
      vouchers = generateVouchers(Math.round(Math.random() * 20));
    }
    return Promise.resolve(vouchers);
  },
};
<commit_msg>Added ability to fetch one voucher<commit_after>import faker from 'faker';

function generateVoucher(index = Math.round(Math.random() * Number.MAX_SAFE_INTEGER)) {
  const isPaperVoucher = Math.random() < 0.5;
  const faceValue = Math.round((Math.random() + 1) * 10000);
  const askingPrice = Math.round(faceValue * (1.5 - Math.random()));

  return {
    brand_image_url: `${faker.image.imageUrl(40, 40)}?${index}`,
    brand_name: faker.company.companyName(),
    serial_number: Number.parseInt(faker.finance.account(), 10),
    cvv: isPaperVoucher ? null : Number.parseInt(faker.finance.account(), 10),
    face_value: faceValue,
    asking_price: askingPrice,
    discount: Math.round((100 * (faceValue - askingPrice)) / faceValue),
    seller: faker.name.findName(),
    created: +faker.date.past(),
    id: faker.random.uuid(),
    notes: faker.lorem.sentence(),
    currency: ['GBP', 'US', 'EUR'][Math.floor(Math.random() * 2.99)],
    paper_voucher: isPaperVoucher,
    bulk_id: faker.random.uuid(),
    invoice_number: faker.random.uuid(),
    order_number: index,
    status: [null, 'Active', 'Decline'][Math.floor(Math.random() * 2.99)],
  };
}

function generateVouchers(n = 10, current = 0) {
  return n <= 0 ? [] : generateVouchers(n - 1, current + 1).concat(generateVoucher(current));
}

const vouchers = generateVouchers(Math.round(Math.random() * 20));

export default {
  get(id = null) {
    if (!id) {
      return Promise.resolve(vouchers);
    }
    const voucher = vouchers.find(item => item.id === id);
    return voucher ? Promise.resolve(voucher) : Promise.reject();
  },
};
<|endoftext|>


<commit_before>var Swell = (
	function()
	{
		var done = false;

		function init()
		{
			//make sure this can only run once
			if (done)
			{
				return;
			}
			done = true;

			/*
			 * set some variables that tell us what device we're using
			 */
			var isIphone        = isIPhone();
			var isIpad          = isIPad();
			var isAndroidOs     = isAndroid();
			var isChrome        = isChromeBrowser();
			var androidVersion  = null; //accurate to one minor revision

			/*
			 * get the initial phone orientation
			 */
			var phoneOrientation = window.orientation;

			//orientation offest explained later
			var orientationOffset = 0;

			//if it's an Android phone find out what version
			if (isAndroidOs)
			{
				androidVersion = getAndroidVersion();

				/*
				 * work out the orientation 'offset'
				 *
				 * the default orientation on some Android devices is 'landscape' so we need to check
				 * that the width:height ratio is what we would expect for the orientation the
				 * device is reporting. Otherwise we need to calculate an offset value
				 */
				if (window.orientation == 0|| window.orientation ==180)
				{
					orientationOffset = screen.height > screen.width ? 0 : 90;
				}

				if (window.orientation == 90|| window.orientation ==-90)
				{
					orientationOffset = screen.height < screen.width ? 0 : 90;
				}
			}

			/* On android 4+ orientation event gets fired before browser has actually worked out how to display things in the
			 * new orientation so it's pretty useless. Need to use resize event for android.
			 * Android <3 doesn't fire orientation event so that doesn't matter. We need a different
			 * method to handle the old Android phones because they behave differently - more on this later.
			 *
			 * Whacker than a bag of hammers.
			 */
			if (isAndroidOs && !isChrome)
			{
				if (androidVersion < 3)
				{

					window.addEventListener('resize', oldAndroidResizeHandler);
				}
				else
				{
					window.addEventListener('resize', resizeHandler);
				}
			}
			else
			{
				window.addEventListener('orientationchange', orientationChangeHandler);
			}

			/**
			 * Method used to handle re-orientation on android 3+
			 */
			function resizeHandler()
			{
				/*
				 * opening the keyboard triggers a resize event so check
				 * that the orientation has actually changed
				 */
				if (window.orientation == phoneOrientation) return;

				/*
				 * remember the new orientation
				 */
				phoneOrientation = window.orientation;

				/*
				 * for Android 3+ we reset the Document after we last hid the URL bar
				 * (for reasons that are explained in other comments). So all we need
				 * to do here is give the browser 200ms to come to terms with the fact
				 * that it has been re-oriented then hide the URL bar.
				 */
				setTimeout(hideUrlBar,400);
			}

			/**
			 * Method used to handle re-orientation on Android <=2.3
			 */
			function oldAndroidResizeHandler()
			{
				/*
				 * Older Android seems to fire resize events for loads of things sometimes it seems
				 * almost random. We need to check that the orientation has actually changed before we do anything
				 */
				if (window.orientation == phoneOrientation) return;

				/*
				 * remember the new orientation
				 */
				phoneOrientation = window.orientation;

				/*
				 * we need to reset the styles that have been added to the document to allow
				 * the browser to calculate it's new natural height. Then give the browser
				 * 200ms to come to terms with the changes and hide the URL bar
				 */
				resetDocument();

				setTimeout(hideUrlBar,400);
			}

			/**
			 * method used to handle re-orientation on iOS
			 */
			function orientationChangeHandler()
			{
				/*
				* if we're running on an iPhone reset the document styles - to allow the device
				* to calculate DOM dimensions for the new orientation
				*
				* then hide the URL bar again
				*/

				if (isIphone)
				{
					resetDocument();
					hideUrlBar();
					return;
				}

				/*
				 * if it's an iPad or Android running chrome then just publish the orientation change
				 */
				publishChange();
			}

			/**
			 * reset any height style applied to the document
			 */
			function resetDocument()
			{
				document.documentElement.style.height = '';
			}

			/**
			 * method to hide the URL bar from the visible area of the screen
			 */
			function hideUrlBar()
			{
				//iPhone and android 2.3 need 60 extra pixels to hide the URL bar android 4 only 52
				var additionalHeight = isAndroidOs && androidVersion >=3 ? 52 : 60;

				var fullheight = document.documentElement.clientHeight + additionalHeight;

				document.documentElement.style.height = fullheight + 'px';

				//scroll the URL bar above the top of the screen
				window.scrollTo(0,1);

				//publish the fact that orientation has changed with a value of true or false specifying whether the orientation is landscape
				publishChange();
				/*
				 * on Android 4 afer we've set the height we give it 200ms then clear
				 * the style set on the document. Android 4 maintains the fullscreen height
				 * even after we've cleared it and doesn't try and bring back the URL bar (unlike the other OSs).
				 *
				 * We need to do this otherwise it becomes very difficult to reset the height on
				 * re-orientation. As Android 4 remembers the height - if you re-orientate then clear
				 * the size isn't adjusted to the new re-oriented screen size. The current size of the document
				 * overrides the screen size so 100% = whatever the current document size is.
				 */
				 if (isAndroidOs && androidVersion >= 3)
				 {
					setTimeout(resetDocument,200);
				 }
			}

			function publishChange()
			{
				/*
				here is where we can publish to the rest of our app that we are ready and pass something like this:
				(window.orientation + orientationOffset == 90 || window.orientation + orientationOffset == -90);
				telling our app whether we're in landscape or portrait
				*/
			}

			/*
			 * on load - if it's not an iPad and not chrome then hide the URL bar
			 * use a 0 timeout to give iOS a chance to calculate
			 * the correct DOM dimensions
			 */
			  if (!isIpad && !isChrome) setTimeout(hideUrlBar, 0);
		}

		function isIOSDevice()
		{
			return navigator.userAgent.match(/iPad|iPhone|iPod/i) != null;
		}

		function isIPad()
		{
			return navigator.userAgent.match(/iPad/i) != null;
		}

		function isIPhone()
		{
			return navigator.userAgent.match(/iPhone|iPod/i) != null;
		}

		function isAndroid()
		{
			return navigator.userAgent.match(/Android/i) != null;
		}

		function isSonyPhone()
		{
			return navigator.userAgent.match(/SonyEricsson/i) != null;
		}

		function getAndroidVersion()
		{
			var androidRegexResult = /Android (\d+(?:\.\d+)+)/.exec(navigator.userAgent);

			if (androidRegexResult && androidRegexResult.length && androidRegexResult[0].split(' ')[1])
			{
				androidVersion = parseFloat(androidRegexResult[0].split(' ')[1]);

				return androidVersion;
			}
		}

		function iOSVersion(){
			if (isIOSDevice()){

				var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);

			        // Usual iOS user agent format
			        if(v && v.length > 2)
			        {
			            return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
			        }

			}

			return -1;
		}

		function isChromeBrowser()
		{
			return navigator.userAgent.match(/Chrome/i) != null;
		}
		return {init:init};
	})();

<commit_msg>Update swell.js<commit_after>var Swell = (
	function()
	{
		var done = false;

		function init()
		{
			//make sure this can only run once
			if (done)
			{
				return;
			}
			done = true;

			/*
			 * set some variables that tell us what device we're using
			 */
			var isIphone        = isIPhone();
			var isIpad          = isIPad();
			var isAndroidOs     = isAndroid();
			var isChrome        = isChromeBrowser();
			var androidVersion  = null; //accurate to one minor revision

			/*
			 * get the initial phone orientation
			 */
			var phoneOrientation = window.orientation;

			//orientation offest explained later
			var orientationOffset = 0;

			//if it's an Android phone find out what version
			if (isAndroidOs)
			{
				androidVersion = getAndroidVersion();

				/*
				 * work out the orientation 'offset'
				 *
				 * the default orientation on some Android devices is 'landscape' so we need to check
				 * that the width:height ratio is what we would expect for the orientation the
				 * device is reporting. Otherwise we need to calculate an offset value
				 */
				if (window.orientation == 0|| window.orientation ==180)
				{
					orientationOffset = screen.height > screen.width ? 0 : 90;
				}

				if (window.orientation == 90|| window.orientation ==-90)
				{
					orientationOffset = screen.height < screen.width ? 0 : 90;
				}
			}

			/* On android 4+ orientation event gets fired before browser has actually worked out how to display things in the
			 * new orientation so it's pretty useless. Need to use resize event for android.
			 * Android <3 doesn't fire orientation event so that doesn't matter. We need a different
			 * method to handle the old Android phones because they behave differently - more on this later.
			 *
			 * Whacker than a bag of hammers.
			 */
			if (isAndroidOs && !isChrome)
			{
				if (androidVersion < 3)
				{

					window.addEventListener('resize', oldAndroidResizeHandler);
				}
				else
				{
					window.addEventListener('resize', resizeHandler);
				}
			}
			else
			{
				window.addEventListener('orientationchange', orientationChangeHandler);
			}

			/**
			 * Method used to handle re-orientation on android 3+
			 */
			function resizeHandler()
			{
				/*
				 * opening the keyboard triggers a resize event so check
				 * that the orientation has actually changed
				 */
				if (window.orientation == phoneOrientation) return;

				/*
				 * remember the new orientation
				 */
				phoneOrientation = window.orientation;

				/*
				 * for Android 3+ we reset the Document after we last hid the URL bar
				 * (for reasons that are explained in other comments). So all we need
				 * to do here is give the browser 200ms to come to terms with the fact
				 * that it has been re-oriented then hide the URL bar.
				 */
				setTimeout(hideUrlBar,400);
			}

			/**
			 * Method used to handle re-orientation on Android <=2.3
			 */
			function oldAndroidResizeHandler()
			{
				/*
				 * Older Android seems to fire resize events for loads of things sometimes it seems
				 * almost random. We need to check that the orientation has actually changed before we do anything
				 */
				if (window.orientation == phoneOrientation) return;

				/*
				 * remember the new orientation
				 */
				phoneOrientation = window.orientation;

				/*
				 * we need to reset the styles that have been added to the document to allow
				 * the browser to calculate it's new natural height. Then give the browser
				 * 200ms to come to terms with the changes and hide the URL bar
				 */
				resetDocument();

				setTimeout(hideUrlBar,400);
			}

			/**
			 * method used to handle re-orientation on iOS
			 */
			function orientationChangeHandler()
			{
				/*
				* if we're running on an iPhone reset the document styles - to allow the device
				* to calculate DOM dimensions for the new orientation
				*
				* then hide the URL bar again
				*/

				if (isIphone)
				{
					resetDocument();
					hideUrlBar();
					return;
				}

				/*
				 * if it's an iPad or Android running chrome then just publish the orientation change
				 */
				publishChange();
			}

			/**
			 * reset any height style applied to the document
			 */
			function resetDocument()
			{
				document.documentElement.style.height = '';
			}

			/**
			 * method to hide the URL bar from the visible area of the screen
			 */
			function hideUrlBar()
			{
				//iPhone and android 2.3 need 60 extra pixels to hide the URL bar android 4 only 52
				var additionalHeight = isAndroidOs && androidVersion >=3 ? 52 : 60;

				var fullheight = document.documentElement.clientHeight + additionalHeight;

				document.documentElement.style.height = fullheight + 'px';

				/*
				 * scroll the URL bar above the top of the screen
				 * we need the 400 ms for Samsung phones. It might not benecessary for all phone
				 * but the 400 ms is of minimal detriment to user experiend so just do it for all
				*/
				setTimeout(function(){window.scrollTo(0,1);}, 400);

				//publish the fact that orientation has changed with a value of true or false specifying whether the orientation is landscape
				publishChange();
				/*
				 * on Android 4 afer we've set the height we give it 500ms then clear
				 * the style set on the document. Android 4 maintains the fullscreen height
				 * even after we've cleared it and doesn't try and bring back the URL bar (unlike the other OSs).
				 *
				 * We need to do this otherwise it becomes very difficult to reset the height on
				 * re-orientation. As Android 4 remembers the height - if you re-orientate then clear
				 * the size isn't adjusted to the new re-oriented screen size. The current size of the document
				 * overrides the screen size so 100% = whatever the current document size is.
				 */
				 if (isAndroidOs && androidVersion >= 3)
				 {
					setTimeout(resetDocument,500);
				 }
			}

			function publishChange()
			{
				/*
				here is where we can publish to the rest of our app that we are ready and pass something like this:
				(window.orientation + orientationOffset == 90 || window.orientation + orientationOffset == -90);
				telling our app whether we're in landscape or portrait
				*/
			}

			/*
			 * on load - if it's not an iPad and not chrome then hide the URL bar
			 * use a 0 timeout to give iOS a chance to calculate
			 * the correct DOM dimensions
			 */
			  if (!isIpad && !isChrome) setTimeout(hideUrlBar, 0);
		}

		function isIOSDevice()
		{
			return navigator.userAgent.match(/iPad|iPhone|iPod/i) != null;
		}

		function isIPad()
		{
			return navigator.userAgent.match(/iPad/i) != null;
		}

		function isIPhone()
		{
			return navigator.userAgent.match(/iPhone|iPod/i) != null;
		}

		function isAndroid()
		{
			return navigator.userAgent.match(/Android/i) != null;
		}

		function isSonyPhone()
		{
			return navigator.userAgent.match(/SonyEricsson/i) != null;
		}

		function getAndroidVersion()
		{
			var androidRegexResult = /Android (\d+(?:\.\d+)+)/.exec(navigator.userAgent);

			if (androidRegexResult && androidRegexResult.length && androidRegexResult[0].split(' ')[1])
			{
				androidVersion = parseFloat(androidRegexResult[0].split(' ')[1]);

				return androidVersion;
			}
		}

		function iOSVersion(){
			if (isIOSDevice()){

				var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);

			        // Usual iOS user agent format
			        if(v && v.length > 2)
			        {
			            return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
			        }

			}

			return -1;
		}

		function isChromeBrowser()
		{
			return navigator.userAgent.match(/Chrome/i) != null;
		}
		return {init:init};
	})();

<|endoftext|>


<commit_before>/**
 * @file Dialog component
 * @author chao(chao.zhang@derbysoft.com)
 */

import React, {Component} from 'react';

import SubtreeContainer from '../_SubtreeContainer';
import DialogBody from '../_DialogBody/DialogBody';

import Dom from '../_vendors/Dom';

export default class Dialog extends Component {

    static ButtonUITypes = DialogBody.ButtonUITypes;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.setBodyLock = this::this.setBodyLock;

    }

    setBodyLock(props = this.props) {

        if (!props) {
            return;
        }

        props.showModal && Dom.toggleClass(document.querySelector('body'), 'dialog-modal-lock', props.visible);

    }

    componentDidMount() {
        this.setBodyLock();
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.visible !== this.props.visible) {
            this.setBodyLock(nextProps);
        }
    }

    componentWillUnmount() {
        this.setBodyLock(false);
    }

    render() {

        const {visible} = this.props;

        return (
            <SubtreeContainer visible={visible}>
                <DialogBody {...this.props}/>
            </SubtreeContainer>
        );

    }

};

Dialog.propTypes = DialogBody.propTypes;
Dialog.defaultProps = DialogBody.defaultProps;<commit_msg>Dialog bug fixed<commit_after>/**
 * @file Dialog component
 * @author chao(chao.zhang@derbysoft.com)
 */

import React, {Component} from 'react';

import SubtreeContainer from '../_SubtreeContainer';
import DialogBody from '../_DialogBody/DialogBody';

import Dom from '../_vendors/Dom';

export default class Dialog extends Component {

    static ButtonUITypes = DialogBody.ButtonUITypes;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.setBodyLock = this::this.setBodyLock;

    }

    setBodyLock(props = this.props) {

        if (!props) {
            return;
        }

        props.showModal && Dom.toggleClass(document.querySelector('body'), 'dialog-modal-lock', props.visible);

    }

    resetBody() {
        Dom.removeClass(document.querySelector('body'), 'dialog-modal-lock');
    }

    componentDidMount() {
        this.setBodyLock();
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.visible !== this.props.visible) {
            this.setBodyLock(nextProps);
        }
    }

    componentWillUnmount() {
        this.resetBody();
    }

    render() {

        const {visible} = this.props;

        return (
            <SubtreeContainer visible={visible}>
                <DialogBody {...this.props}/>
            </SubtreeContainer>
        );

    }

};

Dialog.propTypes = DialogBody.propTypes;
Dialog.defaultProps = DialogBody.defaultProps;<|endoftext|>