<commit_before>/*
  Angular Moment.js Moment Picker
*/

// TODO: A concept of 'interval' instead of 'unit', and a way of setting the display bounds? E.g., mid-month month-end picker.

'use strict';

angular.module('moment')

.directive('momentPicker', ['$moment', '$log', 'indexOf', function inputDirective($moment, $log, indexOf) {
  var weekStartDay = $moment().startOf('week').format('d'),
      weekEndDay   = $moment().endOf('week')  .format('d'),
      getTemplateDefinition = function(templateName) {
        return $moment.$$pickerTemplates[templateName || 'default'];
      };

  return {
    restrict: 'A',
    templateUrl: function(tElement, tAttrs) {
      var template = getTemplateDefinition(tAttrs.template);
      if (template)
        return template.url;
      // Ya dun' goofed.
      $log.error('Error: [momentDatepicker] Picker template \''+ tAttrs.template +'\' is undefined. Templates must be defined with \'$momentProvider.definePickerTemplate\'.');
    },
    scope: {
      dateModel:   '=momentPicker',
      format:      '=?',
      modelFormat: '=?',
      min:         '=?',
      max:         '=?',
      ngShow:      '=?'
    },
    controller: ['$scope', '$element', '$attrs', '$parse', '$animate', '$moment', 'indexOf',
      function($scope, $element, $attr, $parse, $animate, $moment, indexOf) {
        var self = this;

        self.template = getTemplateDefinition($attr.template);
        self.format   = $moment.$defaultModelFormat;

        self.pickedMoment = Number.NaN;
        self.maxMoment    = Number.NaN;
        self.minMoment    = Number.NaN;

        self.visible = true;
        self.hidden  = false;

        function init() {
          self.setDisplayMoment($moment());
          rebuildScopeMoments();

          $scope.weekMoments = [];
          var i = 7;
          while (i--)
            $scope.weekMoments.unshift($moment().startOf('week').add(i, 'day'));
        }

        // Ctrl Methods
        ////////////////

        self.setVisibility = function(isVisible) {
          self.hidden  = !isVisible;
          self.visible = !!isVisible;
          $animate[!isVisible ? 'addClass' : 'removeClass']($element, 'ng-hide');
        };

        self.setPickedMoment = function(input, format, lang, strict) {
          var moment  = $moment.apply(null, arguments),
              isValid = input && moment.isValid();

          if (!isValid) {
            self.pickedMoment   = null;
            $scope.pickedMoment = null;
            return;
          }

          if (self.minMoment && moment.isBefore(self.minMoment, self.template.unit))
            return;
          if (self.maxMoment && moment.isAfter(self.maxMoment, self.template.unit))
            return;

          // Clamp it to the min/max to keep it valid
          if (self.minMoment)
            moment = moment.min(self.minMoment);
          if (self.maxMoment)
            moment = moment.max(self.maxMoment);

          if (moment.isSame(self.pickedMoment))
            return;

          self.pickedMoment   = moment.clone();
          $scope.pickedMoment = moment.clone();
          $scope.dateModel    = moment.format(format);

          self.setDisplayMoment(moment);
        };

        self.setDisplayMoment = function(input, format, lang, strict) {
          var moment  = $moment.apply(null, arguments),
              isValid = input && moment.isValid(),
              fallbackMoment = self.pickedMoment ? self.pickedMoment : $moment();

          self.displayMoment   = isValid ? moment.clone() : fallbackMoment.clone();
          $scope.displayMoment = isValid ? moment.clone() : fallbackMoment.clone();

          rebuildScopeMoments();
        };

        self.setMinMoment = function(input, format, lang, strict) {
          var moment  = $moment.apply(null, arguments),
              isValid = input && moment.isValid();

          self.minMoment = isValid ? moment.clone() : null;
        };

        self.setMaxMoment = function(input, format, lang, strict) {
          var moment  = $moment.apply(null, arguments),
              isValid = input && moment.isValid();

          self.maxMoment = isValid ? moment.clone() : null;
        };

        self.setFormat = function(format) {
          if (angular.equals(self.format, format))
            return;

          self.format = format || $moment.$defaultModelFormat;
          self.setPickedMoment($scope.dateModel, self.format, $moment.$strictModel);
        };

        // Watchers
        ///////////

        $scope.$watch('dateModel', function(modelValue) {
          self.setPickedMoment(modelValue, self.format, $moment.$strictModel);
        });

        // Format or modelFormat
        if ($attr.format && !$attr.modelFormat) {
          self.format = $scope.format || $moment.$defaultModelFormat;
          $scope.$watch('format', self.setFormat);
        }
        else if ($attr.modelFormat) {
          self.format = $scope.modelFormat || $moment.$defaultModelFormat;
          $scope.$watch('modelFormat', self.setFormat);
        }

        // Min, Max
        if ($attr.min) {
          $scope.$watch('min', function(minValue) {
            var isArray = angular.isArray(minValue);
            self.setMinMoment.apply(null, isArray ? minValue : [minValue]);
          }, true);
        }

        if ($attr.max) {
          $scope.$watch('max', function(maxValue) {
            var isArray = angular.isArray(maxValue);
            self.setMaxMoment.apply(null, isArray ? maxValue : [maxValue]);
          }, true);
        }

        // Add ctrl methods to the scope that are
        // suitable for use in the picker template.
        ///////////////////////////////////////////

        $scope.setPickerVisibility = self.setVisibility;
        $scope.setDisplayMoment    = self.setDisplayMoment;
        $scope.setPickedMoment     = self.setPickedMoment;

        // Private methods

        function rebuildScopeMoments() {
          // TODO: Check if rebuild is necessary

          $scope.today                 = $moment();
          $scope.lastMonthMoments      = [];
          $scope.thisMonthMoments      = [];
          $scope.nextMonthMoments      = [];
          $scope.monthsThisYearMoments = [];

          var lastMonthMoment = self.displayMoment.clone().startOf('month'),
              thisMonthMoment = lastMonthMoment.clone(),
              nextMonthMoment = self.displayMoment.clone().endOf('month'),
              thisMonth       = self.displayMoment.format('M'),
              thisYear        = self.displayMoment.format('YYYY');

          while (lastMonthMoment.format('d') !== weekStartDay)
            $scope.lastMonthMoments.unshift(lastMonthMoment.subtract(1, 'day').clone());

          while (thisMonthMoment.format('M') === thisMonth) {
            $scope.thisMonthMoments.push(thisMonthMoment.clone());
            thisMonthMoment.add(1, 'day');
          }

          while ($scope.lastMonthMoments.length + $scope.thisMonthMoments.length + $scope.nextMonthMoments.length < 42)
            $scope.nextMonthMoments.push(nextMonthMoment.add(1, 'day').clone());

          while ($scope.monthsThisYearMoments.length < 12)
            $scope.monthsThisYearMoments.push(moment({ year:thisYear, month:$scope.monthsThisYearMoments.length }));

        }

        init();
      }
    ],
    require: 'momentPicker',
    link: function(scope, element, attr, ctrl) {

      // View helpers
      ////////////////

      scope.getClasses = function(moment, classes) {
        var isWeekend   = /6|7/.test(moment.isoWeekday()),
            isWeekday   = !isWeekend,
            classObject = {
              weekend: isWeekend,
              weekday: isWeekday
            };

        // Convenience classes: jan fri
        classObject[ moment.format('MMM ddd').toLowerCase() ] = true;

        if (!classes)
          return classObject;

        // Iterate over requested class names
        angular.forEach(classes.split(' '), function(className) {
          var name = className.split('-')[0],
              unit = className.split('-')[1] || ctrl.template.unit;

          if (scope.pickedMoment && name == 'picked')
            classObject[className] = moment.isSame(scope.pickedMoment, ctrl.template.unit);

          else if (name == 'current')
            classObject[className +' current'] = moment.isSame(scope.today, unit);

          else if (name == 'invalid' && (ctrl.minMoment || ctrl.maxMoment)) {
            if (ctrl.minMoment && moment.isBefore(ctrl.minMoment, unit))
              classObject[className + ' invalid'] = true;
            else if (ctrl.maxMoment && moment.isAfter(ctrl.maxMoment, unit))
              classObject[className + ' invalid'] = true;
          }

        });

        return classObject;
      };

    }
  };

}])

// Picker extends moment input directive with positioned momentPicker

.directive('picker', ['$moment', '$compile', 'getOffset', function inputDirective($moment, $compile, getOffset) {
  var defaultStyleAttr = 'style="position:absolute" class="input-picker"',
      copiedAttrs      = 'format modelFormat min max'.split(' ');

  var toSpinalCase = function(string) {
    return string.replace(/[a-z][A-Z]/g, function(w) { return w[0] +'-'+ w[1]; }).toLowerCase();
  };

  return {
    restrict: 'A',
    require: ['?ngModel'],
    link: function(scope, element, attr, ctrl) {
      if (!ctrl || attr.type !== 'moment')
        return;

      var pickerAttrs = [ defaultStyleAttr ],
          pickerElem, pickerScope, pickerCtrl,
          deregisterWatch;

      function init() {
        deregisterWatch();
        pickerScope = pickerElem.isolateScope();
        pickerCtrl  = pickerElem.controller('momentPicker');
        pickerCtrl.setVisibility(false);
        pickerScope.showClose = true;
      }

      // Copy relevent attrs from input to picker
      if (attr.picker)
        pickerAttrs.push('template='+ attr.picker);

      angular.forEach(copiedAttrs, function(name) {
        if (attr[name])
          pickerAttrs.push(toSpinalCase(name) +'="'+ attr[name] +'"');
      });

      // Compile/inject/bind events to picker
      pickerElem = $compile('

')(scope);

      // Watch for controller instantiation
      deregisterWatch = scope.$watch(function() {
        if (pickerElem.controller('momentPicker'))
          init();
      });

      // DOM manipulation and event watching
      angular.element(document.body).append(pickerElem);

      pickerElem.on('mousedown', function(event) {
        event.preventDefault();
      });

      // Input event binding
      element.on('focus click', function(event) {
        var offset = getOffset(element[0]);

        pickerElem.css({
          left: offset.left + 'px',
          top: offset.bottom + 'px'
        });

        pickerCtrl.setVisibility(true);
      });

      element.on('blur keydown', function(event) {
        if (event.type == 'keydown' && event.which !== 27)
          return;
        pickerCtrl.setVisibility(false);
      });

      // Destruction cleanup
      scope.$on('$destroy', function() {
        pickerElem.off().remove();
      });

    }
  };
}]);

<commit_msg>Don't rebuild scope moments if not necessary<commit_after>/*
  Angular Moment.js Moment Picker
*/

// TODO: A concept of 'interval' instead of 'unit', and a way of setting the display bounds? E.g., mid-month month-end picker.

'use strict';

angular.module('moment')

.directive('momentPicker', ['$moment', '$log', 'indexOf', function inputDirective($moment, $log, indexOf) {
  var weekStartDay = $moment().startOf('week').format('d'),
      weekEndDay   = $moment().endOf('week')  .format('d'),
      getTemplateDefinition = function(templateName) {
        return $moment.$$pickerTemplates[templateName || 'default'];
      };

  return {
    restrict: 'A',
    templateUrl: function(tElement, tAttrs) {
      var template = getTemplateDefinition(tAttrs.template);
      if (template)
        return template.url;
      // Ya dun' goofed.
      $log.error('Error: [momentDatepicker] Picker template \''+ tAttrs.template +'\' is undefined. Templates must be defined with \'$momentProvider.definePickerTemplate\'.');
    },
    scope: {
      dateModel:   '=momentPicker',
      format:      '=?',
      modelFormat: '=?',
      min:         '=?',
      max:         '=?',
      ngShow:      '=?'
    },
    controller: ['$scope', '$element', '$attrs', '$parse', '$animate', '$moment', 'indexOf',
      function($scope, $element, $attr, $parse, $animate, $moment, indexOf) {
        var self = this;

        self.template = getTemplateDefinition($attr.template);
        self.format   = $moment.$defaultModelFormat;

        self.pickedMoment = Number.NaN;
        self.maxMoment    = Number.NaN;
        self.minMoment    = Number.NaN;

        self.visible = true;
        self.hidden  = false;

        function init() {
          $scope.today = $moment();
          $scope.lastMonthMoments = [];
          $scope.thisMonthMoments = [];
          $scope.nextMonthMoments = [];
          $scope.monthsThisYearMoments = [];

          self.setDisplayMoment($moment());
          rebuildScopeMoments();

          $scope.weekMoments = [];
          var i = 7;
          while (i--)
            $scope.weekMoments.unshift($moment().startOf('week').add(i, 'day'));
        }

        // Ctrl Methods
        ////////////////

        self.setVisibility = function(isVisible) {
          self.hidden  = !isVisible;
          self.visible = !!isVisible;
          $animate[!isVisible ? 'addClass' : 'removeClass']($element, 'ng-hide');
        };

        self.setPickedMoment = function(input, format, lang, strict) {
          var moment  = $moment.apply(null, arguments),
              isValid = input && moment.isValid();

          if (!isValid) {
            self.pickedMoment   = null;
            $scope.pickedMoment = null;
            return;
          }

          if (self.minMoment && moment.isBefore(self.minMoment, self.template.unit))
            return;
          if (self.maxMoment && moment.isAfter(self.maxMoment, self.template.unit))
            return;

          // Clamp it to the min/max to keep it valid
          if (self.minMoment)
            moment = moment.min(self.minMoment);
          if (self.maxMoment)
            moment = moment.max(self.maxMoment);

          if (moment.isSame(self.pickedMoment))
            return;

          self.pickedMoment   = moment.clone();
          $scope.pickedMoment = moment.clone();
          $scope.dateModel    = moment.format(format);

          self.setDisplayMoment(moment);
        };

        self.setDisplayMoment = function(input, format, lang, strict) {
          var moment  = $moment.apply(null, arguments),
              isValid = input && moment.isValid(),
              fallbackMoment = self.pickedMoment ? self.pickedMoment : $moment();

          self.displayMoment   = isValid ? moment.clone() : fallbackMoment.clone();
          $scope.displayMoment = isValid ? moment.clone() : fallbackMoment.clone();

          rebuildScopeMoments();
        };

        self.setMinMoment = function(input, format, lang, strict) {
          var moment  = $moment.apply(null, arguments),
              isValid = input && moment.isValid();

          self.minMoment = isValid ? moment.clone() : null;
        };

        self.setMaxMoment = function(input, format, lang, strict) {
          var moment  = $moment.apply(null, arguments),
              isValid = input && moment.isValid();

          self.maxMoment = isValid ? moment.clone() : null;
        };

        self.setFormat = function(format) {
          if (angular.equals(self.format, format))
            return;

          self.format = format || $moment.$defaultModelFormat;
          self.setPickedMoment($scope.dateModel, self.format, $moment.$strictModel);
        };

        // Watchers
        ///////////

        $scope.$watch('dateModel', function(modelValue) {
          self.setPickedMoment(modelValue, self.format, $moment.$strictModel);
        });

        // Format or modelFormat
        if ($attr.format && !$attr.modelFormat) {
          self.format = $scope.format || $moment.$defaultModelFormat;
          $scope.$watch('format', self.setFormat);
        }
        else if ($attr.modelFormat) {
          self.format = $scope.modelFormat || $moment.$defaultModelFormat;
          $scope.$watch('modelFormat', self.setFormat);
        }

        // Min, Max
        if ($attr.min) {
          $scope.$watch('min', function(minValue) {
            var isArray = angular.isArray(minValue);
            self.setMinMoment.apply(null, isArray ? minValue : [minValue]);
          }, true);
        }

        if ($attr.max) {
          $scope.$watch('max', function(maxValue) {
            var isArray = angular.isArray(maxValue);
            self.setMaxMoment.apply(null, isArray ? maxValue : [maxValue]);
          }, true);
        }

        // Add ctrl methods to the scope that are
        // suitable for use in the picker template.
        ///////////////////////////////////////////

        $scope.setPickerVisibility = self.setVisibility;
        $scope.setDisplayMoment    = self.setDisplayMoment;
        $scope.setPickedMoment     = self.setPickedMoment;

        // Private methods

        function rebuildScopeMoments() {
          var lastMonthMoment = self.displayMoment.clone().startOf('month'),
              thisMonthMoment = lastMonthMoment.clone(),
              nextMonthMoment = self.displayMoment.clone().endOf('month'),
              thisMonth       = self.displayMoment.format('M'),
              thisYear        = self.displayMoment.format('YYYY');

          if ($scope.thisMonthMoments.length && thisMonthMoment.isSame($scope.thisMonthMoments[0]))
            return;

          $scope.today                 = $moment();
          $scope.lastMonthMoments      = [];
          $scope.thisMonthMoments      = [];
          $scope.nextMonthMoments      = [];
          $scope.monthsThisYearMoments = [];

          while (lastMonthMoment.format('d') !== weekStartDay)
            $scope.lastMonthMoments.unshift(lastMonthMoment.subtract(1, 'day').clone());

          while (thisMonthMoment.format('M') === thisMonth) {
            $scope.thisMonthMoments.push(thisMonthMoment.clone());
            thisMonthMoment.add(1, 'day');
          }

          while ($scope.lastMonthMoments.length + $scope.thisMonthMoments.length + $scope.nextMonthMoments.length < 42)
            $scope.nextMonthMoments.push(nextMonthMoment.add(1, 'day').clone());

          while ($scope.monthsThisYearMoments.length < 12)
            $scope.monthsThisYearMoments.push(moment({ year:thisYear, month:$scope.monthsThisYearMoments.length }));

        }

        init();
      }
    ],
    require: 'momentPicker',
    link: function(scope, element, attr, ctrl) {

      // View helpers
      ////////////////

      scope.getClasses = function(moment, classes) {
        var isWeekend   = /6|7/.test(moment.isoWeekday()),
            isWeekday   = !isWeekend,
            classObject = {
              weekend: isWeekend,
              weekday: isWeekday
            };

        // Convenience classes: jan fri
        classObject[ moment.format('MMM ddd').toLowerCase() ] = true;

        if (!classes)
          return classObject;

        // Iterate over requested class names
        angular.forEach(classes.split(' '), function(className) {
          var name = className.split('-')[0],
              unit = className.split('-')[1] || ctrl.template.unit;

          if (scope.pickedMoment && name == 'picked')
            classObject[className] = moment.isSame(scope.pickedMoment, ctrl.template.unit);

          else if (name == 'current')
            classObject[className +' current'] = moment.isSame(scope.today, unit);

          else if (name == 'invalid' && (ctrl.minMoment || ctrl.maxMoment)) {
            if (ctrl.minMoment && moment.isBefore(ctrl.minMoment, unit))
              classObject[className + ' invalid'] = true;
            else if (ctrl.maxMoment && moment.isAfter(ctrl.maxMoment, unit))
              classObject[className + ' invalid'] = true;
          }

        });

        return classObject;
      };

    }
  };

}])

// Picker extends moment input directive with positioned momentPicker

.directive('picker', ['$moment', '$compile', 'getOffset', function inputDirective($moment, $compile, getOffset) {
  var defaultStyleAttr = 'style="position:absolute" class="input-picker"',
      copiedAttrs      = 'format modelFormat min max'.split(' ');

  var toSpinalCase = function(string) {
    return string.replace(/[a-z][A-Z]/g, function(w) { return w[0] +'-'+ w[1]; }).toLowerCase();
  };

  return {
    restrict: 'A',
    require: ['?ngModel'],
    link: function(scope, element, attr, ctrl) {
      if (!ctrl || attr.type !== 'moment')
        return;

      var pickerAttrs = [ defaultStyleAttr ],
          pickerElem, pickerScope, pickerCtrl,
          deregisterWatch;

      function init() {
        deregisterWatch();
        pickerScope = pickerElem.isolateScope();
        pickerCtrl  = pickerElem.controller('momentPicker');
        pickerCtrl.setVisibility(false);
        pickerScope.showClose = true;
      }

      // Copy relevent attrs from input to picker
      if (attr.picker)
        pickerAttrs.push('template='+ attr.picker);

      angular.forEach(copiedAttrs, function(name) {
        if (attr[name])
          pickerAttrs.push(toSpinalCase(name) +'="'+ attr[name] +'"');
      });

      // Compile/inject/bind events to picker
      pickerElem = $compile('

')(scope);

      // Watch for controller instantiation
      deregisterWatch = scope.$watch(function() {
        if (pickerElem.controller('momentPicker'))
          init();
      });

      // DOM manipulation and event watching
      angular.element(document.body).append(pickerElem);

      pickerElem.on('mousedown', function(event) {
        event.preventDefault();
      });

      // Input event binding
      element.on('focus click', function(event) {
        var offset = getOffset(element[0]);

        pickerElem.css({
          left: offset.left + 'px',
          top: offset.bottom + 'px'
        });

        pickerCtrl.setVisibility(true);
      });

      element.on('blur keydown', function(event) {
        if (event.type == 'keydown' && event.which !== 27)
          return;
        pickerCtrl.setVisibility(false);
      });

      // Destruction cleanup
      scope.$on('$destroy', function() {
        pickerElem.off().remove();
      });

    }
  };
}]);

<|endoftext|>


<commit_before>'use strict';

function FloatingRow(parent_wall, row_number, row_name) {
    this.__init__(parent_wall, row_number, row_name);
}

FloatingRow.prototype = {

    __init__: function(parent_wall, row_number, row_name) {
        this.parent_wall = parent_wall;
        this.row_number = row_number;
        this.row_name = row_name;
        this.elements = [];
    },

    add_3D_element: function(text, type, color) {
        var floating_element = new Floating3DText(text, type, this.parent_wall.world);

        // TODO : Add ability to center later on.
        floating_element.set_attachment_horizontal_offset(null, -HALF);

        if (is_defined(color)) {
            floating_element.set_default_color(color);
            floating_element.set_color(color, true);
        }

        floating_element.set_attachment_vertical_offset(-32 * this.row_number, HALF);
        floating_element.attach_to(this.parent_wall);

        floating_element.add_tag(SAVE_TAG_3D_ROW);

        this.elements.push(floating_element);
        return floating_element;
    },

    add_2D_label_and_input: function(x_divide_mark, text, syntax_checks) {
        var label = this.add_2D_element([0, x_divide_mark], text, TYPE_CONSTANT);
        var input = this.add_2D_element([x_divide_mark, 1], '', TYPE_INPUT, null, syntax_checks);
        this.elements.push(label);
        this.elements.push(input);
        return [label, input];
    },

    add_2D_label_and_button: function(x_divide_mark, label_text, button_text, function_to_bind) {
        var label = this.add_2D_element([0, x_divide_mark], label_text, TYPE_CONSTANT);
        var button = this.add_2D_element([x_divide_mark, 1], button_text, TYPE_BUTTON);
        if (is_defined(function_to_bind)) {
            button.set_engage_function(function_to_bind);
        }
        this.elements.push(label);
        this.elements.push(button);
        return [label, button];
    },

    add_2D_element: function(x_start_n_stop, text, type, color, syntax_checks) {
        var total_percentage_of_parent_width = x_start_n_stop[1] - x_start_n_stop[0];
        var floating_element_width = this.parent_wall.width * total_percentage_of_parent_width;

        var floating_element = new Floating2DText(floating_element_width, text, type, this.parent_wall.world, syntax_checks);

        if (is_defined(color)) {
            floating_element.set_default_color(color);
            floating_element.set_color(color, true);
        }

        floating_element.set_attachment_depth_offset(1);
        floating_element.set_attachment_horizontal_offset(null, -HALF + x_start_n_stop[0] + total_percentage_of_parent_width / 2);

        // TODO : This should eventually be dynamic.
        floating_element.set_attachment_vertical_offset(-8 + -16 * this.row_number, HALF);

        floating_element.attach_to(this.parent_wall);

        this.elements.push(floating_element);
        return floating_element;
    },

    add_2D_button: function(x_start_n_stop, text, color, function_to_bind) {
        var button = this.add_2D_element(x_start_n_stop, text, TYPE_BUTTON, color);
        button.set_engage_function(function_to_bind);
        return button;
    },

    shift_up: function() {
        this.row_number -= 1;
        var objects_to_shift = this.get_all_elements_and_sub_attachments();
        for (var s = 0; s < objects_to_shift.length; s++) {
            objects_to_shift[s].apply_delta_to_vertical_offset(16, null);
        }
    },

    shift_down: function() {
        this.row_number += 1;
        var objects_to_shift = this.get_all_elements_and_sub_attachments();
        for (var s = 0; s < objects_to_shift.length; s++) {
            objects_to_shift[s].apply_delta_to_vertical_offset(-16, null);
        }
    },

    /*__   ___ ___ ___  ___  __   __
     / _` |__   |   |  |__  |__) /__`
     \__> |___  |   |  |___ |  \ .__/ */
    get_3D_rows_save_data: function() {
        var save_data = '';
        var all_elements = this.get_all_elements_and_sub_attachments();
        for (var e = 0; e < all_elements.length; e++) {
            if (all_elements[e].has_tag(SAVE_TAG_3D_ROW)) {

                var element = all_elements[e];

                save_data += this.row_number + '+';
                save_data += element.get_text() + '+';
                save_data += element.get_type();
            }
        }
        if (save_data === '') {
            return NO_SAVE_DATA;
        }
        return save_data;
    },

    get_all_elements_with_tag: function(tag) {
        var elements = [];
        var all_elements = this.get_all_elements_and_sub_attachments();
        for (var e = 0; e < all_elements.length; e++) {
            if (all_elements[e].has_tag(tag)) {
                elements.push(all_elements[e]);
            }
        }
        return elements;
    },

    get_all_fields_of_type: function(type) {
        var fields = [];
        var all_elements = this.get_all_elements_and_sub_attachments();
        for (var e = 0; e < all_elements.length; e++) {
            if (is_defined(all_elements[e].type)) {
                if (all_elements[e].type === type) {
                    fields.push(all_elements[e]);
                }
            }
        }
        return fields;
    },

    get_all_elements_and_sub_attachments: function() {
        var all_objects = [];
        for (var e = 0; e < this.elements.length; e++) {
            all_objects.push(this.elements[e]);
            var all_sub_attachments = this.elements[e]._get_all_attachments_recursively();
            for (var a = 0; a < all_sub_attachments.length; a++) {
                if (all_objects.indexOf(all_sub_attachments[a]) === NOT_FOUND) {
                    all_objects.push(all_sub_attachments[a]);
                }
            }
        }
        return all_objects;
    },

    /*__   ___       ___ ___    __
     |  \ |__  |    |__   |  | /  \ |\ |
     |__/ |___ |___ |___  |  | \__/ | \| */
    delete_all_elements: function() {
        var all_objects = this.get_all_elements_and_sub_attachments();
        for (var a = 0; a < all_objects.length; a++) {
            all_objects[a].fully_remove_self_and_all_sub_attachments();
        }
    }

};<commit_msg>schedule features 39<commit_after>'use strict';

function FloatingRow(parent_wall, row_number, row_name) {
    this.__init__(parent_wall, row_number, row_name);
}

FloatingRow.prototype = {

    __init__: function(parent_wall, row_number, row_name) {
        this.parent_wall = parent_wall;
        this.row_number = row_number;
        this.row_name = row_name;
        this.elements = [];
    },

    add_3D_element: function(text, type, color) {
        var floating_element = new Floating3DText(text, type, this.parent_wall.world);

        // TODO : Add ability to center later on.
        floating_element.set_attachment_horizontal_offset(null, -HALF);

        if (is_defined(color)) {
            floating_element.set_default_color(color);
            floating_element.set_color(color, true);
        }

        floating_element.set_attachment_vertical_offset(-32 * this.row_number, HALF);
        floating_element.attach_to(this.parent_wall);

        floating_element.add_tag(SAVE_TAG_3D_ROW);

        this.elements.push(floating_element);
        return floating_element;
    },

    add_2D_label_and_input: function(x_divide_mark, text, syntax_checks) {
        var label = this.add_2D_element([0, x_divide_mark], text, TYPE_CONSTANT);
        var input = this.add_2D_element([x_divide_mark, 1], '', TYPE_INPUT, null, syntax_checks);
        this.elements.push(label);
        this.elements.push(input);
        return [label, input];
    },

    add_2D_label_and_button: function(x_divide_mark, label_text, button_text, function_to_bind) {
        var label = this.add_2D_element([0, x_divide_mark], label_text, TYPE_CONSTANT);
        var button = this.add_2D_element([x_divide_mark, 1], button_text, TYPE_BUTTON);
        if (is_defined(function_to_bind)) {
            button.set_engage_function(function_to_bind);
        }
        this.elements.push(label);
        this.elements.push(button);
        return [label, button];
    },

    add_2D_element: function(x_start_n_stop, text, type, color, syntax_checks) {
        var total_percentage_of_parent_width = x_start_n_stop[1] - x_start_n_stop[0];
        var floating_element_width = this.parent_wall.width * total_percentage_of_parent_width;

        var floating_element = new Floating2DText(floating_element_width, text, type, this.parent_wall.world, syntax_checks);

        if (is_defined(color)) {
            floating_element.set_default_color(color);
            floating_element.set_color(color, true);
        }

        floating_element.set_attachment_depth_offset(1);
        floating_element.set_attachment_horizontal_offset(null, -HALF + x_start_n_stop[0] + total_percentage_of_parent_width / 2);

        // TODO : This should eventually be dynamic.
        floating_element.set_attachment_vertical_offset(-8 + -16 * this.row_number, HALF);

        floating_element.attach_to(this.parent_wall);

        this.elements.push(floating_element);
        return floating_element;
    },

    add_2D_button: function(x_start_n_stop, text, color, function_to_bind) {
        var button = this.add_2D_element(x_start_n_stop, text, TYPE_BUTTON, color);
        button.set_engage_function(function_to_bind);
        return button;
    },

    shift_up: function() {
        this.row_number -= 1;
        var objects_to_shift = this.get_all_elements_and_sub_attachments();
        for (var s = 0; s < objects_to_shift.length; s++) {
            objects_to_shift[s].apply_delta_to_vertical_offset(16, null);
        }
    },

    shift_down: function() {
        this.row_number += 1;
        var objects_to_shift = this.get_all_elements_and_sub_attachments();
        for (var s = 0; s < objects_to_shift.length; s++) {
            objects_to_shift[s].apply_delta_to_vertical_offset(-16, null);
        }
    },

    /*__   ___ ___ ___  ___  __   __
     / _` |__   |   |  |__  |__) /__`
     \__> |___  |   |  |___ |  \ .__/ */
    get_3D_rows_save_data: function() {
        var save_data = '';
        for (var e = 0; e < this.elements.length; e++) {
            if (this.elements[e].has_tag(SAVE_TAG_3D_ROW)) {

                var element = this.elements[e];

                save_data += this.row_number + '+';
                save_data += element.get_text() + '+';
                save_data += element.get_type();
            }
        }
        if (save_data === '') {
            return NO_SAVE_DATA;
        }
        return save_data;
    },

    get_all_elements_with_tag: function(tag) {
        var elements = [];
        var all_elements = this.get_all_elements_and_sub_attachments();
        for (var e = 0; e < all_elements.length; e++) {
            if (all_elements[e].has_tag(tag)) {
                elements.push(all_elements[e]);
            }
        }
        return elements;
    },

    get_all_fields_of_type: function(type) {
        var fields = [];
        var all_elements = this.get_all_elements_and_sub_attachments();
        for (var e = 0; e < all_elements.length; e++) {
            if (is_defined(all_elements[e].type)) {
                if (all_elements[e].type === type) {
                    fields.push(all_elements[e]);
                }
            }
        }
        return fields;
    },

    get_all_elements_and_sub_attachments: function() {
        var all_objects = [];
        for (var e = 0; e < this.elements.length; e++) {
            all_objects.push(this.elements[e]);
            var all_sub_attachments = this.elements[e]._get_all_attachments_recursively();
            for (var a = 0; a < all_sub_attachments.length; a++) {
                if (all_objects.indexOf(all_sub_attachments[a]) === NOT_FOUND) {
                    all_objects.push(all_sub_attachments[a]);
                }
            }
        }
        return all_objects;
    },

    /*__   ___       ___ ___    __
     |  \ |__  |    |__   |  | /  \ |\ |
     |__/ |___ |___ |___  |  | \__/ | \| */
    delete_all_elements: function() {
        var all_objects = this.get_all_elements_and_sub_attachments();
        for (var a = 0; a < all_objects.length; a++) {
            all_objects[a].fully_remove_self_and_all_sub_attachments();
        }
    }

};<|endoftext|>


<commit_before>'use strict';

const expect = require('chai').expect;
const postcss = require('postcss');
const plugin = require('./index.js');
const parser = require('./parser/parse');
const stringify = require('./parser/stringify');
const Decl = require('./lib/Declaration');
const helpers = require('./lib/helpers');
const mixins = {
	spacedBlock(...args) {
		let props = this.spaced(...args);

		args.shift();
		return props.concat(this.block(...args));
	},
	block(...args) {
		let props = [
			new Decl('display', 'block')
		];

		props.push(new Decl('width', args[0]));

		if (args[1]) {
			props.push(new Decl('height', args[1]));
		}

		return props;
	},
	spaced(value) {
		if (helpers.isEmpty(value) || helpers.isObject(value)) {
			value = 2;
		}

		return this.margin({ bottom: value });
	},
	margin(...args) {
		let props = [];

		return props.concat(Decl.createManyFromObj(args[0], 'margin'));
	},
	font(...args) {
		var props = [];

		props.push(new Decl('font-family', args[0]));

		if (args[1]) {
			props.push(new Decl('font-size', args[1]));
		}

		if (args[2]) {
			props.push(new Decl('font-weight', args[2]));
		}

		if (args[3]) {
			props.push(new Decl('line-height', args[3]));
		}

		if (args[4]) {
			props.push(new Decl('font-style', args[4]));
		}

		return props;
	}
};

function process(input, expected, opts = {}, warnings = 0) {
	return postcss([ plugin(opts) ]).process(input, {
			parser: parser,
			stringifier: stringify
		})
		.then(result => {
			expect(result.css).to.equal(expected);
			expect(result.warnings().length).to.equal(warnings);
		});
}

describe('mixins', () => {
	it('should generate a single declaration', () => {
		return process(
			`.block {
				background(#fff);
			}`,
			`.block {
				background: #fff;
			}`,
			{
				mixins: {
					background(color) {
						return { prop: 'background', value: color }
					}
				}
			}
		);
	});

	it('should generate multiple declarations from one mixin', () => {
		return process(
			`.block {
				background(#fff, no-repeat, right, top);
			}`,
			`.block {
				background-color: #fff;
				background-repeat: no-repeat;
				background-position: right top;
			}`,
			{
				mixins: {
					background(color, repeat, x = '0%', y = '0%') {
						return [
							{ prop: 'background-color', value: color },
							{ prop: 'background-repeat', value: repeat },
							{ prop: 'background-position', value: x + ' ' + y }
						];
					}
				}
			}
		);
	});

	it('should convert numbers', () => {
		return process(
			`.block {
				opacity(.4);
			}`,
			`.block {
				opacity: 0.4;
			}`,
			{
				mixins: {
					opacity(value) {
						return new Decl('opacity', helpers.calcOpacity(value));
					}
				}
			}
		);
	});

	it('should parse unmatched variables as individual parameters', () => {
		return process(
			`.block {
				spacedBlock($margin, 10);
			}`,
			`.block {
				margin-bottom: $margin;
				display: block;
				width: 10rem;
			}`,
			{
				mixins: mixins
			}
		);
	});

	it('should throw a warning if mixin does not exist', () => {
		return process(
			`.block {
				customMixin(#fff);
			}`,
			`.block {
			}`,
			{},
			1
		);
	});
});

describe('default units', () => {
	after(() => {
		return process('', '', {
			mixins: {},
			units: {
				default: 'rem',
				lineHeight: 'em'
			}
		});
	});

	it('should fallback to rem and em (line-height only)', () => {
		return process(
			`.block {
				font('Open Sans' Arial sans-serif, 5, bold, 1.2);
			}`,
			`.block {
				font-family: 'Open Sans', Arial, sans-serif;
				font-size: 5rem;
				font-weight: bold;
				line-height: 1.2em;
			}`,
			{
				mixins: mixins
			}
		);
	});

	it('should be registered in options object', () => {
		return process(
			`.block {
				spacedBlock($margin, 10);
				font(Arial, 5, bold, 1.2);
			}`,
			`.block {
				margin-bottom: $margin;
				display: block;
				width: 10px;
				font-family: Arial;
				font-size: 5px;
				font-weight: bold;
				line-height: 1.2%;
			}`,
			{
				mixins: mixins,
				units: {
					default: 'px',
					lineHeight: '%'
				}
			}
		);
	});
});

describe('declarations', () => {
	it('should create many declaration instances by mapping two arrays together', () => {
		return process(
			`.block {
				margin(1, 2, 3, 4);
			}`,
			`.block {
				margin-top: 1rem;
				margin-right: 2rem;
				margin-left: 3rem;
				margin-bottom: 4rem;
			}`,
			{
				mixins: {
					margin(...args) {
						return Decl.createMany([
							'margin-top',
							'margin-right',
							'margin-left',
							'margin-bottom'
						], args);
					}
				}
			}
		);
	});

	it('should create many by reusing the same value', () => {
		return process(
			`.block {
				verticalPadding(2);
			}`,
			`.block {
				padding-top: 2rem;
				padding-bottom: 2rem;
			}`,
			{
				mixins: {
					verticalPadding(value) {
						return Decl.createMany([
							'padding-top',
							'padding-bottom'
						], helpers.unit(value));
					}
				}
			}
		);
	});

	it('should create many declaration instances from an object', () => {
		return process(
			`.block {
				margin(top: 1, bottom: 4, right: 2, left: 3);
			}`,
			`.block {
				margin-top: 1rem;
				margin-bottom: 4rem;
				margin-right: 2rem;
				margin-left: 3rem;
			}`,
			{
				mixins: {
					margin(obj) {
						return Decl.createManyFromObj(obj, 'margin');
					}
				}
			}
		);
	});
});

describe('helpers: calcOpacity', () => {
	it('should convert percentage argument into proper format', () => {
		return expect(helpers.calcOpacity('20%')).to.equal(0.2);
	});

	it('should convert numerical value over 1 into proper format', () => {
		return expect(helpers.calcOpacity(20)).to.equal(0.2);
	});

	it('should return the raw value if not percentage or greater than 1', () => {
		return expect(helpers.calcOpacity(0.2)).to.equal(0.2);
	});
});

describe('helpers: hexToRgba', () => {
	it('should convert a hex value to RGB', () => {
		return expect(helpers.hexToRgba('#f5830f')).to.equal('rgb(245, 131, 15)');
	});

	it('should convert a hex value with opacity into RGBA', () => {
		return expect(helpers.hexToRgba('#f5830f', 0.2)).to.equal('rgba(245, 131, 15, 0.2)');
	});

	it('should convert three character hex value into proper rgb', () => {
		return expect(helpers.hexToRgba('#fff')).to.equal('rgb(255, 255, 255)');
	});
});

describe('helpers: isColor', () => {
	it('should identify hex values as color', () => {
		expect(helpers.isColor('#fff')).to.equal(true);
		expect(helpers.isColor('#f7f7f7')).to.equal(true);
	});

	it('should identify rgb/rgba values as color', () => {
		expect(helpers.isColor('rgb(0, 0, 0)')).to.equal(true);
		expect(helpers.isColor('rgba(0, 0, 0, 0.5)')).to.equal(true);
	});

	it('should identify hsl/hsla values as color', () => {
		expect(helpers.isColor('hsl(0, 100%, 50%)')).to.equal(true);
		expect(helpers.isColor('hsla(0, 100%, 50%, 1)')).to.equal(true);
	});

	it('should identify non-color strings as false', () => {
		expect(helpers.isColor('something')).to.equal(false);
	});
});

describe('helpers: isEmpty', () => {
	it('should identify empty array as empty', () => {
		expect(helpers.isEmpty([])).to.equal(true);
	});

	it('should identify undefined as empty', () => {
		expect(helpers.isEmpty()).to.equal(true);
	});
});

describe('helpers: isNumber', () => {
	it('should identify string numbers and numbers as a number', () => {
		expect(helpers.isNumber('2')).to.equal(true);
		expect(helpers.isNumber(2)).to.equal(true);
		expect(helpers.isNumber(2.1)).to.equal(true);
	});

	it('should not identify any other types', () => {
		expect(helpers.isNumber('string')).to.equal(false);
		expect(helpers.isNumber(undefined)).to.equal(false);
		expect(helpers.isNumber(null)).to.equal(false);
		expect(helpers.isNumber(true)).to.equal(false);
		expect(helpers.isNumber([])).to.equal(false);
		expect(helpers.isNumber({})).to.equal(false);
	});
});

describe('helpers: isObject', () => {
	it('should identify only {} as an object', () => {
		expect(helpers.isObject({})).to.equal(true);
		expect(helpers.isObject('string')).to.equal(false);
		expect(helpers.isObject(undefined)).to.equal(false);
		expect(helpers.isObject(null)).to.equal(false);
		expect(helpers.isObject(true)).to.equal(false);
		expect(helpers.isObject([])).to.equal(false);
	});
});

describe('helpers: isPercentage', () => {
	it('should identify string number with %', () => {
		expect(helpers.isPercentage('20%')).to.equal(true);
	});
});

describe('helpers: isString', () => {
	it('should identify string', () => {
		expect(helpers.isString('string')).to.equal(true);
		expect(helpers.isString({})).to.equal(false);
		expect(helpers.isString(undefined)).to.equal(false);
		expect(helpers.isString(null)).to.equal(false);
		expect(helpers.isString(true)).to.equal(false);
		expect(helpers.isString([])).to.equal(false);
	});
});

describe('helpers: isUnit', () => {
	it('should identify a CSS unit', () => {
		expect(helpers.isUnit('10px')).to.equal(true);
		expect(helpers.isUnit('10rem')).to.equal(true);
		expect(helpers.isUnit('10em')).to.equal(true);
		expect(helpers.isUnit('10%')).to.equal(true);
		expect(helpers.isUnit('10vh')).to.equal(true);
		expect(helpers.isUnit('10vw')).to.equal(true);
		expect(helpers.isUnit('10vmin')).to.equal(true);
	});

	it('should not identify numeric values without unit', () => {
		expect(helpers.isUnit('10')).to.equal(false);
		expect(helpers.isUnit(10)).to.equal(false);
	});
});

describe('helpers: prefix', () => {
	it('should return raw input if no prefix is false', () => {
		expect(helpers.prefix('margin', false)).to.equal('margin');
	});

	it('should return prefix appended to standard value', () => {
		expect(helpers.prefix('family', 'font')).to.equal('font-family');
	});

	it('should return dash case version of input if prefix is supposed to be ignored', () => {
		expect(helpers.prefix('family', 'font', ['font'])).to.equal('family');
	});
});

describe('helpers: toDashCase', () => {
	it('should return camelCase input to dash case', () => {
		expect(helpers.toDashCase('toDashCase')).to.equal('to-dash-case');
	});
});<commit_msg>Test type helper<commit_after>'use strict';

const expect = require('chai').expect;
const postcss = require('postcss');
const plugin = require('./index.js');
const parser = require('./parser/parse');
const stringify = require('./parser/stringify');
const Decl = require('./lib/Declaration');
const helpers = require('./lib/helpers');
const mixins = {
	spacedBlock(...args) {
		let props = this.spaced(...args);

		args.shift();
		return props.concat(this.block(...args));
	},
	block(...args) {
		let props = [
			new Decl('display', 'block')
		];

		props.push(new Decl('width', args[0]));

		if (args[1]) {
			props.push(new Decl('height', args[1]));
		}

		return props;
	},
	spaced(value) {
		if (helpers.isEmpty(value) || helpers.isObject(value)) {
			value = 2;
		}

		return this.margin({ bottom: value });
	},
	margin(...args) {
		let props = [];

		return props.concat(Decl.createManyFromObj(args[0], 'margin'));
	},
	font(...args) {
		var props = [];

		props.push(new Decl('font-family', args[0]));

		if (args[1]) {
			props.push(new Decl('font-size', args[1]));
		}

		if (args[2]) {
			props.push(new Decl('font-weight', args[2]));
		}

		if (args[3]) {
			props.push(new Decl('line-height', args[3]));
		}

		if (args[4]) {
			props.push(new Decl('font-style', args[4]));
		}

		return props;
	}
};

function process(input, expected, opts = {}, warnings = 0) {
	return postcss([ plugin(opts) ]).process(input, {
			parser: parser,
			stringifier: stringify
		})
		.then(result => {
			expect(result.css).to.equal(expected);
			expect(result.warnings().length).to.equal(warnings);
		});
}

describe('mixins', () => {
	it('should generate a single declaration', () => {
		return process(
			`.block {
				background(#fff);
			}`,
			`.block {
				background: #fff;
			}`,
			{
				mixins: {
					background(color) {
						return { prop: 'background', value: color }
					}
				}
			}
		);
	});

	it('should generate multiple declarations from one mixin', () => {
		return process(
			`.block {
				background(#fff, no-repeat, right, top);
			}`,
			`.block {
				background-color: #fff;
				background-repeat: no-repeat;
				background-position: right top;
			}`,
			{
				mixins: {
					background(color, repeat, x = '0%', y = '0%') {
						return [
							{ prop: 'background-color', value: color },
							{ prop: 'background-repeat', value: repeat },
							{ prop: 'background-position', value: x + ' ' + y }
						];
					}
				}
			}
		);
	});

	it('should convert numbers', () => {
		return process(
			`.block {
				opacity(.4);
			}`,
			`.block {
				opacity: 0.4;
			}`,
			{
				mixins: {
					opacity(value) {
						return new Decl('opacity', helpers.calcOpacity(value));
					}
				}
			}
		);
	});

	it('should parse unmatched variables as individual parameters', () => {
		return process(
			`.block {
				spacedBlock($margin, 10);
			}`,
			`.block {
				margin-bottom: $margin;
				display: block;
				width: 10rem;
			}`,
			{
				mixins: mixins
			}
		);
	});

	it('should throw a warning if mixin does not exist', () => {
		return process(
			`.block {
				customMixin(#fff);
			}`,
			`.block {
			}`,
			{},
			1
		);
	});
});

describe('default units', () => {
	after(() => {
		return process('', '', {
			mixins: {},
			units: {
				default: 'rem',
				lineHeight: 'em'
			}
		});
	});

	it('should fallback to rem and em (line-height only)', () => {
		return process(
			`.block {
				font('Open Sans' Arial sans-serif, 5, bold, 1.2);
			}`,
			`.block {
				font-family: 'Open Sans', Arial, sans-serif;
				font-size: 5rem;
				font-weight: bold;
				line-height: 1.2em;
			}`,
			{
				mixins: mixins
			}
		);
	});

	it('should be registered in options object', () => {
		return process(
			`.block {
				spacedBlock($margin, 10);
				font(Arial, 5, bold, 1.2);
			}`,
			`.block {
				margin-bottom: $margin;
				display: block;
				width: 10px;
				font-family: Arial;
				font-size: 5px;
				font-weight: bold;
				line-height: 1.2%;
			}`,
			{
				mixins: mixins,
				units: {
					default: 'px',
					lineHeight: '%'
				}
			}
		);
	});
});

describe('declarations', () => {
	it('should create many declaration instances by mapping two arrays together', () => {
		return process(
			`.block {
				margin(1, 2, 3, 4);
			}`,
			`.block {
				margin-top: 1rem;
				margin-right: 2rem;
				margin-left: 3rem;
				margin-bottom: 4rem;
			}`,
			{
				mixins: {
					margin(...args) {
						return Decl.createMany([
							'margin-top',
							'margin-right',
							'margin-left',
							'margin-bottom'
						], args);
					}
				}
			}
		);
	});

	it('should create many by reusing the same value', () => {
		return process(
			`.block {
				verticalPadding(2);
			}`,
			`.block {
				padding-top: 2rem;
				padding-bottom: 2rem;
			}`,
			{
				mixins: {
					verticalPadding(value) {
						return Decl.createMany([
							'padding-top',
							'padding-bottom'
						], helpers.unit(value));
					}
				}
			}
		);
	});

	it('should create many declaration instances from an object', () => {
		return process(
			`.block {
				margin(top: 1, bottom: 4, right: 2, left: 3);
			}`,
			`.block {
				margin-top: 1rem;
				margin-bottom: 4rem;
				margin-right: 2rem;
				margin-left: 3rem;
			}`,
			{
				mixins: {
					margin(obj) {
						return Decl.createManyFromObj(obj, 'margin');
					}
				}
			}
		);
	});
});

describe('helpers: calcOpacity', () => {
	it('should convert percentage argument into proper format', () => {
		return expect(helpers.calcOpacity('20%')).to.equal(0.2);
	});

	it('should convert numerical value over 1 into proper format', () => {
		return expect(helpers.calcOpacity(20)).to.equal(0.2);
	});

	it('should return the raw value if not percentage or greater than 1', () => {
		return expect(helpers.calcOpacity(0.2)).to.equal(0.2);
	});
});

describe('helpers: hexToRgba', () => {
	it('should convert a hex value to RGB', () => {
		return expect(helpers.hexToRgba('#f5830f')).to.equal('rgb(245, 131, 15)');
	});

	it('should convert a hex value with opacity into RGBA', () => {
		return expect(helpers.hexToRgba('#f5830f', 0.2)).to.equal('rgba(245, 131, 15, 0.2)');
	});

	it('should convert three character hex value into proper rgb', () => {
		return expect(helpers.hexToRgba('#fff')).to.equal('rgb(255, 255, 255)');
	});
});

describe('helpers: isColor', () => {
	it('should identify hex values as color', () => {
		expect(helpers.isColor('#fff')).to.equal(true);
		expect(helpers.isColor('#f7f7f7')).to.equal(true);
	});

	it('should identify rgb/rgba values as color', () => {
		expect(helpers.isColor('rgb(0, 0, 0)')).to.equal(true);
		expect(helpers.isColor('rgba(0, 0, 0, 0.5)')).to.equal(true);
	});

	it('should identify hsl/hsla values as color', () => {
		expect(helpers.isColor('hsl(0, 100%, 50%)')).to.equal(true);
		expect(helpers.isColor('hsla(0, 100%, 50%, 1)')).to.equal(true);
	});

	it('should identify non-color strings as false', () => {
		expect(helpers.isColor('something')).to.equal(false);
	});
});

describe('helpers: isEmpty', () => {
	it('should identify empty array as empty', () => {
		expect(helpers.isEmpty([])).to.equal(true);
	});

	it('should identify undefined as empty', () => {
		expect(helpers.isEmpty()).to.equal(true);
	});
});

describe('helpers: isNumber', () => {
	it('should identify string numbers and numbers as a number', () => {
		expect(helpers.isNumber('2')).to.equal(true);
		expect(helpers.isNumber(2)).to.equal(true);
		expect(helpers.isNumber(2.1)).to.equal(true);
	});

	it('should not identify any other types', () => {
		expect(helpers.isNumber('string')).to.equal(false);
		expect(helpers.isNumber(undefined)).to.equal(false);
		expect(helpers.isNumber(null)).to.equal(false);
		expect(helpers.isNumber(true)).to.equal(false);
		expect(helpers.isNumber([])).to.equal(false);
		expect(helpers.isNumber({})).to.equal(false);
	});
});

describe('helpers: isObject', () => {
	it('should identify only {} as an object', () => {
		expect(helpers.isObject({})).to.equal(true);
		expect(helpers.isObject('string')).to.equal(false);
		expect(helpers.isObject(undefined)).to.equal(false);
		expect(helpers.isObject(null)).to.equal(false);
		expect(helpers.isObject(true)).to.equal(false);
		expect(helpers.isObject([])).to.equal(false);
	});
});

describe('helpers: isPercentage', () => {
	it('should identify string number with %', () => {
		expect(helpers.isPercentage('20%')).to.equal(true);
	});
});

describe('helpers: isString', () => {
	it('should identify string', () => {
		expect(helpers.isString('string')).to.equal(true);
		expect(helpers.isString({})).to.equal(false);
		expect(helpers.isString(undefined)).to.equal(false);
		expect(helpers.isString(null)).to.equal(false);
		expect(helpers.isString(true)).to.equal(false);
		expect(helpers.isString([])).to.equal(false);
	});
});

describe('helpers: isUnit', () => {
	it('should identify a CSS unit', () => {
		expect(helpers.isUnit('10px')).to.equal(true);
		expect(helpers.isUnit('10rem')).to.equal(true);
		expect(helpers.isUnit('10em')).to.equal(true);
		expect(helpers.isUnit('10%')).to.equal(true);
		expect(helpers.isUnit('10vh')).to.equal(true);
		expect(helpers.isUnit('10vw')).to.equal(true);
		expect(helpers.isUnit('10vmin')).to.equal(true);
	});

	it('should not identify numeric values without unit', () => {
		expect(helpers.isUnit('10')).to.equal(false);
		expect(helpers.isUnit(10)).to.equal(false);
	});
});

describe('helpers: prefix', () => {
	it('should return raw input if no prefix is false', () => {
		expect(helpers.prefix('margin', false)).to.equal('margin');
	});

	it('should return prefix appended to standard value', () => {
		expect(helpers.prefix('family', 'font')).to.equal('font-family');
	});

	it('should return dash case version of input if prefix is supposed to be ignored', () => {
		expect(helpers.prefix('family', 'font', ['font'])).to.equal('family');
	});
});

describe('helpers: toDashCase', () => {
	it('should return camelCase input to dash case', () => {
		expect(helpers.toDashCase('toDashCase')).to.equal('to-dash-case');
	});
});

describe('helpers: type', () => {
	it('should return the text value of type', () => {
		expect(helpers.type('string')).to.equal('string');
		expect(helpers.type([])).to.equal('array');
		expect(helpers.type({})).to.equal('object');
		expect(helpers.type(1)).to.equal('number');
		expect(helpers.type(true)).to.equal('boolean');
		expect(helpers.type(undefined)).to.equal('undefined');
		expect(helpers.type(null)).to.equal('null');
	});
});<|endoftext|>


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

'use strict'

const _ = require('lodash')

module.exports = {

  subjectParser: 'angular',

  getGitReferenceFromVersion: 'v-prefix',

  editChangelog: true,

  editVersion: false,

  addEntryToChangelog: {
    preset: 'prepend',
    fromLine: 5
  },

  includeCommitWhen: (commit) => {
    return commit.footer['Changelog-Entry']
  },

  getIncrementLevelFromCommit: (commit) => {
    return commit.footer['Change-Type']
  },

  transformTemplateData: (data) => {
    data.features = _.filter(data.commits, {
      subject: {
        type: 'feat'
      }
    })

    data.fixes = _.filter(data.commits, {
      subject: {
        type: 'fix'
      }
    })

    data.misc = _.filter(data.commits, (commit) => {
      return !_.includes([ 'fix', 'feat' ], commit.subject.type)
    })

    return data
  },

  template: [
    '## v{{version}} - {{moment date "Y-MM-DD"}}',
    '{{#if features.length}}',
    '',
    '### Features',
    '',
    '{{#each features}}',
    '{{#with footer}}',
    '- {{capitalize Changelog-Entry}}',
    '{{/with}}',
    '{{/each}}',
    '{{/if}}',
    '{{#if fixes.length}}',
    '',
    '### Fixes',
    '',
    '{{#each fixes}}',
    '{{#with footer}}',
    '- {{capitalize Changelog-Entry}}',
    '{{/with}}',
    '{{/each}}',
    '{{/if}}',
    '{{#if misc.length}}',
    '',
    '### Misc',
    '',
    '{{#each misc}}',
    '{{#with footer}}',
    '- {{capitalize Changelog-Entry}}',
    '{{/with}}',
    '{{/each}}',
    '{{/if}}'
  ].join('\n')

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

'use strict'

const _ = require('lodash')

module.exports = {

  subjectParser: 'angular',

  getGitReferenceFromVersion: 'v-prefix',

  editChangelog: true,

  editVersion: false,

  addEntryToChangelog: {
    preset: 'prepend',
    fromLine: 5
  },

  includeCommitWhen: (commit) => {
    return commit.footer['Changelog-Entry']
  },

  getIncrementLevelFromCommit: (commit) => {
    if (/none/i.test(commit.footer['Change-Type'])) {
      return null
    }
    return commit.footer['Change-Type'] &&
      _.toLower(commit.footer['Change-Type'])
  },

  transformTemplateData: (data) => {
    data.features = _.filter(data.commits, {
      subject: {
        type: 'feat'
      }
    })

    data.fixes = _.filter(data.commits, {
      subject: {
        type: 'fix'
      }
    })

    data.misc = _.filter(data.commits, (commit) => {
      return !_.includes([ 'fix', 'feat' ], commit.subject.type)
    })

    return data
  },

  template: [
    '## v{{version}} - {{moment date "Y-MM-DD"}}',
    '{{#if features.length}}',
    '',
    '### Features',
    '',
    '{{#each features}}',
    '{{#with footer}}',
    '- {{capitalize Changelog-Entry}}',
    '{{/with}}',
    '{{/each}}',
    '{{/if}}',
    '{{#if fixes.length}}',
    '',
    '### Fixes',
    '',
    '{{#each fixes}}',
    '{{#with footer}}',
    '- {{capitalize Changelog-Entry}}',
    '{{/with}}',
    '{{/each}}',
    '{{/if}}',
    '{{#if misc.length}}',
    '',
    '### Misc',
    '',
    '{{#each misc}}',
    '{{#with footer}}',
    '- {{capitalize Changelog-Entry}}',
    '{{/with}}',
    '{{/each}}',
    '{{/if}}'
  ].join('\n')

}
<|endoftext|>


<commit_before>/* eslint-disable no-param-reassign, no-continue */

const fs = require('fs-extra');
const path = require('path');
const chalk = require('chalk');
const _ = require('lodash');

const ENVIRONMENTS = require('../../../constants/environments');
const getApiClient = require('./api');
const facilityLocationPath = require('./utilities-drupal');

const DRUPAL_CACHE_FILENAME = 'drupal.json';

// If "--pull-drupal" is passed into the build args, then the build
// should pull the latest Drupal data.
const PULL_DRUPAL_BUILD_ARG = 'pull-drupal';

const ENABLED_ENVIRONMENTS = new Set([
  ENVIRONMENTS.LOCALHOST,
  ENVIRONMENTS.VAGOVDEV,
  ENVIRONMENTS.VAGOVSTAGING,
]);

const DRUPAL_COLORIZED_OUTPUT = chalk.rgb(73, 167, 222);

// eslint-disable-next-line no-console
const log = message => console.log(DRUPAL_COLORIZED_OUTPUT(message));

function writeDrupalDebugPage(files) {
  log('Drupal debug page written to /drupal/debug.');

  const drupalPages = Object.keys(files)
    .filter(page => page.startsWith('drupal'))
    .map(page => `<li><a href="/${page}">/${page}</a></li>`)
    .join('');

  const drupalIndex = `

The following pages were provided by Drupal:

${drupalPages}

  `;

  files['drupal/debug/index.html'] = {
    contents: Buffer.from(drupalIndex),
  };
}

// Creates the file object to add to the file list using the page and layout
function createFileObj(page, layout) {
  // Exclude some types from sitemap.
  const privateTypes = ['outreach_asset', 'person_profile', 'support_service'];
  let privStatus = false;
  if (privateTypes.indexOf(page.entityBundle) > -1) {
    privStatus = true;
  }

  return {
    ...page,
    isDrupalPage: true,
    layout,
    contents: Buffer.from(''),
    debug: JSON.stringify(page, null, 4),
    private: privStatus,
  };
}

function paginationPath(pageNum) {
  return pageNum === 0 ? '' : `/page-${pageNum + 1}`;
}

// Turn one big page into a series of paginated pages.
function paginatePages(
  page,
  pagePath,
  files,
  field,
  layout,
  ariaLabel,
  perPage,
) {
  perPage = perPage || 10;

  if (typeof ariaLabel === 'undefined') {
    ariaLabel = '';
  } else {
    ariaLabel = ` of ${ariaLabel}`;
  }

  const pagedEntities = _.chunk(page[field].entities, perPage);
  for (let pageNum = 0; pageNum < pagedEntities.length; pageNum++) {
    const pagedPage = Object.assign({}, page);
    pagedPage.pagedItems = pagedEntities[pageNum];
    const innerPages = [];

    if (pagedEntities.length > 1) {
      // add page numbers
      const numPageLinks = 3;
      let start;
      let length;
      if (pagedEntities.length <= numPageLinks) {
        start = 0;
        length = pagedEntities.length;
      } else {
        length = numPageLinks;

        if (pageNum + numPageLinks > pagedEntities.length) {
          start = pagedEntities.length - numPageLinks;
        } else {
          start = pageNum;
        }
      }
      for (let num = start; num < start + length; num++) {
        innerPages.push({
          href: num === pageNum ? null : `${pagePath}${paginationPath(num)}`,
          label: num + 1,
          class: num === pageNum ? 'va-pagination-active' : '',
        });
      }

      pagedPage.paginator = {
        ariaLabel,
        prev: pageNum > 0 ? `${pagePath}${paginationPath(pageNum - 1)}` : null,
        inner: innerPages,
        next:
          pageNum < pagedEntities.length - 1
            ? `${pagePath}${paginationPath(pageNum + 1)}`
            : null,
      };
    }

    files[
      `drupal${pagePath}${paginationPath(pageNum)}/index.html`
    ] = createFileObj(pagedPage, layout);
  }
}

// Creates the top-level health care region list pages (Locations, Services, etc.)
function createHealthCareRegionListPages(page, drupalPagePath, files) {
  // Create the top-level locations page for Health Care Regions
  files[`drupal${drupalPagePath}/locations/index.html`] = createFileObj(
    page,
    'health_care_region_locations_page.drupal.liquid',
  );

  paginatePages(
    page,
    `${drupalPagePath}/press-releases`,
    files,
    'allPressReleaseTeasers',
    'press_releases_page.drupal.liquid',
    'press releases',
  );

  const relatedLinks = { fieldRelatedLinks: page.fieldRelatedLinks };

  // Create the detail page for healthcare local facilities
  if (page.mainFacilities !== undefined || page.otherFacilities !== undefined) {
    for (const facility of [
      ...page.mainFacilities.entities,
      ...page.otherFacilities.entities,
    ]) {
      if (facility.entityBundle === 'health_care_local_facility') {
        const facilityCompiled = Object.assign(facility, relatedLinks);

        const pagePath = facilityLocationPath(
          drupalPagePath,
          facility.fieldFacilityLocatorApiId,
          facility.fieldNicknameForThisFacility,
        );
        files[`drupal${pagePath}/index.html`] = createFileObj(
          facilityCompiled,
          'health_care_local_facility_page.drupal.liquid',
        );
      }
    }
  }

  files[`drupal${drupalPagePath}/health-services/index.html`] = createFileObj(
    page,
    'health_care_region_health_services_page.drupal.liquid',
  );
}

function pipeDrupalPagesIntoMetalsmith(contentData, files) {
  const {
    data: {
      nodeQuery: { entities: pages },
      sidebarQuery: sidebarNav,
      alerts: alertsItem,
    },
  } = contentData;

  const sidebarNavItems = { sidebar: sidebarNav };
  const alertItems = { alert: alertsItem };

  for (const page of pages) {
    // At this time, null values are returned for pages that are not yet published.
    // Once the Content-Preview server is up and running, then unpublished pages should
    // reliably return like any other page and we can delete this.
    if (!page) {
      log('Skipping null entity...');
      continue;
    }

    if (!Object.keys(page).length) {
      log('Skipping empty entity...');
      continue;
    }

    const {
      entityUrl: { path: drupalPagePath },
      entityBundle,
    } = page;

    const pageIdRaw = parseInt(page.entityId, 10);
    const pageId = { pid: pageIdRaw };
    let pageCompiled;

    if (entityBundle === 'page') {
      pageCompiled = Object.assign(page, sidebarNavItems, alertItems, pageId);
    } else {
      pageCompiled = Object.assign(page, alertItems, pageId);
    }

    files[`drupal${drupalPagePath}/index.html`] = createFileObj(
      pageCompiled,
      `${entityBundle}.drupal.liquid`,
    );

    if (page.entityBundle === 'health_care_region_page') {
      createHealthCareRegionListPages(pageCompiled, drupalPagePath, files);
    }
  }

  writeDrupalDebugPage(files);
  files[`drupal/index.md`] = {
    ...files['index.md'],
    path: 'drupal/index.html',
    isDrupalPage: true,
    private: true,
  };
}

async function loadDrupal(buildOptions) {
  const drupalCache = path.join(
    buildOptions.cacheDirectory,
    DRUPAL_CACHE_FILENAME,
  );
  const isDrupalAvailableInCache = fs.existsSync(drupalCache);

  let shouldPullDrupal = buildOptions[PULL_DRUPAL_BUILD_ARG];
  let drupalPages = null;

  if (!isDrupalAvailableInCache) {
    log('Drupal content unavailable in cache');
    shouldPullDrupal = true;
  }

  if (shouldPullDrupal) {
    log('Attempting to load Drupal content from API...');

    const contentApi = getApiClient(buildOptions);

    drupalPages = await contentApi.getAllPages();

    if (buildOptions.buildtype === ENVIRONMENTS.LOCALHOST) {
      const serialized = Buffer.from(JSON.stringify(drupalPages, null, 2));
      fs.ensureDirSync(buildOptions.cacheDirectory);
      fs.writeFileSync(drupalCache, serialized);
    }
  } else {
    log('Attempting to load Drupal content from cache...');
    log(`To pull latest, run with "--${PULL_DRUPAL_BUILD_ARG}" flag.`);

    // eslint-disable-next-line import/no-dynamic-require
    drupalPages = require(drupalCache);
  }

  log('Drupal successfully loaded!');
  return drupalPages;
}

function getDrupalContent(buildOptions) {
  if (!ENABLED_ENVIRONMENTS.has(buildOptions.buildtype)) {
    log(`Drupal integration disabled for buildtype ${buildOptions.buildtype}`);
    const noop = () => {};
    return noop;
  }

  // Declared above the middleware scope so that it's cached during the watch task.
  let drupalData = null;

  return async (files, metalsmith, done) => {
    try {
      if (!drupalData) {
        drupalData = await loadDrupal(buildOptions);
      }
      pipeDrupalPagesIntoMetalsmith(drupalData, files);
      log('Successfully piped Drupal content into Metalsmith!');
      done();
    } catch (err) {
      log(err.stack);
      log('Failed to pipe Drupal content into Metalsmith!');
      log('Continuing with build anyway...');
      // done(err);
      done();
    }
  };
}

module.exports = getDrupalContent;
<commit_msg>Set title, breadcrumb, and path for pages without corresponding drupal nodes. (#9693)<commit_after>/* eslint-disable no-param-reassign, no-continue */

const fs = require('fs-extra');
const path = require('path');
const chalk = require('chalk');
const _ = require('lodash');

const ENVIRONMENTS = require('../../../constants/environments');
const getApiClient = require('./api');
const facilityLocationPath = require('./utilities-drupal');

const DRUPAL_CACHE_FILENAME = 'drupal.json';

// If "--pull-drupal" is passed into the build args, then the build
// should pull the latest Drupal data.
const PULL_DRUPAL_BUILD_ARG = 'pull-drupal';

const ENABLED_ENVIRONMENTS = new Set([
  ENVIRONMENTS.LOCALHOST,
  ENVIRONMENTS.VAGOVDEV,
  ENVIRONMENTS.VAGOVSTAGING,
]);

const DRUPAL_COLORIZED_OUTPUT = chalk.rgb(73, 167, 222);

// eslint-disable-next-line no-console
const log = message => console.log(DRUPAL_COLORIZED_OUTPUT(message));

function writeDrupalDebugPage(files) {
  log('Drupal debug page written to /drupal/debug.');

  const drupalPages = Object.keys(files)
    .filter(page => page.startsWith('drupal'))
    .map(page => `<li><a href="/${page}">/${page}</a></li>`)
    .join('');

  const drupalIndex = `

The following pages were provided by Drupal:

${drupalPages}

  `;

  files['drupal/debug/index.html'] = {
    contents: Buffer.from(drupalIndex),
  };
}

// Creates the file object to add to the file list using the page and layout
function createFileObj(page, layout) {
  // Exclude some types from sitemap.
  const privateTypes = ['outreach_asset', 'person_profile', 'support_service'];
  let privStatus = false;
  if (privateTypes.indexOf(page.entityBundle) > -1) {
    privStatus = true;
  }

  return {
    ...page,
    isDrupalPage: true,
    layout,
    contents: Buffer.from(''),
    debug: JSON.stringify(page, null, 4),
    private: privStatus,
  };
}

function paginationPath(pageNum) {
  return pageNum === 0 ? '' : `/page-${pageNum + 1}`;
}

// Turn one big page into a series of paginated pages.
function paginatePages(page, files, field, layout, ariaLabel, perPage) {
  perPage = perPage || 10;

  if (typeof ariaLabel === 'undefined') {
    ariaLabel = '';
  } else {
    ariaLabel = ` of ${ariaLabel}`;
  }

  const pagedEntities = _.chunk(page[field].entities, perPage);
  for (let pageNum = 0; pageNum < pagedEntities.length; pageNum++) {
    const pagedPage = Object.assign({}, page);
    pagedPage.pagedItems = pagedEntities[pageNum];
    const innerPages = [];

    if (pagedEntities.length > 1) {
      // add page numbers
      const numPageLinks = 3;
      let start;
      let length;
      if (pagedEntities.length <= numPageLinks) {
        start = 0;
        length = pagedEntities.length;
      } else {
        length = numPageLinks;

        if (pageNum + numPageLinks > pagedEntities.length) {
          start = pagedEntities.length - numPageLinks;
        } else {
          start = pageNum;
        }
      }
      for (let num = start; num < start + length; num++) {
        innerPages.push({
          href:
            num === pageNum
              ? null
              : `${page.entityUrl.path}${paginationPath(num)}`,
          label: num + 1,
          class: num === pageNum ? 'va-pagination-active' : '',
        });
      }

      pagedPage.paginator = {
        ariaLabel,
        prev:
          pageNum > 0
            ? `${page.entityUrl.path}${paginationPath(pageNum - 1)}`
            : null,
        inner: innerPages,
        next:
          pageNum < pagedEntities.length - 1
            ? `${page.entityUrl.path}${paginationPath(pageNum + 1)}`
            : null,
      };
    }

    files[
      `drupal${page.entityUrl.path}${paginationPath(pageNum)}/index.html`
    ] = createFileObj(pagedPage, layout);
  }
}

// Return page object with path, breadcrump and title set.
function createEntityUrl(page, drupalPagePath, title) {
  const pathSuffix = title.replace(/\s+/g, '-').toLowerCase();
  const generatedPage = Object.assign({}, page);
  generatedPage.entityUrl.breadcrumb.push({
    url: { path: drupalPagePath },
    text: page.title,
  });
  generatedPage.entityUrl.path = `${drupalPagePath}/${pathSuffix}`;
  generatedPage.title = title;
  return generatedPage;
}

// Creates the top-level health care region list pages (Locations, Services, etc.)
function createHealthCareRegionListPages(page, drupalPagePath, files) {
  // Create the top-level locations page for Health Care Regions
  files[`drupal${drupalPagePath}/locations/index.html`] = createFileObj(
    page,
    'health_care_region_locations_page.drupal.liquid',
  );

  // Press Release listing page
  const prPage = createEntityUrl(page, drupalPagePath, 'Press Releases');
  paginatePages(
    prPage,
    files,
    'allPressReleaseTeasers',
    'press_releases_page.drupal.liquid',
    'press releases',
  );

  const relatedLinks = { fieldRelatedLinks: page.fieldRelatedLinks };

  // Create the detail page for healthcare local facilities
  if (page.mainFacilities !== undefined || page.otherFacilities !== undefined) {
    for (const facility of [
      ...page.mainFacilities.entities,
      ...page.otherFacilities.entities,
    ]) {
      if (facility.entityBundle === 'health_care_local_facility') {
        const facilityCompiled = Object.assign(facility, relatedLinks);

        const pagePath = facilityLocationPath(
          drupalPagePath,
          facility.fieldFacilityLocatorApiId,
          facility.fieldNicknameForThisFacility,
        );
        files[`drupal${pagePath}/index.html`] = createFileObj(
          facilityCompiled,
          'health_care_local_facility_page.drupal.liquid',
        );
      }
    }
  }

  files[`drupal${drupalPagePath}/health-services/index.html`] = createFileObj(
    page,
    'health_care_region_health_services_page.drupal.liquid',
  );
}

function pipeDrupalPagesIntoMetalsmith(contentData, files) {
  const {
    data: {
      nodeQuery: { entities: pages },
      sidebarQuery: sidebarNav,
      alerts: alertsItem,
    },
  } = contentData;

  const sidebarNavItems = { sidebar: sidebarNav };
  const alertItems = { alert: alertsItem };

  for (const page of pages) {
    // At this time, null values are returned for pages that are not yet published.
    // Once the Content-Preview server is up and running, then unpublished pages should
    // reliably return like any other page and we can delete this.
    if (!page) {
      log('Skipping null entity...');
      continue;
    }

    if (!Object.keys(page).length) {
      log('Skipping empty entity...');
      continue;
    }

    const {
      entityUrl: { path: drupalPagePath },
      entityBundle,
    } = page;

    const pageIdRaw = parseInt(page.entityId, 10);
    const pageId = { pid: pageIdRaw };
    let pageCompiled;

    if (entityBundle === 'page') {
      pageCompiled = Object.assign(page, sidebarNavItems, alertItems, pageId);
    } else {
      pageCompiled = Object.assign(page, alertItems, pageId);
    }

    files[`drupal${drupalPagePath}/index.html`] = createFileObj(
      pageCompiled,
      `${entityBundle}.drupal.liquid`,
    );

    if (page.entityBundle === 'health_care_region_page') {
      createHealthCareRegionListPages(pageCompiled, drupalPagePath, files);
    }
  }

  writeDrupalDebugPage(files);
  files[`drupal/index.md`] = {
    ...files['index.md'],
    path: 'drupal/index.html',
    isDrupalPage: true,
    private: true,
  };
}

async function loadDrupal(buildOptions) {
  const drupalCache = path.join(
    buildOptions.cacheDirectory,
    DRUPAL_CACHE_FILENAME,
  );
  const isDrupalAvailableInCache = fs.existsSync(drupalCache);

  let shouldPullDrupal = buildOptions[PULL_DRUPAL_BUILD_ARG];
  let drupalPages = null;

  if (!isDrupalAvailableInCache) {
    log('Drupal content unavailable in cache');
    shouldPullDrupal = true;
  }

  if (shouldPullDrupal) {
    log('Attempting to load Drupal content from API...');

    const contentApi = getApiClient(buildOptions);

    drupalPages = await contentApi.getAllPages();

    if (buildOptions.buildtype === ENVIRONMENTS.LOCALHOST) {
      const serialized = Buffer.from(JSON.stringify(drupalPages, null, 2));
      fs.ensureDirSync(buildOptions.cacheDirectory);
      fs.writeFileSync(drupalCache, serialized);
    }
  } else {
    log('Attempting to load Drupal content from cache...');
    log(`To pull latest, run with "--${PULL_DRUPAL_BUILD_ARG}" flag.`);

    // eslint-disable-next-line import/no-dynamic-require
    drupalPages = require(drupalCache);
  }

  log('Drupal successfully loaded!');
  return drupalPages;
}

function getDrupalContent(buildOptions) {
  if (!ENABLED_ENVIRONMENTS.has(buildOptions.buildtype)) {
    log(`Drupal integration disabled for buildtype ${buildOptions.buildtype}`);
    const noop = () => {};
    return noop;
  }

  // Declared above the middleware scope so that it's cached during the watch task.
  let drupalData = null;

  return async (files, metalsmith, done) => {
    try {
      if (!drupalData) {
        drupalData = await loadDrupal(buildOptions);
      }
      pipeDrupalPagesIntoMetalsmith(drupalData, files);
      log('Successfully piped Drupal content into Metalsmith!');
      done();
    } catch (err) {
      log(err.stack);
      log('Failed to pipe Drupal content into Metalsmith!');
      log('Continuing with build anyway...');
      // done(err);
      done();
    }
  };
}

module.exports = getDrupalContent;
<|endoftext|>


<commit_before>var dashing = require('dashing-js');

// Set your auth token here
//dashing.auth_token = 'YOUR_AUTH_TOKEN';

/*
dashing._protected = function(req, res, next) {
  // Put any authentication code you want in here.
  // This method is run before accessing any resource.
  // if (true) next();
}
*/

// Set your default dashboard here
//default_dashboard = 'mydashboard';

dashing.start();
<commit_msg>Fixed typo<commit_after>var dashing = require('dashing-js');

// Set your auth token here
//dashing.auth_token = 'YOUR_AUTH_TOKEN';

/*
dashing._protected = function(req, res, next) {
  // Put any authentication code you want in here.
  // This method is run before accessing any resource.
  // if (true) next();
}
*/

// Set your default dashboard here
//dashing.default_dashboard = 'mydashboard';

dashing.start();
<|endoftext|>


<commit_before>/*
 * Video.js Hotkeys
 * https://github.com/ctd1500/videojs-hotkeys
 *
 * Copyright (c) 2014 Chris Dougherty
 * Licensed under the Apache-2.0 license.
 */

(function(window, videojs) {
  'use strict';

  var hotkeys = function(options) {
    var player = this;
    var def_options = {
      volumeStep: 0.1,
      seekStep: 5,
      enableMute: true
    };
    options = options || {};

    // Set default player tabindex to handle keydown events
    if (!player.el().hasAttribute('tabIndex')) {
      player.el().setAttribute('tabIndex', '-1');
    }

    player.on('keydown', function(event) {
      var player = this;
      var volumeStep = options.volumeStep || def_options.volumeStep;
      var seekStep = options.seekStep || def_options.seekStep;
      var enableMute = options.enableMute || def_options.enableMute;

      // When controls are disabled, hotkeys will be disabled as well
      if (player.controls()) {

        // Don't catch keys if any control buttons are focused
        if (document.activeElement == player.el() ||
            document.activeElement == player.el().querySelector('.vjs-tech')) {

          // Spacebar toggles play/pause
          if (event.which === 32) {
            event.preventDefault();
            if (player.paused()) {
              player.play();
            } else {
              player.pause();
            }
          }

          // Seeking with the left/right arrow keys
          else if (event.which == 37) { // Left Arrow
            event.preventDefault();
            var curTime = player.currentTime() - seekStep;

            // The flash player tech will allow you to seek into negative
            // numbers and break the seekbar, so try to prevent that.
            if (player.currentTime() <= seekStep) {
              curTime = 0;
            }
          } else if (event.which == 39) { // Right Arrow
            event.preventDefault();
            player.currentTime(player.currentTime() + seekStep);
          }

          // Volume control with the up/down arrow keys
          else if (event.which == 40) { // Down Arrow
            event.preventDefault();
            player.volume(player.volume() - volumeStep);
          } else if (event.which == 38) { // Up Arrow
            event.preventDefault();
            player.volume(player.volume() + volumeStep);
          }

          // Toggle Mute with the M key
          else if (event.which == 77) {
            if (enableMute) {
              if (player.muted()) {
                player.muted(false);
              } else {
                player.muted(true);
              }
            }
          }
        }
      }
    });

    return this;
  };

  videojs.plugin('hotkeys', hotkeys);

})(window, window.videojs);
<commit_msg>Adding fullscreen hotkey<commit_after>/*
 * Video.js Hotkeys
 * https://github.com/ctd1500/videojs-hotkeys
 *
 * Copyright (c) 2014 Chris Dougherty
 * Licensed under the Apache-2.0 license.
 */

(function(window, videojs) {
  'use strict';

  var hotkeys = function(options) {
    var player = this;
    var def_options = {
      volumeStep: 0.1,
      seekStep: 5,
      enableMute: true,
      enableFullscreen: true
    };
    options = options || {};

    // Set default player tabindex to handle keydown events
    if (!player.el().hasAttribute('tabIndex')) {
      player.el().setAttribute('tabIndex', '-1');
    }

    player.on('keydown', function(event) {
      var player = this;
      var volumeStep = options.volumeStep || def_options.volumeStep;
      var seekStep = options.seekStep || def_options.seekStep;
      var enableMute = options.enableMute || def_options.enableMute;
      var enableFull = options.enableFullscreen || def_options.enableFullscreen;

      // When controls are disabled, hotkeys will be disabled as well
      if (player.controls()) {

        // Don't catch keys if any control buttons are focused
        if (document.activeElement == player.el() ||
            document.activeElement == player.el().querySelector('.vjs-tech')) {

          // Spacebar toggles play/pause
          if (event.which === 32) {
            event.preventDefault();
            if (player.paused()) {
              player.play();
            } else {
              player.pause();
            }
          }

          // Seeking with the left/right arrow keys
          else if (event.which == 37) { // Left Arrow
            event.preventDefault();
            var curTime = player.currentTime() - seekStep;

            // The flash player tech will allow you to seek into negative
            // numbers and break the seekbar, so try to prevent that.
            if (player.currentTime() <= seekStep) {
              curTime = 0;
            }
            player.currentTime(curTime);
          } else if (event.which == 39) { // Right Arrow
            event.preventDefault();
            player.currentTime(player.currentTime() + seekStep);
          }

          // Volume control with the up/down arrow keys
          else if (event.which == 40) { // Down Arrow
            event.preventDefault();
            player.volume(player.volume() - volumeStep);
          } else if (event.which == 38) { // Up Arrow
            event.preventDefault();
            player.volume(player.volume() + volumeStep);
          }

          // Toggle Mute with the M key
          else if (event.which == 77) {
            if (enableMute) {
              if (player.muted()) {
                player.muted(false);
              } else {
                player.muted(true);
              }
            }
          }

          // Toggle Fullscreen with the F key
          else if (event.which == 70) {
            if (enableFull) {
              if (player.isFullscreen()) {
                player.exitFullscreen();
              } else {
                player.requestFullscreen();
              }
            }
          }
        }
      }
    });

    return this;
  };

  videojs.plugin('hotkeys', hotkeys);

})(window, window.videojs);
<|endoftext|>