<commit_before>/**
 * @ngdoc module
 * @name material.components.checkbox
 * @description Checkbox module!
 */
angular
  .module('material.components.checkbox', ['material.core'])
  .directive('mdCheckbox', MdCheckboxDirective);

/**
 * @ngdoc directive
 * @name mdCheckbox
 * @module material.components.checkbox
 * @restrict E
 *
 * @description
 * The checkbox directive is used like the normal
 * [angular checkbox](https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D).
 *
 * As per the [Material Design spec](https://material.io/archive/guidelines/style/color.html#color-color-palette)
 * the checkbox is in the accent color by default. The primary color palette may be used with
 * the `md-primary` class.
 *
 * @param {expression} ng-model Assignable angular expression to data-bind to.
 * @param {string=} name Property name of the form under which the control is published.
 * @param {expression=} ng-true-value The value to which the expression should be set when selected.
 * @param {expression=} ng-false-value The value to which the expression should be set when not
 *    selected.
 * @param {expression=} ng-change Expression to be executed when the model value changes.
 * @param {boolean=} md-no-ink If present, disable ink ripple effects.
 * @param {string=} aria-label Adds label to checkbox for accessibility.
 *    Defaults to checkbox's text. If no default text is found, a warning will be logged.
 * @param {expression=} md-indeterminate This determines when the checkbox should be rendered as
 *    'indeterminate'. If a truthy expression or no value is passed in the checkbox renders in the
 *    md-indeterminate state. If falsy expression is passed in it just looks like a normal unchecked
 *    checkbox. The indeterminate, checked, and unchecked states are mutually exclusive. A box
 *    cannot be in any two states at the same time. Adding the 'md-indeterminate' attribute
 *    overrides any checked/unchecked rendering logic. When using the 'md-indeterminate' attribute
 *    use 'ng-checked' to define rendering logic instead of using 'ng-model'.
 * @param {expression=} ng-checked If this expression evaluates as truthy, the 'md-checked' css
 *    class is added to the checkbox and it will appear checked.
 *
 * @usage
 * <hljs lang="html">
 * <md-checkbox ng-model="isChecked" aria-label="Finished?">
 *   Finished ?
 * </md-checkbox>
 *
 * <md-checkbox md-no-ink ng-model="hasInk" aria-label="No Ink Effects">
 *   No Ink Effects
 * </md-checkbox>
 *
 * <md-checkbox ng-disabled="true" ng-model="isDisabled" aria-label="Disabled">
 *   Disabled
 * </md-checkbox>
 *
 * </hljs>
 *
 */
function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $mdUtil, $mdInteraction) {
  inputDirective = inputDirective[0];

  return {
    restrict: 'E',
    transclude: true,
    require: ['^?mdInputContainer', '?ngModel', '?^form'],
    priority: $mdConstant.BEFORE_NG_ARIA,
    template:
      '
' +
        '

' +
      '
' +
      '

',
    compile: compile
  };

  // **********************************************************
  // Private Methods
  // **********************************************************

  function compile (tElement, tAttrs) {
    tAttrs.$set('tabindex', tAttrs.tabindex || '0');
    tAttrs.$set('type', 'checkbox');
    tAttrs.$set('role', tAttrs.type);

    return  {
      pre: function(scope, element) {
        // Attach a click handler during preLink, in order to immediately stop propagation
        // (especially for ng-click) when the checkbox is disabled.
        element.on('click', function(e) {
          if (this.hasAttribute('disabled')) {
            e.stopImmediatePropagation();
          }
        });
      },
      post: postLink
    };

    function postLink(scope, element, attr, ctrls) {
      var isIndeterminate;
      var containerCtrl = ctrls[0];
      var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel();
      var formCtrl = ctrls[2];

      if (containerCtrl) {
        var isErrorGetter = containerCtrl.isErrorGetter || function() {
          return ngModelCtrl.$invalid && (ngModelCtrl.$touched || (formCtrl && formCtrl.$submitted));
        };

        containerCtrl.input = element;

        scope.$watch(isErrorGetter, containerCtrl.setInvalid);
      }

      $mdTheming(element);

      // Redirect focus events to the root element, because IE11 is always focusing the container element instead
      // of the md-checkbox element. This causes issues when using ngModelOptions: `updateOnBlur`
      element.children().on('focus', function() {
        element.focus();
      });

      if ($mdUtil.parseAttributeBoolean(attr.mdIndeterminate)) {
        setIndeterminateState();
        scope.$watch(attr.mdIndeterminate, setIndeterminateState);
      }

      if (attr.ngChecked) {
        scope.$watch(scope.$eval.bind(scope, attr.ngChecked), function(value) {
          ngModelCtrl.$setViewValue(value);
          ngModelCtrl.$render();
        });
      }

      $$watchExpr('ngDisabled', 'tabindex', {
        true: '-1',
        false: attr.tabindex
      });

      $mdAria.expectWithText(element, 'aria-label');

      // Reuse the original input[type=checkbox] directive from AngularJS core.
      // This is a bit hacky as we need our own event listener and own render
      // function.
      inputDirective.link.pre(scope, {
        on: angular.noop,
        0: {}
      }, attr, [ngModelCtrl]);

      element.on('click', listener)
        .on('keypress', keypressHandler)
        .on('focus', function() {
          if ($mdInteraction.getLastInteractionType() === 'keyboard') {
            element.addClass('md-focused');
          }
        })
        .on('blur', function() {
          element.removeClass('md-focused');
        });

      ngModelCtrl.$render = render;

      function $$watchExpr(expr, htmlAttr, valueOpts) {
        if (attr[expr]) {
          scope.$watch(attr[expr], function(val) {
            if (valueOpts[val]) {
              element.attr(htmlAttr, valueOpts[val]);
            }
          });
        }
      }

      function keypressHandler(ev) {
        var keyCode = ev.which || ev.keyCode;
        ev.preventDefault();
        switch(keyCode) {
          case $mdConstant.KEY_CODE.SPACE:
            element.addClass('md-focused');
            listener(ev);
            break;
          case $mdConstant.KEY_CODE.ENTER:
            var form = $mdUtil.getClosest(ev.target, 'form');
            // We have to use a native event here as the form directive does not use jqlite
            if (form) {
              form.dispatchEvent(new Event('submit'));
            }
            break;
        }
      }

      function listener(ev) {
        // skipToggle boolean is used by the switch directive to prevent the click event
        // when releasing the drag. There will be always a click if releasing the drag over the checkbox
        if (element[0].hasAttribute('disabled') || scope.skipToggle) {
          return;
        }

        scope.$apply(function() {
          // Toggle the checkbox value...
          var viewValue = attr.ngChecked && attr.ngClick ? attr.checked : !ngModelCtrl.$viewValue;

          ngModelCtrl.$setViewValue(viewValue, ev && ev.type);
          ngModelCtrl.$render();
        });
      }

      function render() {
        // Cast the $viewValue to a boolean since it could be undefined
        element.toggleClass('md-checked', !!ngModelCtrl.$viewValue && !isIndeterminate);
      }

      function setIndeterminateState(newValue) {
        isIndeterminate = newValue !== false;
        if (isIndeterminate) {
          element.attr('aria-checked', 'mixed');
        }
        element.toggleClass('md-indeterminate', isIndeterminate);
      }
    }
  }
}
<commit_msg>style(lint): fix missing space in checkbox.js (#11630)<commit_after>/**
 * @ngdoc module
 * @name material.components.checkbox
 * @description Checkbox module!
 */
angular
  .module('material.components.checkbox', ['material.core'])
  .directive('mdCheckbox', MdCheckboxDirective);

/**
 * @ngdoc directive
 * @name mdCheckbox
 * @module material.components.checkbox
 * @restrict E
 *
 * @description
 * The checkbox directive is used like the normal
 * [angular checkbox](https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D).
 *
 * As per the [Material Design spec](https://material.io/archive/guidelines/style/color.html#color-color-palette)
 * the checkbox is in the accent color by default. The primary color palette may be used with
 * the `md-primary` class.
 *
 * @param {expression} ng-model Assignable angular expression to data-bind to.
 * @param {string=} name Property name of the form under which the control is published.
 * @param {expression=} ng-true-value The value to which the expression should be set when selected.
 * @param {expression=} ng-false-value The value to which the expression should be set when not
 *    selected.
 * @param {expression=} ng-change Expression to be executed when the model value changes.
 * @param {boolean=} md-no-ink If present, disable ink ripple effects.
 * @param {string=} aria-label Adds label to checkbox for accessibility.
 *    Defaults to checkbox's text. If no default text is found, a warning will be logged.
 * @param {expression=} md-indeterminate This determines when the checkbox should be rendered as
 *    'indeterminate'. If a truthy expression or no value is passed in the checkbox renders in the
 *    md-indeterminate state. If falsy expression is passed in it just looks like a normal unchecked
 *    checkbox. The indeterminate, checked, and unchecked states are mutually exclusive. A box
 *    cannot be in any two states at the same time. Adding the 'md-indeterminate' attribute
 *    overrides any checked/unchecked rendering logic. When using the 'md-indeterminate' attribute
 *    use 'ng-checked' to define rendering logic instead of using 'ng-model'.
 * @param {expression=} ng-checked If this expression evaluates as truthy, the 'md-checked' css
 *    class is added to the checkbox and it will appear checked.
 *
 * @usage
 * <hljs lang="html">
 * <md-checkbox ng-model="isChecked" aria-label="Finished?">
 *   Finished ?
 * </md-checkbox>
 *
 * <md-checkbox md-no-ink ng-model="hasInk" aria-label="No Ink Effects">
 *   No Ink Effects
 * </md-checkbox>
 *
 * <md-checkbox ng-disabled="true" ng-model="isDisabled" aria-label="Disabled">
 *   Disabled
 * </md-checkbox>
 *
 * </hljs>
 *
 */
function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $mdUtil, $mdInteraction) {
  inputDirective = inputDirective[0];

  return {
    restrict: 'E',
    transclude: true,
    require: ['^?mdInputContainer', '?ngModel', '?^form'],
    priority: $mdConstant.BEFORE_NG_ARIA,
    template:
      '
' +
        '

' +
      '
' +
      '

',
    compile: compile
  };

  // **********************************************************
  // Private Methods
  // **********************************************************

  function compile (tElement, tAttrs) {
    tAttrs.$set('tabindex', tAttrs.tabindex || '0');
    tAttrs.$set('type', 'checkbox');
    tAttrs.$set('role', tAttrs.type);

    return  {
      pre: function(scope, element) {
        // Attach a click handler during preLink, in order to immediately stop propagation
        // (especially for ng-click) when the checkbox is disabled.
        element.on('click', function(e) {
          if (this.hasAttribute('disabled')) {
            e.stopImmediatePropagation();
          }
        });
      },
      post: postLink
    };

    function postLink(scope, element, attr, ctrls) {
      var isIndeterminate;
      var containerCtrl = ctrls[0];
      var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel();
      var formCtrl = ctrls[2];

      if (containerCtrl) {
        var isErrorGetter = containerCtrl.isErrorGetter || function() {
          return ngModelCtrl.$invalid && (ngModelCtrl.$touched || (formCtrl && formCtrl.$submitted));
        };

        containerCtrl.input = element;

        scope.$watch(isErrorGetter, containerCtrl.setInvalid);
      }

      $mdTheming(element);

      // Redirect focus events to the root element, because IE11 is always focusing the container element instead
      // of the md-checkbox element. This causes issues when using ngModelOptions: `updateOnBlur`
      element.children().on('focus', function() {
        element.focus();
      });

      if ($mdUtil.parseAttributeBoolean(attr.mdIndeterminate)) {
        setIndeterminateState();
        scope.$watch(attr.mdIndeterminate, setIndeterminateState);
      }

      if (attr.ngChecked) {
        scope.$watch(scope.$eval.bind(scope, attr.ngChecked), function(value) {
          ngModelCtrl.$setViewValue(value);
          ngModelCtrl.$render();
        });
      }

      $$watchExpr('ngDisabled', 'tabindex', {
        true: '-1',
        false: attr.tabindex
      });

      $mdAria.expectWithText(element, 'aria-label');

      // Reuse the original input[type=checkbox] directive from AngularJS core.
      // This is a bit hacky as we need our own event listener and own render
      // function.
      inputDirective.link.pre(scope, {
        on: angular.noop,
        0: {}
      }, attr, [ngModelCtrl]);

      element.on('click', listener)
        .on('keypress', keypressHandler)
        .on('focus', function() {
          if ($mdInteraction.getLastInteractionType() === 'keyboard') {
            element.addClass('md-focused');
          }
        })
        .on('blur', function() {
          element.removeClass('md-focused');
        });

      ngModelCtrl.$render = render;

      function $$watchExpr(expr, htmlAttr, valueOpts) {
        if (attr[expr]) {
          scope.$watch(attr[expr], function(val) {
            if (valueOpts[val]) {
              element.attr(htmlAttr, valueOpts[val]);
            }
          });
        }
      }

      function keypressHandler(ev) {
        var keyCode = ev.which || ev.keyCode;
        ev.preventDefault();
        switch (keyCode) {
          case $mdConstant.KEY_CODE.SPACE:
            element.addClass('md-focused');
            listener(ev);
            break;
          case $mdConstant.KEY_CODE.ENTER:
            var form = $mdUtil.getClosest(ev.target, 'form');
            // We have to use a native event here as the form directive does not use jqlite
            if (form) {
              form.dispatchEvent(new Event('submit'));
            }
            break;
        }
      }

      function listener(ev) {
        // skipToggle boolean is used by the switch directive to prevent the click event
        // when releasing the drag. There will be always a click if releasing the drag over the checkbox
        if (element[0].hasAttribute('disabled') || scope.skipToggle) {
          return;
        }

        scope.$apply(function() {
          // Toggle the checkbox value...
          var viewValue = attr.ngChecked && attr.ngClick ? attr.checked : !ngModelCtrl.$viewValue;

          ngModelCtrl.$setViewValue(viewValue, ev && ev.type);
          ngModelCtrl.$render();
        });
      }

      function render() {
        // Cast the $viewValue to a boolean since it could be undefined
        element.toggleClass('md-checked', !!ngModelCtrl.$viewValue && !isIndeterminate);
      }

      function setIndeterminateState(newValue) {
        isIndeterminate = newValue !== false;
        if (isIndeterminate) {
          element.attr('aria-checked', 'mixed');
        }
        element.toggleClass('md-indeterminate', isIndeterminate);
      }
    }
  }
}
<|endoftext|>


<commit_before>var fontByLang = {
  'tw': {
    'NotoSans': '思源黑體',
    'NotoSerif': '思源宋體',
    'SetoFont': '瀨戶字體',
    'HanaMin': '花園明朝',
    'GenJyuu': '思源柔黑體'
  },
  'cn': {
    'NotoSans': '思源黑体',
    'NotoSerif': '思源宋体',
    'HanaMin': '花园明朝',
  },
};

var designerByFont = {
  'NotoSans': 'Google & Adobe',
  'NotoSerif': 'Google & Adobe',
  'HanaMin': '上地宏一',
  'SetoFont': '瀬戸のぞみ',
  'GenJyuu': '自家製フォント工房'
};

var fontListByLang = {
  'tw': Object.keys( fontByLang['tw'] ),
  'cn': Object.keys( fontByLang['cn'] ),
};

var CONFIG = {
  'lang': 'tw',
  'font': fontListByLang['tw'][0],
};

function init() {
  var hash = window.location.hash;

  var hash = hash.replace('#','').split('-');

  if(hash[0]=='cn') {
    CONFIG.lang = 'cn';
    CONFIG.font = fontListByLang['cn'][0];
  }

  if(hash.length>1) {
    if(fontByLang[CONFIG.lang][hash[1]]) {
      CONFIG.font = hash[1];
    }
  }

  console.log('CONFIG',CONFIG);
  createPages();
  return;
}

function updateDesignerInfo(index) {
  CONFIG.font = fontListByLang[CONFIG.lang][index-1];
  console.log('afterLoad',index,CONFIG.font );
  var designer = designerByFont[CONFIG.font];
  $('#designer-info').text(designer);
}

function createPages() {
  $('#headerbar .lang').removeClass('selected');
  $('#headerbar .lang[content="'+CONFIG.lang+'"]').addClass('selected');
  $('#pages').remove();
  $('#pp-nav').remove();

  var pages = $('#pages-'+CONFIG.lang).clone();
  var pages = pages.attr('id','pages').attr('class','');
  pages.appendTo('body');
  pages.find('> div').addClass('section');

  pages.pagepiling({
    direction: 'vertical',
    verticalCentered: false,
    sectionsColor: ['#ddd','#bbb','#999','#ccc'],
    scrollingSpeed: 100,
    navigation: {
      'textColor': '#000',
      'bulletsColor': '#000',
      'position': 'bottomleft',
      'tooltips': Object.values( fontByLang[CONFIG.lang] )
    },
    onLeave: function(index, nextIndex, direction){
      console.log('onLeave');
    },
		afterLoad: function(anchorLink, index){
      console.log('afterLoad');
      updateDesignerInfo(index);
    },
		afterRender: function(){
      var idx = fontListByLang[CONFIG.lang].indexOf( CONFIG.font )+1;
      $.fn.pagepiling.moveTo(idx);
      updateDesignerInfo( idx );
      console.log( 'afterRender scroll to font', idx );
    },
  });

}

$( document ).ready(function() {

  // init
  init();

  // init headerbar
  $('#headerbar .lang').click(function(ev){
    var lang = $(ev.target).attr('content');
    if(lang==CONFIG.lang) { return; }
    CONFIG.lang = lang;
    CONFIG.font = fontListByLang[lang][0];
    createPages();
  });

});
<commit_msg>added hashtag udpate<commit_after>var fontByLang = {
  'tw': {
    'NotoSans': '思源黑體',
    'NotoSerif': '思源宋體',
    'SetoFont': '瀨戶字體',
    'HanaMin': '花園明朝',
    'GenJyuu': '思源柔黑體'
  },
  'cn': {
    'NotoSans': '思源黑体',
    'NotoSerif': '思源宋体',
    'HanaMin': '花园明朝',
  },
};

var designerByFont = {
  'NotoSans': 'Google & Adobe',
  'NotoSerif': 'Google & Adobe',
  'HanaMin': '上地宏一',
  'SetoFont': '瀬戸のぞみ',
  'GenJyuu': '自家製フォント工房'
};

var fontListByLang = {
  'tw': Object.keys( fontByLang['tw'] ),
  'cn': Object.keys( fontByLang['cn'] ),
};

var CONFIG = {
  'lang': 'tw',
  'font': fontListByLang['tw'][0],
};

function init() {
  var hash = window.location.hash;

  var hash = hash.replace('#','').split('-');

  if(hash[0]=='cn') {
    CONFIG.lang = 'cn';
    CONFIG.font = fontListByLang['cn'][0];
  }

  if(hash.length>1) {
    if(fontByLang[CONFIG.lang][hash[1]]) {
      CONFIG.font = hash[1];
    }
  }

  createPages();
  return;
}

function updateDesignerInfoAndHash(index) {
  CONFIG.font = fontListByLang[CONFIG.lang][index-1];
  var designer = designerByFont[CONFIG.font];
  $('#designer-info').text(designer);

  var hash = '#'+CONFIG.lang+'-'+CONFIG.font;
  if(history.pushState) {
    history.pushState(null, null, hash);
  }
  else {
    location.hash = hash;
  }
}

function createPages() {
  $('#headerbar .lang').removeClass('selected');
  $('#headerbar .lang[content="'+CONFIG.lang+'"]').addClass('selected');
  $('#pages').remove();
  $('#pp-nav').remove();

  var pages = $('#pages-'+CONFIG.lang).clone();
  var pages = pages.attr('id','pages').attr('class','');
  pages.appendTo('body');
  pages.find('> div').addClass('section');

  pages.pagepiling({
    direction: 'vertical',
    verticalCentered: false,
    sectionsColor: ['#ddd','#bbb','#999','#ccc'],
    scrollingSpeed: 100,
    navigation: {
      'textColor': '#000',
      'bulletsColor': '#000',
      'position': 'bottomleft',
      'tooltips': Object.values( fontByLang[CONFIG.lang] )
    },
    onLeave: function(index, nextIndex, direction){
      console.log('onLeave');
    },
		afterLoad: function(anchorLink, index){
      console.log('afterLoad');
      updateDesignerInfoAndHash(index);
    },
		afterRender: function(){
      console.log( 'afterRender');
      var idx = fontListByLang[CONFIG.lang].indexOf( CONFIG.font )+1;
      $.fn.pagepiling.moveTo(idx);
      updateDesignerInfoAndHash( idx );

    },
  });

}

$( document ).ready(function() {

  // init
  init();

  // init headerbar
  $('#headerbar .lang').click(function(ev){
    var lang = $(ev.target).attr('content');
    if(lang==CONFIG.lang) { return; }
    CONFIG.lang = lang;
    CONFIG.font = fontListByLang[lang][0];
    createPages();
  });

});
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Mon Jul 27 2015 11:34:50 GMT+0530 (IST)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['browserify', 'jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'test/**/*spec.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'test/**/*spec.js': [ 'browserify' ]
    },

    browserify: {
      debug: true,
      transform: [ 'brfs' ]
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['dots'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true
  })
}
<commit_msg>chore(karma.conf.js): exclude preboot tests for now<commit_after>// Karma configuration
// Generated on Mon Jul 27 2015 11:34:50 GMT+0530 (IST)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['browserify', 'jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'test/**/*spec.js'
    ],

    // list of files to exclude
    exclude: [
      'test/preboot/**'
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'test/**/*spec.js': [ 'browserify' ]
    },

    browserify: {
      debug: true,
      transform: [ 'brfs' ]
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['dots'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true
  })
}
<|endoftext|>


<commit_before>(function (vc) {

    vc.extends({
        propTypes: {
            callBackListener: vc.propTypes.string, //父组件名称
            callBackFunction: vc.propTypes.string //父组件监听方法
        },
        data: {
            addApplicationKeyInfo: {
                applicationKeyId: '',
                name: '',
                tel: '',
                typeCd: '',
                sex: '',
                age: '',
                idCard: '',
                startTime: '',
                endTime: '',
                floorId: '',
                floorNum: '',
                floorName: '',
                unitId: '',
                unitName: '',
                roomId: '',
                locationTypeCd: '',
                locationObjId: '',
                roomName: '',
                typeFlag:'',
                photo: '',
                mediaStreamTrack: null
            }
        },
        _initMethod: function () {
            vc.component._initAddApplicationKeyDateInfo();
            //vc.component._initAddApplicationKeyMedia();
        },
        _initEvent: function () {
            vc.on('addApplicationKey', 'openAddApplicationKeyModal', function () {
                $('#addApplicationKeyModel').modal('show');
            });
            vc.on('addApplicationKey', "_initAddApplicationKeyData", function () {
                vc.component._initAddApplicationKeyMedia();
            });
            vc.on("addApplicationKey", "notify", function (_param) {
                if (_param.hasOwnProperty("floorId")) {
                    vc.component.addApplicationKeyInfo.floorId = _param.floorId;
                }

                if (_param.hasOwnProperty("unitId")) {
                    vc.component.addApplicationKeyInfo.unitId = _param.unitId;
                }

                if (_param.hasOwnProperty("roomId")) {
                    vc.component.addApplicationKeyInfo.roomId = _param.roomId;
                }
            });
        },
        methods: {
            _initAddApplicationKeyDateInfo: function () {
                vc.component.addApplicationKeyInfo.startTime = vc.dateFormat(new Date().getTime());
                $('.addApplicationStartTime').datetimepicker({
                    language: 'zh-CN',
                    format: 'yyyy-mm-dd hh:ii:ss',
                    initTime: true,
                    initialDate: new Date(),
                    autoClose: 1,
                    todayBtn: true

                });
                $('.addApplicationStartTime').datetimepicker()
                    .on('changeDate', function (ev) {
                        var value = $(".addApplicationStartTime").val();
                        vc.component.addApplicationKeyInfo.startTime = value;
                    });
                $('.addApplicationEndTime').datetimepicker({
                    language: 'zh-CN',
                    format: 'yyyy-mm-dd hh:ii:ss',
                    initTime: true,
                    initialDate: new Date(),
                    autoClose: 1,
                    todayBtn: true
                });
                $('.addApplicationEndTime').datetimepicker()
                    .on('changeDate', function (ev) {
                        var value = $(".addApplicationEndTime").val();
                        vc.component.addApplicationKeyInfo.endTime = value;
                    });
            },

            addApplicationKeyValidate: function () {
                return vc.validate.validate({
                    addApplicationKeyInfo: vc.component.addApplicationKeyInfo
                }, {
                    'addApplicationKeyInfo.name': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "姓名不能为空"
                        },
                        {
                            limit: "maxin",
                            param: "1,64",
                            errInfo: "姓名不能超过64位"
                        },
                    ],
                    'addApplicationKeyInfo.tel': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "手机号不能为空"
                        },
                        {
                            limit: "phone",
                            param: "",
                            errInfo: "手机号格式错误"
                        },
                    ],
                    'addApplicationKeyInfo.typeCd': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "用户类型不能为空"
                        },
                        {
                            limit: "num",
                            param: "",
                            errInfo: "用户类型格式错误"
                        },
                    ],
                    'addApplicationKeyInfo.sex': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "性别不能为空"
                        }

                    ],
                    'addApplicationKeyInfo.age': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "年龄不能为空"
                        },
                        {
                            limit: "num",
                            param: "",
                            errInfo: "年龄不是有效数字"
                        },
                    ],
                    'addApplicationKeyInfo.idCard': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "身份证号不能为空"
                        },
                        {
                            limit: "idCard",
                            param: "",
                            errInfo: "不是有效的身份证号"
                        },
                    ],
                    'addApplicationKeyInfo.startTime': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "开始时间不能为空"
                        },
                        {
                            limit: "dateTime",
                            param: "",
                            errInfo: "不是有效的时间格式"
                        },
                    ],
                    'addApplicationKeyInfo.endTime': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "结束时间不能为空"
                        },
                        {
                            limit: "dateTime",
                            param: "",
                            errInfo: "不是有效的时间格式"
                        },
                    ],
                    'addApplicationKeyInfo.locationTypeCd': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "申请位置不能为空"
                        }
                    ],
                    'addApplicationKeyInfo.locationObjId': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "具体位置不能为空"
                        }
                    ]

                });
            },
            saveApplicationKeyInfo: function () {
                vc.component.addApplicationKeyInfo.communityId = vc.getCurrentCommunity().communityId;
                if (vc.component.addApplicationKeyInfo.locationTypeCd != '2000' && vc.component.addApplicationKeyInfo.locationTypeCd != '3000') { //大门时直接写 小区ID
                    vc.component.addApplicationKeyInfo.locationObjId = vc.component.addApplicationKeyInfo.communityId;
                } else if (vc.component.addApplicationKeyInfo.locationTypeCd == '2000') {
                    vc.component.addApplicationKeyInfo.locationObjId = vc.component.addApplicationKeyInfo.unitId;
                } else if (vc.component.addApplicationKeyInfo.locationTypeCd == '3000') {
                    vc.component.addApplicationKeyInfo.locationObjId = vc.component.addApplicationKeyInfo.roomId;
                } else {
                    vc.toast("设备位置值错误");
                    return;
                }
                if (!vc.component.addApplicationKeyValidate()) {
                    vc.toast(vc.validate.errInfo);

                    return;
                }

                //不提交数据将数据 回调给侦听处理
                if (vc.notNull($props.callBackListener)) {
                    vc.emit($props.callBackListener, $props.callBackFunction, vc.component.addApplicationKeyInfo);
                    $('#addApplicationKeyModel').modal('hide');
                    return;
                }

                vc.http.post(
                    'addApplicationKey',
                    'save',
                    JSON.stringify(vc.component.addApplicationKeyInfo),
                    {
                        emulateJSON: true
                    },
                    function (json, res) {
                        //vm.menus = vm.refreshMenuActive(JSON.parse(json),0);
                        if (res.status == 200) {
                            //关闭model
                            //$('#addApplicationKeyModel').modal('hide');
                            vc.component.clearAddApplicationKeyInfo();
                            vc.emit('applicationKeyManage', 'listApplicationKey', {});

                            return;
                        }
                        vc.message(json);

                    },
                    function (errInfo, error) {
                        console.log('请求失败处理');

                        vc.message(errInfo);

                    });
            },
            clearAddApplicationKeyInfo: function () {
                vc.component.addApplicationKeyInfo = {
                    name: '',
                    tel: '',
                    typeCd: '',
                    sex: '',
                    age: '',
                    idCard: '',
                    startTime: '',
                    endTime: '',
                    floorId: '',
                    floorNum: '',
                    floorName: '',
                    unitId: '',
                    unitName: '',
                    roomId: '',
                    locationTypeCd: '',
                    locationObjId: '',
                    roomName: '',
                    photo: '',
                    videoPlaying: false,
                    mediaStreamTrack: null,
                    typeFlag:'',
                };
            },
            _closeAddApplicationKeyView: function () {
                vc.emit('applicationKeyManage', 'listApplicationKey', {});
                //关闭拍照摄像头
                if (vc.component.addApplicationKeyInfo.mediaStreamTrack != null) {
                    vc.component.addApplicationKeyInfo.mediaStreamTrack.stop();
                }
            },
            _addUserMedia: function () {
                return navigator.getUserMedia = navigator.getUserMedia ||
                    navigator.webkitGetUserMedia ||
                    navigator.mozGetUserMedia ||
                    navigator.msGetUserMedia || null;
            },
            _initAddApplicationKeyMedia: function () {
                if (vc.component._addUserMedia()) {
                    vc.component.addApplicationKeyInfo.videoPlaying = false;
                    var constraints = {
                        video: true,
                        audio: false
                    };
                    var addVideo = document.getElementById('addApplicationKeyPhoto');
                    var media = navigator.getUserMedia(constraints, function (stream) {
                        var url = window.URL || window.webkitURL;
                        //video.src = url ? url.createObjectURL(stream) : stream;
                        vc.component.addApplicationKeyInfo.mediaStreamTrack = typeof stream.stop === 'function' ? stream : stream.getTracks()[0];
                        try {
                            addVideo.src = url ? url.createObjectURL(stream) : stream;
                        } catch (error) {
                            addVideo.srcObject = stream;
                        }
                        addVideo.play();
                        vc.component.addApplicationKeyInfo.videoPlaying = true;
                    }, function (error) {
                        console.log("ERROR");
                        console.log(error);
                    });
                } else {
                    console.log("初始化视频失败");
                }
            },
            _takePhoto: function () {
                if (vc.component.addApplicationKeyInfo.videoPlaying) {
                    var canvas = document.getElementById('AddApplicationKeyCanvas');
                    var takeAddVideo = document.getElementById('AddApplicationKeyPhoto');
                    canvas.width = takeAddVideo.videoWidth;
                    canvas.height = takeAddVideo.videoHeight;
                    canvas.getContext('2d').drawImage(takeAddVideo, 0, 0);
                    var data = canvas.toDataURL('image/webp');
                    vc.component.addApplicationKeyInfo.photo = data;
                    //document.getElementById('photo').setAttribute('src', data);
                }
            },
            _uploadPhoto: function (event) {
                $("#uploadApplicationKeyPhoto").trigger("click")
            },
            _choosePhoto: function (event) {
                var photoFiles = event.target.files;
                if (photoFiles && photoFiles.length > 0) {
                    // 获取目前上传的文件
                    var file = photoFiles[0];// 文件大小校验的动作
                    if (file.size > 1024 * 1024 * 1) {
                        vc.toast("图片大小不能超过 2MB!")
                        return false;
                    }
                    var reader = new FileReader(); //新建FileReader对象
                    reader.readAsDataURL(file); //读取为base64
                    reader.onloadend = function (e) {
                        vc.component.addApplicationKeyInfo.photo = reader.result;
                    }
                }
            },
        }
    });

})(window.vc);
<commit_msg>优化 添加钥匙拍照功能<commit_after>(function (vc) {

    vc.extends({
        propTypes: {
            callBackListener: vc.propTypes.string, //父组件名称
            callBackFunction: vc.propTypes.string //父组件监听方法
        },
        data: {
            addApplicationKeyInfo: {
                applicationKeyId: '',
                name: '',
                tel: '',
                typeCd: '',
                sex: '',
                age: '',
                idCard: '',
                startTime: '',
                endTime: '',
                floorId: '',
                floorNum: '',
                floorName: '',
                unitId: '',
                unitName: '',
                roomId: '',
                locationTypeCd: '',
                locationObjId: '',
                roomName: '',
                typeFlag:'',
                photo: '',
                mediaStreamTrack: null
            }
        },
        _initMethod: function () {
            vc.component._initAddApplicationKeyDateInfo();
            //vc.component._initAddApplicationKeyMedia();
        },
        _initEvent: function () {
            vc.on('addApplicationKey', 'openAddApplicationKeyModal', function () {
                $('#addApplicationKeyModel').modal('show');
            });
            vc.on('addApplicationKey', "_initAddApplicationKeyData", function () {
                vc.component._initAddApplicationKeyMedia();
            });
            vc.on("addApplicationKey", "notify", function (_param) {
                if (_param.hasOwnProperty("floorId")) {
                    vc.component.addApplicationKeyInfo.floorId = _param.floorId;
                }

                if (_param.hasOwnProperty("unitId")) {
                    vc.component.addApplicationKeyInfo.unitId = _param.unitId;
                }

                if (_param.hasOwnProperty("roomId")) {
                    vc.component.addApplicationKeyInfo.roomId = _param.roomId;
                }
            });
        },
        methods: {
            _initAddApplicationKeyDateInfo: function () {
                vc.component.addApplicationKeyInfo.startTime = vc.dateFormat(new Date().getTime());
                $('.addApplicationStartTime').datetimepicker({
                    language: 'zh-CN',
                    format: 'yyyy-mm-dd hh:ii:ss',
                    initTime: true,
                    initialDate: new Date(),
                    autoClose: 1,
                    todayBtn: true

                });
                $('.addApplicationStartTime').datetimepicker()
                    .on('changeDate', function (ev) {
                        var value = $(".addApplicationStartTime").val();
                        vc.component.addApplicationKeyInfo.startTime = value;
                    });
                $('.addApplicationEndTime').datetimepicker({
                    language: 'zh-CN',
                    format: 'yyyy-mm-dd hh:ii:ss',
                    initTime: true,
                    initialDate: new Date(),
                    autoClose: 1,
                    todayBtn: true
                });
                $('.addApplicationEndTime').datetimepicker()
                    .on('changeDate', function (ev) {
                        var value = $(".addApplicationEndTime").val();
                        vc.component.addApplicationKeyInfo.endTime = value;
                    });
            },

            addApplicationKeyValidate: function () {
                return vc.validate.validate({
                    addApplicationKeyInfo: vc.component.addApplicationKeyInfo
                }, {
                    'addApplicationKeyInfo.name': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "姓名不能为空"
                        },
                        {
                            limit: "maxin",
                            param: "1,64",
                            errInfo: "姓名不能超过64位"
                        },
                    ],
                    'addApplicationKeyInfo.tel': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "手机号不能为空"
                        },
                        {
                            limit: "phone",
                            param: "",
                            errInfo: "手机号格式错误"
                        },
                    ],
                    'addApplicationKeyInfo.typeCd': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "用户类型不能为空"
                        },
                        {
                            limit: "num",
                            param: "",
                            errInfo: "用户类型格式错误"
                        },
                    ],
                    'addApplicationKeyInfo.sex': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "性别不能为空"
                        }

                    ],
                    'addApplicationKeyInfo.age': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "年龄不能为空"
                        },
                        {
                            limit: "num",
                            param: "",
                            errInfo: "年龄不是有效数字"
                        },
                    ],
                    'addApplicationKeyInfo.idCard': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "身份证号不能为空"
                        },
                        {
                            limit: "idCard",
                            param: "",
                            errInfo: "不是有效的身份证号"
                        },
                    ],
                    'addApplicationKeyInfo.startTime': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "开始时间不能为空"
                        },
                        {
                            limit: "dateTime",
                            param: "",
                            errInfo: "不是有效的时间格式"
                        },
                    ],
                    'addApplicationKeyInfo.endTime': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "结束时间不能为空"
                        },
                        {
                            limit: "dateTime",
                            param: "",
                            errInfo: "不是有效的时间格式"
                        },
                    ],
                    'addApplicationKeyInfo.locationTypeCd': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "申请位置不能为空"
                        }
                    ],
                    'addApplicationKeyInfo.locationObjId': [
                        {
                            limit: "required",
                            param: "",
                            errInfo: "具体位置不能为空"
                        }
                    ]

                });
            },
            saveApplicationKeyInfo: function () {
                vc.component.addApplicationKeyInfo.communityId = vc.getCurrentCommunity().communityId;
                if (vc.component.addApplicationKeyInfo.locationTypeCd != '2000' && vc.component.addApplicationKeyInfo.locationTypeCd != '3000') { //大门时直接写 小区ID
                    vc.component.addApplicationKeyInfo.locationObjId = vc.component.addApplicationKeyInfo.communityId;
                } else if (vc.component.addApplicationKeyInfo.locationTypeCd == '2000') {
                    vc.component.addApplicationKeyInfo.locationObjId = vc.component.addApplicationKeyInfo.unitId;
                } else if (vc.component.addApplicationKeyInfo.locationTypeCd == '3000') {
                    vc.component.addApplicationKeyInfo.locationObjId = vc.component.addApplicationKeyInfo.roomId;
                } else {
                    vc.toast("设备位置值错误");
                    return;
                }
                if (!vc.component.addApplicationKeyValidate()) {
                    vc.toast(vc.validate.errInfo);

                    return;
                }

                //不提交数据将数据 回调给侦听处理
                if (vc.notNull($props.callBackListener)) {
                    vc.emit($props.callBackListener, $props.callBackFunction, vc.component.addApplicationKeyInfo);
                    $('#addApplicationKeyModel').modal('hide');
                    return;
                }

                vc.http.post(
                    'addApplicationKey',
                    'save',
                    JSON.stringify(vc.component.addApplicationKeyInfo),
                    {
                        emulateJSON: true
                    },
                    function (json, res) {
                        //vm.menus = vm.refreshMenuActive(JSON.parse(json),0);
                        if (res.status == 200) {
                            //关闭model
                            //$('#addApplicationKeyModel').modal('hide');
                            vc.component.clearAddApplicationKeyInfo();
                            vc.emit('applicationKeyManage', 'listApplicationKey', {});

                            return;
                        }
                        vc.message(json);

                    },
                    function (errInfo, error) {
                        console.log('请求失败处理');

                        vc.message(errInfo);

                    });
            },
            clearAddApplicationKeyInfo: function () {
                vc.component.addApplicationKeyInfo = {
                    name: '',
                    tel: '',
                    typeCd: '',
                    sex: '',
                    age: '',
                    idCard: '',
                    startTime: '',
                    endTime: '',
                    floorId: '',
                    floorNum: '',
                    floorName: '',
                    unitId: '',
                    unitName: '',
                    roomId: '',
                    locationTypeCd: '',
                    locationObjId: '',
                    roomName: '',
                    photo: '',
                    videoPlaying: false,
                    mediaStreamTrack: null,
                    typeFlag:'',
                };
            },
            _closeAddApplicationKeyView: function () {
                vc.emit('applicationKeyManage', 'listApplicationKey', {});
                //关闭拍照摄像头
                if (vc.component.addApplicationKeyInfo.mediaStreamTrack != null) {
                    vc.component.addApplicationKeyInfo.mediaStreamTrack.stop();
                }
            },
            _addUserMedia: function () {
                return navigator.getUserMedia = navigator.getUserMedia ||
                    navigator.webkitGetUserMedia ||
                    navigator.mozGetUserMedia ||
                    navigator.msGetUserMedia || null;
            },
            _initAddApplicationKeyMedia: function () {
                if (vc.component._addUserMedia()) {
                    vc.component.addApplicationKeyInfo.videoPlaying = false;
                    var constraints = {
                        video: true,
                        audio: false
                    };
                    var addVideo = document.getElementById('addApplicationKeyPhoto');
                    var media = navigator.getUserMedia(constraints, function (stream) {
                        var url = window.URL || window.webkitURL;
                        //video.src = url ? url.createObjectURL(stream) : stream;
                        vc.component.addApplicationKeyInfo.mediaStreamTrack = typeof stream.stop === 'function' ? stream : stream.getTracks()[0];
                        try {
                            addVideo.src = url ? url.createObjectURL(stream) : stream;
                        } catch (error) {
                            addVideo.srcObject = stream;
                        }
                        addVideo.play();
                        vc.component.addApplicationKeyInfo.videoPlaying = true;
                    }, function (error) {
                        console.log("ERROR");
                        console.log(error);
                    });
                } else {
                    console.log("初始化视频失败");
                }
            },
            _takePhoto: function () {
                if (vc.component.addApplicationKeyInfo.videoPlaying) {
                    var canvas = document.getElementById('AddApplicationKeyCanvas');
                    var takeAddVideo = document.getElementById('addApplicationKeyPhoto');
                    canvas.width = takeAddVideo.videoWidth;
                    canvas.height = takeAddVideo.videoHeight;
                    canvas.getContext('2d').drawImage(takeAddVideo, 0, 0);
                    var data = canvas.toDataURL('image/webp');
                    vc.component.addApplicationKeyInfo.photo = data;
                    //document.getElementById('photo').setAttribute('src', data);
                }
            },
            _uploadPhoto: function (event) {
                $("#uploadApplicationKeyPhoto").trigger("click")
            },
            _choosePhoto: function (event) {
                var photoFiles = event.target.files;
                if (photoFiles && photoFiles.length > 0) {
                    // 获取目前上传的文件
                    var file = photoFiles[0];// 文件大小校验的动作
                    if (file.size > 1024 * 1024 * 1) {
                        vc.toast("图片大小不能超过 2MB!")
                        return false;
                    }
                    var reader = new FileReader(); //新建FileReader对象
                    reader.readAsDataURL(file); //读取为base64
                    reader.onloadend = function (e) {
                        vc.component.addApplicationKeyInfo.photo = reader.result;
                    }
                }
            },
        }
    });

})(window.vc);
<|endoftext|>


<commit_before>function draw(groups, renderFn) {
  $.get("/api/v1/jobs", function(jobsPayload) {
    var jobs = JSON.parse(jobsPayload);

    $.get("/api/v1/resources", function(resourcesPayload) {
      var resources = JSON.parse(resourcesPayload);

      renderFn(jobs, resources);
    });
  });
}

function drawContinuously(svg, groups) {
  draw(groups, function(jobs, resources) {
    var graph = createGraph(svg, groups, jobs, resources);

    var svgEdges = svg.selectAll("g.edge")
      .data(graph.edges(), function(e) { return e.id() + e.source.node.class });

    svgEdges.exit().remove();

    var svgNodes = svg.selectAll("g.node")
      .data(graph.nodes(), function(n) { return n.id + "-" + n.class });

    svgNodes.exit().remove();

    var svgEdge = svgEdges.enter().append("g")
      .attr("class", function(edge) { return "edge " + edge.source.node.status })

    function highlight(thing) {
      if (!thing.key) {
        return
      }

      svgEdges.each(function(edge) {
        if (edge.source.key == thing.key) {
          d3.select(this).classed({
            active: true
          })
        }
      })

      svgNodes.each(function(node) {
        if (node.key == thing.key) {
          d3.select(this).classed({
            active: true
          })
        }
      })
    }

    function lowlight(thing) {
      if (!thing.key) {
        return
      }

      svgEdges.classed({ active: false })
      svgNodes.classed({ active: false })
    }

    var svgNode = svgNodes.enter().append("g")
      .attr("class", function(node) { return "node " + node.class })
      .on("mouseover", highlight)
      .on("mouseout", lowlight)

    var nodeLink = svgNode.append("svg:a")
      .attr("xlink:href", function(node) { return node.url })

    var nodeBackground = nodeLink.append("rect")
      .attr("height", function(node) { return node.height() })

    nodeLink.append("text")
      .text(function(node) { return node.name })
      .attr("dominant-baseline", "middle")
      .attr("text-anchor", "middle")
      .attr("x", function(node) { return node.width() / 2 })
      .attr("y", function(node) { return node.height() / 2 })

    nodeBackground.attr("width", function(node) { return node.width() })

    graph.layout()

    svgNode.attr("transform", function(node) {
      var position = node.position();
      return "translate("+position.x+", "+position.y+")"
    })

    svgEdge.append("path")
      .attr("d", function(edge) { return edge.path() })
      .on("mouseover", highlight)
      .on("mouseout", lowlight)

    var bbox = svg.node().getBBox();
    d3.select(svg.node().parentNode)
      .attr("viewBox", "" + (bbox.x - 20) + " " + (bbox.y - 20) + " " + (bbox.width + 40) + " " + (bbox.height + 40))

    setTimeout(function() {
      drawContinuously(svg, groups)
    }, 5000)
  });
}

function renderPipeline(groups) {
  var svg = d3.select("#pipeline")
    .append("svg")
      .attr("width", "100%")
      .attr("height", "100%");

  svg.append("defs").append("filter")
    .attr("id", "embiggen")
    .append("feMorphology")
    .attr("operator", "dilate")
    .attr("radius", "4")

  var g = svg.append("g");
  svg.call(d3.behavior.zoom().on("zoom", function() {
    var ev = d3.event;
    g.attr("transform", "translate(" + ev.translate + ") scale(" + ev.scale + ")");
  }));

  drawContinuously(g, groups);

  $("ul.groups li a").click(function(e) {
    var group = e.target.text;

    if (e.shiftKey) {
      groups[group] = !groups[group];
    } else {
      for (var name in groups) {
        groups[name] = name == group;
      }
    }

    var groupQueries = [];
    for (var name in groups) {
      if (groups[name]) {
        groupQueries.push("groups="+name);
      }
    }

    window.location.search = "?" + groupQueries.join("&");

    return false;
  });
}

function createGraph(svg, groups, jobs, resources) {
  var graph = new Graph();

  var resourceURLs = {};

  for (var i in resources) {
    var resource = resources[i];
    resourceURLs[resource.name] = resource.url;
  }

  for (var i in jobs) {
    var job = jobs[i];

    if (!groupsMatch(job.groups, groups)) {
      continue;
    }

    var id = jobNode(job.name);

    var classes = ["job"];
    var status = "pending";

    var url = job.url;
    if (job.next_build) {
      url = job.next_build.url;
    } else if (job.finished_build) {
      url = job.finished_build.url;
    }

    if (job.next_build) {
      classes.push(job.next_build.status)
    }

    if (job.finished_build) {
      status = job.finished_build.status;
      classes.push(job.finished_build.status);
    }

    graph.setNode(id, new Node({
      id: id,
      name: job.name,
      class: classes.join(" "),
      status: status,
      url: url,
      svg: svg
    }));
  }

  // populate job output nodes and edges
  for (var i in jobs) {
    var job = jobs[i];
    var id = jobNode(job.name);

    if (!groupsMatch(job.groups, groups)) {
      continue;
    }

    for (var j in job.outputs) {
      var output = job.outputs[j];

      var outputId = outputNode(job.name, output.resource);

      graph.setNode(outputId, new Node({
        id: outputId,
        name: output.resource,
        key: output.resource,
        class: "output",
        url: resourceURLs[output.resource],
        svg: svg
      }));

      graph.addEdge(id, outputId, output.resource)
    }
  }

  // populate dependant job input edges
  //
  // do this first as this is what primarily determines node ranks
  for (var i in jobs) {
    var job = jobs[i];
    var id = jobNode(job.name);

    if (!groupsMatch(job.groups, groups)) {
      continue;
    }

    for (var j in job.inputs) {
      var input = job.inputs[j];

      if (input.passed && input.passed.length > 0) {
        for (var p in input.passed) {
          var sourceJobNode = jobNode(input.passed[p]);

          var sourceOutputNode = outputNode(input.passed[p], input.resource);
          var sourceInputNode = inputNode(input.passed[p], input.resource);

          var sourceNode;
          if (graph.node(sourceOutputNode)) {
            sourceNode = sourceOutputNode;
          } else {
            if (!graph.node(sourceInputNode)) {
              graph.setNode(sourceInputNode, new Node({
                id: sourceInputNode,
                name: input.resource,
                key: input.resource,
                class: "constrained-input",
                url: resourceURLs[input.resource],
                svg: svg
              }));
            }

            if (graph.node(sourceJobNode)) {
              graph.addEdge(sourceJobNode, sourceInputNode, input.resource);
            }

            sourceNode = sourceInputNode;
          }

          graph.addEdge(sourceNode, id, input.resource);
        }
      }
    }
  }

  // populate unconstrained job inputs
  //
  // now that we know the rank, draw one unconstrained input per rank
  for (var i in jobs) {
    var job = jobs[i];
    var id = jobNode(job.name);

    if (!groupsMatch(job.groups, groups)) {
      continue;
    }

    var node = graph.node(id);
    var rank = node.rank();

    for (var j in job.inputs) {
      var input = job.inputs[j];

      if (!input.passed || input.passed.length == 0) {
        var inputId = inputNode(rank, input.resource);

        if (!graph.node(inputId)) {
          graph.setNode(inputId, new Node({
            id: inputId,
            name: input.resource,
            key: input.resource,
            class: "input",
            url: resourceURLs[input.resource],
            svg: svg
          }));
        }

        graph.addEdge(inputId, id, input.resource)
      }
    }
  }

  return graph;
}

function groupsMatch(groups, groupEnabled) {
  for(var i in groups) {
    if (groupEnabled[groups[i]]) {
      return true;
    }
  }

  return false;
}

function groupNode(name) {
  return "group-"+name;
}

function resourceNode(name) {
  return "resource-"+name;
}

function jobNode(name) {
  return "job-"+name;
}

function gatewayNode(jobNames) {
  return "gateway-"+jobNames.sort().join("-")
}

function outputNode(jobName, resourceName) {
  return "job-"+jobName+"-output-"+resourceName
}

function inputNode(jobName, resourceName) {
  return "job-"+jobName+"-input-"+resourceName
}
<commit_msg>show jobs with no status as pending<commit_after>function draw(groups, renderFn) {
  $.get("/api/v1/jobs", function(jobsPayload) {
    var jobs = JSON.parse(jobsPayload);

    $.get("/api/v1/resources", function(resourcesPayload) {
      var resources = JSON.parse(resourcesPayload);

      renderFn(jobs, resources);
    });
  });
}

function drawContinuously(svg, groups) {
  draw(groups, function(jobs, resources) {
    var graph = createGraph(svg, groups, jobs, resources);

    var svgEdges = svg.selectAll("g.edge")
      .data(graph.edges(), function(e) { return e.id() + e.source.node.class });

    svgEdges.exit().remove();

    var svgNodes = svg.selectAll("g.node")
      .data(graph.nodes(), function(n) { return n.id + "-" + n.class });

    svgNodes.exit().remove();

    var svgEdge = svgEdges.enter().append("g")
      .attr("class", function(edge) { return "edge " + edge.source.node.status })

    function highlight(thing) {
      if (!thing.key) {
        return
      }

      svgEdges.each(function(edge) {
        if (edge.source.key == thing.key) {
          d3.select(this).classed({
            active: true
          })
        }
      })

      svgNodes.each(function(node) {
        if (node.key == thing.key) {
          d3.select(this).classed({
            active: true
          })
        }
      })
    }

    function lowlight(thing) {
      if (!thing.key) {
        return
      }

      svgEdges.classed({ active: false })
      svgNodes.classed({ active: false })
    }

    var svgNode = svgNodes.enter().append("g")
      .attr("class", function(node) { return "node " + node.class })
      .on("mouseover", highlight)
      .on("mouseout", lowlight)

    var nodeLink = svgNode.append("svg:a")
      .attr("xlink:href", function(node) { return node.url })

    var nodeBackground = nodeLink.append("rect")
      .attr("height", function(node) { return node.height() })

    nodeLink.append("text")
      .text(function(node) { return node.name })
      .attr("dominant-baseline", "middle")
      .attr("text-anchor", "middle")
      .attr("x", function(node) { return node.width() / 2 })
      .attr("y", function(node) { return node.height() / 2 })

    nodeBackground.attr("width", function(node) { return node.width() })

    graph.layout()

    svgNode.attr("transform", function(node) {
      var position = node.position();
      return "translate("+position.x+", "+position.y+")"
    })

    svgEdge.append("path")
      .attr("d", function(edge) { return edge.path() })
      .on("mouseover", highlight)
      .on("mouseout", lowlight)

    var bbox = svg.node().getBBox();
    d3.select(svg.node().parentNode)
      .attr("viewBox", "" + (bbox.x - 20) + " " + (bbox.y - 20) + " " + (bbox.width + 40) + " " + (bbox.height + 40))

    setTimeout(function() {
      drawContinuously(svg, groups)
    }, 5000)
  });
}

function renderPipeline(groups) {
  var svg = d3.select("#pipeline")
    .append("svg")
      .attr("width", "100%")
      .attr("height", "100%");

  svg.append("defs").append("filter")
    .attr("id", "embiggen")
    .append("feMorphology")
    .attr("operator", "dilate")
    .attr("radius", "4")

  var g = svg.append("g");
  svg.call(d3.behavior.zoom().on("zoom", function() {
    var ev = d3.event;
    g.attr("transform", "translate(" + ev.translate + ") scale(" + ev.scale + ")");
  }));

  drawContinuously(g, groups);

  $("ul.groups li a").click(function(e) {
    var group = e.target.text;

    if (e.shiftKey) {
      groups[group] = !groups[group];
    } else {
      for (var name in groups) {
        groups[name] = name == group;
      }
    }

    var groupQueries = [];
    for (var name in groups) {
      if (groups[name]) {
        groupQueries.push("groups="+name);
      }
    }

    window.location.search = "?" + groupQueries.join("&");

    return false;
  });
}

function createGraph(svg, groups, jobs, resources) {
  var graph = new Graph();

  var resourceURLs = {};

  for (var i in resources) {
    var resource = resources[i];
    resourceURLs[resource.name] = resource.url;
  }

  for (var i in jobs) {
    var job = jobs[i];

    if (!groupsMatch(job.groups, groups)) {
      continue;
    }

    var id = jobNode(job.name);

    var classes = ["job"];

    var url = job.url;
    if (job.next_build) {
      url = job.next_build.url;
    } else if (job.finished_build) {
      url = job.finished_build.url;
    }

    var status;
    if (job.finished_build) {
      status = job.finished_build.status
    } else {
      status = "pending";
    }

    classes.push(status);

    if (job.next_build) {
      classes.push("started");
    }

    graph.setNode(id, new Node({
      id: id,
      name: job.name,
      class: classes.join(" "),
      status: status,
      url: url,
      svg: svg
    }));
  }

  // populate job output nodes and edges
  for (var i in jobs) {
    var job = jobs[i];
    var id = jobNode(job.name);

    if (!groupsMatch(job.groups, groups)) {
      continue;
    }

    for (var j in job.outputs) {
      var output = job.outputs[j];

      var outputId = outputNode(job.name, output.resource);

      graph.setNode(outputId, new Node({
        id: outputId,
        name: output.resource,
        key: output.resource,
        class: "output",
        url: resourceURLs[output.resource],
        svg: svg
      }));

      graph.addEdge(id, outputId, output.resource)
    }
  }

  // populate dependant job input edges
  //
  // do this first as this is what primarily determines node ranks
  for (var i in jobs) {
    var job = jobs[i];
    var id = jobNode(job.name);

    if (!groupsMatch(job.groups, groups)) {
      continue;
    }

    for (var j in job.inputs) {
      var input = job.inputs[j];

      if (input.passed && input.passed.length > 0) {
        for (var p in input.passed) {
          var sourceJobNode = jobNode(input.passed[p]);

          var sourceOutputNode = outputNode(input.passed[p], input.resource);
          var sourceInputNode = inputNode(input.passed[p], input.resource);

          var sourceNode;
          if (graph.node(sourceOutputNode)) {
            sourceNode = sourceOutputNode;
          } else {
            if (!graph.node(sourceInputNode)) {
              graph.setNode(sourceInputNode, new Node({
                id: sourceInputNode,
                name: input.resource,
                key: input.resource,
                class: "constrained-input",
                url: resourceURLs[input.resource],
                svg: svg
              }));
            }

            if (graph.node(sourceJobNode)) {
              graph.addEdge(sourceJobNode, sourceInputNode, input.resource);
            }

            sourceNode = sourceInputNode;
          }

          graph.addEdge(sourceNode, id, input.resource);
        }
      }
    }
  }

  // populate unconstrained job inputs
  //
  // now that we know the rank, draw one unconstrained input per rank
  for (var i in jobs) {
    var job = jobs[i];
    var id = jobNode(job.name);

    if (!groupsMatch(job.groups, groups)) {
      continue;
    }

    var node = graph.node(id);
    var rank = node.rank();

    for (var j in job.inputs) {
      var input = job.inputs[j];

      if (!input.passed || input.passed.length == 0) {
        var inputId = inputNode(rank, input.resource);

        if (!graph.node(inputId)) {
          graph.setNode(inputId, new Node({
            id: inputId,
            name: input.resource,
            key: input.resource,
            class: "input",
            url: resourceURLs[input.resource],
            svg: svg
          }));
        }

        graph.addEdge(inputId, id, input.resource)
      }
    }
  }

  return graph;
}

function groupsMatch(groups, groupEnabled) {
  for(var i in groups) {
    if (groupEnabled[groups[i]]) {
      return true;
    }
  }

  return false;
}

function groupNode(name) {
  return "group-"+name;
}

function resourceNode(name) {
  return "resource-"+name;
}

function jobNode(name) {
  return "job-"+name;
}

function gatewayNode(jobNames) {
  return "gateway-"+jobNames.sort().join("-")
}

function outputNode(jobName, resourceName) {
  return "job-"+jobName+"-output-"+resourceName
}

function inputNode(jobName, resourceName) {
  return "job-"+jobName+"-input-"+resourceName
}
<|endoftext|>


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

    config.set({

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',

        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine', 'requirejs'],

        // list of files / patterns to load in the browser
        files: [
            { pattern: 'test/**/*Spec.js', included: false },
            { pattern: 'dev/**/*.js', included: false },
            { pattern: 'dev/app/**/*.html', included: false },
            'test/test-main.js'
        ],

        // list of files to exclude
        exclude: [
            'dev/main.js',
            'dev/app/application.js'
        ],

        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {
        },

        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['progress'],

        // web server port
        port: 9876,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_DEBUG,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,

        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['PhantomJS'],

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: false
    });
};
<commit_msg>chore(karma-config): use info logging level for karma configuration<commit_after>module.exports = function(config) {
    'use strict';

    config.set({

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',

        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine', 'requirejs'],

        // list of files / patterns to load in the browser
        files: [
            { pattern: 'test/**/*Spec.js', included: false },
            { pattern: 'dev/**/*.js', included: false },
            { pattern: 'dev/app/**/*.html', included: false },
            'test/test-main.js'
        ],

        // list of files to exclude
        exclude: [
            'dev/main.js',
            'dev/app/application.js'
        ],

        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {
        },

        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['dots'],

        // web server port
        port: 9876,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,

        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['PhantomJS'],

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: false
    });
};
<|endoftext|>


<commit_before>// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
(function () {
  var lastTime = 0;
  var vendors = ['ms', 'moz', 'webkit', 'o'];
  for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
    window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
  }

  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = function (callback, element) {
      var currTime = new Date().getTime();
      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
      var id = window.setTimeout(function () {
        callback(currTime + timeToCall);
      },
      timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };
  }

  if (!window.cancelAnimationFrame) {
    window.cancelAnimationFrame = function (id) {
      clearTimeout(id);
    };
  }
}());

var spec;
var reporterCurrentSpec = {
  specStarted: function (result) {
    spec = result.fullName;
  }
};

jasmine.getEnv().addReporter(reporterCurrentSpec);

/**
 * Utilities to help the tests
 * @type {Object}
 */
var TestUtil = {};

_t = function (attrs) {
  return attrs;
};

TestUtil.config = {
};

TestUtil.linkedObjects = function (view) {
  function flatten_views (view) {
    var flatten = [];
    var sub = view._subviews;
    flatten.push(view);
    for (var k in sub) {
      var v = sub[k];
      flatten.push(v);
      flatten = flatten.concat(flatten_views(v));
    }
    return flatten;
  }
  var views = flatten_views(view);
  view.clean = _.once(view.clean); // eslint-disable-line
  view.clean();

  function evented_objects (obj) {
    var ev = [];
    for (var k in obj) {
      var o = obj[k];
      if (k !== '_parent' && o && obj.hasOwnProperty(k) && o._callbacks) {
        ev.push(o);
      }
    }
    return ev;
  }

  function callback_context (o) {
    var c = [];
    var callbacks = o._callbacks;
    for (var i in callbacks) {
      var node = callbacks[i];
      var end = node.tail;
      while ((node = node.next) !== end) {
        if (node.context) {
          c.push(node.context.cid);
        }
      }
    }
    return c;
  }

  function already_linked () {
    var linked = [];
    // check no pending callbacks
    for (var k in views) {
      var v = views[k];
      var objs = evented_objects(v);
      for (var o in objs) {
        if (_.include(callback_context(objs[o]), v.cid)) {
          linked.push(v);
        }
      }
    }
    return linked;
  }

  return already_linked();
};

TestUtil.assertNotLeaks = function (view) {
  expect(TestUtil.linkedObjects(view).length).toEqual(0);
};

TestUtil._view = function (v) {
  // 1) name of view property in "this" context, e.g. "foobarView"
  // 2) view object as param, e.g. foobarView
  // 3) this.view defined in a beforeEach
  return this[v] || v || this.view;
};

beforeEach(function () {
  cdb.config.attributes = {};
  cdb.config.set(TestUtil.config);
  jasmine.addMatchers({
    toHaveNoLeaks: function () {
      if (arguments.length > 0) {
        // throw new Error('toHaveNoLeaks not take arguments, use toHaveBeenCalledWith');
      }

      return {
        compare: function (actual, expected) {
          var linked = TestUtil.linkedObjects(actual);
          if (linked.length) {
            console.log('** linked objects');
            for (var i in linked) {
              console.log(linked[i]);
            }
          }
          var result = {};
          result.pass = linked.length === 0;
          result.message = 'Expected objects not linked (' + linked.length + '), check the console';
          return result;
        }
      };
    }
  });

  this.innerHTML = function (v) {
    return TestUtil._view.call(this, v).el.innerHTML;
  };

  this.$html = function (v) {
    return TestUtil._view.call(this, v).$el.html();
  };
});

afterEach(function () {
  if (this.view && this.view.clean) {
    this.view.clean();
  }

  var afterTestResults = false;
  var childNodes = [];

  Array.prototype.slice.call(document.body.children).forEach(function (childNode) {
    if (!afterTestResults && childNode.className === 'jasmine_html-reporter') {
      afterTestResults = true;
    } else if (afterTestResults) {
      childNodes.push(childNode);
    }
  });

  if (childNodes.length > 0) {
    var msg = spec + ': DOM nodes found that should have been removed! By default this.view.clean() is called afterEach test, do you have others?';
    console.warn(msg, childNodes);
    throw new Error(msg + ' See dev console for details');
  }
});
<commit_msg>Deleted a couple of lines from SpecHelper3.js<commit_after>// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
(function () {
  var lastTime = 0;
  var vendors = ['ms', 'moz', 'webkit', 'o'];
  for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
    window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
  }

  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = function (callback, element) {
      var currTime = new Date().getTime();
      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
      var id = window.setTimeout(function () {
        callback(currTime + timeToCall);
      },
      timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };
  }

  if (!window.cancelAnimationFrame) {
    window.cancelAnimationFrame = function (id) {
      clearTimeout(id);
    };
  }
}());

var spec;
var reporterCurrentSpec = {
  specStarted: function (result) {
    spec = result.fullName;
  }
};

jasmine.getEnv().addReporter(reporterCurrentSpec);

/**
 * Utilities to help the tests
 * @type {Object}
 */
var TestUtil = {};

_t = function (attrs) {
  return attrs;
};

TestUtil.config = {
};

TestUtil.linkedObjects = function (view) {
  function flatten_views (view) {
    var flatten = [];
    var sub = view._subviews;
    flatten.push(view);
    for (var k in sub) {
      var v = sub[k];
      flatten.push(v);
      flatten = flatten.concat(flatten_views(v));
    }
    return flatten;
  }
  var views = flatten_views(view);
  view.clean = _.once(view.clean); // eslint-disable-line
  view.clean();

  function evented_objects (obj) {
    var ev = [];
    for (var k in obj) {
      var o = obj[k];
      if (k !== '_parent' && o && obj.hasOwnProperty(k) && o._callbacks) {
        ev.push(o);
      }
    }
    return ev;
  }

  function callback_context (o) {
    var c = [];
    var callbacks = o._callbacks;
    for (var i in callbacks) {
      var node = callbacks[i];
      var end = node.tail;
      while ((node = node.next) !== end) {
        if (node.context) {
          c.push(node.context.cid);
        }
      }
    }
    return c;
  }

  function already_linked () {
    var linked = [];
    // check no pending callbacks
    for (var k in views) {
      var v = views[k];
      var objs = evented_objects(v);
      for (var o in objs) {
        if (_.include(callback_context(objs[o]), v.cid)) {
          linked.push(v);
        }
      }
    }
    return linked;
  }

  return already_linked();
};

TestUtil.assertNotLeaks = function (view) {
  expect(TestUtil.linkedObjects(view).length).toEqual(0);
};

TestUtil._view = function (v) {
  // 1) name of view property in "this" context, e.g. "foobarView"
  // 2) view object as param, e.g. foobarView
  // 3) this.view defined in a beforeEach
  return this[v] || v || this.view;
};

beforeEach(function () {
  jasmine.addMatchers({
    toHaveNoLeaks: function () {
      if (arguments.length > 0) {
        // throw new Error('toHaveNoLeaks not take arguments, use toHaveBeenCalledWith');
      }

      return {
        compare: function (actual, expected) {
          var linked = TestUtil.linkedObjects(actual);
          if (linked.length) {
            console.log('** linked objects');
            for (var i in linked) {
              console.log(linked[i]);
            }
          }
          var result = {};
          result.pass = linked.length === 0;
          result.message = 'Expected objects not linked (' + linked.length + '), check the console';
          return result;
        }
      };
    }
  });

  this.innerHTML = function (v) {
    return TestUtil._view.call(this, v).el.innerHTML;
  };

  this.$html = function (v) {
    return TestUtil._view.call(this, v).$el.html();
  };
});

afterEach(function () {
  if (this.view && this.view.clean) {
    this.view.clean();
  }

  var afterTestResults = false;
  var childNodes = [];

  Array.prototype.slice.call(document.body.children).forEach(function (childNode) {
    if (!afterTestResults && childNode.className === 'jasmine_html-reporter') {
      afterTestResults = true;
    } else if (afterTestResults) {
      childNodes.push(childNode);
    }
  });

  if (childNodes.length > 0) {
    var msg = spec + ': DOM nodes found that should have been removed! By default this.view.clean() is called afterEach test, do you have others?';
    console.warn(msg, childNodes);
    throw new Error(msg + ' See dev console for details');
  }
});
<|endoftext|>


<commit_before><commit_msg>Add aria labels to edit/remove row buttons.<commit_after><|endoftext|>


<commit_before>// Karma configuration
// Generated on Wed Aug 24 2016 11:30:50 GMT+0900 (JST)

module.exports = function (config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['mocha'],

    plugins: [
      'karma-phantomjs-launcher',
      'karma-mocha',
      'karma-webpack',
      'karma-coverage'
    ],

    // list of files / patterns to load in the browser
    files: [
      'bower_components/jquery/dist/jquery.min.js',
      'node_modules/lodash/lodash.min.js',
      'node_modules/moment/min/moment.min.js',
      'node_modules/react/dist/react.min.js',
      'node_modules/react-dom/dist/react-dom.min.js',
      'spec/**/*_spec.js'
    ],

    // list of files to exclude
    exclude: [],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'spec/**/*_spec.js': ['webpack']
    },

    webpack: require('./webpack.config.js'),
    webpackMiddleware: {
      noInfo: true,
      stats: {
        chunks: false,
        children: false
      }
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'coverage'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
}
<commit_msg>Improve karma build speed<commit_after>// Karma configuration
// Generated on Wed Aug 24 2016 11:30:50 GMT+0900 (JST)

module.exports = function (config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['mocha'],

    plugins: [
      'karma-phantomjs-launcher',
      'karma-mocha',
      'karma-webpack',
      'karma-coverage'
    ],

    // list of files / patterns to load in the browser
    files: [
      'bower_components/jquery/dist/jquery.min.js',
      'node_modules/lodash/lodash.min.js',
      'node_modules/moment/min/moment.min.js',
      'node_modules/react/dist/react.min.js',
      'node_modules/react-dom/dist/react-dom.min.js',
      'spec/**/*_spec.js'
    ],

    // list of files to exclude
    exclude: [],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'spec/**/*_spec.js': ['webpack']
    },

    webpack: function() {
      var conf = require('./webpack.config.js');
      delete conf.entry;
      delete conf.devtool;
      return conf;
    }(),

    webpackMiddleware: {
      noInfo: false,
      stats: {
        chunks: false,
        children: false
      }
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'coverage'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import classNames from 'classnames'
import { FORM_CONTROL_STATUS as STATUS } from '../../constants/gui_types'
import { RequestIcon, SuccessIcon, FailureIcon } from '../forms/FormIcons'

class FormControl extends Component {

  static propTypes = {
    classList: PropTypes.string,
    className: PropTypes.string,
    id: PropTypes.string.isRequired,
    kind: PropTypes.string,
    label: PropTypes.string,
    name: PropTypes.string,
    onBlur: PropTypes.func,
    onChange: PropTypes.func,
    onFocus: PropTypes.func,
    placeholder: PropTypes.string,
    renderStatus: PropTypes.func,
    renderFeedback: PropTypes.func,
    status: PropTypes.string,
    tabIndex: PropTypes.string.isRequired,
    text: PropTypes.string,
  }

  static defaultProps = {
    kind: 'input',
    renderFeedback: null,
    status: STATUS.INDETERMINATE,
    tabIndex: '0',
    text: '',
    type: 'text',
  }

  componentWillMount() {
    const { text } = this.props
    this.state = {
      hasFocus: false,
      hasValue: text && text.length,
      isInitialValue: true,
      text,
    }
    this.initialValue = text
    this.timer = setTimeout(this.checkValue, 250)
  }

  componentWillUnmount() {
    if (this.timer) {
      clearTimeout(this.timer)
    }
  }

  onFocusControl = (e) => {
    this.setState({ hasFocus: true })
    const { onFocus } = this.props
    if (typeof onFocus === 'function') {
      onFocus(e)
    }
  }

  onBlurControl = (e) => {
    this.setState({ hasFocus: false })
    const { onBlur } = this.props
    if (typeof onBlur === 'function') {
      onBlur(e)
    }
  }

  onChangeControl = (e) => {
    this.onChangeValue(e.target.value)
  }

  onChangeValue = (val) => {
    const { id, onChange } = this.props
    this.setState({
      text: val,
      hasValue: val.length,
      isInitialValue: val === this.initialValue,
    })
    if (id && typeof onChange === 'function') {
      onChange({ [id]: val })
    }
  }

  getStatusAsClassName() {
    const { status } = this.props
    switch (status) {
      case STATUS.REQUEST:
        return 'isValidating'
      case STATUS.FAILURE:
        return 'isFailing'
      case STATUS.SUCCESS:
        return 'isSucceeding'
      case STATUS.INDETERMINATE:
      default:
        return 'isIndeterminate'
    }
  }

  getGroupClassNames() {
    const { classList, className } = this.props
    const { hasFocus, hasValue, isInitialValue } = this.state
    const statusClassName = this.getStatusAsClassName()
    return classNames(
      'FormControl',
      classList,
      className,
      statusClassName,
      { hasFocus },
      { hasValue },
      { isInitialValue },
    )
  }

  getLabelClassNames() {
    const { classList } = this.props
    return classNames('FormControlLabel', classList)
  }

  getInputClassNames() {
    const { classList } = this.props
    return classNames('FormControlInput', classList)
  }

  getStatusClassNames() {
    const { classList } = this.props
    return classNames('FormControlStatus', classList)
  }

  getStatusIcon() {
    const { status } = this.props
    switch (status) {
      case STATUS.REQUEST:
        return <RequestIcon />
      case STATUS.FAILURE:
        return <FailureIcon />
      case STATUS.SUCCESS:
        return <SuccessIcon />
      case STATUS.INDETERMINATE:
      default:
        return null
    }
  }

  checkValue = () => {
    const inputControl = this.refs.input
    this.timer = setTimeout(this.checkValue, 250)
  }

  clear() {
    this.setState({ text: '' })
  }

  renderLabel() {
    const { id, label } = this.props
    const labelClassNames = this.getLabelClassNames()
    return (

        { label }

    )
  }

  renderTextArea(text, inputClassNames) {
    return (
      <textarea
        { ...this.props }
        className={ inputClassNames }
        onFocus={ this.onFocusControl }
        onBlur={ this.onBlurControl }
        onChange={ this.onChangeControl }
        ref="input"
        value={ text }
      />
    )
  }

  renderInput(text, inputClassNames) {
    return (
      <input
        { ...this.props }
        className={ inputClassNames }
        onFocus={ this.onFocusControl }
        onBlur={ this.onBlurControl }
        onChange={ this.onChangeControl }
        ref="input"
        value={ text }
      />
    )
  }

  render() {
    const { kind, label, renderFeedback, renderStatus } = this.props
    const { text } = this.state
    const groupClassNames = this.getGroupClassNames()
    const inputClassNames = this.getInputClassNames()
    const statusClassNames = this.getStatusClassNames()
    return (

        { label && this.renderLabel() }
        {
          kind === 'textarea' ?
            this.renderTextArea(text, inputClassNames) :
            this.renderInput(text, inputClassNames)
        }

          { this.getStatusIcon() }

        { renderStatus ? renderStatus() : null }
        { renderFeedback ? renderFeedback() : null }

    )
  }
}

export default FormControl
<commit_msg>I'll need the text!<commit_after>import React, { Component, PropTypes } from 'react'
import classNames from 'classnames'
import { FORM_CONTROL_STATUS as STATUS } from '../../constants/gui_types'
import { RequestIcon, SuccessIcon, FailureIcon } from '../forms/FormIcons'

class FormControl extends Component {

  static propTypes = {
    classList: PropTypes.string,
    className: PropTypes.string,
    id: PropTypes.string.isRequired,
    kind: PropTypes.string,
    label: PropTypes.string,
    name: PropTypes.string,
    onBlur: PropTypes.func,
    onChange: PropTypes.func,
    onFocus: PropTypes.func,
    placeholder: PropTypes.string,
    renderStatus: PropTypes.func,
    renderFeedback: PropTypes.func,
    status: PropTypes.string,
    tabIndex: PropTypes.string.isRequired,
    text: PropTypes.string,
  }

  static defaultProps = {
    kind: 'input',
    renderFeedback: null,
    status: STATUS.INDETERMINATE,
    tabIndex: '0',
    text: '',
    type: 'text',
  }

  componentWillMount() {
    const { text } = this.props
    this.state = {
      hasFocus: false,
      hasValue: text && text.length,
      isInitialValue: true,
      text,
    }
    this.initialValue = text
    this.timer = setTimeout(this.checkValue, 250)
  }

  componentWillUnmount() {
    if (this.timer) {
      clearTimeout(this.timer)
    }
  }

  onFocusControl = (e) => {
    this.setState({ hasFocus: true })
    const { onFocus } = this.props
    if (typeof onFocus === 'function') {
      onFocus(e)
    }
  }

  onBlurControl = (e) => {
    this.setState({ hasFocus: false })
    const { onBlur } = this.props
    if (typeof onBlur === 'function') {
      onBlur(e)
    }
  }

  onChangeControl = (e) => {
    this.onChangeValue(e.target.value)
  }

  onChangeValue = (val) => {
    const { id, onChange } = this.props
    this.setState({
      text: val,
      hasValue: val.length,
      isInitialValue: val === this.initialValue,
    })
    if (id && typeof onChange === 'function') {
      onChange({ [id]: val })
    }
  }

  getStatusAsClassName() {
    const { status } = this.props
    switch (status) {
      case STATUS.REQUEST:
        return 'isValidating'
      case STATUS.FAILURE:
        return 'isFailing'
      case STATUS.SUCCESS:
        return 'isSucceeding'
      case STATUS.INDETERMINATE:
      default:
        return 'isIndeterminate'
    }
  }

  getGroupClassNames() {
    const { classList, className } = this.props
    const { hasFocus, hasValue, isInitialValue } = this.state
    const statusClassName = this.getStatusAsClassName()
    return classNames(
      'FormControl',
      classList,
      className,
      statusClassName,
      { hasFocus },
      { hasValue },
      { isInitialValue },
    )
  }

  getLabelClassNames() {
    const { classList } = this.props
    return classNames('FormControlLabel', classList)
  }

  getInputClassNames() {
    const { classList } = this.props
    return classNames('FormControlInput', classList)
  }

  getStatusClassNames() {
    const { classList } = this.props
    return classNames('FormControlStatus', classList)
  }

  getStatusIcon() {
    const { status } = this.props
    switch (status) {
      case STATUS.REQUEST:
        return <RequestIcon />
      case STATUS.FAILURE:
        return <FailureIcon />
      case STATUS.SUCCESS:
        return <SuccessIcon />
      case STATUS.INDETERMINATE:
      default:
        return null
    }
  }

  checkValue = () => {
    const inputControl = this.refs.input
    const text = ""
    this.timer = setTimeout(this.checkValue, 250)
  }

  clear() {
    this.setState({ text: '' })
  }

  renderLabel() {
    const { id, label } = this.props
    const labelClassNames = this.getLabelClassNames()
    return (

        { label }

    )
  }

  renderTextArea(text, inputClassNames) {
    return (
      <textarea
        { ...this.props }
        className={ inputClassNames }
        onFocus={ this.onFocusControl }
        onBlur={ this.onBlurControl }
        onChange={ this.onChangeControl }
        ref="input"
        value={ text }
      />
    )
  }

  renderInput(text, inputClassNames) {
    return (
      <input
        { ...this.props }
        className={ inputClassNames }
        onFocus={ this.onFocusControl }
        onBlur={ this.onBlurControl }
        onChange={ this.onChangeControl }
        ref="input"
        value={ text }
      />
    )
  }

  render() {
    const { kind, label, renderFeedback, renderStatus } = this.props
    const { text } = this.state
    const groupClassNames = this.getGroupClassNames()
    const inputClassNames = this.getInputClassNames()
    const statusClassNames = this.getStatusClassNames()
    return (

        { label && this.renderLabel() }
        {
          kind === 'textarea' ?
            this.renderTextArea(text, inputClassNames) :
            this.renderInput(text, inputClassNames)
        }

          { this.getStatusIcon() }

        { renderStatus ? renderStatus() : null }
        { renderFeedback ? renderFeedback() : null }

    )
  }
}

export default FormControl
<|endoftext|>