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

  function hasClass(el, className) {
    if (el.classList)
      return el.classList.contains(className)
    else
      return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'))
  }

  function my_addClass(el, className) {
    console.log(el, className);
    if (el.classList)
      el.classList.add(className)
    else if (!hasClass(el, className)) el.className += " " + className
  }

  function my_removeClass(el, className) {
    console.log(el, className);
    if (el.classList)
      el.classList.remove(className)
    else if (hasClass(el, className)) {
      var reg = new RegExp('(\\s|^)' + className + '(\\s|$)')
      el.className=el.className.replace(reg, ' ')
    }
  }

  function each(o, cb, s){
    var n;
    if (!o){
      return 0;
    }
    s = !s ? o : s;
    if (o instanceof Array){
      // Indexed arrays, needed for Safari
      for (n=0; n<o.length; n++) {
        if (cb.call(s, o[n], n, o) === false){
          return 0;
        }
      }
    } else {
      // Hashtables
      for (n in o){
        if (o.hasOwnProperty(n)) {
          if (cb.call(s, o[n], n, o) === false){
            return 0;
          }
        }
      }
    }
    return 1;
  };

  $(document).ready(function() {
    var
      scrollElements,
      inValidRange,
      setVisibility,
      setVisibilityAll,
      resetScroll;

    // scrollElements = $('[data-scroll-element]');
    console.log($('[data-scroll-element]'));
    scrollElements = document.querySelectorAll('[data-scroll-element]');
    console.log(scrollElements);

    inValidRange = function(offset, limit) {
      return offset >= 0 && offset < limit;
    };

    setVisibility = function(element, nextOffset, limit) {
      if(inValidRange(nextOffset, limit)) {
        // element.removeClass('disabled');
        my_removeClass(element, 'disabled');
      } else {
        // element.addClass('disabled');
        my_addClass(element, 'disabled');
      }
    };

    setVisibilityAll = function(elements, newOffset) {
      var
        $this,
        increment,
        nextOffset,
        limit;

      elements.each(function() {
        $this      = $(this);
        increment  = $this.data('scroll-increment');
        nextOffset = newOffset + increment;
        limit      = $this.data('scroll-limit');

        setVisibility($this, nextOffset, limit);
      });
    };

    resetScroll = function(element) {
      element[0].scrollTop = 0;
      element.data('scroll-offset', 0);
    };

    $('[data-clipboard-target]').each(function() {
      var
          clipboard,
          notification;

      clipboard = new Clipboard(this);

      var notify = function(clipboardNotification, notificationText) {
          notification = $($(clipboardNotification));
          notification.text(notificationText);
          notification.addClass('in').delay(1400).queue(function() {
              notification.removeClass('in');
              $(this).dequeue();
          })
      };

      var notifySuccess = function(e) {
          notify(e.trigger.dataset.clipboardNotification, "Copied!");
      };

      var notifyFailure = function(e) {
          //if the copy function failed the text should still be selected, so just ask the user to hit ctrl+c
          notify(e.trigger.dataset.clipboardNotification, "Press Ctrl+C to copy");
      };

      clipboard.on('success', notifySuccess);
      clipboard.on('error', notifyFailure);
    });

    each(scrollElements, function(el) {
      var
        $this,
        element,
        increment,
        limit,
        offset,
        nextOffset,
        newOffset;

      $this     = $(this);
      element   = $($this.data('scroll-element'));
      console.log(element);

      element = document.querySelector(el.dataset.scrollElement);
      console.log(element);
      increment = el.dataset.scrollIncrement;
      limit     = element.dataset.scrollLimit;
      offset    = element.dataset.scrollOffset;

      console.log("increment", increment);
      console.log("limit", limit);
      console.log("offset", offset);

      element.dataset.scrollLimit = limit;

      nextOffset = offset + increment;
      setVisibility($this, nextOffset, limit);

      // Force IE to forget previous scroll top value
      resetScroll(element);

      $this.on('click', function() {
        offset = element.data('scroll-offset');

        newOffset = offset + increment;
        if (inValidRange(newOffset, limit)) {
          element.animate({
              scrollTop: $('#' + newOffset).position().top
          }, 400);
          element.data('scroll-offset', newOffset);

          setVisibilityAll(scrollElements, newOffset);
        }
      });
    })
    // scrollElements.each();

    $('[data-open-panel]').each(function() {
      var
        $this,
        element;

      $this   = $(this);
      element = $($this.data('open-panel'));

      $this.on('click', function() {
        element.addClass('open');
      });
    });

    $('[data-close-panel]').each(function() {
      var
        $this,
        element;

      $this   = $(this);
      element = $($this.data('close-panel'));

      $this.on('click', function() {
        element
          .one('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function() {
            $this.trigger('panel:closed');
          }).removeClass('open');
      });
    });

    $('[data-scroll-reset]').each(function() {
      var
        $this,
        element;

      $this   = $(this);
      element = $($this.data('scroll-reset'));

      $this.on('click', function() {
        $this.one('panel:closed', function() {
          resetScroll(element);
          setVisibilityAll(scrollElements, 0);
        });
      });
    });

    $('[data-moment]').each(function() {
      var $this;

      $this = $(this);

      var time = moment(parseInt($this.text()));
      $this.text(time.fromNow());
    });
  });

  var setContainerHeight = function(containerEl) {
    // TODO: Refactor this to make simpler
    var
      bodyEl,
      bodyHeight,
      bodyHeightWithoutTitle,
      titleEl,
      panelEl,
      referralsEl,
      referralsTitleEl,
      panelHeight,
      css,
      stylesheet;

    bodyEl           = $('.squatch-body');
    titleEl          = bodyEl.find('.squatch-title');
    panelEl          = $('#squatch-panel');
    referralsEl      = $('.squatch-referrals');
    referralsTitleEl = $('.squatch-referrals-title');

    bodyHeight = bodyEl.outerHeight();
    bodyHeightWithoutTitle = bodyHeight - titleEl.outerHeight(true) - titleEl.position().top;
    panelHeight = panelEl.outerHeight();

    if (referralsEl.is(':visible')) {
      panelHeight -= referralsEl.outerHeight();
    }

    if (referralsTitleEl.is(':visible')) {
      panelHeight -= referralsTitleEl.outerHeight();
    }

    containerEl.css('height', bodyHeight + panelHeight);

    stylesheet = document.createElement('style');
    stylesheet.type = 'text/css';

    css = '#squatch-panel.open {' +
      '-webkit-transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
      '-ms-transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
      '-o-transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
      'transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
      '}' +
      'html.lt-ie9 #squatch-panel.open {' +
      'top: -' + bodyHeightWithoutTitle + 'px;' +
      '}';

    if (stylesheet.styleSheet){
      // IE
      stylesheet.styleSheet.cssText = css;
    } else {
      // W3C Standard
      stylesheet.appendChild(document.createTextNode(css));
    }

    document.querySelector('head').appendChild(stylesheet);
  };

  var containerEl = $('.squatch-container-popup');
  if (containerEl.length) {
    var setContainerHeightForPopup = setContainerHeight.bind(undefined, containerEl);
    var windowEl = $(window);

    // Workaround for popup height being incorrectly set during iframe resizing.
    // This is due to the popup being displayed inconsistently - with responsive styles activated, sometimes the mobile view will be displayed, even on a desktop monitor with more than 500 pixels width. The solution may be to set the iframe width to the full browser width, rather than the width of the widget theme.
    // There is another hack in _popup.less as well
    // TODO: Find a solution for this and enable responsive styles again.
    windowEl.on('load', function (i) {
      var setContainerHeightIfWideEnough = function () {
        var width = windowEl.width();

        if (width === 500) {
          setContainerHeightForPopup();
        } else {
          setTimeout(function() {
            setContainerHeightIfWideEnough();
          }, 50);
        }
      };

      setContainerHeightIfWideEnough();
    });

    // The content has a different height in mobile
    // TODO: Find a a solution for responsive in popups and re-enable this
    // var mql = window.matchMedia('(max-width: 500px)');
    // mql.addListener(setContainerHeightForPopup);
  }
})();
<commit_msg>Test my each<commit_after>(function() {
  'use strict';

  function hasClass(el, className) {
    if (el.classList)
      return el.classList.contains(className)
    else
      return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'))
  }

  function my_addClass(el, className) {
    console.log(el, className);
    if (el.classList)
      el.classList.add(className)
    else if (!hasClass(el, className)) el.className += " " + className
  }

  function my_removeClass(el, className) {
    console.log(el, className);
    if (el.classList)
      el.classList.remove(className)
    else if (hasClass(el, className)) {
      var reg = new RegExp('(\\s|^)' + className + '(\\s|$)')
      el.className=el.className.replace(reg, ' ')
    }
  }

  function each(o, cb, s){
    var n;
    if (!o){
      return 0;
    }
    s = !s ? o : s;
    if (o instanceof Array){
      // Indexed arrays, needed for Safari
      for (n=0; n<o.length; n++) {
        if (cb.call(s, o[n], n, o) === false){
          return 0;
        }
      }
    } else {
      // Hashtables
      for (n in o){
        if (o.hasOwnProperty(n)) {
          if (cb.call(s, o[n], n, o) === false){
            return 0;
          }
        }
      }
    }
    return 1;
  };

  $(document).ready(function() {
    var
      scrollElements,
      inValidRange,
      setVisibility,
      setVisibilityAll,
      resetScroll;

    // scrollElements = $('[data-scroll-element]');
    scrollElements = document.querySelectorAll('[data-scroll-element]');

    inValidRange = function(offset, limit) {
      return offset >= 0 && offset < limit;
    };

    setVisibility = function(element, nextOffset, limit) {
      console.log("Set visibility");
      console.log("element", element);
      console.log("nextOffset", nextOffset);
      console.log("limit", limit);
      if(inValidRange(nextOffset, limit)) {
        // element.removeClass('disabled');
        my_removeClass(element, 'disabled');
      } else {
        // element.addClass('disabled');
        my_addClass(element, 'disabled');
      }
    };

    setVisibilityAll = function(elements, newOffset) {
      var
        $this,
        increment,
        nextOffset,
        limit;

      elements.each(function() {
        $this      = $(this);
        increment  = $this.data('scroll-increment');
        nextOffset = newOffset + increment;
        limit      = $this.data('scroll-limit');

        setVisibility($this, nextOffset, limit);
      });
    };

    resetScroll = function(element) {
      element[0].scrollTop = 0;
      element.data('scroll-offset', 0);
    };

    $('[data-clipboard-target]').each(function() {
      var
          clipboard,
          notification;

      clipboard = new Clipboard(this);

      var notify = function(clipboardNotification, notificationText) {
          notification = $($(clipboardNotification));
          notification.text(notificationText);
          notification.addClass('in').delay(1400).queue(function() {
              notification.removeClass('in');
              $(this).dequeue();
          })
      };

      var notifySuccess = function(e) {
          notify(e.trigger.dataset.clipboardNotification, "Copied!");
      };

      var notifyFailure = function(e) {
          //if the copy function failed the text should still be selected, so just ask the user to hit ctrl+c
          notify(e.trigger.dataset.clipboardNotification, "Press Ctrl+C to copy");
      };

      clipboard.on('success', notifySuccess);
      clipboard.on('error', notifyFailure);
    });

    each(scrollElements, function(el) {
      var
        $this,
        element,
        increment,
        limit,
        offset,
        nextOffset,
        newOffset;

      element = document.querySelector(el.dataset.scrollElement);
      increment = el.dataset.scrollIncrement;
      limit     = element.dataset.scrollLimit;
      offset    = element.dataset.scrollOffset;

      console.log("increment", increment);
      console.log("limit", limit);
      console.log("offset", offset);

      element.dataset.scrollLimit = limit;

      nextOffset = offset + increment;
      console.log(nextOffset);
      setVisibility($this, nextOffset, limit);

      // Force IE to forget previous scroll top value
      resetScroll(element);

      $this.on('click', function() {
        offset = element.data('scroll-offset');

        newOffset = offset + increment;
        if (inValidRange(newOffset, limit)) {
          element.animate({
              scrollTop: $('#' + newOffset).position().top
          }, 400);
          element.data('scroll-offset', newOffset);

          setVisibilityAll(scrollElements, newOffset);
        }
      });
    })
    // scrollElements.each();

    $('[data-open-panel]').each(function() {
      var
        $this,
        element;

      $this   = $(this);
      element = $($this.data('open-panel'));

      $this.on('click', function() {
        element.addClass('open');
      });
    });

    $('[data-close-panel]').each(function() {
      var
        $this,
        element;

      $this   = $(this);
      element = $($this.data('close-panel'));

      $this.on('click', function() {
        element
          .one('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function() {
            $this.trigger('panel:closed');
          }).removeClass('open');
      });
    });

    $('[data-scroll-reset]').each(function() {
      var
        $this,
        element;

      $this   = $(this);
      element = $($this.data('scroll-reset'));

      $this.on('click', function() {
        $this.one('panel:closed', function() {
          resetScroll(element);
          setVisibilityAll(scrollElements, 0);
        });
      });
    });

    $('[data-moment]').each(function() {
      var $this;

      $this = $(this);

      var time = moment(parseInt($this.text()));
      $this.text(time.fromNow());
    });
  });

  var setContainerHeight = function(containerEl) {
    // TODO: Refactor this to make simpler
    var
      bodyEl,
      bodyHeight,
      bodyHeightWithoutTitle,
      titleEl,
      panelEl,
      referralsEl,
      referralsTitleEl,
      panelHeight,
      css,
      stylesheet;

    bodyEl           = $('.squatch-body');
    titleEl          = bodyEl.find('.squatch-title');
    panelEl          = $('#squatch-panel');
    referralsEl      = $('.squatch-referrals');
    referralsTitleEl = $('.squatch-referrals-title');

    bodyHeight = bodyEl.outerHeight();
    bodyHeightWithoutTitle = bodyHeight - titleEl.outerHeight(true) - titleEl.position().top;
    panelHeight = panelEl.outerHeight();

    if (referralsEl.is(':visible')) {
      panelHeight -= referralsEl.outerHeight();
    }

    if (referralsTitleEl.is(':visible')) {
      panelHeight -= referralsTitleEl.outerHeight();
    }

    containerEl.css('height', bodyHeight + panelHeight);

    stylesheet = document.createElement('style');
    stylesheet.type = 'text/css';

    css = '#squatch-panel.open {' +
      '-webkit-transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
      '-ms-transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
      '-o-transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
      'transform: translate(0, -' + bodyHeightWithoutTitle + 'px);' +
      '}' +
      'html.lt-ie9 #squatch-panel.open {' +
      'top: -' + bodyHeightWithoutTitle + 'px;' +
      '}';

    if (stylesheet.styleSheet){
      // IE
      stylesheet.styleSheet.cssText = css;
    } else {
      // W3C Standard
      stylesheet.appendChild(document.createTextNode(css));
    }

    document.querySelector('head').appendChild(stylesheet);
  };

  var containerEl = $('.squatch-container-popup');
  if (containerEl.length) {
    var setContainerHeightForPopup = setContainerHeight.bind(undefined, containerEl);
    var windowEl = $(window);

    // Workaround for popup height being incorrectly set during iframe resizing.
    // This is due to the popup being displayed inconsistently - with responsive styles activated, sometimes the mobile view will be displayed, even on a desktop monitor with more than 500 pixels width. The solution may be to set the iframe width to the full browser width, rather than the width of the widget theme.
    // There is another hack in _popup.less as well
    // TODO: Find a solution for this and enable responsive styles again.
    windowEl.on('load', function (i) {
      var setContainerHeightIfWideEnough = function () {
        var width = windowEl.width();

        if (width === 500) {
          setContainerHeightForPopup();
        } else {
          setTimeout(function() {
            setContainerHeightIfWideEnough();
          }, 50);
        }
      };

      setContainerHeightIfWideEnough();
    });

    // The content has a different height in mobile
    // TODO: Find a a solution for responsive in popups and re-enable this
    // var mql = window.matchMedia('(max-width: 500px)');
    // mql.addListener(setContainerHeightForPopup);
  }
})();
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc overview
 * @name snotes30App
 * @description
 * # snotes30App
 *
 * Main module of the application.
 */
angular
  .module('snotes30App', [
    'ui.router',
    'ngAnimate',
    'ngCookies',
    'ngResource',
    'ngRoute',
    'ngSanitize',
    'angularSpectrumColorpicker',
    'cgBusy',
    'btford.socket-io',
    'restangular'
  ])
  .config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider, RestangularProvider, CONFIG) {
    var docResvolers = {
      'doc': ['DocumentService', '$stateParams', function (DocumentService, $stateParams) {
        return DocumentService.getByName($stateParams.name);
      }],
      'docname': ['$stateParams', function ($stateParams) {
        return $stateParams.name;
      }]
    };

    $urlRouterProvider.otherwise("/404");

    $stateProvider
      .state('livelist', {
        url: '/',
        templateUrl: 'components/livelist/livelist.html',
        controller: 'LiveListCtrl'
      })
      .state('document-edit', {
        url: '/doc/:name/',
        templateUrl: 'components/document/edit/document-edit.html',
        controller: 'DocumentEditCtrl',
        resolve: docResvolers
      })
      .state('document-readonly', {
        url: '/doc/:name/readonly/',
        templateUrl: 'components/document/readonly/document-readonly.html',
        controller: 'DocumentReadonlyCtrl',
        resolve: docResvolers
      })
      .state('document-readonly-pub', {
        url: '/doc/:name/readonly/:pub/',
        templateUrl: 'components/document/readonly/document-readonly.html',
        controller: 'DocumentReadonlyCtrl',
        resolve: docResvolers
      })
      .state('document-sighting', {
        url: '/doc/:name/sigh/',
        templateUrl: 'components/document/sighting/document-sighting.html',
        controller: 'DocumentSightingCtrl',
        resolve: docResvolers
      })
      .state('admin-board', {
        url: '/admin/',
        templateUrl: 'components/admin/board/board.html'
      })
      .state('admin-sighting', {
        url: '/admin/sigh/',
        templateUrl: 'components/admin/sighting/sighting.html'
      })
      .state('admin-importstatus', {
        url: '/admin/importstatus/',
        templateUrl: 'components/admin/importstatus/importstatus.html',
        controller: 'ImportStatusCtrl'
      })
      .state('archive', {
        url: '/archive/',
        templateUrl: 'components/archive/archive.html'
      })
      .state('archive-search', {
        url: '/archive/search/',
        templateUrl: 'components/archive/search.html'
      })
      .state('archive-podcast', {
        url: '/archive/podcast/',
        templateUrl: 'components/archive/podcast.html'
      })
      .state('faq', {
        url: '/faq/',
        templateUrl: 'components/static/faq.html'
      })
      .state('rules', {
        url: '/rules/',
        templateUrl: 'components/static/rules.html'
      })
      .state('community', {
        url: '/community/',
        templateUrl: 'components/static/community.html'
      })
      .state('privacy', {
        url: '/privacy/',
        templateUrl: 'components/static/privacy.html'
      })
      .state('donate', {
        url: '/donate/',
        templateUrl: 'components/static/donate.html'
      })
      .state('imprint', {
        url: '/imprint/',
        templateUrl: 'components/static/imprint.html'
      })
      .state('styleguide', {
        url: '/styleguide/',
        templateUrl: 'components/static/imprint.html'
      })
      .state('publicprofile', {
        url: '/profile/:username/',
        templateUrl: 'components/publicprofile/publicprofile.html',
        controller: 'PublicProfileCtrl'
      })
      .state('user-profile', {
        url: '/user/profile/',
        templateUrl: 'components/user/profile/profile.html',
        controller: 'UserProfileCtrl'
      })
      .state('user-upgrade', {
        url: '/user/upgrade/',
        templateUrl: 'components/user/upgrade/upgrade.html',
        controller: 'UserUpgradeCtrl'
      })
      .state('user-registration', {
        url: '/user/registration/',
        templateUrl: 'components/user/regcomplete/regcomplete.html'
      })
      .state('user-pwreset', {
        url: '/user/pwreset/:username/:token/',
        templateUrl: 'components/user/pwreset/pwreset.html',
        controller: 'UserPwResetCtrl'
      })
      .state('user-activate', {
        url: '/user/activate/:username/:token/',
        templateUrl: 'components/user/activate/activate.html',
        controller: 'UserActivateCtrl'
      })
      .state('user-confirm-email', {
        url: '/user/confirm/:username/:token/',
        templateUrl: 'components/user/activate/activate.html',
        controller: 'UserActivateCtrl'
      });

    $locationProvider.html5Mode(true);

    RestangularProvider.setBaseUrl(CONFIG.apiBaseUrl);
    RestangularProvider.setRequestSuffix('/');
    RestangularProvider.setDefaultHttpFields({
      withCredentials: true
    });

    RestangularProvider.setDefaultHeaders({'Content-Type': 'application/json'});

    RestangularProvider.addElementTransformer('soonepisodes', false, function(element) {
      element.create_date = new Date(element.create_date);
      element.date = new Date(element.date);
      element.podcast.create_date = new Date(element.podcast.create_date);
      if(element.document)
        element.document.create_date = new Date(element.document.create_date);
      return element;
    });

    RestangularProvider.addElementTransformer('importerlogs', false, function(element) {
      function patchTimeAttrs(obj) {
        obj.starttime = new Date(obj.starttime);
        obj.endtime = new Date(obj.endtime);
      }

      patchTimeAttrs(element);

      for(var i = 0; i < element.sources.length; i++) {
        var source = element.sources[i];
        patchTimeAttrs(source);

        for(var j = 0; j < source.jobs.length; j++) {
          var job = source.jobs[i];
          patchTimeAttrs(job);
        }
      }

      return element;
    });

    function delayHttp() {
      $httpProvider.responseInterceptors.push(["$q", "$timeout", function ($q, $timeout) {
        return function (promise) {
          var defer = $q.defer();
          $timeout(function () { promise.then(defer.resolve, defer.reject); }, 700);
          return defer.promise;
        };
      }]);
    }

    //delayHttp();
  })
  .run(function ($cookies, Restangular) {
    Restangular.addFullRequestInterceptor(function(element, operation, what, url, headers, query) {
      headers['X-CSRFToken'] = $cookies.csrftoken;
    });
  })
  .run(function ($rootScope, $location) {
    $rootScope.$on("$routeChangeError", function(event, current, previous, rejection) {
      console.log(rejection);

      if(rejection.status && rejection.status == 404) {
        $location.url('/404');
      }
    });
  })
  .value('cgBusyDefaults',{
    message:'Doing magic!',
    backdrop: true,
    templateUrl: 'components/shared/loading.html',
    delay: 200,
    minDuration: 50
  });
<commit_msg>proper 404 umbending @derLukers <3<commit_after>'use strict';

/**
 * @ngdoc overview
 * @name snotes30App
 * @description
 * # snotes30App
 *
 * Main module of the application.
 */
angular
  .module('snotes30App', [
    'ui.router',
    'ngAnimate',
    'ngCookies',
    'ngResource',
    'ngRoute',
    'ngSanitize',
    'angularSpectrumColorpicker',
    'cgBusy',
    'btford.socket-io',
    'restangular'
  ])
  .config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider, RestangularProvider, CONFIG) {
    var docResvolers = {
      'doc': ['DocumentService', '$stateParams', function (DocumentService, $stateParams) {
        return DocumentService.getByName($stateParams.name);
      }],
      'docname': ['$stateParams', function ($stateParams) {
        return $stateParams.name;
      }]
    };

    $urlRouterProvider.otherwise("/404");

    $stateProvider
      .state('livelist', {
        url: '/',
        templateUrl: 'components/livelist/livelist.html',
        controller: 'LiveListCtrl'
      })
      .state('document-edit', {
        url: '/doc/:name/',
        templateUrl: 'components/document/edit/document-edit.html',
        controller: 'DocumentEditCtrl',
        resolve: docResvolers
      })
      .state('document-readonly', {
        url: '/doc/:name/readonly/',
        templateUrl: 'components/document/readonly/document-readonly.html',
        controller: 'DocumentReadonlyCtrl',
        resolve: docResvolers
      })
      .state('document-readonly-pub', {
        url: '/doc/:name/readonly/:pub/',
        templateUrl: 'components/document/readonly/document-readonly.html',
        controller: 'DocumentReadonlyCtrl',
        resolve: docResvolers
      })
      .state('document-sighting', {
        url: '/doc/:name/sigh/',
        templateUrl: 'components/document/sighting/document-sighting.html',
        controller: 'DocumentSightingCtrl',
        resolve: docResvolers
      })
      .state('admin-board', {
        url: '/admin/',
        templateUrl: 'components/admin/board/board.html'
      })
      .state('admin-sighting', {
        url: '/admin/sigh/',
        templateUrl: 'components/admin/sighting/sighting.html'
      })
      .state('admin-importstatus', {
        url: '/admin/importstatus/',
        templateUrl: 'components/admin/importstatus/importstatus.html',
        controller: 'ImportStatusCtrl'
      })
      .state('404', {
        templateUrl: '404.html'
      })
      .state('403', {
        templateUrl: '403.html'
      })
      .state('archive', {
        url: '/archive/',
        templateUrl: 'components/archive/archive.html'
      })
      .state('archive-search', {
        url: '/archive/search/',
        templateUrl: 'components/archive/search.html'
      })
      .state('archive-podcast', {
        url: '/archive/podcast/',
        templateUrl: 'components/archive/podcast.html'
      })
      .state('faq', {
        url: '/faq/',
        templateUrl: 'components/static/faq.html'
      })
      .state('rules', {
        url: '/rules/',
        templateUrl: 'components/static/rules.html'
      })
      .state('community', {
        url: '/community/',
        templateUrl: 'components/static/community.html'
      })
      .state('privacy', {
        url: '/privacy/',
        templateUrl: 'components/static/privacy.html'
      })
      .state('donate', {
        url: '/donate/',
        templateUrl: 'components/static/donate.html'
      })
      .state('imprint', {
        url: '/imprint/',
        templateUrl: 'components/static/imprint.html'
      })
      .state('styleguide', {
        url: '/styleguide/',
        templateUrl: 'components/static/imprint.html'
      })
      .state('publicprofile', {
        url: '/profile/:username/',
        templateUrl: 'components/publicprofile/publicprofile.html',
        controller: 'PublicProfileCtrl'
      })
      .state('user-profile', {
        url: '/user/profile/',
        templateUrl: 'components/user/profile/profile.html',
        controller: 'UserProfileCtrl'
      })
      .state('user-upgrade', {
        url: '/user/upgrade/',
        templateUrl: 'components/user/upgrade/upgrade.html',
        controller: 'UserUpgradeCtrl'
      })
      .state('user-registration', {
        url: '/user/registration/',
        templateUrl: 'components/user/regcomplete/regcomplete.html'
      })
      .state('user-pwreset', {
        url: '/user/pwreset/:username/:token/',
        templateUrl: 'components/user/pwreset/pwreset.html',
        controller: 'UserPwResetCtrl'
      })
      .state('user-activate', {
        url: '/user/activate/:username/:token/',
        templateUrl: 'components/user/activate/activate.html',
        controller: 'UserActivateCtrl'
      })
      .state('user-confirm-email', {
        url: '/user/confirm/:username/:token/',
        templateUrl: 'components/user/activate/activate.html',
        controller: 'UserActivateCtrl'
      });

    $locationProvider.html5Mode(true);

    RestangularProvider.setBaseUrl(CONFIG.apiBaseUrl);
    RestangularProvider.setRequestSuffix('/');
    RestangularProvider.setDefaultHttpFields({
      withCredentials: true
    });

    RestangularProvider.setDefaultHeaders({'Content-Type': 'application/json'});

    RestangularProvider.addElementTransformer('soonepisodes', false, function(element) {
      element.create_date = new Date(element.create_date);
      element.date = new Date(element.date);
      element.podcast.create_date = new Date(element.podcast.create_date);
      if(element.document)
        element.document.create_date = new Date(element.document.create_date);
      return element;
    });

    RestangularProvider.addElementTransformer('importerlogs', false, function(element) {
      function patchTimeAttrs(obj) {
        obj.starttime = new Date(obj.starttime);
        obj.endtime = new Date(obj.endtime);
      }

      patchTimeAttrs(element);

      for(var i = 0; i < element.sources.length; i++) {
        var source = element.sources[i];
        patchTimeAttrs(source);

        for(var j = 0; j < source.jobs.length; j++) {
          var job = source.jobs[i];
          patchTimeAttrs(job);
        }
      }

      return element;
    });

    function delayHttp() {
      $httpProvider.responseInterceptors.push(["$q", "$timeout", function ($q, $timeout) {
        return function (promise) {
          var defer = $q.defer();
          $timeout(function () { promise.then(defer.resolve, defer.reject); }, 700);
          return defer.promise;
        };
      }]);
    }

    //delayHttp();
  })
  .run(function ($cookies, Restangular) {
    Restangular.addFullRequestInterceptor(function(element, operation, what, url, headers, query) {
      headers['X-CSRFToken'] = $cookies.csrftoken;
    });
  })
  .run(function ($rootScope, $state) {
    $rootScope.$on("$stateChangeError", function(event, toState, toParams, fromState, fromParams, error) {
      if(error.status === 404) {
        $state.go('404');
      }
    });
  })
  .value('cgBusyDefaults',{
    message:'Doing magic!',
    backdrop: true,
    templateUrl: 'components/shared/loading.html',
    delay: 200,
    minDuration: 50
  });
<|endoftext|>


<commit_before>import clamp from './clamp';

// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
const XYZMatrix = {
  X: [0.4124564, 0.3575761, 0.1804375],
  Y: [0.2126729, 0.7151522, 0.0721750],
  Z: [0.0193339, 0.1191920, 0.9503041]
};

const RGBMatrix = {
  R: [3.2404542, -1.5371385, -0.4985314],
  G: [-0.9692660, 1.8760108, 0.0415560],
  B: [0.0556434, -0.2040259, 1.0572252]
};

export const XYZtoRGB = (x, y, z) => {
  const N = 1 / 2.4;
  const M = 0.0031308;

  const r = x * RGBMatrix.R[0] + y * RGBMatrix.R[1] + z * RGBMatrix.R[2];
  const g = x * RGBMatrix.G[0] + y * RGBMatrix.G[1] + z * RGBMatrix.G[2];
  const b = x * RGBMatrix.B[0] + y * RGBMatrix.B[1] + z * RGBMatrix.B[2];

  return {
    r: clamp((r > M ? 1.055 * Math.pow(r, N) - 0.055 : 12.92 * r), 0, 1),
    g: clamp((g > M ? 1.055 * Math.pow(g, N) - 0.055 : 12.92 * g), 0, 1),
    b: clamp((b > M ? 1.055 * Math.pow(b, N) - 0.055 : 12.92 * b), 0, 1)
  };
}

export const RGBtoXYZ = (r, g, b) => {
  const N = 0.04045;
  const red = (r > N ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92);
  const green = (g > N ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92);
  const blue = (b > N ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92);

  return {
    x: red * XYZMatrix.X[0] + green * XYZMatrix.X[1] + blue * XYZMatrix.X[2],
    y: red * XYZMatrix.Y[0] + green * XYZMatrix.Y[1] + blue * XYZMatrix.Y[2],
    z: red * XYZMatrix.Z[0] + green * XYZMatrix.Z[1] + blue * XYZMatrix.Z[2]
  };
};

export const hueToRGB = (hue) => {
  const h = hue / 60;
  const mod = Math.floor(h);
  const i = (h === 6) ? 0 : (h - mod);

  return {
    r: Math.round([1, 1 - i, 0, 0, i, 1][mod] * 255),
    g: Math.round([i, 1, 1, 1 - i, 0, 0][mod] * 255),
    b: Math.round([0, 0, i, 1, 1, 1 - i][mod] * 255)
  };
};

export const RGBtoHSV = (r, g, b) => {
  let hue;
  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  const delta = max - min;

  if (delta === 0) {
    hue = 0;
  } else if (r === max) {
    hue = ((g - b) / delta) % 6;
  } else if (g === max) {
    hue = (b - r) / delta + 2;
  } else if (b === max) {
    hue = (r - g) / delta + 4;
  }

  hue = Math.round(hue * 60);

  if (hue < 0) {
    hue += 360;
  }

  return {
    h: hue,
    s: Math.round((max === 0 ? 0 : (delta / max)) * 100),
    v: Math.round(max / 255 * 100)
  };
};

export const HSVtoRGB = (h, s, v) => {
  const ns = s / 100;
  const nv = v / 100;
  const c = nv * ns;
  const nh = h / 60;
  const x = c * (1 - Math.abs(nh % 2 - 1));
  const m = nv - c;
  const p = parseInt(nh, 10);

  let rgb = [];

  switch (p) {
    case 0: rgb = [c, x, 0]; break;
    case 1: rgb = [x, c, 0]; break;
    case 2: rgb = [0, c, x]; break;
    case 3: rgb = [0, x, c]; break;
    case 4: rgb = [x, 0, c]; break;
    case 5: rgb = [c, 0, x]; break;
    default: rgb;
  }

  return {
    r: Math.round(255 * (rgb[0] + m)),
    g: Math.round(255 * (rgb[1] + m)),
    b: Math.round(255 * (rgb[2] + m))
  };
};

export const RGBtoHEX = (r, g, b, a) => {
  const hex = ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);

  if (a < 1 && a !== 1) {
    const val = Math.round(a * 255).toString(16);

    return hex + (val < 16 ? '0' : '') + val.toString(16);
  }

  return hex;
};

export const HEXtoRGB = (hex) => {
  let val = hex;
  if (hex.length === 3) {
    val = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
  }

  const color = {
    r: parseInt(val[0] + val[1], 16),
    g: parseInt(val[2] + val[3], 16),
    b: parseInt(val[4] + val[5], 16)
  };

  if (hex.length === 8) {
    color['a'] = parseInt(val[6] + val[7], 16);
  }

  return color;
};
<commit_msg>bug fix<commit_after>import clamp from './clamp';

// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
const XYZMatrix = {
  X: [0.4124564, 0.3575761, 0.1804375],
  Y: [0.2126729, 0.7151522, 0.0721750],
  Z: [0.0193339, 0.1191920, 0.9503041]
};

const RGBMatrix = {
  R: [3.2404542, -1.5371385, -0.4985314],
  G: [-0.9692660, 1.8760108, 0.0415560],
  B: [0.0556434, -0.2040259, 1.0572252]
};

export const XYZtoRGB = (x, y, z) => {
  const N = 1 / 2.4;
  const M = 0.0031308;

  const r = x * RGBMatrix.R[0] + y * RGBMatrix.R[1] + z * RGBMatrix.R[2];
  const g = x * RGBMatrix.G[0] + y * RGBMatrix.G[1] + z * RGBMatrix.G[2];
  const b = x * RGBMatrix.B[0] + y * RGBMatrix.B[1] + z * RGBMatrix.B[2];

  return {
    r: clamp((r > M ? 1.055 * Math.pow(r, N) - 0.055 : 12.92 * r), 0, 1),
    g: clamp((g > M ? 1.055 * Math.pow(g, N) - 0.055 : 12.92 * g), 0, 1),
    b: clamp((b > M ? 1.055 * Math.pow(b, N) - 0.055 : 12.92 * b), 0, 1)
  };
}

export const RGBtoXYZ = (r, g, b) => {
  const N = 0.04045;
  const red = (r > N ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92);
  const green = (g > N ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92);
  const blue = (b > N ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92);

  return {
    x: red * XYZMatrix.X[0] + green * XYZMatrix.X[1] + blue * XYZMatrix.X[2],
    y: red * XYZMatrix.Y[0] + green * XYZMatrix.Y[1] + blue * XYZMatrix.Y[2],
    z: red * XYZMatrix.Z[0] + green * XYZMatrix.Z[1] + blue * XYZMatrix.Z[2]
  };
};

export const hueToRGB = (hue) => {
  const h = hue / 60;
  const mod = Math.floor(h);
  const i = (h === 6) ? 0 : (h - mod);

  return {
    r: Math.round([1, 1 - i, 0, 0, i, 1][mod] * 255),
    g: Math.round([i, 1, 1, 1 - i, 0, 0][mod] * 255),
    b: Math.round([0, 0, i, 1, 1, 1 - i][mod] * 255)
  };
};

export const RGBtoHSV = (r, g, b) => {
  let hue;
  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  const delta = max - min;

  if (delta === 0) {
    hue = 0;
  } else if (r === max) {
    hue = ((g - b) / delta) % 6;
  } else if (g === max) {
    hue = (b - r) / delta + 2;
  } else if (b === max) {
    hue = (r - g) / delta + 4;
  }

  hue = Math.round(hue * 60);

  if (hue < 0) {
    hue += 360;
  }

  return {
    h: hue,
    s: Math.round((max === 0 ? 0 : (delta / max)) * 100),
    v: Math.round(max / 255 * 100)
  };
};

export const HSVtoRGB = (h, s, v) => {
  const ns = s / 100;
  const nv = v / 100;
  const c = nv * ns;
  const nh = h / 60;
  const x = c * (1 - Math.abs(nh % 2 - 1));
  const m = nv - c;
  const p = parseInt(nh, 10);

  let rgb = [];

  switch (p) {
    case 0: rgb = [c, x, 0]; break;
    case 1: rgb = [x, c, 0]; break;
    case 2: rgb = [0, c, x]; break;
    case 3: rgb = [0, x, c]; break;
    case 4: rgb = [x, 0, c]; break;
    case 5: rgb = [c, 0, x]; break;
    default: rgb;
  }

  return {
    r: Math.round(255 * (rgb[0] + m)),
    g: Math.round(255 * (rgb[1] + m)),
    b: Math.round(255 * (rgb[2] + m))
  };
};

export const RGBtoHEX = (r, g, b, a) => {
  const hex = ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);

  if (a < 1 && a !== 1) {
    const val = Math.round(a * 255).toString(16);

    return (val.length === 1) ? hex + '0' + val : hex + val;
  }

  return hex;
};

export const HEXtoRGB = (hex) => {
  let val = hex;
  if (hex.length === 3) {
    val = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
  }

  const color = {
    r: parseInt(val[0] + val[1], 16),
    g: parseInt(val[2] + val[3], 16),
    b: parseInt(val[4] + val[5], 16)
  };

  if (hex.length === 8) {
    color['a'] = +(parseInt(val[6] + val[7], 16) / 255).toFixed(2);
  }

  return color;
};
<|endoftext|>


<commit_before>// The following code is based off a toggle menu by @Bradcomp
// source: https://gist.github.com/Bradcomp/a9ef2ef322a8e8017443b626208999c1

(function() {
    var burger = document.querySelector('.nav-toggle');
    var menu = document.querySelector('.nav-menu');
    burger.addEventListener('click', function() {
        burger.classList.toggle('is-active');
        menu.classList.toggle('is-active');
    });
})();

$(document).ready(function(){
	$("#menu").on("click","a", function (event) {
		event.preventDefault();
		var id  = $(this).attr('href'),
			top = $(id).offset().top;
		$('body,html').animate({scrollTop: top}, 1000);
	});

    $(".show-auto-button").on('click', function () {
        console.log($('.auto-list').length);
        $('.auto-list').show();
        $('.show-auto-button').hide();
    });
});

$("#toyotaCamry-1").click(function() {
  $("#modalToyotaCamry").addClass("is-active");
});

$("#mercedesBenzE-1").click(function() {
  $("#modalMercedesBenzE").addClass("is-active");
});

$("#mercedesBenzSprinter-1").click(function() {
    $("#modalMercedesBenzSprinter").addClass("is-active");
});

$("#mercedesBenzS-1").click(function() {
  $("#modalMercedesBenzS").addClass("is-active");
});

$("#lexusLX570-1").click(function() {
  $("#modalLexusLX570").addClass("is-active");
});

$("#toyotaCamry-2").click(function() {
  $("#modalToyotaCamry").addClass("is-active");
});

$("#mercedesBenzE-2").click(function() {
  $("#modalMercedesBenzE").addClass("is-active");
});

$("#mercedesBenzS-2").click(function() {
  $("#modalMercedesBenzS").addClass("is-active");
});

$("#mercedesBenzSprinter-2").click(function() {
    $("#modalMercedesBenzSprinter").addClass("is-active");
});

$("#lexusLX570-2").click(function() {
  $("#modalLexusLX570").addClass("is-active");
});

$("#toyotaCamry-3").click(function() {
  $("#modalToyotaCamry").addClass("is-active");
});

$("#mercedesBenzE-3").click(function() {
  $("#modalMercedesBenzE").addClass("is-active");
});

$("#mercedesBenzS-3").click(function() {
  $("#modalMercedesBenzS").addClass("is-active");
});

$("#mercedesBenzSprinter-3").click(function() {
    $("#modalMercedesBenzSprinter").addClass("is-active");
});

$("#lexusLX570-3").click(function() {
  $("#modalLexusLX570").addClass("is-active");
});

$(".car-modal-close").click(function() {
   $(".modal").removeClass("is-active");
});
<commit_msg>add fix hidden block for bulma.js<commit_after>// The following code is based off a toggle menu by @Bradcomp
// source: https://gist.github.com/Bradcomp/a9ef2ef322a8e8017443b626208999c1

(function() {
    var burger = document.querySelector('.nav-toggle');
    var menu = document.querySelector('.nav-menu');
    burger.addEventListener('click', function() {
        burger.classList.toggle('is-active');
        menu.classList.toggle('is-active');
    });
})();

$(document).ready(function(){
	$("#menu").on("click","a", function (event) {
		event.preventDefault();
		var id  = $(this).attr('href'),
			top = $(id).offset().top;
		$('body,html').animate({scrollTop: top}, 1000);
	});

    $(".show-auto-button").on('click', function () {
        console.log($(this));
        $(this).siblings('.auto-list').show();
        $('.show-auto-button').hide();
    });
});

$("#toyotaCamry-1").click(function() {
  $("#modalToyotaCamry").addClass("is-active");
});

$("#mercedesBenzE-1").click(function() {
  $("#modalMercedesBenzE").addClass("is-active");
});

$("#mercedesBenzSprinter-1").click(function() {
    $("#modalMercedesBenzSprinter").addClass("is-active");
});

$("#mercedesBenzS-1").click(function() {
  $("#modalMercedesBenzS").addClass("is-active");
});

$("#lexusLX570-1").click(function() {
  $("#modalLexusLX570").addClass("is-active");
});

$("#toyotaCamry-2").click(function() {
  $("#modalToyotaCamry").addClass("is-active");
});

$("#mercedesBenzE-2").click(function() {
  $("#modalMercedesBenzE").addClass("is-active");
});

$("#mercedesBenzS-2").click(function() {
  $("#modalMercedesBenzS").addClass("is-active");
});

$("#mercedesBenzSprinter-2").click(function() {
    $("#modalMercedesBenzSprinter").addClass("is-active");
});

$("#lexusLX570-2").click(function() {
  $("#modalLexusLX570").addClass("is-active");
});

$("#toyotaCamry-3").click(function() {
  $("#modalToyotaCamry").addClass("is-active");
});

$("#mercedesBenzE-3").click(function() {
  $("#modalMercedesBenzE").addClass("is-active");
});

$("#mercedesBenzS-3").click(function() {
  $("#modalMercedesBenzS").addClass("is-active");
});

$("#mercedesBenzSprinter-3").click(function() {
    $("#modalMercedesBenzSprinter").addClass("is-active");
});

$("#lexusLX570-3").click(function() {
  $("#modalLexusLX570").addClass("is-active");
});

$(".car-modal-close").click(function() {
   $(".modal").removeClass("is-active");
});
<|endoftext|>


<commit_before>// node.js server for long polling message passing
// start with:
// node web.js
//
// POST requests
// -------------
// the post data is stored for the specified request path
// a version number is incremented if there is no version request parameter
// the version number is stored if it is in the request parameter
// if dataType=json is specified the post data is considered to be valid json e.g. {message:'yomama'}
//
// GET requests
// ------------
// the connection will be held open until there is a POST for the specified path
// to wait based on a version number it can be specified e.g. version=99
// if you request version=99 and the current message version is 50 then the connection will remain open until the next message
// if you request version=199 and the current message version is 150 then the message will be returned immediately
// to get the last message sent for the path specify version=0 since the current message is undoubtedly higher
// if callback=MYMETHOD is specified then the result is wrapped in a js function e.g. MYMETHOD( {body:'yomama', version:1}
// or MYMETHOD( {json:{method:'yomama'}, version:1}
// using a callback provides cross site support

var http = require('http');
var fs   = require('fs');

var PORT = process.env.PORT || 8000;
var TOKEN = process.env.TOKEN || 'TOKEN';
var MAX_MESSAGE_LENGTH = 2048;

require('./json2.js');
var Broadcast = require('./broadcast.js');

var server = http.createServer(function (request, response) {

  var parsed = require('url').parse( request.url, true );
  var path = parsed.pathname;
  var params = parsed.query;
  var version = parseInt(params.version);
  var callback = params.callback;
  var dataType = params.dataType;

  if( request.method == "GET" ) {
    //get the current message for the path or wait for the next one
    console.log( "GET " + path);
    if( ['/test.html', '/json2.js','/noodle.js'].indexOf( path ) >= 0) {
        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write(fs.readFileSync(__dirname + path, 'utf8')); // <--- add this line
        response.end();
    } else {
        response.writeHead(200, {'Content-Type': 'text/plain'});
  	    Broadcast.add( path, version, response, callback );
    }
  } else {
    //post data broadcasts to any listeners for the path
    console.log( "POST " + path);
    if( params.token != TOKEN ) {
        response.writeHead( 403, {'Content-Type': 'text/plain'});
        response.end( "not authorized for posting messages without a valid 'token'\n" );
        return;
    }
    var body = '';
    request.on( 'data', function(data) {
      if( body.length > MAX_MESSAGE_LENGTH ) { return; }
      body += data.toString();
      console.log( body );
      if( body.length > MAX_MESSAGE_LENGTH ) {
        response.writeHead( 406, {'Content-Type': 'text/plain'});
        response.end( "message length exceeds " + MAX_MESSAGE_LENGTH + " bytes\n" );
      }
    });
    request.on( 'end', function() {
      //once the whole message has been received then broadcast it
      Broadcast.send( path, body, dataType, version );
      response.end('success\n');
      console.log( 'end' );
    });
  }
});
server.listen(PORT, "0.0.0.0");
console.log("Listening on " + PORT);

<commit_msg>rename noodle.js to long_noodle.js<commit_after>// node.js server for long polling message passing
// start with:
// node web.js
//
// POST requests
// -------------
// the post data is stored for the specified request path
// a version number is incremented if there is no version request parameter
// the version number is stored if it is in the request parameter
// if dataType=json is specified the post data is considered to be valid json e.g. {message:'yomama'}
//
// GET requests
// ------------
// the connection will be held open until there is a POST for the specified path
// to wait based on a version number it can be specified e.g. version=99
// if you request version=99 and the current message version is 50 then the connection will remain open until the next message
// if you request version=199 and the current message version is 150 then the message will be returned immediately
// to get the last message sent for the path specify version=0 since the current message is undoubtedly higher
// if callback=MYMETHOD is specified then the result is wrapped in a js function e.g. MYMETHOD( {body:'yomama', version:1}
// or MYMETHOD( {json:{method:'yomama'}, version:1}
// using a callback provides cross site support

var http = require('http');
var fs   = require('fs');

var PORT = process.env.PORT || 8000;
var TOKEN = process.env.TOKEN || 'TOKEN';
var MAX_MESSAGE_LENGTH = 2048;

require('./json2.js');
var Broadcast = require('./broadcast.js');

var server = http.createServer(function (request, response) {

  var parsed = require('url').parse( request.url, true );
  var path = parsed.pathname;
  var params = parsed.query;
  var version = parseInt(params.version);
  var callback = params.callback;
  var dataType = params.dataType;

  if( request.method == "GET" ) {
    //get the current message for the path or wait for the next one
    console.log( "GET " + path);
    if( ['/test.html', '/json2.js','/long_noodle.js'].indexOf( path ) >= 0) {
        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write(fs.readFileSync(__dirname + path, 'utf8')); // <--- add this line
        response.end();
    } else {
        response.writeHead(200, {'Content-Type': 'text/plain'});
  	    Broadcast.add( path, version, response, callback );
    }
  } else {
    //post data broadcasts to any listeners for the path
    console.log( "POST " + path);
    if( params.token != TOKEN ) {
        response.writeHead( 403, {'Content-Type': 'text/plain'});
        response.end( "not authorized for posting messages without a valid 'token'\n" );
        return;
    }
    var body = '';
    request.on( 'data', function(data) {
      if( body.length > MAX_MESSAGE_LENGTH ) { return; }
      body += data.toString();
      console.log( body );
      if( body.length > MAX_MESSAGE_LENGTH ) {
        response.writeHead( 406, {'Content-Type': 'text/plain'});
        response.end( "message length exceeds " + MAX_MESSAGE_LENGTH + " bytes\n" );
      }
    });
    request.on( 'end', function() {
      //once the whole message has been received then broadcast it
      Broadcast.send( path, body, dataType, version );
      response.end('success\n');
      console.log( 'end' );
    });
  }
});
server.listen(PORT, "0.0.0.0");
console.log("Listening on " + PORT);

<|endoftext|>


<commit_before>'use strict';
var fs = require('fs'),
	readline = require('readline'),
	MongoClient = require('mongodb').MongoClient,
	assert = require('assert');

function getDictSize(dict){
	return new Promise(function(resolve, reject){
		var dictSize = 0;
		var rl = readline.createInterface({
			input: fs.createReadStream(dict),
			output: process.stdout,
			terminal: false
		});
		rl.on('line', function(line){
			dictSize += 1;
		});
		rl.on('close', function(){
			resolve(dictSize);
		})
	});
}

function getCurrCode(mongoURI){
	return new Promise(function (resolve, reject){
		MongoClient.connect(mongoURI , function(err, db){
			if(err){
				console.log("GET CURRCODE: OPENING", err);
				reject(err);
			} else {
					var collection = db.collection('codeCounter');
					collection.find().toArray(function(err, res) {
					if(err){
						console.log("GET CURRCODE: READING", err);
					} else{
						console.log(res[0]);
						resolve(res[0].codeCounter);
						//
					}
					})
				}
			});
	});
}

/*limitation, if codeCounter exceeds [n,n,n,n], where n
is dictSize, counter does not change*/
function makeNewCode(codeCounter, dictSize){
	/*
	codeCounter is an array counting current permutation of game code
	returned by the currcode promise

	*/

	/*!!! still need to return code Counter after change, idk how to do that
	for promises*/
	return new Promise(function (resolve, reject){
		var k = 0;
		k ++;
		k ++;
		k ++;
		resolve(k);
		var i;
		for(i = (codeCounter.length - 1); i > 0; i--){
			if((codeCounter[i] + 1) > (dictSize - 1) && (i - 1 >= 0)){
				codeCounter[i] = 0;
				codeCounter[i - 1] += 1;
			}
		}
	});
}

/*needs the mongoURI and the local dictionary JSON file path
chose to take in mongoURI rather than get from process because this is module*/
module.exports.genCode = function(dict, mongoURI){
	return new Promise(function(resolve, reject){
		var makeCode;
		var getCode;
		getDictSize(dict).then(function(dictSize){
			makeCode = makeNewCode.bind(null, dictSize);
			getCode = getCurrCode.bind(mongoURI);
		})
		.then(getCurrCode)
		.then(makeCode);
	});
}
<commit_msg>Fixed Danny's Code<commit_after>'use strict';
var fs = require('fs'),
	readline = require('readline'),
	MongoClient = require('mongodb').MongoClient,
	assert = require('assert');

var dict = function(){
	fs.readFile('file', 'utf8', function (err, data) {
		if(err) throw err;
		return JSON.parse(data);
	}
	}()
/*function getDictSize(dict){
	var rawFile = new XMLHttpRequest();
	rawFile.open("GET", filename, false);
	rawFile.onreadystatechange = function () {
		if (rawFile.readyState === 4) {
			if (rawFile.status === 200 || rawFile.status == 0){
				dict = JSON.parse
			}
		}
	}
	return new Promise(function(resolve, reject){
		var dictSize = 0;
		var rl = readline.createInterface({
			input: fs.createReadStream(dict),
			output: process.stdout,
			terminal: false
		});
		rl.on('line', function(line){
			dictSize += 1;
		});
		rl.on('close', function(){
			resolve(dictSize);
		})
	});
}*/

function getCurrCode(mongoURI){
	return new Promise(function (resolve, reject){
		MongoClient.connect(mongoURI , function(err, db){
			if(err){
				console.log("GET CURRCODE: OPENING", err);
				reject(err);
			} else {
					var collection = db.collection('codeCounter');
					collection.find().toArray(function(err, res) {
					if(err){
						console.log("GET CURRCODE: READING", err);
					} else{
						console.log(res[0]);
						resolve(res[0].codeCounter);
						//
					}
					})
				}
			});
	});
}

/*limitation, if codeCounter exceeds [n,n,n,n], where n
is dictSize, counter does not change*/
function makeNewCode(codeCounter, dictSize){
		var k = 0;
		var i;
		for(i = (codeCounter.length - 1); i > 0; i--){
			if((codeCounter[i] + 1) > (dictSize - 1) && (i - 1 >= 0)){
				codeCounter[i] = 0;
				codeCounter[i - 1] += 1;
			}
		}
		return codeCounter;
	});
}

/*needs the mongoURI and the local dictionary JSON file path
chose to take in mongoURI rather than get from process because this is module*/
module.exports.genCode = function(dict, mongoURI){
	return new Promise(function(resolve, reject){
		var makeCode;
		makeCode = makeNewCode.bind(null, dict.lengt);
		resolve(getCurrCode(mongoURI).then(makeCode));
	});
}
<|endoftext|>


<commit_before>(function () {
  var moveContainer = '.move-container';
  var formulaContainer = '.formula-container';
  var compensationAmount = moveContainer + ' .compensation .amount';
  var defaultValue = '--';

  var CONTRACT_TYPE_EMPLOYEE = 'employee_factor';
  var CONTRACT_TYPE_CONTRACTOR = 'contractor_factor';

  // Dropdown Core functionality

  var setDropdown = function(e) {
    var $selected = $(e.currentTarget);

    if ($selected.hasClass('filter-container')) {
      return;
    }

    var key = $selected.find('.key').text();
    var value = $selected.find('.value').text();
    var displayValue = $selected.find('.display-value').text();
    var $title = $selected.parents('.dropdown').find('.title');
    var $subtitle = $selected.parents('.dropdown').find('.subtitle');

    $title.text(key);
    $title.data('selected', value || key);

    if (value) {
      $subtitle.text(value);
    } else if (displayValue) {
      $subtitle.text(displayValue);
    }
  }

  var isFullyVisible = function(el, parentEl) {
    var parentHeight = parentEl.height() - el.height(); // Make sure last item is not partially visible
    var elTop = el.position().top;
    var elBottom = elTop + (el.height());

    return (elBottom > 0 && elTop < parentHeight);
  }

  this.MoveCalculator = (function() {
    function MoveCalculator() {
      this.bindEvents();
    }

    MoveCalculator.prototype.bindEvents = function() {
      var $currentCountryDropdownContainer = $(moveContainer + ' .current-country');
      var $currentCountryDropdown = $currentCountryDropdownContainer.find('li:not(.filter-container)');
      var $currentCountryFilter = $currentCountryDropdownContainer.find('.js-country-filter');
      var $newCountryDropdownContainer = $(moveContainer + ' .new-country');
      var $newCountryDropdown = $newCountryDropdownContainer.find('li:not(.filter-container)');
      var $newCountryFilter = $newCountryDropdownContainer.find('.js-country-filter');

      var $currentCityDropdownContainer = $(moveContainer + ' .current-city');
      var $currentCityDropdown = $currentCityDropdownContainer.find('li:not(.filter-container)');
      var $currentCityFilter = $currentCityDropdownContainer.find('.js-city-filter');
      var $newCityDropdownContainer = $(moveContainer + ' .new-city');
      var $newCityDropdown = $newCityDropdownContainer.find('li:not(.filter-container)');
      var $newCityFilter = $newCityDropdownContainer.find('.js-city-filter');

      var $currentSalary = $(moveContainer + ' .current-salary input');

      // Set selected dropdown value
      $currentCountryDropdown.on('click', setDropdown);
      $currentCityDropdown.on('click', setDropdown);
      $newCountryDropdown.on('click', setDropdown);
      $newCityDropdown.on('click', setDropdown);

      // Render compensation
      $currentSalary.on('keyup', this.inputSalary.bind(this));
      // $countryDropdown.on('click', this.render.bind(this));
      // $cityDropdown.on('click', this.render.bind(this));

      // Unlock and filter city dropdown
      $currentCountryDropdown.on('click', this.filterCityDropdown.bind(this));
      $currentCountryDropdown.on('click', this.resetCityDropdown.bind(this));
      $newCountryDropdown.on('click', this.filterCityDropdown.bind(this));
      $newCountryDropdown.on('click', this.resetCityDropdown.bind(this));

      // Render Formula

      // Highlighting for Filter
      $currentCountryDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));
      $currentCityDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));
      $newCountryDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));
      $newCityDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));

      // Filtering
      $currentCountryFilter.on('keyup', this.search.bind(null, {
        dropdown: 'current-country'
      }));
      $newCountryFilter.on('keyup', this.search.bind(null, {
        dropdown: 'new-country'
      }));

      $currentCityFilter.on('keyup', this.search.bind(this, {
        dropdown: 'current-city',
        filter: 'country',
      }));
      $newCityFilter.on('keyup', this.search.bind(this, {
        dropdown: 'new-city',
        filter: 'country',
      }));

      $currentSalary.on('keypress', this.preventLetters.bind(this));

      $currentCountryDropdownContainer.on('shown.bs.dropdown', this.focusInput);
      $newCountryDropdownContainer.on('shown.bs.dropdown', this.focusInput);
      $currentCityDropdownContainer.on('shown.bs.dropdown', this.focusInput);
      $newCityDropdownContainer.on('shown.bs.dropdown', this.focusInput);
    }

    MoveCalculator.prototype.focusInput = function(e) {
      $(e.target).find('.filter-input').focus();
    }

    MoveCalculator.prototype.search = function(data, e) {
      var text = e.target.value.toLowerCase();
      var filterValue = null;
      var $items = $(moveContainer + ' .' + data.dropdown + ' li:not(.filter-container)');

      if (data.filter) {
        filterValue = this.getElementValues()[data.filter];
      }

      $items.each(function(index, element) {
        var $element = $(element);
        var value = $element.children('.key').text().toLowerCase();
        var searchableElement = true;

        if (data.filter && filterValue) {
          searchableElement = $element.data(data.filter) === filterValue;
        }

        if (value.indexOf(text) == -1 || !searchableElement) {
          $element.addClass('hidden');
        } else {
          $element.removeClass('hidden');
        }
      });
    }

    MoveCalculator.prototype.highlightDropdownItem = function(e) {
      var $this = $(e.currentTarget);
      var listContainer = $this.find('.dropdown-menu.dropdown-scroll');
      var list = $this.find('li:not(.hidden):not(.filter-container)');
      var nextLi, prevLi, focusedLi;

      if ($this.hasClass('open')) {
        if ((e.keyCode === 38 || e.keyCode === 40) &&
            !list.filter('li.is-focused').length) {
          list.first().addClass('is-focused');
          return;
        }

        if (e.keyCode === 38 && !list.first().hasClass('is-focused')) { // Up
          prevLi = list
                    .filter('li.is-focused')
                    .prevAll('li:not(.hidden)').first();

          list.removeClass('is-focused');

          if (prevLi[0] === list.first()[0]) {
            list.first().addClass('is-focused');
          } else {
            prevLi.addClass('is-focused');
          }
        } else if (e.keyCode === 40 && !list.last().hasClass('is-focused')) { // Down
          nextLi = list
                    .filter('li.is-focused')
                    .nextAll('li:not(.hidden)').first();

          list.removeClass('is-focused');

          if (nextLi[0] === list.last()[0]) {
            list.last().addClass('is-focused');
          } else {
            nextLi.addClass('is-focused');
          }
        } else if (e.keyCode === 9) { // Tab keyCode
          $this.removeClass('open');
        } else if (e.keyCode === 13) { // Enter / Return
          // Triggering click directly while keydown is active
          // doesn't hide dropdown, this workaround does. Need better way :/
          setTimeout(function() { list.filter('li.is-focused').trigger('click'); });
        }

        focusedLi = list.filter('li.is-focused');
        if (focusedLi.length && list.first()[0] !== focusedLi[0]) {
          if (!isFullyVisible(focusedLi, listContainer)) {
            listContainer.scrollTop(list.index(focusedLi) * focusedLi.outerHeight());
          }
        }
        else {
          listContainer.scrollTop(0);
        }
      }
    }

    MoveCalculator.prototype.preventLetters = function(e) {
      if (e.which < 48 || e.which > 57) {
        e.preventDefault();
      }
    }

    MoveCalculator.prototype.inputSalary = function(e) {
      $currentSalary = $(formulaContainer + ' .currentSalary');
      $currentSalary.find('.value').text('$'+ $(e.currentTarget).val());
    }
    // Custom dropdown functionality

    MoveCalculator.prototype.filterCityDropdown = function(e) {
      var $countryDropdown = $(e.currentTarget).parents('.dropdown');
      var selectedCountry, $cities;

      if ($countryDropdown.hasClass('current-country')) {
        selectedCountry = this.getElementValues().currentCountry;
        $cities = $(moveContainer + ' .current-city li:not(.filter-container)');
      } else if ($countryDropdown.hasClass('new-country')) {
        selectedCountry = this.getElementValues().newCountry;
        $cities = $(moveContainer + ' .new-city li:not(.filter-container)');
      }

      $cities.each(function(index, element) {
        var $element = $(element);
        if ($element.data('country') === selectedCountry) {
          $element.removeClass('hidden');
        } else {
          $element.addClass('hidden');
        }
      });
    }

    MoveCalculator.prototype.resetCityDropdown = function(e) {
      var $countryDropdown = $(e.currentTarget).parents('.dropdown');
      var $cityDropdownBtn, $filterInput;

      if ($countryDropdown.hasClass('current-country')) {
        $cityDropdownBtn = $(moveContainer + ' .current-city .btn');
        $filterInput = $('.current-city .js-city-filter');
      } else if ($countryDropdown.hasClass('new-country')) {
        $cityDropdownBtn = $(moveContainer + ' .new-city .btn');
        $filterInput = $('.new-city .js-city-filter');
      }

      var $cityDropdownTitle = $cityDropdownBtn.find('.title');
      $filterInput.val('');
      $cityDropdownBtn.removeClass('disabled');
      $cityDropdownTitle.text('--');
      $cityDropdownTitle.data('selected', '');
      $cityDropdownBtn.find('.subtitle').text('');
      this.renderInvalidCompensation();
      this.renderFormula();
    }

    // Render functions

    MoveCalculator.prototype.getData = function() {
      var deferred = jQuery.Deferred();

      $.get('/salary/data.json').then(function(data) {
        // Cache the data
        this.data = data;
        deferred.resolve();
      }.bind(this));

      return deferred.promise();
    }

    MoveCalculator.prototype.render = function() {
      var input = this.getElementValues();

      function renderData() {
        if (input.currentCountry && input.currenCity &&
            input.newCountry && input.newCity &&
            input.salary) {
          this.renderCompensation(input);
        } else {
          this.renderInvalidCompensation();
        }

        this.renderFormula();
        this.renderContractType();
      }

      if (this.data) {
        renderData.call(this);
      } else {
        $.when(this.getData()).then(function() {
            renderData.call(this);
          }.bind(this),
          this.renderInvalidCompensation.bind(this));
      }
    }

    MoveCalculator.prototype.renderCompensation = function(input) {
      var numbeo = this.data.numbeo;
      var contracts = this.data.contractTypes;

      var levelIndex = parseFloat(input.level);
      var benchmark = input.salary;

      var rentIndex = this.calculateRentIndex(input.city, input.country);
      var contract = this.calculateContractFactor(input.country);

      if (!rentIndex) {
        //When the city and country combination selected do not exist
        return this.renderInvalidCompensation();
      }

      var min = this.calculateCompensation(benchmark, rentIndex, levelIndex, contract.factor, input.experience.min);
      var max = this.calculateCompensation(benchmark, rentIndex, levelIndex, contract.factor, input.experience.max);
      $(compensationAmount).text(this.formatAmount(min) + ' - ' + this.formatAmount(max) + ' USD');
    }

    MoveCalculator.prototype.renderContractType = function() {
      var country = this.getElementValues().country;
      var contract = this.calculateContractFactor(country);
      var $container = $('.contract-type-container');

      if (contract.type === CONTRACT_TYPE_EMPLOYEE) {
        $container.find('.grammer').text('an');
        $container.find('.company-type').text('Inc.');
        $container.find('.contract-type').text('employee');
      } else {
        $container.find('.grammer').text('a');
        $container.find('.company-type').text('BV.');
        $container.find('.contract-type').text('contractor');
      }

      $container.removeClass('hidden');
    }

    MoveCalculator.prototype.renderFormula = function() {
      var values = this.getElementValues();
      var rentIndex = this.calculateRentIndex(values.city, values.country);
      var contract = this.calculateContractFactor(values.country);
      var experience = values.experience;

      $('.formula .rentIndex .value').text(rentIndex ? rentIndex : defaultValue);
      $('.formula .contractType .value').text(contract.hasOwnProperty('factor') ? contract.factor.toFixed(2) : defaultValue);
    }

    MoveCalculator.prototype.renderInvalidCompensation = function() {
      $(compensationAmount).text(defaultValue);
    }

    // Pure functions

    MoveCalculator.prototype.getElementValues = function() {
      return {
        salary: $(moveContainer + ' .current-salary input').val() || '',
        currentCountry: $(moveContainer + ' .current-country .title').data('selected') || '',
        currentCity: $(moveContainer + ' .current-city .title').data('selected') || '',
        newCountry: $(moveContainer + ' .new-country .title').data('selected') || '',
        newCity: $(moveContainer + ' .new-city .title').data('selected') || ''
      };
    }

    MoveCalculator.prototype.calculateRentIndex = function(city, country) {
      if (this.data && this.data.numbeo) {
        var locationData = this.data.numbeo.find(function(o) {
          return o.country === country && o.city === city;
        });

        return locationData ? parseFloat((locationData.rentIndex * 0.01).toFixed(2)) : 0;
      }

      // Rent Index will never be zero, safe to return as error value
      return 0;
    }

    MoveCalculator.prototype.calculateContractFactor = function(country) {
      var contract = {};

      if (this.data && this.data.contractTypes) {
        var defaultContractorFactor = this.data.contractTypes.find(function (o) {
          return o.country === '*';
        }).contractor_factor;

        var contractType = this.data.contractTypes.find(function(o) {
          return o.country === country
        });

        if (contractType && contractType.hasOwnProperty(CONTRACT_TYPE_EMPLOYEE)) {
          contract.type = CONTRACT_TYPE_EMPLOYEE;
          contract.factor = contractType[CONTRACT_TYPE_EMPLOYEE];
        } else if (contractType) {
          contract.type = CONTRACT_TYPE_CONTRACTOR;
          contract.factor = contractType[CONTRACT_TYPE_CONTRACTOR];
        } else {
          contract.type = CONTRACT_TYPE_CONTRACTOR;
          contract.factor = defaultContractorFactor;
        }
      }

      return contract;
    }

    MoveCalculator.prototype.calculateCompensation = function(benchmark, rentIndex, levelIndex, contractType, experienceFactor) {
      return Math.round(benchmark * (rentIndex + 0.25) * levelIndex * contractType * experienceFactor);
    };

    MoveCalculator.prototype.formatAmount = function(amount) {
      return '$' + amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    }

    return MoveCalculator;
  })();

  new MoveCalculator();
})();
<commit_msg>Fix render functions and formula for calculating Move<commit_after>(function () {
  var moveContainer = '.move-container';
  var formulaContainer = '.formula-container';
  var compensationAmount = moveContainer + ' .compensation .amount';
  var defaultValue = '--';

  var CONTRACT_TYPE_EMPLOYEE = 'employee_factor';
  var CONTRACT_TYPE_CONTRACTOR = 'contractor_factor';

  // Dropdown Core functionality

  var setDropdown = function(e) {
    var $selected = $(e.currentTarget);

    if ($selected.hasClass('filter-container')) {
      return;
    }

    var key = $selected.find('.key').text();
    var value = $selected.find('.value').text();
    var displayValue = $selected.find('.display-value').text();
    var $title = $selected.parents('.dropdown').find('.title');
    var $subtitle = $selected.parents('.dropdown').find('.subtitle');

    $title.text(key);
    $title.data('selected', value || key);

    if (value) {
      $subtitle.text(value);
    } else if (displayValue) {
      $subtitle.text(displayValue);
    }
  }

  var isFullyVisible = function(el, parentEl) {
    var parentHeight = parentEl.height() - el.height(); // Make sure last item is not partially visible
    var elTop = el.position().top;
    var elBottom = elTop + (el.height());

    return (elBottom > 0 && elTop < parentHeight);
  }

  this.MoveCalculator = (function() {
    function MoveCalculator() {
      this.bindEvents();
    }

    MoveCalculator.prototype.bindEvents = function() {
      var $currentCountryDropdownContainer = $(moveContainer + ' .current-country');
      var $currentCountryDropdown = $currentCountryDropdownContainer.find('li:not(.filter-container)');
      var $currentCountryFilter = $currentCountryDropdownContainer.find('.js-country-filter');
      var $newCountryDropdownContainer = $(moveContainer + ' .new-country');
      var $newCountryDropdown = $newCountryDropdownContainer.find('li:not(.filter-container)');
      var $newCountryFilter = $newCountryDropdownContainer.find('.js-country-filter');

      var $currentCityDropdownContainer = $(moveContainer + ' .current-city');
      var $currentCityDropdown = $currentCityDropdownContainer.find('li:not(.filter-container)');
      var $currentCityFilter = $currentCityDropdownContainer.find('.js-city-filter');
      var $newCityDropdownContainer = $(moveContainer + ' .new-city');
      var $newCityDropdown = $newCityDropdownContainer.find('li:not(.filter-container)');
      var $newCityFilter = $newCityDropdownContainer.find('.js-city-filter');

      var $currentSalary = $(moveContainer + ' .current-salary input');

      // Set selected dropdown value
      $currentCountryDropdown.on('click', setDropdown);
      $currentCityDropdown.on('click', setDropdown);
      $newCountryDropdown.on('click', setDropdown);
      $newCityDropdown.on('click', setDropdown);

      // Render compensation
      $currentSalary.on('keyup', this.render.bind(this));
      $currentCountryDropdown.on('click', this.render.bind(this));
      $currentCityDropdown.on('click', this.render.bind(this));
      $newCountryDropdown.on('click', this.render.bind(this));
      $newCityDropdown.on('click', this.render.bind(this));

      // Unlock and filter city dropdown
      $currentCountryDropdown.on('click', this.filterCityDropdown.bind(this));
      $currentCountryDropdown.on('click', this.resetCityDropdown.bind(this));
      $newCountryDropdown.on('click', this.filterCityDropdown.bind(this));
      $newCountryDropdown.on('click', this.resetCityDropdown.bind(this));

      // Render Formula

      // Highlighting for Filter
      $currentCountryDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));
      $currentCityDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));
      $newCountryDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));
      $newCityDropdownContainer.on('keydown', this.highlightDropdownItem.bind(this));

      // Filtering
      $currentCountryFilter.on('keyup', this.search.bind(null, {
        dropdown: 'current-country'
      }));
      $newCountryFilter.on('keyup', this.search.bind(null, {
        dropdown: 'new-country'
      }));

      $currentCityFilter.on('keyup', this.search.bind(this, {
        dropdown: 'current-city',
        filter: 'currentCountry',
      }));
      $newCityFilter.on('keyup', this.search.bind(this, {
        dropdown: 'new-city',
        filter: 'newCountry',
      }));

      $currentSalary.on('keypress', this.preventLetters.bind(this));

      $currentCountryDropdownContainer.on('shown.bs.dropdown', this.focusInput);
      $newCountryDropdownContainer.on('shown.bs.dropdown', this.focusInput);
      $currentCityDropdownContainer.on('shown.bs.dropdown', this.focusInput);
      $newCityDropdownContainer.on('shown.bs.dropdown', this.focusInput);
    }

    MoveCalculator.prototype.focusInput = function(e) {
      $(e.target).find('.filter-input').focus();
    }

    MoveCalculator.prototype.search = function(data, e) {
      var text = e.target.value.toLowerCase();
      var filterValue = null;
      var $items = $(moveContainer + ' .' + data.dropdown + ' li:not(.filter-container)');

      if (data.filter) {
        filterValue = this.getElementValues()[data.filter];
      }

      $items.each(function(index, element) {
        var $element = $(element);
        var value = $element.children('.key').text().toLowerCase();
        var searchableElement = true;

        if (data.filter && filterValue) {
          searchableElement = $element.data(data.filter.toLowerCase()) === filterValue;
        }

        if (value.indexOf(text) == -1 || !searchableElement) {
          $element.addClass('hidden');
        } else {
          $element.removeClass('hidden');
        }
      });
    }

    MoveCalculator.prototype.highlightDropdownItem = function(e) {
      var $this = $(e.currentTarget);
      var listContainer = $this.find('.dropdown-menu.dropdown-scroll');
      var list = $this.find('li:not(.hidden):not(.filter-container)');
      var nextLi, prevLi, focusedLi;

      if ($this.hasClass('open')) {
        if ((e.keyCode === 38 || e.keyCode === 40) &&
            !list.filter('li.is-focused').length) {
          list.first().addClass('is-focused');
          return;
        }

        if (e.keyCode === 38 && !list.first().hasClass('is-focused')) { // Up
          prevLi = list
                    .filter('li.is-focused')
                    .prevAll('li:not(.hidden)').first();

          list.removeClass('is-focused');

          if (prevLi[0] === list.first()[0]) {
            list.first().addClass('is-focused');
          } else {
            prevLi.addClass('is-focused');
          }
        } else if (e.keyCode === 40 && !list.last().hasClass('is-focused')) { // Down
          nextLi = list
                    .filter('li.is-focused')
                    .nextAll('li:not(.hidden)').first();

          list.removeClass('is-focused');

          if (nextLi[0] === list.last()[0]) {
            list.last().addClass('is-focused');
          } else {
            nextLi.addClass('is-focused');
          }
        } else if (e.keyCode === 9) { // Tab keyCode
          $this.removeClass('open');
        } else if (e.keyCode === 13) { // Enter / Return
          // Triggering click directly while keydown is active
          // doesn't hide dropdown, this workaround does. Need better way :/
          setTimeout(function() { list.filter('li.is-focused').trigger('click'); });
        }

        focusedLi = list.filter('li.is-focused');
        if (focusedLi.length && list.first()[0] !== focusedLi[0]) {
          if (!isFullyVisible(focusedLi, listContainer)) {
            listContainer.scrollTop(list.index(focusedLi) * focusedLi.outerHeight());
          }
        }
        else {
          listContainer.scrollTop(0);
        }
      }
    }

    MoveCalculator.prototype.preventLetters = function(e) {
      if (e.which < 48 || e.which > 57) {
        e.preventDefault();
      }
    }

    MoveCalculator.prototype.inputSalary = function(e) {
      $currentSalary = $(formulaContainer + ' .currentSalary');
      $currentSalary.find('.value').text('$'+ $(e.currentTarget).val());
    }
    // Custom dropdown functionality

    MoveCalculator.prototype.filterCityDropdown = function(e) {
      var $countryDropdown = $(e.currentTarget).parents('.dropdown');
      var selectedCountry, $cities, dataAttr;

      if ($countryDropdown.hasClass('current-country')) {
        selectedCountry = this.getElementValues().currentCountry;
        dataAttr = 'currentcountry';
        $cities = $(moveContainer + ' .current-city li:not(.filter-container)');
      } else if ($countryDropdown.hasClass('new-country')) {
        selectedCountry = this.getElementValues().newCountry;
        dataAttr = 'newcountry';
        $cities = $(moveContainer + ' .new-city li:not(.filter-container)');
      }

      $cities.each(function(index, element) {
        var $element = $(element);
        if ($element.data(dataAttr) === selectedCountry) {
          $element.removeClass('hidden');
        } else {
          $element.addClass('hidden');
        }
      });
    }

    MoveCalculator.prototype.resetCityDropdown = function(e) {
      var $countryDropdown = $(e.currentTarget).parents('.dropdown');
      var $cityDropdownBtn, $filterInput;

      if ($countryDropdown.hasClass('current-country')) {
        $cityDropdownBtn = $(moveContainer + ' .current-city .btn');
        $filterInput = $('.current-city .js-city-filter');
      } else if ($countryDropdown.hasClass('new-country')) {
        $cityDropdownBtn = $(moveContainer + ' .new-city .btn');
        $filterInput = $('.new-city .js-city-filter');
      }

      var $cityDropdownTitle = $cityDropdownBtn.find('.title');
      $filterInput.val('');
      $cityDropdownBtn.removeClass('disabled');
      $cityDropdownTitle.text('--');
      $cityDropdownTitle.data('selected', '');
      $cityDropdownBtn.find('.subtitle').text('');
      this.renderInvalidCompensation();
      this.renderFormula();
    }

    // Render functions

    MoveCalculator.prototype.getData = function() {
      var deferred = jQuery.Deferred();

      $.get('/salary/data.json').then(function(data) {
        // Cache the data
        this.data = data;
        deferred.resolve();
      }.bind(this));

      return deferred.promise();
    }

    MoveCalculator.prototype.render = function() {
      var input = this.getElementValues();

      function renderData() {
        if (input.currentCountry && input.currentCity &&
            input.newCountry && input.newCity &&
            input.salary) {
          this.renderCompensation(input);
          this.renderContractType();
        } else {
          this.renderInvalidCompensation();
        }

        this.renderFormula();
      }

      if (this.data) {
        renderData.call(this);
      } else {
        $.when(this.getData()).then(function() {
            renderData.call(this);
          }.bind(this),
          this.renderInvalidCompensation.bind(this));
      }
    }

    MoveCalculator.prototype.renderCompensation = function(input) {
      var numbeo = this.data.numbeo;
      var contracts = this.data.contractTypes;

      var salary = input.salary;

      var currentRentIndex = this.calculateRentIndex(input.currentCity, input.currentCountry);
      var currentContract = this.calculateContractFactor(input.currentCountry);
      var newRentIndex = this.calculateRentIndex(input.newCity, input.newCountry);
      var newContract = this.calculateContractFactor(input.newCountry);

      if (!currentRentIndex ||
          !newRentIndex) {
        //When the city and country combination selected do not exist
        return this.renderInvalidCompensation();
      }

      var newSalary = this.calculateCompensation(salary, currentRentIndex, newRentIndex, currentContract.factor, newContract.factor);
      $(compensationAmount).text(this.formatAmount(newSalary) + ' USD');
    }

    MoveCalculator.prototype.renderContractType = function() {
      var values = this.getElementValues();
      // var currentCountry = values.currentCountry;
      var newCountry = values.newContract;
      // var currentContract = this.calculateContractFactor(currentCountry);
      var newContract = this.calculateContractFactor(newCountry);
      var $container = $('.contract-type-container');

      if (newContract.type === CONTRACT_TYPE_EMPLOYEE) {
        $container.find('.grammer').text('an');
        $container.find('.company-type').text('Inc.');
        $container.find('.contract-type').text('employee');
      } else {
        $container.find('.grammer').text('a');
        $container.find('.company-type').text('BV.');
        $container.find('.contract-type').text('contractor');
      }

      $container.removeClass('hidden');
    }

    MoveCalculator.prototype.renderFormula = function() {
      var values = this.getElementValues();
      var currentRentIndex = this.calculateRentIndex(values.currentCity, values.currentCountry);
      var newRentIndex = this.calculateRentIndex(values.newCity, values.newCountry);
      var currentContract = this.calculateContractFactor(values.currentCountry);
      var newContract = this.calculateContractFactor(values.newCountry);
      var salary = values.salary;

      $('.formula .currentSalary .value').text(this.formatAmount(salary));
      $('.formula .currentRentIndex .value').text(currentRentIndex ? currentRentIndex : defaultValue);
      $('.formula .newRentIndex .value').text(newRentIndex ? newRentIndex : defaultValue);
      $('.formula .currentContractTypeFactor .value').text(currentContract.hasOwnProperty('factor') ? currentContract.factor.toFixed(2) : defaultValue);
      $('.formula .newContractTypeFactor .value').text(newContract.hasOwnProperty('factor') ? newContract.factor.toFixed(2) : defaultValue);
    }

    MoveCalculator.prototype.renderInvalidCompensation = function() {
      $(compensationAmount).text(defaultValue);
    }

    // Pure functions

    MoveCalculator.prototype.getElementValues = function() {
      return {
        salary: $(moveContainer + ' .current-salary input').val() || '',
        currentCountry: $(moveContainer + ' .current-country .title').data('selected') || '',
        currentCity: $(moveContainer + ' .current-city .title').data('selected') || '',
        newCountry: $(moveContainer + ' .new-country .title').data('selected') || '',
        newCity: $(moveContainer + ' .new-city .title').data('selected') || ''
      };
    }

    MoveCalculator.prototype.calculateRentIndex = function(city, country) {
      if (this.data && this.data.numbeo) {
        var locationData = this.data.numbeo.find(function(o) {
          return o.country === country && o.city === city;
        });

        return locationData ? parseFloat((locationData.rentIndex * 0.01).toFixed(2)) : 0;
      }

      // Rent Index will never be zero, safe to return as error value
      return 0;
    }

    MoveCalculator.prototype.calculateContractFactor = function(country) {
      var contract = {};

      if (this.data && this.data.contractTypes) {
        var defaultContractorFactor = this.data.contractTypes.find(function (o) {
          return o.country === '*';
        }).contractor_factor;

        var contractType = this.data.contractTypes.find(function(o) {
          return o.country === country
        });

        if (contractType && contractType.hasOwnProperty(CONTRACT_TYPE_EMPLOYEE)) {
          contract.type = CONTRACT_TYPE_EMPLOYEE;
          contract.factor = contractType[CONTRACT_TYPE_EMPLOYEE];
        } else if (contractType) {
          contract.type = CONTRACT_TYPE_CONTRACTOR;
          contract.factor = contractType[CONTRACT_TYPE_CONTRACTOR];
        } else {
          contract.type = CONTRACT_TYPE_CONTRACTOR;
          contract.factor = defaultContractorFactor;
        }
      }

      return contract;
    }

    MoveCalculator.prototype.calculateCompensation = function(salary, currentRentIndex, newRentIndex, currentContractTypeFactor, newContractTypeFactor) {
      return Math.round(salary * (newRentIndex + 0.25) / (currentRentIndex + 0.25) * (newContractTypeFactor / currentContractTypeFactor));
    };

    MoveCalculator.prototype.formatAmount = function(amount) {
      return '$' + amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    }

    return MoveCalculator;
  })();

  new MoveCalculator();
})();
<|endoftext|>