<commit_before>define([
  'lodash',
  'jquery',
  '../core_module',
],
function (_, $, coreModule) {
  'use strict';

  coreModule.directive('metricSegment', function($compile, $sce) {
    var inputTemplate = '<input type="text" data-provide="typeahead" ' +
      ' class="tight-form-clear-input input-medium"' +
      ' spellcheck="false" style="display:none"></input>';

    var buttonTemplate = '';

    return {
      scope: {
        segment: "=",
        getOptions: "&",
        onChange: "&",
      },

      link: function($scope, elem) {
        var $input = $(inputTemplate);
        var $button = $(buttonTemplate);
        var segment = $scope.segment;
        var options = null;
        var cancelBlur = null;

        $input.appendTo(elem);
        $button.appendTo(elem);

        $scope.updateVariableValue = function(value) {
          if (value === '' || segment.value === value) {
            return;
          }

          $scope.$apply(function() {
            var selected = _.findWhere($scope.altSegments, { value: value });
            if (selected) {
              segment.value = selected.value;
              segment.html = selected.html;
              segment.fake = false;
              segment.expandable = selected.expandable;
            }
            else if (segment.custom !== 'false') {
              segment.value = value;
              segment.html = $sce.trustAsHtml(value);
              segment.expandable = true;
              segment.fake = false;
            }

            $scope.onChange();
          });
        };

        $scope.switchToLink = function(now) {
          if (now === true || cancelBlur) {
            clearTimeout(cancelBlur);
            cancelBlur = null;
            $input.hide();
            $button.show();
            $scope.updateVariableValue($input.val());
          }
          else {
            // need to have long delay because the blur
            // happens long before the click event on the typeahead options
            cancelBlur = setTimeout($scope.switchToLink, 100);
          }
        };

        $scope.source = function(query, callback) {
          if (options) { return options; }

          $scope.$apply(function() {
            $scope.getOptions().then(function(altSegments) {
              $scope.altSegments = altSegments;
              options = _.map($scope.altSegments, function(alt) { return alt.value; });

              // add custom values
              if (segment.custom !== 'false') {
                if (!segment.fake && _.indexOf(options, segment.value) === -1) {
                  options.unshift(segment.value);
                }
              }

              callback(options);
            });
          });
        };

        $scope.updater = function(value) {
          if (value === segment.value) {
            clearTimeout(cancelBlur);
            $input.focus();
            return value;
          }

          $input.val(value);
          $scope.switchToLink(true);

          return value;
        };

        $scope.matcher = function(item) {
          var str = this.query;
          if (str[0] === '/') { str = str.substring(1); }
          if (str[str.length - 1] === '/') { str = str.substring(0, str.length-1); }
          try {
            return item.toLowerCase().match(str);
          } catch(e) {
            return false;
          }
        };

        $input.attr('data-provide', 'typeahead');
        $input.typeahead({ source: $scope.source, minLength: 0, items: 10000, updater: $scope.updater, matcher: $scope.matcher });

        var typeahead = $input.data('typeahead');
        typeahead.lookup = function () {
          this.query = this.$element.val() || '';
          var items = this.source(this.query, $.proxy(this.process, this));
          return items ? this.process(items) : items;
        };

        $button.keydown(function(evt) {
          // trigger typeahead on down arrow or enter key
          if (evt.keyCode === 40 || evt.keyCode === 13) {
            $button.click();
          }
        });

        $button.click(function() {
          options = null;
          $input.css('width', ($button.width() + 16) + 'px');

          $button.hide();
          $input.show();
          $input.focus();

          var typeahead = $input.data('typeahead');
          if (typeahead) {
            $input.val('');
            typeahead.lookup();
          }
        });

        $input.blur($scope.switchToLink);

        $compile(elem.contents())($scope);
      }
    };
  });

  coreModule.directive('metricSegmentModel', function(uiSegmentSrv, $q) {
    return {
      template: '<metric-segment segment="segment" get-options="getOptionsInternal()" on-change="onSegmentChange()"></metric-segment>',
      restrict: 'E',
      scope: {
        property: "=",
        options: "=",
        getOptions: "&",
        onChange: "&",
      },
      link: {
        pre: function postLink($scope, elem, attrs) {

          $scope.valueToSegment = function(value) {
            var option = _.findWhere($scope.options, {value: value});
            var segment = {
              cssClass: attrs.cssClass,
              custom: attrs.custom,
              value: option ? option.text : value,
            };
            return uiSegmentSrv.newSegment(segment);
          };

          $scope.getOptionsInternal = function() {
            if ($scope.options) {
              var optionSegments = _.map($scope.options, function(option) {
                return uiSegmentSrv.newSegment({value: option.text});
              });
              return $q.when(optionSegments);
            } else {
              return $scope.getOptions();
            }
          };

          $scope.onSegmentChange = function() {
            if ($scope.options) {
              var option = _.findWhere($scope.options, {text: $scope.segment.value});
              if (option && option.value !== $scope.property) {
                $scope.property = option.value;
              } else if (attrs.custom !== 'false') {
                $scope.property = $scope.segment.value;
              }
            } else {
              $scope.property = $scope.segment.value;
            }

            // needs to call this after digest so
            // property is synced with outerscope
            $scope.$$postDigest(function() {
              $scope.onChange();
            });
          };

          $scope.segment = $scope.valueToSegment($scope.property);
        }
      }
    };
  });
});
<commit_msg>fix(influxdb): fixed issue with metric segment component that caused double events<commit_after>define([
  'lodash',
  'jquery',
  '../core_module',
],
function (_, $, coreModule) {
  'use strict';

  coreModule.directive('metricSegment', function($compile, $sce) {
    var inputTemplate = '<input type="text" data-provide="typeahead" ' +
      ' class="tight-form-clear-input input-medium"' +
      ' spellcheck="false" style="display:none"></input>';

    var buttonTemplate = '';

    return {
      scope: {
        segment: "=",
        getOptions: "&",
        onChange: "&",
      },

      link: function($scope, elem) {
        var $input = $(inputTemplate);
        var $button = $(buttonTemplate);
        var segment = $scope.segment;
        var options = null;
        var cancelBlur = null;
        var linkMode = true;

        $input.appendTo(elem);
        $button.appendTo(elem);

        $scope.updateVariableValue = function(value) {
          if (value === '' || segment.value === value) {
            return;
          }

          $scope.$apply(function() {
            var selected = _.findWhere($scope.altSegments, { value: value });
            if (selected) {
              segment.value = selected.value;
              segment.html = selected.html;
              segment.fake = false;
              segment.expandable = selected.expandable;
            }
            else if (segment.custom !== 'false') {
              segment.value = value;
              segment.html = $sce.trustAsHtml(value);
              segment.expandable = true;
              segment.fake = false;
            }

            $scope.onChange();
          });
        };

        $scope.switchToLink = function() {
          if (linkMode) { return; }

          clearTimeout(cancelBlur);
          cancelBlur = null;
          linkMode = true;
          $input.hide();
          $button.show();
          $scope.updateVariableValue($input.val());
        };

        $scope.inputBlur = function() {
          // happens long before the click event on the typeahead options
          // need to have long delay because the blur
          cancelBlur = setTimeout($scope.switchToLink, 100);
        };

        $scope.source = function(query, callback) {
          if (options) { return options; }

          $scope.$apply(function() {
            $scope.getOptions().then(function(altSegments) {
              $scope.altSegments = altSegments;
              options = _.map($scope.altSegments, function(alt) { return alt.value; });

              // add custom values
              if (segment.custom !== 'false') {
                if (!segment.fake && _.indexOf(options, segment.value) === -1) {
                  options.unshift(segment.value);
                }
              }

              callback(options);
            });
          });
        };

        $scope.updater = function(value) {
          if (value === segment.value) {
            clearTimeout(cancelBlur);
            $input.focus();
            return value;
          }

          $input.val(value);
          $scope.switchToLink();

          return value;
        };

        $scope.matcher = function(item) {
          var str = this.query;
          if (str[0] === '/') { str = str.substring(1); }
          if (str[str.length - 1] === '/') { str = str.substring(0, str.length-1); }
          try {
            return item.toLowerCase().match(str);
          } catch(e) {
            return false;
          }
        };

        $input.attr('data-provide', 'typeahead');
        $input.typeahead({ source: $scope.source, minLength: 0, items: 10000, updater: $scope.updater, matcher: $scope.matcher });

        var typeahead = $input.data('typeahead');
        typeahead.lookup = function () {
          this.query = this.$element.val() || '';
          var items = this.source(this.query, $.proxy(this.process, this));
          return items ? this.process(items) : items;
        };

        $button.keydown(function(evt) {
          // trigger typeahead on down arrow or enter key
          if (evt.keyCode === 40 || evt.keyCode === 13) {
            $button.click();
          }
        });

        $button.click(function() {
          options = null;
          $input.css('width', ($button.width() + 16) + 'px');

          $button.hide();
          $input.show();
          $input.focus();

          linkMode = false;

          var typeahead = $input.data('typeahead');
          if (typeahead) {
            $input.val('');
            typeahead.lookup();
          }
        });

        $input.blur($scope.inputBlur);

        $compile(elem.contents())($scope);
      }
    };
  });

  coreModule.directive('metricSegmentModel', function(uiSegmentSrv, $q) {
    return {
      template: '<metric-segment segment="segment" get-options="getOptionsInternal()" on-change="onSegmentChange()"></metric-segment>',
      restrict: 'E',
      scope: {
        property: "=",
        options: "=",
        getOptions: "&",
        onChange: "&",
      },
      link: {
        pre: function postLink($scope, elem, attrs) {

          $scope.valueToSegment = function(value) {
            var option = _.findWhere($scope.options, {value: value});
            var segment = {
              cssClass: attrs.cssClass,
              custom: attrs.custom,
              value: option ? option.text : value,
            };
            return uiSegmentSrv.newSegment(segment);
          };

          $scope.getOptionsInternal = function() {
            if ($scope.options) {
              var optionSegments = _.map($scope.options, function(option) {
                return uiSegmentSrv.newSegment({value: option.text});
              });
              return $q.when(optionSegments);
            } else {
              return $scope.getOptions();
            }
          };

          $scope.onSegmentChange = function() {
            if ($scope.options) {
              var option = _.findWhere($scope.options, {text: $scope.segment.value});
              if (option && option.value !== $scope.property) {
                $scope.property = option.value;
              } else if (attrs.custom !== 'false') {
                $scope.property = $scope.segment.value;
              }
            } else {
              $scope.property = $scope.segment.value;
            }

            // needs to call this after digest so
            // property is synced with outerscope
            $scope.$$postDigest(function() {
              $scope.onChange();
            });
          };

          $scope.segment = $scope.valueToSegment($scope.property);
        }
      }
    };
  });
});
<|endoftext|>


<commit_before>define([
  'common/collections/grouped_timeseries',
  'extensions/collections/collection',
  'extensions/models/query'
],
function (GroupedTimeseries, Collection, Query) {
  describe('GroupedTimeseries', function () {
    var response;

    var collection;
    beforeEach(function () {
      response = {
        'data': [
          {
            'some:value': 3,
            'values': [
              {
                '_end_at': '2012-09-01T00:00:00+00:00',
                'some:value': 3,
                '_start_at': '2012-08-01T00:00:00+00:00'
              },
              {
                '_end_at': '2012-10-01T00:00:00+00:00',
                'some:value': null,
                '_start_at': '2012-09-01T00:00:00+00:00'
              }
            ],
            'some-category': 'xyz'
          },
          {
            'some:value': 7,
            'values': [
              {
                '_end_at': '2012-09-01T00:00:00+00:00',
                'some:value': 3,
                '_start_at': '2012-08-01T00:00:00+00:00'
              },
              {
                '_end_at': '2012-10-01T00:00:00+00:00',
                'some:value': 6,
                '_start_at': '2012-09-01T00:00:00+00:00'
              }
            ],
            'some-category': 'abc'
          },
          {
            'some:value': 16,
            'values': [
              {
                '_end_at': '2012-09-01T00:00:00+00:00',
                'some:value': 6,
                '_start_at': '2012-08-01T00:00:00+00:00'
              },
              {
                '_end_at': '2012-10-01T00:00:00+00:00',
                'some:value': 10,
                '_start_at': '2012-09-01T00:00:00+00:00'
              }
            ],
            'some-category': 'def'
          }
        ]
      };
      collection = new GroupedTimeseries([], {
        'data-type': 'some-type',
        'data-group': 'some-group',
        valueAttr: 'some:value',
        category: 'some-category',
        period: 'month',
        startAt: '2014-01-10T00:00:00+00:00',
        endAt: '2014-03-10T00:00:00+00:00',
        axes: {
          x: {
            'label': 'Date',
            'key': '_start_at'
          },
          y: [
            {
              'label': 'ABC',
              'groupId': 'abc'
            },
            {
              'label': 'DEF',
              'groupId': 'def'
            },
            {
              'label': 'XYZ',
              'groupId': 'xyz'
            }
          ]
        }
      });
      collection.backdropUrl = '//testdomain/{{ data-group }}/{{ data-type }}';
    });

    it('should pass through duration to query generator which wont add it to the url', function () {
      spyOn(Query.prototype, 'set');
      var durationCollection = new GroupedTimeseries([], {
        'period': 'week',
        'duration': 60
      });
      var args = durationCollection.query.set.mostRecentCall.args;
      expect(durationCollection.query.set).toHaveBeenCalled();
      expect(args[0].duration).toEqual(60);
      expect(durationCollection.url()).not.toContain('duration');
    });

    it('should not add duration to url undefined', function () {
      var durationCollection = new GroupedTimeseries([], {'period': 'week'});
      expect(durationCollection.url()).not.toContain('duration');
    });

    describe('url', function () {
      /* global decodeURIComponent */
      it('should query backdrop with the correct url for the config', function () {
        expect(decodeURIComponent(collection.url())).toContain('some-group');
        expect(decodeURIComponent(collection.url())).toContain('some-type');
        expect(decodeURIComponent(collection.url())).toContain('period=month');
        expect(decodeURIComponent(collection.url())).toContain('start_at=2014-01-10T00:00:00+00:00');
        expect(decodeURIComponent(collection.url())).toContain('end_at=2014-03-10T00:00:00+00:00');
        expect(decodeURIComponent(collection.url())).toContain('group_by=some-category');
        expect(decodeURIComponent(collection.url())).toContain('collect=some:value');
        expect(decodeURIComponent(collection.url())).not.toContain('filter_by');
      });

      it('should contain filters', function () {
        var filteredCollection = new GroupedTimeseries([], {
          'data-type': 'some-type',
          'data-group': 'some-group',
          filterBy: ['filter_1:abc', 'filter_2:def']
        });
        filteredCollection.backdropUrl = '//testdomain/{{ data-group }}/{{ data-type }}';

        expect(filteredCollection.url()).toContain('filter_by=filter_1%3Aabc');
        expect(filteredCollection.url()).toContain('filter_by=filter_2%3Adef');
      });
    });

    describe('parse', function () {

      it('collapses groups into single models', function () {
        var parsed = collection.parse(response);
        expect(parsed.length).toEqual(2);
        expect(parsed[0]['abc:some:value']).toEqual(3);
        expect(parsed[1]['abc:some:value']).toEqual(6);
        expect(parsed[0]['def:some:value']).toEqual(6);
        expect(parsed[1]['def:some:value']).toEqual(10);
        expect(parsed[0]['xyz:some:value']).toEqual(3);
        expect(parsed[1]['xyz:some:value']).toEqual(null);
      });

      it('adds total properties to models', function () {
        var parsed = collection.parse(response);
        expect(parsed[0]['total:some:value']).toEqual(12);
        expect(parsed[1]['total:some:value']).toEqual(16);
      });

      it('adds percent properties to models for each axis property', function () {
        var parsed = collection.parse(response);
        expect(parsed[0]['abc:some:value:percent']).toEqual(0.25);
        expect(parsed[0]['def:some:value:percent']).toEqual(0.5);
        expect(parsed[0]['xyz:some:value:percent']).toEqual(0.25);
        expect(parsed[1]['abc:some:value:percent']).toEqual(0.375);
        expect(parsed[1]['def:some:value:percent']).toEqual(0.625);
        expect(parsed[1]['xyz:some:value:percent']).toEqual(0);
      });

      it('copes if not all of the specified series are present in the response', function () {
        var collectionWithExtraSeries = new GroupedTimeseries([], {
          'data-type': 'some-type',
          'data-group': 'some-group',
          valueAttr: 'some:value',
          category: 'some-category',
          period: 'month',
          axes: {
            x: {
              'label': 'Date',
              'key': '_start_at'
            },
            y: [
              {
                'label': 'ABC',
                'groupId': 'abc'
              },
              {
                'label': 'DEF',
                'groupId': 'def'
              },
              {
                'label': 'XYZ',
                'groupId': 'xyz'
              },
              {
                'label': 'GHI',
                'groupId': 'ghi'
              }
            ]
          }
        });

        var parsed = collectionWithExtraSeries.parse(response);

        expect(parsed[0]['ghi:some:value']).toEqual(null);
        expect(parsed[1]['ghi:some:value']).toEqual(null);
        expect(parsed[0]['ghi:some:value:percent']).toEqual(0);
        expect(parsed[1]['ghi:some:value:percent']).toEqual(0);
      });

      it('copes if none of the specified series are present in the response', function () {
        var collectionWithExtraSeries = new GroupedTimeseries([], {
          'data-type': 'some-type',
          'data-group': 'some-group',
          valueAttr: 'some:value',
          category: 'some-category',
          period: 'month',
          axes: {
            x: {
              'label': 'Date',
              'key': '_start_at'
            },
            y: [
              {
                'label': 'GHI',
                'groupId': 'ghi'
              }
            ]
          }
        });

        var parsed = collectionWithExtraSeries.parse(response);
        expect(parsed[0]['ghi:some:value']).toEqual(null);
        expect(parsed[1]['ghi:some:value']).toEqual(null);
        expect(parsed[0]['total:some:value']).toEqual(null);
        expect(parsed[1]['total:some:value']).toEqual(null);
        expect(parsed[0]['ghi:some:value:percent']).toEqual(null);
        expect(parsed[1]['ghi:some:value:percent']).toEqual(null);
      });

      it('groups categories together if mappings are provided', function () {
        collection.options.groupMapping = {
          def: 'abc'
        };
        collection.options.axes.y = [
          {
            'label': 'ABC',
            'groupId': 'abc'
          },
          {
            'label': 'XYZ',
            'groupId': 'xyz'
          }
        ];

        var parsed = collection.parse(response);
        expect(parsed[0]['abc:some:value']).toEqual(9);
        expect(parsed[1]['abc:some:value']).toEqual(16);
        expect(parsed[0]['abc:some:value:percent']).toEqual(0.75);
        expect(parsed[1]['abc:some:value:percent']).toEqual(1);

        expect(parsed[0]['def:some:value']).toBeUndefined();
        expect(parsed[1]['def:some:value']).toBeUndefined();
        expect(parsed[0]['def:some:value:percent']).toBeUndefined();
        expect(parsed[1]['def:some:value:percent']).toBeUndefined();

        expect(parsed[0]['total:some:value']).toEqual(12);
        expect(parsed[1]['total:some:value']).toEqual(16);

      });

    });

  });
});
<commit_msg>Fix GroupedTimeseries tests<commit_after>define([
  'common/collections/grouped_timeseries',
  'extensions/collections/collection'
],
function (GroupedTimeseries, Collection) {
  describe('GroupedTimeseries', function () {
    var response;

    var collection;
    beforeEach(function () {
      response = {
        'data': [
          {
            'some:value': 3,
            'values': [
              {
                '_end_at': '2012-09-01T00:00:00+00:00',
                'some:value': 3,
                '_start_at': '2012-08-01T00:00:00+00:00'
              },
              {
                '_end_at': '2012-10-01T00:00:00+00:00',
                'some:value': null,
                '_start_at': '2012-09-01T00:00:00+00:00'
              }
            ],
            'some-category': 'xyz'
          },
          {
            'some:value': 7,
            'values': [
              {
                '_end_at': '2012-09-01T00:00:00+00:00',
                'some:value': 3,
                '_start_at': '2012-08-01T00:00:00+00:00'
              },
              {
                '_end_at': '2012-10-01T00:00:00+00:00',
                'some:value': 6,
                '_start_at': '2012-09-01T00:00:00+00:00'
              }
            ],
            'some-category': 'abc'
          },
          {
            'some:value': 16,
            'values': [
              {
                '_end_at': '2012-09-01T00:00:00+00:00',
                'some:value': 6,
                '_start_at': '2012-08-01T00:00:00+00:00'
              },
              {
                '_end_at': '2012-10-01T00:00:00+00:00',
                'some:value': 10,
                '_start_at': '2012-09-01T00:00:00+00:00'
              }
            ],
            'some-category': 'def'
          }
        ]
      };
      collection = new GroupedTimeseries([], {
        dataSource: {
          'data-type': 'some-type',
          'data-group': 'some-group',
          'query-params': {
            period: 'month',
            start_at: '2014-01-10T00:00:00+00:00',
            end_at: '2014-03-10T00:00:00+00:00',
            collect: 'some:value',
            group_by: 'some-category'
          }
        },
        valueAttr: 'some:value',
        category: 'some-category',
        axes: {
          x: {
            'label': 'Date',
            'key': '_start_at'
          },
          y: [
            {
              'label': 'ABC',
              'groupId': 'abc'
            },
            {
              'label': 'DEF',
              'groupId': 'def'
            },
            {
              'label': 'XYZ',
              'groupId': 'xyz'
            }
          ]
        }
      });
      collection.dataSource.backdropUrl = '//testdomain/{{ data-group }}/{{ data-type }}';
    });

    describe('parse', function () {

      it('collapses groups into single models', function () {
        var parsed = collection.parse(response);
        expect(parsed.length).toEqual(2);
        expect(parsed[0]['abc:some:value']).toEqual(3);
        expect(parsed[1]['abc:some:value']).toEqual(6);
        expect(parsed[0]['def:some:value']).toEqual(6);
        expect(parsed[1]['def:some:value']).toEqual(10);
        expect(parsed[0]['xyz:some:value']).toEqual(3);
        expect(parsed[1]['xyz:some:value']).toEqual(null);
      });

      it('adds total properties to models', function () {
        var parsed = collection.parse(response);
        expect(parsed[0]['total:some:value']).toEqual(12);
        expect(parsed[1]['total:some:value']).toEqual(16);
      });

      it('adds percent properties to models for each axis property', function () {
        var parsed = collection.parse(response);
        expect(parsed[0]['abc:some:value:percent']).toEqual(0.25);
        expect(parsed[0]['def:some:value:percent']).toEqual(0.5);
        expect(parsed[0]['xyz:some:value:percent']).toEqual(0.25);
        expect(parsed[1]['abc:some:value:percent']).toEqual(0.375);
        expect(parsed[1]['def:some:value:percent']).toEqual(0.625);
        expect(parsed[1]['xyz:some:value:percent']).toEqual(0);
      });

      it('copes if not all of the specified series are present in the response', function () {
        var collectionWithExtraSeries = new GroupedTimeseries([], {
          dataSource: {
            'data-type': 'some-type',
            'data-group': 'some-group',
            'query-params': {
              period: 'month',
              group_by: 'some-category'
            }
          },
          valueAttr: 'some:value',
          category: 'some-category',
          axes: {
            x: {
              'label': 'Date',
              'key': '_start_at'
            },
            y: [
              {
                'label': 'ABC',
                'groupId': 'abc'
              },
              {
                'label': 'DEF',
                'groupId': 'def'
              },
              {
                'label': 'XYZ',
                'groupId': 'xyz'
              },
              {
                'label': 'GHI',
                'groupId': 'ghi'
              }
            ]
          }
        });

        var parsed = collectionWithExtraSeries.parse(response);

        expect(parsed[0]['ghi:some:value']).toEqual(null);
        expect(parsed[1]['ghi:some:value']).toEqual(null);
        expect(parsed[0]['ghi:some:value:percent']).toEqual(0);
        expect(parsed[1]['ghi:some:value:percent']).toEqual(0);
      });

      it('copes if none of the specified series are present in the response', function () {
        var collectionWithExtraSeries = new GroupedTimeseries([], {
          'data-type': 'some-type',
          'data-group': 'some-group',
          valueAttr: 'some:value',
          category: 'some-category',
          period: 'month',
          axes: {
            x: {
              'label': 'Date',
              'key': '_start_at'
            },
            y: [
              {
                'label': 'GHI',
                'groupId': 'ghi'
              }
            ]
          }
        });

        var parsed = collectionWithExtraSeries.parse(response);
        expect(parsed[0]['ghi:some:value']).toEqual(null);
        expect(parsed[1]['ghi:some:value']).toEqual(null);
        expect(parsed[0]['total:some:value']).toEqual(null);
        expect(parsed[1]['total:some:value']).toEqual(null);
        expect(parsed[0]['ghi:some:value:percent']).toEqual(null);
        expect(parsed[1]['ghi:some:value:percent']).toEqual(null);
      });

      it('groups categories together if mappings are provided', function () {
        collection.options.groupMapping = {
          def: 'abc'
        };
        collection.options.axes.y = [
          {
            'label': 'ABC',
            'groupId': 'abc'
          },
          {
            'label': 'XYZ',
            'groupId': 'xyz'
          }
        ];

        var parsed = collection.parse(response);
        expect(parsed[0]['abc:some:value']).toEqual(9);
        expect(parsed[1]['abc:some:value']).toEqual(16);
        expect(parsed[0]['abc:some:value:percent']).toEqual(0.75);
        expect(parsed[1]['abc:some:value:percent']).toEqual(1);

        expect(parsed[0]['def:some:value']).toBeUndefined();
        expect(parsed[1]['def:some:value']).toBeUndefined();
        expect(parsed[0]['def:some:value:percent']).toBeUndefined();
        expect(parsed[1]['def:some:value:percent']).toBeUndefined();

        expect(parsed[0]['total:some:value']).toEqual(12);
        expect(parsed[1]['total:some:value']).toEqual(16);

      });

    });

  });
});
<|endoftext|>


<commit_before>var bootcards = bootcards || {

    offCanvasToggleEl : null,
    offCanvasMenuEl : null,
    mainContentEl : null,
    _isXS : null,
    _isIOS : false,
    _isFullScreen : false,
    _simplePortraitMode : false

};

bootcards.init = function( options ) {

    $(document).ready( function() {

        //detect full screen mode on iOS
        if (
            ("standalone" in window.navigator) &&
            window.navigator.standalone
            ) {

                bootcards._isIOS = true;
                bootcards._isFullScreen = true;

                $("body").addClass("ios fullscreen");

        }

        //initialize the off canvas menu
        bootcards._initOffCanvasMenu(
          $('.offcanvas'),
          $('.bootcards-container'),
          options.offCanvasHideOnMainClick
        );

        if (options.enableTabletPortraitMode) {

           if (options.simplePortraitMode) {
                bootcards._simplePortraitMode = options.simplePortraitMode;
           }

           bootcards._initTabletPortraitMode();

        }

        if (options.disableRubberBanding) {

            bootcards.disableRubberBanding();
        }

    } );

};

/*
 * Get the Bootstrap enviroment we're in.
 * Found at http://stackoverflow.com/questions/14441456/how-to-detect-which-device-view-youre-on-using-twitter-bootstrap-api/15150381#15150381
 * Function writen by Raphael_ (http://stackoverflow.com/users/1661358/raphael)
 */
bootcards.findBootstrapEnvironment = function() {
    var envs = ["ExtraSmall", "Small", "Medium", "Large"];
    var envValues = ["xs", "sm", "md", "lg"];

    $el = $('
');
    $el.appendTo($('body'));

    for (var i = envValues.length - 1; i >= 0; i--) {
        var envVal = envValues[i];

        $el.addClass('hidden-'+envVal);
        if ($el.is(':hidden')) {
            $el.remove();
            return envs[i];
        }
    }
};

bootcards.isXS = function() {
    if (this._isXS === null ) {
        this._isXS = (this.findBootstrapEnvironment() == "ExtraSmall");
    }
    return this._isXS;
};

/*
 * Disable rubberbanding effect in iOS.
 * Based on the 'Baking Soda Paste' technique written by Armagan Amcalar at
 * http://blog.armaganamcalar.com/post/70847348271/baking-soda-paste
 */
bootcards.disableRubberBanding = function() {
   document.body.addEventListener('touchstart', function() {
        document.body.addEventListener('touchmove', function moveListener(e) {
            document.body.removeEventListener('touchmove', moveListener);

            var el = e.target;

            do {

                var h = parseInt(window.getComputedStyle(el, null).height, 10);
                var sH = el.scrollHeight;

                if (h < sH) {
                    return;
                }
            } while (el != document.body && el.parentElement != document.body && (el = el.parentElement));

            e.preventDefault();
        });
    });

};

/*
 * Initialize an off-canvas menu. This takes 3 arguments:
 * - the off canvas element to slide in
 * - the main content area to push away when the off canvas element slides in
 * - a boolean indicating if the off canvas menu should be hidden when the main content area is clicked
 *
 * An off canvas menu is required for the portrait-single-pane mode
 *
 */
bootcards._initOffCanvasMenu = function(offCanvasMenuEl, mainContentEl, hideOnMainClick) {

    var $toggleEl = $('[data-toggle=offcanvas]');

    if ($toggleEl.length===0 ) {
        return;
    }

    this.offCanvasToggleEl = $toggleEl;
    this.offCanvasMenuEl = offCanvasMenuEl;
    this.mainContentEl = mainContentEl;

    this.offCanvasToggleEl
        .on("click", function() {
          bootcards.offCanvasMenuEl.toggleClass("active");
          if (bootcards.mainContentEl) { bootcards.mainContentEl.toggleClass("active-left"); }
        });

    //hide the offcanvas if you click on the body
    if (hideOnMainClick && bootcards.mainContentEl) {
        this.mainContentEl.on("click", function() {
            bootcards.offCanvasMenuEl.removeClass('active');
            var $this = $(this);
            if ($this.hasClass('active-left') ) {
                $this.removeClass('active-left');
            }

        });
    }

};

//hide the offcanvas menu
bootcards.hideOffCanvasMenu = function() {

    if (this.offCanvasMenuEl) { this.offCanvasMenuEl.removeClass('active'); }
    if (this.mainContentEl) { this.mainContentEl.removeClass('active'); }

};

bootcards._initTabletPortraitMode = function() {

    //don't activate on desktop or smartphones
    if ( typeof window.orientation == 'undefined' || bootcards.isXS() ) {
        return;
    }

    $(window)
        .on( 'resize', function() {
            setTimeout( function() {
                bootcards._setOrientation(false);
            } , 150);
            event.stopPropagation();
        } )
        .on( 'load', bootcards._setOrientation(true) );

};

bootcards._setOrientation = function(init) {

    var isPortrait = ($(window).width() > $(window).height())? false : true;

    if (!init) {
        if (bootcards.offCanvasMenuEl) { bootcards.offCanvasMenuEl.removeClass("active"); }
    }

    bootcards._initListEl();
    bootcards._initCardsEl();

    if (isPortrait) {

        //get the columns classes for the list and details
        if (bootcards.listEl.length === 0) {
            return;
        }

        //immediately hide the list on load in portrait mode
        if (init) {
            bootcards.listEl.hide();
        }

        //set the column to full width
        bootcards.cardsEl
            .removeClass(bootcards.cardsColClass)
            .addClass('col-xs-12');

        //hide the az picker
        $('.bootcards-az-picker').hide();

        //in simple mode, we only hide the list column and show the content in full width
        if ( bootcards._simplePortraitMode ) {
            bootcards.listEl
                .hide()
                .removeClass(bootcards.listColClass);
            return; //we're done
        }

        if (!bootcards.listOffcanvasToggle) {
            //create the list title & toggle elements

             //create the button that shows/hides the list
            bootcards.listOffcanvasToggle = $('<button type="button" class="btn btn-default pull-left offcanvaslist-toggle">' +
                '' + bootcards.listTitle + '' +
                '</button>')
                    .on("click", function() {

                        //on click: show the list & title
                        bootcards.listEl.toggleClass("active");
                        bootcards.listTitleEl.toggleClass("active");

                    });

            //create the title element of the list offcanvas
            bootcards.listTitleEl = $("
"  +
               "" + bootcards.listTitle + "
");

            if (bootcards.offCanvasToggleEl) {
                //if we have an offcanvas: add the toggle button to the list

                //create the title element for the menu offcanvas, insert it before the menu offcanvas
                bootcards.offCanvasMenuTitleEl = $("
"  +
                   "Menu
");
                bootcards.offCanvasMenuEl.before( bootcards.offCanvasMenuTitleEl );

                //clone the button to show/hide the menu in the list title
                bootcards.offCanvasToggleEl.clone(false)
                    .prependTo(bootcards.listTitleEl)
                    .on("click", function() {
                        bootcards.offCanvasMenuEl.toggleClass("active");
                        bootcards.offCanvasMenuTitleEl.toggleClass("active");
                    })
                    .children("i")
                        .removeClass('fa-bars')
                        .addClass('fa-angle-left');
            }

            //add the title element and the toggle button to the top navbar
           $('.navbar-header')
               .after(
                    bootcards.listTitleEl, bootcards.listOffcanvasToggle);

            //hide the list & list title when to body is clicked
            bootcards.mainContentEl.on("click", function() {

                bootcards.listEl.removeClass('active');
                bootcards.listTitleEl.removeClass('active');
                bootcards.offCanvasMenuTitleEl.removeClass('active');

            });

            bootcards.listEl.on("click", function() {

                var $this = $(this);
                $this.removeClass('active');
                bootcards.listTitleEl.removeClass('active');

                //stop propagation when an element in the list offcanvas is clicked
                event.stopPropagation();

            });

            //increase the width of the menu: set it to the same size as the list
            if ( bootcards.offCanvasMenuEl ) {
                bootcards.offCanvasMenuEl
                    .addClass('offcanvas-list')
                    .on("click", function() {

                        //hide the menu on click
                        var $this = $(this);
                        $this.removeClass('active');
                        bootcards.offCanvasMenuTitleEl.removeClass('active');
                        bootcards.listEl.removeClass('active');
                        bootcards.listTitleEl.removeClass('active');

                        //stop propagation when an element in the list offcanvas is clicked
                        event.stopPropagation();

                    });
            }

        }

        //hide the menu button
        if (bootcards.offCanvasToggleEl) {
            bootcards.offCanvasToggleEl.hide();
        }

        //show the button to toggle the list
        bootcards.listOffcanvasToggle.show();

        bootcards.listEl
            .removeClass(bootcards.listColClass)
            .addClass('offcanvas-list')
            .show();

    } else {

        //show the menu button
        if (bootcards.offCanvasToggleEl) {
            bootcards.offCanvasToggleEl.show();
        }

        //show the list again
        if (bootcards.listEl) {
            bootcards.listEl
                .removeClass('offcanvas-list active')
                .addClass(bootcards.listColClass)
                .show();
        }

        //hide the button to show the list, remove the list & title
        if ( bootcards.listOffcanvasToggle ) {
            bootcards.listOffcanvasToggle.hide();
            bootcards.listTitleEl.removeClass("active");
        }

        if (bootcards.cardsEl) {
            bootcards.cardsEl
                .removeClass('col-xs-12')
                .addClass( bootcards.cardsColClass );
        }

        $('.bootcards-az-picker').show();

    }

};

//get the list element and it's classes
bootcards._initListEl = function() {

    if (bootcards.listEl != null) {
        return bootcards.listEl;
    }

    bootcards.listEl = $('.bootcards-list');
    bootcards.listColClass = '';

    if ( bootcards.listEl.length > 0 ) {

        bootcards.listTitle = bootcards.listEl.data('title') || 'List';

        $.each(bootcards.listEl.prop('class').split(' '), function(idx, value) {
            if (value.indexOf('col')===0) {
                bootcards.listColClass += value + ' ';
            }
        });

    }

};

bootcards._initCardsEl = function() {

    if (bootcards.cardsEl != null) {
        return bootcards.cardsEl;
    }

    bootcards.cardsEl = $('.bootcards-cards');
    bootcards.cardsColClass = '';

    if ( bootcards.cardsEl.length > 0 ) {

        $.each(bootcards.cardsEl.prop('class').split(' '), function(idx, value) {
            if (value.indexOf('col')===0) {
                bootcards.cardsColClass += value + ' ';
            }
        });
    }

};

//initialize the AZ picker
bootcards.initAZPicker = function( target ) {

    var azPicker = $(target);

    if (azPicker.length > 0) {

        // Register the letter click events
        $("a", azPicker).off().on('click', function(event) {

            var $this = $(this);

            event.stopPropagation();
            bootcards._jumpToLetter($this, event);
            return false;

        });

        //move the az picker to a different location so we can give it a fixed position
        var $list = azPicker.parents(".bootcards-list");

        if ( $list.length > 0) {

            //determine the width of the list column
            var classList = $list.attr('class').split(/\s+/);
            var colClass = "";
            $.each( classList, function(index, entry) {
                if (entry.indexOf('col-') ===0 ) {
                    colClass = entry;
                    return;
                }

            });

            //translate the column name to one of the Bootstrap 'push' classes
            var colSize = colClass.substring( colClass.lastIndexOf('-') + 1 );
            var colPushClass = colClass.substring( 0, colClass.lastIndexOf('-')) + "-push-" + colSize;

            //move the picker as a direct child of the main bootcards container so we can give it fixed positioning
            azPicker
                .appendTo( $('.bootcards-container') )
                .addClass(colPushClass);

        }

    }

};

//jump to a specific letter in the list
bootcards._jumpToLetter = function(letterelement, event) {

    var $list = $('#list');

    $list.animate( {
        scrollTop : 0
    }, 0);

    var letter = letterelement.text().toLowerCase();
    var sel = "#list .list-group a";
    if ($(".bootcards-list-subheading").length > 0){
        sel = ".bootcards-list-subheading";
    }

    var $sel = $(sel);

    var scrolled = false;
    $sel.each( function(idx, entry) {
        var $entry = $(entry);

        var summary = "";
        if ($entry.prop('tagName').toLowerCase() == "a"){
            summary = $entry.find("h4").text();
        }else{
            summary = $entry.text();
        }

        var firstletter = summary.substring(0, 1).toLowerCase();
        var scrollTop = null;

        if (firstletter == letter) {
            scrollTop = $entry.offset().top - 60;
        } else if (firstletter > letter) {
            scrollTop = $entry.offset().top - 120;
        }

        if (scrollTop !== null) {
            $list.animate( {
                scrollTop : scrollTop
            }, 0);
            scrolled = true;
            return false;
        }
    });

    if (!scrolled) {

        var $last = $( $sel[$sel.length-1] );
        $list.animate( {
            scrollTop : $last.offset().top - 120
        }, 0);
    }

};
<commit_msg>hide menu title on rotation<commit_after>var bootcards = bootcards || {

    offCanvasToggleEl : null,
    offCanvasMenuEl : null,
    mainContentEl : null,
    _isXS : null,
    _isIOS : false,
    _isFullScreen : false,
    _simplePortraitMode : false

};

bootcards.init = function( options ) {

    $(document).ready( function() {

        //detect full screen mode on iOS
        if (
            ("standalone" in window.navigator) &&
            window.navigator.standalone
            ) {

                bootcards._isIOS = true;
                bootcards._isFullScreen = true;

                $("body").addClass("ios fullscreen");

        }

        //initialize the off canvas menu
        bootcards._initOffCanvasMenu(
          $('.offcanvas'),
          $('.bootcards-container'),
          options.offCanvasHideOnMainClick
        );

        if (options.enableTabletPortraitMode) {

           if (options.simplePortraitMode) {
                bootcards._simplePortraitMode = options.simplePortraitMode;
           }

           bootcards._initTabletPortraitMode();

        }

        if (options.disableRubberBanding) {

            bootcards.disableRubberBanding();
        }

    } );

};

/*
 * Get the Bootstrap enviroment we're in.
 * Found at http://stackoverflow.com/questions/14441456/how-to-detect-which-device-view-youre-on-using-twitter-bootstrap-api/15150381#15150381
 * Function writen by Raphael_ (http://stackoverflow.com/users/1661358/raphael)
 */
bootcards.findBootstrapEnvironment = function() {
    var envs = ["ExtraSmall", "Small", "Medium", "Large"];
    var envValues = ["xs", "sm", "md", "lg"];

    $el = $('
');
    $el.appendTo($('body'));

    for (var i = envValues.length - 1; i >= 0; i--) {
        var envVal = envValues[i];

        $el.addClass('hidden-'+envVal);
        if ($el.is(':hidden')) {
            $el.remove();
            return envs[i];
        }
    }
};

bootcards.isXS = function() {
    if (this._isXS === null ) {
        this._isXS = (this.findBootstrapEnvironment() == "ExtraSmall");
    }
    return this._isXS;
};

/*
 * Disable rubberbanding effect in iOS.
 * Based on the 'Baking Soda Paste' technique written by Armagan Amcalar at
 * http://blog.armaganamcalar.com/post/70847348271/baking-soda-paste
 */
bootcards.disableRubberBanding = function() {
   document.body.addEventListener('touchstart', function() {
        document.body.addEventListener('touchmove', function moveListener(e) {
            document.body.removeEventListener('touchmove', moveListener);

            var el = e.target;

            do {

                var h = parseInt(window.getComputedStyle(el, null).height, 10);
                var sH = el.scrollHeight;

                if (h < sH) {
                    return;
                }
            } while (el != document.body && el.parentElement != document.body && (el = el.parentElement));

            e.preventDefault();
        });
    });

};

/*
 * Initialize an off-canvas menu. This takes 3 arguments:
 * - the off canvas element to slide in
 * - the main content area to push away when the off canvas element slides in
 * - a boolean indicating if the off canvas menu should be hidden when the main content area is clicked
 *
 * An off canvas menu is required for the portrait-single-pane mode
 *
 */
bootcards._initOffCanvasMenu = function(offCanvasMenuEl, mainContentEl, hideOnMainClick) {

    var $toggleEl = $('[data-toggle=offcanvas]');

    if ($toggleEl.length===0 ) {
        return;
    }

    this.offCanvasToggleEl = $toggleEl;
    this.offCanvasMenuEl = offCanvasMenuEl;
    this.mainContentEl = mainContentEl;

    this.offCanvasToggleEl
        .on("click", function() {
          bootcards.offCanvasMenuEl.toggleClass("active");
          if (bootcards.mainContentEl) { bootcards.mainContentEl.toggleClass("active-left"); }
        });

    //hide the offcanvas if you click on the body
    if (hideOnMainClick && bootcards.mainContentEl) {
        this.mainContentEl.on("click", function() {
            bootcards.offCanvasMenuEl.removeClass('active');
            var $this = $(this);
            if ($this.hasClass('active-left') ) {
                $this.removeClass('active-left');
            }

        });
    }

};

//hide the offcanvas menu
bootcards.hideOffCanvasMenu = function() {

    if (this.offCanvasMenuEl) { this.offCanvasMenuEl.removeClass('active'); }
    if (this.mainContentEl) { this.mainContentEl.removeClass('active'); }

};

bootcards._initTabletPortraitMode = function() {

    //don't activate on desktop or smartphones
    if ( typeof window.orientation == 'undefined' || bootcards.isXS() ) {
        return;
    }

    $(window)
        .on( 'resize', function() {
            setTimeout( function() {
                bootcards._setOrientation(false);
            } , 150);
            event.stopPropagation();
        } )
        .on( 'load', bootcards._setOrientation(true) );

};

bootcards._setOrientation = function(init) {

    var isPortrait = ($(window).width() > $(window).height())? false : true;

    if (!init) {
        if (bootcards.offCanvasMenuEl) { bootcards.offCanvasMenuEl.removeClass("active"); }
        if (bootcards.offCanvasMenuTitleEl) { bootcards.offCanvasMenuTitleEl.removeClass("active"); }
    }

    bootcards._initListEl();
    bootcards._initCardsEl();

    if (isPortrait) {

        //get the columns classes for the list and details
        if (bootcards.listEl.length === 0) {
            return;
        }

        //immediately hide the list on load in portrait mode
        if (init) {
            bootcards.listEl.hide();
        }

        //set the column to full width
        bootcards.cardsEl
            .removeClass(bootcards.cardsColClass)
            .addClass('col-xs-12');

        //hide the az picker
        $('.bootcards-az-picker').hide();

        //in simple mode, we only hide the list column and show the content in full width
        if ( bootcards._simplePortraitMode ) {
            bootcards.listEl
                .hide()
                .removeClass(bootcards.listColClass);
            return; //we're done
        }

        if (!bootcards.listOffcanvasToggle) {
            //create the list title & toggle elements

             //create the button that shows/hides the list
            bootcards.listOffcanvasToggle = $('<button type="button" class="btn btn-default pull-left offcanvaslist-toggle">' +
                '' + bootcards.listTitle + '' +
                '</button>')
                    .on("click", function() {

                        //on click: show the list & title
                        bootcards.listEl.toggleClass("active");
                        bootcards.listTitleEl.toggleClass("active");

                    });

            //create the title element of the list offcanvas
            bootcards.listTitleEl = $("
"  +
               "" + bootcards.listTitle + "
");

            if (bootcards.offCanvasToggleEl) {
                //if we have an offcanvas: add the toggle button to the list

                //create the title element for the menu offcanvas, insert it before the menu offcanvas
                bootcards.offCanvasMenuTitleEl = $("
"  +
                   "Menu
");
                bootcards.offCanvasMenuEl.before( bootcards.offCanvasMenuTitleEl );

                //clone the button to show/hide the menu in the list title
                bootcards.offCanvasToggleEl.clone(false)
                    .prependTo(bootcards.listTitleEl)
                    .on("click", function() {
                        bootcards.offCanvasMenuEl.toggleClass("active");
                        bootcards.offCanvasMenuTitleEl.toggleClass("active");
                    })
                    .children("i")
                        .removeClass('fa-bars')
                        .addClass('fa-angle-left');
            }

            //add the title element and the toggle button to the top navbar
           $('.navbar-header')
               .after(
                    bootcards.listTitleEl, bootcards.listOffcanvasToggle);

            //hide the list & list title when to body is clicked
            bootcards.mainContentEl.on("click", function() {

                bootcards.listEl.removeClass('active');
                bootcards.listTitleEl.removeClass('active');
                bootcards.offCanvasMenuTitleEl.removeClass('active');

            });

            bootcards.listEl.on("click", function() {

                var $this = $(this);
                $this.removeClass('active');
                bootcards.listTitleEl.removeClass('active');

                //stop propagation when an element in the list offcanvas is clicked
                event.stopPropagation();

            });

            //increase the width of the menu: set it to the same size as the list
            if ( bootcards.offCanvasMenuEl ) {
                bootcards.offCanvasMenuEl
                    .addClass('offcanvas-list')
                    .on("click", function() {

                        //hide the menu on click
                        var $this = $(this);
                        $this.removeClass('active');
                        bootcards.offCanvasMenuTitleEl.removeClass('active');
                        bootcards.listEl.removeClass('active');
                        bootcards.listTitleEl.removeClass('active');

                        //stop propagation when an element in the list offcanvas is clicked
                        event.stopPropagation();

                    });
            }

        }

        //hide the menu button
        if (bootcards.offCanvasToggleEl) {
            bootcards.offCanvasToggleEl.hide();
        }

        //show the button to toggle the list
        bootcards.listOffcanvasToggle.show();

        bootcards.listEl
            .removeClass(bootcards.listColClass)
            .addClass('offcanvas-list')
            .show();

    } else {

        //show the menu button
        if (bootcards.offCanvasToggleEl) {
            bootcards.offCanvasToggleEl.show();
        }

        //show the list again
        if (bootcards.listEl) {
            bootcards.listEl
                .removeClass('offcanvas-list active')
                .addClass(bootcards.listColClass)
                .show();
        }

        //hide the button to show the list, remove the list & title
        if ( bootcards.listOffcanvasToggle ) {
            bootcards.listOffcanvasToggle.hide();
            bootcards.listTitleEl.removeClass("active");
        }

        if (bootcards.cardsEl) {
            bootcards.cardsEl
                .removeClass('col-xs-12')
                .addClass( bootcards.cardsColClass );
        }

        $('.bootcards-az-picker').show();

    }

};

//get the list element and it's classes
bootcards._initListEl = function() {

    if (bootcards.listEl != null) {
        return bootcards.listEl;
    }

    bootcards.listEl = $('.bootcards-list');
    bootcards.listColClass = '';

    if ( bootcards.listEl.length > 0 ) {

        bootcards.listTitle = bootcards.listEl.data('title') || 'List';

        $.each(bootcards.listEl.prop('class').split(' '), function(idx, value) {
            if (value.indexOf('col')===0) {
                bootcards.listColClass += value + ' ';
            }
        });

    }

};

bootcards._initCardsEl = function() {

    if (bootcards.cardsEl != null) {
        return bootcards.cardsEl;
    }

    bootcards.cardsEl = $('.bootcards-cards');
    bootcards.cardsColClass = '';

    if ( bootcards.cardsEl.length > 0 ) {

        $.each(bootcards.cardsEl.prop('class').split(' '), function(idx, value) {
            if (value.indexOf('col')===0) {
                bootcards.cardsColClass += value + ' ';
            }
        });
    }

};

//initialize the AZ picker
bootcards.initAZPicker = function( target ) {

    var azPicker = $(target);

    if (azPicker.length > 0) {

        // Register the letter click events
        $("a", azPicker).off().on('click', function(event) {

            var $this = $(this);

            event.stopPropagation();
            bootcards._jumpToLetter($this, event);
            return false;

        });

        //move the az picker to a different location so we can give it a fixed position
        var $list = azPicker.parents(".bootcards-list");

        if ( $list.length > 0) {

            //determine the width of the list column
            var classList = $list.attr('class').split(/\s+/);
            var colClass = "";
            $.each( classList, function(index, entry) {
                if (entry.indexOf('col-') ===0 ) {
                    colClass = entry;
                    return;
                }

            });

            //translate the column name to one of the Bootstrap 'push' classes
            var colSize = colClass.substring( colClass.lastIndexOf('-') + 1 );
            var colPushClass = colClass.substring( 0, colClass.lastIndexOf('-')) + "-push-" + colSize;

            //move the picker as a direct child of the main bootcards container so we can give it fixed positioning
            azPicker
                .appendTo( $('.bootcards-container') )
                .addClass(colPushClass);

        }

    }

};

//jump to a specific letter in the list
bootcards._jumpToLetter = function(letterelement, event) {

    var $list = $('#list');

    $list.animate( {
        scrollTop : 0
    }, 0);

    var letter = letterelement.text().toLowerCase();
    var sel = "#list .list-group a";
    if ($(".bootcards-list-subheading").length > 0){
        sel = ".bootcards-list-subheading";
    }

    var $sel = $(sel);

    var scrolled = false;
    $sel.each( function(idx, entry) {
        var $entry = $(entry);

        var summary = "";
        if ($entry.prop('tagName').toLowerCase() == "a"){
            summary = $entry.find("h4").text();
        }else{
            summary = $entry.text();
        }

        var firstletter = summary.substring(0, 1).toLowerCase();
        var scrollTop = null;

        if (firstletter == letter) {
            scrollTop = $entry.offset().top - 60;
        } else if (firstletter > letter) {
            scrollTop = $entry.offset().top - 120;
        }

        if (scrollTop !== null) {
            $list.animate( {
                scrollTop : scrollTop
            }, 0);
            scrolled = true;
            return false;
        }
    });

    if (!scrolled) {

        var $last = $( $sel[$sel.length-1] );
        $list.animate( {
            scrollTop : $last.offset().top - 120
        }, 0);
    }

};
<|endoftext|>


<commit_before>/**
 * Collate "materials" - html and md files
 * @description Gets contents of files, parses, and creates JSON
 */

'use strict';

var beautifyHtml = require('js-beautify').html;
var changeCase = require('change-case');
var cheerio = require('cheerio');
var fs = require('fs');
var gutil = require('gulp-util');
var Handlebars = require('handlebars');
var junk = require('junk');
var markdown = require('marked');
var mkpath = require('mkpath');
var path = require('path');

/**
 * Compiled component/structure/etc data
 * @type {Object}
 */
var data;

// configure marked
markdown.setOptions({
	langPrefix: 'language-'
});

// configure beautifier
var beautifyOptions = {
	'indent_size': 1,
	'indent_char': '    ',
	'indent_with_tabs': true
};

/**
 * Register each component and structure as a helper in Handlebars
 * This turns each item into a helper so that we can
 * include them in other files.
 */
var registerHelper = function (item) {

	Handlebars.registerHelper(item.id, function () {

		// get helper classes if passed in
		var helperClasses = (typeof arguments[0] === 'string') ? arguments[0] : '';

		// init cheerio
		var $ = cheerio.load(item.content);

		// add helper classes to first element
		$('*').first().addClass(helperClasses);

		return new Handlebars.SafeString($.html());

	});

};

/**
 * Block iteration
 * @description Repeat a block a given amount of times.
 * @example
 * {{#iterate 20}}
 *
List Item

 * {{/iterate}}
 */
Handlebars.registerHelper('iterate', function (n, block) {
	var accum = '';
	for (var i = 0; i < n; ++i) {
		accum += block.fn(i);
	}
	return accum;
});

/**
 * Parse a directory of files
 * @param {Sting} dir The directory that contains .html and .md files to be parsed
 * @return {Function} A stream
 */
var parse = function (dir) {

	// create key if it doesn't exist
	if (!data[dir]) {
		data[dir] = {};
	}

	// get directory contents
	var raw = fs.readdirSync('src/toolkit/' + dir).filter(junk.not);

	// create an array of file names
	var fileNames = raw.map(function (e, i) {
		return e.replace(path.extname(e), '');
	});

	// de-dupe file names (both .html and .md present)
	var items = fileNames.filter(function (e, i, a) {
		return a.indexOf(e) === i;
	});

	// iterate over each item, parse, add to item object
	for (var i = 0, length = items.length; i < length; i++) {

		var item = {};

		item.id = items[i];
		item.name = changeCase.titleCase(item.id.replace(/-/ig, ' '));

		try {
			// compile templates
			var content = fs.readFileSync('src/toolkit/' + dir + '/' + items[i] + '.html', 'utf8');
			var template = Handlebars.compile(content);
			item.content = beautifyHtml(template(), beautifyOptions);
			// register the helper
			registerHelper(item);
		} catch (e) {}

		try {
			var notes = fs.readFileSync('src/toolkit/' + dir + '/' + items[i] + '.md', 'utf8');
			item.notes = markdown(notes);
		} catch (e) {}

		data[dir][item.id.replace(/-/g, '')] = item;
	}

};

module.exports = function (opts, cb) {

	data = {};

	// iterate over each "materials" directory
	for (var i = 0, length = opts.materials.length; i < length; i++) {
		parse(opts.materials[i]);
	}

	// write the json file
	mkpath.sync(path.dirname(opts.dest));

	fs.writeFile(opts.dest, JSON.stringify(data), function (err) {
		if (err) {
			gutil.log(err);
		} else {
			cb();
		}
	});

};
<commit_msg>remove trailing empty lines<commit_after>/**
 * Collate "materials" - html and md files
 * @description Gets contents of files, parses, and creates JSON
 */

'use strict';

var beautifyHtml = require('js-beautify').html;
var changeCase = require('change-case');
var cheerio = require('cheerio');
var fs = require('fs');
var gutil = require('gulp-util');
var Handlebars = require('handlebars');
var junk = require('junk');
var markdown = require('marked');
var mkpath = require('mkpath');
var path = require('path');

/**
 * Compiled component/structure/etc data
 * @type {Object}
 */
var data;

// configure marked
markdown.setOptions({
	langPrefix: 'language-'
});

// configure beautifier
var beautifyOptions = {
	'indent_size': 1,
	'indent_char': '    ',
	'indent_with_tabs': true
};

/**
 * Register each component and structure as a helper in Handlebars
 * This turns each item into a helper so that we can
 * include them in other files.
 */
var registerHelper = function (item) {

	Handlebars.registerHelper(item.id, function () {

		// get helper classes if passed in
		var helperClasses = (typeof arguments[0] === 'string') ? arguments[0] : '';

		// init cheerio
		var $ = cheerio.load(item.content);

		// add helper classes to first element
		$('*').first().addClass(helperClasses);

		return new Handlebars.SafeString($.html());

	});

};

/**
 * Block iteration
 * @description Repeat a block a given amount of times.
 * @example
 * {{#iterate 20}}
 *
List Item

 * {{/iterate}}
 */
Handlebars.registerHelper('iterate', function (n, block) {
	var accum = '';
	for (var i = 0; i < n; ++i) {
		accum += block.fn(i);
	}
	return accum;
});

/**
 * Parse a directory of files
 * @param {Sting} dir The directory that contains .html and .md files to be parsed
 * @return {Function} A stream
 */
var parse = function (dir) {

	// create key if it doesn't exist
	if (!data[dir]) {
		data[dir] = {};
	}

	// get directory contents
	var raw = fs.readdirSync('src/toolkit/' + dir).filter(junk.not);

	// create an array of file names
	var fileNames = raw.map(function (e, i) {
		return e.replace(path.extname(e), '');
	});

	// de-dupe file names (both .html and .md present)
	var items = fileNames.filter(function (e, i, a) {
		return a.indexOf(e) === i;
	});

	// iterate over each item, parse, add to item object
	for (var i = 0, length = items.length; i < length; i++) {

		var item = {};

		item.id = items[i];
		item.name = changeCase.titleCase(item.id.replace(/-/ig, ' '));

		try {
			// compile templates
			var content = fs.readFileSync('src/toolkit/' + dir + '/' + items[i] + '.html', 'utf8').replace(/(\s*(\r?\n|\r))+$/, '');
			var template = Handlebars.compile(content);
			item.content = beautifyHtml(template(), beautifyOptions);
			// register the helper
			registerHelper(item);
		} catch (e) {}

		try {
			var notes = fs.readFileSync('src/toolkit/' + dir + '/' + items[i] + '.md', 'utf8');
			item.notes = markdown(notes);
		} catch (e) {}

		data[dir][item.id.replace(/-/g, '')] = item;
	}

};

module.exports = function (opts, cb) {

	data = {};

	// iterate over each "materials" directory
	for (var i = 0, length = opts.materials.length; i < length; i++) {
		parse(opts.materials[i]);
	}

	// write the json file
	mkpath.sync(path.dirname(opts.dest));

	fs.writeFile(opts.dest, JSON.stringify(data), function (err) {
		if (err) {
			gutil.log(err);
		} else {
			cb();
		}
	});

};
<|endoftext|>


<commit_before>import {
  isExisty,
} from '../lib/helpers';
import gravity from '../lib/loaders/gravity';
import cached from './fields/cached';
import date from './fields/date';
import markdown from './fields/markdown';
import Artist from './artist';
import Partner from './partner';
import Fair from './fair';
import Artwork from './artwork';
import Location from './location';
import Image from './image';
import PartnerShowEventType from './partner_show_event';
import {
  GraphQLObjectType,
  GraphQLString,
  GraphQLNonNull,
  GraphQLList,
  GraphQLInt,
  GraphQLBoolean,
} from 'graphql';

const PartnerShowType = new GraphQLObjectType({
  name: 'PartnerShow',
  fields: () => ({
    cached,
    _id: {
      type: GraphQLString,
    },
    id: {
      type: GraphQLString,
    },
    href: {
      type: GraphQLString,
      resolve: ({ id }) => `/show/${id}`,
    },
    kind: {
      type: GraphQLString,
      resolve: ({ artists, fair }) => {
        if (isExisty(fair)) return 'fair';
        if (artists.length > 1) return 'group';
        if (artists.length === 1) return 'solo';
      },
    },
    name: {
      type: GraphQLString,
      description: 'The exhibition title',
    },
    description: {
      type: GraphQLString,
    },
    displayable: {
      type: GraphQLBoolean,
    },
    press_release: markdown(),
    start_at: date,
    end_at: date,
    artists: {
      type: new GraphQLList(Artist.type),
      resolve: ({ artists }) => artists,
    },
    partner: {
      type: Partner.type,
      resolve: ({ partner }) => partner,
    },
    fair: {
      type: Fair.type,
      resolve: ({ fair }) => fair,
    },
    location: {
      type: Location.type,
      resolve: ({ location, fair_location }) => location || fair_location,
    },
    status: {
      type: GraphQLString,
    },
    events: {
      type: new GraphQLList(PartnerShowEventType),
      resolve: ({ events }) => events,
    },
    artworks: {
      type: new GraphQLList(Artwork.type),
      args: {
        size: {
          type: GraphQLInt,
          description: 'Number of artworks to return',
        },
        published: {
          type: GraphQLBoolean,
          defaultValue: true,
        },
        page: {
          type: GraphQLInt,
        },
      },
      resolve: (show, options) => {
        return gravity(`partner/${show.partner.id}/show/${show.id}/artworks`, options);
      },
    },
    cover_image: {
      type: Image.type,
      resolve: ({ id }, options) => {
        return gravity(`partner_show/${id}/default_image`, options)
          .then(Image.resolve)
          // Shows without a default_image reject with a 404
          .catch(() => null);
      },
    },
    images: {
      type: new GraphQLList(Image.type),
      args: {
        size: {
          type: GraphQLInt,
          description: 'Number of images to return',
        },
        default: {
          type: GraphQLBoolean,
          description: 'Pass true/false to include cover or not',
        },
        page: {
          type: GraphQLInt,
        },
      },
      resolve: ({ id }, options) => {
        return gravity(`partner_show/${id}/images`, options)
          .then(Image.resolve);
      },
    },
  }),
});

const PartnerShow = {
  type: PartnerShowType,
  description: 'A Partner Show',
  args: {
    id: {
      type: new GraphQLNonNull(GraphQLString),
      description: 'The slug or ID of the PartnerShow',
    },
  },
  resolve: (root, { id }) => {
    return gravity(`show/${id}`)
      .then(show => {
        if (!show.displayable) return new Error('Show Not Found');
        return show;
      });
  },
};

export default PartnerShow;
<commit_msg>cover image for partner show without subsequent fetch?<commit_after>import {
  isExisty,
} from '../lib/helpers';
import gravity from '../lib/loaders/gravity';
import cached from './fields/cached';
import date from './fields/date';
import markdown from './fields/markdown';
import Artist from './artist';
import Partner from './partner';
import Fair from './fair';
import Artwork from './artwork';
import Location from './location';
import Image from './image';
import PartnerShowEventType from './partner_show_event';
import {
  GraphQLObjectType,
  GraphQLString,
  GraphQLNonNull,
  GraphQLList,
  GraphQLInt,
  GraphQLBoolean,
} from 'graphql';

const PartnerShowType = new GraphQLObjectType({
  name: 'PartnerShow',
  fields: () => ({
    cached,
    _id: {
      type: GraphQLString,
    },
    id: {
      type: GraphQLString,
    },
    href: {
      type: GraphQLString,
      resolve: ({ id }) => `/show/${id}`,
    },
    kind: {
      type: GraphQLString,
      resolve: ({ artists, fair }) => {
        if (isExisty(fair)) return 'fair';
        if (artists.length > 1) return 'group';
        if (artists.length === 1) return 'solo';
      },
    },
    name: {
      type: GraphQLString,
      description: 'The exhibition title',
    },
    description: {
      type: GraphQLString,
    },
    displayable: {
      type: GraphQLBoolean,
    },
    press_release: markdown(),
    start_at: date,
    end_at: date,
    artists: {
      type: new GraphQLList(Artist.type),
      resolve: ({ artists }) => artists,
    },
    partner: {
      type: Partner.type,
      resolve: ({ partner }) => partner,
    },
    fair: {
      type: Fair.type,
      resolve: ({ fair }) => fair,
    },
    location: {
      type: Location.type,
      resolve: ({ location, fair_location }) => location || fair_location,
    },
    status: {
      type: GraphQLString,
    },
    events: {
      type: new GraphQLList(PartnerShowEventType),
      resolve: ({ events }) => events,
    },
    artworks: {
      type: new GraphQLList(Artwork.type),
      args: {
        size: {
          type: GraphQLInt,
          description: 'Number of artworks to return',
        },
        published: {
          type: GraphQLBoolean,
          defaultValue: true,
        },
        page: {
          type: GraphQLInt,
        },
      },
      resolve: (show, options) => {
        return gravity(`partner/${show.partner.id}/show/${show.id}/artworks`, options);
      },
    },
    cover_image: {
      type: Image.type,
      resolve: ({ image_versions, image_url }) => Image.resolve({ image_versions, image_url }),
    },
    images: {
      type: new GraphQLList(Image.type),
      args: {
        size: {
          type: GraphQLInt,
          description: 'Number of images to return',
        },
        default: {
          type: GraphQLBoolean,
          description: 'Pass true/false to include cover or not',
        },
        page: {
          type: GraphQLInt,
        },
      },
      resolve: ({ id }, options) => {
        return gravity(`partner_show/${id}/images`, options)
          .then(Image.resolve);
      },
    },
  }),
});

const PartnerShow = {
  type: PartnerShowType,
  description: 'A Partner Show',
  args: {
    id: {
      type: new GraphQLNonNull(GraphQLString),
      description: 'The slug or ID of the PartnerShow',
    },
  },
  resolve: (root, { id }) => {
    return gravity(`show/${id}`)
      .then(show => {
        if (!show.displayable) return new Error('Show Not Found');
        return show;
      });
  },
};

export default PartnerShow;
<|endoftext|>


<commit_before>'use strict';
module.exports = function (grunt) {

    require('grunt-treeshake/tasks/treeshake')(grunt);
    require('grunt-angular-templates/tasks/angular-templates')(grunt);
    require('grunt-contrib-less/tasks/less')(grunt);
    require('grunt-contrib-copy/tasks/copy')(grunt);
    require('grunt-string-replace/tasks/string-replace')(grunt);
    require('grunt-contrib-clean/tasks/clean')(grunt);

    var extend = function (target, source) {
        var args = Array.prototype.slice.apply(arguments), i = 1, len = args.length, item, j;
        var options = this || {};
        while (i < len) {
            item = args[i];
            for (j in item) {
                if (item.hasOwnProperty(j)) {
                    //grunt.log.writeln(j, target[j], Object.prototype.toString.call(target[j]))
                    if (target[j] && typeof target[j] === 'object' && !item[j] instanceof Array) {
                        target[j] = extend.apply(options, [target[j], item[j]]);
                    } else if (item[j] instanceof Array) {
                        target[j] = target[j] || (options && options.arrayAsObject ? {length: item[j].length} : []);
                        if (item[j].length) {
                            target[j] = extend.apply(options, [target[j], item[j]]);
                        }
                    } else if (item[j] && typeof item[j] === 'object') {
                        if (Object.prototype.toString.call(item[j]) === '[object RegExp]') {
                            target[j] = item[j];
                        } else if (options.objectsAsArray && typeof item[j].length === "number") {
                            if (!(target[j] instanceof Array)) {
                                target[j] = [];
                            }
                            target[j] = extend.apply(options, [target[j] || {}, item[j]]);
                        } else {
                            target[j] = extend.apply(options, [target[j] || {}, item[j]]);
                        }
                    } else {
                        target[j] = item[j];
                    }
                }
            }
            i += 1;
        }
        return target;
    };

    function wrapOptions(target, options) {
        var opts = {};
        opts[target] = options;
        return opts;
    }

    grunt.registerMultiTask('compile', 'Optimize files added', function () {
        var target = this.target,
            data = this.data || {options: {}},
            cache = {};

        var scripts = "scripts";

        var wrap = target;
        var filename = target;
        if (data.options[scripts].wrap) {
            wrap = data.options[scripts].wrap;
            filename = wrap;
        } else if (data.treeshake && data.treeshake.options) {
            if(data.treeshake.options.wrap) {
                wrap = data.treeshake.options.wrap;
                filename = wrap;
            }
        }

        var defaults = {
            less: {
                options: {
                    paths: ["**/*.less"],
                    strictImports: true,
                    syncImport: true
                },
                files: {
                    'build/css/widgets.css': [
                        "src/**/*.less"
                    ]
                }
            },
            "ngtemplates": {
                src: [
                    '**/*.html'
                ],
                cwd: 'src',
                dest: '.tmp_compile/app-templates.js',
                options: {
                    module: wrap,
                    htmlmin: {
                        collapseBooleanAttributes: true,
                        collapseWhitespace: true,
                        removeAttributeQuotes: true,
                        removeComments: true, // Only if you don't use comment directives!
                        removeEmptyAttributes: true,
                        removeRedundantAttributes: true,
                        removeScriptTypeAttributes: true,
                        removeStyleLinkTypeAttributes: true
                    }
                }
            },
            "string-replace": {
                files: {
                    '.tmp_compile/app-templates-replaced.js': '.tmp_compile/app-templates.js'
                },
                options: {
                    replacements: [
                        {
                            pattern: /angular\.module\(.*?\{/i,
                            replacement: "internal('templates', ['" + wrap + "'], function(app) {"
                        },
                        {
                            pattern: /'use strict';/i,
                            replacement: ''
                        },
                        {
                            pattern: /\$templateCache\.put/gi,
                            replacement: 'app.template'
                        },
                        {
                            pattern: /templates\//gi,
                            replacement: ''
                        },
                        {
                            pattern: /scripts\/directives\//gi,
                            replacement: ''
                        },
                        {
                            pattern: /\.html/gi,
                            replacement: ''
                        },
                        {
                            pattern: /\}\]\);/gim,
                            replacement: '});'
                        }
                    ]
                }
            },
            treeshake: {
                options: {
                    wrap: 'app',
                    minify: true,
                    match: function (searchText) {
                        var camelCase = function (str) {
                            return str.replace(/-([a-z])/g, function (g) {
                                return g[1].toUpperCase();
                            });
                        };

                        var results = searchText.match(new RegExp('(' + wrap + '|app|hb)-[\\w|-]+', 'gim'));
                        //grunt.log.writeln('(' + data.treeshake.options.wrap + '|app|hb)-[\\w|-]+');
                        //console.log('--RESULTS--', results);

                        for (var e in results) {
                            if (results[e].indexOf('hb-') === 0) {
                                results[e] = 'hbd.' + camelCase(results[e].replace('hb-', ''));
                            } else {
                                results[e] = camelCase(results[e].replace('app-', wrap + '-'));
                            }
                        }
                        //console.log('----', results);
                        return results;
                    },
                    ignore: [],
                    inspect: [],
                    export: [],
                    exclude: [],
                    import: [],
                    report: 'verbose',
                    log: 'logs/' + wrap + '.log'
                },
                files: {
                    '.tmp_compile/app.js': [
                        'node_modules/hbjs/src/**/**.js',
                        '.tmp_compile/app-templates-replaced.js'
                    ]
                }
            },
            "string-replace-treeshake": {
                files: {},
                options: {
                    replacements: [
                        {
                            pattern: /(("|')app\2|app\-)/gim,
                            replacement: function (match) {
                                return match.split('app').join(wrap);
                            }
                        }
                    ]
                }
            },
            copy: {
                files: [
                    {expand: true, cwd: 'src', src: ['**/images/**/*'], dest: 'build'},
                ]
            },
            clean: {}
        };

        var compileOptions = extend(defaults, data);

        // setup from options (overrides all)
        if (compileOptions.options) {
            var opts = compileOptions.options;
            if (opts[scripts]) {
                var treeshake = opts[scripts];
                if (treeshake.wrap) {
                    compileOptions.treeshake.options.wrap = wrap;
                }
                if (treeshake.inspect) {
                    compileOptions.treeshake.options.inspect = compileOptions.treeshake.options.inspect.concat(treeshake.inspect);
                }
                if (treeshake.ignore) {
                    compileOptions.treeshake.options.ignore = compileOptions.treeshake.options.ignore.concat(treeshake.ignore);
                }
                if (treeshake.import) {
                    compileOptions.treeshake.options.import = compileOptions.treeshake.options.import.concat(treeshake.import);
                }
                if (treeshake.exclude) {
                    compileOptions.treeshake.options.exclude = compileOptions.treeshake.options.exclude.concat(treeshake.exclude);
                }
                if (treeshake.export) {
                    compileOptions.treeshake.options.export = compileOptions.treeshake.options.export.concat(treeshake.export);
                }
                if (treeshake.src) {
                    compileOptions.treeshake.files['.tmp_compile/app.js'] = compileOptions.treeshake.files['.tmp_compile/app.js'].concat(treeshake.src);
                }
                if(treeshake.filename) {
                    if(data.treeshake.filename) {
                        filename = data.treeshake.filename;
                    }
                }

            }
            if (opts.templates) {
                var templates = opts.templates;
                if (templates.cwd) {
                    compileOptions.ngtemplates.cwd = templates.cwd;
                }
                if (templates.src) {
                    compileOptions.ngtemplates.src = templates.src;
                }
            }
        }

        // set the build file paths.
        var ts = compileOptions['string-replace-treeshake'];
        grunt.log.writeln(compileOptions.options.build + '/' + compileOptions.treeshake.options.wrap + '.js');
        ts.files[compileOptions.options.build + '/' + filename + '.js'] = '.tmp_compile/app.js';
        ts.files[compileOptions.options.build + '/' + filename + '.min.js'] = '.tmp_compile/app.min.js';

        // to look up directives in use for templates.
        compileOptions.treeshake.options.inspect.push('.tmp_compile/app-templates-replaced.js');

        function run(name, options, forceName) {
            var targetName = forceName || target;
            if (!cache[name]) {
                options = wrapOptions(targetName, options);
                cache[name] = options;
            } else {
                cache[name][targetName] = options;
            }
            options = cache[name];
            grunt.config.set(name, options);
            grunt.task.run(name + ':' + targetName);
        }

        if (compileOptions.options.styles && compileOptions.options.styles.src && compileOptions.options.styles.src.length) {
            run('less', compileOptions.less);
        }

        if (compileOptions.options.templates) {
            var templates = compileOptions.options.templates;
            if (templates.src || templates.cwd) {
                run('ngtemplates', compileOptions.ngtemplates);
            }
        }

        run("string-replace", compileOptions["string-replace"]);
        run('treeshake', compileOptions.treeshake);
        run('string-replace', compileOptions['string-replace-treeshake'], 'treeshake');
        run('copy', compileOptions.copy);

        grunt.config.set('clean', {hb_compile: '.tmp_compile'});
        grunt.task.run('clean:hb_compile');
    });
};<commit_msg>fixed filename reference in compile task<commit_after>'use strict';
module.exports = function (grunt) {

    require('grunt-treeshake/tasks/treeshake')(grunt);
    require('grunt-angular-templates/tasks/angular-templates')(grunt);
    require('grunt-contrib-less/tasks/less')(grunt);
    require('grunt-contrib-copy/tasks/copy')(grunt);
    require('grunt-string-replace/tasks/string-replace')(grunt);
    require('grunt-contrib-clean/tasks/clean')(grunt);

    var extend = function (target, source) {
        var args = Array.prototype.slice.apply(arguments), i = 1, len = args.length, item, j;
        var options = this || {};
        while (i < len) {
            item = args[i];
            for (j in item) {
                if (item.hasOwnProperty(j)) {
                    //grunt.log.writeln(j, target[j], Object.prototype.toString.call(target[j]))
                    if (target[j] && typeof target[j] === 'object' && !item[j] instanceof Array) {
                        target[j] = extend.apply(options, [target[j], item[j]]);
                    } else if (item[j] instanceof Array) {
                        target[j] = target[j] || (options && options.arrayAsObject ? {length: item[j].length} : []);
                        if (item[j].length) {
                            target[j] = extend.apply(options, [target[j], item[j]]);
                        }
                    } else if (item[j] && typeof item[j] === 'object') {
                        if (Object.prototype.toString.call(item[j]) === '[object RegExp]') {
                            target[j] = item[j];
                        } else if (options.objectsAsArray && typeof item[j].length === "number") {
                            if (!(target[j] instanceof Array)) {
                                target[j] = [];
                            }
                            target[j] = extend.apply(options, [target[j] || {}, item[j]]);
                        } else {
                            target[j] = extend.apply(options, [target[j] || {}, item[j]]);
                        }
                    } else {
                        target[j] = item[j];
                    }
                }
            }
            i += 1;
        }
        return target;
    };

    function wrapOptions(target, options) {
        var opts = {};
        opts[target] = options;
        return opts;
    }

    grunt.registerMultiTask('compile', 'Optimize files added', function () {
        var target = this.target,
            data = this.data || {options: {}},
            cache = {};

        var scripts = "scripts";

        var wrap = target;
        var filename = target;
        if (data.options[scripts].wrap) {
            wrap = data.options[scripts].wrap;
            filename = wrap;
        } else if (data.treeshake && data.treeshake.options) {
            if (data.treeshake.options.wrap) {
                wrap = data.treeshake.options.wrap;
                filename = wrap;
            }
        }

        if (data.options.filename) {
            filename = data.options.filename;
        }

        var defaults = {
            less: {
                options: {
                    paths: ["**/*.less"],
                    strictImports: true,
                    syncImport: true
                },
                files: {
                    'build/css/widgets.css': [
                        "src/**/*.less"
                    ]
                }
            },
            "ngtemplates": {
                src: [
                    '**/*.html'
                ],
                cwd: 'src',
                dest: '.tmp_compile/app-templates.js',
                options: {
                    module: wrap,
                    htmlmin: {
                        collapseBooleanAttributes: true,
                        collapseWhitespace: true,
                        removeAttributeQuotes: true,
                        removeComments: true, // Only if you don't use comment directives!
                        removeEmptyAttributes: true,
                        removeRedundantAttributes: true,
                        removeScriptTypeAttributes: true,
                        removeStyleLinkTypeAttributes: true
                    }
                }
            },
            "string-replace": {
                files: {
                    '.tmp_compile/app-templates-replaced.js': '.tmp_compile/app-templates.js'
                },
                options: {
                    replacements: [
                        {
                            pattern: /angular\.module\(.*?\{/i,
                            replacement: "internal('templates', ['" + wrap + "'], function(app) {"
                        },
                        {
                            pattern: /'use strict';/i,
                            replacement: ''
                        },
                        {
                            pattern: /\$templateCache\.put/gi,
                            replacement: 'app.template'
                        },
                        {
                            pattern: /templates\//gi,
                            replacement: ''
                        },
                        {
                            pattern: /scripts\/directives\//gi,
                            replacement: ''
                        },
                        {
                            pattern: /\.html/gi,
                            replacement: ''
                        },
                        {
                            pattern: /\}\]\);/gim,
                            replacement: '});'
                        }
                    ]
                }
            },
            treeshake: {
                options: {
                    wrap: 'app',
                    minify: true,
                    match: function (searchText) {
                        var camelCase = function (str) {
                            return str.replace(/-([a-z])/g, function (g) {
                                return g[1].toUpperCase();
                            });
                        };

                        var results = searchText.match(new RegExp('(' + wrap + '|app|hb)-[\\w|-]+', 'gim'));
                        //grunt.log.writeln('(' + data.treeshake.options.wrap + '|app|hb)-[\\w|-]+');
                        //console.log('--RESULTS--', results);

                        for (var e in results) {
                            if (results[e].indexOf('hb-') === 0) {
                                results[e] = 'hbd.' + camelCase(results[e].replace('hb-', ''));
                            } else {
                                results[e] = camelCase(results[e].replace('app-', wrap + '-'));
                            }
                        }
                        //console.log('----', results);
                        return results;
                    },
                    ignore: [],
                    inspect: [],
                    export: [],
                    exclude: [],
                    import: [],
                    report: 'verbose',
                    log: 'logs/' + wrap + '.log'
                },
                files: {
                    '.tmp_compile/app.js': [
                        'node_modules/hbjs/src/**/**.js',
                        '.tmp_compile/app-templates-replaced.js'
                    ]
                }
            },
            "string-replace-treeshake": {
                files: {},
                options: {
                    replacements: [
                        {
                            pattern: /(("|')app\2|app\-)/gim,
                            replacement: function (match) {
                                return match.split('app').join(wrap);
                            }
                        }
                    ]
                }
            },
            copy: {
                files: [
                    {expand: true, cwd: 'src', src: ['**/images/**/*'], dest: 'build'},
                ]
            },
            clean: {}
        };

        var compileOptions = extend(defaults, data);

        // setup from options (overrides all)
        if (compileOptions.options) {
            var opts = compileOptions.options;
            if (opts[scripts]) {
                var treeshake = opts[scripts];
                if (treeshake.wrap) {
                    compileOptions.treeshake.options.wrap = wrap;
                }
                if (treeshake.inspect) {
                    compileOptions.treeshake.options.inspect = compileOptions.treeshake.options.inspect.concat(treeshake.inspect);
                }
                if (treeshake.ignore) {
                    compileOptions.treeshake.options.ignore = compileOptions.treeshake.options.ignore.concat(treeshake.ignore);
                }
                if (treeshake.import) {
                    compileOptions.treeshake.options.import = compileOptions.treeshake.options.import.concat(treeshake.import);
                }
                if (treeshake.exclude) {
                    compileOptions.treeshake.options.exclude = compileOptions.treeshake.options.exclude.concat(treeshake.exclude);
                }
                if (treeshake.export) {
                    compileOptions.treeshake.options.export = compileOptions.treeshake.options.export.concat(treeshake.export);
                }
                if (treeshake.src) {
                    compileOptions.treeshake.files['.tmp_compile/app.js'] = compileOptions.treeshake.files['.tmp_compile/app.js'].concat(treeshake.src);
                }
                if (treeshake.filename) {
                    if (data.treeshake.filename) {
                        filename = data.treeshake.filename;
                    }
                }

            }
            if (opts.templates) {
                var templates = opts.templates;
                if (templates.cwd) {
                    compileOptions.ngtemplates.cwd = templates.cwd;
                }
                if (templates.src) {
                    compileOptions.ngtemplates.src = templates.src;
                }
            }
        }

        // set the build file paths.
        var ts = compileOptions['string-replace-treeshake'];
        grunt.log.writeln(compileOptions.options.build + '/' + compileOptions.treeshake.options.wrap + '.js');
        ts.files[compileOptions.options.build + '/' + filename + '.js'] = '.tmp_compile/app.js';
        ts.files[compileOptions.options.build + '/' + filename + '.min.js'] = '.tmp_compile/app.min.js';

        // to look up directives in use for templates.
        compileOptions.treeshake.options.inspect.push('.tmp_compile/app-templates-replaced.js');

        function run(name, options, forceName) {
            var targetName = forceName || target;
            if (!cache[name]) {
                options = wrapOptions(targetName, options);
                cache[name] = options;
            } else {
                cache[name][targetName] = options;
            }
            options = cache[name];
            grunt.config.set(name, options);
            grunt.task.run(name + ':' + targetName);
        }

        if (compileOptions.options.styles && compileOptions.options.styles.src && compileOptions.options.styles.src.length) {
            run('less', compileOptions.less);
        }

        if (compileOptions.options.templates) {
            var templates = compileOptions.options.templates;
            if (templates.src || templates.cwd) {
                run('ngtemplates', compileOptions.ngtemplates);
            }
        }

        run("string-replace", compileOptions["string-replace"]);
        run('treeshake', compileOptions.treeshake);
        run('string-replace', compileOptions['string-replace-treeshake'], 'treeshake');
        run('copy', compileOptions.copy);

        grunt.config.set('clean', {hb_compile: '.tmp_compile'});
        grunt.task.run('clean:hb_compile');
    });
};<|endoftext|>


<commit_before>var vars = exports;
vars.HOST = 'boiling-brook-7206.herokuapp.com';
vars.PORT = 5000;

// Server static files

vars.staticPath = 'client/';

vars.staticFiles = [
	'style.css',
	'jquery.min.js',
	'utils.js',
	'latlonspan.js',
	'app.js',
	'client.js',
	'map.js',
	'images/crosshair.png',
	'images/sprite.png'
];
<commit_msg>tweak host name<commit_after>var vars = exports;
vars.HOST = 'localhost';
vars.PORT = 5000;

// Server static files

vars.staticPath = 'client/';

vars.staticFiles = [
	'style.css',
	'jquery.min.js',
	'utils.js',
	'latlonspan.js',
	'app.js',
	'client.js',
	'map.js',
	'images/crosshair.png',
	'images/sprite.png'
];
<|endoftext|>


<commit_before>define(["app/app"], function(App) {
  "use strict";

  App.CreateAttachmentView = Ember.View.extend({
    tagName: 'input',
    type: 'file',
    attributeBindings: ['type'],

    change: function(event) {
      if (event.target.files.length > 0) {
        // Send the file object to controller
        var newFile = event.target.files[0]
        this.get('controller').send('addAttachment', newFile)

        // Clear the file input in DOM: 1) wrap with the form, 2) reset the form, 3) unwrap
        this.$().wrap('<form>').closest('form').get(0).reset()
        this.$().unwrap()
      }
    },

    click: function() {
      var view = this.get('parentView.sendTo')
      if (view)
        view.set('isVisible', true)
    }
  })
})
<commit_msg>Attachments: disable 'send-to' field showing up on a click<commit_after>define(["app/app"], function(App) {
  "use strict";

  App.CreateAttachmentView = Ember.View.extend({
    tagName: 'input',
    type: 'file',
    attributeBindings: ['type'],

    change: function(event) {
      if (event.target.files.length > 0) {
        // Send the file object to controller
        var newFile = event.target.files[0]
        this.get('controller').send('addAttachment', newFile)

        // Clear the file input in DOM: 1) wrap with the form, 2) reset the form, 3) unwrap
        this.$().wrap('<form>').closest('form').get(0).reset()
        this.$().unwrap()
      }
    }
  })
})
<|endoftext|>