<commit_before>/**
 *
 * DEPENDENCIES:  resource/js/vendor/underscore
 *                resource/js/vendor/jquery
 *                resource/js/vendor/jquery.panzoom
 *                resource/js/vendor/jquery.dotdotdot
 *                resource/js/vendor/jquery.mousewheel
 *                resource/js/vendor/foundation
 *
 */
var FigureLightbox = {};
(function($) {

  FigureLightbox = {
    // All events are triggered on this container
    lbContainerSelector:     '#figure-lightbox-container',

    /* internal selectors */
    lbSelector:               '#figure-lightbox',
    lbTemplateSelector:       '#figure-lightbox-template',
    contextTemplateSelector:  '#image-context-template',
    lbCloseButtonSelector:    '#figure-lightbox .lb-close',
    zoomRangeSelector:        '#figure-lightbox .range-slider',
    $panZoomEl:               null,
    imgData:                  null,

    /* internal config variables */
    imgPath:        WombatConfig.figurePath || 'IMG_PATH_NOT_LOADED'
  };

  FigureLightbox.insertLightboxTemplate = function () {
    var articleData = this.fetchArticleData();

    var lbTemplate = _.template($(this.lbTemplateSelector).html());
    $(this.lbContainerSelector).append(lbTemplate(articleData));
  };

  FigureLightbox.fetchArticleData = function () {
    // @TODO: Do not parse article. Fetch data via an ajax call
    var $articleContainer = $('.article');
    return {
      doi: this.imgData.doi,
      strippedDoi: this.imgData.strippedDoi,

      articleTitle: $articleContainer.find('#artTitle').text(),
      authorList: $articleContainer.find('.author-name').text(),
      figureList: this.imgList
    };
  };

  FigureLightbox.fetchImageData = function () {
    return {
      doi: this.imgData.doi,
      strippedDoi: this.imgData.strippedDoi,

      title: this.imgData.imgElement.find('.figcaption').html(),
      description: this.imgData.imgElement.find('.caption_target').next().html(),
      fileSizes: {
        original: this.imgData.imgElement.find('.file-size[data-size="original"]').text(),
        large: this.imgData.imgElement.find('.file-size[data-size="large"]').text()
      }
    };
  };

  FigureLightbox.bindBehavior = function () {
    var that = this;

    // Escape key destroys and closes lightbox
    $(document).on('keyup.figure-lightbox', function(e) {
      if (e.keyCode === 27) {
        that.close();
      }
    });

    $(this.lbContainerSelector)
        .data('is-inited', true)
        // Bind close button
        .find(this.lbCloseButtonSelector).on('click', function () {
          that.close();
        }).end()

        // Bind buttons to change images
        .find('.change-img').on('click', function () {
          that.switchImage(this.getAttribute('data-doi'));
        }).end()

        // Bind button to show all images
        .find('.all-fig-btn').on('click', function () {
          var $figList = $('#figures-list');

          if($figList.hasClass('figures-list-open')) {
            $figList.removeClass('figures-list-open');
          }
          else {
            $figList.addClass('figures-list-open');
          }

        }).end()

        // Bind mousewheel in figure list. Prevent image zooming
        .find('#figures-list').on('mousewheel', function(e) {
          e.stopPropagation();
        }).end()

        // Bind show in context button
        .find('#image-context').on('click', '.show-context', function () {
          that.close();
        }).end()

        // Bind next figure button
        .find('.next-fig-btn').on('click', function () {
          return that.nextImage();
        }).end()

        // Bind next figure button
        .find('.prev-fig-btn').on('click', function () {
          return that.prevImage();
        }).end()

        .find('#view-more').on('click', function () {
          $('#view-more-wrapper').hide();
        }).end()

        .find('#lb-zoom-min').on('click', function () {
          that.zoomOut();
        }).end()

        .find('#lb-zoom-max').on('click', function () {
          that.zoomIn();
        }).end()

        .on('image-switch.lightbox', function (e, data) {
          // Show both prev and next buttons
          var buttons = $(that.lbSelector).find('.fig-btn').show();
          if (data.index === 0) {
            buttons.filter('.prev-fig-btn').hide(); // Hide prev button
          }
          if (data.index === (that.imgList.length - 1)) {
            buttons.filter('.next-fig-btn').hide(); // Hide next button
          }

          $(that.lbSelector).find('#view-more, #view-less').on('click', function () {
            that.toggleDescription();
          });
        });
  };

  FigureLightbox.nextImage = function () {
    var newIndex = this.getCurrentImageIndex() + 1;
    var nextImg = this.imgList[newIndex];
    if (!nextImg) {
      return false;
    }
    this.switchImage(nextImg.getAttribute('data-doi'));
  };

  FigureLightbox.prevImage = function () {
    var newIndex = this.getCurrentImageIndex() - 1;
    var prevImg = this.imgList[newIndex];
    if (!prevImg) {
      return false;
    }
    this.switchImage(prevImg.getAttribute('data-doi'));
  };

  FigureLightbox.getCurrentImageIndex = function () {
    var that = this;
    var currentIx = null;
    this.imgList.each(function (ix, img) {
      if (img.getAttribute('data-doi') === that.imgData.strippedDoi) {
        currentIx = ix;
        return false;
      }
    });
    return currentIx;
  };

  FigureLightbox.switchImage = function (imgDoi, options) {
    var defaultOptions = {
      descriptionExpanded: this.descriptionExpanded || false
    };
    options = $.extend(defaultOptions, options);

    this.imgData = {
      doi: imgDoi
    };
    this.imgData.strippedDoi = this.imgData.doi.replace(/^info:doi\//, '');
    var currentIndex = this.getCurrentImageIndex();
    this.imgData.imgElement = $(this.imgList[currentIndex]);
    // Get data to populate image context
    var imageData = this.fetchImageData();
    var templateFunctions = {
      showInContext: this.showInContext
    };
    var templateData = $.extend(imageData, templateFunctions, options);
    var lbTemplate = _.template($(this.contextTemplateSelector).html());
    // Remove actual img context
    $(this.lbSelector + ' #image-context').children().remove().end()
        // Append new img context
        .append(lbTemplate(templateData));
    if (this.descriptionExpanded) {
      this.retractDescription();
    }
    this.renderImg(this.imgData.doi);

    if (!this.descriptionExpanded) {
      this.truncateDescription();
    }

    $(this.lbContainerSelector).trigger('image-switch.lightbox', {index: currentIndex, element: this.imgData.imgElement});
  };

  FigureLightbox.truncateDescription = function () {
    var $viewMoreWrapper = $(this.lbSelector + ' #view-more-wrapper');
    if (!$viewMoreWrapper.data('is-truncated')) {
      if ($viewMoreWrapper.find('img').length > 0) {
        // Workaround: If description has inline images reduce
        // the height of the container to avoid hiding the show
        // more button when the images are rendered and ocuppy more space
        $viewMoreWrapper.css({ maxHeight: function( index, value ) {
          return parseFloat( value ) * 3/4;
        }});
      }
      $viewMoreWrapper.dotdotdot({after: '#view-more'}).data('is-truncated', true);
    }
  };

  FigureLightbox.toggleDescription = function () {
    if (this.descriptionExpanded) {
      this.retractDescription();
    } else {
      this.expandDescription();
    }
  };

  FigureLightbox.expandDescription = function () {
    $('#image-context').addClass('full-display');
    // Workaround to slide and fade at the same time
    $('#view-more-wrapper').stop(true, true).fadeOut({ queue: false }).slideUp();
    $('#view-less-wrapper').slideDown('slow');
    this.descriptionExpanded = true;
  };

  FigureLightbox.retractDescription = function () {
    var that = this;
    // Workaround to slide and fade at the same time
    $('#view-less-wrapper').stop(true, true).fadeOut({ queue: false }).slideUp(500, function () {
      $('#image-context').removeClass('full-display');
      $('#view-more-wrapper').show();
      // Dotdotdot in case description is initialized expanded
      that.truncateDescription();
    });
    this.descriptionExpanded = false;
  };

  FigureLightbox.isInited = function () {
    return $(this.lbContainerSelector).data('is-inited');
  };

  FigureLightbox.loadImage = function (lbContainer, imgDoi, cb) {
    $(this.lbContainerSelector).trigger('opened.lightbox');
    this.lbContainerSelector = lbContainer || this.lbContainerSelector;

    this.imgData = {
      doi: imgDoi
    };
    this.imgData.strippedDoi = this.imgData.doi.replace(/^info:doi\//, '');

    if (!this.isInited()) {
      this.imgList = $('.figure');
      this.insertLightboxTemplate();
      this.bindBehavior();
    }
    $(this.lbSelector)
        .foundation('reveal', 'open');
    this.switchImage(this.imgData.doi);

    if (typeof cb === 'function') {
      cb();
    }
  };

  FigureLightbox.renderImg = function (imgDoi) {
    var that = this;
    this.showLoader();
    var $image = $(this.lbSelector).find('img.main-lightbox-image')
        .attr('src', this.buildImgUrl(imgDoi))
        .one('load', function () {
          // Reset to original zoom
          that.resetZoom();
          that.hideLoader();
        });
    this.panZoom($image);

    // Reinitialize sliders
    $(document).foundation('slider', 'reflow');
  };

  FigureLightbox.showLoader = function () {
    $(this.lbSelector).find('.loader').addClass('showing');
  };

  FigureLightbox.hideLoader = function () {
    $(this.lbSelector).find('.loader').removeClass('showing');
  };

  FigureLightbox.close = function () {
    this.destroy();
    $(this.lbSelector).foundation('reveal', 'close');
    $(this.lbContainerSelector).trigger('closed.lightbox');
  };

  FigureLightbox.showInContext = function (imgDoi) {
    imgDoi = imgDoi.split('/');
    imgDoi = imgDoi[1].slice(8);
    imgDoi = imgDoi.replace(/\./g,'-');
    return '#' + imgDoi;
  };

  FigureLightbox.panZoom = function ($image) {
    var that = this;
    this.$panZoomEl = $image.panzoom({
      contain: false,
      minScale: 1
    });

    /* Bind panzoom and slider to mutually control each other */
    this.bindPanZoomToSlider();
    this.bindSliderToPanZoom();

    this.$panZoomEl.parent().off('mousewheel').on('mousewheel', function(e) {
      e.preventDefault();
      $(that.lbContainerSelector).trigger('mousewheel-zoom.lightbox', e);
      var delta = e.delta || e.originalEvent.wheelDelta;
      var zoomOut = delta ? delta < 0 : e.originalEvent.deltaY > 0;
      that.zoom(zoomOut, e);
    });
  };

  FigureLightbox.zoomIn = function () {
    this.zoom(false);
  };

  FigureLightbox.zoomOut = function () {
    this.zoom(true);
  };

  FigureLightbox.resetZoom = function () {
    this.$panZoomEl.panzoom('reset', false);
  };

  FigureLightbox.zoom = function (zoomOut, focal) {
    zoomOut = zoomOut || false;
    this.$panZoomEl.panzoom('zoom', zoomOut, {
      increment: 0.05,
      animate: false
    });
  };

  FigureLightbox.bindPanZoomToSlider = function () {
    var that = this;
    var panzoomInstance = that.$panZoomEl.panzoom('instance');
    $(this.zoomRangeSelector).off('change.fndtn.slider').on('change.fndtn.slider', function(){
      // If values differ, change them
      var matrix = panzoomInstance.getMatrix();
      var newSliderValue = parseFloat(this.dataset.slider);
      if (matrix[0] !== newSliderValue || matrix[3] !== newSliderValue) {
        $(that.lbContainerSelector).trigger('slider-zoom.lightbox');
        matrix[0] = matrix[3] = newSliderValue;
        panzoomInstance.setMatrix(matrix);
      }
    });
  };
  FigureLightbox.bindSliderToPanZoom = function () {
    var that = this;
    this.$panZoomEl.off('panzoomzoom').on('panzoomzoom', function(e, panzoom, scale) {
      $(that.zoomRangeSelector).foundation('slider', 'set_value', scale);
      // Bug in foundation unbinds after set_value. Workaround: rebind everytime
      that.bindPanZoomToSlider();
    });

    this.$panZoomEl.off('panzoomreset').on('panzoomreset', function(e) {
      $(that.zoomRangeSelector).foundation('slider', 'set_value', 1);
      // Bug in foundation unbinds after set_value. Workaround: rebind everytime
      that.bindPanZoomToSlider();
    });
  };

  FigureLightbox.buildImgUrl = function (imgDoi, options) {
    var defaultOptions = {
      path: this.imgPath,
      size: 'large'
    };
    options = _.extend(defaultOptions, options);
    return options.path + '?size=' + options.size + '&id=' + imgDoi;
  };

  FigureLightbox.destroy = function () {
      // @TODO: Check when to destroy modal with images,
    ///add destroy when opening in other pages.
/*    $(this.lbContainerSelector)
      // Unbind close button
        .find(this.lbCloseButtonSelector).off('click').end()
      // Unbind buttons to change images
        .find('.change-img').off('click').end()
      // Unbind button to show all images
        .find('.all-fig-btn').off('click').end()
        .off('image-switch.lightbox');
      this.$panZoomEl.panzoom('destroy');
        */
  };

  })(jQuery);<commit_msg>DPRO-2096 - [Figure Lightbox] - Navigate in the lightbox with the arrows keys<commit_after>/**
 *
 * DEPENDENCIES:  resource/js/vendor/underscore
 *                resource/js/vendor/jquery
 *                resource/js/vendor/jquery.panzoom
 *                resource/js/vendor/jquery.dotdotdot
 *                resource/js/vendor/jquery.mousewheel
 *                resource/js/vendor/foundation
 *
 */
var FigureLightbox = {};
(function($) {

  FigureLightbox = {
    // All events are triggered on this container
    lbContainerSelector:     '#figure-lightbox-container',

    /* internal selectors */
    lbSelector:               '#figure-lightbox',
    lbTemplateSelector:       '#figure-lightbox-template',
    contextTemplateSelector:  '#image-context-template',
    lbCloseButtonSelector:    '#figure-lightbox .lb-close',
    zoomRangeSelector:        '#figure-lightbox .range-slider',
    $panZoomEl:               null,
    imgData:                  null,

    /* internal config variables */
    imgPath:        WombatConfig.figurePath || 'IMG_PATH_NOT_LOADED'
  };

  FigureLightbox.insertLightboxTemplate = function () {
    var articleData = this.fetchArticleData();

    var lbTemplate = _.template($(this.lbTemplateSelector).html());
    $(this.lbContainerSelector).append(lbTemplate(articleData));
  };

  FigureLightbox.fetchArticleData = function () {
    // @TODO: Do not parse article. Fetch data via an ajax call
    var $articleContainer = $('.article');
    return {
      doi: this.imgData.doi,
      strippedDoi: this.imgData.strippedDoi,

      articleTitle: $articleContainer.find('#artTitle').text(),
      authorList: $articleContainer.find('.author-name').text(),
      figureList: this.imgList
    };
  };

  FigureLightbox.fetchImageData = function () {
    return {
      doi: this.imgData.doi,
      strippedDoi: this.imgData.strippedDoi,

      title: this.imgData.imgElement.find('.figcaption').html(),
      description: this.imgData.imgElement.find('.caption_target').next().html(),
      fileSizes: {
        original: this.imgData.imgElement.find('.file-size[data-size="original"]').text(),
        large: this.imgData.imgElement.find('.file-size[data-size="large"]').text()
      }
    };
  };

  FigureLightbox.bindBehavior = function () {
    var that = this;

    // Escape key destroys and closes lightbox
    $(document).on('keyup.figure-lightbox', function(e) {
      if($(that.lbSelector).hasClass('open')) {
        switch (e.which) {
          case 27: // esc
            that.close();
            break;
          case 37: // left
            that.prevImage();
            break;

          case 38: // up
            that.nextImage();
            break;

          case 39: // right
            that.nextImage();
            break;

          case 40: // down
            that.prevImage();
            break;

          default:
            return; // exit this handler for other keys
        }
        e.preventDefault();
      }
    });

    $(this.lbContainerSelector)
        .data('is-inited', true)
        // Bind close button
        .find(this.lbCloseButtonSelector).on('click', function () {
          that.close();
        }).end()

        // Bind buttons to change images
        .find('.change-img').on('click', function () {
          that.switchImage(this.getAttribute('data-doi'));
        }).end()

        // Bind button to show all images
        .find('.all-fig-btn').on('click', function () {
          var $figList = $('#figures-list');

          if($figList.hasClass('figures-list-open')) {
            $figList.removeClass('figures-list-open');
          }
          else {
            $figList.addClass('figures-list-open');
          }

        }).end()

        // Bind mousewheel in figure list. Prevent image zooming
        .find('#figures-list').on('mousewheel', function(e) {
          e.stopPropagation();
        }).end()

        // Bind show in context button
        .find('#image-context').on('click', '.show-context', function () {
          that.close();
        }).end()

        // Bind next figure button
        .find('.next-fig-btn').on('click', function () {
          return that.nextImage();
        }).end()

        // Bind next figure button
        .find('.prev-fig-btn').on('click', function () {
          return that.prevImage();
        }).end()

        .find('#view-more').on('click', function () {
          $('#view-more-wrapper').hide();
        }).end()

        .find('#lb-zoom-min').on('click', function () {
          that.zoomOut();
        }).end()

        .find('#lb-zoom-max').on('click', function () {
          that.zoomIn();
        }).end()

        .on('image-switch.lightbox', function (e, data) {
          // Show both prev and next buttons
          var buttons = $(that.lbSelector).find('.fig-btn').show();
          if (data.index === 0) {
            buttons.filter('.prev-fig-btn').hide(); // Hide prev button
          }
          if (data.index === (that.imgList.length - 1)) {
            buttons.filter('.next-fig-btn').hide(); // Hide next button
          }

          $(that.lbSelector).find('#view-more, #view-less').on('click', function () {
            that.toggleDescription();
          });
        });
  };

  FigureLightbox.nextImage = function () {
    var newIndex = this.getCurrentImageIndex() + 1;
    var nextImg = this.imgList[newIndex];
    if (!nextImg) {
      return false;
    }
    this.switchImage(nextImg.getAttribute('data-doi'));
  };

  FigureLightbox.prevImage = function () {
    var newIndex = this.getCurrentImageIndex() - 1;
    var prevImg = this.imgList[newIndex];
    if (!prevImg) {
      return false;
    }
    this.switchImage(prevImg.getAttribute('data-doi'));
  };

  FigureLightbox.getCurrentImageIndex = function () {
    var that = this;
    var currentIx = null;
    this.imgList.each(function (ix, img) {
      if (img.getAttribute('data-doi') === that.imgData.strippedDoi) {
        currentIx = ix;
        return false;
      }
    });
    return currentIx;
  };

  FigureLightbox.switchImage = function (imgDoi, options) {
    var defaultOptions = {
      descriptionExpanded: this.descriptionExpanded || false
    };
    options = $.extend(defaultOptions, options);

    this.imgData = {
      doi: imgDoi
    };
    this.imgData.strippedDoi = this.imgData.doi.replace(/^info:doi\//, '');
    var currentIndex = this.getCurrentImageIndex();
    this.imgData.imgElement = $(this.imgList[currentIndex]);
    // Get data to populate image context
    var imageData = this.fetchImageData();
    var templateFunctions = {
      showInContext: this.showInContext
    };
    var templateData = $.extend(imageData, templateFunctions, options);
    var lbTemplate = _.template($(this.contextTemplateSelector).html());
    // Remove actual img context
    $(this.lbSelector + ' #image-context').children().remove().end()
        // Append new img context
        .append(lbTemplate(templateData));
    if (this.descriptionExpanded) {
      this.retractDescription();
    }
    this.renderImg(this.imgData.doi);

    if (!this.descriptionExpanded) {
      this.truncateDescription();
    }

    $(this.lbContainerSelector).trigger('image-switch.lightbox', {index: currentIndex, element: this.imgData.imgElement});
  };

  FigureLightbox.truncateDescription = function () {
    var $viewMoreWrapper = $(this.lbSelector + ' #view-more-wrapper');
    if (!$viewMoreWrapper.data('is-truncated')) {
      if ($viewMoreWrapper.find('img').length > 0) {
        // Workaround: If description has inline images reduce
        // the height of the container to avoid hiding the show
        // more button when the images are rendered and ocuppy more space
        $viewMoreWrapper.css({ maxHeight: function( index, value ) {
          return parseFloat( value ) * 3/4;
        }});
      }
      $viewMoreWrapper.dotdotdot({after: '#view-more'}).data('is-truncated', true);
    }
  };

  FigureLightbox.toggleDescription = function () {
    if (this.descriptionExpanded) {
      this.retractDescription();
    } else {
      this.expandDescription();
    }
  };

  FigureLightbox.expandDescription = function () {
    $('#image-context').addClass('full-display');
    // Workaround to slide and fade at the same time
    $('#view-more-wrapper').stop(true, true).fadeOut({ queue: false }).slideUp();
    $('#view-less-wrapper').slideDown('slow');
    this.descriptionExpanded = true;
  };

  FigureLightbox.retractDescription = function () {
    var that = this;
    // Workaround to slide and fade at the same time
    $('#view-less-wrapper').stop(true, true).fadeOut({ queue: false }).slideUp(500, function () {
      $('#image-context').removeClass('full-display');
      $('#view-more-wrapper').show();
      // Dotdotdot in case description is initialized expanded
      that.truncateDescription();
    });
    this.descriptionExpanded = false;
  };

  FigureLightbox.isInited = function () {
    return $(this.lbContainerSelector).data('is-inited');
  };

  FigureLightbox.loadImage = function (lbContainer, imgDoi, cb) {
    $(this.lbContainerSelector).trigger('opened.lightbox');
    this.lbContainerSelector = lbContainer || this.lbContainerSelector;

    this.imgData = {
      doi: imgDoi
    };
    this.imgData.strippedDoi = this.imgData.doi.replace(/^info:doi\//, '');

    if (!this.isInited()) {
      this.imgList = $('.figure');
      this.insertLightboxTemplate();
      this.bindBehavior();
    }
    $(this.lbSelector)
        .foundation('reveal', 'open');
    this.switchImage(this.imgData.doi);

    if (typeof cb === 'function') {
      cb();
    }
  };

  FigureLightbox.renderImg = function (imgDoi) {
    var that = this;
    this.showLoader();
    var $image = $(this.lbSelector).find('img.main-lightbox-image')
        .attr('src', this.buildImgUrl(imgDoi))
        .one('load', function () {
          // Reset to original zoom
          that.resetZoom();
          that.hideLoader();
        });
    this.panZoom($image);

    // Reinitialize sliders
    $(document).foundation('slider', 'reflow');
  };

  FigureLightbox.showLoader = function () {
    $(this.lbSelector).find('.loader').addClass('showing');
  };

  FigureLightbox.hideLoader = function () {
    $(this.lbSelector).find('.loader').removeClass('showing');
  };

  FigureLightbox.close = function () {
    this.destroy();
    $(this.lbSelector).foundation('reveal', 'close');
    $(this.lbContainerSelector).trigger('closed.lightbox');
  };

  FigureLightbox.showInContext = function (imgDoi) {
    imgDoi = imgDoi.split('/');
    imgDoi = imgDoi[1].slice(8);
    imgDoi = imgDoi.replace(/\./g,'-');
    return '#' + imgDoi;
  };

  FigureLightbox.panZoom = function ($image) {
    var that = this;
    this.$panZoomEl = $image.panzoom({
      contain: false,
      minScale: 1
    });

    /* Bind panzoom and slider to mutually control each other */
    this.bindPanZoomToSlider();
    this.bindSliderToPanZoom();

    this.$panZoomEl.parent().off('mousewheel').on('mousewheel', function(e) {
      e.preventDefault();
      $(that.lbContainerSelector).trigger('mousewheel-zoom.lightbox', e);
      var delta = e.delta || e.originalEvent.wheelDelta;
      var zoomOut = delta ? delta < 0 : e.originalEvent.deltaY > 0;
      that.zoom(zoomOut, e);
    });
  };

  FigureLightbox.zoomIn = function () {
    this.zoom(false);
  };

  FigureLightbox.zoomOut = function () {
    this.zoom(true);
  };

  FigureLightbox.resetZoom = function () {
    this.$panZoomEl.panzoom('reset', false);
  };

  FigureLightbox.zoom = function (zoomOut, focal) {
    zoomOut = zoomOut || false;
    this.$panZoomEl.panzoom('zoom', zoomOut, {
      increment: 0.05,
      animate: false
    });
  };

  FigureLightbox.bindPanZoomToSlider = function () {
    var that = this;
    var panzoomInstance = that.$panZoomEl.panzoom('instance');
    $(this.zoomRangeSelector).off('change.fndtn.slider').on('change.fndtn.slider', function(){
      // If values differ, change them
      var matrix = panzoomInstance.getMatrix();
      var newSliderValue = parseFloat(this.dataset.slider);
      if (matrix[0] !== newSliderValue || matrix[3] !== newSliderValue) {
        $(that.lbContainerSelector).trigger('slider-zoom.lightbox');
        matrix[0] = matrix[3] = newSliderValue;
        panzoomInstance.setMatrix(matrix);
      }
    });
  };
  FigureLightbox.bindSliderToPanZoom = function () {
    var that = this;
    this.$panZoomEl.off('panzoomzoom').on('panzoomzoom', function(e, panzoom, scale) {
      $(that.zoomRangeSelector).foundation('slider', 'set_value', scale);
      // Bug in foundation unbinds after set_value. Workaround: rebind everytime
      that.bindPanZoomToSlider();
    });

    this.$panZoomEl.off('panzoomreset').on('panzoomreset', function(e) {
      $(that.zoomRangeSelector).foundation('slider', 'set_value', 1);
      // Bug in foundation unbinds after set_value. Workaround: rebind everytime
      that.bindPanZoomToSlider();
    });
  };

  FigureLightbox.buildImgUrl = function (imgDoi, options) {
    var defaultOptions = {
      path: this.imgPath,
      size: 'large'
    };
    options = _.extend(defaultOptions, options);
    return options.path + '?size=' + options.size + '&id=' + imgDoi;
  };

  FigureLightbox.destroy = function () {
      // @TODO: Check when to destroy modal with images,
    ///add destroy when opening in other pages.
/*    $(this.lbContainerSelector)
      // Unbind close button
        .find(this.lbCloseButtonSelector).off('click').end()
      // Unbind buttons to change images
        .find('.change-img').off('click').end()
      // Unbind button to show all images
        .find('.all-fig-btn').off('click').end()
        .off('image-switch.lightbox');
      this.$panZoomEl.panzoom('destroy');
        */
  };

  })(jQuery);<|endoftext|>


<commit_before>window.RouteOverlay = Backbone.View.extend({

	id: 'route-overlay',

	events: {

	},

	initialize: function(data) {
		this.data = data;
	},

	render: function () {
		$('#algview').hide();

		var html = templates.routeOverlay();
		jQuery.each(this.data, function(i, val) {
			if (i === "distance" || i === "altitude") val = val / 1000 + " km"
			if (i === "time") val = Math.floor(val / 3600) + " h " + Math.floor((val - (Math.floor(val / 3600) * 3600)) / 60) + " min";
			i = i.charAt(0).toUpperCase() + i.slice(1);
			html += templates.routeOverlayAttribute({name: $._(i), value: val});
		});

		this.$el.html(html);

		$('#main').append(this.el);

		var left = ($("#map").width() - this.$el.innerWidth()) / 2 + $("#sidebar").width();
		this.$el.css("left", left + "px");

		return this;
	},

	remove: function () {
		$(this.el).remove();
	}

});<commit_msg>hide travel time from being displayed. unhide if server sends better data. just delete marked line in route.js<commit_after>window.RouteOverlay = Backbone.View.extend({

	id: 'route-overlay',

	events: {

	},

	initialize: function(data) {
		this.data = data;
	},

	render: function () {
		$('#algview').hide();

		var html = templates.routeOverlay();
		jQuery.each(this.data, function(i, val) {
			if (i === "distance" || i === "altitude") val = val / 1000 + " km"
			if (i === "time") val = Math.floor(val / 3600) + " h " + Math.floor((val - (Math.floor(val / 3600) * 3600)) / 60) + " min";
			i = i.charAt(0).toUpperCase() + i.slice(1);
			if (i !== "Time") // temporally skip time; remove this line to display time again
				html += templates.routeOverlayAttribute({name: $._(i), value: val});
		});

		this.$el.html(html);

		$('#main').append(this.el);

		var left = ($("#map").width() - this.$el.innerWidth()) / 2 + $("#sidebar").width();
		this.$el.css("left", left + "px");

		return this;
	},

	remove: function () {
		$(this.el).remove();
	}

});<|endoftext|>


<commit_before>'use strict';

var extend = require('extend');

var utils = require('./utils');
var p = null;
var appConf = null;

exports.init = initConfig;
exports.get = get;
exports.getInt = getInt;
exports.getT = getT;

/**
 * @name get
 * @param {String|Array}    parameter   Parameter name (e.g. 'mysql.host' or ['mysql', 'host'])
 * @param {*=}              def         Default value
 * @returns {*}
 * The specified parameter value
 * @description
 * Returns the specified parameter value
 */
function get(parameter, def) {
    initConfig();
    var ret = {};
    var v1 = utils.get(p, parameter, def);
    var v2 = utils.get(appConf, parameter, def);
    if (!utils.isset(v2)) {
        return v1;
    }
    extend(true, ret, {
        x: v1
    }, {
        x: v2
    });
    return ret.x;
}

/**
 * @name getT
 * @param {String|Array}    parameter
 * @param {String}  type    Value type
 * @param {*=}      def     Default value
 * @returns {*}
 * The specified parameter value
 * @description
 * Returns the specified parameter value
 */
function getT(parameter, type, def) {
    var ret = get(parameter, def);
    return utils.mixedTo(ret, type);
}

/**
 * @name getInt
 * @param {String|Array}    parameter
 * @param {*=}      def     Default value
 * @returns {*}
 * The specified parameter value
 * @description
 * Returns the specified parameter value
 */
function getInt(parameter, def) {
    return getT(parameter, 'i', def);
}

/**
 * @name initConfig
 * @param {string}       file configuration file
 * @param {string|Array} path JSON path
 * @description
 * Initializes configuration
 * file
 */
function initConfig(file, path) {
    if (appConf === null) {
        appConf = {};

        var conf = require(file);
        p = utils.get(conf, path, {});

        var m;
        process.argv.forEach(function (val) {
            if (m = val.match(/^--([^-]+)-([^=]+)=?(.*)$/)) {
                utils.set(appConf, [m[1], m[2]], m[3] || true);
            }
        });
        for (var key in process.env) {
            if (process.env.hasOwnProperty(key)) {
                if (m = key.match(/^([^-]+)-(.+)$/)) {
                    utils.set(appConf, [m[1], m[2]], process.env[key] || true);
                }
            }
        }
    }
}
<commit_msg>config init<commit_after>'use strict';

var extend = require('extend');

var utils = require('./utils');
var p = null;
var appConf = null;

exports.init = initConfig;
exports.get = get;
exports.getInt = getInt;
exports.getT = getT;

/**
 * @name get
 * @param {String|Array}    parameter   Parameter name (e.g. 'mysql.host' or ['mysql', 'host'])
 * @param {*=}              def         Default value
 * @returns {*}
 * The specified parameter value
 * @description
 * Returns the specified parameter value
 */
function get(parameter, def) {
    var ret = {};
    var v1 = utils.get(p, parameter, def);
    var v2 = utils.get(appConf, parameter, def);
    if (!utils.isset(v2)) {
        return v1;
    }
    extend(true, ret, {
        x: v1
    }, {
        x: v2
    });
    return ret.x;
}

/**
 * @name getT
 * @param {String|Array}    parameter
 * @param {String}  type    Value type
 * @param {*=}      def     Default value
 * @returns {*}
 * The specified parameter value
 * @description
 * Returns the specified parameter value
 */
function getT(parameter, type, def) {
    var ret = get(parameter, def);
    return utils.mixedTo(ret, type);
}

/**
 * @name getInt
 * @param {String|Array}    parameter
 * @param {*=}      def     Default value
 * @returns {*}
 * The specified parameter value
 * @description
 * Returns the specified parameter value
 */
function getInt(parameter, def) {
    return getT(parameter, 'i', def);
}

/**
 * @name initConfig
 * @param {string}          file configuration file
 * @param {string|Array=}   path JSON path
 * @description
 * Initializes configuration
 * file
 */
function initConfig(file, path) {
    appConf = {};

    var conf = require(file);
    p = utils.get(conf, path, {});

    var m;
    process.argv.forEach(function (val) {
        if (m = val.match(/^--([^-]+)-([^=]+)=?(.*)$/)) {
            utils.set(appConf, [m[1], m[2]], m[3] || true);
        }
    });
    for (var key in process.env) {
        if (process.env.hasOwnProperty(key)) {
            if (m = key.match(/^([^-]+)-(.+)$/)) {
                utils.set(appConf, [m[1], m[2]], process.env[key] || true);
            }
        }
    }
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Shaka Player demo, main section.
 *
 * @suppress {visibility} to work around compiler errors until we can
 *   refactor the demo into classes that talk via public method.  TODO
 */

/** @suppress {duplicate} */
var shakaDemo = shakaDemo || {};

/**
 * @return {!Promise}
 * @private
 */
shakaDemo.setupAssets_ = function() {
  // Populate the asset list.
  var assetList = document.getElementById('assetList');
  /** @type {!Object.<string, !HTMLOptGroupElement>} */
  var groups = {};
  var first = null;
  shakaAssets.testAssets.forEach(function(asset) {
    if (asset.disabled) return;

    var group = groups[asset.source];
    if (!group) {
      group = /** @type {!HTMLOptGroupElement} */(
          document.createElement('optgroup'));
      group.label = asset.source;
      groups[asset.source] = group;
      assetList.appendChild(group);
    }

    var option = document.createElement('option');
    option.textContent = asset.name;
    option.asset = asset;  // custom attribute to map back to the asset
    group.appendChild(option);

    if (asset.drm.length && !asset.drm.some(
        function(keySystem) { return shakaDemo.support_.drm[keySystem]; })) {
      option.disabled = true;
    }

    var mimeTypes = [];
    if (asset.features.indexOf(shakaAssets.Feature.WEBM) >= 0)
      mimeTypes.push('video/webm');
    if (asset.features.indexOf(shakaAssets.Feature.MP4) >= 0)
      mimeTypes.push('video/mp4');
    if (!mimeTypes.some(
        function(type) { return shakaDemo.support_.media[type]; })) {
      option.disabled = true;
    }

    if (!option.disabled) {
      first = first || option;
      if (asset.focus) first = option;
    }
  });

  if (first) {
    first.selected = true;
  }

  // This needs to be started before we add the custom asset option.
  var asyncOfflineSetup = shakaDemo.setupOfflineAssets_();

  // Add an extra option for custom assets.
  var option = document.createElement('option');
  option.textContent = '(custom asset)';
  assetList.appendChild(option);

  assetList.addEventListener('change', function() {
    // Show/hide the custom asset fields based on the selection.
    var asset = assetList.options[assetList.selectedIndex].asset;
    var customAsset = document.getElementById('customAsset');
    customAsset.style.display = asset ? 'none' : 'block';

    // Update the hash to reflect this change.
    shakaDemo.hashShouldChange_();
  });

  document.getElementById('loadButton').addEventListener(
      'click', shakaDemo.load);
  document.getElementById('licenseServerInput').addEventListener(
      'keyup', shakaDemo.onAssetKeyUp_);
  document.getElementById('manifestInput').addEventListener(
      'keyup', shakaDemo.onAssetKeyUp_);

  return asyncOfflineSetup;
};

/**
 * @param {!Event} event
 * @private
 */
shakaDemo.onAssetKeyUp_ = function(event) {
  // Mirror the users input as they type.
  shakaDemo.hashShouldChange_();
  // Load the asset if the user presses enter.
  if (event.keyCode != 13) return;
  shakaDemo.load();
};

/**
 * Prepares the Player to load the given assets by setting the configuration
 * values.  This does not load the asset.
 *
 * @param {?shakaAssets.AssetInfo} asset
 * @return {shakaAssets.AssetInfo}
 * @private
 */
shakaDemo.preparePlayer_ = function(asset) {
  shakaDemo.closeError();

  var player = shakaDemo.player_;

  var config = /** @type {shakaExtern.PlayerConfiguration} */(
      { abr: {}, manifest: { dash: {} } });
  config.manifest.dash.clockSyncUri =
      '//shaka-player-demo.appspot.com/time.txt';

  if (!asset) {
    // Use the custom fields.
    var licenseServer = document.getElementById('licenseServerInput').value;
    asset = /** @type {shakaAssets.AssetInfo} */ ({
      manifestUri: document.getElementById('manifestInput').value,
      // Use the custom license server for all key systems.
      // This simplifies configuration for the user.
      // They will simply fill in a Widevine license server on Chrome, etc.
      licenseServers: {
        'com.widevine.alpha': licenseServer,
        'com.microsoft.playready': licenseServer,
        'com.adobe.primetime': licenseServer
      }
    });
  }

  player.resetConfiguration();

  // Add config from this asset.
  ShakaDemoUtils.setupAssetMetadata(asset, player);
  shakaDemo.castProxy_.setAppData({
    'asset': asset,
    'isYtDrm': asset.drmCallback == shakaAssets.YouTubeCallback
  });

  // Add configuration from the UI.
  config.preferredAudioLanguage =
      document.getElementById('preferredAudioLanguage').value;
  config.preferredTextLanguage =
      document.getElementById('preferredTextLanguage').value;
  config.abr.enabled =
      document.getElementById('enableAdaptation').checked;

  player.configure(config);

  // TODO: document demo app debugging features
  if (window.debugConfig) {
    player.configure(window.debugConfig);
  }

  return asset;
};

/** Load the selected asset. */
shakaDemo.load = function() {
  var assetList = document.getElementById('assetList');
  var option = assetList.options[assetList.selectedIndex];
  var player = shakaDemo.player_;

  var asset = shakaDemo.preparePlayer_(option.asset);

  // Load the manifest.
  player.load(asset.manifestUri).then(function() {
    // Update control state in case autoplay is disabled.
    shakaDemo.controls_.loadComplete();

    // Disallow casting of offline content.
    var isOffline = asset.manifestUri.indexOf('offline:') == 0;
    shakaDemo.controls_.allowCast(!isOffline);

    (asset.extraText || []).forEach(function(extraText) {
      player.addTextTrack(extraText.uri, extraText.language, extraText.kind,
                          extraText.mime, extraText.codecs);
    });

    // Check if browser supports Media Session first.
    if ('mediaSession' in navigator) {
      // Set media session title.
      navigator.mediaSession.metadata = new MediaMetadata({title: asset.name});
    }
  }, function(reason) {
    var error = /** @type {!shaka.util.Error} */(reason);
    if (error.code == shaka.util.Error.Code.LOAD_INTERRUPTED) {
      shaka.log.debug('load() interrupted');
    } else {
      shakaDemo.onError_(error);
    }
  });

  // While the manifest is being loaded in parallel, go ahead and ask the video
  // to play.  This can help with autoplay on Android, since Android requires
  // user interaction to play a video and this function is called from a click
  // event.  This seems to work only because Shaka Player has already created a
  // MediaSource object and set video.src.
  shakaDemo.video_.play();
};
<commit_msg>Update the hash after a new asset is loaded<commit_after>/**
 * @license
 * Copyright 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Shaka Player demo, main section.
 *
 * @suppress {visibility} to work around compiler errors until we can
 *   refactor the demo into classes that talk via public method.  TODO
 */

/** @suppress {duplicate} */
var shakaDemo = shakaDemo || {};

/**
 * @return {!Promise}
 * @private
 */
shakaDemo.setupAssets_ = function() {
  // Populate the asset list.
  var assetList = document.getElementById('assetList');
  /** @type {!Object.<string, !HTMLOptGroupElement>} */
  var groups = {};
  var first = null;
  shakaAssets.testAssets.forEach(function(asset) {
    if (asset.disabled) return;

    var group = groups[asset.source];
    if (!group) {
      group = /** @type {!HTMLOptGroupElement} */(
          document.createElement('optgroup'));
      group.label = asset.source;
      groups[asset.source] = group;
      assetList.appendChild(group);
    }

    var option = document.createElement('option');
    option.textContent = asset.name;
    option.asset = asset;  // custom attribute to map back to the asset
    group.appendChild(option);

    if (asset.drm.length && !asset.drm.some(
        function(keySystem) { return shakaDemo.support_.drm[keySystem]; })) {
      option.disabled = true;
    }

    var mimeTypes = [];
    if (asset.features.indexOf(shakaAssets.Feature.WEBM) >= 0)
      mimeTypes.push('video/webm');
    if (asset.features.indexOf(shakaAssets.Feature.MP4) >= 0)
      mimeTypes.push('video/mp4');
    if (!mimeTypes.some(
        function(type) { return shakaDemo.support_.media[type]; })) {
      option.disabled = true;
    }

    if (!option.disabled) {
      first = first || option;
      if (asset.focus) first = option;
    }
  });

  if (first) {
    first.selected = true;
  }

  // This needs to be started before we add the custom asset option.
  var asyncOfflineSetup = shakaDemo.setupOfflineAssets_();

  // Add an extra option for custom assets.
  var option = document.createElement('option');
  option.textContent = '(custom asset)';
  assetList.appendChild(option);

  assetList.addEventListener('change', function() {
    // Show/hide the custom asset fields based on the selection.
    var asset = assetList.options[assetList.selectedIndex].asset;
    var customAsset = document.getElementById('customAsset');
    customAsset.style.display = asset ? 'none' : 'block';

    // Update the hash to reflect this change.
    shakaDemo.hashShouldChange_();
  });

  document.getElementById('loadButton').addEventListener(
      'click', shakaDemo.load);
  document.getElementById('licenseServerInput').addEventListener(
      'keyup', shakaDemo.onAssetKeyUp_);
  document.getElementById('manifestInput').addEventListener(
      'keyup', shakaDemo.onAssetKeyUp_);

  return asyncOfflineSetup;
};

/**
 * @param {!Event} event
 * @private
 */
shakaDemo.onAssetKeyUp_ = function(event) {
  // Mirror the users input as they type.
  shakaDemo.hashShouldChange_();
  // Load the asset if the user presses enter.
  if (event.keyCode != 13) return;
  shakaDemo.load();
};

/**
 * Prepares the Player to load the given assets by setting the configuration
 * values.  This does not load the asset.
 *
 * @param {?shakaAssets.AssetInfo} asset
 * @return {shakaAssets.AssetInfo}
 * @private
 */
shakaDemo.preparePlayer_ = function(asset) {
  shakaDemo.closeError();

  var player = shakaDemo.player_;

  var config = /** @type {shakaExtern.PlayerConfiguration} */(
      { abr: {}, manifest: { dash: {} } });
  config.manifest.dash.clockSyncUri =
      '//shaka-player-demo.appspot.com/time.txt';

  if (!asset) {
    // Use the custom fields.
    var licenseServer = document.getElementById('licenseServerInput').value;
    asset = /** @type {shakaAssets.AssetInfo} */ ({
      manifestUri: document.getElementById('manifestInput').value,
      // Use the custom license server for all key systems.
      // This simplifies configuration for the user.
      // They will simply fill in a Widevine license server on Chrome, etc.
      licenseServers: {
        'com.widevine.alpha': licenseServer,
        'com.microsoft.playready': licenseServer,
        'com.adobe.primetime': licenseServer
      }
    });
  }

  player.resetConfiguration();

  // Add config from this asset.
  ShakaDemoUtils.setupAssetMetadata(asset, player);
  shakaDemo.castProxy_.setAppData({
    'asset': asset,
    'isYtDrm': asset.drmCallback == shakaAssets.YouTubeCallback
  });

  // Add configuration from the UI.
  config.preferredAudioLanguage =
      document.getElementById('preferredAudioLanguage').value;
  config.preferredTextLanguage =
      document.getElementById('preferredTextLanguage').value;
  config.abr.enabled =
      document.getElementById('enableAdaptation').checked;

  player.configure(config);

  // TODO: document demo app debugging features
  if (window.debugConfig) {
    player.configure(window.debugConfig);
  }

  return asset;
};

/** Load the selected asset. */
shakaDemo.load = function() {
  var assetList = document.getElementById('assetList');
  var option = assetList.options[assetList.selectedIndex];
  var player = shakaDemo.player_;

  var asset = shakaDemo.preparePlayer_(option.asset);

  // Load the manifest.
  player.load(asset.manifestUri).then(function() {
    // Update control state in case autoplay is disabled.
    shakaDemo.controls_.loadComplete();

    shakaDemo.hashShouldChange_();

    // Disallow casting of offline content.
    var isOffline = asset.manifestUri.indexOf('offline:') == 0;
    shakaDemo.controls_.allowCast(!isOffline);

    (asset.extraText || []).forEach(function(extraText) {
      player.addTextTrack(extraText.uri, extraText.language, extraText.kind,
                          extraText.mime, extraText.codecs);
    });

    // Check if browser supports Media Session first.
    if ('mediaSession' in navigator) {
      // Set media session title.
      navigator.mediaSession.metadata = new MediaMetadata({title: asset.name});
    }
  }, function(reason) {
    var error = /** @type {!shaka.util.Error} */(reason);
    if (error.code == shaka.util.Error.Code.LOAD_INTERRUPTED) {
      shaka.log.debug('load() interrupted');
    } else {
      shakaDemo.onError_(error);
    }
  });

  // While the manifest is being loaded in parallel, go ahead and ask the video
  // to play.  This can help with autoplay on Android, since Android requires
  // user interaction to play a video and this function is called from a click
  // event.  This seems to work only because Shaka Player has already created a
  // MediaSource object and set video.src.
  shakaDemo.video_.play();
};
<|endoftext|>


<commit_before>const CHECK_INTERVAL = 30000;

export default class Scheduler {
    constructor(storage,history,logger,valveController) {
        this.storage = storage;
        this.history = history;
        this.logger = logger;
        this.valveController = valveController;

        this.schedule = [];
        this.waterUntil = 0;
    }
    start() {
        this.check();
    }
    reload() {
        this.logger.info('Restarting scheduler...');
        this.storage.getItem('schedule',(err,value) => {
           if (err) {
                return this.logger.error(`Unable to reload schedule ${err.stack}`);
           }
           if (!value) {
                this.schedule = [];
                this.waterUntil = 0;
           } else {
                this.waterUntil = value.waterUntil;
                this.schedule = value.items;
           }
        });
    }
    check = () => {
        const now = new Date();
        const currentHour = now.getHours();
        this.logger.verbose(`Checking schedule for hour ${currentHour}`);
        let updated = false;
        this.schedule.map(item => {
            this.logger.debug(JSON.stringify(item));
            if (item.time === currentHour
            && (!item.nextRun || now.getTime() >= item.nextRun)
            // don't start new tasks if we're more than 5 minutes
            // past the hour
            && now.getMinutes() < 5) {
                item.nextRun = this.startOfDay(now) + ((86400 * item.frequency) + (3600 * item.time)) * 1000;
                this.logger.verbose(`Schedule item will run - next run ${item.nextRun}`);
                const nextWater = now.getTime() + (item.duration * 60 * 1000);
                if (nextWater > this.waterUntil) {
                    this.logger.verbose(`Will water until ${nextWater}`);
                    this.waterUntil = nextWater;
                    updated = true;
                }
            }
        });

        const checkValve = () => {
            this.valveController.setOpen(this.waterUntil > now.getTime(),'scheduler',err => {
               if (err) {
                    this.logger.error(`Unable to set valve status ${err.stack}`);
                }
                setTimeout(this.check,CHECK_INTERVAL);
            });
        }

        if (updated) {
            this.storage.setItem('schedule',{
                waterUntil: this.waterUntil,
                items: this.schedule
            },err => {
                if (err) {
                    return this.logger.error(`Unable to update schedule ${err.stack}`);
                }
                checkValve();
            });
        } else {
            checkValve();
        }
    }
    startOfDay(now) {
        return (new Date(now.getFullYear(),now.getMonth(),now.getDate())).getTime();
    }

}
<commit_msg>Scheduler no longer stops watering set manually by the user<commit_after>const CHECK_INTERVAL = 30000;

export default class Scheduler {
    constructor(storage,history,logger,valveController) {
        this.storage = storage;
        this.history = history;
        this.logger = logger;
        this.valveController = valveController;

        this.schedule = [];
        this.waterUntil = 0;
        this.valveController.on('setOpen',function({source}) {
            // manually setting the valve disables any
            // scheduled waterings
            if (source !== 'scheduler') {
                this.waterUntil = 0;
            }
        });
    }
    start() {
        this.check();
    }
    reload() {
        this.logger.info('Restarting scheduler...');
        this.storage.getItem('schedule',(err,value) => {
           if (err) {
                return this.logger.error(`Unable to reload schedule ${err.stack}`);
           }
           if (!value) {
                this.schedule = [];
           } else {
                this.schedule = value.items;
           }
        });
    }
    check = () => {
        const now = new Date();
        const currentHour = now.getHours();
        this.logger.verbose(`Checking schedule for hour ${currentHour}`);
        let updated = false;
        this.schedule.map(item => {
            this.logger.debug(JSON.stringify(item));
            if (item.time === currentHour
            && (!item.nextRun || now.getTime() >= item.nextRun)
            // don't start new tasks if we're more than 5 minutes
            // past the hour
            && now.getMinutes() < 5) {
                item.nextRun = this.startOfDay(now) + ((86400 * item.frequency) + (3600 * item.time)) * 1000;
                this.logger.verbose(`Schedule item will run - next run ${item.nextRun}`);
                const nextWater = now.getTime() + (item.duration * 60 * 1000);
                if (nextWater > this.waterUntil) {
                    this.logger.verbose(`Will water until ${nextWater}`);
                    this.waterUntil = nextWater;
                    updated = true;
                }
            }
        });

        const checkValve = () => {
            if (!this.waterUntil) return;
            this.valveController.setOpen(this.waterUntil > now.getTime(),'scheduler',err => {
               if (err) {
                    this.logger.error(`Unable to set valve status ${err.stack}`);
                }
                setTimeout(this.check,CHECK_INTERVAL);
            });
        }

        if (updated) {
            this.storage.setItem('schedule',{
                items: this.schedule
            },err => {
                if (err) {
                    return this.logger.error(`Unable to update schedule ${err.stack}`);
                }
                checkValve();
            });
        } else {
            checkValve();
        }
    }
    startOfDay(now) {
        return (new Date(now.getFullYear(),now.getMonth(),now.getDate())).getTime();
    }

}
<|endoftext|>


<commit_before>import React from 'react';
import {HorizontalBar} from 'react-chartjs-2';

const data = {
 labels: ['Cantidad', 'Horas', 'Salario'],
 datasets: [
   {
     label: 'Mujeres',
     backgroundColor: '#20A19A',
     borderColor: '#20A19A',
     borderWidth: 1,
     hoverBackgroundColor: '#20A19A',
     hoverBorderColor: '#20A19A',
     data: [50, 49, 47, 0]
   },
   {
     label: 'Hombres',
     backgroundColor: '#FF6A72',
     borderColor: '#FF6A72',
     borderWidth: 1,
     hoverBackgroundColor: '#FF6A72',
     hoverBorderColor: '#FF6A72',
     data: [50, 51, 53, 0]
   }
 ]

};

const Parity = () => {
 return (

Paridad del Equipo

     <HorizontalBar
      width={650}
      data={data}
      height={450}
      options={{
       scales:
       {
         xAxes:[{display: false}],
         yAxes:[
           {
             gridLines: {display: false},
            ticks: {fontSize: 20}
          }
          ]
       }
       }
     }
      />

 )

}

export default Parity<commit_msg>changed to state component<commit_after>import React, {Component} from 'react';
import {HorizontalBar} from 'react-chartjs-2';

class Parity extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: {
        labels: ['Cantidad', 'Horas', 'Salario'],
        datasets: [
          {
            label: 'Mujeres',
            backgroundColor: '#20A19A',
            borderColor: '#20A19A',
            borderWidth: 1,
            hoverBackgroundColor: '#20A19A',
            hoverBorderColor: '#20A19A',
            data: [50, 49, 47, 0]
          },
          {
            label: 'Hombres',
            backgroundColor: '#FF6A72',
            borderColor: '#FF6A72',
            borderWidth: 1,
            hoverBackgroundColor: '#FF6A72',
            hoverBorderColor: '#FF6A72',
            data: [50, 51, 53, 0]
          }
        ]

       }
    }
  }
render() {
  const {data} = this.state
  const {subtitle} = this.props
  return (

{subtitle}

      <HorizontalBar
       width={650}
       data={data}
       height={450}
       options={{
        scales:
        {
          xAxes:[{display: false}],
          yAxes:[
            {
              gridLines: {display: false},
             ticks: {fontSize: 20}
           }
           ]
        }
        }
      }
       />

  )

}
}

export default Parity<|endoftext|>


<commit_before>/*eslint no-console: 0*/

import { mapUrl } from './utils/url.js'
import * as actions from './actions/index'
import PrettyError from 'pretty-error'
import upstreamConfig from './config'
import express from 'express'
import redis from 'redis'
import serverConfig from '../server/config'

const pretty = new PrettyError()
const app = express()

let redisClient = redis.createClient({
  host: upstreamConfig.REDIS_HOST || 'localhost',
  port: upstreamConfig.REDIS_PORT || '',
  retry_strategy: function (options) {
    if (options.error.code === 'ECONNREFUSED') {
      // End reconnecting on a specific error and flush all commands with a individual error
      return new Error('The server refused the connection')
    }
    if (options.total_retry_time > 1000 * 60 * 60) {
      // End reconnecting after a specific timeout and flush all commands with a individual error
      return new Error('Retry time exhausted')
    }
    if (options.times_connected > 10) {
      // End reconnecting with built in error
      return undefined
    }
    // reconnect after
    return Math.max(options.attempt * 100, 3000)
  }
})

function getDataFromRedis(url) {
  return new Promise((resolve, reject) => {
    redisClient.get(url, (err, reply) => {
      try {
        reply = JSON.parse(reply)
        // url is not cached
        if (reply === null) {
          return reject()
        }

        // cached
        return resolve(reply)
      } catch(e) {
        console.warning('Getting cache from REDIS occurs error: ', e)
        return reject(e)
      }
    })
  })
}

function getDataFromAPI(req, res) {
  const splittedUrlPath = req.url.split('?')[0].split('/').slice(1)
  const { action, params } = mapUrl(actions, splittedUrlPath)
  if (action) {
    action(req, params)
      .then((result) => {
        if (result instanceof Function) {
          result(res)
        } else {
          res.json(result)
          redisClient.set(req.url, JSON.stringify(result), redis.print)
        }
      }, (reason) => {
        if (reason && reason.redirect) {
          res.redirect(reason.redirect)
        } else {
          console.error('API ERROR:', pretty.render(reason))
          res.status(reason.status || 500).json(reason)
        }
      })
  } else {
    res.status(404).end('NOT FOUND')
  }
}

app.use((req, res) => {
  getDataFromRedis(req.url).then((cacheData) => {
    res.json(cacheData)
  }, (reason) => { // eslint-disable-line
    getDataFromAPI(req, res)
  })
})

if (serverConfig.apiPort) {
  app.listen(serverConfig.apiPort, (err) => {
    if (err) {
      console.error(err)
    }
    console.info('----\n==> 🌎  API is running on port %s', serverConfig.apiPort)
    console.info('==> 💻  Send requests to http://%s:%s', serverConfig.apiHost, serverConfig.apiPort)
  })
} else {
  console.error('==>     ERROR: No PORT environment variable has been specified')
}
<commit_msg>[Enhancement] set expiration on redis set<commit_after>/*eslint no-console: 0*/

import { mapUrl } from './utils/url.js'
import * as actions from './actions/index'
import PrettyError from 'pretty-error'
import upstreamConfig from './config'
import express from 'express'
import redis from 'redis'
import serverConfig from '../server/config'

const EXPIRE = 600 // ten mins
const pretty = new PrettyError()
const app = express()

let redisClient = redis.createClient({
  host: upstreamConfig.REDIS_HOST || 'localhost',
  port: upstreamConfig.REDIS_PORT || '',
  retry_strategy: function (options) {
    if (options.error.code === 'ECONNREFUSED') {
      // End reconnecting on a specific error and flush all commands with a individual error
      return new Error('The server refused the connection')
    }
    if (options.total_retry_time > 1000 * 60 * 60) {
      // End reconnecting after a specific timeout and flush all commands with a individual error
      return new Error('Retry time exhausted')
    }
    if (options.times_connected > 10) {
      // End reconnecting with built in error
      return undefined
    }
    // reconnect after
    return Math.max(options.attempt * 100, 3000)
  }
})

function getDataFromRedis(url) {
  return new Promise((resolve, reject) => {
    redisClient.get(url, (err, reply) => {
      try {
        reply = JSON.parse(reply)
        // url is not cached
        if (reply === null) {
          return reject()
        }
        // cached
        return resolve(reply)
      } catch(e) {
        console.warning('Getting cache from REDIS occurs error: ', e)
        return reject(e)
      }
    })
  })
}

function getDataFromAPI(req, res) {
  const splittedUrlPath = req.url.split('?')[0].split('/').slice(1)
  const { action, params } = mapUrl(actions, splittedUrlPath)
  if (action) {
    action(req, params)
      .then((result) => {
        if (result instanceof Function) {
          result(res)
        } else {
          res.json(result)
          redisClient.setex(req.url, EXPIRE, JSON.stringify(result), redis.print)
        }
      }, (reason) => {
        if (reason && reason.redirect) {
          res.redirect(reason.redirect)
        } else {
          console.error('API ERROR:', pretty.render(reason))
          res.status(reason.status || 500).json(reason)
        }
      })
  } else {
    res.status(404).end('NOT FOUND')
  }
}

app.use((req, res) => {
  getDataFromRedis(req.url).then((cacheData) => {
    res.json(cacheData)
  }, (reason) => { // eslint-disable-line
    getDataFromAPI(req, res)
  })
})

if (serverConfig.apiPort) {
  app.listen(serverConfig.apiPort, (err) => {
    if (err) {
      console.error(err)
    }
    console.info('----\n==> 🌎  API is running on port %s', serverConfig.apiPort)
    console.info('==> 💻  Send requests to http://%s:%s', serverConfig.apiHost, serverConfig.apiPort)
  })
} else {
  console.error('==>     ERROR: No PORT environment variable has been specified')
}
<|endoftext|>


<commit_before>/**
*
* Manual Multi-Sample Anti-Aliasing Render Pass
*
* @author bhouston / http://clara.io/
*
* This manual approach to MSAA re-renders the scene ones for each sample with camera jitter and accumulates the results.
*
* References: https://en.wikipedia.org/wiki/Multisample_anti-aliasing
*
*/

THREE.ManualMSAARenderPass = function ( scene, camera, clearColor, clearAlpha ) {

	THREE.Pass.call( this );

	this.scene = scene;
	this.camera = camera;

	this.sampleLevel = 4; // specified as n, where the number of samples is 2^n, so sampleLevel = 4, is 2^4 samples, 16.
	this.unbiased = true;

	// as we need to clear the buffer in this pass, clearColor must be set to something, defaults to black.
	this.clearColor = ( clearColor !== undefined ) ? clearColor : new THREE.Color( 0, 0, 0 );
	this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 1;

	if ( THREE.CopyShader === undefined ) console.error( "THREE.ManualMSAARenderPass relies on THREE.CopyShader" );

	var copyShader = THREE.CopyShader;
	this.copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms );

	this.copyMaterial = new THREE.ShaderMaterial(	{
		uniforms: this.copyUniforms,
		vertexShader: copyShader.vertexShader,
		fragmentShader: copyShader.fragmentShader,
		premultipliedAlpha: true,
		transparent: true,
		blending: THREE.AdditiveBlending,
		depthTest: false,
		depthWrite: false
	} );

	this.camera2 = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
	this.scene2	= new THREE.Scene();
	this.quad2 = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), this.copyMaterial );
	this.scene2.add( this.quad2 );

};

THREE.ManualMSAARenderPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), {

	constructor: THREE.ManualMSAARenderPass,

	dispose: function() {

		if ( this.sampleRenderTarget ) {

			this.sampleRenderTarget.dispose();
			this.sampleRenderTarget = null;

		}

	},

	setSize: function ( width, height ) {

		if ( this.sampleRenderTarget )	this.sampleRenderTarget.setSize( width, height );

	},

	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {

		if ( ! this.sampleRenderTarget ) {

			this.sampleRenderTarget = new THREE.WebGLRenderTarget( readBuffer.width, readBuffer.height,
				{ minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat } );

		}

		var jitterOffsets = THREE.ManualMSAARenderPass.JitterVectors[ Math.max( 0, Math.min( this.sampleLevel, 5 ) ) ];

		var autoClear = renderer.autoClear;
		renderer.autoClear = false;

		var oldClearColorHex = renderer.getClearColor().getHex(), oldClearAlpha = renderer.getClearAlpha();

		var baseSampleWeight = 1.0 / jitterOffsets.length;
		var roundingRange = 1 / 32;
		this.copyUniforms[ "tDiffuse" ].value = this.sampleRenderTarget.texture;

		var width = readBuffer.width, height = readBuffer.height;

		// render the scene multiple times, each slightly jitter offset from the last and accumulate the results.
		for ( var i = 0; i < jitterOffsets.length; i ++ ) {

			var jitterOffset = jitterOffsets[i];
			if ( this.camera.setViewOffset ) {
				this.camera.setViewOffset( width, height,
					jitterOffset[ 0 ] * 0.0625, jitterOffset[ 1 ] * 0.0625,   // 0.0625 = 1 / 16
					width, height );
			}

			var sampleWeight = baseSampleWeight;
			if( this.unbiased ) {
				// the theory is that equal weights for each sample lead to an accumulation of rounding errors.
				// The following equation varies the sampleWeight per sample so that it is uniformly distributed
				// across a range of values whose rounding errors cancel each other out.
				var uniformCenteredDistribution = ( -0.5 + ( i + 0.5 ) / jitterOffsets.length );
				sampleWeight += roundingRange * uniformCenteredDistribution;
			}

			this.copyUniforms[ "opacity" ].value = sampleWeight;
			renderer.setClearColor( this.clearColor, this.clearAlpha );
			renderer.render( this.scene, this.camera, this.sampleRenderTarget, true );
			if (i === 0) {
				renderer.setClearColor( 0x000000, 0.0 );
			}
			renderer.render( this.scene2, this.camera2, this.renderToScreen ? null : writeBuffer, (i === 0) );

		}

		if ( this.camera.clearViewOffset ) this.camera.clearViewOffset();

		renderer.autoClear = autoClear;
		renderer.setClearColor( oldClearColorHex, oldClearAlpha );
	}

} );

// These jitter vectors are specified in integers because it is easier.
// I am assuming a [-8,8) integer grid, but it needs to be mapped onto [-0.5,0.5)
// before being used, thus these integers need to be scaled by 1/16.
//
// Sample patterns reference: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476218%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
THREE.ManualMSAARenderPass.JitterVectors = [
	[
		[ 0, 0 ]
	],
	[
		[ 4, 4 ], [ - 4, - 4 ]
	],
	[
		[ - 2, - 6 ], [ 6, - 2 ], [ - 6, 2 ], [ 2, 6 ]
	],
	[
		[ 1, - 3 ], [ - 1, 3 ], [ 5, 1 ], [ - 3, - 5 ],
		[ - 5, 5 ], [ - 7, - 1 ], [ 3, 7 ], [ 7, - 7 ]
	],
	[
		[ 1, 1 ], [ - 1, - 3 ], [ - 3, 2 ], [ 4, - 1 ],
		[ - 5, - 2 ], [ 2, 5 ], [ 5, 3 ], [ 3, - 5 ],
		[ - 2, 6 ], [ 0, - 7 ], [ - 4, - 6 ], [ - 6, 4 ],
		[ - 8, 0 ], [ 7, - 4 ], [ 6, 7 ], [ - 7, - 8 ]
	],
	[
		[ - 4, - 7 ], [ - 7, - 5 ], [ - 3, - 5 ], [ - 5, - 4 ],
		[ - 1, - 4 ], [ - 2, - 2 ], [ - 6, - 1 ], [ - 4, 0 ],
		[ - 7, 1 ], [ - 1, 2 ], [ - 6, 3 ], [ - 3, 3 ],
		[ - 7, 6 ], [ - 3, 6 ], [ - 5, 7 ], [ - 1, 7 ],
		[ 5, - 7 ], [ 1, - 6 ], [ 6, - 5 ], [ 4, - 4 ],
		[ 2, - 3 ], [ 7, - 2 ], [ 1, - 1 ], [ 4, - 1 ],
		[ 2, 1 ], [ 6, 2 ], [ 0, 4 ], [ 4, 4 ],
		[ 2, 5 ], [ 7, 5 ], [ 5, 6 ], [ 3, 7 ]
	]
];
<commit_msg>hex rather than color.<commit_after>/**
*
* Manual Multi-Sample Anti-Aliasing Render Pass
*
* @author bhouston / http://clara.io/
*
* This manual approach to MSAA re-renders the scene ones for each sample with camera jitter and accumulates the results.
*
* References: https://en.wikipedia.org/wiki/Multisample_anti-aliasing
*
*/

THREE.ManualMSAARenderPass = function ( scene, camera, clearColor, clearAlpha ) {

	THREE.Pass.call( this );

	this.scene = scene;
	this.camera = camera;

	this.sampleLevel = 4; // specified as n, where the number of samples is 2^n, so sampleLevel = 4, is 2^4 samples, 16.
	this.unbiased = true;

	// as we need to clear the buffer in this pass, clearColor must be set to something, defaults to black.
	this.clearColor = ( clearColor !== undefined ) ? clearColor : 0x000000;
	this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 1;

	if ( THREE.CopyShader === undefined ) console.error( "THREE.ManualMSAARenderPass relies on THREE.CopyShader" );

	var copyShader = THREE.CopyShader;
	this.copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms );

	this.copyMaterial = new THREE.ShaderMaterial(	{
		uniforms: this.copyUniforms,
		vertexShader: copyShader.vertexShader,
		fragmentShader: copyShader.fragmentShader,
		premultipliedAlpha: true,
		transparent: true,
		blending: THREE.AdditiveBlending,
		depthTest: false,
		depthWrite: false
	} );

	this.camera2 = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
	this.scene2	= new THREE.Scene();
	this.quad2 = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), this.copyMaterial );
	this.scene2.add( this.quad2 );

};

THREE.ManualMSAARenderPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), {

	constructor: THREE.ManualMSAARenderPass,

	dispose: function() {

		if ( this.sampleRenderTarget ) {

			this.sampleRenderTarget.dispose();
			this.sampleRenderTarget = null;

		}

	},

	setSize: function ( width, height ) {

		if ( this.sampleRenderTarget )	this.sampleRenderTarget.setSize( width, height );

	},

	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {

		if ( ! this.sampleRenderTarget ) {

			this.sampleRenderTarget = new THREE.WebGLRenderTarget( readBuffer.width, readBuffer.height,
				{ minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat } );

		}

		var jitterOffsets = THREE.ManualMSAARenderPass.JitterVectors[ Math.max( 0, Math.min( this.sampleLevel, 5 ) ) ];

		var autoClear = renderer.autoClear;
		renderer.autoClear = false;

		var oldClearColorHex = renderer.getClearColor().getHex(), oldClearAlpha = renderer.getClearAlpha();

		var baseSampleWeight = 1.0 / jitterOffsets.length;
		var roundingRange = 1 / 32;
		this.copyUniforms[ "tDiffuse" ].value = this.sampleRenderTarget.texture;

		var width = readBuffer.width, height = readBuffer.height;

		// render the scene multiple times, each slightly jitter offset from the last and accumulate the results.
		for ( var i = 0; i < jitterOffsets.length; i ++ ) {

			var jitterOffset = jitterOffsets[i];
			if ( this.camera.setViewOffset ) {
				this.camera.setViewOffset( width, height,
					jitterOffset[ 0 ] * 0.0625, jitterOffset[ 1 ] * 0.0625,   // 0.0625 = 1 / 16
					width, height );
			}

			var sampleWeight = baseSampleWeight;
			if( this.unbiased ) {
				// the theory is that equal weights for each sample lead to an accumulation of rounding errors.
				// The following equation varies the sampleWeight per sample so that it is uniformly distributed
				// across a range of values whose rounding errors cancel each other out.
				var uniformCenteredDistribution = ( -0.5 + ( i + 0.5 ) / jitterOffsets.length );
				sampleWeight += roundingRange * uniformCenteredDistribution;
			}

			this.copyUniforms[ "opacity" ].value = sampleWeight;
			renderer.setClearColor( this.clearColor, this.clearAlpha );
			renderer.render( this.scene, this.camera, this.sampleRenderTarget, true );
			if (i === 0) {
				renderer.setClearColor( 0x000000, 0.0 );
			}
			renderer.render( this.scene2, this.camera2, this.renderToScreen ? null : writeBuffer, (i === 0) );

		}

		if ( this.camera.clearViewOffset ) this.camera.clearViewOffset();

		renderer.autoClear = autoClear;
		renderer.setClearColor( oldClearColorHex, oldClearAlpha );
	}

} );

// These jitter vectors are specified in integers because it is easier.
// I am assuming a [-8,8) integer grid, but it needs to be mapped onto [-0.5,0.5)
// before being used, thus these integers need to be scaled by 1/16.
//
// Sample patterns reference: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476218%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
THREE.ManualMSAARenderPass.JitterVectors = [
	[
		[ 0, 0 ]
	],
	[
		[ 4, 4 ], [ - 4, - 4 ]
	],
	[
		[ - 2, - 6 ], [ 6, - 2 ], [ - 6, 2 ], [ 2, 6 ]
	],
	[
		[ 1, - 3 ], [ - 1, 3 ], [ 5, 1 ], [ - 3, - 5 ],
		[ - 5, 5 ], [ - 7, - 1 ], [ 3, 7 ], [ 7, - 7 ]
	],
	[
		[ 1, 1 ], [ - 1, - 3 ], [ - 3, 2 ], [ 4, - 1 ],
		[ - 5, - 2 ], [ 2, 5 ], [ 5, 3 ], [ 3, - 5 ],
		[ - 2, 6 ], [ 0, - 7 ], [ - 4, - 6 ], [ - 6, 4 ],
		[ - 8, 0 ], [ 7, - 4 ], [ 6, 7 ], [ - 7, - 8 ]
	],
	[
		[ - 4, - 7 ], [ - 7, - 5 ], [ - 3, - 5 ], [ - 5, - 4 ],
		[ - 1, - 4 ], [ - 2, - 2 ], [ - 6, - 1 ], [ - 4, 0 ],
		[ - 7, 1 ], [ - 1, 2 ], [ - 6, 3 ], [ - 3, 3 ],
		[ - 7, 6 ], [ - 3, 6 ], [ - 5, 7 ], [ - 1, 7 ],
		[ 5, - 7 ], [ 1, - 6 ], [ 6, - 5 ], [ 4, - 4 ],
		[ 2, - 3 ], [ 7, - 2 ], [ 1, - 1 ], [ 4, - 1 ],
		[ 2, 1 ], [ 6, 2 ], [ 0, 4 ], [ 4, 4 ],
		[ 2, 5 ], [ 7, 5 ], [ 5, 6 ], [ 3, 7 ]
	]
];
<|endoftext|>


<commit_before>import Ember from 'ember';
import { ACTION } from '../-private/internals';

const {
  A: emberArray,
  Helper,
  assert,
  computed,
  typeOf,
  get,
  getOwner,
  run,
  runInDebug
} = Ember;

function getRoutes(router) {
  return emberArray(router.router.currentHandlerInfos)
    .mapBy('handler')
    .reverse();
}

function getRouteWithAction(router, actionName) {
  let action;
  let handler = emberArray(getRoutes(router)).find((route) => {
    let actions = route.actions || route._actions;
    action = actions[actionName];

    return typeOf(action) === 'function';
  });

  return { action, handler };
}

export default Helper.extend({
  router: computed(function() {
    return getOwner(this).lookup('router:main');
  }).readOnly(),

  compute([actionName, ...params]) {
    let router = get(this, 'router');
    assert('[ember-route-action-helper] Unable to lookup router', router);

    runInDebug(() => {
      let { handler } = getRouteWithAction(router, actionName);
      assert(`[ember-route-action-helper] Unable to find action ${actionName}`, handler);
    });

    let routeAction = function(...invocationArgs) {
      let { action, handler } = getRouteWithAction(router, actionName);
      let args = params.concat(invocationArgs);
      return run.join(handler, action, ...args);
    };

    routeAction[ACTION] = true;

    return routeAction;
  }
});
<commit_msg>Fix deprecation warning in Ember 2.13 (#53)<commit_after>import Ember from 'ember';
import { ACTION } from '../-private/internals';

const {
  A: emberArray,
  Helper,
  assert,
  computed,
  typeOf,
  get,
  getOwner,
  run,
  runInDebug
} = Ember;

function getCurrentHandlerInfos(router) {
  let routerLib = router._routerMicrolib || router.router;

  return routerLib.currentHandlerInfos;
}

function getRoutes(router) {
  return emberArray(getCurrentHandlerInfos(router))
    .mapBy('handler')
    .reverse();
}

function getRouteWithAction(router, actionName) {
  let action;
  let handler = emberArray(getRoutes(router)).find((route) => {
    let actions = route.actions || route._actions;
    action = actions[actionName];

    return typeOf(action) === 'function';
  });

  return { action, handler };
}

export default Helper.extend({
  router: computed(function() {
    return getOwner(this).lookup('router:main');
  }).readOnly(),

  compute([actionName, ...params]) {
    let router = get(this, 'router');
    assert('[ember-route-action-helper] Unable to lookup router', router);

    runInDebug(() => {
      let { handler } = getRouteWithAction(router, actionName);
      assert(`[ember-route-action-helper] Unable to find action ${actionName}`, handler);
    });

    let routeAction = function(...invocationArgs) {
      let { action, handler } = getRouteWithAction(router, actionName);
      let args = params.concat(invocationArgs);
      return run.join(handler, action, ...args);
    };

    routeAction[ACTION] = true;

    return routeAction;
  }
});
<|endoftext|>


<commit_before>// ===================================================================================================
//                           _  __     _ _
//                          | |/ /__ _| | |_ _  _ _ _ __ _
//                          | ' </ _` | |  _| || | '_/ _` |
//                          |_|\_\__,_|_|\__|\_,_|_| \__,_|
//
// This file is part of the Kaltura Collaborative Media Suite which allows users
// to do with audio, video, and animation what Wiki platfroms allow them to do with
// text.
//
// Copyright (C) 2006-2019  Kaltura Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// @ignore
// ===================================================================================================
/**
 * The Kaltura Client - this is the facade through which all service actions should be called.
 * @param config the Kaltura configuration object holding partner credentials (type: KalturaConfiguration).
 */
var util = require('util');
var kaltura = require('./KalturaClientBase');
kaltura.services = require('./KalturaServices');
kaltura.objects = require('./KalturaModel');
kaltura.enums = require('./KalturaTypes');

class Client extends kaltura.ClientBase {

	/**
	 * @param Configuration config
	 */
	constructor(config) {
		super(config);
		this.setApiVersion('15.8.0');
		this.setClientTag('node:19-10-06');
	}
}

module.exports = kaltura;
module.exports.Client = Client;

<commit_msg>New clientlib ver<commit_after>// ===================================================================================================
//                           _  __     _ _
//                          | |/ /__ _| | |_ _  _ _ _ __ _
//                          | ' </ _` | |  _| || | '_/ _` |
//                          |_|\_\__,_|_|\__|\_,_|_| \__,_|
//
// This file is part of the Kaltura Collaborative Media Suite which allows users
// to do with audio, video, and animation what Wiki platfroms allow them to do with
// text.
//
// Copyright (C) 2006-2019  Kaltura Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// @ignore
// ===================================================================================================
/**
 * The Kaltura Client - this is the facade through which all service actions should be called.
 * @param config the Kaltura configuration object holding partner credentials (type: KalturaConfiguration).
 */
var util = require('util');
var kaltura = require('./KalturaClientBase');
kaltura.services = require('./KalturaServices');
kaltura.objects = require('./KalturaModel');
kaltura.enums = require('./KalturaTypes');

class Client extends kaltura.ClientBase {

	/**
	 * @param Configuration config
	 */
	constructor(config) {
		super(config);
		this.setApiVersion('15.8.0');
		this.setClientTag('node:19-10-07');
	}
}

module.exports = kaltura;
module.exports.Client = Client;

<|endoftext|>


<commit_before>/* eslint-disable no-console */
import Table from 'cli-table';
import { inspect } from 'util';
import { Module } from 'oors';
import winston from 'winston';

class LoggerModule extends Module {
  static schema = {
    type: 'object',
    properties: {
      printModules: {
        type: 'boolean',
        default: true,
      },
      printDependencyGraph: {
        type: 'boolean',
        default: true,
      },
      printMiddlewares: {
        type: 'boolean',
        default: true,
      },
      logger: {
        type: 'object',
      },
    },
    required: [],
  };
  name = 'oors.logger';

  initialize({ printModules, printDependencyGraph, printMiddlewares, logger }) {
    this.logger =
      logger ||
      winston.createLogger({
        transports: [
          new winston.transports.Console({
            colorize: true,
            handleExceptions: true,
            humanReadableUnhandledException: true,
            json: true,
            prettyPrint: true,
          }),
        ],
      });

    process.on('unhandledRejection', reason => this.logger.error(reason));

    this.export({
      logger: this.logger,
      log: (...args) => this.logger.log(...args),
      ...['error', 'warn', 'info', 'verbose', 'debug', 'silly'].reduce(
        (acc, level) => ({
          ...acc,
          [level]: (...args) => this.logger[level](...args),
        }),
        {},
      ),
    });

    if (printModules) {
      this.printModules();
    }

    if (printDependencyGraph) {
      this.printDependencyGraph();
    }

    if (printMiddlewares) {
      this.printMiddlewares();
    }
  }

  printModules() {
    const modulesTable = new Table({
      head: ['Modules'],
    });

    this.on('module:loaded', module => {
      modulesTable.push([module.name]);
    });

    this.app.once('after:boot', () => {
      this.logger.info(modulesTable.toString());
    });
  }

  printDependencyGraph() {
    this.app.once('after:boot', () => {
      console.log(this.manager.expandedDependencyGraph);
    });
  }

  printMiddlewares() {
    this.app.once('after:boot', () => {
      const table = new Table({
        head: ['Id', 'Path', 'Params'],
      });

      this.app.middlewares.reject({ enabled: false }).forEach(({ path: mPath, id, params }) => {
        table.push([id, mPath || '/', typeof params !== 'undefined' ? inspect(params) : 'N/A']);
      });

      console.log(table.toString());
    });
  }
}

export { LoggerModule as default };
<commit_msg>[oors-logger] bugfix<commit_after>/* eslint-disable no-console */
import Table from 'cli-table';
import { inspect } from 'util';
import { Module } from 'oors';
import * as winston from 'winston';

class LoggerModule extends Module {
  static schema = {
    type: 'object',
    properties: {
      printModules: {
        type: 'boolean',
        default: true,
      },
      printDependencyGraph: {
        type: 'boolean',
        default: true,
      },
      printMiddlewares: {
        type: 'boolean',
        default: true,
      },
      logger: {
        type: 'object',
      },
    },
    required: [],
  };
  name = 'oors.logger';

  initialize({ printModules, printDependencyGraph, printMiddlewares, logger }) {
    this.logger =
      logger ||
      winston.createLogger({
        transports: [
          new winston.transports.Console({
            colorize: true,
            handleExceptions: true,
            humanReadableUnhandledException: true,
            json: true,
            prettyPrint: true,
          }),
        ],
      });

    process.on('unhandledRejection', reason => this.logger.error(reason));

    this.export({
      logger: this.logger,
      log: (...args) => this.logger.log(...args),
      ...['error', 'warn', 'info', 'verbose', 'debug', 'silly'].reduce(
        (acc, level) => ({
          ...acc,
          [level]: (...args) => this.logger[level](...args),
        }),
        {},
      ),
    });

    if (printModules) {
      this.printModules();
    }

    if (printDependencyGraph) {
      this.printDependencyGraph();
    }

    if (printMiddlewares) {
      this.printMiddlewares();
    }
  }

  printModules() {
    const modulesTable = new Table({
      head: ['Modules'],
    });

    this.on('module:loaded', module => {
      modulesTable.push([module.name]);
    });

    this.app.once('after:boot', () => {
      this.logger.info(modulesTable.toString());
    });
  }

  printDependencyGraph() {
    this.app.once('after:boot', () => {
      console.log(this.manager.expandedDependencyGraph);
    });
  }

  printMiddlewares() {
    this.app.once('after:boot', () => {
      const table = new Table({
        head: ['Id', 'Path', 'Params'],
      });

      this.app.middlewares.reject({ enabled: false }).forEach(({ path: mPath, id, params }) => {
        table.push([id, mPath || '/', typeof params !== 'undefined' ? inspect(params) : 'N/A']);
      });

      console.log(table.toString());
    });
  }
}

export { LoggerModule as default };
<|endoftext|>


<commit_before>module.exports = {
  // D3 Overrides
  rules: {
    // allow trailing commas in object literals
    'comma-dangle': [2, 'never'],

    // specify the maximum length of a line in your program
    'max-len': [
      1,
      200,
      2,
      {
        ignoreUrls: true,
        ignoreComments: false,
        ignoreRegExpLiterals: true,
        ignoreStrings: true,
        ignoreTemplateLiterals: true
      }
    ],

    // requires to declare all vars on top of their containing scope
    'vars-on-top': 0,

    // enforce padding within blocks
    'padded-blocks': [0, 'never'],

    // require return statements to either always or never specify values
    'consistent-return': 0,

    // allow spacing inside array brackets
    'array-bracket-spacing': 0,

    // allow dangling underscores in variable names
    'no-underscore-dangle': 0,

    // allow use of classes and functions before they are defined
    'no-use-before-define': [1, { classes: false, functions: false }],

    // allow use of Object.prototypes builtins directly
    'no-prototype-builtins': [0],

    // enforce that class methods use "this"
    // http://eslint.org/docs/rules/class-methods-use-this
    'class-methods-use-this': [
      'off',
      {
        exceptMethods: []
      }
    ],

    // Allow chaining of assignment of variables
    'no-multi-assign': ['off'],

    // enforce line breaks between braces
    // http://eslint.org/docs/rules/object-curly-newline
    'object-curly-newline': [
      'error',
      {
        ObjectExpression: { minProperties: 6, multiline: true, consistent: true },
        ObjectPattern: { minProperties: 6, multiline: true, consistent: true }
      }
    ],

    // Don't enforce line breaks in classes
    'lines-between-class-members': 'off',

    // Allow returning assignment if inside parens
    'no-return-assign': ['error', 'except-parens'],

    // Prefer destructuring from arrays and objects
    // https://eslint.org/docs/rules/prefer-destructuring
    'prefer-destructuring': [
      'error',
      {
        VariableDeclarator: {
          array: false,
          object: true
        },
        AssignmentExpression: {
          array: false,
          object: false
        }
      },
      {
        enforceForRenamedProperties: false
      }
    ],

    // Allow promise rejections to be strings
    'prefer-promise-reject-errors': 'off',

    // This doesn't really follow our pattern for reducers
    'default-case': 'off',

    // unused
    'no-useless-catch': ['off'],
    'jsx-a11y/control-has-associated-label': 'off'
  }
};
<commit_msg>fix: allow using await in a loop<commit_after>module.exports = {
  // D3 Overrides
  rules: {
    // allow trailing commas in object literals
    'comma-dangle': [2, 'never'],

    // specify the maximum length of a line in your program
    'max-len': [
      1,
      200,
      2,
      {
        ignoreUrls: true,
        ignoreComments: false,
        ignoreRegExpLiterals: true,
        ignoreStrings: true,
        ignoreTemplateLiterals: true
      }
    ],

    // requires to declare all vars on top of their containing scope
    'vars-on-top': 0,

    // enforce padding within blocks
    'padded-blocks': [0, 'never'],

    // require return statements to either always or never specify values
    'consistent-return': 0,

    // allow spacing inside array brackets
    'array-bracket-spacing': 0,

    // allow dangling underscores in variable names
    'no-underscore-dangle': 0,

    // allow use of classes and functions before they are defined
    'no-use-before-define': [1, { classes: false, functions: false }],

    // allow use of Object.prototypes builtins directly
    'no-prototype-builtins': [0],

    // enforce that class methods use "this"
    // http://eslint.org/docs/rules/class-methods-use-this
    'class-methods-use-this': [
      'off',
      {
        exceptMethods: []
      }
    ],

    // Allow chaining of assignment of variables
    'no-multi-assign': ['off'],

    // enforce line breaks between braces
    // http://eslint.org/docs/rules/object-curly-newline
    'object-curly-newline': [
      'error',
      {
        ObjectExpression: { minProperties: 6, multiline: true, consistent: true },
        ObjectPattern: { minProperties: 6, multiline: true, consistent: true }
      }
    ],

    // Don't enforce line breaks in classes
    'lines-between-class-members': 'off',

    // Allow returning assignment if inside parens
    'no-return-assign': ['error', 'except-parens'],

    // Prefer destructuring from arrays and objects
    // https://eslint.org/docs/rules/prefer-destructuring
    'prefer-destructuring': [
      'error',
      {
        VariableDeclarator: {
          array: false,
          object: true
        },
        AssignmentExpression: {
          array: false,
          object: false
        }
      },
      {
        enforceForRenamedProperties: false
      }
    ],

    // Allow promise rejections to be strings
    'prefer-promise-reject-errors': 'off',

    // This doesn't really follow our pattern for reducers
    'default-case': 'off',

    // Allow using await in a loop
    'no-await-in-loop': 'off',

    // unused
    'no-useless-catch': ['off'],
    'jsx-a11y/control-has-associated-label': 'off'
  }
};
<|endoftext|>


<commit_before>/**
 * The function `reverse` computes the reversal
 * of a given `String` (don't copy-pasta!)
 *
 * @param {String} S to reverse
 * @return {String}
 */
function reverse(S){
      // YOUR CODE HERE: NO INTERNET COPY-PASTA!
    var newString = S;
    var reversed = "";
    for (var i = newString.length-1; i >= 0; i--){
      reversed = reversed + newString[i];
    }
    return reversed;
}

console.assert(reverse("") === ""); // Really?

console.assert(reverse("A") === "A"); // Jerk.

console.assert(reverse("cat") === "tac");

console.assert(reverse("ward") === "draw");

console.assert(reverse("books") === "skoob"); // Rokey, dokey...

console.assert(
  reverse("we don't want no trouble") === "elbuort on tnaw t'nod ew"
);

/**
 * Function `encode` accepts a `String` and produces
 * the appropriate ROT13 "encoded" version, i.e. every
 * character in `phrase` is "rotated" ahead by 13 characters.
 *
 * @see String.prototype.charCodeAt
 * @see String.prototype.fromCharCode
 * @see http://en.wikipedia.org/wiki/ROT13
 *
 * // Start with just `phrase`...
 * @param {String} phrase to encode
 * // Add `N` in part 2!
 * // @param {Number} N rotation to apply, default 13
 * @return {String} encoded with ROT13
 */
function encode(phrase,N ){
    var rot = N || 13;
    var oldString = phrase;
    var newString = "";
    for (var i in oldString){
      var currentNum = oldString.charCodeAt(i);
      var newNum = currentNum + rot;
      if (newNum > 122) {
        newNum = newNum - 26;
      }
      newString = newString + String.fromCharCode(newNum);
    }
    return newString;
    // YOUR CODE HERE... EMPHASIS ON **YOUR**
}

/**
 * Function `decode` accepts a `phrase` and `N` and
 * decoded it appropriately, i.e. every _word_ character
 * in `phrase` is rotated backward by `N` characters.
 *
 * @param {String} phrase to decode
 * @param {Number} N rotation to apply, default 13
 * @return {String} decoded by ROT-N
 */
function decode(phrase, N){
    // YOUR CODE HERE
}

// Produce more examples, please...
console.assert(encode("abc") === "nop");
console.assert(encode("hello") === "uryyb");
console.assert(encode("uryyb") === "hello");

console.assert(encode("hello", 2) === "jgnnq")
console.assert(decode("jgnnq", 2) === "hello")
<commit_msg>ROT13 working<commit_after>/**
 * The function `reverse` computes the reversal
 * of a given `String` (don't copy-pasta!)
 *
 * @param {String} S to reverse
 * @return {String}
 */
function reverse(S){
      // YOUR CODE HERE: NO INTERNET COPY-PASTA!
    var newString = S;
    var reversed = "";
    for (var i = newString.length-1; i >= 0; i--){
      reversed = reversed + newString[i];
    }
    return reversed;
}

console.assert(reverse("") === ""); // Really?

console.assert(reverse("A") === "A"); // Jerk.

console.assert(reverse("cat") === "tac");

console.assert(reverse("ward") === "draw");

console.assert(reverse("books") === "skoob"); // Rokey, dokey...

console.assert(
  reverse("we don't want no trouble") === "elbuort on tnaw t'nod ew"
);

/**
 * Function `encode` accepts a `String` and produces
 * the appropriate ROT13 "encoded" version, i.e. every
 * character in `phrase` is "rotated" ahead by 13 characters.
 *
 * @see String.prototype.charCodeAt
 * @see String.prototype.fromCharCode
 * @see http://en.wikipedia.org/wiki/ROT13
 *
 * // Start with just `phrase`...
 * @param {String} phrase to encode
 * // Add `N` in part 2!
 * // @param {Number} N rotation to apply, default 13
 * @return {String} encoded with ROT13
 */
function encode(phrase,N ){
    var rot = N || 13;
    var oldString = phrase;
    var newString = "";
    for (var i in oldString){
      var currentNum = oldString.charCodeAt(i);
      var newNum = currentNum + rot;
      if (newNum > 122) {
        newNum = newNum - 26;
      }
      newString = newString + String.fromCharCode(newNum);
    }
    return newString;
    // YOUR CODE HERE... EMPHASIS ON **YOUR**
}

/**
 * Function `decode` accepts a `phrase` and `N` and
 * decoded it appropriately, i.e. every _word_ character
 * in `phrase` is rotated backward by `N` characters.
 *
 * @param {String} phrase to decode
 * @param {Number} N rotation to apply, default 13
 * @return {String} decoded by ROT-N
 */
function decode(phrase, N){
  var rot = N || 13;
  var oldString = phrase;
  var newString = "";
  for (var i in oldString){
    var currentNum = oldString.charCodeAt(i);
    var newNum = currentNum - rot;
    if (newNum < 97) {
      newNum = newNum + 26;
    }
    newString = newString + String.fromCharCode(newNum);
  }
  return newString;
  // YOUR CODE HERE... EMPHASIS ON **YOUR**
}

// Produce more examples, please...
console.assert(encode("abc") === "nop");
console.assert(encode("hello") === "uryyb");
console.assert(encode("uryyb") === "hello");

console.assert(encode("hello", 2) === "jgnnq")
console.assert(decode("jgnnq", 2) === "hello")
<|endoftext|>