ledgerrb/public/app/bower_components/angular-material/modules/js/radioButton/radioButton.js
2015-02-04 16:08:32 +01:00

296 lines
8.5 KiB
JavaScript

/*!
* Angular Material Design
* https://github.com/angular/material
* @license MIT
* v0.7.0-rc3
*/
(function() {
'use strict';
/**
* @ngdoc module
* @name material.components.radioButton
* @description radioButton module!
*/
angular.module('material.components.radioButton', [
'material.core'
])
.directive('mdRadioGroup', mdRadioGroupDirective)
.directive('mdRadioButton', mdRadioButtonDirective);
/**
* @ngdoc directive
* @module material.components.radioButton
* @name mdRadioGroup
*
* @restrict E
*
* @description
* The `<md-radio-group>` directive identifies a grouping
* container for the 1..n grouped radio buttons; specified using nested
* `<md-radio-button>` tags.
*
* Note: `<md-radio-group>` and `<md-radio-button>` handle tabindex differently
* than the native `<input type='radio'>` controls. Whereas the native controls
* force the user to tab through all the radio buttons, `<md-radio-group>`
* is focusable, and by default the `<md-radio-button>`s are not.
*
* @param {string} ng-model Assignable angular expression to data-bind to.
* @param {boolean=} md-no-ink Use of attribute indicates flag to disable ink ripple effects.
*
* @usage
* <hljs lang="html">
* <md-radio-group ng-model="selected">
*
* <md-radio-button
* ng-repeat="d in colorOptions"
* ng-value="d.value" aria-label="{{ d.label }}">
*
* {{ d.label }}
*
* </md-radio-button>
*
* </md-radio-group>
* </hljs>
*
*/
function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming) {
RadioGroupController.prototype = createRadioGroupControllerProto();
return {
restrict: 'E',
controller: ['$element', RadioGroupController],
require: ['mdRadioGroup', '?ngModel'],
link: { pre: linkRadioGroup }
};
function linkRadioGroup(scope, element, attr, ctrls) {
$mdTheming(element);
var rgCtrl = ctrls[0];
var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel();
function keydownListener(ev) {
switch(ev.keyCode) {
case $mdConstant.KEY_CODE.LEFT_ARROW:
case $mdConstant.KEY_CODE.UP_ARROW:
ev.preventDefault();
rgCtrl.selectPrevious();
break;
case $mdConstant.KEY_CODE.RIGHT_ARROW:
case $mdConstant.KEY_CODE.DOWN_ARROW:
ev.preventDefault();
rgCtrl.selectNext();
break;
case $mdConstant.KEY_CODE.ENTER:
var form = angular.element($mdUtil.getClosest(element[0], 'form'));
if (form.length > 0) {
form.triggerHandler('submit');
}
break;
}
}
rgCtrl.init(ngModelCtrl);
element.attr({
'role': 'radiogroup',
'tabIndex': element.attr('tabindex') || '0'
})
.on('keydown', keydownListener);
}
function RadioGroupController($element) {
this._radioButtonRenderFns = [];
this.$element = $element;
}
function createRadioGroupControllerProto() {
return {
init: function(ngModelCtrl) {
this._ngModelCtrl = ngModelCtrl;
this._ngModelCtrl.$render = angular.bind(this, this.render);
},
add: function(rbRender) {
this._radioButtonRenderFns.push(rbRender);
},
remove: function(rbRender) {
var index = this._radioButtonRenderFns.indexOf(rbRender);
if (index !== -1) {
this._radioButtonRenderFns.splice(index, 1);
}
},
render: function() {
this._radioButtonRenderFns.forEach(function(rbRender) {
rbRender();
});
},
setViewValue: function(value, eventType) {
this._ngModelCtrl.$setViewValue(value, eventType);
// update the other radio buttons as well
this.render();
},
getViewValue: function() {
return this._ngModelCtrl.$viewValue;
},
selectNext: function() {
return changeSelectedButton(this.$element, 1);
},
selectPrevious : function() {
return changeSelectedButton(this.$element, -1);
},
setActiveDescendant: function (radioId) {
this.$element.attr('aria-activedescendant', radioId);
}
};
}
/**
* Change the radio group's selected button by a given increment.
* If no button is selected, select the first button.
*/
function changeSelectedButton(parent, increment) {
// Coerce all child radio buttons into an array, then wrap then in an iterator
var buttons = $mdUtil.iterator(
Array.prototype.slice.call(parent[0].querySelectorAll('md-radio-button')),
true
);
if (buttons.count()) {
var validate = function (button) {
// If disabled, then NOT valid
return !angular.element(button).attr("disabled");
};
var selected = parent[0].querySelector('md-radio-button.md-checked');
var target = buttons[increment < 0 ? 'previous' : 'next'](selected, validate) || buttons.first();
// Activate radioButton's click listener (triggerHandler won't create a real click event)
angular.element(target).triggerHandler('click');
}
}
}
mdRadioGroupDirective.$inject = ["$mdUtil", "$mdConstant", "$mdTheming"];
/**
* @ngdoc directive
* @module material.components.radioButton
* @name mdRadioButton
*
* @restrict E
*
* @description
* The `<md-radio-button>`directive is the child directive required to be used within `<md-radio-group>` elements.
*
* While similar to the `<input type="radio" ng-model="" value="">` directive,
* the `<md-radio-button>` directive provides ink effects, ARIA support, and
* supports use within named radio groups.
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} ngChange Angular expression to be executed when input changes due to user
* interaction with the input element.
* @param {string} ngValue Angular expression which sets the value to which the expression should
* be set when selected.*
* @param {string} value The value to which the expression should be set when selected.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} ariaLabel Adds label to radio button for accessibility.
* Defaults to radio button's text. If no default text is found, a warning will be logged.
*
* @usage
* <hljs lang="html">
*
* <md-radio-button value="1" aria-label="Label 1">
* Label 1
* </md-radio-button>
*
* <md-radio-button ng-model="color" ng-value="specialValue" aria-label="Green">
* Green
* </md-radio-button>
*
* </hljs>
*
*/
function mdRadioButtonDirective($mdAria, $mdUtil, $mdTheming) {
var CHECKED_CSS = 'md-checked';
return {
restrict: 'E',
require: '^mdRadioGroup',
transclude: true,
template: '<div class="md-container" md-ink-ripple md-ink-ripple-checkbox>' +
'<div class="md-off"></div>' +
'<div class="md-on"></div>' +
'</div>' +
'<div ng-transclude class="md-label"></div>',
link: link
};
function link(scope, element, attr, rgCtrl) {
var lastChecked;
$mdTheming(element);
configureAria(element, scope);
rgCtrl.add(render);
attr.$observe('value', render);
element
.on('click', listener)
.on('$destroy', function() {
rgCtrl.remove(render);
});
function listener(ev) {
if (element[0].hasAttribute('disabled')) return;
scope.$apply(function() {
rgCtrl.setViewValue(attr.value, ev && ev.type);
});
}
function render() {
var checked = (rgCtrl.getViewValue() == attr.value);
if (checked === lastChecked) {
return;
}
lastChecked = checked;
element.attr('aria-checked', checked);
if (checked) {
element.addClass(CHECKED_CSS);
rgCtrl.setActiveDescendant(element.attr('id'));
} else {
element.removeClass(CHECKED_CSS);
}
}
/**
* Inject ARIA-specific attributes appropriate for each radio button
*/
function configureAria( element, scope ){
scope.ariaId = buildAriaID();
element.attr({
'id' : scope.ariaId,
'role' : 'radio',
'aria-checked' : 'false'
});
$mdAria.expectWithText(element, 'aria-label');
/**
* Build a unique ID for each radio button that will be used with aria-activedescendant.
* Preserve existing ID if already specified.
* @returns {*|string}
*/
function buildAriaID() {
return attr.id || ( 'radio' + "_" + $mdUtil.nextUid() );
}
}
}
}
mdRadioButtonDirective.$inject = ["$mdAria", "$mdUtil", "$mdTheming"];
})();