angular-ui-bootstrap-rails 0.5.0.0 → 0.6.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 41a0eb3d7f6aa58214fe4456e6d59f881acd73e6
4
- data.tar.gz: 5ce4e249235df1ccd64d828b214ee2acf9b123ac
3
+ metadata.gz: 673a0814c0c59e273cfa7ad9d557d3d62881d370
4
+ data.tar.gz: ed5fd830fe8a45a920b1c53caa82a4beae0798ab
5
5
  SHA512:
6
- metadata.gz: c1ecdcb8ea3ddb278edffd213e8befdbeafe6c114b8d1e3a33d8d0cd80f3bb9ee9a07ebab0f84072e887989bcf34bf562d6180ba6d508c2d08841edddb42eed5
7
- data.tar.gz: 81b75e66f470254abae8c087b6a8fac53531cd2f9129a76977ec311941e86c386a459d63b03906dda4da0b3f9b5f61a6ebc575a3802b187f29dba9644189e2b7
6
+ metadata.gz: 76e6035443aaf8bc45b0fdffce94458455ab9e7033452bd61fb987d83d9caeaaf3da80c765ef3149fb39639c47921cb2f26720daac327ec88d27e0c5ee37b397
7
+ data.tar.gz: e54f8515bf6cb2cd25c30ef6fae535b0f897261f7289a52bc51403f561b0ba97214ee578235fc7606d00ec4ad7fd51e07cc44bd39cb0af07bd53f56f3e228fae
@@ -1,7 +1,7 @@
1
1
  module AngularUI
2
2
  module Bootstrap
3
3
  module Rails
4
- VERSION = "0.5.0.0"
4
+ VERSION = "0.6.0.0"
5
5
  end
6
6
  end
7
7
  end
@@ -1,5 +1,5 @@
1
- angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dialog","ui.bootstrap.dropdownToggle","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
2
- angular.module("ui.bootstrap.tpls", ["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/datepicker/popup.html","template/dialog/message.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset-titles.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]);
1
+ angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.bindHtml","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdownToggle","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
2
+ angular.module("ui.bootstrap.tpls", ["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/datepicker/popup.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset-titles.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]);
3
3
  angular.module('ui.bootstrap.transition', [])
4
4
 
5
5
  /**
@@ -337,6 +337,16 @@ angular.module("ui.bootstrap.alert", []).directive('alert', function () {
337
337
  };
338
338
  });
339
339
 
340
+ angular.module('ui.bootstrap.bindHtml', [])
341
+
342
+ .directive('bindHtmlUnsafe', function () {
343
+ return function (scope, element, attr) {
344
+ element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe);
345
+ scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) {
346
+ element.html(value || '');
347
+ });
348
+ };
349
+ });
340
350
  angular.module('ui.bootstrap.buttons', [])
341
351
 
342
352
  .constant('buttonConfig', {
@@ -408,7 +418,7 @@ angular.module('ui.bootstrap.buttons', [])
408
418
  /**
409
419
  * @ngdoc overview
410
420
  * @name ui.bootstrap.carousel
411
- *
421
+ *
412
422
  * @description
413
423
  * AngularJS version of an image carousel.
414
424
  *
@@ -439,10 +449,10 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
439
449
  }
440
450
  function goNext() {
441
451
  //If we have a slide to transition from and we have a transition type and we're allowed, go
442
- if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {
452
+ if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {
443
453
  //We shouldn't do class manip in here, but it's the same weird thing bootstrap does. need to fix sometime
444
454
  nextSlide.$element.addClass(direction);
445
- nextSlide.$element[0].offsetWidth = nextSlide.$element[0].offsetWidth; //force reflow
455
+ var reflow = nextSlide.$element[0].offsetWidth; //force reflow
446
456
 
447
457
  //Set all other slides to stop doing their stuff for the new transition
448
458
  angular.forEach(slides, function(slide) {
@@ -481,7 +491,7 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
481
491
 
482
492
  $scope.next = function() {
483
493
  var newIndex = (currentIndex + 1) % slides.length;
484
-
494
+
485
495
  //Prevent this user-triggered transition from occurring if there is already one in progress
486
496
  if (!$scope.$currentTransition) {
487
497
  return self.select(slides[newIndex], 'next');
@@ -490,7 +500,7 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
490
500
 
491
501
  $scope.prev = function() {
492
502
  var newIndex = currentIndex - 1 < 0 ? slides.length - 1 : currentIndex - 1;
493
-
503
+
494
504
  //Prevent this user-triggered transition from occurring if there is already one in progress
495
505
  if (!$scope.$currentTransition) {
496
506
  return self.select(slides[newIndex], 'prev');
@@ -707,7 +717,7 @@ function CarouselDemoCtrl($scope) {
707
717
  var lastValue = scope.active = getActive(scope.$parent);
708
718
  scope.$watch(function parentActiveWatch() {
709
719
  var parentActive = getActive(scope.$parent);
710
-
720
+
711
721
  if (parentActive !== scope.active) {
712
722
  // we are out of sync and need to copy
713
723
  if (parentActive !== lastValue) {
@@ -747,13 +757,6 @@ angular.module('ui.bootstrap.position', [])
747
757
  */
748
758
  .factory('$position', ['$document', '$window', function ($document, $window) {
749
759
 
750
- var mouseX, mouseY;
751
-
752
- $document.bind('mousemove', function mouseMoved(event) {
753
- mouseX = event.pageX;
754
- mouseY = event.pageY;
755
- });
756
-
757
760
  function getStyle(el, cssprop) {
758
761
  if (el.currentStyle) { //IE
759
762
  return el.currentStyle[cssprop];
@@ -817,16 +820,9 @@ angular.module('ui.bootstrap.position', [])
817
820
  return {
818
821
  width: element.prop('offsetWidth'),
819
822
  height: element.prop('offsetHeight'),
820
- top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop),
821
- left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft)
823
+ top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
824
+ left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
822
825
  };
823
- },
824
-
825
- /**
826
- * Provides the coordinates of the mouse
827
- */
828
- mouse: function () {
829
- return {x: mouseX, y: mouseY};
830
826
  }
831
827
  };
832
828
  }]);
@@ -1105,26 +1101,9 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1105
1101
  scope.$destroy();
1106
1102
  });
1107
1103
 
1108
- function formatDate(value) {
1109
- return (value) ? dateFilter(value, dateFormat) : null;
1110
- }
1111
- ngModel.$formatters.push(formatDate);
1112
-
1113
- // TODO: reverse from dateFilter string to Date object
1114
- function parseDate(value) {
1115
- if ( value ) {
1116
- var date = new Date(value);
1117
- if (!isNaN(date)) {
1118
- return date;
1119
- }
1120
- }
1121
- return value;
1122
- }
1123
- ngModel.$parsers.push(parseDate);
1124
-
1125
1104
  var getIsOpen, setIsOpen;
1126
- if ( attrs.open ) {
1127
- getIsOpen = $parse(attrs.open);
1105
+ if ( attrs.isOpen ) {
1106
+ getIsOpen = $parse(attrs.isOpen);
1128
1107
  setIsOpen = getIsOpen.assign;
1129
1108
 
1130
1109
  originalScope.$watch(getIsOpen, function updateOpen(value) {
@@ -1166,33 +1145,58 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1166
1145
  datepickerEl.attr(angular.extend({}, originalScope.$eval(attrs.datepickerOptions)));
1167
1146
  }
1168
1147
 
1169
- var $setModelValue = $parse(attrs.ngModel).assign;
1148
+ // TODO: reverse from dateFilter string to Date object
1149
+ function parseDate(viewValue) {
1150
+ if (!viewValue) {
1151
+ ngModel.$setValidity('date', true);
1152
+ return null;
1153
+ } else if (angular.isDate(viewValue)) {
1154
+ ngModel.$setValidity('date', true);
1155
+ return viewValue;
1156
+ } else if (angular.isString(viewValue)) {
1157
+ var date = new Date(viewValue);
1158
+ if (isNaN(date)) {
1159
+ ngModel.$setValidity('date', false);
1160
+ return undefined;
1161
+ } else {
1162
+ ngModel.$setValidity('date', true);
1163
+ return date;
1164
+ }
1165
+ } else {
1166
+ ngModel.$setValidity('date', false);
1167
+ return undefined;
1168
+ }
1169
+ }
1170
+ ngModel.$parsers.unshift(parseDate);
1170
1171
 
1171
1172
  // Inner change
1172
1173
  scope.dateSelection = function() {
1173
- $setModelValue(originalScope, scope.date);
1174
+ ngModel.$setViewValue(scope.date);
1175
+ ngModel.$render();
1176
+
1174
1177
  if (closeOnDateSelection) {
1175
1178
  setOpen( false );
1176
1179
  }
1177
1180
  };
1178
1181
 
1182
+ element.bind('input change keyup', function() {
1183
+ scope.$apply(function() {
1184
+ updateCalendar();
1185
+ });
1186
+ });
1187
+
1179
1188
  // Outter change
1180
- scope.$watch(function() {
1181
- return ngModel.$modelValue;
1182
- }, function(value) {
1183
- if (angular.isString(value)) {
1184
- var date = parseDate(value);
1185
-
1186
- if (value && !date) {
1187
- $setModelValue(originalScope, null);
1188
- throw new Error(value + ' cannot be parsed to a date object.');
1189
- } else {
1190
- value = date;
1191
- }
1192
- }
1193
- scope.date = value;
1189
+ ngModel.$render = function() {
1190
+ var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';
1191
+ element.val(date);
1192
+
1193
+ updateCalendar();
1194
+ };
1195
+
1196
+ function updateCalendar() {
1197
+ scope.date = ngModel.$modelValue;
1194
1198
  updatePosition();
1195
- });
1199
+ }
1196
1200
 
1197
1201
  function addWatchableAttribute(attribute, scopeProperty, datepickerAttribute) {
1198
1202
  if (attribute) {
@@ -1219,15 +1223,22 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1219
1223
  scope.position.top = scope.position.top + element.prop('offsetHeight');
1220
1224
  }
1221
1225
 
1226
+ var documentBindingInitialized = false, elementFocusInitialized = false;
1222
1227
  scope.$watch('isOpen', function(value) {
1223
1228
  if (value) {
1224
1229
  updatePosition();
1225
1230
  $document.bind('click', documentClickBind);
1226
- element.unbind('focus', elementFocusBind);
1227
- element.focus();
1231
+ if(elementFocusInitialized) {
1232
+ element.unbind('focus', elementFocusBind);
1233
+ }
1234
+ element[0].focus();
1235
+ documentBindingInitialized = true;
1228
1236
  } else {
1229
- $document.unbind('click', documentClickBind);
1237
+ if(documentBindingInitialized) {
1238
+ $document.unbind('click', documentClickBind);
1239
+ }
1230
1240
  element.bind('focus', elementFocusBind);
1241
+ elementFocusInitialized = true;
1231
1242
  }
1232
1243
 
1233
1244
  if ( setIsOpen ) {
@@ -1235,6 +1246,8 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1235
1246
  }
1236
1247
  });
1237
1248
 
1249
+ var $setModelValue = $parse(attrs.ngModel).assign;
1250
+
1238
1251
  scope.today = function() {
1239
1252
  $setModelValue(originalScope, new Date());
1240
1253
  };
@@ -1261,286 +1274,6 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
1261
1274
  }
1262
1275
  };
1263
1276
  }]);
1264
- // The `$dialogProvider` can be used to configure global defaults for your
1265
- // `$dialog` service.
1266
- var dialogModule = angular.module('ui.bootstrap.dialog', ['ui.bootstrap.transition']);
1267
-
1268
- dialogModule.controller('MessageBoxController', ['$scope', 'dialog', 'model', function($scope, dialog, model){
1269
- $scope.title = model.title;
1270
- $scope.message = model.message;
1271
- $scope.buttons = model.buttons;
1272
- $scope.close = function(res){
1273
- dialog.close(res);
1274
- };
1275
- }]);
1276
-
1277
- dialogModule.provider("$dialog", function(){
1278
-
1279
- // The default options for all dialogs.
1280
- var defaults = {
1281
- backdrop: true,
1282
- dialogClass: 'modal',
1283
- backdropClass: 'modal-backdrop',
1284
- transitionClass: 'fade',
1285
- triggerClass: 'in',
1286
- resolve:{},
1287
- backdropFade: false,
1288
- dialogFade:false,
1289
- keyboard: true, // close with esc key
1290
- backdropClick: true // only in conjunction with backdrop=true
1291
- /* other options: template, templateUrl, controller */
1292
- };
1293
-
1294
- var globalOptions = {};
1295
-
1296
- var activeBackdrops = {value : 0};
1297
-
1298
- // The `options({})` allows global configuration of all dialogs in the application.
1299
- //
1300
- // var app = angular.module('App', ['ui.bootstrap.dialog'], function($dialogProvider){
1301
- // // don't close dialog when backdrop is clicked by default
1302
- // $dialogProvider.options({backdropClick: false});
1303
- // });
1304
- this.options = function(value){
1305
- globalOptions = value;
1306
- };
1307
-
1308
- // Returns the actual `$dialog` service that is injected in controllers
1309
- this.$get = ["$http", "$document", "$compile", "$rootScope", "$controller", "$templateCache", "$q", "$transition", "$injector",
1310
- function ($http, $document, $compile, $rootScope, $controller, $templateCache, $q, $transition, $injector) {
1311
-
1312
- var body = $document.find('body');
1313
-
1314
- function createElement(clazz) {
1315
- var el = angular.element("<div>");
1316
- el.addClass(clazz);
1317
- return el;
1318
- }
1319
-
1320
- // The `Dialog` class represents a modal dialog. The dialog class can be invoked by providing an options object
1321
- // containing at lest template or templateUrl and controller:
1322
- //
1323
- // var d = new Dialog({templateUrl: 'foo.html', controller: 'BarController'});
1324
- //
1325
- // Dialogs can also be created using templateUrl and controller as distinct arguments:
1326
- //
1327
- // var d = new Dialog('path/to/dialog.html', MyDialogController);
1328
- function Dialog(opts) {
1329
-
1330
- var self = this, options = this.options = angular.extend({}, defaults, globalOptions, opts);
1331
- this._open = false;
1332
-
1333
- this.backdropEl = createElement(options.backdropClass);
1334
- if(options.backdropFade){
1335
- this.backdropEl.addClass(options.transitionClass);
1336
- this.backdropEl.removeClass(options.triggerClass);
1337
- }
1338
-
1339
- this.modalEl = createElement(options.dialogClass);
1340
- if(options.dialogFade){
1341
- this.modalEl.addClass(options.transitionClass);
1342
- this.modalEl.removeClass(options.triggerClass);
1343
- }
1344
-
1345
- this.handledEscapeKey = function(e) {
1346
- if (e.which === 27) {
1347
- self.close();
1348
- e.preventDefault();
1349
- self.$scope.$apply();
1350
- }
1351
- };
1352
-
1353
- this.handleBackDropClick = function(e) {
1354
- self.close();
1355
- e.preventDefault();
1356
- self.$scope.$apply();
1357
- };
1358
- }
1359
-
1360
- // The `isOpen()` method returns wether the dialog is currently visible.
1361
- Dialog.prototype.isOpen = function(){
1362
- return this._open;
1363
- };
1364
-
1365
- // The `open(templateUrl, controller)` method opens the dialog.
1366
- // Use the `templateUrl` and `controller` arguments if specifying them at dialog creation time is not desired.
1367
- Dialog.prototype.open = function(templateUrl, controller){
1368
- var self = this, options = this.options;
1369
-
1370
- if(templateUrl){
1371
- options.templateUrl = templateUrl;
1372
- }
1373
- if(controller){
1374
- options.controller = controller;
1375
- }
1376
-
1377
- if(!(options.template || options.templateUrl)) {
1378
- throw new Error('Dialog.open expected template or templateUrl, neither found. Use options or open method to specify them.');
1379
- }
1380
-
1381
- this._loadResolves().then(function(locals) {
1382
- var $scope = locals.$scope = self.$scope = locals.$scope ? locals.$scope : $rootScope.$new();
1383
-
1384
- self.modalEl.html(locals.$template);
1385
-
1386
- if (self.options.controller) {
1387
- var ctrl = $controller(self.options.controller, locals);
1388
- self.modalEl.children().data('ngControllerController', ctrl);
1389
- }
1390
-
1391
- $compile(self.modalEl)($scope);
1392
- self._addElementsToDom();
1393
-
1394
- // trigger tranisitions
1395
- setTimeout(function(){
1396
- if(self.options.dialogFade){ self.modalEl.addClass(self.options.triggerClass); }
1397
- if(self.options.backdropFade){ self.backdropEl.addClass(self.options.triggerClass); }
1398
- });
1399
-
1400
- self._bindEvents();
1401
- });
1402
-
1403
- this.deferred = $q.defer();
1404
- return this.deferred.promise;
1405
- };
1406
-
1407
- // closes the dialog and resolves the promise returned by the `open` method with the specified result.
1408
- Dialog.prototype.close = function(result){
1409
- var self = this;
1410
- var fadingElements = this._getFadingElements();
1411
-
1412
- if(fadingElements.length > 0){
1413
- for (var i = fadingElements.length - 1; i >= 0; i--) {
1414
- $transition(fadingElements[i], removeTriggerClass).then(onCloseComplete);
1415
- }
1416
- return;
1417
- }
1418
-
1419
- this._onCloseComplete(result);
1420
-
1421
- function removeTriggerClass(el){
1422
- el.removeClass(self.options.triggerClass);
1423
- }
1424
-
1425
- function onCloseComplete(){
1426
- if(self._open){
1427
- self._onCloseComplete(result);
1428
- }
1429
- }
1430
- };
1431
-
1432
- Dialog.prototype._getFadingElements = function(){
1433
- var elements = [];
1434
- if(this.options.dialogFade){
1435
- elements.push(this.modalEl);
1436
- }
1437
- if(this.options.backdropFade){
1438
- elements.push(this.backdropEl);
1439
- }
1440
-
1441
- return elements;
1442
- };
1443
-
1444
- Dialog.prototype._bindEvents = function() {
1445
- if(this.options.keyboard){ body.bind('keydown', this.handledEscapeKey); }
1446
- if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.bind('click', this.handleBackDropClick); }
1447
- };
1448
-
1449
- Dialog.prototype._unbindEvents = function() {
1450
- if(this.options.keyboard){ body.unbind('keydown', this.handledEscapeKey); }
1451
- if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.unbind('click', this.handleBackDropClick); }
1452
- };
1453
-
1454
- Dialog.prototype._onCloseComplete = function(result) {
1455
- this._removeElementsFromDom();
1456
- this._unbindEvents();
1457
-
1458
- this.deferred.resolve(result);
1459
- };
1460
-
1461
- Dialog.prototype._addElementsToDom = function(){
1462
- body.append(this.modalEl);
1463
-
1464
- if(this.options.backdrop) {
1465
- if (activeBackdrops.value === 0) {
1466
- body.append(this.backdropEl);
1467
- }
1468
- activeBackdrops.value++;
1469
- }
1470
-
1471
- this._open = true;
1472
- };
1473
-
1474
- Dialog.prototype._removeElementsFromDom = function(){
1475
- this.modalEl.remove();
1476
-
1477
- if(this.options.backdrop) {
1478
- activeBackdrops.value--;
1479
- if (activeBackdrops.value === 0) {
1480
- this.backdropEl.remove();
1481
- }
1482
- }
1483
- this._open = false;
1484
- };
1485
-
1486
- // Loads all `options.resolve` members to be used as locals for the controller associated with the dialog.
1487
- Dialog.prototype._loadResolves = function(){
1488
- var values = [], keys = [], templatePromise, self = this;
1489
-
1490
- if (this.options.template) {
1491
- templatePromise = $q.when(this.options.template);
1492
- } else if (this.options.templateUrl) {
1493
- templatePromise = $http.get(this.options.templateUrl, {cache:$templateCache})
1494
- .then(function(response) { return response.data; });
1495
- }
1496
-
1497
- angular.forEach(this.options.resolve || [], function(value, key) {
1498
- keys.push(key);
1499
- values.push(angular.isString(value) ? $injector.get(value) : $injector.invoke(value));
1500
- });
1501
-
1502
- keys.push('$template');
1503
- values.push(templatePromise);
1504
-
1505
- return $q.all(values).then(function(values) {
1506
- var locals = {};
1507
- angular.forEach(values, function(value, index) {
1508
- locals[keys[index]] = value;
1509
- });
1510
- locals.dialog = self;
1511
- return locals;
1512
- });
1513
- };
1514
-
1515
- // The actual `$dialog` service that is injected in controllers.
1516
- return {
1517
- // Creates a new `Dialog` with the specified options.
1518
- dialog: function(opts){
1519
- return new Dialog(opts);
1520
- },
1521
- // creates a new `Dialog` tied to the default message box template and controller.
1522
- //
1523
- // Arguments `title` and `message` are rendered in the modal header and body sections respectively.
1524
- // The `buttons` array holds an object with the following members for each button to include in the
1525
- // modal footer section:
1526
- //
1527
- // * `result`: the result to pass to the `close` method of the dialog when the button is clicked
1528
- // * `label`: the label of the button
1529
- // * `cssClass`: additional css class(es) to apply to the button for styling
1530
- messageBox: function(title, message, buttons){
1531
- return new Dialog({templateUrl: 'template/dialog/message.html', controller: 'MessageBoxController', resolve:
1532
- {model: function() {
1533
- return {
1534
- title: title,
1535
- message: message,
1536
- buttons: buttons
1537
- };
1538
- }
1539
- }});
1540
- }
1541
- };
1542
- }];
1543
- });
1544
1277
 
1545
1278
  /*
1546
1279
  * dropdownToggle - Provides dropdown menu functionality in place of bootstrap js
@@ -1594,93 +1327,391 @@ angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle', ['
1594
1327
  }
1595
1328
  };
1596
1329
  }]);
1597
- angular.module('ui.bootstrap.modal', ['ui.bootstrap.dialog'])
1598
- .directive('modal', ['$parse', '$dialog', function($parse, $dialog) {
1599
- return {
1600
- restrict: 'EA',
1601
- terminal: true,
1602
- link: function(scope, elm, attrs) {
1603
- var opts = angular.extend({}, scope.$eval(attrs.uiOptions || attrs.bsOptions || attrs.options));
1604
- var shownExpr = attrs.modal || attrs.show;
1605
- var setClosed;
1606
-
1607
- // Create a dialog with the template as the contents of the directive
1608
- // Add the current scope as the resolve in order to make the directive scope as a dialog controller scope
1609
- opts = angular.extend(opts, {
1610
- template: elm.html(),
1611
- resolve: { $scope: function() { return scope; } }
1612
- });
1613
- var dialog = $dialog.dialog(opts);
1330
+ angular.module('ui.bootstrap.modal', [])
1614
1331
 
1615
- elm.remove();
1332
+ /**
1333
+ * A helper, internal data structure that acts as a map but also allows getting / removing
1334
+ * elements in the LIFO order
1335
+ */
1336
+ .factory('$$stackedMap', function () {
1337
+ return {
1338
+ createNew: function () {
1339
+ var stack = [];
1616
1340
 
1617
- if (attrs.close) {
1618
- setClosed = function() {
1619
- $parse(attrs.close)(scope);
1341
+ return {
1342
+ add: function (key, value) {
1343
+ stack.push({
1344
+ key: key,
1345
+ value: value
1346
+ });
1347
+ },
1348
+ get: function (key) {
1349
+ for (var i = 0; i < stack.length; i++) {
1350
+ if (key == stack[i].key) {
1351
+ return stack[i];
1352
+ }
1353
+ }
1354
+ },
1355
+ keys: function() {
1356
+ var keys = [];
1357
+ for (var i = 0; i < stack.length; i++) {
1358
+ keys.push(stack[i].key);
1359
+ }
1360
+ return keys;
1361
+ },
1362
+ top: function () {
1363
+ return stack[stack.length - 1];
1364
+ },
1365
+ remove: function (key) {
1366
+ var idx = -1;
1367
+ for (var i = 0; i < stack.length; i++) {
1368
+ if (key == stack[i].key) {
1369
+ idx = i;
1370
+ break;
1371
+ }
1372
+ }
1373
+ return stack.splice(idx, 1)[0];
1374
+ },
1375
+ removeTop: function () {
1376
+ return stack.splice(stack.length - 1, 1)[0];
1377
+ },
1378
+ length: function () {
1379
+ return stack.length;
1380
+ }
1620
1381
  };
1621
- } else {
1622
- setClosed = function() {
1623
- if (angular.isFunction($parse(shownExpr).assign)) {
1624
- $parse(shownExpr).assign(scope, false);
1382
+ }
1383
+ };
1384
+ })
1385
+
1386
+ /**
1387
+ * A helper directive for the $modal service. It creates a backdrop element.
1388
+ */
1389
+ .directive('modalBackdrop', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
1390
+ return {
1391
+ restrict: 'EA',
1392
+ replace: true,
1393
+ templateUrl: 'template/modal/backdrop.html',
1394
+ link: function (scope, element, attrs) {
1395
+
1396
+ //trigger CSS transitions
1397
+ $timeout(function () {
1398
+ scope.animate = true;
1399
+ });
1400
+
1401
+ scope.close = function (evt) {
1402
+ var modal = $modalStack.getTop();
1403
+ if (modal && modal.value.backdrop && modal.value.backdrop != 'static') {
1404
+ evt.preventDefault();
1405
+ evt.stopPropagation();
1406
+ $modalStack.dismiss(modal.key, 'backdrop click');
1625
1407
  }
1626
1408
  };
1627
1409
  }
1410
+ };
1411
+ }])
1628
1412
 
1629
- scope.$watch(shownExpr, function(isShown, oldShown) {
1630
- if (isShown) {
1631
- dialog.open().then(function(){
1632
- setClosed();
1633
- });
1634
- } else {
1635
- //Make sure it is not opened
1636
- if (dialog.isOpen()){
1637
- dialog.close();
1413
+ .directive('modalWindow', ['$timeout', function ($timeout) {
1414
+ return {
1415
+ restrict: 'EA',
1416
+ scope: {
1417
+ index: '@'
1418
+ },
1419
+ replace: true,
1420
+ transclude: true,
1421
+ templateUrl: 'template/modal/window.html',
1422
+ link: function (scope, element, attrs) {
1423
+ scope.windowClass = attrs.windowClass || '';
1424
+
1425
+ //trigger CSS transitions
1426
+ $timeout(function () {
1427
+ scope.animate = true;
1428
+ });
1429
+ }
1430
+ };
1431
+ }])
1432
+
1433
+ .factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap',
1434
+ function ($document, $compile, $rootScope, $$stackedMap) {
1435
+
1436
+ var backdropjqLiteEl, backdropDomEl;
1437
+ var backdropScope = $rootScope.$new(true);
1438
+ var body = $document.find('body').eq(0);
1439
+ var openedWindows = $$stackedMap.createNew();
1440
+ var $modalStack = {};
1441
+
1442
+ function backdropIndex() {
1443
+ var topBackdropIndex = -1;
1444
+ var opened = openedWindows.keys();
1445
+ for (var i = 0; i < opened.length; i++) {
1446
+ if (openedWindows.get(opened[i]).value.backdrop) {
1447
+ topBackdropIndex = i;
1638
1448
  }
1639
1449
  }
1450
+ return topBackdropIndex;
1451
+ }
1452
+
1453
+ $rootScope.$watch(backdropIndex, function(newBackdropIndex){
1454
+ backdropScope.index = newBackdropIndex;
1640
1455
  });
1641
- }
1642
- };
1643
- }]);
1456
+
1457
+ function removeModalWindow(modalInstance) {
1458
+
1459
+ var modalWindow = openedWindows.get(modalInstance).value;
1460
+
1461
+ //clean up the stack
1462
+ openedWindows.remove(modalInstance);
1463
+
1464
+ //remove window DOM element
1465
+ modalWindow.modalDomEl.remove();
1466
+
1467
+ //remove backdrop if no longer needed
1468
+ if (backdropIndex() == -1) {
1469
+ backdropDomEl.remove();
1470
+ backdropDomEl = undefined;
1471
+ }
1472
+
1473
+ //destroy scope
1474
+ modalWindow.modalScope.$destroy();
1475
+ }
1476
+
1477
+ $document.bind('keydown', function (evt) {
1478
+ var modal;
1479
+
1480
+ if (evt.which === 27) {
1481
+ modal = openedWindows.top();
1482
+ if (modal && modal.value.keyboard) {
1483
+ $rootScope.$apply(function () {
1484
+ $modalStack.dismiss(modal.key);
1485
+ });
1486
+ }
1487
+ }
1488
+ });
1489
+
1490
+ $modalStack.open = function (modalInstance, modal) {
1491
+
1492
+ openedWindows.add(modalInstance, {
1493
+ deferred: modal.deferred,
1494
+ modalScope: modal.scope,
1495
+ backdrop: modal.backdrop,
1496
+ keyboard: modal.keyboard
1497
+ });
1498
+
1499
+ var angularDomEl = angular.element('<div modal-window></div>');
1500
+ angularDomEl.attr('window-class', modal.windowClass);
1501
+ angularDomEl.attr('index', openedWindows.length() - 1);
1502
+ angularDomEl.html(modal.content);
1503
+
1504
+ var modalDomEl = $compile(angularDomEl)(modal.scope);
1505
+ openedWindows.top().value.modalDomEl = modalDomEl;
1506
+ body.append(modalDomEl);
1507
+
1508
+ if (backdropIndex() >= 0 && !backdropDomEl) {
1509
+ backdropjqLiteEl = angular.element('<div modal-backdrop></div>');
1510
+ backdropDomEl = $compile(backdropjqLiteEl)(backdropScope);
1511
+ body.append(backdropDomEl);
1512
+ }
1513
+ };
1514
+
1515
+ $modalStack.close = function (modalInstance, result) {
1516
+ var modal = openedWindows.get(modalInstance);
1517
+ if (modal) {
1518
+ modal.value.deferred.resolve(result);
1519
+ removeModalWindow(modalInstance);
1520
+ }
1521
+ };
1522
+
1523
+ $modalStack.dismiss = function (modalInstance, reason) {
1524
+ var modalWindow = openedWindows.get(modalInstance).value;
1525
+ if (modalWindow) {
1526
+ modalWindow.deferred.reject(reason);
1527
+ removeModalWindow(modalInstance);
1528
+ }
1529
+ };
1530
+
1531
+ $modalStack.getTop = function () {
1532
+ return openedWindows.top();
1533
+ };
1534
+
1535
+ return $modalStack;
1536
+ }])
1537
+
1538
+ .provider('$modal', function () {
1539
+
1540
+ var $modalProvider = {
1541
+ options: {
1542
+ backdrop: true, //can be also false or 'static'
1543
+ keyboard: true
1544
+ },
1545
+ $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack',
1546
+ function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
1547
+
1548
+ var $modal = {};
1549
+
1550
+ function getTemplatePromise(options) {
1551
+ return options.template ? $q.when(options.template) :
1552
+ $http.get(options.templateUrl, {cache: $templateCache}).then(function (result) {
1553
+ return result.data;
1554
+ });
1555
+ }
1556
+
1557
+ function getResolvePromises(resolves) {
1558
+ var promisesArr = [];
1559
+ angular.forEach(resolves, function (value, key) {
1560
+ if (angular.isFunction(value) || angular.isArray(value)) {
1561
+ promisesArr.push($q.when($injector.invoke(value)));
1562
+ }
1563
+ });
1564
+ return promisesArr;
1565
+ }
1566
+
1567
+ $modal.open = function (modalOptions) {
1568
+
1569
+ var modalResultDeferred = $q.defer();
1570
+ var modalOpenedDeferred = $q.defer();
1571
+
1572
+ //prepare an instance of a modal to be injected into controllers and returned to a caller
1573
+ var modalInstance = {
1574
+ result: modalResultDeferred.promise,
1575
+ opened: modalOpenedDeferred.promise,
1576
+ close: function (result) {
1577
+ $modalStack.close(modalInstance, result);
1578
+ },
1579
+ dismiss: function (reason) {
1580
+ $modalStack.dismiss(modalInstance, reason);
1581
+ }
1582
+ };
1583
+
1584
+ //merge and clean up options
1585
+ modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
1586
+ modalOptions.resolve = modalOptions.resolve || {};
1587
+
1588
+ //verify options
1589
+ if (!modalOptions.template && !modalOptions.templateUrl) {
1590
+ throw new Error('One of template or templateUrl options is required.');
1591
+ }
1592
+
1593
+ var templateAndResolvePromise =
1594
+ $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
1595
+
1596
+
1597
+ templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
1598
+
1599
+ var modalScope = (modalOptions.scope || $rootScope).$new();
1600
+ modalScope.$close = modalInstance.close;
1601
+ modalScope.$dismiss = modalInstance.dismiss;
1602
+
1603
+ var ctrlInstance, ctrlLocals = {};
1604
+ var resolveIter = 1;
1605
+
1606
+ //controllers
1607
+ if (modalOptions.controller) {
1608
+ ctrlLocals.$scope = modalScope;
1609
+ ctrlLocals.$modalInstance = modalInstance;
1610
+ angular.forEach(modalOptions.resolve, function (value, key) {
1611
+ ctrlLocals[key] = tplAndVars[resolveIter++];
1612
+ });
1613
+
1614
+ ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
1615
+ }
1616
+
1617
+ $modalStack.open(modalInstance, {
1618
+ scope: modalScope,
1619
+ deferred: modalResultDeferred,
1620
+ content: tplAndVars[0],
1621
+ backdrop: modalOptions.backdrop,
1622
+ keyboard: modalOptions.keyboard,
1623
+ windowClass: modalOptions.windowClass
1624
+ });
1625
+
1626
+ }, function resolveError(reason) {
1627
+ modalResultDeferred.reject(reason);
1628
+ });
1629
+
1630
+ templateAndResolvePromise.then(function () {
1631
+ modalOpenedDeferred.resolve(true);
1632
+ }, function () {
1633
+ modalOpenedDeferred.reject(false);
1634
+ });
1635
+
1636
+ return modalInstance;
1637
+ };
1638
+
1639
+ return $modal;
1640
+ }]
1641
+ };
1642
+
1643
+ return $modalProvider;
1644
+ });
1644
1645
  angular.module('ui.bootstrap.pagination', [])
1645
1646
 
1646
- .controller('PaginationController', ['$scope', '$interpolate', function ($scope, $interpolate) {
1647
+ .controller('PaginationController', ['$scope', '$attrs', '$parse', '$interpolate', function ($scope, $attrs, $parse, $interpolate) {
1648
+ var self = this;
1647
1649
 
1648
- this.currentPage = 1;
1650
+ this.init = function(defaultItemsPerPage) {
1651
+ if ($attrs.itemsPerPage) {
1652
+ $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {
1653
+ self.itemsPerPage = parseInt(value, 10);
1654
+ $scope.totalPages = self.calculateTotalPages();
1655
+ });
1656
+ } else {
1657
+ this.itemsPerPage = defaultItemsPerPage;
1658
+ }
1659
+ };
1649
1660
 
1650
1661
  this.noPrevious = function() {
1651
- return this.currentPage === 1;
1662
+ return this.page === 1;
1652
1663
  };
1653
1664
  this.noNext = function() {
1654
- return this.currentPage === $scope.numPages;
1665
+ return this.page === $scope.totalPages;
1655
1666
  };
1656
1667
 
1657
1668
  this.isActive = function(page) {
1658
- return this.currentPage === page;
1669
+ return this.page === page;
1659
1670
  };
1660
1671
 
1661
- this.reset = function() {
1662
- $scope.pages = [];
1663
- this.currentPage = parseInt($scope.currentPage, 10);
1672
+ this.calculateTotalPages = function() {
1673
+ return this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage);
1674
+ };
1664
1675
 
1665
- if ( this.currentPage > $scope.numPages ) {
1666
- $scope.selectPage($scope.numPages);
1667
- }
1676
+ this.getAttributeValue = function(attribute, defaultValue, interpolate) {
1677
+ return angular.isDefined(attribute) ? (interpolate ? $interpolate(attribute)($scope.$parent) : $scope.$parent.$eval(attribute)) : defaultValue;
1678
+ };
1679
+
1680
+ this.render = function() {
1681
+ this.page = parseInt($scope.page, 10) || 1;
1682
+ $scope.pages = this.getPages(this.page, $scope.totalPages);
1668
1683
  };
1669
1684
 
1670
- var self = this;
1671
1685
  $scope.selectPage = function(page) {
1672
- if ( ! self.isActive(page) && page > 0 && page <= $scope.numPages) {
1673
- $scope.currentPage = page;
1686
+ if ( ! self.isActive(page) && page > 0 && page <= $scope.totalPages) {
1687
+ $scope.page = page;
1674
1688
  $scope.onSelectPage({ page: page });
1675
1689
  }
1676
1690
  };
1677
1691
 
1678
- this.getAttributeValue = function(attribute, defaultValue, interpolate) {
1679
- return angular.isDefined(attribute) ? (interpolate ? $interpolate(attribute)($scope.$parent) : $scope.$parent.$eval(attribute)) : defaultValue;
1680
- };
1692
+ $scope.$watch('totalItems', function() {
1693
+ $scope.totalPages = self.calculateTotalPages();
1694
+ });
1695
+
1696
+ $scope.$watch('totalPages', function(value) {
1697
+ if ( $attrs.numPages ) {
1698
+ $scope.numPages = value; // Readonly variable
1699
+ }
1700
+
1701
+ if ( self.page > value ) {
1702
+ $scope.selectPage(value);
1703
+ } else {
1704
+ self.render();
1705
+ }
1706
+ });
1707
+
1708
+ $scope.$watch('page', function() {
1709
+ self.render();
1710
+ });
1681
1711
  }])
1682
1712
 
1683
1713
  .constant('paginationConfig', {
1714
+ itemsPerPage: 10,
1684
1715
  boundaryLinks: false,
1685
1716
  directionLinks: true,
1686
1717
  firstText: 'First',
@@ -1690,14 +1721,14 @@ angular.module('ui.bootstrap.pagination', [])
1690
1721
  rotate: true
1691
1722
  })
1692
1723
 
1693
- .directive('pagination', ['paginationConfig', function(config) {
1724
+ .directive('pagination', ['$parse', 'paginationConfig', function($parse, config) {
1694
1725
  return {
1695
1726
  restrict: 'EA',
1696
1727
  scope: {
1697
- numPages: '=',
1698
- currentPage: '=',
1699
- maxSize: '=',
1700
- onSelectPage: '&'
1728
+ page: '=',
1729
+ totalItems: '=',
1730
+ onSelectPage:' &',
1731
+ numPages: '='
1701
1732
  },
1702
1733
  controller: 'PaginationController',
1703
1734
  templateUrl: 'template/pagination/pagination.html',
@@ -1705,13 +1736,23 @@ angular.module('ui.bootstrap.pagination', [])
1705
1736
  link: function(scope, element, attrs, paginationCtrl) {
1706
1737
 
1707
1738
  // Setup configuration parameters
1708
- var boundaryLinks = paginationCtrl.getAttributeValue(attrs.boundaryLinks, config.boundaryLinks ),
1709
- directionLinks = paginationCtrl.getAttributeValue(attrs.directionLinks, config.directionLinks ),
1710
- firstText = paginationCtrl.getAttributeValue(attrs.firstText, config.firstText, true),
1711
- previousText = paginationCtrl.getAttributeValue(attrs.previousText, config.previousText, true),
1712
- nextText = paginationCtrl.getAttributeValue(attrs.nextText, config.nextText, true),
1713
- lastText = paginationCtrl.getAttributeValue(attrs.lastText, config.lastText, true),
1714
- rotate = paginationCtrl.getAttributeValue(attrs.rotate, config.rotate);
1739
+ var maxSize,
1740
+ boundaryLinks = paginationCtrl.getAttributeValue(attrs.boundaryLinks, config.boundaryLinks ),
1741
+ directionLinks = paginationCtrl.getAttributeValue(attrs.directionLinks, config.directionLinks ),
1742
+ firstText = paginationCtrl.getAttributeValue(attrs.firstText, config.firstText, true),
1743
+ previousText = paginationCtrl.getAttributeValue(attrs.previousText, config.previousText, true),
1744
+ nextText = paginationCtrl.getAttributeValue(attrs.nextText, config.nextText, true),
1745
+ lastText = paginationCtrl.getAttributeValue(attrs.lastText, config.lastText, true),
1746
+ rotate = paginationCtrl.getAttributeValue(attrs.rotate, config.rotate);
1747
+
1748
+ paginationCtrl.init(config.itemsPerPage);
1749
+
1750
+ if (attrs.maxSize) {
1751
+ scope.$parent.$watch($parse(attrs.maxSize), function(value) {
1752
+ maxSize = parseInt(value, 10);
1753
+ paginationCtrl.render();
1754
+ });
1755
+ }
1715
1756
 
1716
1757
  // Create page object used in template
1717
1758
  function makePage(number, text, isActive, isDisabled) {
@@ -1723,76 +1764,79 @@ angular.module('ui.bootstrap.pagination', [])
1723
1764
  };
1724
1765
  }
1725
1766
 
1726
- scope.$watch('numPages + currentPage + maxSize', function() {
1727
- paginationCtrl.reset();
1767
+ paginationCtrl.getPages = function(currentPage, totalPages) {
1768
+ var pages = [];
1728
1769
 
1729
1770
  // Default page limits
1730
- var startPage = 1, endPage = scope.numPages;
1731
- var isMaxSized = ( angular.isDefined(scope.maxSize) && scope.maxSize < scope.numPages );
1771
+ var startPage = 1, endPage = totalPages;
1772
+ var isMaxSized = ( angular.isDefined(maxSize) && maxSize < totalPages );
1732
1773
 
1733
1774
  // recompute if maxSize
1734
1775
  if ( isMaxSized ) {
1735
1776
  if ( rotate ) {
1736
1777
  // Current page is displayed in the middle of the visible ones
1737
- startPage = Math.max(paginationCtrl.currentPage - Math.floor(scope.maxSize/2), 1);
1738
- endPage = startPage + scope.maxSize - 1;
1778
+ startPage = Math.max(currentPage - Math.floor(maxSize/2), 1);
1779
+ endPage = startPage + maxSize - 1;
1739
1780
 
1740
1781
  // Adjust if limit is exceeded
1741
- if (endPage > scope.numPages) {
1742
- endPage = scope.numPages;
1743
- startPage = endPage - scope.maxSize + 1;
1782
+ if (endPage > totalPages) {
1783
+ endPage = totalPages;
1784
+ startPage = endPage - maxSize + 1;
1744
1785
  }
1745
1786
  } else {
1746
1787
  // Visible pages are paginated with maxSize
1747
- startPage = ((Math.ceil(paginationCtrl.currentPage / scope.maxSize) - 1) * scope.maxSize) + 1;
1788
+ startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1;
1748
1789
 
1749
1790
  // Adjust last page if limit is exceeded
1750
- endPage = Math.min(startPage + scope.maxSize - 1, scope.numPages);
1791
+ endPage = Math.min(startPage + maxSize - 1, totalPages);
1751
1792
  }
1752
1793
  }
1753
1794
 
1754
1795
  // Add page number links
1755
1796
  for (var number = startPage; number <= endPage; number++) {
1756
1797
  var page = makePage(number, number, paginationCtrl.isActive(number), false);
1757
- scope.pages.push(page);
1798
+ pages.push(page);
1758
1799
  }
1759
1800
 
1760
1801
  // Add links to move between page sets
1761
1802
  if ( isMaxSized && ! rotate ) {
1762
1803
  if ( startPage > 1 ) {
1763
1804
  var previousPageSet = makePage(startPage - 1, '...', false, false);
1764
- scope.pages.unshift(previousPageSet);
1805
+ pages.unshift(previousPageSet);
1765
1806
  }
1766
1807
 
1767
- if ( endPage < scope.numPages ) {
1808
+ if ( endPage < totalPages ) {
1768
1809
  var nextPageSet = makePage(endPage + 1, '...', false, false);
1769
- scope.pages.push(nextPageSet);
1810
+ pages.push(nextPageSet);
1770
1811
  }
1771
1812
  }
1772
1813
 
1773
1814
  // Add previous & next links
1774
1815
  if (directionLinks) {
1775
- var previousPage = makePage(paginationCtrl.currentPage - 1, previousText, false, paginationCtrl.noPrevious());
1776
- scope.pages.unshift(previousPage);
1816
+ var previousPage = makePage(currentPage - 1, previousText, false, paginationCtrl.noPrevious());
1817
+ pages.unshift(previousPage);
1777
1818
 
1778
- var nextPage = makePage(paginationCtrl.currentPage + 1, nextText, false, paginationCtrl.noNext());
1779
- scope.pages.push(nextPage);
1819
+ var nextPage = makePage(currentPage + 1, nextText, false, paginationCtrl.noNext());
1820
+ pages.push(nextPage);
1780
1821
  }
1781
1822
 
1782
1823
  // Add first & last links
1783
1824
  if (boundaryLinks) {
1784
1825
  var firstPage = makePage(1, firstText, false, paginationCtrl.noPrevious());
1785
- scope.pages.unshift(firstPage);
1826
+ pages.unshift(firstPage);
1786
1827
 
1787
- var lastPage = makePage(scope.numPages, lastText, false, paginationCtrl.noNext());
1788
- scope.pages.push(lastPage);
1828
+ var lastPage = makePage(totalPages, lastText, false, paginationCtrl.noNext());
1829
+ pages.push(lastPage);
1789
1830
  }
1790
- });
1831
+
1832
+ return pages;
1833
+ };
1791
1834
  }
1792
1835
  };
1793
1836
  }])
1794
1837
 
1795
1838
  .constant('pagerConfig', {
1839
+ itemsPerPage: 10,
1796
1840
  previousText: '« Previous',
1797
1841
  nextText: 'Next »',
1798
1842
  align: true
@@ -1802,9 +1846,10 @@ angular.module('ui.bootstrap.pagination', [])
1802
1846
  return {
1803
1847
  restrict: 'EA',
1804
1848
  scope: {
1805
- numPages: '=',
1806
- currentPage: '=',
1807
- onSelectPage: '&'
1849
+ page: '=',
1850
+ totalItems: '=',
1851
+ onSelectPage:' &',
1852
+ numPages: '='
1808
1853
  },
1809
1854
  controller: 'PaginationController',
1810
1855
  templateUrl: 'template/pagination/pager.html',
@@ -1816,6 +1861,8 @@ angular.module('ui.bootstrap.pagination', [])
1816
1861
  nextText = paginationCtrl.getAttributeValue(attrs.nextText, config.nextText, true),
1817
1862
  align = paginationCtrl.getAttributeValue(attrs.align, config.align);
1818
1863
 
1864
+ paginationCtrl.init(config.itemsPerPage);
1865
+
1819
1866
  // Create page object used in template
1820
1867
  function makePage(number, text, isDisabled, isPrevious, isNext) {
1821
1868
  return {
@@ -1827,16 +1874,12 @@ angular.module('ui.bootstrap.pagination', [])
1827
1874
  };
1828
1875
  }
1829
1876
 
1830
- scope.$watch('numPages + currentPage', function() {
1831
- paginationCtrl.reset();
1832
-
1833
- // Add previous & next links
1834
- var previousPage = makePage(paginationCtrl.currentPage - 1, previousText, paginationCtrl.noPrevious(), true, false);
1835
- scope.pages.unshift(previousPage);
1836
-
1837
- var nextPage = makePage(paginationCtrl.currentPage + 1, nextText, paginationCtrl.noNext(), false, true);
1838
- scope.pages.push(nextPage);
1839
- });
1877
+ paginationCtrl.getPages = function(currentPage) {
1878
+ return [
1879
+ makePage(currentPage - 1, previousText, paginationCtrl.noPrevious(), true, false),
1880
+ makePage(currentPage + 1, nextText, paginationCtrl.noNext(), false, true)
1881
+ ];
1882
+ };
1840
1883
  }
1841
1884
  };
1842
1885
  }]);
@@ -1846,7 +1889,7 @@ angular.module('ui.bootstrap.pagination', [])
1846
1889
  * function, placement as a function, inside, support for more triggers than
1847
1890
  * just mouse enter/leave, html tooltips, and selector delegation.
1848
1891
  */
1849
- angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
1892
+ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap.bindHtml' ] )
1850
1893
 
1851
1894
  /**
1852
1895
  * The $tooltip service creates tooltip- and popover-like directives as well as
@@ -2027,13 +2070,6 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
2027
2070
  // Calculate the tooltip's top and left coordinates to center it with
2028
2071
  // this directive.
2029
2072
  switch ( scope.tt_placement ) {
2030
- case 'mouse':
2031
- var mousePos = $position.mouse();
2032
- ttPosition = {
2033
- top: mousePos.y,
2034
- left: mousePos.x
2035
- };
2036
- break;
2037
2073
  case 'right':
2038
2074
  ttPosition = {
2039
2075
  top: position.top + position.height / 2 - ttHeight / 2,
@@ -2314,60 +2350,86 @@ angular.module('ui.bootstrap.progressbar', ['ui.bootstrap.transition'])
2314
2350
  angular.module('ui.bootstrap.rating', [])
2315
2351
 
2316
2352
  .constant('ratingConfig', {
2317
- max: 5
2353
+ max: 5,
2354
+ stateOn: null,
2355
+ stateOff: null
2318
2356
  })
2319
2357
 
2320
- .directive('rating', ['ratingConfig', '$parse', function(ratingConfig, $parse) {
2321
- return {
2322
- restrict: 'EA',
2323
- scope: {
2324
- value: '=',
2325
- onHover: '&',
2326
- onLeave: '&'
2327
- },
2328
- templateUrl: 'template/rating/rating.html',
2329
- replace: true,
2330
- link: function(scope, element, attrs) {
2358
+ .controller('RatingController', ['$scope', '$attrs', '$parse', 'ratingConfig', function($scope, $attrs, $parse, ratingConfig) {
2331
2359
 
2332
- var maxRange = angular.isDefined(attrs.max) ? scope.$parent.$eval(attrs.max) : ratingConfig.max;
2360
+ this.maxRange = angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max;
2361
+ this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
2362
+ this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
2333
2363
 
2334
- scope.range = [];
2335
- for (var i = 1; i <= maxRange; i++) {
2336
- scope.range.push(i);
2337
- }
2364
+ this.createDefaultRange = function(len) {
2365
+ var defaultStateObject = {
2366
+ stateOn: this.stateOn,
2367
+ stateOff: this.stateOff
2368
+ };
2338
2369
 
2339
- scope.rate = function(value) {
2340
- if ( ! scope.readonly ) {
2341
- scope.value = value;
2342
- }
2343
- };
2370
+ var states = new Array(len);
2371
+ for (var i = 0; i < len; i++) {
2372
+ states[i] = defaultStateObject;
2373
+ }
2374
+ return states;
2375
+ };
2344
2376
 
2345
- scope.enter = function(value) {
2346
- if ( ! scope.readonly ) {
2347
- scope.val = value;
2348
- }
2349
- scope.onHover({value: value});
2350
- };
2377
+ this.normalizeRange = function(states) {
2378
+ for (var i = 0, n = states.length; i < n; i++) {
2379
+ states[i].stateOn = states[i].stateOn || this.stateOn;
2380
+ states[i].stateOff = states[i].stateOff || this.stateOff;
2381
+ }
2382
+ return states;
2383
+ };
2351
2384
 
2352
- scope.reset = function() {
2353
- scope.val = angular.copy(scope.value);
2354
- scope.onLeave();
2355
- };
2356
- scope.reset();
2385
+ // Get objects used in template
2386
+ $scope.range = angular.isDefined($attrs.ratingStates) ? this.normalizeRange(angular.copy($scope.$parent.$eval($attrs.ratingStates))): this.createDefaultRange(this.maxRange);
2357
2387
 
2358
- scope.$watch('value', function(value) {
2359
- scope.val = value;
2360
- });
2388
+ $scope.rate = function(value) {
2389
+ if ( $scope.readonly || $scope.value === value) {
2390
+ return;
2391
+ }
2361
2392
 
2362
- scope.readonly = false;
2363
- if (attrs.readonly) {
2364
- scope.$parent.$watch($parse(attrs.readonly), function(value) {
2365
- scope.readonly = !!value;
2366
- });
2367
- }
2393
+ $scope.value = value;
2394
+ };
2395
+
2396
+ $scope.enter = function(value) {
2397
+ if ( ! $scope.readonly ) {
2398
+ $scope.val = value;
2368
2399
  }
2400
+ $scope.onHover({value: value});
2369
2401
  };
2370
- }]);
2402
+
2403
+ $scope.reset = function() {
2404
+ $scope.val = angular.copy($scope.value);
2405
+ $scope.onLeave();
2406
+ };
2407
+
2408
+ $scope.$watch('value', function(value) {
2409
+ $scope.val = value;
2410
+ });
2411
+
2412
+ $scope.readonly = false;
2413
+ if ($attrs.readonly) {
2414
+ $scope.$parent.$watch($parse($attrs.readonly), function(value) {
2415
+ $scope.readonly = !!value;
2416
+ });
2417
+ }
2418
+ }])
2419
+
2420
+ .directive('rating', function() {
2421
+ return {
2422
+ restrict: 'EA',
2423
+ scope: {
2424
+ value: '=',
2425
+ onHover: '&',
2426
+ onLeave: '&'
2427
+ },
2428
+ controller: 'RatingController',
2429
+ templateUrl: 'template/rating/rating.html',
2430
+ replace: true
2431
+ };
2432
+ });
2371
2433
 
2372
2434
  /**
2373
2435
  * @ngdoc overview
@@ -2455,7 +2517,7 @@ function TabsetCtrl($scope, $element) {
2455
2517
  templateUrl: 'template/tabs/tabset.html',
2456
2518
  compile: function(elm, attrs, transclude) {
2457
2519
  return function(scope, element, attrs, tabsetCtrl) {
2458
- scope.vertical = angular.isDefined(attrs.vertical) ? scope.$eval(attrs.vertical) : false;
2520
+ scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
2459
2521
  scope.type = angular.isDefined(attrs.type) ? scope.$parent.$eval(attrs.type) : 'tabs';
2460
2522
  scope.direction = angular.isDefined(attrs.direction) ? scope.$parent.$eval(attrs.direction) : 'top';
2461
2523
  scope.tabsAbove = (scope.direction != 'below');
@@ -2663,7 +2725,7 @@ function($parse, $http, $templateCache, $compile) {
2663
2725
  }
2664
2726
  }])
2665
2727
 
2666
- .directive('tabsetTitles', function($http) {
2728
+ .directive('tabsetTitles', ['$http', function($http) {
2667
2729
  return {
2668
2730
  restrict: 'A',
2669
2731
  require: '^tabset',
@@ -2680,22 +2742,13 @@ function($parse, $http, $templateCache, $compile) {
2680
2742
  }
2681
2743
  }
2682
2744
  };
2683
- })
2745
+ }])
2684
2746
 
2685
2747
  ;
2686
2748
 
2687
2749
 
2688
2750
  angular.module('ui.bootstrap.timepicker', [])
2689
2751
 
2690
- .filter('pad', function() {
2691
- return function(input) {
2692
- if ( angular.isDefined(input) && input.toString().length < 2 ) {
2693
- input = '0' + input;
2694
- }
2695
- return input;
2696
- };
2697
- })
2698
-
2699
2752
  .constant('timepickerConfig', {
2700
2753
  hourStep: 1,
2701
2754
  minuteStep: 1,
@@ -2705,16 +2758,18 @@ angular.module('ui.bootstrap.timepicker', [])
2705
2758
  mousewheel: true
2706
2759
  })
2707
2760
 
2708
- .directive('timepicker', ['padFilter', '$parse', 'timepickerConfig', function (padFilter, $parse, timepickerConfig) {
2761
+ .directive('timepicker', ['$parse', '$log', 'timepickerConfig', function ($parse, $log, timepickerConfig) {
2709
2762
  return {
2710
2763
  restrict: 'EA',
2711
- require:'ngModel',
2764
+ require:'?^ngModel',
2712
2765
  replace: true,
2766
+ scope: {},
2713
2767
  templateUrl: 'template/timepicker/timepicker.html',
2714
- scope: {
2715
- model: '=ngModel'
2716
- },
2717
- link: function(scope, element, attrs, ngModelCtrl) {
2768
+ link: function(scope, element, attrs, ngModel) {
2769
+ if ( !ngModel ) {
2770
+ return; // do nothing if no ng-model
2771
+ }
2772
+
2718
2773
  var selected = new Date(), meridians = timepickerConfig.meridians;
2719
2774
 
2720
2775
  var hourStep = timepickerConfig.hourStep;
@@ -2735,28 +2790,27 @@ angular.module('ui.bootstrap.timepicker', [])
2735
2790
  scope.showMeridian = timepickerConfig.showMeridian;
2736
2791
  if (attrs.showMeridian) {
2737
2792
  scope.$parent.$watch($parse(attrs.showMeridian), function(value) {
2738
- scope.showMeridian = !! value;
2739
-
2740
- if ( ! scope.model ) {
2741
- // Reset
2742
- var dt = new Date( selected );
2743
- var hours = getScopeHours();
2744
- if (angular.isDefined( hours )) {
2745
- dt.setHours( hours );
2793
+ scope.showMeridian = !!value;
2794
+
2795
+ if ( ngModel.$error.time ) {
2796
+ // Evaluate from template
2797
+ var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
2798
+ if (angular.isDefined( hours ) && angular.isDefined( minutes )) {
2799
+ selected.setHours( hours );
2800
+ refresh();
2746
2801
  }
2747
- scope.model = new Date( dt );
2748
2802
  } else {
2749
- refreshTemplate();
2803
+ updateTemplate();
2750
2804
  }
2751
2805
  });
2752
2806
  }
2753
2807
 
2754
2808
  // Get scope.hours in 24H mode if valid
2755
- function getScopeHours ( ) {
2809
+ function getHoursFromTemplate ( ) {
2756
2810
  var hours = parseInt( scope.hours, 10 );
2757
2811
  var valid = ( scope.showMeridian ) ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);
2758
2812
  if ( !valid ) {
2759
- return;
2813
+ return undefined;
2760
2814
  }
2761
2815
 
2762
2816
  if ( scope.showMeridian ) {
@@ -2770,14 +2824,22 @@ angular.module('ui.bootstrap.timepicker', [])
2770
2824
  return hours;
2771
2825
  }
2772
2826
 
2827
+ function getMinutesFromTemplate() {
2828
+ var minutes = parseInt(scope.minutes, 10);
2829
+ return ( minutes >= 0 && minutes < 60 ) ? minutes : undefined;
2830
+ }
2831
+
2832
+ function pad( value ) {
2833
+ return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value;
2834
+ }
2835
+
2773
2836
  // Input elements
2774
- var inputs = element.find('input');
2775
- var hoursInputEl = inputs.eq(0), minutesInputEl = inputs.eq(1);
2837
+ var inputs = element.find('input'), hoursInputEl = inputs.eq(0), minutesInputEl = inputs.eq(1);
2776
2838
 
2777
2839
  // Respond on mousewheel spin
2778
2840
  var mousewheel = (angular.isDefined(attrs.mousewheel)) ? scope.$eval(attrs.mousewheel) : timepickerConfig.mousewheel;
2779
2841
  if ( mousewheel ) {
2780
-
2842
+
2781
2843
  var isScrollingUp = function(e) {
2782
2844
  if (e.originalEvent) {
2783
2845
  e = e.originalEvent;
@@ -2786,7 +2848,7 @@ angular.module('ui.bootstrap.timepicker', [])
2786
2848
  var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY;
2787
2849
  return (e.detail || delta > 0);
2788
2850
  };
2789
-
2851
+
2790
2852
  hoursInputEl.bind('mousewheel wheel', function(e) {
2791
2853
  scope.$apply( (isScrollingUp(e)) ? scope.incrementHours() : scope.decrementHours() );
2792
2854
  e.preventDefault();
@@ -2798,50 +2860,54 @@ angular.module('ui.bootstrap.timepicker', [])
2798
2860
  });
2799
2861
  }
2800
2862
 
2801
- var keyboardChange = false;
2802
2863
  scope.readonlyInput = (angular.isDefined(attrs.readonlyInput)) ? scope.$eval(attrs.readonlyInput) : timepickerConfig.readonlyInput;
2803
2864
  if ( ! scope.readonlyInput ) {
2865
+
2866
+ var invalidate = function(invalidHours, invalidMinutes) {
2867
+ ngModel.$setViewValue( null );
2868
+ ngModel.$setValidity('time', false);
2869
+ if (angular.isDefined(invalidHours)) {
2870
+ scope.invalidHours = invalidHours;
2871
+ }
2872
+ if (angular.isDefined(invalidMinutes)) {
2873
+ scope.invalidMinutes = invalidMinutes;
2874
+ }
2875
+ };
2876
+
2804
2877
  scope.updateHours = function() {
2805
- var hours = getScopeHours();
2878
+ var hours = getHoursFromTemplate();
2806
2879
 
2807
2880
  if ( angular.isDefined(hours) ) {
2808
- keyboardChange = 'h';
2809
- if ( scope.model === null ) {
2810
- scope.model = new Date( selected );
2811
- }
2812
- scope.model.setHours( hours );
2881
+ selected.setHours( hours );
2882
+ refresh( 'h' );
2813
2883
  } else {
2814
- scope.model = null;
2815
- scope.validHours = false;
2884
+ invalidate(true);
2816
2885
  }
2817
2886
  };
2818
2887
 
2819
2888
  hoursInputEl.bind('blur', function(e) {
2820
- if ( scope.validHours && scope.hours < 10) {
2889
+ if ( !scope.validHours && scope.hours < 10) {
2821
2890
  scope.$apply( function() {
2822
- scope.hours = padFilter( scope.hours );
2891
+ scope.hours = pad( scope.hours );
2823
2892
  });
2824
2893
  }
2825
2894
  });
2826
2895
 
2827
2896
  scope.updateMinutes = function() {
2828
- var minutes = parseInt(scope.minutes, 10);
2829
- if ( minutes >= 0 && minutes < 60 ) {
2830
- keyboardChange = 'm';
2831
- if ( scope.model === null ) {
2832
- scope.model = new Date( selected );
2833
- }
2834
- scope.model.setMinutes( minutes );
2897
+ var minutes = getMinutesFromTemplate();
2898
+
2899
+ if ( angular.isDefined(minutes) ) {
2900
+ selected.setMinutes( minutes );
2901
+ refresh( 'm' );
2835
2902
  } else {
2836
- scope.model = null;
2837
- scope.validMinutes = false;
2903
+ invalidate(undefined, true);
2838
2904
  }
2839
2905
  };
2840
2906
 
2841
2907
  minutesInputEl.bind('blur', function(e) {
2842
- if ( scope.validMinutes && scope.minutes < 10 ) {
2908
+ if ( !scope.invalidMinutes && scope.minutes < 10 ) {
2843
2909
  scope.$apply( function() {
2844
- scope.minutes = padFilter( scope.minutes );
2910
+ scope.minutes = pad( scope.minutes );
2845
2911
  });
2846
2912
  }
2847
2913
  });
@@ -2850,38 +2916,49 @@ angular.module('ui.bootstrap.timepicker', [])
2850
2916
  scope.updateMinutes = angular.noop;
2851
2917
  }
2852
2918
 
2853
- scope.$watch( function getModelTimestamp() {
2854
- return +scope.model;
2855
- }, function( timestamp ) {
2856
- if ( !isNaN( timestamp ) && timestamp > 0 ) {
2857
- selected = new Date( timestamp );
2858
- refreshTemplate();
2859
- }
2860
- });
2919
+ ngModel.$render = function() {
2920
+ var date = ngModel.$modelValue ? new Date( ngModel.$modelValue ) : null;
2861
2921
 
2862
- function refreshTemplate() {
2863
- var hours = selected.getHours();
2864
- if ( scope.showMeridian ) {
2865
- // Convert 24 to 12 hour system
2866
- hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12;
2922
+ if ( isNaN(date) ) {
2923
+ ngModel.$setValidity('time', false);
2924
+ $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
2925
+ } else {
2926
+ if ( date ) {
2927
+ selected = date;
2928
+ }
2929
+ makeValid();
2930
+ updateTemplate();
2867
2931
  }
2868
- scope.hours = ( keyboardChange === 'h' ) ? hours : padFilter(hours);
2869
- scope.validHours = true;
2932
+ };
2870
2933
 
2871
- var minutes = selected.getMinutes();
2872
- scope.minutes = ( keyboardChange === 'm' ) ? minutes : padFilter(minutes);
2873
- scope.validMinutes = true;
2934
+ // Call internally when we know that model is valid.
2935
+ function refresh( keyboardChange ) {
2936
+ makeValid();
2937
+ ngModel.$setViewValue( new Date(selected) );
2938
+ updateTemplate( keyboardChange );
2939
+ }
2874
2940
 
2875
- scope.meridian = ( scope.showMeridian ) ? (( selected.getHours() < 12 ) ? meridians[0] : meridians[1]) : '';
2941
+ function makeValid() {
2942
+ ngModel.$setValidity('time', true);
2943
+ scope.invalidHours = false;
2944
+ scope.invalidMinutes = false;
2945
+ }
2946
+
2947
+ function updateTemplate( keyboardChange ) {
2948
+ var hours = selected.getHours(), minutes = selected.getMinutes();
2876
2949
 
2877
- keyboardChange = false;
2950
+ if ( scope.showMeridian ) {
2951
+ hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12; // Convert 24 to 12 hour system
2952
+ }
2953
+ scope.hours = keyboardChange === 'h' ? hours : pad(hours);
2954
+ scope.minutes = keyboardChange === 'm' ? minutes : pad(minutes);
2955
+ scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
2878
2956
  }
2879
2957
 
2880
2958
  function addMinutes( minutes ) {
2881
2959
  var dt = new Date( selected.getTime() + minutes * 60000 );
2882
- selected.setHours( dt.getHours() );
2883
- selected.setMinutes( dt.getMinutes() );
2884
- scope.model = new Date( selected );
2960
+ selected.setHours( dt.getHours(), dt.getMinutes() );
2961
+ refresh();
2885
2962
  }
2886
2963
 
2887
2964
  scope.incrementHours = function() {
@@ -2903,7 +2980,7 @@ angular.module('ui.bootstrap.timepicker', [])
2903
2980
  };
2904
2981
  }]);
2905
2982
 
2906
- angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
2983
+ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap.bindHtml'])
2907
2984
 
2908
2985
  /**
2909
2986
  * A helper service that can parse typeahead's syntax (string provided by users)
@@ -2934,7 +3011,8 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
2934
3011
  };
2935
3012
  }])
2936
3013
 
2937
- .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser', function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) {
3014
+ .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser',
3015
+ function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) {
2938
3016
 
2939
3017
  var HOT_KEYS = [9, 13, 27, 38, 40];
2940
3018
 
@@ -3047,7 +3125,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
3047
3125
 
3048
3126
  //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
3049
3127
  //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
3050
- modelCtrl.$parsers.push(function (inputValue) {
3128
+ modelCtrl.$parsers.unshift(function (inputValue) {
3051
3129
 
3052
3130
  resetMatches();
3053
3131
  if (inputValue && inputValue.length >= minSearch) {
@@ -3063,7 +3141,12 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
3063
3141
  }
3064
3142
  }
3065
3143
 
3066
- return isEditable ? inputValue : undefined;
3144
+ if (isEditable) {
3145
+ return inputValue;
3146
+ } else {
3147
+ modelCtrl.$setValidity('editable', false);
3148
+ return undefined;
3149
+ }
3067
3150
  });
3068
3151
 
3069
3152
  modelCtrl.$formatters.push(function (modelValue) {
@@ -3077,12 +3160,13 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
3077
3160
  return inputFormatter(originalScope, locals);
3078
3161
 
3079
3162
  } else {
3080
- locals[parserResult.itemName] = modelValue;
3081
3163
 
3082
3164
  //it might happen that we don't have enough info to properly render input value
3083
3165
  //we need to check for this situation and simply return model value if we can't apply custom formatting
3166
+ locals[parserResult.itemName] = modelValue;
3084
3167
  candidateViewValue = parserResult.viewMapper(originalScope, locals);
3085
- emptyViewValue = parserResult.viewMapper(originalScope, {});
3168
+ locals[parserResult.itemName] = undefined;
3169
+ emptyViewValue = parserResult.viewMapper(originalScope, locals);
3086
3170
 
3087
3171
  return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue;
3088
3172
  }
@@ -3096,6 +3180,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
3096
3180
  locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
3097
3181
  model = parserResult.modelMapper(originalScope, locals);
3098
3182
  $setModelValue(originalScope, model);
3183
+ modelCtrl.$setValidity('editable', true);
3099
3184
 
3100
3185
  onSelectCallback(originalScope, {
3101
3186
  $item: item,
@@ -3103,8 +3188,9 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
3103
3188
  $label: parserResult.viewMapper(originalScope, locals)
3104
3189
  });
3105
3190
 
3106
- //return focus to the input element if a mach was selected via a mouse click event
3107
3191
  resetMatches();
3192
+
3193
+ //return focus to the input element if a mach was selected via a mouse click event
3108
3194
  element[0].focus();
3109
3195
  };
3110
3196
 
@@ -3139,9 +3225,18 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
3139
3225
  }
3140
3226
  });
3141
3227
 
3142
- $document.bind('click', function(){
3143
- resetMatches();
3144
- scope.$digest();
3228
+ // Keep reference to click handler to unbind it.
3229
+ var dismissClickHandler = function (evt) {
3230
+ if (element[0] !== evt.target) {
3231
+ resetMatches();
3232
+ scope.$digest();
3233
+ }
3234
+ };
3235
+
3236
+ $document.bind('click', dismissClickHandler);
3237
+
3238
+ originalScope.$on('$destroy', function(){
3239
+ $document.unbind('click', dismissClickHandler);
3145
3240
  });
3146
3241
 
3147
3242
  element.after($compile(popUpEl)(scope));
@@ -3209,7 +3304,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
3209
3304
  }
3210
3305
 
3211
3306
  return function(matchItem, query) {
3212
- return query ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : query;
3307
+ return query ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem;
3213
3308
  };
3214
3309
  });
3215
3310
  angular.module("template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
@@ -3302,28 +3397,14 @@ angular.module("template/datepicker/popup.html", []).run(["$templateCache", func
3302
3397
  "</ul>");
3303
3398
  }]);
3304
3399
 
3305
- angular.module("template/dialog/message.html", []).run(["$templateCache", function($templateCache) {
3306
- $templateCache.put("template/dialog/message.html",
3307
- "<div class=\"modal-header\">\n" +
3308
- " <h3>{{ title }}</h3>\n" +
3309
- "</div>\n" +
3310
- "<div class=\"modal-body\">\n" +
3311
- " <p>{{ message }}</p>\n" +
3312
- "</div>\n" +
3313
- "<div class=\"modal-footer\">\n" +
3314
- " <button ng-repeat=\"btn in buttons\" ng-click=\"close(btn.result)\" class=\"btn\" ng-class=\"btn.cssClass\">{{ btn.label }}</button>\n" +
3315
- "</div>\n" +
3316
- "");
3317
- }]);
3318
-
3319
3400
  angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
3320
3401
  $templateCache.put("template/modal/backdrop.html",
3321
- "<div class=\"modal-backdrop fade in\"></div>");
3402
+ "<div class=\"modal-backdrop fade\" ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1040 + index*10}\" ng-click=\"close($event)\"></div>");
3322
3403
  }]);
3323
3404
 
3324
3405
  angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) {
3325
3406
  $templateCache.put("template/modal/window.html",
3326
- "<div class=\"modal in\" ng-transclude></div>");
3407
+ "<div class=\"modal fade {{ windowClass }}\" ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1050 + index*10}\" ng-transclude></div>");
3327
3408
  }]);
3328
3409
 
3329
3410
  angular.module("template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
@@ -3389,7 +3470,7 @@ angular.module("template/progressbar/progress.html", []).run(["$templateCache",
3389
3470
  angular.module("template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
3390
3471
  $templateCache.put("template/rating/rating.html",
3391
3472
  "<span ng-mouseleave=\"reset()\">\n" +
3392
- " <i ng-repeat=\"number in range\" ng-mouseenter=\"enter(number)\" ng-click=\"rate(number)\" ng-class=\"{'icon-star': number <= val, 'icon-star-empty': number > val}\"></i>\n" +
3473
+ " <i ng-repeat=\"r in range\" ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" ng-class=\"$index < val && (r.stateOn || 'icon-star') || (r.stateOff || 'icon-star-empty')\"></i>\n" +
3393
3474
  "</span>");
3394
3475
  }]);
3395
3476
 
@@ -3454,10 +3535,10 @@ angular.module("template/timepicker/timepicker.html", []).run(["$templateCache",
3454
3535
  " <td ng-show=\"showMeridian\"></td>\n" +
3455
3536
  " </tr>\n" +
3456
3537
  " <tr>\n" +
3457
- " <td class=\"control-group\" ng-class=\"{'error': !validHours}\"><input type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"span1 text-center\" ng-mousewheel=\"incrementHours()\" ng-readonly=\"readonlyInput\" maxlength=\"2\" /></td>\n" +
3538
+ " <td class=\"control-group\" ng-class=\"{'error': invalidHours}\"><input type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"span1 text-center\" ng-mousewheel=\"incrementHours()\" ng-readonly=\"readonlyInput\" maxlength=\"2\" /></td>\n" +
3458
3539
  " <td>:</td>\n" +
3459
- " <td class=\"control-group\" ng-class=\"{'error': !validMinutes}\"><input type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"span1 text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\"></td>\n" +
3460
- " <td ng-show=\"showMeridian\"><button ng-click=\"toggleMeridian()\" class=\"btn text-center\">{{meridian}}</button></td>\n" +
3540
+ " <td class=\"control-group\" ng-class=\"{'error': invalidMinutes}\"><input type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"span1 text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\"></td>\n" +
3541
+ " <td ng-show=\"showMeridian\"><button type=\"button\" ng-click=\"toggleMeridian()\" class=\"btn text-center\">{{meridian}}</button></td>\n" +
3461
3542
  " </tr>\n" +
3462
3543
  " <tr class=\"text-center\">\n" +
3463
3544
  " <td><a ng-click=\"decrementHours()\" class=\"btn btn-link\"><i class=\"icon-chevron-down\"></i></a></td>\n" +
@@ -3470,7 +3551,7 @@ angular.module("template/timepicker/timepicker.html", []).run(["$templateCache",
3470
3551
 
3471
3552
  angular.module("template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
3472
3553
  $templateCache.put("template/typeahead/typeahead-match.html",
3473
- "<a tabindex=\"-1\" ng-bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>");
3554
+ "<a tabindex=\"-1\" bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>");
3474
3555
  }]);
3475
3556
 
3476
3557
  angular.module("template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {