diff --git a/src/rating/docs/demo.html b/src/rating/docs/demo.html
new file mode 100644
index 0000000000..24c229d4d5
--- /dev/null
+++ b/src/rating/docs/demo.html
@@ -0,0 +1,10 @@
+
+
+
+
+
Rate: {{rate}} - Readonly is: {{isReadonly}}
+
+
+
+
+
\ No newline at end of file
diff --git a/src/rating/docs/demo.js b/src/rating/docs/demo.js
new file mode 100644
index 0000000000..efb7a88f43
--- /dev/null
+++ b/src/rating/docs/demo.js
@@ -0,0 +1,4 @@
+var RatingDemoCtrl = function ($scope) {
+ $scope.rate = 7;
+ $scope.isReadonly = false;
+};
diff --git a/src/rating/docs/readme.md b/src/rating/docs/readme.md
new file mode 100644
index 0000000000..fbcdfb268c
--- /dev/null
+++ b/src/rating/docs/readme.md
@@ -0,0 +1,3 @@
+Rating directive that will take care of visualising a star rating bar.
+
+It also provides optional attribute `max` to vary the number of stars and `readonly` attribute to diasble user's interaction.
\ No newline at end of file
diff --git a/src/rating/rating.js b/src/rating/rating.js
new file mode 100644
index 0000000000..7d623e1782
--- /dev/null
+++ b/src/rating/rating.js
@@ -0,0 +1,53 @@
+angular.module('ui.bootstrap.rating', [])
+
+.constant('ratingConfig', {
+ max: 5
+})
+
+.directive('rating', ['ratingConfig', '$parse', function(ratingConfig, $parse) {
+ return {
+ restrict: 'EA',
+ scope: {
+ value: '='
+ },
+ templateUrl: 'template/rating/rating.html',
+ replace: true,
+ link: function(scope, element, attrs) {
+
+ var maxRange = angular.isDefined(attrs.max) ? scope.$eval(attrs.max) : ratingConfig.max;
+
+ scope.range = [];
+ for (var i = 1; i <= maxRange; i++) {
+ scope.range.push(i);
+ }
+
+ scope.rate = function(value) {
+ if ( ! scope.readonly ) {
+ scope.value = value;
+ }
+ };
+
+ scope.enter = function(value) {
+ if ( ! scope.readonly ) {
+ scope.val = value;
+ }
+ };
+
+ scope.reset = function() {
+ scope.val = angular.copy(scope.value);
+ };
+ scope.reset();
+
+ scope.$watch('value', function(value) {
+ scope.val = value;
+ });
+
+ scope.readonly = false;
+ if (attrs.readonly) {
+ scope.$parent.$watch($parse(attrs.readonly), function(value) {
+ scope.readonly = !!value;
+ });
+ }
+ }
+ };
+}]);
\ No newline at end of file
diff --git a/src/rating/test/rating.spec.js b/src/rating/test/rating.spec.js
new file mode 100644
index 0000000000..4d289c04ef
--- /dev/null
+++ b/src/rating/test/rating.spec.js
@@ -0,0 +1,128 @@
+describe('rating directive', function () {
+ var $rootScope, element;
+ beforeEach(module('ui.bootstrap.rating'));
+ beforeEach(module('template/rating/rating.html'));
+ beforeEach(inject(function(_$compile_, _$rootScope_) {
+ $compile = _$compile_;
+ $rootScope = _$rootScope_;
+ $rootScope.rate = 3;
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ }));
+
+ function getState(stars) {
+ var state = [];
+ for (var i = 0, n = stars.length; i < n; i++) {
+ state.push( (stars.eq(i).hasClass('icon-star') && ! stars.eq(i).hasClass('icon-star-empty')) );
+ }
+ return state;
+ }
+
+ it('contains the default number of icons', function() {
+ expect(element.find('i').length).toBe(5);
+ });
+
+ it('initializes the default star icons as selected', function() {
+ var stars = element.find('i');
+ expect(getState(stars)).toEqual([true, true, true, false, false]);
+ });
+
+ it('handles correcty the click event', function() {
+ var stars = element.find('i');
+
+ var star2 = stars.eq(1);
+ star2.click();
+ $rootScope.$digest();
+ expect(getState(stars)).toEqual([true, true, false, false, false]);
+ expect($rootScope.rate).toBe(2);
+
+ var star5 = stars.eq(4);
+ star5.click();
+ $rootScope.$digest();
+ expect(getState(stars)).toEqual([true, true, true, true, true]);
+ expect($rootScope.rate).toBe(5);
+ });
+
+ it('handles correcty the hover event', function() {
+ var stars = element.find('i');
+
+ var star2 = stars.eq(1);
+ star2.trigger('mouseover');
+ $rootScope.$digest();
+ expect(getState(stars)).toEqual([true, true, false, false, false]);
+ expect($rootScope.rate).toBe(3);
+
+ var star5 = stars.eq(4);
+ star5.trigger('mouseover');
+ $rootScope.$digest();
+ expect(getState(stars)).toEqual([true, true, true, true, true]);
+ expect($rootScope.rate).toBe(3);
+
+ element.trigger('mouseout');
+ expect(getState(stars)).toEqual([true, true, true, false, false]);
+ expect($rootScope.rate).toBe(3);
+ });
+
+ it('changes the number of selected icons when value changes', function() {
+ $rootScope.rate = 2;
+ $rootScope.$digest();
+
+ var stars = element.find('i');
+ expect(getState(stars)).toEqual([true, true, false, false, false]);
+ });
+
+ it('shows different number of icons when `max` attribute is set', function() {
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+
+ expect(element.find('i').length).toBe(7);
+ });
+
+ it('handles readonly attribute', function() {
+ $rootScope.isReadonly = true;
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+
+ var stars = element.find('i');
+ expect(getState(stars)).toEqual([true, true, true, false, false]);
+
+ var star5 = stars.eq(4);
+ star5.trigger('mouseover');
+ $rootScope.$digest();
+ expect(getState(stars)).toEqual([true, true, true, false, false]);
+
+ $rootScope.isReadonly = false;
+ $rootScope.$digest();
+
+ star5.trigger('mouseover');
+ $rootScope.$digest();
+ expect(getState(stars)).toEqual([true, true, true, true, true]);
+ });
+
+});
+
+describe('setting ratingConfig', function() {
+ var $rootScope, element;
+ var originalConfig = {};
+ beforeEach(module('ui.bootstrap.rating'));
+ beforeEach(module('template/rating/rating.html'));
+ beforeEach(inject(function(_$compile_, _$rootScope_, ratingConfig) {
+ $compile = _$compile_;
+ $rootScope = _$rootScope_;
+ $rootScope.rate = 5;
+ angular.extend(originalConfig, ratingConfig);
+ ratingConfig.max = 10;
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ }));
+ afterEach(inject(function(ratingConfig) {
+ // return it to the original state
+ angular.extend(ratingConfig, originalConfig);
+ }));
+
+ it('should change number of icon elements', function () {
+ expect(element.find('i').length).toBe(10);
+ });
+
+});
+
diff --git a/template/rating/rating.html b/template/rating/rating.html
new file mode 100644
index 0000000000..df902ca3c1
--- /dev/null
+++ b/template/rating/rating.html
@@ -0,0 +1,3 @@
+
+
+