angular-ui-bootstrap-rails 0.5.0.0 → 0.6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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) {