<commit_before>import DinoSheet from 'dinosheets';

function cssText(rule) {
  let style = rule.style.cssText;
  style = style.replace(/\s*$/, '');
  if (style.charAt(style.length - 1) !== ';') {
    style = style + ';';
  }
  return style.toLowerCase();
}

var sheet;

module('DinoSheet', {
  beforeEach() {
    sheet = new DinoSheet();
  },
  afterEach() {
    sheet.destroy();
  }
});

test('it appends a new stylesheet to the dom', function (assert) {
  assert.ok(sheet.id.match(/^dinosheet-\d+$/));
  assert.equal(sheet.element.id, sheet.id);
  const head = document.getElementsByTagName('head')[0] ||
               document.documentElement;
  assert.equal(sheet.element.parentElement, head);
  assert.equal(sheet.element.tagName.toLowerCase(), 'style');
});

test('it interprets nested styles like SCSS', function (assert) {
  sheet.css('body', {
    '.world': {
      '.hello &': {
        color: 'green',
      },
      '&.goodbye': {
        color: 'red'
      }
    }
  });

  let diff = sheet.diff();
  assert.equal(diff.length, 2);
  assert.equal(diff[0].selector, '.hello body .world');
  assert.equal(diff[1].selector, 'body .world.goodbye');
});

test('it applies the changes to the stylesheet after calling applyStyles', function (assert) {
  sheet.css('body', {
    '.world': {
      '.hello &': {
        fontSize: '10px'
      },
      '&.goodbye': {
        width: '20px',
        height: '50px'
      }
    }
  }).applyStyles();

  let rule = sheet.styleSheet.ruleFor('.hello body .world').rule;
  assert.equal(cssText(rule), 'font-size: 10px;')
  rule = sheet.styleSheet.ruleFor('body .world.goodbye').rule;
  assert.equal(cssText(rule), 'width: 20px; height: 50px;');
});

test('changes can be discarded', function (assert) {
  sheet.css('body', {
    '.world': {
      '.hello &': {
        fontSize: '10px'
      },
      '&.goodbye': {
        width: '20px',
        height: '50px'
      }
    }
  });

  let diff = sheet.diff();
  assert.equal(diff.length, 2);
  sheet.discardChanges();
  diff = sheet.diff();
  assert.equal(diff.length, 0);
});

test('selectors can be deleted', function (assert) {
  sheet.css('.tool-tip', {
    position: 'relative',
    '.pointer': {
      position: 'absolute',
      width: '20px',
      height: '10px',
      top: '0px',
      left: '50%',
      marginLeft: '-5px'
    }
  }).applyStyles();

  debugger;
  sheet.css('.tool-tip .pointer', null);

  assert.deepEqual(sheet.diff(), [{
    selector: '.tool-tip .pointer',
    rules: undefined
  }]);
  sheet.applyStyles();
  let rule = sheet.styleSheet.ruleFor('.tool-tip .pointer');
  assert.equal(rule, null);
});

test('rules can be deleted', function (assert) {
  sheet.css('.tool-tip', {
    position: 'relative',
    '.pointer': {
      position: 'absolute',
      width: '20px',
      height: '10px',
      top: '0px',
      left: '50%',
      marginLeft: '-5px'
    }
  }).applyStyles();

  sheet.css('.tool-tip .pointer', {
    marginLeft: null
  });

  assert.deepEqual(sheet.diff(), [{
    selector: '.tool-tip .pointer',
    rules: {
      marginLeft: null
    }
  }]);

  sheet.applyStyles();
  let rule = sheet.styleSheet.ruleFor('.tool-tip .pointer').rule;
  assert.equal(cssText(rule), 'position: absolute; width: 20px; height: 10px; top: 0px; left: 50%;');
});

test('rules can be updated', function (assert) {
  sheet.css('.tool-tip', {
    position: 'relative',
    '.pointer': {
      position: 'absolute',
      width: '20px',
      height: '10px',
      top: '0px',
      left: '50%',
      marginLeft: '-5px'
    }
  }).applyStyles();

  sheet.css('.tool-tip .pointer', {
    width: '10px',
    height: '20px',
    top: '50%',
    left: '-10px',
    marginLeft: null,
    marginTop: '-10px'
  });

  assert.deepEqual(sheet.diff(), [{
    selector: '.tool-tip .pointer',
    rules: {
      width: '10px',
      height: '20px',
      top: '50%',
      left: '-10px',
      marginLeft: null,
      marginTop: '-10px'
    }
  }]);

  sheet.applyStyles();
  let rule = sheet.styleSheet.ruleFor('.tool-tip .pointer').rule;
  assert.equal(cssText(rule), 'position: absolute; width: 10px; height: 20px; top: 50%; left: -10px; margin-top: -10px;');
});

test('destroying the sheet removes all styles from the dom', function (assert) {
  sheet.css('.tool-tip', {
    position: 'relative',
    '.pointer': {
      position: 'absolute',
      width: '20px',
      height: '10px',
      top: '0px',
      left: '50%',
      marginLeft: '-5px'
    }
  }).applyStyles();

  let id = sheet.id;
  sheet.destroy();
  assert.equal(document.getElementById(id), null);
  assert.equal(sheet.element, null);
});
<commit_msg>check for unordered rules for IE compatibility<commit_after>import DinoSheet from 'dinosheets';

function cssText(rule) {
  let style = rule.style.cssText;
  style = style.replace(/\s*$/, '');
  if (style.charAt(style.length - 1) !== ';') {
    style = style + ';';
  }
  return style.toLowerCase();
}

function cssRules(rule) {
  let style = rule.style.cssText;
  style = style.replace(/\s*$/, '');
  if (style.charAt(style.length - 1) === ';') {
    style = style.slice(0, -1);
  }
  return style.toLowerCase().split('; ');
}

QUnit.assert.contains = function (haystack, needle, message) {
  var actual = haystack.indexOf(needle) > -1;
  console.log(haystack, needle);
  this.push(actual, actual, needle, message);
};

var sheet;

module('DinoSheet', {
  beforeEach() {
    sheet = new DinoSheet();
  },
  afterEach() {
    sheet.destroy();
  }
});

test('it appends a new stylesheet to the dom', function (assert) {
  assert.ok(sheet.id.match(/^dinosheet-\d+$/));
  assert.equal(sheet.element.id, sheet.id);
  const head = document.getElementsByTagName('head')[0] ||
               document.documentElement;
  assert.equal(sheet.element.parentElement, head);
  assert.equal(sheet.element.tagName.toLowerCase(), 'style');
});

test('it interprets nested styles like SCSS', function (assert) {
  sheet.css('body', {
    '.world': {
      '.hello &': {
        color: 'green',
      },
      '&.goodbye': {
        color: 'red'
      }
    }
  });

  let diff = sheet.diff();
  assert.equal(diff.length, 2);
  assert.equal(diff[0].selector, '.hello body .world');
  assert.equal(diff[1].selector, 'body .world.goodbye');
});

test('it applies the changes to the stylesheet after calling applyStyles', function (assert) {
  sheet.css('body', {
    '.world': {
      '.hello &': {
        fontSize: '10px'
      },
      '&.goodbye': {
        width: '20px',
        height: '50px'
      }
    }
  }).applyStyles();

  let rule = sheet.styleSheet.ruleFor('.hello body .world').rule;
  assert.equal(cssText(rule), 'font-size: 10px;')
  rule = sheet.styleSheet.ruleFor('body .world.goodbye').rule;
  assert.equal(cssText(rule), 'width: 20px; height: 50px;');
});

test('changes can be discarded', function (assert) {
  sheet.css('body', {
    '.world': {
      '.hello &': {
        fontSize: '10px'
      },
      '&.goodbye': {
        width: '20px',
        height: '50px'
      }
    }
  });

  let diff = sheet.diff();
  assert.equal(diff.length, 2);
  sheet.discardChanges();
  diff = sheet.diff();
  assert.equal(diff.length, 0);
});

test('selectors can be deleted', function (assert) {
  sheet.css('.tool-tip', {
    position: 'relative',
    '.pointer': {
      position: 'absolute',
      width: '20px',
      height: '10px',
      top: '0px',
      left: '50%',
      marginLeft: '-5px'
    }
  }).applyStyles();

  sheet.css('.tool-tip .pointer', null);

  assert.deepEqual(sheet.diff(), [{
    selector: '.tool-tip .pointer',
    rules: undefined
  }]);
  sheet.applyStyles();
  let rule = sheet.styleSheet.ruleFor('.tool-tip .pointer');
  assert.equal(rule, null);
});

test('rules can be deleted', function (assert) {
  sheet.css('.tool-tip', {
    position: 'relative',
    '.pointer': {
      position: 'absolute',
      width: '20px',
      height: '10px',
      top: '0px',
      left: '50%',
      marginLeft: '-5px'
    }
  }).applyStyles();

  sheet.css('.tool-tip .pointer', {
    marginLeft: null
  });

  assert.deepEqual(sheet.diff(), [{
    selector: '.tool-tip .pointer',
    rules: {
      marginLeft: null
    }
  }]);

  sheet.applyStyles();
  let rule = sheet.styleSheet.ruleFor('.tool-tip .pointer').rule;
  let rules = cssRules(rule);
  assert.contains(rules, 'position: absolute');
  assert.contains(rules, 'width: 20px');
  assert.contains(rules, 'height: 10px');
  assert.contains(rules, 'top: 0px');
  assert.contains(rules, 'left: 50%');
  assert.equal(rules.length, 5);
});

test('rules can be updated', function (assert) {
  sheet.css('.tool-tip', {
    position: 'relative',
    '.pointer': {
      position: 'absolute',
      width: '20px',
      height: '10px',
      top: '0px',
      left: '50%',
      marginLeft: '-5px'
    }
  }).applyStyles();

  sheet.css('.tool-tip .pointer', {
    width: '10px',
    height: '20px',
    top: '50%',
    left: '-10px',
    marginLeft: null,
    marginTop: '-10px'
  });

  assert.deepEqual(sheet.diff(), [{
    selector: '.tool-tip .pointer',
    rules: {
      width: '10px',
      height: '20px',
      top: '50%',
      left: '-10px',
      marginLeft: null,
      marginTop: '-10px'
    }
  }]);

  sheet.applyStyles();
  let rule = sheet.styleSheet.ruleFor('.tool-tip .pointer').rule;
  let rules = cssRules(rule);
  assert.contains(rules, 'position: absolute');
  assert.contains(rules, 'width: 10px');
  assert.contains(rules, 'height: 20px');
  assert.contains(rules, 'top: 50%');
  assert.contains(rules, 'left: -10px');
  assert.contains(rules, 'margin-top: -10px');
});

test('destroying the sheet removes all styles from the dom', function (assert) {
  sheet.css('.tool-tip', {
    position: 'relative',
    '.pointer': {
      position: 'absolute',
      width: '20px',
      height: '10px',
      top: '0px',
      left: '50%',
      marginLeft: '-5px'
    }
  }).applyStyles();

  let id = sheet.id;
  sheet.destroy();
  assert.equal(document.getElementById(id), null);
  assert.equal(sheet.element, null);
});
<|endoftext|>


<commit_before>'use strict';

angular.module('mgcrea.ngStrap.select', ['mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.helpers.parseOptions'])

  .provider('$select', function() {

    var defaults = this.defaults = {
      animation: 'am-fade',
      prefixClass: 'select',
      prefixEvent: '$select',
      placement: 'bottom-left',
      template: 'select/select.tpl.html',
      trigger: 'focus',
      container: false,
      keyboard: true,
      html: false,
      delay: 0,
      multiple: false,
      allNoneButtons: false,
      sort: true,
      caretHtml: ' ',
      placeholder: 'Choose among the following...',
      allText: 'All',
      noneText: 'None',
      maxLength: 3,
      maxLengthHtml: 'selected',
      iconCheckmark: 'glyphicon glyphicon-ok'
    };

    this.$get = function($window, $document, $rootScope, $tooltip, $timeout) {

      var bodyEl = angular.element($window.document.body);
      var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
      var isTouch = ('createTouch' in $window.document) && isNative;

      function SelectFactory(element, controller, config) {

        var $select = {};

        // Common vars
        var options = angular.extend({}, defaults, config);

        // parse sort option value to support attribute as string
        // when binded to interpolated value
        options.sort = options.sort.toString().match(/true|1/i);

        $select = $tooltip(element, options);
        var scope = $select.$scope;

        scope.$matches = [];
        scope.$activeIndex = 0;
        scope.$isMultiple = options.multiple;
        scope.$showAllNoneButtons = options.allNoneButtons && options.multiple;
        scope.$iconCheckmark = options.iconCheckmark;
        scope.$allText = options.allText;
        scope.$noneText = options.noneText;

        scope.$activate = function(index) {
          scope.$$postDigest(function() {
            $select.activate(index);
          });
        };

        scope.$select = function(index, evt) {
          scope.$$postDigest(function() {
            $select.select(index);
          });
        };

        scope.$isVisible = function() {
          return $select.$isVisible();
        };

        scope.$isActive = function(index) {
          return $select.$isActive(index);
        };

        scope.$selectAll = function () {
          for (var i = 0; i < scope.$matches.length; i++) {
            if (!scope.$isActive(i)) {
              scope.$select(i);
            }
          }
        };

        scope.$selectNone = function () {
          for (var i = 0; i < scope.$matches.length; i++) {
            if (scope.$isActive(i)) {
              scope.$select(i);
            }
          }
        };

        // Public methods

        $select.update = function(matches) {
          scope.$matches = matches;
          $select.$updateActiveIndex();
        };

        $select.activate = function(index) {
          if(options.multiple) {
            $select.$isActive(index) ? scope.$activeIndex.splice(scope.$activeIndex.indexOf(index), 1) : scope.$activeIndex.push(index);
            if(options.sort) scope.$activeIndex.sort();
          } else {
            scope.$activeIndex = index;
          }
          return scope.$activeIndex;
        };

        $select.select = function(index) {
          var value = scope.$matches[index].value;
          scope.$apply(function() {
            $select.activate(index);
            if(options.multiple) {
              controller.$setViewValue(scope.$activeIndex.map(function(index) {
                return scope.$matches[index].value;
              }));
            } else {
              controller.$setViewValue(value);
              // Hide if single select
              $select.hide();
            }
          });
          // Emit event
          scope.$emit(options.prefixEvent + '.select', value, index, $select);
        };

        // Protected methods

        $select.$updateActiveIndex = function() {
          if(controller.$modelValue && scope.$matches.length) {
            if(options.multiple && angular.isArray(controller.$modelValue)) {
              scope.$activeIndex = controller.$modelValue.map(function(value) {
                return $select.$getIndex(value);
              });
            } else {
              scope.$activeIndex = $select.$getIndex(controller.$modelValue);
            }
          } else if(scope.$activeIndex >= scope.$matches.length) {
            scope.$activeIndex = options.multiple ? [] : 0;
          }
        };

        $select.$isVisible = function() {
          if(!options.minLength || !controller) {
            return scope.$matches.length;
          }
          // minLength support
          return scope.$matches.length && controller.$viewValue.length >= options.minLength;
        };

        $select.$isActive = function(index) {
          if(options.multiple) {
            return scope.$activeIndex.indexOf(index) !== -1;
          } else {
            return scope.$activeIndex === index;
          }
        };

        $select.$getIndex = function(value) {
          var l = scope.$matches.length, i = l;
          if(!l) return;
          for(i = l; i--;) {
            if(scope.$matches[i].value === value) break;
          }
          if(i < 0) return;
          return i;
        };

        $select.$onMouseDown = function(evt) {
          // Prevent blur on mousedown on .dropdown-menu
          evt.preventDefault();
          evt.stopPropagation();
          // Emulate click for mobile devices
          if(isTouch) {
            var targetEl = angular.element(evt.target);
            targetEl.triggerHandler('click');
          }
        };

        $select.$onKeyDown = function(evt) {
          if (!/(9|13|38|40)/.test(evt.keyCode)) return;
          evt.preventDefault();
          evt.stopPropagation();

          // Select with enter
          if(!options.multiple && (evt.keyCode === 13 || evt.keyCode === 9)) {
            return $select.select(scope.$activeIndex);
          }

          // Navigate with keyboard
          if(evt.keyCode === 38 && scope.$activeIndex > 0) scope.$activeIndex--;
          else if(evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1) scope.$activeIndex++;
          else if(angular.isUndefined(scope.$activeIndex)) scope.$activeIndex = 0;
          scope.$digest();
        };

        // Overrides

        var _show = $select.show;
        $select.show = function() {
          _show();
          if(options.multiple) {
            $select.$element.addClass('select-multiple');
          }
          // use timeout to hookup the events to prevent
          // event bubbling from being processed imediately.
          $timeout(function() {
            $select.$element.on(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);
            if(options.keyboard) {
              element.on('keydown', $select.$onKeyDown);
            }
          }, 0, false);
        };

        var _hide = $select.hide;
        $select.hide = function() {
          $select.$element.off(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);
          if(options.keyboard) {
            element.off('keydown', $select.$onKeyDown);
          }
          _hide(true);
        };

        return $select;

      }

      SelectFactory.defaults = defaults;
      return SelectFactory;

    };

  })

  .directive('bsSelect', function($window, $parse, $q, $select, $parseOptions) {

    var defaults = $select.defaults;

    return {
      restrict: 'EAC',
      require: 'ngModel',
      link: function postLink(scope, element, attr, controller) {

        // Directive options
        var options = {scope: scope, placeholder: defaults.placeholder};
        angular.forEach(['placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'template', 'placeholder', 'multiple', 'allNoneButtons', 'maxLength', 'maxLengthHtml', 'allText', 'noneText', 'iconCheckmark', 'autoClose', 'id', 'sort', 'caretHtml'], function(key) {
          if(angular.isDefined(attr[key])) options[key] = attr[key];
        });

        // Add support for select markup
        if(element[0].nodeName.toLowerCase() === 'select') {
          var inputEl = element;
          inputEl.css('display', 'none');
          element = angular.element('<button type="button" class="btn btn-default"></button>');
          inputEl.after(element);
        }

        // Build proper ngOptions
        var parsedOptions = $parseOptions(attr.ngOptions);

        // Initialize select
        var select = $select(element, controller, options);

        // Watch ngOptions values before filtering for changes
        var watchedOptions = parsedOptions.$match[7].replace(/\|.+/, '').trim();
        scope.$watch(watchedOptions, function(newValue, oldValue) {
          // console.warn('scope.$watch(%s)', watchedOptions, newValue, oldValue);
          parsedOptions.valuesFn(scope, controller)
          .then(function(values) {
            select.update(values);
            controller.$render();
          });
        }, true);

        // Watch model for changes
        scope.$watch(attr.ngModel, function(newValue, oldValue) {
          // console.warn('scope.$watch(%s)', attr.ngModel, newValue, oldValue);
          select.$updateActiveIndex();
          controller.$render();
        }, true);

        // Model rendering in view
        controller.$render = function () {
          // console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
          var selected, index;
          if(options.multiple && angular.isArray(controller.$modelValue)) {
            selected = controller.$modelValue.map(function(value) {
              index = select.$getIndex(value);
              return angular.isDefined(index) ? select.$scope.$matches[index].label : false;
            }).filter(angular.isDefined);
            if(selected.length > (options.maxLength || defaults.maxLength)) {
              selected = selected.length + ' ' + (options.maxLengthHtml || defaults.maxLengthHtml);
            } else {
              selected = selected.join(', ');
            }
          } else {
            index = select.$getIndex(controller.$modelValue);
            selected = angular.isDefined(index) ? select.$scope.$matches[index].label : false;
          }
          element.html((selected ? selected : options.placeholder) + (options.caretHtml ? options.caretHtml : defaults.caretHtml));
        };

        if(options.multiple){
          controller.$isEmpty = function(value){
            return !value || value.length === 0;
          };
        }

        // Garbage collection
        scope.$on('$destroy', function() {
          if (select) select.destroy();
          options = null;
          select = null;
        });

      }
    };

  });
<commit_msg>fix(select): allow tab to navigate to next element on multiple select element<commit_after>'use strict';

angular.module('mgcrea.ngStrap.select', ['mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.helpers.parseOptions'])

  .provider('$select', function() {

    var defaults = this.defaults = {
      animation: 'am-fade',
      prefixClass: 'select',
      prefixEvent: '$select',
      placement: 'bottom-left',
      template: 'select/select.tpl.html',
      trigger: 'focus',
      container: false,
      keyboard: true,
      html: false,
      delay: 0,
      multiple: false,
      allNoneButtons: false,
      sort: true,
      caretHtml: ' ',
      placeholder: 'Choose among the following...',
      allText: 'All',
      noneText: 'None',
      maxLength: 3,
      maxLengthHtml: 'selected',
      iconCheckmark: 'glyphicon glyphicon-ok'
    };

    this.$get = function($window, $document, $rootScope, $tooltip, $timeout) {

      var bodyEl = angular.element($window.document.body);
      var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
      var isTouch = ('createTouch' in $window.document) && isNative;

      function SelectFactory(element, controller, config) {

        var $select = {};

        // Common vars
        var options = angular.extend({}, defaults, config);

        // parse sort option value to support attribute as string
        // when binded to interpolated value
        options.sort = options.sort.toString().match(/true|1/i);

        $select = $tooltip(element, options);
        var scope = $select.$scope;

        scope.$matches = [];
        scope.$activeIndex = 0;
        scope.$isMultiple = options.multiple;
        scope.$showAllNoneButtons = options.allNoneButtons && options.multiple;
        scope.$iconCheckmark = options.iconCheckmark;
        scope.$allText = options.allText;
        scope.$noneText = options.noneText;

        scope.$activate = function(index) {
          scope.$$postDigest(function() {
            $select.activate(index);
          });
        };

        scope.$select = function(index, evt) {
          scope.$$postDigest(function() {
            $select.select(index);
          });
        };

        scope.$isVisible = function() {
          return $select.$isVisible();
        };

        scope.$isActive = function(index) {
          return $select.$isActive(index);
        };

        scope.$selectAll = function () {
          for (var i = 0; i < scope.$matches.length; i++) {
            if (!scope.$isActive(i)) {
              scope.$select(i);
            }
          }
        };

        scope.$selectNone = function () {
          for (var i = 0; i < scope.$matches.length; i++) {
            if (scope.$isActive(i)) {
              scope.$select(i);
            }
          }
        };

        // Public methods

        $select.update = function(matches) {
          scope.$matches = matches;
          $select.$updateActiveIndex();
        };

        $select.activate = function(index) {
          if(options.multiple) {
            $select.$isActive(index) ? scope.$activeIndex.splice(scope.$activeIndex.indexOf(index), 1) : scope.$activeIndex.push(index);
            if(options.sort) scope.$activeIndex.sort();
          } else {
            scope.$activeIndex = index;
          }
          return scope.$activeIndex;
        };

        $select.select = function(index) {
          var value = scope.$matches[index].value;
          scope.$apply(function() {
            $select.activate(index);
            if(options.multiple) {
              controller.$setViewValue(scope.$activeIndex.map(function(index) {
                return scope.$matches[index].value;
              }));
            } else {
              controller.$setViewValue(value);
              // Hide if single select
              $select.hide();
            }
          });
          // Emit event
          scope.$emit(options.prefixEvent + '.select', value, index, $select);
        };

        // Protected methods

        $select.$updateActiveIndex = function() {
          if(controller.$modelValue && scope.$matches.length) {
            if(options.multiple && angular.isArray(controller.$modelValue)) {
              scope.$activeIndex = controller.$modelValue.map(function(value) {
                return $select.$getIndex(value);
              });
            } else {
              scope.$activeIndex = $select.$getIndex(controller.$modelValue);
            }
          } else if(scope.$activeIndex >= scope.$matches.length) {
            scope.$activeIndex = options.multiple ? [] : 0;
          }
        };

        $select.$isVisible = function() {
          if(!options.minLength || !controller) {
            return scope.$matches.length;
          }
          // minLength support
          return scope.$matches.length && controller.$viewValue.length >= options.minLength;
        };

        $select.$isActive = function(index) {
          if(options.multiple) {
            return scope.$activeIndex.indexOf(index) !== -1;
          } else {
            return scope.$activeIndex === index;
          }
        };

        $select.$getIndex = function(value) {
          var l = scope.$matches.length, i = l;
          if(!l) return;
          for(i = l; i--;) {
            if(scope.$matches[i].value === value) break;
          }
          if(i < 0) return;
          return i;
        };

        $select.$onMouseDown = function(evt) {
          // Prevent blur on mousedown on .dropdown-menu
          evt.preventDefault();
          evt.stopPropagation();
          // Emulate click for mobile devices
          if(isTouch) {
            var targetEl = angular.element(evt.target);
            targetEl.triggerHandler('click');
          }
        };

        $select.$onKeyDown = function(evt) {
          if (!/(9|13|38|40)/.test(evt.keyCode)) return;
          evt.preventDefault();
          evt.stopPropagation();

          // release focus on tab
          if (options.multiple && evt.keyCode === 9) {
            return $select.hide();
          }

          // Select with enter
          if(!options.multiple && (evt.keyCode === 13 || evt.keyCode === 9)) {
            return $select.select(scope.$activeIndex);
          }

          // Navigate with keyboard
          if(evt.keyCode === 38 && scope.$activeIndex > 0) scope.$activeIndex--;
          else if(evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1) scope.$activeIndex++;
          else if(angular.isUndefined(scope.$activeIndex)) scope.$activeIndex = 0;
          scope.$digest();
        };

        // Overrides

        var _show = $select.show;
        $select.show = function() {
          _show();
          if(options.multiple) {
            $select.$element.addClass('select-multiple');
          }
          // use timeout to hookup the events to prevent
          // event bubbling from being processed imediately.
          $timeout(function() {
            $select.$element.on(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);
            if(options.keyboard) {
              element.on('keydown', $select.$onKeyDown);
            }
          }, 0, false);
        };

        var _hide = $select.hide;
        $select.hide = function() {
          $select.$element.off(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);
          if(options.keyboard) {
            element.off('keydown', $select.$onKeyDown);
          }
          _hide(true);
        };

        return $select;

      }

      SelectFactory.defaults = defaults;
      return SelectFactory;

    };

  })

  .directive('bsSelect', function($window, $parse, $q, $select, $parseOptions) {

    var defaults = $select.defaults;

    return {
      restrict: 'EAC',
      require: 'ngModel',
      link: function postLink(scope, element, attr, controller) {

        // Directive options
        var options = {scope: scope, placeholder: defaults.placeholder};
        angular.forEach(['placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'template', 'placeholder', 'multiple', 'allNoneButtons', 'maxLength', 'maxLengthHtml', 'allText', 'noneText', 'iconCheckmark', 'autoClose', 'id', 'sort', 'caretHtml'], function(key) {
          if(angular.isDefined(attr[key])) options[key] = attr[key];
        });

        // Add support for select markup
        if(element[0].nodeName.toLowerCase() === 'select') {
          var inputEl = element;
          inputEl.css('display', 'none');
          element = angular.element('<button type="button" class="btn btn-default"></button>');
          inputEl.after(element);
        }

        // Build proper ngOptions
        var parsedOptions = $parseOptions(attr.ngOptions);

        // Initialize select
        var select = $select(element, controller, options);

        // Watch ngOptions values before filtering for changes
        var watchedOptions = parsedOptions.$match[7].replace(/\|.+/, '').trim();
        scope.$watch(watchedOptions, function(newValue, oldValue) {
          // console.warn('scope.$watch(%s)', watchedOptions, newValue, oldValue);
          parsedOptions.valuesFn(scope, controller)
          .then(function(values) {
            select.update(values);
            controller.$render();
          });
        }, true);

        // Watch model for changes
        scope.$watch(attr.ngModel, function(newValue, oldValue) {
          // console.warn('scope.$watch(%s)', attr.ngModel, newValue, oldValue);
          select.$updateActiveIndex();
          controller.$render();
        }, true);

        // Model rendering in view
        controller.$render = function () {
          // console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
          var selected, index;
          if(options.multiple && angular.isArray(controller.$modelValue)) {
            selected = controller.$modelValue.map(function(value) {
              index = select.$getIndex(value);
              return angular.isDefined(index) ? select.$scope.$matches[index].label : false;
            }).filter(angular.isDefined);
            if(selected.length > (options.maxLength || defaults.maxLength)) {
              selected = selected.length + ' ' + (options.maxLengthHtml || defaults.maxLengthHtml);
            } else {
              selected = selected.join(', ');
            }
          } else {
            index = select.$getIndex(controller.$modelValue);
            selected = angular.isDefined(index) ? select.$scope.$matches[index].label : false;
          }
          element.html((selected ? selected : options.placeholder) + (options.caretHtml ? options.caretHtml : defaults.caretHtml));
        };

        if(options.multiple){
          controller.$isEmpty = function(value){
            return !value || value.length === 0;
          };
        }

        // Garbage collection
        scope.$on('$destroy', function() {
          if (select) select.destroy();
          options = null;
          select = null;
        });

      }
    };

  });
<|endoftext|>


<commit_before>/**
 * 水平方向居中容器
 * @class BI.HorizontalAdaptLayout
 * @extends BI.Layout
 */
BI.HorizontalAdaptLayout = BI.inherit(BI.Layout, {
    props: function () {
        return BI.extend(BI.HorizontalAdaptLayout.superclass.props.apply(this, arguments), {
            baseCls: "bi-h-a",
            verticalAlign: BI.VerticalAlign.Top,
            horizontalAlign: BI.HorizontalAlign.Center,
            columnSize: [],
            scrollx: false,
            hgap: 0,
            vgap: 0,
            lgap: 0,
            rgap: 0,
            tgap: 0,
            bgap: 0
        });
    },
    render: function () {
        var self = this, o = this.options;
        BI.HorizontalAdaptLayout.superclass.render.apply(this, arguments);
        return {
            type: "bi.horizontal",
            verticalAlign: o.verticalAlign,
            horizontalAlign: o.horizontalAlign,
            columnSize: o.columnSize,
            items: o.items,
            scrollx: o.scrollx,
            scrolly: o.scrolly,
            scrollable: o.scrollable,
            ref: function (_ref) {
                self.layout = _ref;
            },
            hgap: o.hgap,
            vgap: o.vgap,
            lgap: o.lgap,
            rgap: o.rgap,
            tgap: o.tgap,
            bgap: o.bgap
        };
    },

    resize: function () {
        // console.log("horizontal_adapt布局不需要resize");
    },

    populate: function (items) {
        this.layout.populate.apply(this, arguments);
    }
});
BI.shortcut("bi.horizontal_adapt", BI.HorizontalAdaptLayout);
<commit_msg>DEC-18016 fix: horizontal_adapt默认horizontalAlign为left<commit_after>/**
 * 水平方向居中容器
 * @class BI.HorizontalAdaptLayout
 * @extends BI.Layout
 */
BI.HorizontalAdaptLayout = BI.inherit(BI.Layout, {
    props: function () {
        return BI.extend(BI.HorizontalAdaptLayout.superclass.props.apply(this, arguments), {
            baseCls: "bi-h-a",
            verticalAlign: BI.VerticalAlign.Top,
            horizontalAlign: BI.HorizontalAlign.Left,
            columnSize: [],
            scrollx: false,
            hgap: 0,
            vgap: 0,
            lgap: 0,
            rgap: 0,
            tgap: 0,
            bgap: 0
        });
    },
    render: function () {
        var self = this, o = this.options;
        BI.HorizontalAdaptLayout.superclass.render.apply(this, arguments);
        return {
            type: "bi.horizontal",
            verticalAlign: o.verticalAlign,
            horizontalAlign: o.horizontalAlign,
            columnSize: o.columnSize,
            items: o.items,
            scrollx: o.scrollx,
            scrolly: o.scrolly,
            scrollable: o.scrollable,
            ref: function (_ref) {
                self.layout = _ref;
            },
            hgap: o.hgap,
            vgap: o.vgap,
            lgap: o.lgap,
            rgap: o.rgap,
            tgap: o.tgap,
            bgap: o.bgap
        };
    },

    resize: function () {
        // console.log("horizontal_adapt布局不需要resize");
    },

    populate: function (items) {
        this.layout.populate.apply(this, arguments);
    }
});
BI.shortcut("bi.horizontal_adapt", BI.HorizontalAdaptLayout);
<|endoftext|>


<commit_before>'use strict';

var TaskList = require('../POMs/TasksList.js');

describe('Check that task name can be updated', function() {

  beforeAll(function() {
    TaskList.actionsBeforeAll();
  });

  it('Adding new task', function() {
    TaskList.addTask('protractor test task1');
  });

  it('Updating the task name and verifying that it is saved', function() {
    TaskList.taskEditing(0, 'updated task name');
  });

  afterAll(function() {
    //Deleting all existing tasks before each spec to avoid conflicts
    TaskList.clearAllTasks();
  });

});

<commit_msg>Add timeout to instable test<commit_after>'use strict';

var TaskList = require('../POMs/TasksList.js');

describe('Check that task name can be updated', function() {

  beforeAll(function() {
    TaskList.actionsBeforeAll();
  });

  it('Adding new task', function() {
    TaskList.addTask('protractor test task1');

    //wait untill all task will be loaded
    browser.sleep(config.config.jasmineNodeOpts.defaultTimeoutInterval / 10);
  });

  it('Updating the task name and verifying that it is saved', function() {
    TaskList.taskEditing(0, 'updated task name');
  });

  afterAll(function() {
    //Deleting all existing tasks before each spec to avoid conflicts
    TaskList.clearAllTasks();
  });

});

<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.u2.detail',
  name: 'SectionedDetailPropertyView',
  extends: 'foam.u2.View',

  documentation: `
    View for one property of a SectionedDetailView.
  `,

  requires: [
    'foam.u2.layout.Rows',
    'foam.u2.layout.Cols',
    'foam.u2.layout.Col'
  ],

  properties: [
    'prop',
  ],

  methods: [
    function initE() {
      var self = this;
      this.SUPER();

      this
        .addClass(this.myClass())
        .add(this.slot(function(prop) {
          var errorSlot = self.slot(function(data, prop$validateObj){
            return data && prop$validateObj && data.slot(prop$validateObj);
          });
          return self.E()
            .start(self.Rows)
              .add(prop.label$)
              .start(self.Cols, { alignmentType: foam.u2.layout.AlignmentType.START })
                .add(prop)
                .callIf(prop.help, function() {
                  this.start({class: 'foam.u2.tag.Image', data: 'images/question-icon.svg'})
                    .attrs({ title: prop.help })
                  .end();
                })
              .end()
              .add(errorSlot.map((s) => {
                return self.E().add(s);
              }));
        }));
    }
  ]
});
<commit_msg>Fixed contentJustification according to the new layouts package refactor<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.u2.detail',
  name: 'SectionedDetailPropertyView',
  extends: 'foam.u2.View',

  documentation: `
    View for one property of a SectionedDetailView.
  `,

  requires: [
    'foam.u2.layout.Rows',
    'foam.u2.layout.Cols',
    'foam.u2.layout.Col'
  ],

  properties: [
    'prop',
  ],

  methods: [
    function initE() {
      var self = this;
      this.SUPER();

      this
        .addClass(this.myClass())
        .add(this.slot(function(prop) {
          var errorSlot = self.slot(function(data, prop$validateObj){
            return data && prop$validateObj && data.slot(prop$validateObj);
          });
          return self.E()
            .start(self.Rows)
              .add(prop.label$)
              .start(self.Cols, { contentJustification: foam.u2.layout.ContentJustification.START })
                .add(prop)
                .callIf(prop.help, function() {
                  this.start({class: 'foam.u2.tag.Image', data: 'images/question-icon.svg'})
                    .attrs({ title: prop.help })
                  .end();
                })
              .end()
              .add(errorSlot.map((s) => {
                return self.E().add(s);
              }));
        }));
    }
  ]
});
<|endoftext|>


<commit_before>import React from 'react'
import {mount} from 'enzyme'
import toJson from 'enzyme-to-json'
import CustomTooltip from './CustomTooltip'

describe('<CustomTooltip />', () => {
  it('should mount in a full DOM with rain information', () => {
    const wrapper = mount(
      <CustomTooltip
        payload={
        [
          {
            momentId: '2017:8:14:4',
            timespan: 1505401200,
            temp: 23,
            weatherIconCode: 500,
            rain: 0.15,
            day: 'Thu, Sep 14',
            hour: '05 PM'
          }
        ]}
    />
    )
    expect(toJson(wrapper)).toMatchSnapshot()
  })

  it('should mount in a full DOM without rain information', () => {
    const wrapper = mount(
      <CustomTooltip
        payload={
        [
          {
            momentId: '2017:8:14:4',
            timespan: 1505401200,
            temp: 23,
            weatherIconCode: 500,
            rain: 'N/A',
            day: 'Thu, Sep 14',
            hour: '05 PM'
          }
        ]}
    />
    )
    expect(toJson(wrapper)).toMatchSnapshot()
  })
})
<commit_msg>Add test cases<commit_after>import React from 'react'
import {mount} from 'enzyme'
import sinon from 'sinon'
import toJson from 'enzyme-to-json'
import CustomTooltip from './CustomTooltip'

describe('<CustomTooltip />', () => {
  it('calls render', () => {
    sinon.spy(CustomTooltip.prototype, 'render')
    expect(CustomTooltip.prototype.render.calledOnce).toEqual(true)
  })

  it('should mount in a full DOM with rain information', () => {
    const wrapper = mount(
      <CustomTooltip
        payload={
        [
          {
            momentId: '2017:8:14:4',
            timespan: 1505401200,
            temp: 23,
            weatherIconCode: 500,
            rain: 0.15,
            day: 'Thu, Sep 14',
            hour: '05 PM'
          }
        ]}
    />
    )
    expect(toJson(wrapper)).toMatchSnapshot()
  })

  it('should mount in a full DOM without rain information', () => {
    const wrapper = mount(
      <CustomTooltip
        payload={
        [
          {
            momentId: '2017:8:14:4',
            timespan: 1505401200,
            temp: 23,
            weatherIconCode: 500,
            rain: 'N/A',
            day: 'Thu, Sep 14',
            hour: '05 PM'
          }
        ]}
    />
    )
    expect(toJson(wrapper)).toMatchSnapshot()
  })
})
<|endoftext|>


<commit_before>var fs   = require('fs'),
    path = require('path'),
    glob = require('glob');

var settings = {
	web_root     : '/js/',
	include_file : 'client_require.js',
	app_root     : process.env.PWD,
	env          : process.env.NODE_ENV,
	uglify       : true
}, require_paths = [], initPaths = [];

function config(k,v) {
	settings[k] = v;
}

var REQUIRE_METH = '__fhtagn__.req';

var scripts = false, scriptCallbacks = [], scriptPending = false;
function getScripts(cb) {

	if (!cb) return;

	if (scripts) return cb(false, scripts);

	scriptCallbacks.push(cb);

	if (scriptPending) return;

	scriptPending = true;

	var modules = [], pending = 0;

	function unpend() {
		if (pending===0) {
			scriptPending = false;
			scripts = modules;
			var s = scriptCallbacks.shift();
			while(s) {
				s(null,modules);
				s = scriptCallbacks.shift();
			}
		}
	}

	function contextualize(p,modulePath) {

		var file, mod, web, index, rpath,
			fpath   = p.split('/');

		function rel(f) {
			return path.relative(settings.app_root,f).replace(/\.\.\//g, '');
		}

		if (fpath[fpath.length-2]=="server"||fpath[fpath.length-2]=="client") {
			fpath[fpath.length-2] = "client";
			file = fpath.join('/');
			fpath.splice(-2,1);
			mod = fpath.join('/');
		} else {
			file = p;
			mod  = p;
		}

		file  = file;
		mod   = rel(mod);
		web   = settings.web_root+rel(file);
		index = (modulePath) ? (rel(modulePath) || "__index__") : false;
		rpath = rel(p);

		return {file:file, module:mod, web:web, relative:rpath, index: index};

	}

	function grabFile(file, modulePath, initPath) {
		var paths = contextualize(file,modulePath);
		var module_path = modulePath || paths.module;
		pending++;
		fs.readFile(paths.file,'utf-8',function (err, f){
			pending--;
			modules.push({
				content: f,
				module_path: paths.module,
				file_path: paths.file,
				web_path: paths.web,
				relative_path: paths.relative,
				index_path: paths.index
			});
			if (initPath) initPaths.push(paths.module);
			unpend();
		});
	}

	function walk(p,initPath) {
		p = path.normalize(p);
		pending++;
		fs.readFile(path.join(p, 'package.json'), 'utf-8', function(e,d) {

			if (e) throw e;
			pending--;
			d = JSON.parse(d || "{}");
			var deps  = d.client_dependencies || [],
				index = d.main;
			if (!index.match(/\.js$/)) index += '.js';
			deps.forEach(function(dep) {
				walk(path.join(p, 'node_modules', dep));
			});

			grabFile(path.join(p, index), p, initPath);

			if (!d.client_require) return unpend();
			pending++;

			// Glob up everything else in the path.
			glob(path.join(p, "**", "*.js"), function(e,files) {
				pending--;
				if (!files) return unpend();
				files.forEach(function(file) {
					// If we went through all the node modules recursively as if
					// they were all full client-side requires, we'd end up with
					// a crapload of test code and server-side stuff loading on
					// the client.
					if (file.match(new RegExp('^'+p+'/node_modules'))) {
						return;
					}
					// Explicitly ignore things in /server/.
					if (file.match(/\/server\//)) return;
					grabFile(file);
				});
				unpend();
			});
		});
	}

	for (var p in require_paths) walk(require_paths[p], true);
	walk(settings.app_root, true);

}

var packed = false, packCallbacks = [], packPending = false;
function packScripts(scripts,cb) {

	if (!cb) return;

	if (packed) return cb(false, packed);

	packCallbacks.push(cb);

	if (packPending) return;

	packPending = true;

	// Separating the manifest from the files allows us to load each file once
	// without losing its module path aliases.
	var files = {}, manifest = {};

	function callback(e) {
		packed = files;
		var p = packCallbacks.shift();
		while(p) {
			p(null,packed);
			p = packCallbacks.shift();
		}
	}

	var moduleTemplate = path.join(__dirname, '..', 'templates', 'module.js');
	var indexFile;

	fs.readFile(moduleTemplate, 'utf-8', function(e,tmp) {
		tmp = tmp.replace(/\s+/g, " ");
		files[settings.web_root+'fhtagn.js'] = function(fun) {
			var fhtagn = path.join(__dirname, '..', 'client', 'fhtagn.js');
			fs.readFile(fhtagn,'utf-8',function (err, txt){
				fun(null,txt);
			});
		};
		scripts.forEach(function(f) {
			if (!manifest[f.web_path]) manifest[f.web_path] = [];
			manifest[f.web_path].push(f.module_path);
			if (f.module_path != f.relative_path) {
				manifest[f.web_path].push(f.relative_path);
			}
			if (f.index_path) {
				manifest[f.web_path].push(f.index_path);
			}
			if (f.module_path=="__index__") indexFile = true;
			var pack = function(cb) {
				fs.readFile(f.file_path,'utf-8',function (err, d){
					if (err) throw "Error loading file "+f.file_path+': '+e;
					var o = {};
					o.content = '\n'+d+'\n';
					o.module_path    = JSON.stringify(manifest[f.web_path]);
					o.canonical_path = path.dirname(f.relative_path);
					if (o.caninical_path = '.') o.caninical_path = "";
					o.canonical_path = JSON.stringify(o.canonical_path);
					var c = tmp.replace(/{{(\w*)}}/g, function(m,k) {
						return o[k] || '';
					});
					cb(err, c);
				});
			};
			files[f.web_path] = pack;
		});
		callback();
	});

}

function getInitCode() {
	var out = "";
	initPaths.forEach(function(p) {
		out += '\n'+REQUIRE_METH+'("'+p+'");';
	});
	return out;
}

var compiled = false, compilerCallbacks = [], compiling = false;
function compileScripts(cb) {

	if (compiled) return cb(null, compiled);
	if (cb) compilerCallbacks.push(cb);
	if (compiling) return;

	compiling = true;

	function callback(e) {
		compiling = false;
		var p = compilerCallbacks.shift();
		while(p) {
			p(null,packed);
			p = compilerCallbacks.shift();
		}
	}

	getScripts(function(e,scripts) {

		packScripts(scripts, function(e,files) {

			var q = [], modules = "";

			for (var f in files) q.push(files[f]);
			function next() {
				var f = q.shift();
				if (!f) return serveModules();
				f(function(e,d) {
					modules += d;
					next();
				});
			} next();

			function serveModules() {
				var app = path.join(
					__dirname, '..', 'templates', 'global_closure.js'
				), fhtagn = path.join(
					__dirname, '..', 'client', 'fhtagn.js'
				);
				fs.readFile(app, 'utf-8', function(e,tmp) {
					fs.readFile(fhtagn, 'utf-8', function(e,fhtagn) {
						if (e) return cb(e);
						tmp = tmp.split("{{fhtagn.js}}").join(fhtagn);
						tmp = tmp.split("{{modules}}").join(modules);
						tmp = tmp.split("{{initialize}}").join(getInitCode());
						if (settings.uglify) {
							//Compress with UglifyJs
							var jsp  = require("uglify-js").parser,
								pro  = require("uglify-js").uglify,
								code = jsp.parse(tmp);
							code = pro.ast_mangle(code);
							code = pro.ast_squeeze(code);
							tmp  = pro.gen_code(code);
						}
						compiled = tmp;
						callback();
					});
				});
			}

		});

	});

	function callback(e) {
		compiling = false;
		var p = compilerCallbacks.shift();
		while(p) {
			p(null,packed);
			p = compilerCallbacks.shift();
		}
	}

}

function new_require(p) {

	if (!p.match(/^(\/|\\)/)) {
		p = path.normalize(path.join(path.dirname(module.parent.id),p));
	}

	var mpath = path.dirname(module.parent.filename),
		sp    = mpath.replace(/server/, '')+p;

	//If the module doesn't exist, check in the server/ path.
	if (!path.existsSync(p) && path.existsSync(sp)) p = sp;

	return module.parent.require(p);

}

function getSrcs(cb) {
	getScripts(function(e,scripts) {
		packScripts(scripts,function(e,d) {
			var srcs = [];
			for (var k in d) srcs.push(k);
			cb(null, srcs);
		});
	});
}

function serveScript(p, cb) {

	p = settings.web_root+p;

	var is_app = (p==settings.web_root+settings.include_file);

	getScripts(function(e,scripts) {

		packScripts(scripts, function(e,files) {

			if (settings.env!="production") {

				if (is_app) {
					var tmp = path.join(__dirname, '..', 'templates', 'client_require.js'),
						lab = path.join(__dirname, '..', 'templates', 'LAB.js');
					getSrcs(function(e,srcs) {
						var includes = "$LAB";
						srcs.forEach(function(src) {
							includes += ".script('"+src+"')\n";
						});
						fs.readFile(tmp, 'utf-8', function(e,tmp) {
							tmp = tmp.replace('{{LABScripts}}', includes);
							var inits = "";
							tmp = tmp.replace('{{initialize}}', getInitCode());
							fs.readFile(lab, 'utf-8', function(e,f) {
								tmp = tmp.replace('{{LAB.js}}', f);
								cb(null, tmp);
							});
						});
					});
					return;
				}

				if (files[p]) {
					files[p](function(e,d) { cb(e,d); });
				} else {
					cb(e||"File not found");
				}

			} else {

				if (is_app) return compileScripts(cb);

			}

		});

	});

}

function handle(req,res) {

	var url  = require('url').parse(req.url).path,
		patt = '^'+settings.web_root.replace('/', '\/')+'(.*)$',
		m    = url.match(new RegExp(patt));

	if (!m) return false;

	serveScript(m[1], function(e,d) {
		if (e) {
			res.writeHead('404');
			res.end("File not found");
		} else {
			res.writeHead('200');
			res.end(d);
		}
	});

	return true;

}

function listen(server) {

	server.on('request', function (req,res) {
		handle(req, res);
	});

}

function connect_server() {

	return function(req, res, next) {
		if (!handle(req,res)) next();
	}

}

function getIncludePath() {
	return settings.web_root+settings.include_file;
}

function add_path(p) {
	require_paths.push(p);
}

if (settings.env=="production") {
	compileScripts();
}

module.exports = {
	set      : config,
	require  : new_require,
	connect  : connect_server,
	add_path : add_path,
	get_src  : getIncludePath,
	handle   : handle
};<commit_msg>Added missing semicolon.<commit_after>var fs   = require('fs'),
    path = require('path'),
    glob = require('glob');

var settings = {
	web_root     : '/js/',
	include_file : 'client_require.js',
	app_root     : process.env.PWD,
	env          : process.env.NODE_ENV,
	uglify       : true
}, require_paths = [], initPaths = [];

function config(k,v) {
	settings[k] = v;
}

var REQUIRE_METH = '__fhtagn__.req';

var scripts = false, scriptCallbacks = [], scriptPending = false;
function getScripts(cb) {

	if (!cb) return;

	if (scripts) return cb(false, scripts);

	scriptCallbacks.push(cb);

	if (scriptPending) return;

	scriptPending = true;

	var modules = [], pending = 0;

	function unpend() {
		if (pending===0) {
			scriptPending = false;
			scripts = modules;
			var s = scriptCallbacks.shift();
			while(s) {
				s(null,modules);
				s = scriptCallbacks.shift();
			}
		}
	}

	function contextualize(p,modulePath) {

		var file, mod, web, index, rpath,
			fpath   = p.split('/');

		function rel(f) {
			return path.relative(settings.app_root,f).replace(/\.\.\//g, '');
		}

		if (fpath[fpath.length-2]=="server"||fpath[fpath.length-2]=="client") {
			fpath[fpath.length-2] = "client";
			file = fpath.join('/');
			fpath.splice(-2,1);
			mod = fpath.join('/');
		} else {
			file = p;
			mod  = p;
		}

		file  = file;
		mod   = rel(mod);
		web   = settings.web_root+rel(file);
		index = (modulePath) ? (rel(modulePath) || "__index__") : false;
		rpath = rel(p);

		return {file:file, module:mod, web:web, relative:rpath, index: index};

	}

	function grabFile(file, modulePath, initPath) {
		var paths = contextualize(file,modulePath);
		var module_path = modulePath || paths.module;
		pending++;
		fs.readFile(paths.file,'utf-8',function (err, f){
			pending--;
			modules.push({
				content: f,
				module_path: paths.module,
				file_path: paths.file,
				web_path: paths.web,
				relative_path: paths.relative,
				index_path: paths.index
			});
			if (initPath) initPaths.push(paths.module);
			unpend();
		});
	}

	function walk(p,initPath) {
		p = path.normalize(p);
		pending++;
		fs.readFile(path.join(p, 'package.json'), 'utf-8', function(e,d) {

			if (e) throw e;
			pending--;
			d = JSON.parse(d || "{}");
			var deps  = d.client_dependencies || [],
				index = d.main;
			if (!index.match(/\.js$/)) index += '.js';
			deps.forEach(function(dep) {
				walk(path.join(p, 'node_modules', dep));
			});

			grabFile(path.join(p, index), p, initPath);

			if (!d.client_require) return unpend();
			pending++;

			// Glob up everything else in the path.
			glob(path.join(p, "**", "*.js"), function(e,files) {
				pending--;
				if (!files) return unpend();
				files.forEach(function(file) {
					// If we went through all the node modules recursively as if
					// they were all full client-side requires, we'd end up with
					// a crapload of test code and server-side stuff loading on
					// the client.
					if (file.match(new RegExp('^'+p+'/node_modules'))) {
						return;
					}
					// Explicitly ignore things in /server/.
					if (file.match(/\/server\//)) return;
					grabFile(file);
				});
				unpend();
			});
		});
	}

	for (var p in require_paths) walk(require_paths[p], true);
	walk(settings.app_root, true);

}

var packed = false, packCallbacks = [], packPending = false;
function packScripts(scripts,cb) {

	if (!cb) return;

	if (packed) return cb(false, packed);

	packCallbacks.push(cb);

	if (packPending) return;

	packPending = true;

	// Separating the manifest from the files allows us to load each file once
	// without losing its module path aliases.
	var files = {}, manifest = {};

	function callback(e) {
		packed = files;
		var p = packCallbacks.shift();
		while(p) {
			p(null,packed);
			p = packCallbacks.shift();
		}
	}

	var moduleTemplate = path.join(__dirname, '..', 'templates', 'module.js');
	var indexFile;

	fs.readFile(moduleTemplate, 'utf-8', function(e,tmp) {
		tmp = tmp.replace(/\s+/g, " ");
		files[settings.web_root+'fhtagn.js'] = function(fun) {
			var fhtagn = path.join(__dirname, '..', 'client', 'fhtagn.js');
			fs.readFile(fhtagn,'utf-8',function (err, txt){
				fun(null,txt);
			});
		};
		scripts.forEach(function(f) {
			if (!manifest[f.web_path]) manifest[f.web_path] = [];
			manifest[f.web_path].push(f.module_path);
			if (f.module_path != f.relative_path) {
				manifest[f.web_path].push(f.relative_path);
			}
			if (f.index_path) {
				manifest[f.web_path].push(f.index_path);
			}
			if (f.module_path=="__index__") indexFile = true;
			var pack = function(cb) {
				fs.readFile(f.file_path,'utf-8',function (err, d){
					if (err) throw "Error loading file "+f.file_path+': '+e;
					var o = {};
					o.content = '\n'+d+'\n';
					o.module_path    = JSON.stringify(manifest[f.web_path]);
					o.canonical_path = path.dirname(f.relative_path);
					if (o.caninical_path = '.') o.caninical_path = "";
					o.canonical_path = JSON.stringify(o.canonical_path);
					var c = tmp.replace(/{{(\w*)}}/g, function(m,k) {
						return o[k] || '';
					});
					cb(err, c);
				});
			};
			files[f.web_path] = pack;
		});
		callback();
	});

}

function getInitCode() {
	var out = "";
	initPaths.forEach(function(p) {
		out += '\n'+REQUIRE_METH+'("'+p+'");';
	});
	return out;
}

var compiled = false, compilerCallbacks = [], compiling = false;
function compileScripts(cb) {

	if (compiled) return cb(null, compiled);
	if (cb) compilerCallbacks.push(cb);
	if (compiling) return;

	compiling = true;

	function callback(e) {
		compiling = false;
		var p = compilerCallbacks.shift();
		while(p) {
			p(null,packed);
			p = compilerCallbacks.shift();
		}
	}

	getScripts(function(e,scripts) {

		packScripts(scripts, function(e,files) {

			var q = [], modules = "";

			for (var f in files) q.push(files[f]);
			function next() {
				var f = q.shift();
				if (!f) return serveModules();
				f(function(e,d) {
					modules += d;
					next();
				});
			} next();

			function serveModules() {
				var app = path.join(
					__dirname, '..', 'templates', 'global_closure.js'
				), fhtagn = path.join(
					__dirname, '..', 'client', 'fhtagn.js'
				);
				fs.readFile(app, 'utf-8', function(e,tmp) {
					fs.readFile(fhtagn, 'utf-8', function(e,fhtagn) {
						if (e) return cb(e);
						tmp = tmp.split("{{fhtagn.js}}").join(fhtagn);
						tmp = tmp.split("{{modules}}").join(modules);
						tmp = tmp.split("{{initialize}}").join(getInitCode());
						if (settings.uglify) {
							//Compress with UglifyJs
							var jsp  = require("uglify-js").parser,
								pro  = require("uglify-js").uglify,
								code = jsp.parse(tmp);
							code = pro.ast_mangle(code);
							code = pro.ast_squeeze(code);
							tmp  = pro.gen_code(code);
						}
						compiled = tmp;
						callback();
					});
				});
			}

		});

	});

	function callback(e) {
		compiling = false;
		var p = compilerCallbacks.shift();
		while(p) {
			p(null,packed);
			p = compilerCallbacks.shift();
		}
	}

}

function new_require(p) {

	if (!p.match(/^(\/|\\)/)) {
		p = path.normalize(path.join(path.dirname(module.parent.id),p));
	}

	var mpath = path.dirname(module.parent.filename),
		sp    = mpath.replace(/server/, '')+p;

	//If the module doesn't exist, check in the server/ path.
	if (!path.existsSync(p) && path.existsSync(sp)) p = sp;

	return module.parent.require(p);

}

function getSrcs(cb) {
	getScripts(function(e,scripts) {
		packScripts(scripts,function(e,d) {
			var srcs = [];
			for (var k in d) srcs.push(k);
			cb(null, srcs);
		});
	});
}

function serveScript(p, cb) {

	p = settings.web_root+p;

	var is_app = (p==settings.web_root+settings.include_file);

	getScripts(function(e,scripts) {

		packScripts(scripts, function(e,files) {

			if (settings.env!="production") {

				if (is_app) {
					var tmp = path.join(__dirname, '..', 'templates', 'client_require.js'),
						lab = path.join(__dirname, '..', 'templates', 'LAB.js');
					getSrcs(function(e,srcs) {
						var includes = "$LAB";
						srcs.forEach(function(src) {
							includes += ".script('"+src+"')\n";
						});
						fs.readFile(tmp, 'utf-8', function(e,tmp) {
							tmp = tmp.replace('{{LABScripts}}', includes);
							var inits = "";
							tmp = tmp.replace('{{initialize}}', getInitCode());
							fs.readFile(lab, 'utf-8', function(e,f) {
								tmp = tmp.replace('{{LAB.js}}', f);
								cb(null, tmp);
							});
						});
					});
					return;
				}

				if (files[p]) {
					files[p](function(e,d) { cb(e,d); });
				} else {
					cb(e||"File not found");
				}

			} else {

				if (is_app) return compileScripts(cb);

			}

		});

	});

}

function handle(req,res) {

	var url  = require('url').parse(req.url).path,
		patt = '^'+settings.web_root.replace('/', '\/')+'(.*)$',
		m    = url.match(new RegExp(patt));

	if (!m) return false;

	serveScript(m[1], function(e,d) {
		if (e) {
			res.writeHead('404');
			res.end("File not found");
		} else {
			res.writeHead('200');
			res.end(d);
		}
	});

	return true;

}

function listen(server) {

	server.on('request', function (req,res) {
		handle(req, res);
	});

}

function connect_server() {

	return function(req, res, next) {
		if (!handle(req,res)) next();
	};

}

function getIncludePath() {
	return settings.web_root+settings.include_file;
}

function add_path(p) {
	require_paths.push(p);
}

if (settings.env=="production") {
	compileScripts();
}

module.exports = {
	set      : config,
	require  : new_require,
	connect  : connect_server,
	add_path : add_path,
	get_src  : getIncludePath,
	handle   : handle
};<|endoftext|>


<commit_before>/* eslint no-console: ["error", { allow: ["log", "error"] }] */
import path from 'path';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import express from 'express';
import webpackConfig from '../../webpack.config.dev';
import ajaxProxyRouter from './lib/ajaxProxyRouter';
import morgan from 'morgan';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import passport from 'passport';
import { OAuth2Strategy as GoogleStrategy } from 'passport-google-oauth';
import authConfig from './auth/credentials';
import { upsert } from './lib/util';

delete process.env.BROWSER;

const isDeveloping = process.env.NODE_ENV !== 'production';
const port = isDeveloping ? 3000 : process.env.PORT;
const server = express();

// TODO: write tests for server.js

/**
 ******************************************
 passport stuff - TODO: Move into separate file
 ******************************************
 */
passport.serializeUser((user, done) => {
  const sessionUser = {
    first_name: user.first_name,
    last_name: user.last_name,
    email: user.email,
    photo_url: user.photo_url,
  };
  done(null, sessionUser);
});

passport.deserializeUser((sessionUser, done) => done(null, sessionUser));

passport.use(new GoogleStrategy(
  authConfig.google,
  (accessToken, refreshToken, profile, done) => {
    const params = {
      email: profile.emails[0].value,
      external_auth_type: 'google',
    };
    const data = {
      first_name: profile.name.givenName,
      last_name: profile.name.familyName,
      email: profile.emails.length && profile.emails[0].value,
      photo_url: profile.photos.length && profile.photos[0].value,
      external_auth_type: 'google',
      external_auth_id: profile.id,
    };

    upsert('/users', params, data)
      .then(resp => done(null, resp))
      .catch(err => done(err));
  }
));

/**
 ******************************************
 * helper functions
 ******************************************
 */
function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    next();
  }
  res.redirect('/');
}

/**
 ******************************************
 * middlewares
 ******************************************
 */

// TODO: Move log format into server config file
// Log requests to stdout
server.use(
  morgan(':remote-addr - - :date[clf] :method :url HTTP/:http-version :status -')
);

server.use(cookieParser());

// TODO: move session config into server config file
server.use(session({
  secret: 'some',
  resave: true,
  saveUninitialized: true,
  cookie: { maxAge: 2419200000 },
}));

server.use(passport.initialize());

server.use(passport.session());

/**
 ******************************************
 * user auth endpoints
 ******************************************
 */

server.get('/auth/google',
  passport.authenticate('google', { scope: ['openid email profile'] })
);

server.get('/auth/google/callback',
  passport.authenticate('google', { failureRedirect: '/' }),
  (req, res) => {
    res.redirect('/');
  }
);

server.get('/account', ensureAuthenticated, (req, res) => {
  res.send(req.user);
});

server.get('/logout', (req, res) => {
  req.session.destroy((err) => {
    if (err) {
      console.log(err);
    } else {
      res.redirect('/');
    }
  });
});

/**
 ******************************************
 * api/ping endpoints
 ******************************************
 */

server.use('/api', ajaxProxyRouter());
server.get('/ping', (req, res) => {
  res.header('Content-Type', 'text/plain');
  res.send(new Date().toISOString());
});

/**
 ******************************************
 * serving front-end application
 ******************************************
 */

if (isDeveloping) {
  console.log('DEVELOPMENT');
  const compiler = webpack(webpackConfig);
  const middleware = webpackDevMiddleware(compiler, {
    publicPath: webpackConfig.output.publicPath,
    // https://webpack.github.io/docs/webpack-dev-middleware.html
    noInfo: true,
    // for hot-reloading with docker
    watchOptions: {
      poll: true,
    },
    stats: {
      colors: true,
    },
  });

  server.use(middleware);
  server.use(webpackHotMiddleware(compiler));

  // serve static assets normally
  server.use(express.static(path.join(__dirname, '../app')));

  // handle every other route with index.html, which will
  // contain a script tag to your application's JavaScript
  // file(s).
  server.use('*', (req, res) => {
    res.sendFile(path.join(__dirname, '../app/index.html'));
  });
} else {
  console.log('PRODUCTION');
  server.use(express.static(path.join(__dirname, '../app')));
}

server.listen(port, '0.0.0.0', (err) => {
  if (err) {
    console.error(err);
  }
  console.log('Listening at http://0.0.0.0:%s/', port);
});
<commit_msg>WD-41 Added else statement in `ensureAuthenticated`<commit_after>/* eslint no-console: ["error", { allow: ["log", "error"] }] */
import path from 'path';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import express from 'express';
import webpackConfig from '../../webpack.config.dev';
import ajaxProxyRouter from './lib/ajaxProxyRouter';
import morgan from 'morgan';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import passport from 'passport';
import { OAuth2Strategy as GoogleStrategy } from 'passport-google-oauth';
import authConfig from './auth/credentials';
import { upsert } from './lib/util';

delete process.env.BROWSER;

const isDeveloping = process.env.NODE_ENV !== 'production';
const port = isDeveloping ? 3000 : process.env.PORT;
const server = express();

// TODO: write tests for server.js

/**
 ******************************************
 passport stuff - TODO: Move into separate file
 ******************************************
 */
passport.serializeUser((user, done) => {
  const sessionUser = {
    first_name: user.first_name,
    last_name: user.last_name,
    email: user.email,
    photo_url: user.photo_url,
  };
  done(null, sessionUser);
});

passport.deserializeUser((sessionUser, done) => done(null, sessionUser));

passport.use(new GoogleStrategy(
  authConfig.google,
  (accessToken, refreshToken, profile, done) => {
    const params = {
      email: profile.emails[0].value,
      external_auth_type: 'google',
    };
    const data = {
      first_name: profile.name.givenName,
      last_name: profile.name.familyName,
      email: profile.emails.length && profile.emails[0].value,
      photo_url: profile.photos.length && profile.photos[0].value,
      external_auth_type: 'google',
      external_auth_id: profile.id,
    };

    upsert('/users', params, data)
      .then(resp => done(null, resp))
      .catch(err => done(err));
  }
));

/**
 ******************************************
 * helper functions
 ******************************************
 */
function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    next();
  } else {
    res.redirect('/');
  }
}

/**
 ******************************************
 * middlewares
 ******************************************
 */

// TODO: Move log format into server config file
// Log requests to stdout
server.use(
  morgan(':remote-addr - - :date[clf] :method :url HTTP/:http-version :status -')
);

server.use(cookieParser());

// TODO: move session config into server config file
server.use(session({
  secret: 'some',
  resave: true,
  saveUninitialized: true,
  cookie: { maxAge: 2419200000 },
}));

server.use(passport.initialize());

server.use(passport.session());

/**
 ******************************************
 * user auth endpoints
 ******************************************
 */

server.get('/auth/google',
  passport.authenticate('google', { scope: ['openid email profile'] })
);

server.get('/auth/google/callback',
  passport.authenticate('google', { failureRedirect: '/' }),
  (req, res) => {
    res.redirect('/');
  }
);

server.get('/account', ensureAuthenticated, (req, res) => {
  res.send(req.user);
});

server.get('/logout', (req, res) => {
  req.session.destroy((err) => {
    if (err) {
      console.log(err);
    } else {
      res.redirect('/');
    }
  });
});

/**
 ******************************************
 * api/ping endpoints
 ******************************************
 */

server.use('/api', ajaxProxyRouter());
server.get('/ping', (req, res) => {
  res.header('Content-Type', 'text/plain');
  res.send(new Date().toISOString());
});

/**
 ******************************************
 * serving front-end application
 ******************************************
 */

if (isDeveloping) {
  console.log('DEVELOPMENT');
  const compiler = webpack(webpackConfig);
  const middleware = webpackDevMiddleware(compiler, {
    publicPath: webpackConfig.output.publicPath,
    // https://webpack.github.io/docs/webpack-dev-middleware.html
    noInfo: true,
    // for hot-reloading with docker
    watchOptions: {
      poll: true,
    },
    stats: {
      colors: true,
    },
  });

  server.use(middleware);
  server.use(webpackHotMiddleware(compiler));

  // serve static assets normally
  server.use(express.static(path.join(__dirname, '../app')));

  // handle every other route with index.html, which will
  // contain a script tag to your application's JavaScript
  // file(s).
  server.use('*', (req, res) => {
    res.sendFile(path.join(__dirname, '../app/index.html'));
  });
} else {
  console.log('PRODUCTION');
  server.use(express.static(path.join(__dirname, '../app')));
}

server.listen(port, '0.0.0.0', (err) => {
  if (err) {
    console.error(err);
  }
  console.log('Listening at http://0.0.0.0:%s/', port);
});
<|endoftext|>


<commit_before>require("babel-core/register");
require("babel-polyfill");

import path from 'path';
import express from 'express';
import cors from 'cors';
import morgan from 'morgan';
import db from './db';
import graphqlapi from './graphql-api';

// ====================
//  CREATE EXPRESS APP
// ====================

// Set up dotenv to read environment variables
require('dotenv').config();

console.log('\n** STARTING SERVER **');
console.log('NODE_ENV: ' + process.env.NODE_ENV);

// Create express app
const app = express();

// ============
//  MIDDLEWARE
// ============

//Connect to MongoDB
db.connect();

// Use morgan to log requests to console
if (process.env.NODE_ENV === 'development') {
	console.log('Using morgan.');
	app.use(morgan('dev'));
}

// Busboy body parser middleware
let busboyBodyParser = require('busboy-body-parser');
app.use(busboyBodyParser());

// Serve static files
app.use(express.static('public'));

// Allow CORS
app.use(cors());

// =========
//	ROUTING
// =========

// Route to API
app.use('/api', graphqlapi.router);

// Return the admin page
app.use('/admin', (req, res) => {
	res.sendFile(path.resolve(__dirname, '..', '..', 'public', 'admin.html'));
});

// Always return the main index.html, so react-router render the route in the client
app.get('*', (req, res) => {
  res.sendFile(path.resolve(__dirname, '..', '..', 'public', 'index.html'));
});

// ==========
//	SERVER
// ==========
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
	console.log("\nServer started on port " + PORT + "... \n");
});
<commit_msg>[ADD] Enable logging on all environments<commit_after>require("babel-core/register");
require("babel-polyfill");

import path from 'path';
import express from 'express';
import cors from 'cors';
import morgan from 'morgan';
import db from './db';
import graphqlapi from './graphql-api';

// ====================
//  CREATE EXPRESS APP
// ====================

// Set up dotenv to read environment variables
require('dotenv').config();

console.log('\n** STARTING SERVER **');
console.log('NODE_ENV: ' + process.env.NODE_ENV);

// Create express app
const app = express();

// ============
//  MIDDLEWARE
// ============

//Connect to MongoDB
db.connect();

// Use morgan to log requests to console
console.log('Using morgan.');
app.use(morgan('dev'));

// if (process.env.NODE_ENV === 'development') {
// 	console.log('Using morgan.');
// 	app.use(morgan('dev'));
// }

// Busboy body parser middleware
let busboyBodyParser = require('busboy-body-parser');
app.use(busboyBodyParser());

// Serve static files
app.use(express.static('public'));

// Allow CORS
app.use(cors());

// =========
//	ROUTING
// =========

// Route to API
app.use('/api', graphqlapi.router);

// Return the admin page
app.use('/admin', (req, res) => {
	res.sendFile(path.resolve(__dirname, '..', '..', 'public', 'admin.html'));
});

// Always return the main index.html, so react-router render the route in the client
app.get('*', (req, res) => {
  res.sendFile(path.resolve(__dirname, '..', '..', 'public', 'index.html'));
});

// ==========
//	SERVER
// ==========
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
	console.log("\nServer started on port " + PORT + "... \n");
});
<|endoftext|>