<commit_before>this.GobiertoBudgetConsultations.ConsultationResponsesController = (function() {
  function ConsultationResponsesController() {}

  ConsultationResponsesController.prototype.new = function() {
    _runConsultationApplication();
  };

  function _runConsultationApplication() {
    $(document).on('click', '.not-allowed', function(e){
      $(this).tipsy({offset: -40, className: 'tip-warning', fade: true, html: true, gravity: isMobile() ? 'sw' : 's', opacity: 1, trigger: 'manual' });
      $(this).tipsy('show');
    });

    $(document).on('click', '.budget-status-figure', function(e){
      $(this).tipsy({ offset: isMobile() ? -20 : 20, className: 'tip-info', fade: true, html: true, gravity: isMobile() ? 's' : 'n', opacity: 1, trigger: 'manual' });
      $(this).tipsy('show');
    });

    $(document).on('click', '.consultation-status-error', function(e){
      $(this).tipsy({ className: 'tip-warning', fade: true, html: true, gravity: 's', opacity: 1, trigger: 'manual' });
      $(this).tipsy('show');
    });

    $(document).on('click', '[data-tipsy-close]', function(e){
      $('.tipsy').remove();
    });

    var bus = new Vue();

    Vue.component('card-description', {
      props: ['card', 'figures'],
      template: (isMobile() ? '#card-description-mobile' : '#card-description-desktop'),
      methods: {
        beforeEnter: function (el) {
          el.style.opacity = 0
        },
        enter: function (el, done) {
          $(el).velocity("transition.slideDownIn", {duration: 200, delay: 100, complete: done});
        },
        leave: function (el, done) {
          $(el).velocity("transition.slideUpOut", {duration: 200, complete: done});
        }
      }
    });

    Vue.component('consultation-card', {
      props: ['card', 'active', 'figures'],
      template: (isMobile() ? '#card-mobile' : '#card-desktop'),
      computed: {
        iconForChoice: function(){
          if(this.card.choice === null) return;

          if(this.card.choice > 0) {
            return "fa-arrow-up";
          } else if (this.card.choice == 0){
            return "fa-circle";
          } else if (this.card.choice < 0){
            return "fa-arrow-down";
          }
        }
      },
      methods: {
        setActive: function(card, e) {
          $('.tipsy').remove();

          // Send the active event to the bus
          bus.$emit('active', card);

          card.toggleDesc = !card.toggleDesc;

          // Reset value
          card.hidden = null;

          // Stick it
          $(".desktop .consultation-info").stick_in_parent();
        }
      }
    });

    Vue.component('budget-box', {
      props: ['card', 'figures'],
      template: '#budget-box',
      methods: {
        showModal: function(d) {
          bus.$emit('open', d);
        },
        makeChoice: function(e) {
          e.preventDefault();
          $('.tipsy').remove();
          bus.$emit('cardOptionChosen', $(e.target), this.card);
        }
      }
    });

    Vue.component('budget-calculator', {
      props: ['active', 'next', 'statusText', 'status', 'statusClass'],
      template: '#budget-calculator',
      methods: {
        nextScreen: function(e){
          e.preventDefault();
          bus.$emit('cardOptionChosen', null, null);
        },
        submitConsultation: function(e){
          e.preventDefault();
          bus.$emit('submitConsultation');
        },
        showConsultationStatusError: function(e){
          e.preventDefault();
        }
      },
      updated: function(){
        var $el = $('[data-status-explanation]');
        $el.attr('title', $el.attr('title_' + this.status));
      }
    });

    Vue.component('description-modal', {
      props: ['current'],
      template: '#description-modal',
      methods: {
        hideModal: function() {
          bus.$emit('close');
        }
      }
    });

    var app = new Vue({
      el: (isMobile() ? "#consultation-mobile-app" : "#consultation-desktop-app"),
      data: {
        figures: false,
        active: false,
        modal: false,
        next: false,
        current: null,
        statusDifference: 0,
        cards: [],
      },
      computed: {
        statusText: function(){
          return I18n.t('gobierto_budget_consultations.consultation_statuses.' + this.status);
        },
        statusClass: function(){
          // Track difference between status
          // The status class variable maxes out at 4 to be applied to the card class
          var classId = this.statusDifference;

          if (classId > 4)  classId = 4;
          if (classId < -4) classId = -4;

          if (classId < 0) return 'surplus surplus-' + Math.abs(classId);
          if (classId > 0) return 'deficit deficit-' + Math.abs(classId);
        },
        status: function(){
          if(this.statusDifference === 0) return 'balance';
          if (this.statusDifference < 0)  return 'surplus';
          return 'deficit';
        }
      },
      methods: {
        fetchData: function(){
          var self = this;
          var url = $(self.$options.el).data('data-url');
          $.ajax({
            url: url,
            dataType: 'json',
            beforeSend: function(xhr){
              xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
            },
            success: function(response, textStatus, jqXHR){
              if(jqXHR.status == 200){
                Vue.set(self, 'cards', response);
              } else {
                // TODO: handle errors
              }
            },
          });
        },
        cardOptionChosenHandler: function($el, currentCard){
          this.choiceCardAndOpenNext($el, currentCard);
          var allSelected = true;
          app.$data.cards.forEach(function(card){
            if(card.choice === null)
              allSelected = false;
          });

          if(allSelected){
            Vue.set(app, 'next', true);
            $('.consultation-confirm').css('width', $('.description').width() - 79);

            $('body').animate({
              scrollTop: $(app.$el).offset().top - 25
            }, 500);
          }
        },
        choiceCardAndOpenNext: function($el, currentCard){
          if(currentCard !== null){
            var positiveCards = 0;
            var negativeCards = 0;
            app.$data.cards.forEach(function(card){
              if(currentCard === card) {
                card.choice = $el.data('value');
                card.toggleDesc = false;
              }
              if(card.choice > 0) {
                positiveCards++;
              } else if (card.choice < 0) {
                negativeCards++;
              }
            });
          }
          this.statusDifference = positiveCards - negativeCards;

          var found = false;
          app.$data.cards.forEach(function(card){
            if(!found && card.choice === null){
              card.toggleDesc = true;
              found = true;
              bus.$emit('active', card);
            }
          });
        },
        submitConsultation: function(){
          var self = this;
          var url = $(self.$options.el).data('submit-url');
          var data = app.$data.cards.map(function(card){
            return {
              item_id: card.id,
              selected_option: card.choice
            }
          });
          $.ajax({
            type: "POST",
            data: {
              consultation_response: {
                selected_options: data
              }
            },
            url: url,
            dataType: 'script',
            beforeSend: function(xhr){
              xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
            }
          });
        }
      },
      created: function() {
        $(this.$options.el).show();
        this.fetchData();

        bus.$on('active', function(currentCard) {
          Vue.set(app, 'active', true);

          app.$data.cards.forEach(function(card){
            if(currentCard != card)
              card.toggleDesc = false;
          });

          if (app.$data.next) {
            $('.consultation-confirm').addClass('slim');
            $('.slim').css('top', 0);
          }

          var $target = $('[data-card-id="'+currentCard.id+'"]');
          var $targetDescription = $('[data-card-description-id="'+currentCard.id+'"]');
          if($targetDescription.length){
            if ($('.card-wrapper').css('top') === 'auto') {
              var offset = $target.offset().top - 220;
            } else {
              var offset = $target.offset().top - 250;
            }
            $targetDescription.css({ top: offset + 'px' });
          }

          window.setTimeout(function(){
            // Scroll to selected card
            // Set the description status
            $('body').animate({
              scrollTop: $target.offset().top - (isMobile() ? 25 : 85)
            }, 300, function() {
              // Set individual card state
              if(isMobile()){
                // Once animation ends, check if the span is visible
                var isVisible = $target.find('.visibilityCheck').visible();

                if (isVisible !== 'undefined') {
                  // If is not visible, set hidden to true
                  currentCard.hidden = isVisible ? false : true;
                }
              }
            });
          }, (isMobile() ? 250 : 0));
        });

        bus.$on('open', function(d) {
          Vue.set(app, 'current', d);
          Vue.set(app, 'modal', true);
        });

        bus.$on('close', function() {
          Vue.set(app, 'modal', false);
        });

        bus.$on('cardOptionChosen', this.cardOptionChosenHandler);

        bus.$on('submitConsultation', this.submitConsultation);
      },
    });
  }

  return ConsultationResponsesController;
})();

this.GobiertoBudgetConsultations.consultation_responses_controller = new GobiertoBudgetConsultations.ConsultationResponsesController;
<commit_msg>Run animation after card is open<commit_after>this.GobiertoBudgetConsultations.ConsultationResponsesController = (function() {
  function ConsultationResponsesController() {}

  ConsultationResponsesController.prototype.new = function() {
    _runConsultationApplication();
  };

  function _runConsultationApplication() {
    $(document).on('click', '.not-allowed', function(e){
      $(this).tipsy({offset: -40, className: 'tip-warning', fade: true, html: true, gravity: isMobile() ? 'sw' : 's', opacity: 1, trigger: 'manual' });
      $(this).tipsy('show');
    });

    $(document).on('click', '.budget-status-figure', function(e){
      $(this).tipsy({ offset: isMobile() ? -20 : 20, className: 'tip-info', fade: true, html: true, gravity: isMobile() ? 's' : 'n', opacity: 1, trigger: 'manual' });
      $(this).tipsy('show');
    });

    $(document).on('click', '.consultation-status-error', function(e){
      $(this).tipsy({ className: 'tip-warning', fade: true, html: true, gravity: 's', opacity: 1, trigger: 'manual' });
      $(this).tipsy('show');
    });

    $(document).on('click', '[data-tipsy-close]', function(e){
      $('.tipsy').remove();
    });

    var bus = new Vue();

    Vue.component('card-description', {
      props: ['card', 'figures'],
      template: (isMobile() ? '#card-description-mobile' : '#card-description-desktop'),
      methods: {
        beforeEnter: function (el) {
          el.style.opacity = 0
        },
        enter: function (el, done) {
          $(el).velocity("transition.slideDownIn", {duration: 200, delay: 250, complete: function(){
            $('body').animate({
              scrollTop: $(el).offset().top - 85
            }, 400);
          }});
        },
        leave: function (el, done) {
          $(el).velocity("transition.slideUpOut", {duration: 200, complete: function(){}});
        }
      }
    });

    Vue.component('consultation-card', {
      props: ['card', 'active', 'figures'],
      template: (isMobile() ? '#card-mobile' : '#card-desktop'),
      computed: {
        iconForChoice: function(){
          if(this.card.choice === null) return;

          if(this.card.choice > 0) {
            return "fa-arrow-up";
          } else if (this.card.choice == 0){
            return "fa-circle";
          } else if (this.card.choice < 0){
            return "fa-arrow-down";
          }
        }
      },
      methods: {
        setActive: function(card, e) {
          $('.tipsy').remove();

          // Send the active event to the bus
          bus.$emit('active', card);

          card.toggleDesc = !card.toggleDesc;

          // Reset value
          card.hidden = null;

          // Stick it
          $(".desktop .consultation-info").stick_in_parent();
        }
      }
    });

    Vue.component('budget-box', {
      props: ['card', 'figures'],
      template: '#budget-box',
      methods: {
        showModal: function(d) {
          bus.$emit('open', d);
        },
        makeChoice: function(e) {
          e.preventDefault();
          $('.tipsy').remove();
          bus.$emit('cardOptionChosen', $(e.target), this.card);
        }
      }
    });

    Vue.component('budget-calculator', {
      props: ['active', 'next', 'statusText', 'status', 'statusClass'],
      template: '#budget-calculator',
      methods: {
        nextScreen: function(e){
          e.preventDefault();
          bus.$emit('cardOptionChosen', null, null);
        },
        submitConsultation: function(e){
          e.preventDefault();
          bus.$emit('submitConsultation');
        },
        showConsultationStatusError: function(e){
          e.preventDefault();
        }
      },
      updated: function(){
        var $el = $('[data-status-explanation]');
        $el.attr('title', $el.attr('title_' + this.status));
      }
    });

    Vue.component('description-modal', {
      props: ['current'],
      template: '#description-modal',
      methods: {
        hideModal: function() {
          bus.$emit('close');
        }
      }
    });

    var app = new Vue({
      el: (isMobile() ? "#consultation-mobile-app" : "#consultation-desktop-app"),
      data: {
        figures: false,
        active: false,
        modal: false,
        next: false,
        current: null,
        statusDifference: 0,
        cards: [],
      },
      computed: {
        statusText: function(){
          return I18n.t('gobierto_budget_consultations.consultation_statuses.' + this.status);
        },
        statusClass: function(){
          // Track difference between status
          // The status class variable maxes out at 4 to be applied to the card class
          var classId = this.statusDifference;

          if (classId > 4)  classId = 4;
          if (classId < -4) classId = -4;

          if (classId < 0) return 'surplus surplus-' + Math.abs(classId);
          if (classId > 0) return 'deficit deficit-' + Math.abs(classId);
        },
        status: function(){
          if(this.statusDifference === 0) return 'balance';
          if (this.statusDifference < 0)  return 'surplus';
          return 'deficit';
        }
      },
      methods: {
        fetchData: function(){
          var self = this;
          var url = $(self.$options.el).data('data-url');
          $.ajax({
            url: url,
            dataType: 'json',
            beforeSend: function(xhr){
              xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
            },
            success: function(response, textStatus, jqXHR){
              if(jqXHR.status == 200){
                Vue.set(self, 'cards', response);
              } else {
                // TODO: handle errors
              }
            },
          });
        },
        cardOptionChosenHandler: function($el, currentCard){
          this.choiceCardAndOpenNext($el, currentCard);
          var allSelected = true;
          app.$data.cards.forEach(function(card){
            if(card.choice === null)
              allSelected = false;
          });

          if(allSelected){
            Vue.set(app, 'next', true);
            $('.consultation-confirm').css('width', $('.description').width() - 79);

            $('body').animate({
              scrollTop: $(app.$el).offset().top - 25
            }, 500);
          }
        },
        choiceCardAndOpenNext: function($el, currentCard){
          if(currentCard !== null){
            var positiveCards = 0;
            var negativeCards = 0;
            app.$data.cards.forEach(function(card){
              if(currentCard === card) {
                card.choice = $el.data('value');
                card.toggleDesc = false;
              }
              if(card.choice > 0) {
                positiveCards++;
              } else if (card.choice < 0) {
                negativeCards++;
              }
            });
          }
          this.statusDifference = positiveCards - negativeCards;

          var found = false;
          app.$data.cards.forEach(function(card){
            if(!found && card.choice === null){
              card.toggleDesc = true;
              found = true;
              bus.$emit('active', card);
            }
          });
        },
        submitConsultation: function(){
          var self = this;
          var url = $(self.$options.el).data('submit-url');
          var data = app.$data.cards.map(function(card){
            return {
              item_id: card.id,
              selected_option: card.choice
            }
          });
          $.ajax({
            type: "POST",
            data: {
              consultation_response: {
                selected_options: data
              }
            },
            url: url,
            dataType: 'script',
            beforeSend: function(xhr){
              xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
            }
          });
        }
      },
      created: function() {
        $(this.$options.el).show();
        this.fetchData();

        bus.$on('active', function(currentCard) {
          Vue.set(app, 'active', true);

          app.$data.cards.forEach(function(card){
            if(currentCard != card)
              card.toggleDesc = false;
          });

          if (app.$data.next) {
            $('.consultation-confirm').addClass('slim');
            $('.slim').css('top', 0);
          }

          var $target = $('[data-card-id="'+currentCard.id+'"]');
          var $targetDescription = $('[data-card-description-id="'+currentCard.id+'"]');
          if($targetDescription.length){
            if ($('.card-wrapper').css('top') === 'auto') {
              var offset = $target.offset().top - 220;
            } else {
              var offset = $target.offset().top - 250;
            }
            $targetDescription.css({ top: offset + 'px' });
          }
        });

        bus.$on('open', function(d) {
          Vue.set(app, 'current', d);
          Vue.set(app, 'modal', true);
        });

        bus.$on('close', function() {
          Vue.set(app, 'modal', false);
        });

        bus.$on('cardOptionChosen', this.cardOptionChosenHandler);

        bus.$on('submitConsultation', this.submitConsultation);
      },
    });
  }

  return ConsultationResponsesController;
})();

this.GobiertoBudgetConsultations.consultation_responses_controller = new GobiertoBudgetConsultations.ConsultationResponsesController;
<|endoftext|>


<commit_before>/**
 * Listbox.js is a simple jQuery plugin that provides a more powerful
 * alternative to the standard `<select>` tag.
 *
 * The main problem of <select> tag is that last one isn't flexible for
 * customization with CSS. Listbox.js solves this problem. This component
 * runs on top of <select> tag and creates an alternative to the last one
 * based on <div> tags. It opens up great possibilities for customization.
 *
 * @copyright   (c) 2012, Igor Kalnitsky.
 * @license     BSD, 3-clause
 */

(function ($) {

    /**
     * Creates an instance of Listbox.
     *
     * @constructor
     * @this {Listbox}
     * @param {object} domelement DOM element to be converted to the Listbox
     * @param {object} options an object with Listbox settings
     */
    function Listbox(domelement, options) {
        // CSS classes used by plugin
        this.MAIN_CLASS      = 'lbjs';
        this.LIST_CLASS      = 'lbjs-list';
        this.LIST_ITEM_CLASS = 'lbjs-item';
        this.SEARCHBAR_CLASS = 'lbjs-searchbar';

        // internal state members
        /** @private */ this._parent   = domelement;
        /** @private */ this._settings = options;

        // initialize object
        this._init.call(this);
    }

    /**
     * Initialize the Listbox object. Hide a parent DOM element
     * and creates the Listbox alternative.
     *
     * @private
     * @this {Listbox}
     */
    Listbox.prototype._init = function () {
        this._createListbox();                  // create new flexible element
        this._parent.css('display', 'none');    // hide parent element
    }

    /**
     * Creates a `div`-based listbox.
     *
     * @private
     * @this {Listbox}
     */
    Listbox.prototype._createListbox = function () {
        this._listbox = $('<div>')
            .addClass(this.MAIN_CLASS)
            .insertAfter(this._parent);

        if (this._settings['class'])
            this._listbox.addClass(this._settings['class']);

        if (this._settings['searchbar'])
            this._createSearchbar();

        this._createList();
    }

    /**
     * Creates a Listbox's searchbar.
     *
     * @private
     * @this {Listbox}
     */
    Listbox.prototype._createSearchbar = function () {
        // searchbar wrapper is needed for properly stretch
        // the seacrhbar over the listbox width
        var searchbarWrapper = $('<div>')
            .addClass(this.SEARCHBAR_CLASS + '-wrapper')
            .appendTo(this._listbox);

        var searchbar = $('<input>')
            .addClass(this.SEARCHBAR_CLASS)
            .appendTo(searchbarWrapper)
            .attr('placeholder', 'search...');

        // set filter handler
        var self = this;
        searchbar.keyup(function () {
            var searchQuery = $(this).val().toLowerCase();

            if (searchQuery !== '') {
                // hide list items which are not matched search query
                self._list.children().each(function (index) {
                    var text = $(this).text().toLowerCase();

                    if (text.search('^' + searchQuery) != -1) {
                        $(this).css('display', 'block');
                    } else {
                        $(this).css('display', 'none');

                        // remove selection from hidden elements to
                        // protect against implicitly influence
                        self._unselectItem($(this));
                    }
                });
            } else {
                // make visible all list items
                self._list.children().each(function () {
                    $(this).css('display', 'block')
                });
            }

            // @hack: call special handler which is used only for SingleSelectListbox
            //        to prevent situation when none of items are selected
            if (self.onFilterChange)
                self.onFilterChange();
        });

        // save for using in _resizeListToListbox()
        this._searchbarWrapper = searchbarWrapper;
    }

    /**
     * Creates a list. The List is an element with list items.
     *
     * @private
     */
    Listbox.prototype._createList = function () {
        // create container
        this._list = $('<div>')
            .addClass(this.LIST_CLASS)
            .appendTo(this._listbox);

        this._resizeListToListbox();

        // create items
        var self = this;
        this._parent.children().each(function () {
            var item = $('<div>')
                .addClass(self.LIST_ITEM_CLASS)
                .appendTo(self._list)
                .text($(this).text())
                .click(function () {
                    self.onItemClick($(this))
                });

            if ($(this).attr('disabled'))
                item.attr('disabled', '');

            if ($(this).attr('selected')) {
                self.onItemClick(item);
            }
                //item.attr('selected', '');
        });
    }

    /**
     * Select a given item in a fake and real lists.
     *
     * @private
     * @param {object} item an item to be selected
     */
    Listbox.prototype._selectItem = function (item) {
        item.attr('selected', 'selected');

        // make changes in real list
        var selectItem = this._parent.children().get(item.index());
        $(selectItem).attr('selected', '');
    }

    /**
     * Unselect a given item in a fake and real lists.
     *
     * @private
     * @param {object} item an item to be unselected
     */
    Listbox.prototype._unselectItem = function (item) {
        item.removeAttr('selected');

        // make changes in real list
        var selectItem = this._parent.children().get(item.index());
        $(selectItem).removeAttr('selected');
    }

    /**
     * Resize list to lisbox. It's a small hack since I can't
     * do it with CSS.
     *
     * @private
     */
    Listbox.prototype._resizeListToListbox = function () {
        var listHeight = this._listbox.height();

        if (this._settings['searchbar'])
            listHeight -= this._searchbarWrapper.outerHeight(true);

        this._list.height(listHeight);
    }

    /**
     * Creates an instance of SingleSelectListbox.
     *
     * @constructor
     * @this {SingleSelectListbox}
     * @param {object} domelement DOM element to be converted to the Listbox
     * @param {object} options an object with Listbox settings
     */
    function SingleSelectListbox(domelement, options) {
        // inherit parent class and call it constructor
        $.extend(SingleSelectListbox.prototype, Listbox.prototype);
        Listbox.call(this, domelement, options);

        // select first item if none selected
        if (!this._selected)
            this.onItemClick(this._list.children().first());
    }

    /**
     * Reset all items and select a given one.
     *
     * @this {SingleSelectListbox}
     * @param {object} item a DOM object
     */
    SingleSelectListbox.prototype.onItemClick = function (item) {
        if (item.attr('disabled'))
            return;

        // select a fake item
        if (this._selected)
            this._selected.removeAttr('selected');
        this._selected = item.attr('selected', '');

        // select a real item
        var selectItem = this._parent.children().get(item.index());
        this._parent.val($(selectItem).val());

        this._parent.trigger('change');
    }

    /**
     * Select first visible item if none selected.
     *
     * @this {SingleSelectListbox}
     */
    SingleSelectListbox.prototype.onFilterChange = function () {
        if (!this._selected || !this._selected.is(':visible'))
            this.onItemClick(this._list.children(':visible').first());
    }

    /**
     * Creates an instance of MultiSelectListbox.
     *
     * @constructor
     * @this {MultiSelectListbox}
     * @param {object} domelement DOM element to be converted to the Listbox
     * @param {object} options an object with Listbox settings
     */
    function MultiSelectListbox(domelement, options) {
        // inherit parent class and call it constructor
        $.extend(MultiSelectListbox.prototype, Listbox.prototype);
        Listbox.call(this, domelement, options);
    }

    /**
     * Toggle item status.
     *
     * @this {MultiSelectListbox}
     * @param {object} item a DOM object
     */
    MultiSelectListbox.prototype.onItemClick = function (item) {
        if (item.attr('disabled'))
            return;

        item.attr('selected')
            ? this._unselectItem(item)
            : this._selectItem(item)
        ;

        this._parent.trigger('change');
    }

    /**
     * jQuery plugin definition.
     *
     * @param {object} options an object with Listbox settings
     */
    $.fn.listbox = function (options) {
        var settings = $.extend({
            'class':        null,
            'searchbar':    false
        }, options);

        return this.each(function () {
            $(this).attr('multiple')
                ? new MultiSelectListbox($(this), settings)
                : new SingleSelectListbox($(this), settings)
            ;
        });
    }
})(jQuery);
<commit_msg>Use more convinient inheritance trick.<commit_after>/**
 * Listbox.js is a simple jQuery plugin that provides a more powerful
 * alternative to the standard `<select>` tag.
 *
 * The main problem of <select> tag is that last one isn't flexible for
 * customization with CSS. Listbox.js solves this problem. This component
 * runs on top of <select> tag and creates an alternative to the last one
 * based on <div> tags. It opens up great possibilities for customization.
 *
 * @copyright   (c) 2012, Igor Kalnitsky.
 * @license     BSD, 3-clause
 */

(function ($) {

    /**
     * Creates an instance of Listbox.
     *
     * @constructor
     * @this {Listbox}
     * @param {object} domelement DOM element to be converted to the Listbox
     * @param {object} options an object with Listbox settings
     */
    function Listbox(domelement, options) {
        // CSS classes used by plugin
        this.MAIN_CLASS      = 'lbjs';
        this.LIST_CLASS      = 'lbjs-list';
        this.LIST_ITEM_CLASS = 'lbjs-item';
        this.SEARCHBAR_CLASS = 'lbjs-searchbar';

        // internal state members
        /** @private */ this._parent   = domelement;
        /** @private */ this._settings = options;

        // initialize object
        this._init.call(this);
    }

    /**
     * Initialize the Listbox object. Hide a parent DOM element
     * and creates the Listbox alternative.
     *
     * @private
     * @this {Listbox}
     */
    Listbox.prototype._init = function () {
        this._createListbox();                  // create new flexible element
        this._parent.css('display', 'none');    // hide parent element
    }

    /**
     * Creates a `div`-based listbox.
     *
     * @private
     * @this {Listbox}
     */
    Listbox.prototype._createListbox = function () {
        this._listbox = $('<div>')
            .addClass(this.MAIN_CLASS)
            .insertAfter(this._parent);

        if (this._settings['class'])
            this._listbox.addClass(this._settings['class']);

        if (this._settings['searchbar'])
            this._createSearchbar();

        this._createList();
    }

    /**
     * Creates a Listbox's searchbar.
     *
     * @private
     * @this {Listbox}
     */
    Listbox.prototype._createSearchbar = function () {
        // searchbar wrapper is needed for properly stretch
        // the seacrhbar over the listbox width
        var searchbarWrapper = $('<div>')
            .addClass(this.SEARCHBAR_CLASS + '-wrapper')
            .appendTo(this._listbox);

        var searchbar = $('<input>')
            .addClass(this.SEARCHBAR_CLASS)
            .appendTo(searchbarWrapper)
            .attr('placeholder', 'search...');

        // set filter handler
        var self = this;
        searchbar.keyup(function () {
            var searchQuery = $(this).val().toLowerCase();

            if (searchQuery !== '') {
                // hide list items which are not matched search query
                self._list.children().each(function (index) {
                    var text = $(this).text().toLowerCase();

                    if (text.search('^' + searchQuery) != -1) {
                        $(this).css('display', 'block');
                    } else {
                        $(this).css('display', 'none');

                        // remove selection from hidden elements to
                        // protect against implicitly influence
                        self._unselectItem($(this));
                    }
                });
            } else {
                // make visible all list items
                self._list.children().each(function () {
                    $(this).css('display', 'block')
                });
            }

            // @hack: call special handler which is used only for SingleSelectListbox
            //        to prevent situation when none of items are selected
            if (self.onFilterChange)
                self.onFilterChange();
        });

        // save for using in _resizeListToListbox()
        this._searchbarWrapper = searchbarWrapper;
    }

    /**
     * Creates a list. The List is an element with list items.
     *
     * @private
     */
    Listbox.prototype._createList = function () {
        // create container
        this._list = $('<div>')
            .addClass(this.LIST_CLASS)
            .appendTo(this._listbox);

        this._resizeListToListbox();

        // create items
        var self = this;
        this._parent.children().each(function () {
            var item = $('<div>')
                .addClass(self.LIST_ITEM_CLASS)
                .appendTo(self._list)
                .text($(this).text())
                .click(function () {
                    self.onItemClick($(this))
                });

            if ($(this).attr('disabled'))
                item.attr('disabled', '');

            if ($(this).attr('selected')) {
                self.onItemClick(item);
            }
                //item.attr('selected', '');
        });
    }

    /**
     * Select a given item in a fake and real lists.
     *
     * @private
     * @param {object} item an item to be selected
     */
    Listbox.prototype._selectItem = function (item) {
        item.attr('selected', 'selected');

        // make changes in real list
        var selectItem = this._parent.children().get(item.index());
        $(selectItem).attr('selected', '');
    }

    /**
     * Unselect a given item in a fake and real lists.
     *
     * @private
     * @param {object} item an item to be unselected
     */
    Listbox.prototype._unselectItem = function (item) {
        item.removeAttr('selected');

        // make changes in real list
        var selectItem = this._parent.children().get(item.index());
        $(selectItem).removeAttr('selected');
    }

    /**
     * Resize list to lisbox. It's a small hack since I can't
     * do it with CSS.
     *
     * @private
     */
    Listbox.prototype._resizeListToListbox = function () {
        var listHeight = this._listbox.height();

        if (this._settings['searchbar'])
            listHeight -= this._searchbarWrapper.outerHeight(true);

        this._list.height(listHeight);
    }

    /**
     * Creates an instance of SingleSelectListbox.
     *
     * Inherit a {Listbox} class.
     *
     * @constructor
     * @this {SingleSelectListbox}
     * @param {object} domelement DOM element to be converted to the Listbox
     * @param {object} options an object with Listbox settings
     */
    function SingleSelectListbox(domelement, options) {
        Listbox.call(this, domelement, options);

        // select first item if none selected
        if (!this._selected)
            this.onItemClick(this._list.children().first());
    }
    SingleSelectListbox.prototype = Object.create(Listbox.prototype);
    SingleSelectListbox.prototype.constructor = SingleSelectListbox;

    /**
     * Reset all items and select a given one.
     *
     * @this {SingleSelectListbox}
     * @param {object} item a DOM object
     */
    SingleSelectListbox.prototype.onItemClick = function (item) {
        if (item.attr('disabled'))
            return;

        // select a fake item
        if (this._selected)
            this._selected.removeAttr('selected');
        this._selected = item.attr('selected', '');

        // select a real item
        var selectItem = this._parent.children().get(item.index());
        this._parent.val($(selectItem).val());

        this._parent.trigger('change');
    }

    /**
     * Select first visible item if none selected.
     *
     * @this {SingleSelectListbox}
     */
    SingleSelectListbox.prototype.onFilterChange = function () {
        if (!this._selected || !this._selected.is(':visible'))
            this.onItemClick(this._list.children(':visible').first());
    }

    /**
     * Creates an instance of MultiSelectListbox.
     *
     * Inherit a {Listbox} class.
     *
     * @constructor
     * @this {MultiSelectListbox}
     * @param {object} domelement DOM element to be converted to the Listbox
     * @param {object} options an object with Listbox settings
     */
    function MultiSelectListbox(domelement, options) {
        Listbox.call(this, domelement, options);
    }
    MultiSelectListbox.prototype = Object.create(Listbox.prototype);
    MultiSelectListbox.prototype.constructor = MultiSelectListbox;

    /**
     * Toggle item status.
     *
     * @this {MultiSelectListbox}
     * @param {object} item a DOM object
     */
    MultiSelectListbox.prototype.onItemClick = function (item) {
        if (item.attr('disabled'))
            return;

        item.attr('selected')
            ? this._unselectItem(item)
            : this._selectItem(item)
        ;

        this._parent.trigger('change');
    }

    /**
     * jQuery plugin definition.
     *
     * @param {object} options an object with Listbox settings
     */
    $.fn.listbox = function (options) {
        var settings = $.extend({
            'class':        null,
            'searchbar':    false
        }, options);

        return this.each(function () {
            $(this).attr('multiple')
                ? new MultiSelectListbox($(this), settings)
                : new SingleSelectListbox($(this), settings)
            ;
        });
    }
})(jQuery);
<|endoftext|>


<commit_before>/* @flow */
/**
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

import type { ValidationContext } from '../index';
import { GraphQLError } from '../../error';
import suggestionList from '../../jsutils/suggestionList';
import quotedOrList from '../../jsutils/quotedOrList';
import type { Field } from '../../language/ast';
import type { GraphQLSchema } from '../../type/schema';
import type { GraphQLOutputType } from '../../type/definition';
import {
  GraphQLObjectType,
  GraphQLInterfaceType,
  GraphQLUnionType,
} from '../../type/definition';

export function undefinedFieldMessage(
  fieldName: string,
  type: string,
  suggestedTypeNames: Array<string>,
  suggestedFieldNames: Array<string>
): string {
  let message = `Cannot query field "${fieldName}" on type "${type}".`;
  if (suggestedTypeNames.length !== 0) {
    const suggestions = quotedOrList(suggestedTypeNames);
    message += ` Did you mean to use an inline fragment on ${suggestions}?`;
  } else if (suggestedFieldNames.length !== 0) {
    message += ` Did you mean ${quotedOrList(suggestedFieldNames)}?`;
  }
  return message;
}

/**
 * Fields on correct type
 *
 * A GraphQL document is only valid if all fields selected are defined by the
 * parent type, or are an allowed meta field such as __typenamme
 */
export function FieldsOnCorrectType(context: ValidationContext): any {
  return {
    Field(node: Field) {
      const type = context.getParentType();
      if (type) {
        const fieldDef = context.getFieldDef();
        if (!fieldDef) {
          // This field doesn't exist, lets look for suggestions.
          const schema = context.getSchema();
          const fieldName = node.name.value;
          // First determine if there are any suggested types to condition on.
          const suggestedTypeNames =
            getSuggestedTypeNames(schema, type, fieldName);
          // If there are no suggested types, then perhaps this was a typo?
          const suggestedFieldNames = suggestedTypeNames.length !== 0 ?
            [] :
            getSuggestedFieldNames(schema, type, fieldName);

          // Report an error, including helpful suggestions.
          context.reportError(new GraphQLError(
            undefinedFieldMessage(
              fieldName,
              type.name,
              suggestedTypeNames,
              suggestedFieldNames
            ),
            [ node ]
          ));
        }
      }
    }
  };
}

/**
 * Go through all of the implementations of type, as well as the interfaces
 * that they implement. If any of those types include the provided field,
 * suggest them, sorted by how often the type is referenced,  starting
 * with Interfaces.
 */
function getSuggestedTypeNames(
  schema: GraphQLSchema,
  type: GraphQLOutputType,
  fieldName: string
): Array<string> {
  if (type instanceof GraphQLInterfaceType ||
      type instanceof GraphQLUnionType) {
    const suggestedObjectTypes = [];
    const interfaceUsageCount = Object.create(null);
    schema.getPossibleTypes(type).forEach(possibleType => {
      if (!possibleType.getFields()[fieldName]) {
        return;
      }
      // This object type defines this field.
      suggestedObjectTypes.push(possibleType.name);
      possibleType.getInterfaces().forEach(possibleInterface => {
        if (!possibleInterface.getFields()[fieldName]) {
          return;
        }
        // This interface type defines this field.
        interfaceUsageCount[possibleInterface.name] =
          (interfaceUsageCount[possibleInterface.name] || 0) + 1;
      });
    });

    // Suggest interface types based on how common they are.
    const suggestedInterfaceTypes = Object.keys(interfaceUsageCount)
      .sort((a, b) => interfaceUsageCount[b] - interfaceUsageCount[a]);

    // Suggest both interface and object types.
    return suggestedInterfaceTypes.concat(suggestedObjectTypes);
  }

  // Otherwise, must be an Object type, which does not have possible fields.
  return [];
}

/**
 * For the field name provided, determine if there are any similar field names
 * that may be the result of a typo.
 */
function getSuggestedFieldNames(
  schema: GraphQLSchema,
  type: GraphQLOutputType,
  fieldName: string
): Array<string> {
  if (type instanceof GraphQLObjectType ||
      type instanceof GraphQLInterfaceType) {
    const possibleFieldNames = Object.keys(type.getFields());
    return suggestionList(fieldName, possibleFieldNames);
  }
  // Otherwise, must be a Union type, which does not define fields.
  return [];
}
<commit_msg>[minor] Fix typo in validation rule comments (#472)<commit_after>/* @flow */
/**
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

import type { ValidationContext } from '../index';
import { GraphQLError } from '../../error';
import suggestionList from '../../jsutils/suggestionList';
import quotedOrList from '../../jsutils/quotedOrList';
import type { Field } from '../../language/ast';
import type { GraphQLSchema } from '../../type/schema';
import type { GraphQLOutputType } from '../../type/definition';
import {
  GraphQLObjectType,
  GraphQLInterfaceType,
  GraphQLUnionType,
} from '../../type/definition';

export function undefinedFieldMessage(
  fieldName: string,
  type: string,
  suggestedTypeNames: Array<string>,
  suggestedFieldNames: Array<string>
): string {
  let message = `Cannot query field "${fieldName}" on type "${type}".`;
  if (suggestedTypeNames.length !== 0) {
    const suggestions = quotedOrList(suggestedTypeNames);
    message += ` Did you mean to use an inline fragment on ${suggestions}?`;
  } else if (suggestedFieldNames.length !== 0) {
    message += ` Did you mean ${quotedOrList(suggestedFieldNames)}?`;
  }
  return message;
}

/**
 * Fields on correct type
 *
 * A GraphQL document is only valid if all fields selected are defined by the
 * parent type, or are an allowed meta field such as __typename.
 */
export function FieldsOnCorrectType(context: ValidationContext): any {
  return {
    Field(node: Field) {
      const type = context.getParentType();
      if (type) {
        const fieldDef = context.getFieldDef();
        if (!fieldDef) {
          // This field doesn't exist, lets look for suggestions.
          const schema = context.getSchema();
          const fieldName = node.name.value;
          // First determine if there are any suggested types to condition on.
          const suggestedTypeNames =
            getSuggestedTypeNames(schema, type, fieldName);
          // If there are no suggested types, then perhaps this was a typo?
          const suggestedFieldNames = suggestedTypeNames.length !== 0 ?
            [] :
            getSuggestedFieldNames(schema, type, fieldName);

          // Report an error, including helpful suggestions.
          context.reportError(new GraphQLError(
            undefinedFieldMessage(
              fieldName,
              type.name,
              suggestedTypeNames,
              suggestedFieldNames
            ),
            [ node ]
          ));
        }
      }
    }
  };
}

/**
 * Go through all of the implementations of type, as well as the interfaces
 * that they implement. If any of those types include the provided field,
 * suggest them, sorted by how often the type is referenced,  starting
 * with Interfaces.
 */
function getSuggestedTypeNames(
  schema: GraphQLSchema,
  type: GraphQLOutputType,
  fieldName: string
): Array<string> {
  if (type instanceof GraphQLInterfaceType ||
      type instanceof GraphQLUnionType) {
    const suggestedObjectTypes = [];
    const interfaceUsageCount = Object.create(null);
    schema.getPossibleTypes(type).forEach(possibleType => {
      if (!possibleType.getFields()[fieldName]) {
        return;
      }
      // This object type defines this field.
      suggestedObjectTypes.push(possibleType.name);
      possibleType.getInterfaces().forEach(possibleInterface => {
        if (!possibleInterface.getFields()[fieldName]) {
          return;
        }
        // This interface type defines this field.
        interfaceUsageCount[possibleInterface.name] =
          (interfaceUsageCount[possibleInterface.name] || 0) + 1;
      });
    });

    // Suggest interface types based on how common they are.
    const suggestedInterfaceTypes = Object.keys(interfaceUsageCount)
      .sort((a, b) => interfaceUsageCount[b] - interfaceUsageCount[a]);

    // Suggest both interface and object types.
    return suggestedInterfaceTypes.concat(suggestedObjectTypes);
  }

  // Otherwise, must be an Object type, which does not have possible fields.
  return [];
}

/**
 * For the field name provided, determine if there are any similar field names
 * that may be the result of a typo.
 */
function getSuggestedFieldNames(
  schema: GraphQLSchema,
  type: GraphQLOutputType,
  fieldName: string
): Array<string> {
  if (type instanceof GraphQLObjectType ||
      type instanceof GraphQLInterfaceType) {
    const possibleFieldNames = Object.keys(type.getFields());
    return suggestionList(fieldName, possibleFieldNames);
  }
  // Otherwise, must be a Union type, which does not define fields.
  return [];
}
<|endoftext|>


<commit_before>var gulp = require('gulp');
var source = require('vinyl-source-stream');
var del = require('del');
var compass = require('gulp-compass');
var browserify = require('browserify');
var hologram = require('gulp-hologram');
var connect = require('gulp-connect');
var open = require('gulp-open');
var ejs = require('gulp-ejs');
var fs = require('fs');
var jshint = require('gulp-jshint');
var stylish = require('jshint-stylish');

gulp.task('default', [
  'watch',
  'serve'
]);

gulp.task('test', [
  '_lint',
  '_cssCritic',
]);

gulp.task('watch', ['assets'], function() {
  gulp.watch("src/**/*", ['assets']);
});

gulp.task('serve', function() {
  connect.server({
    root: ['dist'],
    port: 8000,
    livereload: true
  });
});

gulp.task('clean', function(done) {
  del(['dist'], {force: true}, done);
});

gulp.task('assets', [
  '_styles',
  '_scripts',
  '_images',
  '_fonts',
  '_styleguide'
]);

// private

gulp.task('_styleguide', [
  'clean',
  '_hologramBuild',
  '_copyStyleguideAssets',
  '_copyRandomAssets']);

gulp.task('_styles', [
  'clean',
  '_compassBuild',
  '_copyPrism'
]);

gulp.task('_fonts', [
  'clean',
  '_fontAwesome',
  '_sourceSansPro'
]);

gulp.task('_compassBuild', ['clean'], function() {
  gulp.src(['src/pivotal-ui/pivotal-ui.scss', 'src/style_guide/style_guide.scss'])
    .pipe(compass({
      config_file: './config/compass.rb',
      css: 'dist',
      sass: 'src'
    }).on('error', function() { console.error(arguments) })
  );
});

gulp.task('_copyPrism', ['clean'], function() {
  gulp.src(['src/syntax-highlighting/*.css'])
    .pipe(gulp.dest('./dist/syntax-highlighting/'));
});

gulp.task('_scripts', ['clean'], function() {
  browserify('./src/pivotal-ui/javascripts/pivotal-ui.js').bundle()
    .pipe(source('./pivotal-ui.js'))
    .pipe(gulp.dest('dist/pivotal-ui/'))
});

gulp.task('_images', ['clean'], function() {
  gulp.src('src/images/**/*')
    .pipe(gulp.dest('./dist/images/'));
});

gulp.task('_fontAwesome', ['clean'], function() {
  gulp.src([
    'node_modules/font-awesome/fonts/*',
  ])
    .pipe(gulp.dest('./dist/fonts/'));
});

gulp.task('_sourceSansPro', ['clean'], function() {
  gulp.src([
    'src/source-sans-pro/**/*',
    '!src/source-sans-pro/source-sans-pro.css.scss'
  ])
    .pipe(gulp.dest('./dist/fonts/'));
});

gulp.task('_copyStyleguideAssets', ['clean'], function() {
  gulp.src(['src/style_guide/*.js', 'src/style_guide/github.css'])
    .pipe(gulp.dest('./dist/style_guide'));
});

gulp.task('_copyRandomAssets', ['clean'], function() {
  gulp.src([
    'src/nginx.conf',
    'src/Staticfile',
    'src/style_guide/404.html',
    'src/style_guide/pane.html'
  ])
    .pipe(gulp.dest('./dist/'));
});

gulp.task('_hologramBuild', ['clean'], function() {
  gulp.src('hologram_config.yml')
    .pipe(hologram({
      bundler: true
    }));
});

gulp.task('_copyTestAssets', ['assets'], function() {
  return gulp.src([
    'dist/**/*',
  ]).pipe(gulp.dest('./test/dist/'));
});

gulp.task('_createTestFileList', function(cb) {
  fs.readdir('./test/components/', function(err, files) {
    if (err) {
      console.error(err);
      process.exit(1)
    }

    var stream = gulp.src('./test/regressionRunner.ejs')
      .pipe(ejs({
        files: files
      }, {
        ext: '.js'
      }))
      .pipe(gulp.dest('./test/'));

    stream.on('finish', cb)
  });
});

gulp.task('_cssCritic', ['_lint', '_copyTestAssets', '_createTestFileList'], function() {
  gulp.src("./test/regressionRunner.html")
    .pipe(open("./test/regressionRunner.html",{app:"firefox"}));
});

gulp.task('_lint', function() {
  return gulp.src('./src/pivotal-ui/javascripts/**/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter(stylish))
    .pipe(jshint.reporter('fail'))
});

<commit_msg>Make sure test runs have the most up to date assets<commit_after>var gulp = require('gulp');
var source = require('vinyl-source-stream');
var del = require('del');
var compass = require('gulp-compass');
var browserify = require('browserify');
var hologram = require('gulp-hologram');
var connect = require('gulp-connect');
var open = require('gulp-open');
var ejs = require('gulp-ejs');
var fs = require('fs');
var jshint = require('gulp-jshint');
var stylish = require('jshint-stylish');

gulp.task('default', [
  'watch',
  'serve'
]);

gulp.task('test', [
  '_lint',
  '_cssCritic',
]);

gulp.task('watch', ['assets', '_copyTestAssets'], function() {
  gulp.watch("src/**/*", ['assets', '_copyTestAssets']);
});

gulp.task('serve', function() {
  connect.server({
    root: ['dist'],
    port: 8000,
    livereload: true
  });
});

gulp.task('clean', function(done) {
  del(['dist'], {force: true}, done);
});

gulp.task('assets', [
  '_styles',
  '_scripts',
  '_images',
  '_fonts',
  '_styleguide'
]);

// private

gulp.task('_styleguide', [
  'clean',
  '_hologramBuild',
  '_copyStyleguideAssets',
  '_copyRandomAssets'
]);

gulp.task('_styles', [
  'clean',
  '_compassBuild',
  '_copyPrism'
]);

gulp.task('_fonts', [
  'clean',
  '_fontAwesome',
  '_sourceSansPro'
]);

gulp.task('_compassBuild', ['clean'], function() {
  return gulp.src(['src/pivotal-ui/pivotal-ui.scss', 'src/style_guide/style_guide.scss'])
    .pipe(compass({
      config_file: './config/compass.rb',
      css: 'dist',
      sass: 'src'
    }).on('error', function() { console.error(arguments) })
  );
});

gulp.task('_copyPrism', ['clean'], function() {
  return gulp.src(['src/syntax-highlighting/*.css'])
    .pipe(gulp.dest('./dist/syntax-highlighting/'));
});

gulp.task('_scripts', ['clean'], function() {
  return browserify('./src/pivotal-ui/javascripts/pivotal-ui.js').bundle()
    .pipe(source('./pivotal-ui.js'))
    .pipe(gulp.dest('dist/pivotal-ui/'))
});

gulp.task('_images', ['clean'], function() {
  return gulp.src('src/images/**/*')
    .pipe(gulp.dest('./dist/images/'));
});

gulp.task('_fontAwesome', ['clean'], function() {
  return gulp.src([
    'node_modules/font-awesome/fonts/*',
  ])
    .pipe(gulp.dest('./dist/fonts/'));
});

gulp.task('_sourceSansPro', ['clean'], function() {
  return gulp.src([
    'src/source-sans-pro/**/*',
    '!src/source-sans-pro/source-sans-pro.css.scss'
  ])
    .pipe(gulp.dest('./dist/fonts/'));
});

gulp.task('_copyStyleguideAssets', ['clean'], function() {
  return gulp.src(['src/style_guide/*.js', 'src/style_guide/github.css'])
    .pipe(gulp.dest('./dist/style_guide'));
});

gulp.task('_copyRandomAssets', ['clean'], function() {
  return gulp.src([
    'src/nginx.conf',
    'src/Staticfile',
    'src/style_guide/404.html',
    'src/style_guide/pane.html'
  ])
    .pipe(gulp.dest('./dist/'));
});

gulp.task('_hologramBuild', ['clean'], function() {
  return gulp.src('hologram_config.yml')
    .pipe(hologram({
      bundler: true
    }));
});

gulp.task('_copyTestAssets', ['assets'], function() {
  return gulp.src([
    'dist/**/*',
  ]).pipe(gulp.dest('./test/dist/'));
});

gulp.task('_createTestFileList', function(cb) {
  fs.readdir('./test/components/', function(err, files) {
    if (err) {
      console.error(err);
      process.exit(1)
    }

    var stream = gulp.src('./test/regressionRunner.ejs')
      .pipe(ejs({
        files: files
      }, {
        ext: '.js'
      }))
      .pipe(gulp.dest('./test/'));

    stream.on('finish', cb)
  });
});

gulp.task('_cssCritic', ['_lint', '_copyTestAssets', '_createTestFileList'], function() {
  return gulp.src("./test/regressionRunner.html")
    .pipe(open("./test/regressionRunner.html",{app:"firefox"}));
});

gulp.task('_lint', function() {
  return gulp.src('./src/pivotal-ui/javascripts/**/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter(stylish))
    .pipe(jshint.reporter('fail'))
});

<|endoftext|>


<commit_before>import { injectReducer } from '../../redux/reducer/index';
import reducer from './redux/reducers';

export default store => ({
  path: 'github',
  getComponent(nextState, cb) {
    require.ensure([], (require) => {
      injectReducer(store, { key: 'github', reducer });
      cb(null, require('SHAREDPAGE/Github').default)
    });
  }
})
<commit_msg>bugfixed<commit_after>import { injectReducer } from '../../redux/reducer/index';
import reducer from 'SHAREDPAGE/Github/redux/reducers';

export default store => ({
  path: 'github',
  getComponent(nextState, cb) {
    require.ensure([], (require) => {
      injectReducer(store, { key: 'github', reducer });
      cb(null, require('SHAREDPAGE/Github').default)
    });
  }
})
<|endoftext|>


<commit_before>/* eslint max-nested-callbacks: [2, 5] no-unused-expressions:0*/

"use strict";
import FeedDb from "../../../src/js/feeds/db/FeedDb.js";
import FeedApplicationQueries from "../../../src/js/feeds/db/FeedApplicationQueries.js";
import sinon from "sinon";
import { expect } from "chai";

describe("FeedApplicationQueries", () => {
    describe("allFeedsWithCategoryNames", () => {
        it("should fetch all surf feeds and return with category name", () => {
            let resultDocs = [{
                "key": "fbId1",
                "id": "fbId1",
                "value": {
                    "_id": "sportsCategoryId1"
                },
                "doc": {
                    "docType": "category",
                    "name": "Sports",
                    "_id": "sportsCategoryId1",
                    "_rev": "1-4b61e9edacc78ab1f189b68345d4d410"
                }
            }, {
                "key": "rssId1",
                "id": "feedId1",
                "value": "rssId1",
                "doc": {
                    "docType": "feed",
                    "title": "tn",
                    "description": "www.facebookpolitics.com",
                    "sourceId": "rssId1",
                    "_id": "feedId1",
                    "_rev": "1-e41ef125b2f5fbef4f20d8c896eeea53"
                }
            }, {
                "key": "rssId1",
                "id": "rssId1",
                "value": {
                    "_id": "sportsCategoryId1"
                },
                "doc": {
                    "docType": "category",
                    "name": "Sports",
                    "_id": "sportsCategoryId1",
                    "_rev": "1-4b61e9edacc78ab1f189b68345d4d410"
                }
            }, {
                "key": "rssId1",
                "id": "rssId1",
                "value": {
                    "_id": "politicsCategoryId2"
                },
                "doc": {
                    "docType": "category",
                    "name": "Politics",
                    "_id": "politicsCategoryId2",
                    "_rev": "1-175853337b49fcd1db6474777f871d4a"
                }
            }];

            let expectedSources = [{
                "docType": "feed",
                "title": "tn",
                "description": "www.facebookpolitics.com",
                "sourceId": "rssId1",
                "_id": "feedId1",
                "_rev": "1-e41ef125b2f5fbef4f20d8c896eeea53",
                "categoryNames": "Sports, Politics"
            }];

            let fetchAllSourcesWithCategoriesMock = sinon.mock(FeedDb).expects("fetchSurfFeedsAndCategoriesWithSource");
            fetchAllSourcesWithCategoriesMock.returns(Promise.resolve(resultDocs));
            return FeedApplicationQueries.fetchAllFeedsWithCategoryName().then(sources => {
                expect(expectedSources).to.deep.equal(sources);
                fetchAllSourcesWithCategoriesMock.verify();
                FeedDb.fetchSurfFeedsAndCategoriesWithSource.restore();
            });
        });

        it("should reject with error if fetching documents fails", () => {
            let fetchAllSourcesWithCategoriesStub = sinon.stub(FeedDb, "fetchSurfFeedsAndCategoriesWithSource");
            fetchAllSourcesWithCategoriesStub.returns(Promise.reject("error"));
            return FeedApplicationQueries.fetchAllFeedsWithCategoryName().catch(error => {
                expect(error).to.eq("error");
                FeedDb.fetchSurfFeedsAndCategoriesWithSource.restore();
            });
        });
    });

    describe("fetchAllParkedFeeds", () => {
        it("should fetch all parked feeds and return with category name", () => {
            let resultDocs = [{
                "key": "fbId1",
                "id": "fbId1",
                "value": {
                    "_id": "sportsCategoryId1"
                },
                "doc": {
                    "docType": "category",
                    "name": "Sports",
                    "_id": "sportsCategoryId1",
                    "_rev": "1-4b61e9edacc78ab1f189b68345d4d410"
                }
            }, {
                "key": "rssId1",
                "id": "feedId2",
                "value": "rssId1",
                "doc": {
                    "docType": "feed",
                    "status": "park",
                    "title": "tn",
                    "description": "www.facebookpolitics.com",
                    "sourceId": "rssId1",
                    "_id": "feedId2",
                    "_rev": "1-e41ef125b2f5fbef4f20d8c896eeea53"
                }
            }, {
                "key": "rssId1",
                "id": "rssId1",
                "value": {
                    "_id": "sportsCategoryId1"
                },
                "doc": {
                    "docType": "category",
                    "name": "Sports",
                    "_id": "sportsCategoryId1",
                    "_rev": "1-4b61e9edacc78ab1f189b68345d4d410"
                }
            }, {
                "key": "rssId1",
                "id": "rssId1",
                "value": {
                    "_id": "politicsCategoryId2"
                },
                "doc": {
                    "docType": "category",
                    "name": "Politics",
                    "_id": "politicsCategoryId2",
                    "_rev": "1-175853337b49fcd1db6474777f871d4a"
                }
            }];

            let expectedSources = [{
                "docType": "feed",
                "title": "tn",
                "description": "www.facebookpolitics.com",
                "sourceId": "rssId1",
                "_id": "feedId2",
                "status": "park",
                "_rev": "1-e41ef125b2f5fbef4f20d8c896eeea53",
                "categoryNames": "Sports, Politics"
            }];

            let fetchParkFeedssMock = sinon.mock(FeedDb).expects("fetchParkFeeds");
            fetchParkFeedssMock.returns(Promise.resolve(resultDocs));
            return FeedApplicationQueries.fetchAllParkedFeeds().then(sources => {
                expect(expectedSources).to.deep.equal(sources);
                fetchParkFeedssMock.verify();
                FeedDb.fetchParkFeeds.restore();
            });
        });

        it("should reject with error if fetching documents fails", () => {
            let fetchParkedFeedsStub = sinon.stub(FeedDb, "fetchParkFeeds");
            fetchParkedFeedsStub.returns(Promise.reject("error"));
            return FeedApplicationQueries.fetchAllParkedFeeds().catch(error => {
                expect(error).to.eq("error");
                FeedDb.fetchParkFeeds.restore();
            });
        });
    });

    describe("updateFeed", () => {
        it("should update feed after filtering required fields", () => {
            let rawFeedDoc = {
                "_id": "feedid",
                "_rev": "1-5ec67a3c5068912d169a4d30f4526eea",
                "categoryNames": "hindu",
                "content": "The company has set aside Rs. 1,100 cr for interest free salary advances",
                "docType": "feed",
                "feedType": "rss",
                "sourceId": "52359499-59E9-2D9E-AC38-922DBBB25A63",
                "tags": [],
                "title": "Tata Consultancy offers cash incentives for flood-hit Chennai staff",
                "type": "description"
            };

            let expectedFeedDoc = {
                "_id": "feedid",
                "_rev": "1-5ec67a3c5068912d169a4d30f4526eea",
                "categoryNames": "hindu",
                "content": "The company has set aside Rs. 1,100 cr for interest free salary advances",
                "docType": "feed",
                "feedType": "rss",
                "sourceId": "52359499-59E9-2D9E-AC38-922DBBB25A63",
                "tags": [],
                "title": "Tata Consultancy offers cash incentives for flood-hit Chennai staff",
                "type": "description",
                "status": "park"
            };
            let feedDbMock = sinon.mock(FeedDb).expects("updateFeed");
            feedDbMock.withArgs(expectedFeedDoc).returns(Promise.resolve({ "status": "ok" }));
            return FeedApplicationQueries.updateFeed(rawFeedDoc).then(() => {
                feedDbMock.verify();
                FeedDb.updateFeed.restore();
            });
        });
    });
});
<commit_msg>Fixed test in feeddb<commit_after>/* eslint max-nested-callbacks: [2, 5] no-unused-expressions:0*/

"use strict";
import FeedDb from "../../../src/js/feeds/db/FeedDb.js";
import FeedApplicationQueries from "../../../src/js/feeds/db/FeedApplicationQueries.js";
import sinon from "sinon";
import { expect } from "chai";

describe("FeedApplicationQueries", () => {
    describe("allFeedsWithCategoryNames", () => {
        it("should fetch all surf feeds and return with category name", () => {
            let resultDocs = [{
                "key": "fbId1",
                "id": "fbId1",
                "value": {
                    "_id": "sportsCategoryId1"
                },
                "doc": {
                    "docType": "category",
                    "name": "Sports",
                    "_id": "sportsCategoryId1",
                    "_rev": "1-4b61e9edacc78ab1f189b68345d4d410"
                }
            }, {
                "key": "rssId1",
                "id": "feedId1",
                "value": "rssId1",
                "doc": {
                    "docType": "feed",
                    "title": "tn",
                    "description": "www.facebookpolitics.com",
                    "sourceId": "rssId1",
                    "_id": "feedId1",
                    "_rev": "1-e41ef125b2f5fbef4f20d8c896eeea53"
                }
            }, {
                "key": "rssId1",
                "id": "rssId1",
                "value": {
                    "_id": "sportsCategoryId1"
                },
                "doc": {
                    "docType": "category",
                    "name": "Sports",
                    "_id": "sportsCategoryId1",
                    "_rev": "1-4b61e9edacc78ab1f189b68345d4d410"
                }
            }, {
                "key": "rssId1",
                "id": "rssId1",
                "value": {
                    "_id": "politicsCategoryId2"
                },
                "doc": {
                    "docType": "category",
                    "name": "Politics",
                    "_id": "politicsCategoryId2",
                    "_rev": "1-175853337b49fcd1db6474777f871d4a"
                }
            }];

            let expectedSources = [{
                "docType": "feed",
                "title": "tn",
                "description": "www.facebookpolitics.com",
                "sourceId": "rssId1",
                "_id": "feedId1",
                "_rev": "1-e41ef125b2f5fbef4f20d8c896eeea53",
                "categoryNames": "Sports, Politics"
            }];

            let fetchAllSourcesWithCategoriesMock = sinon.mock(FeedDb).expects("fetchSurfFeedsAndCategoriesWithSource");
            fetchAllSourcesWithCategoriesMock.returns(Promise.resolve(resultDocs));
            return FeedApplicationQueries.fetchAllFeedsWithCategoryName().then(sources => {
                expect(expectedSources).to.deep.equal(sources);
                fetchAllSourcesWithCategoriesMock.verify();
                FeedDb.fetchSurfFeedsAndCategoriesWithSource.restore();
            });
        });

        it("should reject with error if fetching documents fails", () => {
            let fetchAllSourcesWithCategoriesStub = sinon.stub(FeedDb, "fetchSurfFeedsAndCategoriesWithSource");
            fetchAllSourcesWithCategoriesStub.returns(Promise.reject("error"));
            return FeedApplicationQueries.fetchAllFeedsWithCategoryName().catch(error => {
                expect(error).to.eq("error");
                FeedDb.fetchSurfFeedsAndCategoriesWithSource.restore();
            });
        });
    });

    describe("fetchAllParkedFeeds", () => {
        it("should fetch all parked feeds and return with category name", () => {
            let resultDocs = [{
                "key": "fbId1",
                "id": "fbId1",
                "value": {
                    "_id": "sportsCategoryId1"
                },
                "doc": {
                    "docType": "category",
                    "name": "Sports",
                    "_id": "sportsCategoryId1",
                    "_rev": "1-4b61e9edacc78ab1f189b68345d4d410"
                }
            }, {
                "key": "rssId1",
                "id": "feedId2",
                "value": "rssId1",
                "doc": {
                    "docType": "feed",
                    "status": "park",
                    "title": "tn",
                    "description": "www.facebookpolitics.com",
                    "sourceId": "rssId1",
                    "_id": "feedId2",
                    "_rev": "1-e41ef125b2f5fbef4f20d8c896eeea53"
                }
            }, {
                "key": "rssId1",
                "id": "rssId1",
                "value": {
                    "_id": "sportsCategoryId1"
                },
                "doc": {
                    "docType": "category",
                    "name": "Sports",
                    "_id": "sportsCategoryId1",
                    "_rev": "1-4b61e9edacc78ab1f189b68345d4d410"
                }
            }, {
                "key": "rssId1",
                "id": "rssId1",
                "value": {
                    "_id": "politicsCategoryId2"
                },
                "doc": {
                    "docType": "category",
                    "name": "Politics",
                    "_id": "politicsCategoryId2",
                    "_rev": "1-175853337b49fcd1db6474777f871d4a"
                }
            }];

            let expectedSources = [{
                "docType": "feed",
                "title": "tn",
                "description": "www.facebookpolitics.com",
                "sourceId": "rssId1",
                "_id": "feedId2",
                "status": "park",
                "_rev": "1-e41ef125b2f5fbef4f20d8c896eeea53",
                "categoryNames": "Sports, Politics"
            }];

            let fetchParkFeedssMock = sinon.mock(FeedDb).expects("fetchParkFeeds");
            fetchParkFeedssMock.returns(Promise.resolve(resultDocs));
            return FeedApplicationQueries.fetchAllParkedFeeds().then(sources => {
                expect(expectedSources).to.deep.equal(sources);
                fetchParkFeedssMock.verify();
                FeedDb.fetchParkFeeds.restore();
            });
        });

        it("should reject with error if fetching documents fails", () => {
            let fetchParkedFeedsStub = sinon.stub(FeedDb, "fetchParkFeeds");
            fetchParkedFeedsStub.returns(Promise.reject("error"));
            return FeedApplicationQueries.fetchAllParkedFeeds().catch(error => {
                expect(error).to.eq("error");
                FeedDb.fetchParkFeeds.restore();
            });
        });
    });

    describe("updateFeed", () => {
        it("should update feed after filtering required fields", () => {
            let rawFeedDoc = {
                "_id": "feedid",
                "_rev": "1-5ec67a3c5068912d169a4d30f4526eea",
                "categoryNames": "hindu",
                "content": "The company has set aside Rs. 1,100 cr for interest free salary advances",
                "docType": "feed",
                "feedType": "rss",
                "sourceId": "52359499-59E9-2D9E-AC38-922DBBB25A63",
                "tags": [],
                "title": "Tata Consultancy offers cash incentives for flood-hit Chennai staff",
                "type": "description"
            };

            let expectedFeedDoc = {
                "_id": "feedid",
                "_rev": "1-5ec67a3c5068912d169a4d30f4526eea",
                "content": "The company has set aside Rs. 1,100 cr for interest free salary advances",
                "docType": "feed",
                "feedType": "rss",
                "sourceId": "52359499-59E9-2D9E-AC38-922DBBB25A63",
                "tags": [],
                "title": "Tata Consultancy offers cash incentives for flood-hit Chennai staff",
                "type": "description",
                "status": "park"
            };
            let feedDbMock = sinon.mock(FeedDb).expects("updateFeed");
            feedDbMock.withArgs(expectedFeedDoc).returns(Promise.resolve({ "status": "ok" }));
            return FeedApplicationQueries.updateFeed(rawFeedDoc).then(() => {
                feedDbMock.verify();
                FeedDb.updateFeed.restore();
            });
        });
    });
});
<|endoftext|>


<commit_before>var React = require('react-native');

var {
  View,
  Text,
  ListView,
  StyleSheet
} = React;

import currentWeather from "../Api/weatherapi"
import forecast from "../Api/weatherapi"
import weatherIcon from "../Utils/icons"
//constants used for background colors

var Weather = React.createClass ({
   getInitialState: function() {
    return {
      dataSource: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 })
    };
  },
  componentDidMount: function(){
    // this.getWeather();
    this.getForecast();
  },
  getWeather() {
    currentWeather(this.state.latitude, this.state.longitude).then((data) => {
      this.setState(data);
    });
  },

  getForecast() {
    forecast(37, -122).then((data) => {
      console.log(data);
      this.setState({
        dataSource: this.state.dataSource.cloneWithRows(data.list)
      })
    })
  },

renderRow(rowData) {
    return (
      <WeatherCell weather={rowData}/>
  );
},
  render() {
    return (
    <View style={[styles.container, {backgorundColor: this.state.backgorundColor}]}>
      <View style={styles.currentWrapper}>
        <Text>Current Weather</Text>
        <Text>
          {this.state.city}
        </Text>
        <View style={styles.horContainer1}>
          <Text style={styles.icon}>
            {this.state.icon}
          </Text>
          <View style={styles.vertContainer}>
            <View style={styles.horContainer2}>
              <Text>Max: {this.state.temp_max}</Text>
            </View>
            <View style={styles.horContainer2}>
              <Text>Min: {this.state.temp_min}</Text>
            </View>
            <Text>Humidity: {this.state.humidity}</Text>
            <Text>Rain: {this.state.rain}</Text>
            <Text>Wind: {this.state.wind}</Text>
          </View>
      </View>
        <View style={styles.horContainer2}>
          <Text>{this.state.temperature}</Text>
          <Text>{this.state.description}</Text>
        </View>
      </View>
      <View style={styles.forecastWrapper}>
        <Text> Hourly Forecast </Text>
        // <ListView style={styles.listContainer} dataSource={this.state.dataSource} renderRow={this.renderRow}/>
      </View>
    </View>
  );
 }
});

var WeatherCell = React.createClass({
  render() {
    return (
      <View>
          <View style={styles.WeatherCell}>
            // <View style={styles.timeContainer}>
            //   <Text style={styles.darkText}>{formattedTime.time}</Text>
            // </View>
            <Text style={styles.icon}>
              {this.state.icon}
            </Text>
            <View style={styles.tempContainer}>
              <Text style={styles.darkText}>{rowData.temperature}</Text>
            </View>
            <Text style={styles.lightText}>Forecast: {rowData.description}</Text>
            <Text style={styles.lightText}>Rain: {rowData.rain}</Text>
            <Text style={styles.lightText}>Wind Speed: {rowData.wind_speed}</Text>
            <Text style={styles.lightText}>Wind Direction: {rowData.wind_direction}</Text>
        </View>
        <View style={styles.seperator}/>
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'stretch'
  },
  listContainer: {
    backgroundColor: '#F2F2F2'
  },
  timeContainer: {
    alignItems: 'center',
    marginRight: 20
  },
  seperator: {
    height: 1,
    backgroundColor: '#DDDDDD'
  },
  tempContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'flex-end'
  },
  darkText: {
    fontSize: 18
  },
  lightText: {
    color: '#9A9A9A'
  },
  horContainer1: {
    flexDirection: 'row',
    marginBottom: 20
  },
  horContainer2: {
    flexDirection: 'row',
    alignItems: 'flex-start'
  },
  vertContainer: {
    flex: 1,
    marginBottom: 10,
    marginTop: 10,
    backgroundColor: '#CCCCCC',
    padding: 15
  },
  currentWrapper: {
    flex: 3,
    padding: 20,
    marginTop: 65,
    backgroundColor: '#F2F2F2'
  },
  forecastWrapper: {
    flex: 5
  },
  icon: {
    // fontFamily: 'WeatherIcons-Regular',
    padding: 0,
    width: 75,
    height: 75,
    marginRight: 20
  },
  WeatherCell: {
    flex:1,
    flexDirection: 'row',
    justifyContent: 'center',
    marginLeft: 4,
    marginRight: 4,
    padding: 4,
    borderBottomWidth: .5,
    borderColor: 'lightgray'
  }
});

module.exports = Weather;
<commit_msg>add mock data to weather component<commit_after>var React = require('react-native');

var {
  View,
  Text,
  ListView,
  StyleSheet
} = React;

import currentWeather from "../Api/weatherapi"
import forecast from "../Api/weatherapi"
import weatherIcon from "../Utils/icons"
//constants used for background colors

var mockedCurrent = {
  city: 'San Francisco',
  temperature: '70˚F',
  temp_min: '70˚F',
  temp_max: '70˚F',
  description: 'Sun with light overcast',
  humidity: '40%',
  icon: weatherIcon('01d'),
  rain: '0%',
  wind: '10mph'
};

var mockedForecast = [{
  city: 'Oakland',
  temperature: '70˚F',
  description: 'Sun with light overcast',
  icon: weatherIcon('01d'),
  wind_speed: '10mph',
  wind_direction: 'NW',
  rain: '0%'
  },
  {
    city: 'Oakland',
    temperature: '70˚F',
    description: 'Sun with light overcast',
    icon: weatherIcon('01d'),
    wind_speed: '10mph',
    wind_direction: 'NW',
    rain: '0%'
  }
];

var Weather = React.createClass ({
   getInitialState: function() {
    debugger;
    return {
      dataSource: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 })
    };
  },

  componentDidMount: function(){
    // this.getWeather();
    this.getForecast();
  },

  componentWillUnmount: function(){

  },

  getWeather() {
    currentWeather(this.state.latitude, this.state.longitude).then((data) => {
      this.setState(data);
    });
  },

  getForecast() {
    forecast(37, -122).then((data) => {
      console.log(data);
      this.setState({
        dataSource: this.state.dataSource.cloneWithRows(data.list)
      })
    })
  },

renderRow(rowData) {
    return (
      <WeatherCell weather={rowData}/>
  );
},
  render() {
    return (
    <View style={[styles.container, {backgorundColor: this.state.backgorundColor}]}>
      <View style={styles.currentWrapper}>
        <Text>Current Weather</Text>
        <Text>
          // {this.state.city}
          {mockedCurrent.city}
        </Text>
        <View style={styles.horContainer1}>
          <Text style={styles.icon}>
            // {this.state.icon}
            {mockedCurrent.icon}
          </Text>
          <View style={styles.vertContainer}>
            <View style={styles.horContainer2}>
              <Text>
                Max:
                // {this.state.temp_max}
                {mockedCurrent.temp_max}
              </Text>
            </View>
            <View style={styles.horContainer2}>
              <Text>
                Min:
                // {this.state.temp_min}
                {mockedCurrent.temp_min}
              </Text>
            </View>
            <Text>Humidity:
            // {this.state.humidity}
            {mockedCurrent.humidity}
            </Text>
            <Text>Rain:
            // {this.state.rain}
            {mockedCurrent.rain}
            </Text>
            <Text>Wind:
            // {this.state.wind}
            {mockedCurrent.wind}
            </Text>
          </View>
      </View>
        <View style={styles.horContainer2}>
          <Text>
          // {this.state.temperature}
          {mockedCurrent.temperature}
          </Text>
          <Text>
          // {this.state.description}
          {mockedCurrent.description}
          </Text>
        </View>
      </View>
      <View style={styles.forecastWrapper}>
        <Text> Hourly Forecast </Text>
        <ListView style={styles.listContainer} dataSource={this.state.dataSource} renderRow={this.renderRow}/>
      </View>
    </View>
  );
 }
});

var WeatherCell = React.createClass({
  render() {
    return (
      <View>
          <View style={styles.WeatherCell}>
            // <View style={styles.timeContainer}>
            //   <Text style={styles.darkText}>{formattedTime.time}</Text>
            // </View>
            <Text style={styles.icon}>
              {this.state.icon}
            </Text>
            <View style={styles.tempContainer}>
              <Text style={styles.darkText}>{rowData.temperature}</Text>
            </View>
            <Text style={styles.lightText}>Forecast: {rowData.description}</Text>
            <Text style={styles.lightText}>Rain: {rowData.rain}</Text>
            <Text style={styles.lightText}>Wind Speed: {rowData.wind_speed}</Text>
            <Text style={styles.lightText}>Wind Direction: {rowData.wind_direction}</Text>
        </View>
        <View style={styles.seperator}/>
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'stretch'
  },
  listContainer: {
    backgroundColor: '#F2F2F2'
  },
  timeContainer: {
    alignItems: 'center',
    marginRight: 20
  },
  seperator: {
    height: 1,
    backgroundColor: '#DDDDDD'
  },
  tempContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'flex-end'
  },
  darkText: {
    fontSize: 18
  },
  lightText: {
    color: '#9A9A9A'
  },
  horContainer1: {
    flexDirection: 'row',
    marginBottom: 20
  },
  horContainer2: {
    flexDirection: 'row',
    alignItems: 'flex-start'
  },
  vertContainer: {
    flex: 1,
    marginBottom: 10,
    marginTop: 10,
    backgroundColor: '#CCCCCC',
    padding: 15
  },
  currentWrapper: {
    flex: 3,
    padding: 20,
    marginTop: 65,
    backgroundColor: '#F2F2F2'
  },
  forecastWrapper: {
    flex: 5
  },
  icon: {
    // fontFamily: 'WeatherIcons-Regular',
    padding: 0,
    width: 75,
    height: 75,
    marginRight: 20
  },
  WeatherCell: {
    flex:1,
    flexDirection: 'row',
    justifyContent: 'center',
    marginLeft: 4,
    marginRight: 4,
    padding: 4,
    borderBottomWidth: .5,
    borderColor: 'lightgray'
  }
});

module.exports = Weather;
<|endoftext|>