angular-ui-bootstrap-rails 0.4.0.0 → 0.5.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.
@@ -1,2924 +1,4 @@
|
|
1
|
-
angular.module("ui.bootstrap", ["ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.
|
2
|
-
angular.module('ui.bootstrap.transition', [])
|
3
|
-
|
4
|
-
/**
|
5
|
-
* $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
|
6
|
-
* @param {DOMElement} element The DOMElement that will be animated.
|
7
|
-
* @param {string|object|function} trigger The thing that will cause the transition to start:
|
8
|
-
* - As a string, it represents the css class to be added to the element.
|
9
|
-
* - As an object, it represents a hash of style attributes to be applied to the element.
|
10
|
-
* - As a function, it represents a function to be called that will cause the transition to occur.
|
11
|
-
* @return {Promise} A promise that is resolved when the transition finishes.
|
12
|
-
*/
|
13
|
-
.factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
|
14
|
-
|
15
|
-
var $transition = function(element, trigger, options) {
|
16
|
-
options = options || {};
|
17
|
-
var deferred = $q.defer();
|
18
|
-
var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
|
19
|
-
|
20
|
-
var transitionEndHandler = function(event) {
|
21
|
-
$rootScope.$apply(function() {
|
22
|
-
element.unbind(endEventName, transitionEndHandler);
|
23
|
-
deferred.resolve(element);
|
24
|
-
});
|
25
|
-
};
|
26
|
-
|
27
|
-
if (endEventName) {
|
28
|
-
element.bind(endEventName, transitionEndHandler);
|
29
|
-
}
|
30
|
-
|
31
|
-
// Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
|
32
|
-
$timeout(function() {
|
33
|
-
if ( angular.isString(trigger) ) {
|
34
|
-
element.addClass(trigger);
|
35
|
-
} else if ( angular.isFunction(trigger) ) {
|
36
|
-
trigger(element);
|
37
|
-
} else if ( angular.isObject(trigger) ) {
|
38
|
-
element.css(trigger);
|
39
|
-
}
|
40
|
-
//If browser does not support transitions, instantly resolve
|
41
|
-
if ( !endEventName ) {
|
42
|
-
deferred.resolve(element);
|
43
|
-
}
|
44
|
-
});
|
45
|
-
|
46
|
-
// Add our custom cancel function to the promise that is returned
|
47
|
-
// We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
|
48
|
-
// i.e. it will therefore never raise a transitionEnd event for that transition
|
49
|
-
deferred.promise.cancel = function() {
|
50
|
-
if ( endEventName ) {
|
51
|
-
element.unbind(endEventName, transitionEndHandler);
|
52
|
-
}
|
53
|
-
deferred.reject('Transition cancelled');
|
54
|
-
};
|
55
|
-
|
56
|
-
return deferred.promise;
|
57
|
-
};
|
58
|
-
|
59
|
-
// Work out the name of the transitionEnd event
|
60
|
-
var transElement = document.createElement('trans');
|
61
|
-
var transitionEndEventNames = {
|
62
|
-
'WebkitTransition': 'webkitTransitionEnd',
|
63
|
-
'MozTransition': 'transitionend',
|
64
|
-
'OTransition': 'oTransitionEnd',
|
65
|
-
'transition': 'transitionend'
|
66
|
-
};
|
67
|
-
var animationEndEventNames = {
|
68
|
-
'WebkitTransition': 'webkitAnimationEnd',
|
69
|
-
'MozTransition': 'animationend',
|
70
|
-
'OTransition': 'oAnimationEnd',
|
71
|
-
'transition': 'animationend'
|
72
|
-
};
|
73
|
-
function findEndEventName(endEventNames) {
|
74
|
-
for (var name in endEventNames){
|
75
|
-
if (transElement.style[name] !== undefined) {
|
76
|
-
return endEventNames[name];
|
77
|
-
}
|
78
|
-
}
|
79
|
-
}
|
80
|
-
$transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
|
81
|
-
$transition.animationEndEventName = findEndEventName(animationEndEventNames);
|
82
|
-
return $transition;
|
83
|
-
}]);
|
84
|
-
|
85
|
-
angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
|
86
|
-
|
87
|
-
// The collapsible directive indicates a block of html that will expand and collapse
|
88
|
-
.directive('collapse', ['$transition', function($transition) {
|
89
|
-
// CSS transitions don't work with height: auto, so we have to manually change the height to a
|
90
|
-
// specific value and then once the animation completes, we can reset the height to auto.
|
91
|
-
// Unfortunately if you do this while the CSS transitions are specified (i.e. in the CSS class
|
92
|
-
// "collapse") then you trigger a change to height 0 in between.
|
93
|
-
// The fix is to remove the "collapse" CSS class while changing the height back to auto - phew!
|
94
|
-
var fixUpHeight = function(scope, element, height) {
|
95
|
-
// We remove the collapse CSS class to prevent a transition when we change to height: auto
|
96
|
-
element.removeClass('collapse');
|
97
|
-
element.css({ height: height });
|
98
|
-
// It appears that reading offsetWidth makes the browser realise that we have changed the
|
99
|
-
// height already :-/
|
100
|
-
var x = element[0].offsetWidth;
|
101
|
-
element.addClass('collapse');
|
102
|
-
};
|
103
|
-
|
104
|
-
return {
|
105
|
-
link: function(scope, element, attrs) {
|
106
|
-
|
107
|
-
var isCollapsed;
|
108
|
-
var initialAnimSkip = true;
|
109
|
-
scope.$watch(function (){ return element[0].scrollHeight; }, function (value) {
|
110
|
-
//The listener is called when scollHeight changes
|
111
|
-
//It actually does on 2 scenarios:
|
112
|
-
// 1. Parent is set to display none
|
113
|
-
// 2. angular bindings inside are resolved
|
114
|
-
//When we have a change of scrollHeight we are setting again the correct height if the group is opened
|
115
|
-
if (element[0].scrollHeight !== 0) {
|
116
|
-
if (!isCollapsed) {
|
117
|
-
if (initialAnimSkip) {
|
118
|
-
fixUpHeight(scope, element, element[0].scrollHeight + 'px');
|
119
|
-
} else {
|
120
|
-
fixUpHeight(scope, element, 'auto');
|
121
|
-
}
|
122
|
-
}
|
123
|
-
}
|
124
|
-
});
|
125
|
-
|
126
|
-
scope.$watch(attrs.collapse, function(value) {
|
127
|
-
if (value) {
|
128
|
-
collapse();
|
129
|
-
} else {
|
130
|
-
expand();
|
131
|
-
}
|
132
|
-
});
|
133
|
-
|
134
|
-
|
135
|
-
var currentTransition;
|
136
|
-
var doTransition = function(change) {
|
137
|
-
if ( currentTransition ) {
|
138
|
-
currentTransition.cancel();
|
139
|
-
}
|
140
|
-
currentTransition = $transition(element,change);
|
141
|
-
currentTransition.then(
|
142
|
-
function() { currentTransition = undefined; },
|
143
|
-
function() { currentTransition = undefined; }
|
144
|
-
);
|
145
|
-
return currentTransition;
|
146
|
-
};
|
147
|
-
|
148
|
-
var expand = function() {
|
149
|
-
if (initialAnimSkip) {
|
150
|
-
initialAnimSkip = false;
|
151
|
-
if ( !isCollapsed ) {
|
152
|
-
fixUpHeight(scope, element, 'auto');
|
153
|
-
}
|
154
|
-
} else {
|
155
|
-
doTransition({ height : element[0].scrollHeight + 'px' })
|
156
|
-
.then(function() {
|
157
|
-
// This check ensures that we don't accidentally update the height if the user has closed
|
158
|
-
// the group while the animation was still running
|
159
|
-
if ( !isCollapsed ) {
|
160
|
-
fixUpHeight(scope, element, 'auto');
|
161
|
-
}
|
162
|
-
});
|
163
|
-
}
|
164
|
-
isCollapsed = false;
|
165
|
-
};
|
166
|
-
|
167
|
-
var collapse = function() {
|
168
|
-
isCollapsed = true;
|
169
|
-
if (initialAnimSkip) {
|
170
|
-
initialAnimSkip = false;
|
171
|
-
fixUpHeight(scope, element, 0);
|
172
|
-
} else {
|
173
|
-
fixUpHeight(scope, element, element[0].scrollHeight + 'px');
|
174
|
-
doTransition({'height':'0'});
|
175
|
-
}
|
176
|
-
};
|
177
|
-
}
|
178
|
-
};
|
179
|
-
}]);
|
180
|
-
|
181
|
-
angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
182
|
-
|
183
|
-
.constant('accordionConfig', {
|
184
|
-
closeOthers: true
|
185
|
-
})
|
186
|
-
|
187
|
-
.controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function ($scope, $attrs, accordionConfig) {
|
188
|
-
|
189
|
-
// This array keeps track of the accordion groups
|
190
|
-
this.groups = [];
|
191
|
-
|
192
|
-
// Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
|
193
|
-
this.closeOthers = function(openGroup) {
|
194
|
-
var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
|
195
|
-
if ( closeOthers ) {
|
196
|
-
angular.forEach(this.groups, function (group) {
|
197
|
-
if ( group !== openGroup ) {
|
198
|
-
group.isOpen = false;
|
199
|
-
}
|
200
|
-
});
|
201
|
-
}
|
202
|
-
};
|
203
|
-
|
204
|
-
// This is called from the accordion-group directive to add itself to the accordion
|
205
|
-
this.addGroup = function(groupScope) {
|
206
|
-
var that = this;
|
207
|
-
this.groups.push(groupScope);
|
208
|
-
|
209
|
-
groupScope.$on('$destroy', function (event) {
|
210
|
-
that.removeGroup(groupScope);
|
211
|
-
});
|
212
|
-
};
|
213
|
-
|
214
|
-
// This is called from the accordion-group directive when to remove itself
|
215
|
-
this.removeGroup = function(group) {
|
216
|
-
var index = this.groups.indexOf(group);
|
217
|
-
if ( index !== -1 ) {
|
218
|
-
this.groups.splice(this.groups.indexOf(group), 1);
|
219
|
-
}
|
220
|
-
};
|
221
|
-
|
222
|
-
}])
|
223
|
-
|
224
|
-
// The accordion directive simply sets up the directive controller
|
225
|
-
// and adds an accordion CSS class to itself element.
|
226
|
-
.directive('accordion', function () {
|
227
|
-
return {
|
228
|
-
restrict:'EA',
|
229
|
-
controller:'AccordionController',
|
230
|
-
transclude: true,
|
231
|
-
replace: false,
|
232
|
-
templateUrl: 'template/accordion/accordion.html'
|
233
|
-
};
|
234
|
-
})
|
235
|
-
|
236
|
-
// The accordion-group directive indicates a block of html that will expand and collapse in an accordion
|
237
|
-
.directive('accordionGroup', ['$parse', '$transition', '$timeout', function($parse, $transition, $timeout) {
|
238
|
-
return {
|
239
|
-
require:'^accordion', // We need this directive to be inside an accordion
|
240
|
-
restrict:'EA',
|
241
|
-
transclude:true, // It transcludes the contents of the directive into the template
|
242
|
-
replace: true, // The element containing the directive will be replaced with the template
|
243
|
-
templateUrl:'template/accordion/accordion-group.html',
|
244
|
-
scope:{ heading:'@' }, // Create an isolated scope and interpolate the heading attribute onto this scope
|
245
|
-
controller: ['$scope', function($scope) {
|
246
|
-
this.setHeading = function(element) {
|
247
|
-
this.heading = element;
|
248
|
-
};
|
249
|
-
}],
|
250
|
-
link: function(scope, element, attrs, accordionCtrl) {
|
251
|
-
var getIsOpen, setIsOpen;
|
252
|
-
|
253
|
-
accordionCtrl.addGroup(scope);
|
254
|
-
|
255
|
-
scope.isOpen = false;
|
256
|
-
|
257
|
-
if ( attrs.isOpen ) {
|
258
|
-
getIsOpen = $parse(attrs.isOpen);
|
259
|
-
setIsOpen = getIsOpen.assign;
|
260
|
-
|
261
|
-
scope.$watch(
|
262
|
-
function watchIsOpen() { return getIsOpen(scope.$parent); },
|
263
|
-
function updateOpen(value) { scope.isOpen = value; }
|
264
|
-
);
|
265
|
-
|
266
|
-
scope.isOpen = getIsOpen ? getIsOpen(scope.$parent) : false;
|
267
|
-
}
|
268
|
-
|
269
|
-
scope.$watch('isOpen', function(value) {
|
270
|
-
if ( value ) {
|
271
|
-
accordionCtrl.closeOthers(scope);
|
272
|
-
}
|
273
|
-
if ( setIsOpen ) {
|
274
|
-
setIsOpen(scope.$parent, value);
|
275
|
-
}
|
276
|
-
});
|
277
|
-
}
|
278
|
-
};
|
279
|
-
}])
|
280
|
-
|
281
|
-
// Use accordion-heading below an accordion-group to provide a heading containing HTML
|
282
|
-
// <accordion-group>
|
283
|
-
// <accordion-heading>Heading containing HTML - <img src="..."></accordion-heading>
|
284
|
-
// </accordion-group>
|
285
|
-
.directive('accordionHeading', function() {
|
286
|
-
return {
|
287
|
-
restrict: 'EA',
|
288
|
-
transclude: true, // Grab the contents to be used as the heading
|
289
|
-
template: '', // In effect remove this element!
|
290
|
-
replace: true,
|
291
|
-
require: '^accordionGroup',
|
292
|
-
compile: function(element, attr, transclude) {
|
293
|
-
return function link(scope, element, attr, accordionGroupCtrl) {
|
294
|
-
// Pass the heading to the accordion-group controller
|
295
|
-
// so that it can be transcluded into the right place in the template
|
296
|
-
// [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
|
297
|
-
accordionGroupCtrl.setHeading(transclude(scope, function() {}));
|
298
|
-
};
|
299
|
-
}
|
300
|
-
};
|
301
|
-
})
|
302
|
-
|
303
|
-
// Use in the accordion-group template to indicate where you want the heading to be transcluded
|
304
|
-
// You must provide the property on the accordion-group controller that will hold the transcluded element
|
305
|
-
// <div class="accordion-group">
|
306
|
-
// <div class="accordion-heading" ><a ... accordion-transclude="heading">...</a></div>
|
307
|
-
// ...
|
308
|
-
// </div>
|
309
|
-
.directive('accordionTransclude', function() {
|
310
|
-
return {
|
311
|
-
require: '^accordionGroup',
|
312
|
-
link: function(scope, element, attr, controller) {
|
313
|
-
scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) {
|
314
|
-
if ( heading ) {
|
315
|
-
element.html('');
|
316
|
-
element.append(heading);
|
317
|
-
}
|
318
|
-
});
|
319
|
-
}
|
320
|
-
};
|
321
|
-
});
|
322
|
-
|
323
|
-
angular.module("ui.bootstrap.alert", []).directive('alert', function () {
|
324
|
-
return {
|
325
|
-
restrict:'EA',
|
326
|
-
templateUrl:'template/alert/alert.html',
|
327
|
-
transclude:true,
|
328
|
-
replace:true,
|
329
|
-
scope: {
|
330
|
-
type: '=',
|
331
|
-
close: '&'
|
332
|
-
},
|
333
|
-
link: function(scope, iElement, iAttrs, controller) {
|
334
|
-
scope.closeable = "close" in iAttrs;
|
335
|
-
}
|
336
|
-
};
|
337
|
-
});
|
338
|
-
|
339
|
-
angular.module('ui.bootstrap.buttons', [])
|
340
|
-
|
341
|
-
.constant('buttonConfig', {
|
342
|
-
activeClass:'active',
|
343
|
-
toggleEvent:'click'
|
344
|
-
})
|
345
|
-
|
346
|
-
.directive('btnRadio', ['buttonConfig', function (buttonConfig) {
|
347
|
-
var activeClass = buttonConfig.activeClass || 'active';
|
348
|
-
var toggleEvent = buttonConfig.toggleEvent || 'click';
|
349
|
-
|
350
|
-
return {
|
351
|
-
|
352
|
-
require:'ngModel',
|
353
|
-
link:function (scope, element, attrs, ngModelCtrl) {
|
354
|
-
|
355
|
-
//model -> UI
|
356
|
-
ngModelCtrl.$render = function () {
|
357
|
-
element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio)));
|
358
|
-
};
|
359
|
-
|
360
|
-
//ui->model
|
361
|
-
element.bind(toggleEvent, function () {
|
362
|
-
if (!element.hasClass(activeClass)) {
|
363
|
-
scope.$apply(function () {
|
364
|
-
ngModelCtrl.$setViewValue(scope.$eval(attrs.btnRadio));
|
365
|
-
ngModelCtrl.$render();
|
366
|
-
});
|
367
|
-
}
|
368
|
-
});
|
369
|
-
}
|
370
|
-
};
|
371
|
-
}])
|
372
|
-
|
373
|
-
.directive('btnCheckbox', ['buttonConfig', function (buttonConfig) {
|
374
|
-
|
375
|
-
var activeClass = buttonConfig.activeClass || 'active';
|
376
|
-
var toggleEvent = buttonConfig.toggleEvent || 'click';
|
377
|
-
|
378
|
-
return {
|
379
|
-
require:'ngModel',
|
380
|
-
link:function (scope, element, attrs, ngModelCtrl) {
|
381
|
-
|
382
|
-
var trueValue = scope.$eval(attrs.btnCheckboxTrue);
|
383
|
-
var falseValue = scope.$eval(attrs.btnCheckboxFalse);
|
384
|
-
|
385
|
-
trueValue = angular.isDefined(trueValue) ? trueValue : true;
|
386
|
-
falseValue = angular.isDefined(falseValue) ? falseValue : false;
|
387
|
-
|
388
|
-
//model -> UI
|
389
|
-
ngModelCtrl.$render = function () {
|
390
|
-
element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, trueValue));
|
391
|
-
};
|
392
|
-
|
393
|
-
//ui->model
|
394
|
-
element.bind(toggleEvent, function () {
|
395
|
-
scope.$apply(function () {
|
396
|
-
ngModelCtrl.$setViewValue(element.hasClass(activeClass) ? falseValue : trueValue);
|
397
|
-
ngModelCtrl.$render();
|
398
|
-
});
|
399
|
-
});
|
400
|
-
}
|
401
|
-
};
|
402
|
-
}]);
|
403
|
-
/**
|
404
|
-
* @ngdoc overview
|
405
|
-
* @name ui.bootstrap.carousel
|
406
|
-
*
|
407
|
-
* @description
|
408
|
-
* AngularJS version of an image carousel.
|
409
|
-
*
|
410
|
-
*/
|
411
|
-
angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
|
412
|
-
.controller('CarouselController', ['$scope', '$timeout', '$transition', '$q', function ($scope, $timeout, $transition, $q) {
|
413
|
-
var self = this,
|
414
|
-
slides = self.slides = [],
|
415
|
-
currentIndex = -1,
|
416
|
-
currentTimeout, isPlaying;
|
417
|
-
self.currentSlide = null;
|
418
|
-
|
419
|
-
/* direction: "prev" or "next" */
|
420
|
-
self.select = function(nextSlide, direction) {
|
421
|
-
var nextIndex = slides.indexOf(nextSlide);
|
422
|
-
//Decide direction if it's not given
|
423
|
-
if (direction === undefined) {
|
424
|
-
direction = nextIndex > currentIndex ? "next" : "prev";
|
425
|
-
}
|
426
|
-
if (nextSlide && nextSlide !== self.currentSlide) {
|
427
|
-
if ($scope.$currentTransition) {
|
428
|
-
$scope.$currentTransition.cancel();
|
429
|
-
//Timeout so ng-class in template has time to fix classes for finished slide
|
430
|
-
$timeout(goNext);
|
431
|
-
} else {
|
432
|
-
goNext();
|
433
|
-
}
|
434
|
-
}
|
435
|
-
function goNext() {
|
436
|
-
//If we have a slide to transition from and we have a transition type and we're allowed, go
|
437
|
-
if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {
|
438
|
-
//We shouldn't do class manip in here, but it's the same weird thing bootstrap does. need to fix sometime
|
439
|
-
nextSlide.$element.addClass(direction);
|
440
|
-
nextSlide.$element[0].offsetWidth = nextSlide.$element[0].offsetWidth; //force reflow
|
441
|
-
|
442
|
-
//Set all other slides to stop doing their stuff for the new transition
|
443
|
-
angular.forEach(slides, function(slide) {
|
444
|
-
angular.extend(slide, {direction: '', entering: false, leaving: false, active: false});
|
445
|
-
});
|
446
|
-
angular.extend(nextSlide, {direction: direction, active: true, entering: true});
|
447
|
-
angular.extend(self.currentSlide||{}, {direction: direction, leaving: true});
|
448
|
-
|
449
|
-
$scope.$currentTransition = $transition(nextSlide.$element, {});
|
450
|
-
//We have to create new pointers inside a closure since next & current will change
|
451
|
-
(function(next,current) {
|
452
|
-
$scope.$currentTransition.then(
|
453
|
-
function(){ transitionDone(next, current); },
|
454
|
-
function(){ transitionDone(next, current); }
|
455
|
-
);
|
456
|
-
}(nextSlide, self.currentSlide));
|
457
|
-
} else {
|
458
|
-
transitionDone(nextSlide, self.currentSlide);
|
459
|
-
}
|
460
|
-
self.currentSlide = nextSlide;
|
461
|
-
currentIndex = nextIndex;
|
462
|
-
//every time you change slides, reset the timer
|
463
|
-
restartTimer();
|
464
|
-
}
|
465
|
-
function transitionDone(next, current) {
|
466
|
-
angular.extend(next, {direction: '', active: true, leaving: false, entering: false});
|
467
|
-
angular.extend(current||{}, {direction: '', active: false, leaving: false, entering: false});
|
468
|
-
$scope.$currentTransition = null;
|
469
|
-
}
|
470
|
-
};
|
471
|
-
|
472
|
-
/* Allow outside people to call indexOf on slides array */
|
473
|
-
self.indexOfSlide = function(slide) {
|
474
|
-
return slides.indexOf(slide);
|
475
|
-
};
|
476
|
-
|
477
|
-
$scope.next = function() {
|
478
|
-
var newIndex = (currentIndex + 1) % slides.length;
|
479
|
-
|
480
|
-
//Prevent this user-triggered transition from occurring if there is already one in progress
|
481
|
-
if (!$scope.$currentTransition) {
|
482
|
-
return self.select(slides[newIndex], 'next');
|
483
|
-
}
|
484
|
-
};
|
485
|
-
|
486
|
-
$scope.prev = function() {
|
487
|
-
var newIndex = currentIndex - 1 < 0 ? slides.length - 1 : currentIndex - 1;
|
488
|
-
|
489
|
-
//Prevent this user-triggered transition from occurring if there is already one in progress
|
490
|
-
if (!$scope.$currentTransition) {
|
491
|
-
return self.select(slides[newIndex], 'prev');
|
492
|
-
}
|
493
|
-
};
|
494
|
-
|
495
|
-
$scope.select = function(slide) {
|
496
|
-
self.select(slide);
|
497
|
-
};
|
498
|
-
|
499
|
-
$scope.isActive = function(slide) {
|
500
|
-
return self.currentSlide === slide;
|
501
|
-
};
|
502
|
-
|
503
|
-
$scope.slides = function() {
|
504
|
-
return slides;
|
505
|
-
};
|
506
|
-
|
507
|
-
$scope.$watch('interval', restartTimer);
|
508
|
-
function restartTimer() {
|
509
|
-
if (currentTimeout) {
|
510
|
-
$timeout.cancel(currentTimeout);
|
511
|
-
}
|
512
|
-
function go() {
|
513
|
-
if (isPlaying) {
|
514
|
-
$scope.next();
|
515
|
-
restartTimer();
|
516
|
-
} else {
|
517
|
-
$scope.pause();
|
518
|
-
}
|
519
|
-
}
|
520
|
-
var interval = +$scope.interval;
|
521
|
-
if (!isNaN(interval) && interval>=0) {
|
522
|
-
currentTimeout = $timeout(go, interval);
|
523
|
-
}
|
524
|
-
}
|
525
|
-
$scope.play = function() {
|
526
|
-
if (!isPlaying) {
|
527
|
-
isPlaying = true;
|
528
|
-
restartTimer();
|
529
|
-
}
|
530
|
-
};
|
531
|
-
$scope.pause = function() {
|
532
|
-
if (!$scope.noPause) {
|
533
|
-
isPlaying = false;
|
534
|
-
if (currentTimeout) {
|
535
|
-
$timeout.cancel(currentTimeout);
|
536
|
-
}
|
537
|
-
}
|
538
|
-
};
|
539
|
-
|
540
|
-
self.addSlide = function(slide, element) {
|
541
|
-
slide.$element = element;
|
542
|
-
slides.push(slide);
|
543
|
-
//if this is the first slide or the slide is set to active, select it
|
544
|
-
if(slides.length === 1 || slide.active) {
|
545
|
-
self.select(slides[slides.length-1]);
|
546
|
-
if (slides.length == 1) {
|
547
|
-
$scope.play();
|
548
|
-
}
|
549
|
-
} else {
|
550
|
-
slide.active = false;
|
551
|
-
}
|
552
|
-
};
|
553
|
-
|
554
|
-
self.removeSlide = function(slide) {
|
555
|
-
//get the index of the slide inside the carousel
|
556
|
-
var index = slides.indexOf(slide);
|
557
|
-
slides.splice(index, 1);
|
558
|
-
if (slides.length > 0 && slide.active) {
|
559
|
-
if (index >= slides.length) {
|
560
|
-
self.select(slides[index-1]);
|
561
|
-
} else {
|
562
|
-
self.select(slides[index]);
|
563
|
-
}
|
564
|
-
} else if (currentIndex > index) {
|
565
|
-
currentIndex--;
|
566
|
-
}
|
567
|
-
};
|
568
|
-
}])
|
569
|
-
|
570
|
-
/**
|
571
|
-
* @ngdoc directive
|
572
|
-
* @name ui.bootstrap.carousel.directive:carousel
|
573
|
-
* @restrict EA
|
574
|
-
*
|
575
|
-
* @description
|
576
|
-
* Carousel is the outer container for a set of image 'slides' to showcase.
|
577
|
-
*
|
578
|
-
* @param {number=} interval The time, in milliseconds, that it will take the carousel to go to the next slide.
|
579
|
-
* @param {boolean=} noTransition Whether to disable transitions on the carousel.
|
580
|
-
* @param {boolean=} noPause Whether to disable pausing on the carousel (by default, the carousel interval pauses on hover).
|
581
|
-
*
|
582
|
-
* @example
|
583
|
-
<example module="ui.bootstrap">
|
584
|
-
<file name="index.html">
|
585
|
-
<carousel>
|
586
|
-
<slide>
|
587
|
-
<img src="http://placekitten.com/150/150" style="margin:auto;">
|
588
|
-
<div class="carousel-caption">
|
589
|
-
<p>Beautiful!</p>
|
590
|
-
</div>
|
591
|
-
</slide>
|
592
|
-
<slide>
|
593
|
-
<img src="http://placekitten.com/100/150" style="margin:auto;">
|
594
|
-
<div class="carousel-caption">
|
595
|
-
<p>D'aww!</p>
|
596
|
-
</div>
|
597
|
-
</slide>
|
598
|
-
</carousel>
|
599
|
-
</file>
|
600
|
-
<file name="demo.css">
|
601
|
-
.carousel-indicators {
|
602
|
-
top: auto;
|
603
|
-
bottom: 15px;
|
604
|
-
}
|
605
|
-
</file>
|
606
|
-
</example>
|
607
|
-
*/
|
608
|
-
.directive('carousel', [function() {
|
609
|
-
return {
|
610
|
-
restrict: 'EA',
|
611
|
-
transclude: true,
|
612
|
-
replace: true,
|
613
|
-
controller: 'CarouselController',
|
614
|
-
require: 'carousel',
|
615
|
-
templateUrl: 'template/carousel/carousel.html',
|
616
|
-
scope: {
|
617
|
-
interval: '=',
|
618
|
-
noTransition: '=',
|
619
|
-
noPause: '='
|
620
|
-
}
|
621
|
-
};
|
622
|
-
}])
|
623
|
-
|
624
|
-
/**
|
625
|
-
* @ngdoc directive
|
626
|
-
* @name ui.bootstrap.carousel.directive:slide
|
627
|
-
* @restrict EA
|
628
|
-
*
|
629
|
-
* @description
|
630
|
-
* Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}. Must be placed as a child of a carousel element.
|
631
|
-
*
|
632
|
-
* @param {boolean=} active Model binding, whether or not this slide is currently active.
|
633
|
-
*
|
634
|
-
* @example
|
635
|
-
<example module="ui.bootstrap">
|
636
|
-
<file name="index.html">
|
637
|
-
<div ng-controller="CarouselDemoCtrl">
|
638
|
-
<carousel>
|
639
|
-
<slide ng-repeat="slide in slides" active="slide.active">
|
640
|
-
<img ng-src="{{slide.image}}" style="margin:auto;">
|
641
|
-
<div class="carousel-caption">
|
642
|
-
<h4>Slide {{$index}}</h4>
|
643
|
-
<p>{{slide.text}}</p>
|
644
|
-
</div>
|
645
|
-
</slide>
|
646
|
-
</carousel>
|
647
|
-
<div class="row-fluid">
|
648
|
-
<div class="span6">
|
649
|
-
<ul>
|
650
|
-
<li ng-repeat="slide in slides">
|
651
|
-
<button class="btn btn-mini" ng-class="{'btn-info': !slide.active, 'btn-success': slide.active}" ng-disabled="slide.active" ng-click="slide.active = true">select</button>
|
652
|
-
{{$index}}: {{slide.text}}
|
653
|
-
</li>
|
654
|
-
</ul>
|
655
|
-
<a class="btn" ng-click="addSlide()">Add Slide</a>
|
656
|
-
</div>
|
657
|
-
<div class="span6">
|
658
|
-
Interval, in milliseconds: <input type="number" ng-model="myInterval">
|
659
|
-
<br />Enter a negative number to stop the interval.
|
660
|
-
</div>
|
661
|
-
</div>
|
662
|
-
</div>
|
663
|
-
</file>
|
664
|
-
<file name="script.js">
|
665
|
-
function CarouselDemoCtrl($scope) {
|
666
|
-
$scope.myInterval = 5000;
|
667
|
-
var slides = $scope.slides = [];
|
668
|
-
$scope.addSlide = function() {
|
669
|
-
var newWidth = 200 + ((slides.length + (25 * slides.length)) % 150);
|
670
|
-
slides.push({
|
671
|
-
image: 'http://placekitten.com/' + newWidth + '/200',
|
672
|
-
text: ['More','Extra','Lots of','Surplus'][slides.length % 4] + ' '
|
673
|
-
['Cats', 'Kittys', 'Felines', 'Cutes'][slides.length % 4]
|
674
|
-
});
|
675
|
-
};
|
676
|
-
for (var i=0; i<4; i++) $scope.addSlide();
|
677
|
-
}
|
678
|
-
</file>
|
679
|
-
<file name="demo.css">
|
680
|
-
.carousel-indicators {
|
681
|
-
top: auto;
|
682
|
-
bottom: 15px;
|
683
|
-
}
|
684
|
-
</file>
|
685
|
-
</example>
|
686
|
-
*/
|
687
|
-
|
688
|
-
.directive('slide', ['$parse', function($parse) {
|
689
|
-
return {
|
690
|
-
require: '^carousel',
|
691
|
-
restrict: 'EA',
|
692
|
-
transclude: true,
|
693
|
-
replace: true,
|
694
|
-
templateUrl: 'template/carousel/slide.html',
|
695
|
-
scope: {
|
696
|
-
},
|
697
|
-
link: function (scope, element, attrs, carouselCtrl) {
|
698
|
-
//Set up optional 'active' = binding
|
699
|
-
if (attrs.active) {
|
700
|
-
var getActive = $parse(attrs.active);
|
701
|
-
var setActive = getActive.assign;
|
702
|
-
var lastValue = scope.active = getActive(scope.$parent);
|
703
|
-
scope.$watch(function parentActiveWatch() {
|
704
|
-
var parentActive = getActive(scope.$parent);
|
705
|
-
|
706
|
-
if (parentActive !== scope.active) {
|
707
|
-
// we are out of sync and need to copy
|
708
|
-
if (parentActive !== lastValue) {
|
709
|
-
// parent changed and it has precedence
|
710
|
-
lastValue = scope.active = parentActive;
|
711
|
-
} else {
|
712
|
-
// if the parent can be assigned then do so
|
713
|
-
setActive(scope.$parent, parentActive = lastValue = scope.active);
|
714
|
-
}
|
715
|
-
}
|
716
|
-
return parentActive;
|
717
|
-
});
|
718
|
-
}
|
719
|
-
|
720
|
-
carouselCtrl.addSlide(scope, element);
|
721
|
-
//when the scope is destroyed then remove the slide from the current slides array
|
722
|
-
scope.$on('$destroy', function() {
|
723
|
-
carouselCtrl.removeSlide(scope);
|
724
|
-
});
|
725
|
-
|
726
|
-
scope.$watch('active', function(active) {
|
727
|
-
if (active) {
|
728
|
-
carouselCtrl.select(scope);
|
729
|
-
}
|
730
|
-
});
|
731
|
-
}
|
732
|
-
};
|
733
|
-
}]);
|
734
|
-
|
735
|
-
angular.module('ui.bootstrap.datepicker', [])
|
736
|
-
|
737
|
-
.constant('datepickerConfig', {
|
738
|
-
dayFormat: 'dd',
|
739
|
-
monthFormat: 'MMMM',
|
740
|
-
yearFormat: 'yyyy',
|
741
|
-
dayHeaderFormat: 'EEE',
|
742
|
-
dayTitleFormat: 'MMMM yyyy',
|
743
|
-
monthTitleFormat: 'yyyy',
|
744
|
-
showWeeks: true,
|
745
|
-
startingDay: 0,
|
746
|
-
yearRange: 20
|
747
|
-
})
|
748
|
-
|
749
|
-
.directive( 'datepicker', ['dateFilter', '$parse', 'datepickerConfig', function (dateFilter, $parse, datepickerConfig) {
|
750
|
-
return {
|
751
|
-
restrict: 'EA',
|
752
|
-
replace: true,
|
753
|
-
scope: {
|
754
|
-
model: '=ngModel',
|
755
|
-
dateDisabled: '&'
|
756
|
-
},
|
757
|
-
templateUrl: 'template/datepicker/datepicker.html',
|
758
|
-
link: function(scope, element, attrs) {
|
759
|
-
scope.mode = 'day'; // Initial mode
|
760
|
-
|
761
|
-
// Configuration parameters
|
762
|
-
var selected = new Date(), showWeeks, minDate, maxDate, format = {};
|
763
|
-
format.day = angular.isDefined(attrs.dayFormat) ? scope.$eval(attrs.dayFormat) : datepickerConfig.dayFormat;
|
764
|
-
format.month = angular.isDefined(attrs.monthFormat) ? scope.$eval(attrs.monthFormat) : datepickerConfig.monthFormat;
|
765
|
-
format.year = angular.isDefined(attrs.yearFormat) ? scope.$eval(attrs.yearFormat) : datepickerConfig.yearFormat;
|
766
|
-
format.dayHeader = angular.isDefined(attrs.dayHeaderFormat) ? scope.$eval(attrs.dayHeaderFormat) : datepickerConfig.dayHeaderFormat;
|
767
|
-
format.dayTitle = angular.isDefined(attrs.dayTitleFormat) ? scope.$eval(attrs.dayTitleFormat) : datepickerConfig.dayTitleFormat;
|
768
|
-
format.monthTitle = angular.isDefined(attrs.monthTitleFormat) ? scope.$eval(attrs.monthTitleFormat) : datepickerConfig.monthTitleFormat;
|
769
|
-
var startingDay = angular.isDefined(attrs.startingDay) ? scope.$eval(attrs.startingDay) : datepickerConfig.startingDay;
|
770
|
-
var yearRange = angular.isDefined(attrs.yearRange) ? scope.$eval(attrs.yearRange) : datepickerConfig.yearRange;
|
771
|
-
|
772
|
-
if (attrs.showWeeks) {
|
773
|
-
scope.$parent.$watch($parse(attrs.showWeeks), function(value) {
|
774
|
-
showWeeks = !! value;
|
775
|
-
updateShowWeekNumbers();
|
776
|
-
});
|
777
|
-
} else {
|
778
|
-
showWeeks = datepickerConfig.showWeeks;
|
779
|
-
updateShowWeekNumbers();
|
780
|
-
}
|
781
|
-
|
782
|
-
if (attrs.min) {
|
783
|
-
scope.$parent.$watch($parse(attrs.min), function(value) {
|
784
|
-
minDate = new Date(value);
|
785
|
-
refill();
|
786
|
-
});
|
787
|
-
}
|
788
|
-
if (attrs.max) {
|
789
|
-
scope.$parent.$watch($parse(attrs.max), function(value) {
|
790
|
-
maxDate = new Date(value);
|
791
|
-
refill();
|
792
|
-
});
|
793
|
-
}
|
794
|
-
|
795
|
-
function updateCalendar (rows, labels, title) {
|
796
|
-
scope.rows = rows;
|
797
|
-
scope.labels = labels;
|
798
|
-
scope.title = title;
|
799
|
-
}
|
800
|
-
|
801
|
-
// Define whether the week number are visible
|
802
|
-
function updateShowWeekNumbers() {
|
803
|
-
scope.showWeekNumbers = ( scope.mode === 'day' && showWeeks );
|
804
|
-
}
|
805
|
-
|
806
|
-
function compare( date1, date2 ) {
|
807
|
-
if ( scope.mode === 'year') {
|
808
|
-
return date2.getFullYear() - date1.getFullYear();
|
809
|
-
} else if ( scope.mode === 'month' ) {
|
810
|
-
return new Date( date2.getFullYear(), date2.getMonth() ) - new Date( date1.getFullYear(), date1.getMonth() );
|
811
|
-
} else if ( scope.mode === 'day' ) {
|
812
|
-
return (new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) - new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) );
|
813
|
-
}
|
814
|
-
}
|
815
|
-
|
816
|
-
function isDisabled(date) {
|
817
|
-
return ((minDate && compare(date, minDate) > 0) || (maxDate && compare(date, maxDate) < 0) || (scope.dateDisabled && scope.dateDisabled({ date: date, mode: scope.mode })));
|
818
|
-
}
|
819
|
-
|
820
|
-
// Split array into smaller arrays
|
821
|
-
var split = function(a, size) {
|
822
|
-
var arrays = [];
|
823
|
-
while (a.length > 0) {
|
824
|
-
arrays.push(a.splice(0, size));
|
825
|
-
}
|
826
|
-
return arrays;
|
827
|
-
};
|
828
|
-
var getDaysInMonth = function( year, month ) {
|
829
|
-
return new Date(year, month + 1, 0).getDate();
|
830
|
-
};
|
831
|
-
|
832
|
-
var fill = {
|
833
|
-
day: function() {
|
834
|
-
var days = [], labels = [], lastDate = null;
|
835
|
-
|
836
|
-
function addDays( dt, n, isCurrentMonth ) {
|
837
|
-
for (var i =0; i < n; i ++) {
|
838
|
-
days.push( {date: new Date(dt), isCurrent: isCurrentMonth, isSelected: isSelected(dt), label: dateFilter(dt, format.day), disabled: isDisabled(dt) } );
|
839
|
-
dt.setDate( dt.getDate() + 1 );
|
840
|
-
}
|
841
|
-
lastDate = dt;
|
842
|
-
}
|
843
|
-
|
844
|
-
var d = new Date(selected);
|
845
|
-
d.setDate(1);
|
846
|
-
|
847
|
-
var difference = startingDay - d.getDay();
|
848
|
-
var numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference;
|
849
|
-
|
850
|
-
if ( numDisplayedFromPreviousMonth > 0 ) {
|
851
|
-
d.setDate( - numDisplayedFromPreviousMonth + 1 );
|
852
|
-
addDays(d, numDisplayedFromPreviousMonth, false);
|
853
|
-
}
|
854
|
-
addDays(lastDate || d, getDaysInMonth(selected.getFullYear(), selected.getMonth()), true);
|
855
|
-
addDays(lastDate, (7 - days.length % 7) % 7, false);
|
856
|
-
|
857
|
-
// Day labels
|
858
|
-
for (i = 0; i < 7; i++) {
|
859
|
-
labels.push( dateFilter(days[i].date, format.dayHeader) );
|
860
|
-
}
|
861
|
-
updateCalendar( split( days, 7 ), labels, dateFilter(selected, format.dayTitle) );
|
862
|
-
},
|
863
|
-
month: function() {
|
864
|
-
var months = [], i = 0, year = selected.getFullYear();
|
865
|
-
while ( i < 12 ) {
|
866
|
-
var dt = new Date(year, i++, 1);
|
867
|
-
months.push( {date: dt, isCurrent: true, isSelected: isSelected(dt), label: dateFilter(dt, format.month), disabled: isDisabled(dt)} );
|
868
|
-
}
|
869
|
-
updateCalendar( split( months, 3 ), [], dateFilter(selected, format.monthTitle) );
|
870
|
-
},
|
871
|
-
year: function() {
|
872
|
-
var years = [], year = parseInt((selected.getFullYear() - 1) / yearRange, 10) * yearRange + 1;
|
873
|
-
for ( var i = 0; i < yearRange; i++ ) {
|
874
|
-
var dt = new Date(year + i, 0, 1);
|
875
|
-
years.push( {date: dt, isCurrent: true, isSelected: isSelected(dt), label: dateFilter(dt, format.year), disabled: isDisabled(dt)} );
|
876
|
-
}
|
877
|
-
var title = years[0].label + ' - ' + years[years.length - 1].label;
|
878
|
-
updateCalendar( split( years, 5 ), [], title );
|
879
|
-
}
|
880
|
-
};
|
881
|
-
var refill = function() {
|
882
|
-
fill[scope.mode]();
|
883
|
-
};
|
884
|
-
var isSelected = function( dt ) {
|
885
|
-
if ( scope.model && scope.model.getFullYear() === dt.getFullYear() ) {
|
886
|
-
if ( scope.mode === 'year' ) {
|
887
|
-
return true;
|
888
|
-
}
|
889
|
-
if ( scope.model.getMonth() === dt.getMonth() ) {
|
890
|
-
return ( scope.mode === 'month' || (scope.mode === 'day' && scope.model.getDate() === dt.getDate()) );
|
891
|
-
}
|
892
|
-
}
|
893
|
-
return false;
|
894
|
-
};
|
895
|
-
|
896
|
-
scope.$watch('model', function ( dt, olddt ) {
|
897
|
-
if ( angular.isDate(dt) ) {
|
898
|
-
selected = angular.copy(dt);
|
899
|
-
}
|
900
|
-
|
901
|
-
if ( ! angular.equals(dt, olddt) ) {
|
902
|
-
refill();
|
903
|
-
}
|
904
|
-
});
|
905
|
-
scope.$watch('mode', function() {
|
906
|
-
updateShowWeekNumbers();
|
907
|
-
refill();
|
908
|
-
});
|
909
|
-
|
910
|
-
scope.select = function( dt ) {
|
911
|
-
selected = new Date(dt);
|
912
|
-
|
913
|
-
if ( scope.mode === 'year' ) {
|
914
|
-
scope.mode = 'month';
|
915
|
-
selected.setFullYear( dt.getFullYear() );
|
916
|
-
} else if ( scope.mode === 'month' ) {
|
917
|
-
scope.mode = 'day';
|
918
|
-
selected.setMonth( dt.getMonth() );
|
919
|
-
} else if ( scope.mode === 'day' ) {
|
920
|
-
scope.model = new Date(selected);
|
921
|
-
}
|
922
|
-
};
|
923
|
-
scope.move = function(step) {
|
924
|
-
if (scope.mode === 'day') {
|
925
|
-
selected.setMonth( selected.getMonth() + step );
|
926
|
-
} else if (scope.mode === 'month') {
|
927
|
-
selected.setFullYear( selected.getFullYear() + step );
|
928
|
-
} else if (scope.mode === 'year') {
|
929
|
-
selected.setFullYear( selected.getFullYear() + step * yearRange );
|
930
|
-
}
|
931
|
-
refill();
|
932
|
-
};
|
933
|
-
scope.toggleMode = function() {
|
934
|
-
scope.mode = ( scope.mode === 'day' ) ? 'month' : ( scope.mode === 'month' ) ? 'year' : 'day';
|
935
|
-
};
|
936
|
-
scope.getWeekNumber = function(row) {
|
937
|
-
if ( scope.mode !== 'day' || ! scope.showWeekNumbers || row.length !== 7 ) {
|
938
|
-
return;
|
939
|
-
}
|
940
|
-
|
941
|
-
var index = ( startingDay > 4 ) ? 11 - startingDay : 4 - startingDay; // Thursday
|
942
|
-
var d = new Date( row[ index ].date );
|
943
|
-
d.setHours(0, 0, 0);
|
944
|
-
return Math.ceil((((d - new Date(d.getFullYear(), 0, 1)) / 86400000) + 1) / 7); // 86400000 = 1000*60*60*24;
|
945
|
-
};
|
946
|
-
}
|
947
|
-
};
|
948
|
-
}]);
|
949
|
-
// The `$dialogProvider` can be used to configure global defaults for your
|
950
|
-
// `$dialog` service.
|
951
|
-
var dialogModule = angular.module('ui.bootstrap.dialog', ['ui.bootstrap.transition']);
|
952
|
-
|
953
|
-
dialogModule.controller('MessageBoxController', ['$scope', 'dialog', 'model', function($scope, dialog, model){
|
954
|
-
$scope.title = model.title;
|
955
|
-
$scope.message = model.message;
|
956
|
-
$scope.buttons = model.buttons;
|
957
|
-
$scope.close = function(res){
|
958
|
-
dialog.close(res);
|
959
|
-
};
|
960
|
-
}]);
|
961
|
-
|
962
|
-
dialogModule.provider("$dialog", function(){
|
963
|
-
|
964
|
-
// The default options for all dialogs.
|
965
|
-
var defaults = {
|
966
|
-
backdrop: true,
|
967
|
-
dialogClass: 'modal',
|
968
|
-
backdropClass: 'modal-backdrop',
|
969
|
-
transitionClass: 'fade',
|
970
|
-
triggerClass: 'in',
|
971
|
-
resolve:{},
|
972
|
-
backdropFade: false,
|
973
|
-
dialogFade:false,
|
974
|
-
keyboard: true, // close with esc key
|
975
|
-
backdropClick: true // only in conjunction with backdrop=true
|
976
|
-
/* other options: template, templateUrl, controller */
|
977
|
-
};
|
978
|
-
|
979
|
-
var globalOptions = {};
|
980
|
-
|
981
|
-
var activeBackdrops = {value : 0};
|
982
|
-
|
983
|
-
// The `options({})` allows global configuration of all dialogs in the application.
|
984
|
-
//
|
985
|
-
// var app = angular.module('App', ['ui.bootstrap.dialog'], function($dialogProvider){
|
986
|
-
// // don't close dialog when backdrop is clicked by default
|
987
|
-
// $dialogProvider.options({backdropClick: false});
|
988
|
-
// });
|
989
|
-
this.options = function(value){
|
990
|
-
globalOptions = value;
|
991
|
-
};
|
992
|
-
|
993
|
-
// Returns the actual `$dialog` service that is injected in controllers
|
994
|
-
this.$get = ["$http", "$document", "$compile", "$rootScope", "$controller", "$templateCache", "$q", "$transition", "$injector",
|
995
|
-
function ($http, $document, $compile, $rootScope, $controller, $templateCache, $q, $transition, $injector) {
|
996
|
-
|
997
|
-
var body = $document.find('body');
|
998
|
-
|
999
|
-
function createElement(clazz) {
|
1000
|
-
var el = angular.element("<div>");
|
1001
|
-
el.addClass(clazz);
|
1002
|
-
return el;
|
1003
|
-
}
|
1004
|
-
|
1005
|
-
// The `Dialog` class represents a modal dialog. The dialog class can be invoked by providing an options object
|
1006
|
-
// containing at lest template or templateUrl and controller:
|
1007
|
-
//
|
1008
|
-
// var d = new Dialog({templateUrl: 'foo.html', controller: 'BarController'});
|
1009
|
-
//
|
1010
|
-
// Dialogs can also be created using templateUrl and controller as distinct arguments:
|
1011
|
-
//
|
1012
|
-
// var d = new Dialog('path/to/dialog.html', MyDialogController);
|
1013
|
-
function Dialog(opts) {
|
1014
|
-
|
1015
|
-
var self = this, options = this.options = angular.extend({}, defaults, globalOptions, opts);
|
1016
|
-
this._open = false;
|
1017
|
-
|
1018
|
-
this.backdropEl = createElement(options.backdropClass);
|
1019
|
-
if(options.backdropFade){
|
1020
|
-
this.backdropEl.addClass(options.transitionClass);
|
1021
|
-
this.backdropEl.removeClass(options.triggerClass);
|
1022
|
-
}
|
1023
|
-
|
1024
|
-
this.modalEl = createElement(options.dialogClass);
|
1025
|
-
if(options.dialogFade){
|
1026
|
-
this.modalEl.addClass(options.transitionClass);
|
1027
|
-
this.modalEl.removeClass(options.triggerClass);
|
1028
|
-
}
|
1029
|
-
|
1030
|
-
this.handledEscapeKey = function(e) {
|
1031
|
-
if (e.which === 27) {
|
1032
|
-
self.close();
|
1033
|
-
e.preventDefault();
|
1034
|
-
self.$scope.$apply();
|
1035
|
-
}
|
1036
|
-
};
|
1037
|
-
|
1038
|
-
this.handleBackDropClick = function(e) {
|
1039
|
-
self.close();
|
1040
|
-
e.preventDefault();
|
1041
|
-
self.$scope.$apply();
|
1042
|
-
};
|
1043
|
-
|
1044
|
-
this.handleLocationChange = function() {
|
1045
|
-
self.close();
|
1046
|
-
};
|
1047
|
-
}
|
1048
|
-
|
1049
|
-
// The `isOpen()` method returns wether the dialog is currently visible.
|
1050
|
-
Dialog.prototype.isOpen = function(){
|
1051
|
-
return this._open;
|
1052
|
-
};
|
1053
|
-
|
1054
|
-
// The `open(templateUrl, controller)` method opens the dialog.
|
1055
|
-
// Use the `templateUrl` and `controller` arguments if specifying them at dialog creation time is not desired.
|
1056
|
-
Dialog.prototype.open = function(templateUrl, controller){
|
1057
|
-
var self = this, options = this.options;
|
1058
|
-
|
1059
|
-
if(templateUrl){
|
1060
|
-
options.templateUrl = templateUrl;
|
1061
|
-
}
|
1062
|
-
if(controller){
|
1063
|
-
options.controller = controller;
|
1064
|
-
}
|
1065
|
-
|
1066
|
-
if(!(options.template || options.templateUrl)) {
|
1067
|
-
throw new Error('Dialog.open expected template or templateUrl, neither found. Use options or open method to specify them.');
|
1068
|
-
}
|
1069
|
-
|
1070
|
-
this._loadResolves().then(function(locals) {
|
1071
|
-
var $scope = locals.$scope = self.$scope = locals.$scope ? locals.$scope : $rootScope.$new();
|
1072
|
-
|
1073
|
-
self.modalEl.html(locals.$template);
|
1074
|
-
|
1075
|
-
if (self.options.controller) {
|
1076
|
-
var ctrl = $controller(self.options.controller, locals);
|
1077
|
-
self.modalEl.children().data('ngControllerController', ctrl);
|
1078
|
-
}
|
1079
|
-
|
1080
|
-
$compile(self.modalEl)($scope);
|
1081
|
-
self._addElementsToDom();
|
1082
|
-
|
1083
|
-
// trigger tranisitions
|
1084
|
-
setTimeout(function(){
|
1085
|
-
if(self.options.dialogFade){ self.modalEl.addClass(self.options.triggerClass); }
|
1086
|
-
if(self.options.backdropFade){ self.backdropEl.addClass(self.options.triggerClass); }
|
1087
|
-
});
|
1088
|
-
|
1089
|
-
self._bindEvents();
|
1090
|
-
});
|
1091
|
-
|
1092
|
-
this.deferred = $q.defer();
|
1093
|
-
return this.deferred.promise;
|
1094
|
-
};
|
1095
|
-
|
1096
|
-
// closes the dialog and resolves the promise returned by the `open` method with the specified result.
|
1097
|
-
Dialog.prototype.close = function(result){
|
1098
|
-
var self = this;
|
1099
|
-
var fadingElements = this._getFadingElements();
|
1100
|
-
|
1101
|
-
if(fadingElements.length > 0){
|
1102
|
-
for (var i = fadingElements.length - 1; i >= 0; i--) {
|
1103
|
-
$transition(fadingElements[i], removeTriggerClass).then(onCloseComplete);
|
1104
|
-
}
|
1105
|
-
return;
|
1106
|
-
}
|
1107
|
-
|
1108
|
-
this._onCloseComplete(result);
|
1109
|
-
|
1110
|
-
function removeTriggerClass(el){
|
1111
|
-
el.removeClass(self.options.triggerClass);
|
1112
|
-
}
|
1113
|
-
|
1114
|
-
function onCloseComplete(){
|
1115
|
-
if(self._open){
|
1116
|
-
self._onCloseComplete(result);
|
1117
|
-
}
|
1118
|
-
}
|
1119
|
-
};
|
1120
|
-
|
1121
|
-
Dialog.prototype._getFadingElements = function(){
|
1122
|
-
var elements = [];
|
1123
|
-
if(this.options.dialogFade){
|
1124
|
-
elements.push(this.modalEl);
|
1125
|
-
}
|
1126
|
-
if(this.options.backdropFade){
|
1127
|
-
elements.push(this.backdropEl);
|
1128
|
-
}
|
1129
|
-
|
1130
|
-
return elements;
|
1131
|
-
};
|
1132
|
-
|
1133
|
-
Dialog.prototype._bindEvents = function() {
|
1134
|
-
if(this.options.keyboard){ body.bind('keydown', this.handledEscapeKey); }
|
1135
|
-
if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.bind('click', this.handleBackDropClick); }
|
1136
|
-
};
|
1137
|
-
|
1138
|
-
Dialog.prototype._unbindEvents = function() {
|
1139
|
-
if(this.options.keyboard){ body.unbind('keydown', this.handledEscapeKey); }
|
1140
|
-
if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.unbind('click', this.handleBackDropClick); }
|
1141
|
-
};
|
1142
|
-
|
1143
|
-
Dialog.prototype._onCloseComplete = function(result) {
|
1144
|
-
this._removeElementsFromDom();
|
1145
|
-
this._unbindEvents();
|
1146
|
-
|
1147
|
-
this.deferred.resolve(result);
|
1148
|
-
};
|
1149
|
-
|
1150
|
-
Dialog.prototype._addElementsToDom = function(){
|
1151
|
-
body.append(this.modalEl);
|
1152
|
-
|
1153
|
-
if(this.options.backdrop) {
|
1154
|
-
if (activeBackdrops.value === 0) {
|
1155
|
-
body.append(this.backdropEl);
|
1156
|
-
}
|
1157
|
-
activeBackdrops.value++;
|
1158
|
-
}
|
1159
|
-
|
1160
|
-
this._open = true;
|
1161
|
-
};
|
1162
|
-
|
1163
|
-
Dialog.prototype._removeElementsFromDom = function(){
|
1164
|
-
this.modalEl.remove();
|
1165
|
-
|
1166
|
-
if(this.options.backdrop) {
|
1167
|
-
activeBackdrops.value--;
|
1168
|
-
if (activeBackdrops.value === 0) {
|
1169
|
-
this.backdropEl.remove();
|
1170
|
-
}
|
1171
|
-
}
|
1172
|
-
this._open = false;
|
1173
|
-
};
|
1174
|
-
|
1175
|
-
// Loads all `options.resolve` members to be used as locals for the controller associated with the dialog.
|
1176
|
-
Dialog.prototype._loadResolves = function(){
|
1177
|
-
var values = [], keys = [], templatePromise, self = this;
|
1178
|
-
|
1179
|
-
if (this.options.template) {
|
1180
|
-
templatePromise = $q.when(this.options.template);
|
1181
|
-
} else if (this.options.templateUrl) {
|
1182
|
-
templatePromise = $http.get(this.options.templateUrl, {cache:$templateCache})
|
1183
|
-
.then(function(response) { return response.data; });
|
1184
|
-
}
|
1185
|
-
|
1186
|
-
angular.forEach(this.options.resolve || [], function(value, key) {
|
1187
|
-
keys.push(key);
|
1188
|
-
values.push(angular.isString(value) ? $injector.get(value) : $injector.invoke(value));
|
1189
|
-
});
|
1190
|
-
|
1191
|
-
keys.push('$template');
|
1192
|
-
values.push(templatePromise);
|
1193
|
-
|
1194
|
-
return $q.all(values).then(function(values) {
|
1195
|
-
var locals = {};
|
1196
|
-
angular.forEach(values, function(value, index) {
|
1197
|
-
locals[keys[index]] = value;
|
1198
|
-
});
|
1199
|
-
locals.dialog = self;
|
1200
|
-
return locals;
|
1201
|
-
});
|
1202
|
-
};
|
1203
|
-
|
1204
|
-
// The actual `$dialog` service that is injected in controllers.
|
1205
|
-
return {
|
1206
|
-
// Creates a new `Dialog` with the specified options.
|
1207
|
-
dialog: function(opts){
|
1208
|
-
return new Dialog(opts);
|
1209
|
-
},
|
1210
|
-
// creates a new `Dialog` tied to the default message box template and controller.
|
1211
|
-
//
|
1212
|
-
// Arguments `title` and `message` are rendered in the modal header and body sections respectively.
|
1213
|
-
// The `buttons` array holds an object with the following members for each button to include in the
|
1214
|
-
// modal footer section:
|
1215
|
-
//
|
1216
|
-
// * `result`: the result to pass to the `close` method of the dialog when the button is clicked
|
1217
|
-
// * `label`: the label of the button
|
1218
|
-
// * `cssClass`: additional css class(es) to apply to the button for styling
|
1219
|
-
messageBox: function(title, message, buttons){
|
1220
|
-
return new Dialog({templateUrl: 'template/dialog/message.html', controller: 'MessageBoxController', resolve:
|
1221
|
-
{model: function() {
|
1222
|
-
return {
|
1223
|
-
title: title,
|
1224
|
-
message: message,
|
1225
|
-
buttons: buttons
|
1226
|
-
};
|
1227
|
-
}
|
1228
|
-
}});
|
1229
|
-
}
|
1230
|
-
};
|
1231
|
-
}];
|
1232
|
-
});
|
1233
|
-
|
1234
|
-
/*
|
1235
|
-
* dropdownToggle - Provides dropdown menu functionality in place of bootstrap js
|
1236
|
-
* @restrict class or attribute
|
1237
|
-
* @example:
|
1238
|
-
<li class="dropdown">
|
1239
|
-
<a class="dropdown-toggle">My Dropdown Menu</a>
|
1240
|
-
<ul class="dropdown-menu">
|
1241
|
-
<li ng-repeat="choice in dropChoices">
|
1242
|
-
<a ng-href="{{choice.href}}">{{choice.text}}</a>
|
1243
|
-
</li>
|
1244
|
-
</ul>
|
1245
|
-
</li>
|
1246
|
-
*/
|
1247
|
-
|
1248
|
-
angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle', ['$document', '$location', function ($document, $location) {
|
1249
|
-
var openElement = null,
|
1250
|
-
closeMenu = angular.noop;
|
1251
|
-
return {
|
1252
|
-
restrict: 'CA',
|
1253
|
-
link: function(scope, element, attrs) {
|
1254
|
-
scope.$watch('$location.path', function() { closeMenu(); });
|
1255
|
-
element.parent().bind('click', function() { closeMenu(); });
|
1256
|
-
element.bind('click', function (event) {
|
1257
|
-
|
1258
|
-
var elementWasOpen = (element === openElement);
|
1259
|
-
|
1260
|
-
event.preventDefault();
|
1261
|
-
event.stopPropagation();
|
1262
|
-
|
1263
|
-
if (!!openElement) {
|
1264
|
-
closeMenu();
|
1265
|
-
}
|
1266
|
-
|
1267
|
-
if (!elementWasOpen) {
|
1268
|
-
element.parent().addClass('open');
|
1269
|
-
openElement = element;
|
1270
|
-
closeMenu = function (event) {
|
1271
|
-
if (event) {
|
1272
|
-
event.preventDefault();
|
1273
|
-
event.stopPropagation();
|
1274
|
-
}
|
1275
|
-
$document.unbind('click', closeMenu);
|
1276
|
-
element.parent().removeClass('open');
|
1277
|
-
closeMenu = angular.noop;
|
1278
|
-
openElement = null;
|
1279
|
-
};
|
1280
|
-
$document.bind('click', closeMenu);
|
1281
|
-
}
|
1282
|
-
});
|
1283
|
-
}
|
1284
|
-
};
|
1285
|
-
}]);
|
1286
|
-
angular.module('ui.bootstrap.modal', ['ui.bootstrap.dialog'])
|
1287
|
-
.directive('modal', ['$parse', '$dialog', function($parse, $dialog) {
|
1288
|
-
return {
|
1289
|
-
restrict: 'EA',
|
1290
|
-
terminal: true,
|
1291
|
-
link: function(scope, elm, attrs) {
|
1292
|
-
var opts = angular.extend({}, scope.$eval(attrs.uiOptions || attrs.bsOptions || attrs.options));
|
1293
|
-
var shownExpr = attrs.modal || attrs.show;
|
1294
|
-
var setClosed;
|
1295
|
-
|
1296
|
-
// Create a dialog with the template as the contents of the directive
|
1297
|
-
// Add the current scope as the resolve in order to make the directive scope as a dialog controller scope
|
1298
|
-
opts = angular.extend(opts, {
|
1299
|
-
template: elm.html(),
|
1300
|
-
resolve: { $scope: function() { return scope; } }
|
1301
|
-
});
|
1302
|
-
var dialog = $dialog.dialog(opts);
|
1303
|
-
|
1304
|
-
elm.remove();
|
1305
|
-
|
1306
|
-
if (attrs.close) {
|
1307
|
-
setClosed = function() {
|
1308
|
-
$parse(attrs.close)(scope);
|
1309
|
-
};
|
1310
|
-
} else {
|
1311
|
-
setClosed = function() {
|
1312
|
-
if (angular.isFunction($parse(shownExpr).assign)) {
|
1313
|
-
$parse(shownExpr).assign(scope, false);
|
1314
|
-
}
|
1315
|
-
};
|
1316
|
-
}
|
1317
|
-
|
1318
|
-
scope.$watch(shownExpr, function(isShown, oldShown) {
|
1319
|
-
if (isShown) {
|
1320
|
-
dialog.open().then(function(){
|
1321
|
-
setClosed();
|
1322
|
-
});
|
1323
|
-
} else {
|
1324
|
-
//Make sure it is not opened
|
1325
|
-
if (dialog.isOpen()){
|
1326
|
-
dialog.close();
|
1327
|
-
}
|
1328
|
-
}
|
1329
|
-
});
|
1330
|
-
}
|
1331
|
-
};
|
1332
|
-
}]);
|
1333
|
-
angular.module('ui.bootstrap.pagination', [])
|
1334
|
-
|
1335
|
-
.controller('PaginationController', ['$scope', function (scope) {
|
1336
|
-
|
1337
|
-
scope.noPrevious = function() {
|
1338
|
-
return scope.currentPage === 1;
|
1339
|
-
};
|
1340
|
-
scope.noNext = function() {
|
1341
|
-
return scope.currentPage === scope.numPages;
|
1342
|
-
};
|
1343
|
-
|
1344
|
-
scope.isActive = function(page) {
|
1345
|
-
return scope.currentPage === page;
|
1346
|
-
};
|
1347
|
-
|
1348
|
-
scope.selectPage = function(page) {
|
1349
|
-
if ( ! scope.isActive(page) && page > 0 && page <= scope.numPages) {
|
1350
|
-
scope.currentPage = page;
|
1351
|
-
scope.onSelectPage({ page: page });
|
1352
|
-
}
|
1353
|
-
};
|
1354
|
-
}])
|
1355
|
-
|
1356
|
-
.constant('paginationConfig', {
|
1357
|
-
boundaryLinks: false,
|
1358
|
-
directionLinks: true,
|
1359
|
-
firstText: 'First',
|
1360
|
-
previousText: 'Previous',
|
1361
|
-
nextText: 'Next',
|
1362
|
-
lastText: 'Last',
|
1363
|
-
rotate: true
|
1364
|
-
})
|
1365
|
-
|
1366
|
-
.directive('pagination', ['paginationConfig', function(paginationConfig) {
|
1367
|
-
return {
|
1368
|
-
restrict: 'EA',
|
1369
|
-
scope: {
|
1370
|
-
numPages: '=',
|
1371
|
-
currentPage: '=',
|
1372
|
-
maxSize: '=',
|
1373
|
-
onSelectPage: '&'
|
1374
|
-
},
|
1375
|
-
controller: 'PaginationController',
|
1376
|
-
templateUrl: 'template/pagination/pagination.html',
|
1377
|
-
replace: true,
|
1378
|
-
link: function(scope, element, attrs) {
|
1379
|
-
|
1380
|
-
// Setup configuration parameters
|
1381
|
-
var boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks;
|
1382
|
-
var directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$eval(attrs.directionLinks) : paginationConfig.directionLinks;
|
1383
|
-
var firstText = angular.isDefined(attrs.firstText) ? scope.$parent.$eval(attrs.firstText) : paginationConfig.firstText;
|
1384
|
-
var previousText = angular.isDefined(attrs.previousText) ? scope.$parent.$eval(attrs.previousText) : paginationConfig.previousText;
|
1385
|
-
var nextText = angular.isDefined(attrs.nextText) ? scope.$parent.$eval(attrs.nextText) : paginationConfig.nextText;
|
1386
|
-
var lastText = angular.isDefined(attrs.lastText) ? scope.$parent.$eval(attrs.lastText) : paginationConfig.lastText;
|
1387
|
-
var rotate = angular.isDefined(attrs.rotate) ? scope.$eval(attrs.rotate) : paginationConfig.rotate;
|
1388
|
-
|
1389
|
-
// Create page object used in template
|
1390
|
-
function makePage(number, text, isActive, isDisabled) {
|
1391
|
-
return {
|
1392
|
-
number: number,
|
1393
|
-
text: text,
|
1394
|
-
active: isActive,
|
1395
|
-
disabled: isDisabled
|
1396
|
-
};
|
1397
|
-
}
|
1398
|
-
|
1399
|
-
scope.$watch('numPages + currentPage + maxSize', function() {
|
1400
|
-
scope.pages = [];
|
1401
|
-
|
1402
|
-
// Default page limits
|
1403
|
-
var startPage = 1, endPage = scope.numPages;
|
1404
|
-
var isMaxSized = ( angular.isDefined(scope.maxSize) && scope.maxSize < scope.numPages );
|
1405
|
-
|
1406
|
-
// recompute if maxSize
|
1407
|
-
if ( isMaxSized ) {
|
1408
|
-
if ( rotate ) {
|
1409
|
-
// Current page is displayed in the middle of the visible ones
|
1410
|
-
startPage = Math.max(scope.currentPage - Math.floor(scope.maxSize/2), 1);
|
1411
|
-
endPage = startPage + scope.maxSize - 1;
|
1412
|
-
|
1413
|
-
// Adjust if limit is exceeded
|
1414
|
-
if (endPage > scope.numPages) {
|
1415
|
-
endPage = scope.numPages;
|
1416
|
-
startPage = endPage - scope.maxSize + 1;
|
1417
|
-
}
|
1418
|
-
} else {
|
1419
|
-
// Visible pages are paginated with maxSize
|
1420
|
-
startPage = ((Math.ceil(scope.currentPage / scope.maxSize) - 1) * scope.maxSize) + 1;
|
1421
|
-
|
1422
|
-
// Adjust last page if limit is exceeded
|
1423
|
-
endPage = Math.min(startPage + scope.maxSize - 1, scope.numPages);
|
1424
|
-
}
|
1425
|
-
}
|
1426
|
-
|
1427
|
-
// Add page number links
|
1428
|
-
for (var number = startPage; number <= endPage; number++) {
|
1429
|
-
var page = makePage(number, number, scope.isActive(number), false);
|
1430
|
-
scope.pages.push(page);
|
1431
|
-
}
|
1432
|
-
|
1433
|
-
// Add links to move between page sets
|
1434
|
-
if ( isMaxSized && ! rotate ) {
|
1435
|
-
if ( startPage > 1 ) {
|
1436
|
-
var previousPageSet = makePage(startPage - 1, '...', false, false);
|
1437
|
-
scope.pages.unshift(previousPageSet);
|
1438
|
-
}
|
1439
|
-
|
1440
|
-
if ( endPage < scope.numPages ) {
|
1441
|
-
var nextPageSet = makePage(endPage + 1, '...', false, false);
|
1442
|
-
scope.pages.push(nextPageSet);
|
1443
|
-
}
|
1444
|
-
}
|
1445
|
-
|
1446
|
-
// Add previous & next links
|
1447
|
-
if (directionLinks) {
|
1448
|
-
var previousPage = makePage(scope.currentPage - 1, previousText, false, scope.noPrevious());
|
1449
|
-
scope.pages.unshift(previousPage);
|
1450
|
-
|
1451
|
-
var nextPage = makePage(scope.currentPage + 1, nextText, false, scope.noNext());
|
1452
|
-
scope.pages.push(nextPage);
|
1453
|
-
}
|
1454
|
-
|
1455
|
-
// Add first & last links
|
1456
|
-
if (boundaryLinks) {
|
1457
|
-
var firstPage = makePage(1, firstText, false, scope.noPrevious());
|
1458
|
-
scope.pages.unshift(firstPage);
|
1459
|
-
|
1460
|
-
var lastPage = makePage(scope.numPages, lastText, false, scope.noNext());
|
1461
|
-
scope.pages.push(lastPage);
|
1462
|
-
}
|
1463
|
-
|
1464
|
-
if ( scope.currentPage > scope.numPages ) {
|
1465
|
-
scope.selectPage(scope.numPages);
|
1466
|
-
}
|
1467
|
-
});
|
1468
|
-
}
|
1469
|
-
};
|
1470
|
-
}])
|
1471
|
-
|
1472
|
-
.constant('pagerConfig', {
|
1473
|
-
previousText: '« Previous',
|
1474
|
-
nextText: 'Next »',
|
1475
|
-
align: true
|
1476
|
-
})
|
1477
|
-
|
1478
|
-
.directive('pager', ['pagerConfig', function(config) {
|
1479
|
-
return {
|
1480
|
-
restrict: 'EA',
|
1481
|
-
scope: {
|
1482
|
-
numPages: '=',
|
1483
|
-
currentPage: '=',
|
1484
|
-
onSelectPage: '&'
|
1485
|
-
},
|
1486
|
-
controller: 'PaginationController',
|
1487
|
-
templateUrl: 'template/pagination/pager.html',
|
1488
|
-
replace: true,
|
1489
|
-
link: function(scope, element, attrs, paginationCtrl) {
|
1490
|
-
|
1491
|
-
// Setup configuration parameters
|
1492
|
-
var previousText = angular.isDefined(attrs.previousText) ? scope.$parent.$eval(attrs.previousText) : config.previousText;
|
1493
|
-
var nextText = angular.isDefined(attrs.nextText) ? scope.$parent.$eval(attrs.nextText) : config.nextText;
|
1494
|
-
var align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : config.align;
|
1495
|
-
|
1496
|
-
// Create page object used in template
|
1497
|
-
function makePage(number, text, isDisabled, isPrevious, isNext) {
|
1498
|
-
return {
|
1499
|
-
number: number,
|
1500
|
-
text: text,
|
1501
|
-
disabled: isDisabled,
|
1502
|
-
previous: ( align && isPrevious ),
|
1503
|
-
next: ( align && isNext )
|
1504
|
-
};
|
1505
|
-
}
|
1506
|
-
|
1507
|
-
scope.$watch('numPages + currentPage', function() {
|
1508
|
-
scope.pages = [];
|
1509
|
-
|
1510
|
-
// Add previous & next links
|
1511
|
-
var previousPage = makePage(scope.currentPage - 1, previousText, scope.noPrevious(), true, false);
|
1512
|
-
scope.pages.unshift(previousPage);
|
1513
|
-
|
1514
|
-
var nextPage = makePage(scope.currentPage + 1, nextText, scope.noNext(), false, true);
|
1515
|
-
scope.pages.push(nextPage);
|
1516
|
-
|
1517
|
-
if ( scope.currentPage > scope.numPages ) {
|
1518
|
-
scope.selectPage(scope.numPages);
|
1519
|
-
}
|
1520
|
-
});
|
1521
|
-
}
|
1522
|
-
};
|
1523
|
-
}]);
|
1524
|
-
|
1525
|
-
angular.module('ui.bootstrap.position', [])
|
1526
|
-
|
1527
|
-
/**
|
1528
|
-
* A set of utility methods that can be use to retrieve position of DOM elements.
|
1529
|
-
* It is meant to be used where we need to absolute-position DOM elements in
|
1530
|
-
* relation to other, existing elements (this is the case for tooltips, popovers,
|
1531
|
-
* typeahead suggestions etc.).
|
1532
|
-
*/
|
1533
|
-
.factory('$position', ['$document', '$window', function ($document, $window) {
|
1534
|
-
|
1535
|
-
var mouseX, mouseY;
|
1536
|
-
|
1537
|
-
$document.bind('mousemove', function mouseMoved(event) {
|
1538
|
-
mouseX = event.pageX;
|
1539
|
-
mouseY = event.pageY;
|
1540
|
-
});
|
1541
|
-
|
1542
|
-
function getStyle(el, cssprop) {
|
1543
|
-
if (el.currentStyle) { //IE
|
1544
|
-
return el.currentStyle[cssprop];
|
1545
|
-
} else if ($window.getComputedStyle) {
|
1546
|
-
return $window.getComputedStyle(el)[cssprop];
|
1547
|
-
}
|
1548
|
-
// finally try and get inline style
|
1549
|
-
return el.style[cssprop];
|
1550
|
-
}
|
1551
|
-
|
1552
|
-
/**
|
1553
|
-
* Checks if a given element is statically positioned
|
1554
|
-
* @param element - raw DOM element
|
1555
|
-
*/
|
1556
|
-
function isStaticPositioned(element) {
|
1557
|
-
return (getStyle(element, "position") || 'static' ) === 'static';
|
1558
|
-
}
|
1559
|
-
|
1560
|
-
/**
|
1561
|
-
* returns the closest, non-statically positioned parentOffset of a given element
|
1562
|
-
* @param element
|
1563
|
-
*/
|
1564
|
-
var parentOffsetEl = function (element) {
|
1565
|
-
var docDomEl = $document[0];
|
1566
|
-
var offsetParent = element.offsetParent || docDomEl;
|
1567
|
-
while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
|
1568
|
-
offsetParent = offsetParent.offsetParent;
|
1569
|
-
}
|
1570
|
-
return offsetParent || docDomEl;
|
1571
|
-
};
|
1572
|
-
|
1573
|
-
return {
|
1574
|
-
/**
|
1575
|
-
* Provides read-only equivalent of jQuery's position function:
|
1576
|
-
* http://api.jquery.com/position/
|
1577
|
-
*/
|
1578
|
-
position: function (element) {
|
1579
|
-
var elBCR = this.offset(element);
|
1580
|
-
var offsetParentBCR = { top: 0, left: 0 };
|
1581
|
-
var offsetParentEl = parentOffsetEl(element[0]);
|
1582
|
-
if (offsetParentEl != $document[0]) {
|
1583
|
-
offsetParentBCR = this.offset(angular.element(offsetParentEl));
|
1584
|
-
offsetParentBCR.top += offsetParentEl.clientTop;
|
1585
|
-
offsetParentBCR.left += offsetParentEl.clientLeft;
|
1586
|
-
}
|
1587
|
-
|
1588
|
-
return {
|
1589
|
-
width: element.prop('offsetWidth'),
|
1590
|
-
height: element.prop('offsetHeight'),
|
1591
|
-
top: elBCR.top - offsetParentBCR.top,
|
1592
|
-
left: elBCR.left - offsetParentBCR.left
|
1593
|
-
};
|
1594
|
-
},
|
1595
|
-
|
1596
|
-
/**
|
1597
|
-
* Provides read-only equivalent of jQuery's offset function:
|
1598
|
-
* http://api.jquery.com/offset/
|
1599
|
-
*/
|
1600
|
-
offset: function (element) {
|
1601
|
-
var boundingClientRect = element[0].getBoundingClientRect();
|
1602
|
-
return {
|
1603
|
-
width: element.prop('offsetWidth'),
|
1604
|
-
height: element.prop('offsetHeight'),
|
1605
|
-
top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop),
|
1606
|
-
left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft)
|
1607
|
-
};
|
1608
|
-
},
|
1609
|
-
|
1610
|
-
/**
|
1611
|
-
* Provides the coordinates of the mouse
|
1612
|
-
*/
|
1613
|
-
mouse: function () {
|
1614
|
-
return {x: mouseX, y: mouseY};
|
1615
|
-
}
|
1616
|
-
};
|
1617
|
-
}]);
|
1618
|
-
|
1619
|
-
/**
|
1620
|
-
* The following features are still outstanding: animation as a
|
1621
|
-
* function, placement as a function, inside, support for more triggers than
|
1622
|
-
* just mouse enter/leave, html tooltips, and selector delegation.
|
1623
|
-
*/
|
1624
|
-
angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
1625
|
-
|
1626
|
-
/**
|
1627
|
-
* The $tooltip service creates tooltip- and popover-like directives as well as
|
1628
|
-
* houses global options for them.
|
1629
|
-
*/
|
1630
|
-
.provider( '$tooltip', function () {
|
1631
|
-
// The default options tooltip and popover.
|
1632
|
-
var defaultOptions = {
|
1633
|
-
placement: 'top',
|
1634
|
-
animation: true,
|
1635
|
-
popupDelay: 0
|
1636
|
-
};
|
1637
|
-
|
1638
|
-
// Default hide triggers for each show trigger
|
1639
|
-
var triggerMap = {
|
1640
|
-
'mouseenter': 'mouseleave',
|
1641
|
-
'click': 'click',
|
1642
|
-
'focus': 'blur'
|
1643
|
-
};
|
1644
|
-
|
1645
|
-
// The options specified to the provider globally.
|
1646
|
-
var globalOptions = {};
|
1647
|
-
|
1648
|
-
/**
|
1649
|
-
* `options({})` allows global configuration of all tooltips in the
|
1650
|
-
* application.
|
1651
|
-
*
|
1652
|
-
* var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
|
1653
|
-
* // place tooltips left instead of top by default
|
1654
|
-
* $tooltipProvider.options( { placement: 'left' } );
|
1655
|
-
* });
|
1656
|
-
*/
|
1657
|
-
this.options = function( value ) {
|
1658
|
-
angular.extend( globalOptions, value );
|
1659
|
-
};
|
1660
|
-
|
1661
|
-
/**
|
1662
|
-
* This allows you to extend the set of trigger mappings available. E.g.:
|
1663
|
-
*
|
1664
|
-
* $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
|
1665
|
-
*/
|
1666
|
-
this.setTriggers = function setTriggers ( triggers ) {
|
1667
|
-
angular.extend( triggerMap, triggers );
|
1668
|
-
};
|
1669
|
-
|
1670
|
-
/**
|
1671
|
-
* This is a helper function for translating camel-case to snake-case.
|
1672
|
-
*/
|
1673
|
-
function snake_case(name){
|
1674
|
-
var regexp = /[A-Z]/g;
|
1675
|
-
var separator = '-';
|
1676
|
-
return name.replace(regexp, function(letter, pos) {
|
1677
|
-
return (pos ? separator : '') + letter.toLowerCase();
|
1678
|
-
});
|
1679
|
-
}
|
1680
|
-
|
1681
|
-
/**
|
1682
|
-
* Returns the actual instance of the $tooltip service.
|
1683
|
-
* TODO support multiple triggers
|
1684
|
-
*/
|
1685
|
-
this.$get = [ '$window', '$compile', '$timeout', '$parse', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $parse, $document, $position, $interpolate ) {
|
1686
|
-
return function $tooltip ( type, prefix, defaultTriggerShow ) {
|
1687
|
-
var options = angular.extend( {}, defaultOptions, globalOptions );
|
1688
|
-
|
1689
|
-
/**
|
1690
|
-
* Returns an object of show and hide triggers.
|
1691
|
-
*
|
1692
|
-
* If a trigger is supplied,
|
1693
|
-
* it is used to show the tooltip; otherwise, it will use the `trigger`
|
1694
|
-
* option passed to the `$tooltipProvider.options` method; else it will
|
1695
|
-
* default to the trigger supplied to this directive factory.
|
1696
|
-
*
|
1697
|
-
* The hide trigger is based on the show trigger. If the `trigger` option
|
1698
|
-
* was passed to the `$tooltipProvider.options` method, it will use the
|
1699
|
-
* mapped trigger from `triggerMap` or the passed trigger if the map is
|
1700
|
-
* undefined; otherwise, it uses the `triggerMap` value of the show
|
1701
|
-
* trigger; else it will just use the show trigger.
|
1702
|
-
*/
|
1703
|
-
function setTriggers ( trigger ) {
|
1704
|
-
var show, hide;
|
1705
|
-
|
1706
|
-
show = trigger || options.trigger || defaultTriggerShow;
|
1707
|
-
if ( angular.isDefined ( options.trigger ) ) {
|
1708
|
-
hide = triggerMap[options.trigger] || show;
|
1709
|
-
} else {
|
1710
|
-
hide = triggerMap[show] || show;
|
1711
|
-
}
|
1712
|
-
|
1713
|
-
return {
|
1714
|
-
show: show,
|
1715
|
-
hide: hide
|
1716
|
-
};
|
1717
|
-
}
|
1718
|
-
|
1719
|
-
var directiveName = snake_case( type );
|
1720
|
-
var triggers = setTriggers( undefined );
|
1721
|
-
|
1722
|
-
var startSym = $interpolate.startSymbol();
|
1723
|
-
var endSym = $interpolate.endSymbol();
|
1724
|
-
var template =
|
1725
|
-
'<'+ directiveName +'-popup '+
|
1726
|
-
'title="'+startSym+'tt_title'+endSym+'" '+
|
1727
|
-
'content="'+startSym+'tt_content'+endSym+'" '+
|
1728
|
-
'placement="'+startSym+'tt_placement'+endSym+'" '+
|
1729
|
-
'animation="tt_animation()" '+
|
1730
|
-
'is-open="tt_isOpen"'+
|
1731
|
-
'>'+
|
1732
|
-
'</'+ directiveName +'-popup>';
|
1733
|
-
|
1734
|
-
return {
|
1735
|
-
restrict: 'EA',
|
1736
|
-
scope: true,
|
1737
|
-
link: function link ( scope, element, attrs ) {
|
1738
|
-
var tooltip = $compile( template )( scope );
|
1739
|
-
var transitionTimeout;
|
1740
|
-
var popupTimeout;
|
1741
|
-
var $body;
|
1742
|
-
var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;
|
1743
|
-
|
1744
|
-
// By default, the tooltip is not open.
|
1745
|
-
// TODO add ability to start tooltip opened
|
1746
|
-
scope.tt_isOpen = false;
|
1747
|
-
|
1748
|
-
function toggleTooltipBind () {
|
1749
|
-
if ( ! scope.tt_isOpen ) {
|
1750
|
-
showTooltipBind();
|
1751
|
-
} else {
|
1752
|
-
hideTooltipBind();
|
1753
|
-
}
|
1754
|
-
}
|
1755
|
-
|
1756
|
-
// Show the tooltip with delay if specified, otherwise show it immediately
|
1757
|
-
function showTooltipBind() {
|
1758
|
-
if ( scope.tt_popupDelay ) {
|
1759
|
-
popupTimeout = $timeout( show, scope.tt_popupDelay );
|
1760
|
-
} else {
|
1761
|
-
scope.$apply( show );
|
1762
|
-
}
|
1763
|
-
}
|
1764
|
-
|
1765
|
-
function hideTooltipBind () {
|
1766
|
-
scope.$apply(function () {
|
1767
|
-
hide();
|
1768
|
-
});
|
1769
|
-
}
|
1770
|
-
|
1771
|
-
// Show the tooltip popup element.
|
1772
|
-
function show() {
|
1773
|
-
var position,
|
1774
|
-
ttWidth,
|
1775
|
-
ttHeight,
|
1776
|
-
ttPosition;
|
1777
|
-
|
1778
|
-
// Don't show empty tooltips.
|
1779
|
-
if ( ! scope.tt_content ) {
|
1780
|
-
return;
|
1781
|
-
}
|
1782
|
-
|
1783
|
-
// If there is a pending remove transition, we must cancel it, lest the
|
1784
|
-
// tooltip be mysteriously removed.
|
1785
|
-
if ( transitionTimeout ) {
|
1786
|
-
$timeout.cancel( transitionTimeout );
|
1787
|
-
}
|
1788
|
-
|
1789
|
-
// Set the initial positioning.
|
1790
|
-
tooltip.css({ top: 0, left: 0, display: 'block' });
|
1791
|
-
|
1792
|
-
// Now we add it to the DOM because need some info about it. But it's not
|
1793
|
-
// visible yet anyway.
|
1794
|
-
if ( appendToBody ) {
|
1795
|
-
$body = $body || $document.find( 'body' );
|
1796
|
-
$body.append( tooltip );
|
1797
|
-
} else {
|
1798
|
-
element.after( tooltip );
|
1799
|
-
}
|
1800
|
-
|
1801
|
-
// Get the position of the directive element.
|
1802
|
-
position = options.appendToBody ? $position.offset( element ) : $position.position( element );
|
1803
|
-
|
1804
|
-
// Get the height and width of the tooltip so we can center it.
|
1805
|
-
ttWidth = tooltip.prop( 'offsetWidth' );
|
1806
|
-
ttHeight = tooltip.prop( 'offsetHeight' );
|
1807
|
-
|
1808
|
-
// Calculate the tooltip's top and left coordinates to center it with
|
1809
|
-
// this directive.
|
1810
|
-
switch ( scope.tt_placement ) {
|
1811
|
-
case 'mouse':
|
1812
|
-
var mousePos = $position.mouse();
|
1813
|
-
ttPosition = {
|
1814
|
-
top: mousePos.y,
|
1815
|
-
left: mousePos.x
|
1816
|
-
};
|
1817
|
-
break;
|
1818
|
-
case 'right':
|
1819
|
-
ttPosition = {
|
1820
|
-
top: position.top + position.height / 2 - ttHeight / 2,
|
1821
|
-
left: position.left + position.width
|
1822
|
-
};
|
1823
|
-
break;
|
1824
|
-
case 'bottom':
|
1825
|
-
ttPosition = {
|
1826
|
-
top: position.top + position.height,
|
1827
|
-
left: position.left + position.width / 2 - ttWidth / 2
|
1828
|
-
};
|
1829
|
-
break;
|
1830
|
-
case 'left':
|
1831
|
-
ttPosition = {
|
1832
|
-
top: position.top + position.height / 2 - ttHeight / 2,
|
1833
|
-
left: position.left - ttWidth
|
1834
|
-
};
|
1835
|
-
break;
|
1836
|
-
default:
|
1837
|
-
ttPosition = {
|
1838
|
-
top: position.top - ttHeight,
|
1839
|
-
left: position.left + position.width / 2 - ttWidth / 2
|
1840
|
-
};
|
1841
|
-
break;
|
1842
|
-
}
|
1843
|
-
|
1844
|
-
ttPosition.top += 'px';
|
1845
|
-
ttPosition.left += 'px';
|
1846
|
-
|
1847
|
-
// Now set the calculated positioning.
|
1848
|
-
tooltip.css( ttPosition );
|
1849
|
-
|
1850
|
-
// And show the tooltip.
|
1851
|
-
scope.tt_isOpen = true;
|
1852
|
-
}
|
1853
|
-
|
1854
|
-
// Hide the tooltip popup element.
|
1855
|
-
function hide() {
|
1856
|
-
// First things first: we don't show it anymore.
|
1857
|
-
scope.tt_isOpen = false;
|
1858
|
-
|
1859
|
-
//if tooltip is going to be shown after delay, we must cancel this
|
1860
|
-
$timeout.cancel( popupTimeout );
|
1861
|
-
|
1862
|
-
// And now we remove it from the DOM. However, if we have animation, we
|
1863
|
-
// need to wait for it to expire beforehand.
|
1864
|
-
// FIXME: this is a placeholder for a port of the transitions library.
|
1865
|
-
if ( angular.isDefined( scope.tt_animation ) && scope.tt_animation() ) {
|
1866
|
-
transitionTimeout = $timeout( function () { tooltip.remove(); }, 500 );
|
1867
|
-
} else {
|
1868
|
-
tooltip.remove();
|
1869
|
-
}
|
1870
|
-
}
|
1871
|
-
|
1872
|
-
/**
|
1873
|
-
* Observe the relevant attributes.
|
1874
|
-
*/
|
1875
|
-
attrs.$observe( type, function ( val ) {
|
1876
|
-
scope.tt_content = val;
|
1877
|
-
});
|
1878
|
-
|
1879
|
-
attrs.$observe( prefix+'Title', function ( val ) {
|
1880
|
-
scope.tt_title = val;
|
1881
|
-
});
|
1882
|
-
|
1883
|
-
attrs.$observe( prefix+'Placement', function ( val ) {
|
1884
|
-
scope.tt_placement = angular.isDefined( val ) ? val : options.placement;
|
1885
|
-
});
|
1886
|
-
|
1887
|
-
attrs.$observe( prefix+'Animation', function ( val ) {
|
1888
|
-
scope.tt_animation = angular.isDefined( val ) ? $parse( val ) : function(){ return options.animation; };
|
1889
|
-
});
|
1890
|
-
|
1891
|
-
attrs.$observe( prefix+'PopupDelay', function ( val ) {
|
1892
|
-
var delay = parseInt( val, 10 );
|
1893
|
-
scope.tt_popupDelay = ! isNaN(delay) ? delay : options.popupDelay;
|
1894
|
-
});
|
1895
|
-
|
1896
|
-
attrs.$observe( prefix+'Trigger', function ( val ) {
|
1897
|
-
element.unbind( triggers.show );
|
1898
|
-
element.unbind( triggers.hide );
|
1899
|
-
|
1900
|
-
triggers = setTriggers( val );
|
1901
|
-
|
1902
|
-
if ( triggers.show === triggers.hide ) {
|
1903
|
-
element.bind( triggers.show, toggleTooltipBind );
|
1904
|
-
} else {
|
1905
|
-
element.bind( triggers.show, showTooltipBind );
|
1906
|
-
element.bind( triggers.hide, hideTooltipBind );
|
1907
|
-
}
|
1908
|
-
});
|
1909
|
-
|
1910
|
-
attrs.$observe( prefix+'AppendToBody', function ( val ) {
|
1911
|
-
appendToBody = angular.isDefined( val ) ? $parse( val )( scope ) : appendToBody;
|
1912
|
-
});
|
1913
|
-
|
1914
|
-
// if a tooltip is attached to <body> we need to remove it on
|
1915
|
-
// location change as its parent scope will probably not be destroyed
|
1916
|
-
// by the change.
|
1917
|
-
if ( appendToBody ) {
|
1918
|
-
scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () {
|
1919
|
-
if ( scope.tt_isOpen ) {
|
1920
|
-
hide();
|
1921
|
-
}
|
1922
|
-
});
|
1923
|
-
}
|
1924
|
-
|
1925
|
-
// Make sure tooltip is destroyed and removed.
|
1926
|
-
scope.$on('$destroy', function onDestroyTooltip() {
|
1927
|
-
if ( scope.tt_isOpen ) {
|
1928
|
-
hide();
|
1929
|
-
} else {
|
1930
|
-
tooltip.remove();
|
1931
|
-
}
|
1932
|
-
});
|
1933
|
-
}
|
1934
|
-
};
|
1935
|
-
};
|
1936
|
-
}];
|
1937
|
-
})
|
1938
|
-
|
1939
|
-
.directive( 'tooltipPopup', function () {
|
1940
|
-
return {
|
1941
|
-
restrict: 'E',
|
1942
|
-
replace: true,
|
1943
|
-
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
|
1944
|
-
templateUrl: 'template/tooltip/tooltip-popup.html'
|
1945
|
-
};
|
1946
|
-
})
|
1947
|
-
|
1948
|
-
.directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {
|
1949
|
-
return $tooltip( 'tooltip', 'tooltip', 'mouseenter' );
|
1950
|
-
}])
|
1951
|
-
|
1952
|
-
.directive( 'tooltipHtmlUnsafePopup', function () {
|
1953
|
-
return {
|
1954
|
-
restrict: 'E',
|
1955
|
-
replace: true,
|
1956
|
-
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
|
1957
|
-
templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
|
1958
|
-
};
|
1959
|
-
})
|
1960
|
-
|
1961
|
-
.directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {
|
1962
|
-
return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );
|
1963
|
-
}]);
|
1964
|
-
|
1965
|
-
/**
|
1966
|
-
* The following features are still outstanding: popup delay, animation as a
|
1967
|
-
* function, placement as a function, inside, support for more triggers than
|
1968
|
-
* just mouse enter/leave, html popovers, and selector delegatation.
|
1969
|
-
*/
|
1970
|
-
angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
|
1971
|
-
.directive( 'popoverPopup', function () {
|
1972
|
-
return {
|
1973
|
-
restrict: 'EA',
|
1974
|
-
replace: true,
|
1975
|
-
scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' },
|
1976
|
-
templateUrl: 'template/popover/popover.html'
|
1977
|
-
};
|
1978
|
-
})
|
1979
|
-
.directive( 'popover', [ '$compile', '$timeout', '$parse', '$window', '$tooltip', function ( $compile, $timeout, $parse, $window, $tooltip ) {
|
1980
|
-
return $tooltip( 'popover', 'popover', 'click' );
|
1981
|
-
}]);
|
1982
|
-
|
1983
|
-
|
1984
|
-
angular.module('ui.bootstrap.progressbar', ['ui.bootstrap.transition'])
|
1985
|
-
|
1986
|
-
.constant('progressConfig', {
|
1987
|
-
animate: true,
|
1988
|
-
autoType: false,
|
1989
|
-
stackedTypes: ['success', 'info', 'warning', 'danger']
|
1990
|
-
})
|
1991
|
-
|
1992
|
-
.controller('ProgressBarController', ['$scope', '$attrs', 'progressConfig', function($scope, $attrs, progressConfig) {
|
1993
|
-
|
1994
|
-
// Whether bar transitions should be animated
|
1995
|
-
var animate = angular.isDefined($attrs.animate) ? $scope.$eval($attrs.animate) : progressConfig.animate;
|
1996
|
-
var autoType = angular.isDefined($attrs.autoType) ? $scope.$eval($attrs.autoType) : progressConfig.autoType;
|
1997
|
-
var stackedTypes = angular.isDefined($attrs.stackedTypes) ? $scope.$eval('[' + $attrs.stackedTypes + ']') : progressConfig.stackedTypes;
|
1998
|
-
|
1999
|
-
// Create bar object
|
2000
|
-
this.makeBar = function(newBar, oldBar, index) {
|
2001
|
-
var newValue = (angular.isObject(newBar)) ? newBar.value : (newBar || 0);
|
2002
|
-
var oldValue = (angular.isObject(oldBar)) ? oldBar.value : (oldBar || 0);
|
2003
|
-
var type = (angular.isObject(newBar) && angular.isDefined(newBar.type)) ? newBar.type : (autoType) ? getStackedType(index || 0) : null;
|
2004
|
-
|
2005
|
-
return {
|
2006
|
-
from: oldValue,
|
2007
|
-
to: newValue,
|
2008
|
-
type: type,
|
2009
|
-
animate: animate
|
2010
|
-
};
|
2011
|
-
};
|
2012
|
-
|
2013
|
-
function getStackedType(index) {
|
2014
|
-
return stackedTypes[index];
|
2015
|
-
}
|
2016
|
-
|
2017
|
-
this.addBar = function(bar) {
|
2018
|
-
$scope.bars.push(bar);
|
2019
|
-
$scope.totalPercent += bar.to;
|
2020
|
-
};
|
2021
|
-
|
2022
|
-
this.clearBars = function() {
|
2023
|
-
$scope.bars = [];
|
2024
|
-
$scope.totalPercent = 0;
|
2025
|
-
};
|
2026
|
-
this.clearBars();
|
2027
|
-
}])
|
2028
|
-
|
2029
|
-
.directive('progress', function() {
|
2030
|
-
return {
|
2031
|
-
restrict: 'EA',
|
2032
|
-
replace: true,
|
2033
|
-
controller: 'ProgressBarController',
|
2034
|
-
scope: {
|
2035
|
-
value: '=percent',
|
2036
|
-
onFull: '&',
|
2037
|
-
onEmpty: '&'
|
2038
|
-
},
|
2039
|
-
templateUrl: 'template/progressbar/progress.html',
|
2040
|
-
link: function(scope, element, attrs, controller) {
|
2041
|
-
scope.$watch('value', function(newValue, oldValue) {
|
2042
|
-
controller.clearBars();
|
2043
|
-
|
2044
|
-
if (angular.isArray(newValue)) {
|
2045
|
-
// Stacked progress bar
|
2046
|
-
for (var i=0, n=newValue.length; i < n; i++) {
|
2047
|
-
controller.addBar(controller.makeBar(newValue[i], oldValue[i], i));
|
2048
|
-
}
|
2049
|
-
} else {
|
2050
|
-
// Simple bar
|
2051
|
-
controller.addBar(controller.makeBar(newValue, oldValue));
|
2052
|
-
}
|
2053
|
-
}, true);
|
2054
|
-
|
2055
|
-
// Total percent listeners
|
2056
|
-
scope.$watch('totalPercent', function(value) {
|
2057
|
-
if (value >= 100) {
|
2058
|
-
scope.onFull();
|
2059
|
-
} else if (value <= 0) {
|
2060
|
-
scope.onEmpty();
|
2061
|
-
}
|
2062
|
-
}, true);
|
2063
|
-
}
|
2064
|
-
};
|
2065
|
-
})
|
2066
|
-
|
2067
|
-
.directive('progressbar', ['$transition', function($transition) {
|
2068
|
-
return {
|
2069
|
-
restrict: 'EA',
|
2070
|
-
replace: true,
|
2071
|
-
scope: {
|
2072
|
-
width: '=',
|
2073
|
-
old: '=',
|
2074
|
-
type: '=',
|
2075
|
-
animate: '='
|
2076
|
-
},
|
2077
|
-
templateUrl: 'template/progressbar/bar.html',
|
2078
|
-
link: function(scope, element) {
|
2079
|
-
scope.$watch('width', function(value) {
|
2080
|
-
if (scope.animate) {
|
2081
|
-
element.css('width', scope.old + '%');
|
2082
|
-
$transition(element, {width: value + '%'});
|
2083
|
-
} else {
|
2084
|
-
element.css('width', value + '%');
|
2085
|
-
}
|
2086
|
-
});
|
2087
|
-
}
|
2088
|
-
};
|
2089
|
-
}]);
|
2090
|
-
angular.module('ui.bootstrap.rating', [])
|
2091
|
-
|
2092
|
-
.constant('ratingConfig', {
|
2093
|
-
max: 5
|
2094
|
-
})
|
2095
|
-
|
2096
|
-
.directive('rating', ['ratingConfig', '$parse', function(ratingConfig, $parse) {
|
2097
|
-
return {
|
2098
|
-
restrict: 'EA',
|
2099
|
-
scope: {
|
2100
|
-
value: '='
|
2101
|
-
},
|
2102
|
-
templateUrl: 'template/rating/rating.html',
|
2103
|
-
replace: true,
|
2104
|
-
link: function(scope, element, attrs) {
|
2105
|
-
|
2106
|
-
var maxRange = angular.isDefined(attrs.max) ? scope.$eval(attrs.max) : ratingConfig.max;
|
2107
|
-
|
2108
|
-
scope.range = [];
|
2109
|
-
for (var i = 1; i <= maxRange; i++) {
|
2110
|
-
scope.range.push(i);
|
2111
|
-
}
|
2112
|
-
|
2113
|
-
scope.rate = function(value) {
|
2114
|
-
if ( ! scope.readonly ) {
|
2115
|
-
scope.value = value;
|
2116
|
-
}
|
2117
|
-
};
|
2118
|
-
|
2119
|
-
scope.enter = function(value) {
|
2120
|
-
if ( ! scope.readonly ) {
|
2121
|
-
scope.val = value;
|
2122
|
-
}
|
2123
|
-
};
|
2124
|
-
|
2125
|
-
scope.reset = function() {
|
2126
|
-
scope.val = angular.copy(scope.value);
|
2127
|
-
};
|
2128
|
-
scope.reset();
|
2129
|
-
|
2130
|
-
scope.$watch('value', function(value) {
|
2131
|
-
scope.val = value;
|
2132
|
-
});
|
2133
|
-
|
2134
|
-
scope.readonly = false;
|
2135
|
-
if (attrs.readonly) {
|
2136
|
-
scope.$parent.$watch($parse(attrs.readonly), function(value) {
|
2137
|
-
scope.readonly = !!value;
|
2138
|
-
});
|
2139
|
-
}
|
2140
|
-
}
|
2141
|
-
};
|
2142
|
-
}]);
|
2143
|
-
|
2144
|
-
/**
|
2145
|
-
* @ngdoc overview
|
2146
|
-
* @name ui.bootstrap.tabs
|
2147
|
-
*
|
2148
|
-
* @description
|
2149
|
-
* AngularJS version of the tabs directive.
|
2150
|
-
*/
|
2151
|
-
|
2152
|
-
angular.module('ui.bootstrap.tabs', [])
|
2153
|
-
|
2154
|
-
.directive('tabs', function() {
|
2155
|
-
return function() {
|
2156
|
-
throw new Error("The `tabs` directive is deprecated, please migrate to `tabset`. Instructions can be found at http://github.com/angular-ui/bootstrap/tree/master/CHANGELOG.md");
|
2157
|
-
};
|
2158
|
-
})
|
2159
|
-
|
2160
|
-
.controller('TabsetController', ['$scope', '$element',
|
2161
|
-
function TabsetCtrl($scope, $element) {
|
2162
|
-
var ctrl = this,
|
2163
|
-
tabs = ctrl.tabs = $scope.tabs = [];
|
2164
|
-
|
2165
|
-
ctrl.select = function(tab) {
|
2166
|
-
angular.forEach(tabs, function(tab) {
|
2167
|
-
tab.active = false;
|
2168
|
-
});
|
2169
|
-
tab.active = true;
|
2170
|
-
};
|
2171
|
-
|
2172
|
-
ctrl.addTab = function addTab(tab) {
|
2173
|
-
tabs.push(tab);
|
2174
|
-
if (tabs.length == 1) {
|
2175
|
-
ctrl.select(tab);
|
2176
|
-
}
|
2177
|
-
};
|
2178
|
-
|
2179
|
-
ctrl.removeTab = function removeTab(tab) {
|
2180
|
-
var index = tabs.indexOf(tab);
|
2181
|
-
//Select a new tab if the tab to be removed is selected
|
2182
|
-
if (tab.active && tabs.length > 1) {
|
2183
|
-
//If this is the last tab, select the previous tab. else, the next tab.
|
2184
|
-
var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1;
|
2185
|
-
ctrl.select(tabs[newActiveIndex]);
|
2186
|
-
}
|
2187
|
-
tabs.splice(index, 1);
|
2188
|
-
};
|
2189
|
-
}])
|
2190
|
-
|
2191
|
-
/**
|
2192
|
-
* @ngdoc directive
|
2193
|
-
* @name ui.bootstrap.tabs.directive:tabset
|
2194
|
-
* @restrict EA
|
2195
|
-
*
|
2196
|
-
* @description
|
2197
|
-
* Tabset is the outer container for the tabs directive
|
2198
|
-
*
|
2199
|
-
* @param {boolean=} vertical Whether or not to use vertical styling for the tabs.
|
2200
|
-
*
|
2201
|
-
* @example
|
2202
|
-
<example module="ui.bootstrap">
|
2203
|
-
<file name="index.html">
|
2204
|
-
<tabset>
|
2205
|
-
<tab heading="Vertical Tab 1"><b>First</b> Content!</tab>
|
2206
|
-
<tab heading="Vertical Tab 2"><i>Second</i> Content!</tab>
|
2207
|
-
</tabset>
|
2208
|
-
<hr />
|
2209
|
-
<tabset vertical="true">
|
2210
|
-
<tab heading="Vertical Tab 1"><b>First</b> Vertical Content!</tab>
|
2211
|
-
<tab heading="Vertical Tab 2"><i>Second</i> Vertical Content!</tab>
|
2212
|
-
</tabset>
|
2213
|
-
</file>
|
2214
|
-
</example>
|
2215
|
-
*/
|
2216
|
-
.directive('tabset', function() {
|
2217
|
-
return {
|
2218
|
-
restrict: 'EA',
|
2219
|
-
transclude: true,
|
2220
|
-
scope: {},
|
2221
|
-
controller: 'TabsetController',
|
2222
|
-
templateUrl: 'template/tabs/tabset.html',
|
2223
|
-
link: function(scope, element, attrs) {
|
2224
|
-
scope.vertical = angular.isDefined(attrs.vertical) ? scope.$eval(attrs.vertical) : false;
|
2225
|
-
scope.type = angular.isDefined(attrs.type) ? scope.$parent.$eval(attrs.type) : 'tabs';
|
2226
|
-
}
|
2227
|
-
};
|
2228
|
-
})
|
2229
|
-
|
2230
|
-
/**
|
2231
|
-
* @ngdoc directive
|
2232
|
-
* @name ui.bootstrap.tabs.directive:tab
|
2233
|
-
* @restrict EA
|
2234
|
-
*
|
2235
|
-
* @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}.
|
2236
|
-
* @param {string=} select An expression to evaluate when the tab is selected.
|
2237
|
-
* @param {boolean=} active A binding, telling whether or not this tab is selected.
|
2238
|
-
* @param {boolean=} disabled A binding, telling whether or not this tab is disabled.
|
2239
|
-
*
|
2240
|
-
* @description
|
2241
|
-
* Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}.
|
2242
|
-
*
|
2243
|
-
* @example
|
2244
|
-
<example module="ui.bootstrap">
|
2245
|
-
<file name="index.html">
|
2246
|
-
<div ng-controller="TabsDemoCtrl">
|
2247
|
-
<button class="btn btn-small" ng-click="items[0].active = true">
|
2248
|
-
Select item 1, using active binding
|
2249
|
-
</button>
|
2250
|
-
<button class="btn btn-small" ng-click="items[1].disabled = !items[1].disabled">
|
2251
|
-
Enable/disable item 2, using disabled binding
|
2252
|
-
</button>
|
2253
|
-
<br />
|
2254
|
-
<tabset>
|
2255
|
-
<tab heading="Tab 1">First Tab</tab>
|
2256
|
-
<tab select="alertMe()">
|
2257
|
-
<tab-heading><i class="icon-bell"></i> Alert me!</tab-heading>
|
2258
|
-
Second Tab, with alert callback and html heading!
|
2259
|
-
</tab>
|
2260
|
-
<tab ng-repeat="item in items"
|
2261
|
-
heading="{{item.title}}"
|
2262
|
-
disabled="item.disabled"
|
2263
|
-
active="item.active">
|
2264
|
-
{{item.content}}
|
2265
|
-
</tab>
|
2266
|
-
</tabset>
|
2267
|
-
</div>
|
2268
|
-
</file>
|
2269
|
-
<file name="script.js">
|
2270
|
-
function TabsDemoCtrl($scope) {
|
2271
|
-
$scope.items = [
|
2272
|
-
{ title:"Dynamic Title 1", content:"Dynamic Item 0" },
|
2273
|
-
{ title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true }
|
2274
|
-
];
|
2275
|
-
|
2276
|
-
$scope.alertMe = function() {
|
2277
|
-
setTimeout(function() {
|
2278
|
-
alert("You've selected the alert tab!");
|
2279
|
-
});
|
2280
|
-
};
|
2281
|
-
};
|
2282
|
-
</file>
|
2283
|
-
</example>
|
2284
|
-
*/
|
2285
|
-
|
2286
|
-
/**
|
2287
|
-
* @ngdoc directive
|
2288
|
-
* @name ui.bootstrap.tabs.directive:tabHeading
|
2289
|
-
* @restrict EA
|
2290
|
-
*
|
2291
|
-
* @description
|
2292
|
-
* Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element.
|
2293
|
-
*
|
2294
|
-
* @example
|
2295
|
-
<example module="ui.bootstrap">
|
2296
|
-
<file name="index.html">
|
2297
|
-
<tabset>
|
2298
|
-
<tab>
|
2299
|
-
<tab-heading><b>HTML</b> in my titles?!</tab-heading>
|
2300
|
-
And some content, too!
|
2301
|
-
</tab>
|
2302
|
-
<tab>
|
2303
|
-
<tab-heading><i class="icon-heart"></i> Icon heading?!?</tab-heading>
|
2304
|
-
That's right.
|
2305
|
-
</tab>
|
2306
|
-
</tabset>
|
2307
|
-
</file>
|
2308
|
-
</example>
|
2309
|
-
*/
|
2310
|
-
.directive('tab', ['$parse', '$http', '$templateCache', '$compile',
|
2311
|
-
function($parse, $http, $templateCache, $compile) {
|
2312
|
-
return {
|
2313
|
-
require: '^tabset',
|
2314
|
-
restrict: 'EA',
|
2315
|
-
replace: true,
|
2316
|
-
templateUrl: 'template/tabs/tab.html',
|
2317
|
-
transclude: true,
|
2318
|
-
scope: {
|
2319
|
-
heading: '@',
|
2320
|
-
onSelect: '&select' //This callback is called in contentHeadingTransclude
|
2321
|
-
//once it inserts the tab's content into the dom
|
2322
|
-
},
|
2323
|
-
controller: function() {
|
2324
|
-
//Empty controller so other directives can require being 'under' a tab
|
2325
|
-
},
|
2326
|
-
compile: function(elm, attrs, transclude) {
|
2327
|
-
return function postLink(scope, elm, attrs, tabsetCtrl) {
|
2328
|
-
var getActive, setActive;
|
2329
|
-
scope.active = false; // default value
|
2330
|
-
if (attrs.active) {
|
2331
|
-
getActive = $parse(attrs.active);
|
2332
|
-
setActive = getActive.assign;
|
2333
|
-
scope.$parent.$watch(getActive, function updateActive(value) {
|
2334
|
-
if ( !!value && scope.disabled ) {
|
2335
|
-
setActive(scope.$parent, false); // Prevent active assignment
|
2336
|
-
} else {
|
2337
|
-
scope.active = !!value;
|
2338
|
-
}
|
2339
|
-
});
|
2340
|
-
} else {
|
2341
|
-
setActive = getActive = angular.noop;
|
2342
|
-
}
|
2343
|
-
|
2344
|
-
scope.$watch('active', function(active) {
|
2345
|
-
setActive(scope.$parent, active);
|
2346
|
-
if (active) {
|
2347
|
-
tabsetCtrl.select(scope);
|
2348
|
-
scope.onSelect();
|
2349
|
-
}
|
2350
|
-
});
|
2351
|
-
|
2352
|
-
scope.disabled = false;
|
2353
|
-
if ( attrs.disabled ) {
|
2354
|
-
scope.$parent.$watch($parse(attrs.disabled), function(value) {
|
2355
|
-
scope.disabled = !! value;
|
2356
|
-
});
|
2357
|
-
}
|
2358
|
-
|
2359
|
-
scope.select = function() {
|
2360
|
-
if ( ! scope.disabled ) {
|
2361
|
-
scope.active = true;
|
2362
|
-
}
|
2363
|
-
};
|
2364
|
-
|
2365
|
-
tabsetCtrl.addTab(scope);
|
2366
|
-
scope.$on('$destroy', function() {
|
2367
|
-
tabsetCtrl.removeTab(scope);
|
2368
|
-
});
|
2369
|
-
//If the tabset sets this tab to active, set the parent scope's active
|
2370
|
-
//binding too. We do this so the watch for the parent's initial active
|
2371
|
-
//value won't overwrite what is initially set by the tabset
|
2372
|
-
if (scope.active) {
|
2373
|
-
setActive(scope.$parent, true);
|
2374
|
-
}
|
2375
|
-
|
2376
|
-
//Transclude the collection of sibling elements. Use forEach to find
|
2377
|
-
//the heading if it exists. We don't use a directive for tab-heading
|
2378
|
-
//because it is problematic. Discussion @ http://git.io/MSNPwQ
|
2379
|
-
transclude(scope.$parent, function(clone) {
|
2380
|
-
//Look at every element in the clone collection. If it's tab-heading,
|
2381
|
-
//mark it as that. If it's not tab-heading, mark it as tab contents
|
2382
|
-
var contents = [], heading;
|
2383
|
-
angular.forEach(clone, function(el) {
|
2384
|
-
//See if it's a tab-heading attr or element directive
|
2385
|
-
//First make sure it's a normal element, one that has a tagName
|
2386
|
-
if (el.tagName &&
|
2387
|
-
(el.hasAttribute("tab-heading") ||
|
2388
|
-
el.hasAttribute("data-tab-heading") ||
|
2389
|
-
el.tagName.toLowerCase() == "tab-heading" ||
|
2390
|
-
el.tagName.toLowerCase() == "data-tab-heading"
|
2391
|
-
)) {
|
2392
|
-
heading = el;
|
2393
|
-
} else {
|
2394
|
-
contents.push(el);
|
2395
|
-
}
|
2396
|
-
});
|
2397
|
-
//Share what we found on the scope, so our tabHeadingTransclude and
|
2398
|
-
//tabContentTransclude directives can find out what the heading and
|
2399
|
-
//contents are.
|
2400
|
-
if (heading) {
|
2401
|
-
scope.headingElement = angular.element(heading);
|
2402
|
-
}
|
2403
|
-
scope.contentElement = angular.element(contents);
|
2404
|
-
});
|
2405
|
-
};
|
2406
|
-
}
|
2407
|
-
};
|
2408
|
-
}])
|
2409
|
-
|
2410
|
-
.directive('tabHeadingTransclude', [function() {
|
2411
|
-
return {
|
2412
|
-
restrict: 'A',
|
2413
|
-
require: '^tab',
|
2414
|
-
link: function(scope, elm, attrs, tabCtrl) {
|
2415
|
-
scope.$watch('headingElement', function updateHeadingElement(heading) {
|
2416
|
-
if (heading) {
|
2417
|
-
elm.html('');
|
2418
|
-
elm.append(heading);
|
2419
|
-
}
|
2420
|
-
});
|
2421
|
-
}
|
2422
|
-
};
|
2423
|
-
}])
|
2424
|
-
|
2425
|
-
.directive('tabContentTransclude', ['$parse', function($parse) {
|
2426
|
-
return {
|
2427
|
-
restrict: 'A',
|
2428
|
-
require: '^tabset',
|
2429
|
-
link: function(scope, elm, attrs, tabsetCtrl) {
|
2430
|
-
scope.$watch($parse(attrs.tabContentTransclude), function(tab) {
|
2431
|
-
elm.html('');
|
2432
|
-
if (tab) {
|
2433
|
-
elm.append(tab.contentElement);
|
2434
|
-
}
|
2435
|
-
});
|
2436
|
-
}
|
2437
|
-
};
|
2438
|
-
}])
|
2439
|
-
|
2440
|
-
;
|
2441
|
-
|
2442
|
-
|
2443
|
-
angular.module('ui.bootstrap.timepicker', [])
|
2444
|
-
|
2445
|
-
.filter('pad', function() {
|
2446
|
-
return function(input) {
|
2447
|
-
if ( angular.isDefined(input) && input.toString().length < 2 ) {
|
2448
|
-
input = '0' + input;
|
2449
|
-
}
|
2450
|
-
return input;
|
2451
|
-
};
|
2452
|
-
})
|
2453
|
-
|
2454
|
-
.constant('timepickerConfig', {
|
2455
|
-
hourStep: 1,
|
2456
|
-
minuteStep: 1,
|
2457
|
-
showMeridian: true,
|
2458
|
-
meridians: ['AM', 'PM'],
|
2459
|
-
readonlyInput: false,
|
2460
|
-
mousewheel: true
|
2461
|
-
})
|
2462
|
-
|
2463
|
-
.directive('timepicker', ['padFilter', '$parse', 'timepickerConfig', function (padFilter, $parse, timepickerConfig) {
|
2464
|
-
return {
|
2465
|
-
restrict: 'EA',
|
2466
|
-
require:'ngModel',
|
2467
|
-
replace: true,
|
2468
|
-
templateUrl: 'template/timepicker/timepicker.html',
|
2469
|
-
scope: {
|
2470
|
-
model: '=ngModel'
|
2471
|
-
},
|
2472
|
-
link: function(scope, element, attrs, ngModelCtrl) {
|
2473
|
-
var selected = new Date(), meridians = timepickerConfig.meridians;
|
2474
|
-
|
2475
|
-
var hourStep = timepickerConfig.hourStep;
|
2476
|
-
if (attrs.hourStep) {
|
2477
|
-
scope.$parent.$watch($parse(attrs.hourStep), function(value) {
|
2478
|
-
hourStep = parseInt(value, 10);
|
2479
|
-
});
|
2480
|
-
}
|
2481
|
-
|
2482
|
-
var minuteStep = timepickerConfig.minuteStep;
|
2483
|
-
if (attrs.minuteStep) {
|
2484
|
-
scope.$parent.$watch($parse(attrs.minuteStep), function(value) {
|
2485
|
-
minuteStep = parseInt(value, 10);
|
2486
|
-
});
|
2487
|
-
}
|
2488
|
-
|
2489
|
-
// 12H / 24H mode
|
2490
|
-
scope.showMeridian = timepickerConfig.showMeridian;
|
2491
|
-
if (attrs.showMeridian) {
|
2492
|
-
scope.$parent.$watch($parse(attrs.showMeridian), function(value) {
|
2493
|
-
scope.showMeridian = !! value;
|
2494
|
-
|
2495
|
-
if ( ! scope.model ) {
|
2496
|
-
// Reset
|
2497
|
-
var dt = new Date( selected );
|
2498
|
-
var hours = getScopeHours();
|
2499
|
-
if (angular.isDefined( hours )) {
|
2500
|
-
dt.setHours( hours );
|
2501
|
-
}
|
2502
|
-
scope.model = new Date( dt );
|
2503
|
-
} else {
|
2504
|
-
refreshTemplate();
|
2505
|
-
}
|
2506
|
-
});
|
2507
|
-
}
|
2508
|
-
|
2509
|
-
// Get scope.hours in 24H mode if valid
|
2510
|
-
function getScopeHours ( ) {
|
2511
|
-
var hours = parseInt( scope.hours, 10 );
|
2512
|
-
var valid = ( scope.showMeridian ) ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);
|
2513
|
-
if ( !valid ) {
|
2514
|
-
return;
|
2515
|
-
}
|
2516
|
-
|
2517
|
-
if ( scope.showMeridian ) {
|
2518
|
-
if ( hours === 12 ) {
|
2519
|
-
hours = 0;
|
2520
|
-
}
|
2521
|
-
if ( scope.meridian === meridians[1] ) {
|
2522
|
-
hours = hours + 12;
|
2523
|
-
}
|
2524
|
-
}
|
2525
|
-
return hours;
|
2526
|
-
}
|
2527
|
-
|
2528
|
-
// Input elements
|
2529
|
-
var inputs = element.find('input');
|
2530
|
-
var hoursInputEl = inputs.eq(0), minutesInputEl = inputs.eq(1);
|
2531
|
-
|
2532
|
-
// Respond on mousewheel spin
|
2533
|
-
var mousewheel = (angular.isDefined(attrs.mousewheel)) ? scope.$eval(attrs.mousewheel) : timepickerConfig.mousewheel;
|
2534
|
-
if ( mousewheel ) {
|
2535
|
-
|
2536
|
-
var isScrollingUp = function(e) {
|
2537
|
-
if (e.originalEvent) {
|
2538
|
-
e = e.originalEvent;
|
2539
|
-
}
|
2540
|
-
return (e.detail || e.wheelDelta > 0);
|
2541
|
-
};
|
2542
|
-
|
2543
|
-
hoursInputEl.bind('mousewheel', function(e) {
|
2544
|
-
scope.$apply( (isScrollingUp(e)) ? scope.incrementHours() : scope.decrementHours() );
|
2545
|
-
e.preventDefault();
|
2546
|
-
});
|
2547
|
-
|
2548
|
-
minutesInputEl.bind('mousewheel', function(e) {
|
2549
|
-
scope.$apply( (isScrollingUp(e)) ? scope.incrementMinutes() : scope.decrementMinutes() );
|
2550
|
-
e.preventDefault();
|
2551
|
-
});
|
2552
|
-
}
|
2553
|
-
|
2554
|
-
var keyboardChange = false;
|
2555
|
-
scope.readonlyInput = (angular.isDefined(attrs.readonlyInput)) ? scope.$eval(attrs.readonlyInput) : timepickerConfig.readonlyInput;
|
2556
|
-
if ( ! scope.readonlyInput ) {
|
2557
|
-
scope.updateHours = function() {
|
2558
|
-
var hours = getScopeHours();
|
2559
|
-
|
2560
|
-
if ( angular.isDefined(hours) ) {
|
2561
|
-
keyboardChange = 'h';
|
2562
|
-
if ( scope.model === null ) {
|
2563
|
-
scope.model = new Date( selected );
|
2564
|
-
}
|
2565
|
-
scope.model.setHours( hours );
|
2566
|
-
} else {
|
2567
|
-
scope.model = null;
|
2568
|
-
scope.validHours = false;
|
2569
|
-
}
|
2570
|
-
};
|
2571
|
-
|
2572
|
-
hoursInputEl.bind('blur', function(e) {
|
2573
|
-
if ( scope.validHours && scope.hours < 10) {
|
2574
|
-
scope.$apply( function() {
|
2575
|
-
scope.hours = padFilter( scope.hours );
|
2576
|
-
});
|
2577
|
-
}
|
2578
|
-
});
|
2579
|
-
|
2580
|
-
scope.updateMinutes = function() {
|
2581
|
-
var minutes = parseInt(scope.minutes, 10);
|
2582
|
-
if ( minutes >= 0 && minutes < 60 ) {
|
2583
|
-
keyboardChange = 'm';
|
2584
|
-
if ( scope.model === null ) {
|
2585
|
-
scope.model = new Date( selected );
|
2586
|
-
}
|
2587
|
-
scope.model.setMinutes( minutes );
|
2588
|
-
} else {
|
2589
|
-
scope.model = null;
|
2590
|
-
scope.validMinutes = false;
|
2591
|
-
}
|
2592
|
-
};
|
2593
|
-
|
2594
|
-
minutesInputEl.bind('blur', function(e) {
|
2595
|
-
if ( scope.validMinutes && scope.minutes < 10 ) {
|
2596
|
-
scope.$apply( function() {
|
2597
|
-
scope.minutes = padFilter( scope.minutes );
|
2598
|
-
});
|
2599
|
-
}
|
2600
|
-
});
|
2601
|
-
} else {
|
2602
|
-
scope.updateHours = angular.noop;
|
2603
|
-
scope.updateMinutes = angular.noop;
|
2604
|
-
}
|
2605
|
-
|
2606
|
-
scope.$watch( function getModelTimestamp() {
|
2607
|
-
return +scope.model;
|
2608
|
-
}, function( timestamp ) {
|
2609
|
-
if ( !isNaN( timestamp ) && timestamp > 0 ) {
|
2610
|
-
selected = new Date( timestamp );
|
2611
|
-
refreshTemplate();
|
2612
|
-
}
|
2613
|
-
});
|
2614
|
-
|
2615
|
-
function refreshTemplate() {
|
2616
|
-
var hours = selected.getHours();
|
2617
|
-
if ( scope.showMeridian ) {
|
2618
|
-
// Convert 24 to 12 hour system
|
2619
|
-
hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12;
|
2620
|
-
}
|
2621
|
-
scope.hours = ( keyboardChange === 'h' ) ? hours : padFilter(hours);
|
2622
|
-
scope.validHours = true;
|
2623
|
-
|
2624
|
-
var minutes = selected.getMinutes();
|
2625
|
-
scope.minutes = ( keyboardChange === 'm' ) ? minutes : padFilter(minutes);
|
2626
|
-
scope.validMinutes = true;
|
2627
|
-
|
2628
|
-
scope.meridian = ( scope.showMeridian ) ? (( selected.getHours() < 12 ) ? meridians[0] : meridians[1]) : '';
|
2629
|
-
|
2630
|
-
keyboardChange = false;
|
2631
|
-
}
|
2632
|
-
|
2633
|
-
function addMinutes( minutes ) {
|
2634
|
-
var dt = new Date( selected.getTime() + minutes * 60000 );
|
2635
|
-
if ( dt.getDate() !== selected.getDate()) {
|
2636
|
-
dt.setDate( dt.getDate() - 1 );
|
2637
|
-
}
|
2638
|
-
selected.setTime( dt.getTime() );
|
2639
|
-
scope.model = new Date( selected );
|
2640
|
-
}
|
2641
|
-
|
2642
|
-
scope.incrementHours = function() {
|
2643
|
-
addMinutes( hourStep * 60 );
|
2644
|
-
};
|
2645
|
-
scope.decrementHours = function() {
|
2646
|
-
addMinutes( - hourStep * 60 );
|
2647
|
-
};
|
2648
|
-
scope.incrementMinutes = function() {
|
2649
|
-
addMinutes( minuteStep );
|
2650
|
-
};
|
2651
|
-
scope.decrementMinutes = function() {
|
2652
|
-
addMinutes( - minuteStep );
|
2653
|
-
};
|
2654
|
-
scope.toggleMeridian = function() {
|
2655
|
-
addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );
|
2656
|
-
};
|
2657
|
-
}
|
2658
|
-
};
|
2659
|
-
}]);
|
2660
|
-
angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
|
2661
|
-
|
2662
|
-
/**
|
2663
|
-
* A helper service that can parse typeahead's syntax (string provided by users)
|
2664
|
-
* Extracted to a separate service for ease of unit testing
|
2665
|
-
*/
|
2666
|
-
.factory('typeaheadParser', ['$parse', function ($parse) {
|
2667
|
-
|
2668
|
-
// 00000111000000000000022200000000000000003333333333333330000000000044000
|
2669
|
-
var TYPEAHEAD_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+(.*)$/;
|
2670
|
-
|
2671
|
-
return {
|
2672
|
-
parse:function (input) {
|
2673
|
-
|
2674
|
-
var match = input.match(TYPEAHEAD_REGEXP), modelMapper, viewMapper, source;
|
2675
|
-
if (!match) {
|
2676
|
-
throw new Error(
|
2677
|
-
"Expected typeahead specification in form of '_modelValue_ (as _label_)? for _item_ in _collection_'" +
|
2678
|
-
" but got '" + input + "'.");
|
2679
|
-
}
|
2680
|
-
|
2681
|
-
return {
|
2682
|
-
itemName:match[3],
|
2683
|
-
source:$parse(match[4]),
|
2684
|
-
viewMapper:$parse(match[2] || match[1]),
|
2685
|
-
modelMapper:$parse(match[1])
|
2686
|
-
};
|
2687
|
-
}
|
2688
|
-
};
|
2689
|
-
}])
|
2690
|
-
|
2691
|
-
.directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser', function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) {
|
2692
|
-
|
2693
|
-
var HOT_KEYS = [9, 13, 27, 38, 40];
|
2694
|
-
|
2695
|
-
return {
|
2696
|
-
require:'ngModel',
|
2697
|
-
link:function (originalScope, element, attrs, modelCtrl) {
|
2698
|
-
|
2699
|
-
var selected;
|
2700
|
-
|
2701
|
-
//minimal no of characters that needs to be entered before typeahead kicks-in
|
2702
|
-
var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
|
2703
|
-
|
2704
|
-
//minimal wait time after last character typed before typehead kicks-in
|
2705
|
-
var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
|
2706
|
-
|
2707
|
-
//expressions used by typeahead
|
2708
|
-
var parserResult = typeaheadParser.parse(attrs.typeahead);
|
2709
|
-
|
2710
|
-
//should it restrict model values to the ones selected from the popup only?
|
2711
|
-
var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
|
2712
|
-
|
2713
|
-
var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
|
2714
|
-
|
2715
|
-
var onSelectCallback = $parse(attrs.typeaheadOnSelect);
|
2716
|
-
|
2717
|
-
//pop-up element used to display matches
|
2718
|
-
var popUpEl = angular.element('<typeahead-popup></typeahead-popup>');
|
2719
|
-
popUpEl.attr({
|
2720
|
-
matches: 'matches',
|
2721
|
-
active: 'activeIdx',
|
2722
|
-
select: 'select(activeIdx)',
|
2723
|
-
query: 'query',
|
2724
|
-
position: 'position'
|
2725
|
-
});
|
2726
|
-
|
2727
|
-
//create a child scope for the typeahead directive so we are not polluting original scope
|
2728
|
-
//with typeahead-specific data (matches, query etc.)
|
2729
|
-
var scope = originalScope.$new();
|
2730
|
-
originalScope.$on('$destroy', function(){
|
2731
|
-
scope.$destroy();
|
2732
|
-
});
|
2733
|
-
|
2734
|
-
var resetMatches = function() {
|
2735
|
-
scope.matches = [];
|
2736
|
-
scope.activeIdx = -1;
|
2737
|
-
};
|
2738
|
-
|
2739
|
-
var getMatchesAsync = function(inputValue) {
|
2740
|
-
|
2741
|
-
var locals = {$viewValue: inputValue};
|
2742
|
-
isLoadingSetter(originalScope, true);
|
2743
|
-
$q.when(parserResult.source(scope, locals)).then(function(matches) {
|
2744
|
-
|
2745
|
-
//it might happen that several async queries were in progress if a user were typing fast
|
2746
|
-
//but we are interested only in responses that correspond to the current view value
|
2747
|
-
if (inputValue === modelCtrl.$viewValue) {
|
2748
|
-
if (matches.length > 0) {
|
2749
|
-
|
2750
|
-
scope.activeIdx = 0;
|
2751
|
-
scope.matches.length = 0;
|
2752
|
-
|
2753
|
-
//transform labels
|
2754
|
-
for(var i=0; i<matches.length; i++) {
|
2755
|
-
locals[parserResult.itemName] = matches[i];
|
2756
|
-
scope.matches.push({
|
2757
|
-
label: parserResult.viewMapper(scope, locals),
|
2758
|
-
model: matches[i]
|
2759
|
-
});
|
2760
|
-
}
|
2761
|
-
|
2762
|
-
scope.query = inputValue;
|
2763
|
-
//position pop-up with matches - we need to re-calculate its position each time we are opening a window
|
2764
|
-
//with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
|
2765
|
-
//due to other elements being rendered
|
2766
|
-
scope.position = $position.position(element);
|
2767
|
-
scope.position.top = scope.position.top + element.prop('offsetHeight');
|
2768
|
-
|
2769
|
-
} else {
|
2770
|
-
resetMatches();
|
2771
|
-
}
|
2772
|
-
isLoadingSetter(originalScope, false);
|
2773
|
-
}
|
2774
|
-
}, function(){
|
2775
|
-
resetMatches();
|
2776
|
-
isLoadingSetter(originalScope, false);
|
2777
|
-
});
|
2778
|
-
};
|
2779
|
-
|
2780
|
-
resetMatches();
|
2781
|
-
|
2782
|
-
//we need to propagate user's query so we can higlight matches
|
2783
|
-
scope.query = undefined;
|
2784
|
-
|
2785
|
-
//plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
|
2786
|
-
//$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
|
2787
|
-
modelCtrl.$parsers.push(function (inputValue) {
|
2788
|
-
|
2789
|
-
var timeoutId;
|
2790
|
-
|
2791
|
-
resetMatches();
|
2792
|
-
if (selected) {
|
2793
|
-
return inputValue;
|
2794
|
-
} else {
|
2795
|
-
if (inputValue && inputValue.length >= minSearch) {
|
2796
|
-
if (waitTime > 0) {
|
2797
|
-
if (timeoutId) {
|
2798
|
-
$timeout.cancel(timeoutId);//cancel previous timeout
|
2799
|
-
}
|
2800
|
-
timeoutId = $timeout(function () {
|
2801
|
-
getMatchesAsync(inputValue);
|
2802
|
-
}, waitTime);
|
2803
|
-
} else {
|
2804
|
-
getMatchesAsync(inputValue);
|
2805
|
-
}
|
2806
|
-
}
|
2807
|
-
}
|
2808
|
-
|
2809
|
-
return isEditable ? inputValue : undefined;
|
2810
|
-
});
|
2811
|
-
|
2812
|
-
modelCtrl.$render = function () {
|
2813
|
-
var locals = {};
|
2814
|
-
locals[parserResult.itemName] = selected || modelCtrl.$viewValue;
|
2815
|
-
element.val(parserResult.viewMapper(scope, locals) || modelCtrl.$viewValue);
|
2816
|
-
selected = undefined;
|
2817
|
-
};
|
2818
|
-
|
2819
|
-
scope.select = function (activeIdx) {
|
2820
|
-
//called from within the $digest() cycle
|
2821
|
-
var locals = {};
|
2822
|
-
var model, item;
|
2823
|
-
locals[parserResult.itemName] = item = selected = scope.matches[activeIdx].model;
|
2824
|
-
|
2825
|
-
model = parserResult.modelMapper(scope, locals);
|
2826
|
-
modelCtrl.$setViewValue(model);
|
2827
|
-
modelCtrl.$render();
|
2828
|
-
onSelectCallback(scope, {
|
2829
|
-
$item: item,
|
2830
|
-
$model: model,
|
2831
|
-
$label: parserResult.viewMapper(scope, locals)
|
2832
|
-
});
|
2833
|
-
|
2834
|
-
element[0].focus();
|
2835
|
-
};
|
2836
|
-
|
2837
|
-
//bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
|
2838
|
-
element.bind('keydown', function (evt) {
|
2839
|
-
|
2840
|
-
//typeahead is open and an "interesting" key was pressed
|
2841
|
-
if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
|
2842
|
-
return;
|
2843
|
-
}
|
2844
|
-
|
2845
|
-
evt.preventDefault();
|
2846
|
-
|
2847
|
-
if (evt.which === 40) {
|
2848
|
-
scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
|
2849
|
-
scope.$digest();
|
2850
|
-
|
2851
|
-
} else if (evt.which === 38) {
|
2852
|
-
scope.activeIdx = (scope.activeIdx ? scope.activeIdx : scope.matches.length) - 1;
|
2853
|
-
scope.$digest();
|
2854
|
-
|
2855
|
-
} else if (evt.which === 13 || evt.which === 9) {
|
2856
|
-
scope.$apply(function () {
|
2857
|
-
scope.select(scope.activeIdx);
|
2858
|
-
});
|
2859
|
-
|
2860
|
-
} else if (evt.which === 27) {
|
2861
|
-
evt.stopPropagation();
|
2862
|
-
|
2863
|
-
resetMatches();
|
2864
|
-
scope.$digest();
|
2865
|
-
}
|
2866
|
-
});
|
2867
|
-
|
2868
|
-
$document.bind('click', function(){
|
2869
|
-
resetMatches();
|
2870
|
-
scope.$digest();
|
2871
|
-
});
|
2872
|
-
|
2873
|
-
element.after($compile(popUpEl)(scope));
|
2874
|
-
}
|
2875
|
-
};
|
2876
|
-
|
2877
|
-
}])
|
2878
|
-
|
2879
|
-
.directive('typeaheadPopup', function () {
|
2880
|
-
return {
|
2881
|
-
restrict:'E',
|
2882
|
-
scope:{
|
2883
|
-
matches:'=',
|
2884
|
-
query:'=',
|
2885
|
-
active:'=',
|
2886
|
-
position:'=',
|
2887
|
-
select:'&'
|
2888
|
-
},
|
2889
|
-
replace:true,
|
2890
|
-
templateUrl:'template/typeahead/typeahead.html',
|
2891
|
-
link:function (scope, element, attrs) {
|
2892
|
-
|
2893
|
-
scope.isOpen = function () {
|
2894
|
-
return scope.matches.length > 0;
|
2895
|
-
};
|
2896
|
-
|
2897
|
-
scope.isActive = function (matchIdx) {
|
2898
|
-
return scope.active == matchIdx;
|
2899
|
-
};
|
2900
|
-
|
2901
|
-
scope.selectActive = function (matchIdx) {
|
2902
|
-
scope.active = matchIdx;
|
2903
|
-
};
|
2904
|
-
|
2905
|
-
scope.selectMatch = function (activeIdx) {
|
2906
|
-
scope.select({activeIdx:activeIdx});
|
2907
|
-
};
|
2908
|
-
}
|
2909
|
-
};
|
2910
|
-
})
|
2911
|
-
|
2912
|
-
.filter('typeaheadHighlight', function() {
|
2913
|
-
|
2914
|
-
function escapeRegexp(queryToEscape) {
|
2915
|
-
return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
|
2916
|
-
}
|
2917
|
-
|
2918
|
-
return function(matchItem, query) {
|
2919
|
-
return query ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : query;
|
2920
|
-
};
|
2921
|
-
});angular.module("ui.bootstrap", ["ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.datepicker","ui.bootstrap.dialog","ui.bootstrap.dropdownToggle","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.position","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
|
1
|
+
angular.module("ui.bootstrap", ["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"]);
|
2922
2
|
angular.module('ui.bootstrap.transition', [])
|
2923
3
|
|
2924
4
|
/**
|
@@ -3028,7 +108,7 @@ angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
|
|
3028
108
|
var initialAnimSkip = true;
|
3029
109
|
scope.$watch(function (){ return element[0].scrollHeight; }, function (value) {
|
3030
110
|
//The listener is called when scollHeight changes
|
3031
|
-
//It actually does on 2 scenarios:
|
111
|
+
//It actually does on 2 scenarios:
|
3032
112
|
// 1. Parent is set to display none
|
3033
113
|
// 2. angular bindings inside are resolved
|
3034
114
|
//When we have a change of scrollHeight we are setting again the correct height if the group is opened
|
@@ -3042,7 +122,7 @@ angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
|
|
3042
122
|
}
|
3043
123
|
}
|
3044
124
|
});
|
3045
|
-
|
125
|
+
|
3046
126
|
scope.$watch(attrs.collapse, function(value) {
|
3047
127
|
if (value) {
|
3048
128
|
collapse();
|
@@ -3050,7 +130,7 @@ angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
|
|
3050
130
|
expand();
|
3051
131
|
}
|
3052
132
|
});
|
3053
|
-
|
133
|
+
|
3054
134
|
|
3055
135
|
var currentTransition;
|
3056
136
|
var doTransition = function(change) {
|
@@ -3083,7 +163,7 @@ angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])
|
|
3083
163
|
}
|
3084
164
|
isCollapsed = false;
|
3085
165
|
};
|
3086
|
-
|
166
|
+
|
3087
167
|
var collapse = function() {
|
3088
168
|
isCollapsed = true;
|
3089
169
|
if (initialAnimSkip) {
|
@@ -3105,7 +185,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
3105
185
|
})
|
3106
186
|
|
3107
187
|
.controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function ($scope, $attrs, accordionConfig) {
|
3108
|
-
|
188
|
+
|
3109
189
|
// This array keeps track of the accordion groups
|
3110
190
|
this.groups = [];
|
3111
191
|
|
@@ -3120,7 +200,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
3120
200
|
});
|
3121
201
|
}
|
3122
202
|
};
|
3123
|
-
|
203
|
+
|
3124
204
|
// This is called from the accordion-group directive to add itself to the accordion
|
3125
205
|
this.addGroup = function(groupScope) {
|
3126
206
|
var that = this;
|
@@ -3173,7 +253,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
3173
253
|
accordionCtrl.addGroup(scope);
|
3174
254
|
|
3175
255
|
scope.isOpen = false;
|
3176
|
-
|
256
|
+
|
3177
257
|
if ( attrs.isOpen ) {
|
3178
258
|
getIsOpen = $parse(attrs.isOpen);
|
3179
259
|
setIsOpen = getIsOpen.assign;
|
@@ -3182,7 +262,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
3182
262
|
function watchIsOpen() { return getIsOpen(scope.$parent); },
|
3183
263
|
function updateOpen(value) { scope.isOpen = value; }
|
3184
264
|
);
|
3185
|
-
|
265
|
+
|
3186
266
|
scope.isOpen = getIsOpen ? getIsOpen(scope.$parent) : false;
|
3187
267
|
}
|
3188
268
|
|
@@ -3299,21 +379,25 @@ angular.module('ui.bootstrap.buttons', [])
|
|
3299
379
|
require:'ngModel',
|
3300
380
|
link:function (scope, element, attrs, ngModelCtrl) {
|
3301
381
|
|
3302
|
-
|
3303
|
-
|
382
|
+
function getTrueValue() {
|
383
|
+
var trueValue = scope.$eval(attrs.btnCheckboxTrue);
|
384
|
+
return angular.isDefined(trueValue) ? trueValue : true;
|
385
|
+
}
|
3304
386
|
|
3305
|
-
|
3306
|
-
|
387
|
+
function getFalseValue() {
|
388
|
+
var falseValue = scope.$eval(attrs.btnCheckboxFalse);
|
389
|
+
return angular.isDefined(falseValue) ? falseValue : false;
|
390
|
+
}
|
3307
391
|
|
3308
392
|
//model -> UI
|
3309
393
|
ngModelCtrl.$render = function () {
|
3310
|
-
element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue,
|
394
|
+
element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
|
3311
395
|
};
|
3312
396
|
|
3313
397
|
//ui->model
|
3314
398
|
element.bind(toggleEvent, function () {
|
3315
399
|
scope.$apply(function () {
|
3316
|
-
ngModelCtrl.$setViewValue(element.hasClass(activeClass) ?
|
400
|
+
ngModelCtrl.$setViewValue(element.hasClass(activeClass) ? getFalseValue() : getTrueValue());
|
3317
401
|
ngModelCtrl.$render();
|
3318
402
|
});
|
3319
403
|
});
|
@@ -3323,7 +407,7 @@ angular.module('ui.bootstrap.buttons', [])
|
|
3323
407
|
/**
|
3324
408
|
* @ngdoc overview
|
3325
409
|
* @name ui.bootstrap.carousel
|
3326
|
-
*
|
410
|
+
*
|
3327
411
|
* @description
|
3328
412
|
* AngularJS version of an image carousel.
|
3329
413
|
*
|
@@ -3354,7 +438,7 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
|
|
3354
438
|
}
|
3355
439
|
function goNext() {
|
3356
440
|
//If we have a slide to transition from and we have a transition type and we're allowed, go
|
3357
|
-
if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {
|
441
|
+
if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {
|
3358
442
|
//We shouldn't do class manip in here, but it's the same weird thing bootstrap does. need to fix sometime
|
3359
443
|
nextSlide.$element.addClass(direction);
|
3360
444
|
nextSlide.$element[0].offsetWidth = nextSlide.$element[0].offsetWidth; //force reflow
|
@@ -3396,7 +480,7 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
|
|
3396
480
|
|
3397
481
|
$scope.next = function() {
|
3398
482
|
var newIndex = (currentIndex + 1) % slides.length;
|
3399
|
-
|
483
|
+
|
3400
484
|
//Prevent this user-triggered transition from occurring if there is already one in progress
|
3401
485
|
if (!$scope.$currentTransition) {
|
3402
486
|
return self.select(slides[newIndex], 'next');
|
@@ -3405,7 +489,7 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
|
|
3405
489
|
|
3406
490
|
$scope.prev = function() {
|
3407
491
|
var newIndex = currentIndex - 1 < 0 ? slides.length - 1 : currentIndex - 1;
|
3408
|
-
|
492
|
+
|
3409
493
|
//Prevent this user-triggered transition from occurring if there is already one in progress
|
3410
494
|
if (!$scope.$currentTransition) {
|
3411
495
|
return self.select(slides[newIndex], 'prev');
|
@@ -3622,7 +706,7 @@ function CarouselDemoCtrl($scope) {
|
|
3622
706
|
var lastValue = scope.active = getActive(scope.$parent);
|
3623
707
|
scope.$watch(function parentActiveWatch() {
|
3624
708
|
var parentActive = getActive(scope.$parent);
|
3625
|
-
|
709
|
+
|
3626
710
|
if (parentActive !== scope.active) {
|
3627
711
|
// we are out of sync and need to copy
|
3628
712
|
if (parentActive !== lastValue) {
|
@@ -3652,7 +736,101 @@ function CarouselDemoCtrl($scope) {
|
|
3652
736
|
};
|
3653
737
|
}]);
|
3654
738
|
|
3655
|
-
angular.module('ui.bootstrap.
|
739
|
+
angular.module('ui.bootstrap.position', [])
|
740
|
+
|
741
|
+
/**
|
742
|
+
* A set of utility methods that can be use to retrieve position of DOM elements.
|
743
|
+
* It is meant to be used where we need to absolute-position DOM elements in
|
744
|
+
* relation to other, existing elements (this is the case for tooltips, popovers,
|
745
|
+
* typeahead suggestions etc.).
|
746
|
+
*/
|
747
|
+
.factory('$position', ['$document', '$window', function ($document, $window) {
|
748
|
+
|
749
|
+
var mouseX, mouseY;
|
750
|
+
|
751
|
+
$document.bind('mousemove', function mouseMoved(event) {
|
752
|
+
mouseX = event.pageX;
|
753
|
+
mouseY = event.pageY;
|
754
|
+
});
|
755
|
+
|
756
|
+
function getStyle(el, cssprop) {
|
757
|
+
if (el.currentStyle) { //IE
|
758
|
+
return el.currentStyle[cssprop];
|
759
|
+
} else if ($window.getComputedStyle) {
|
760
|
+
return $window.getComputedStyle(el)[cssprop];
|
761
|
+
}
|
762
|
+
// finally try and get inline style
|
763
|
+
return el.style[cssprop];
|
764
|
+
}
|
765
|
+
|
766
|
+
/**
|
767
|
+
* Checks if a given element is statically positioned
|
768
|
+
* @param element - raw DOM element
|
769
|
+
*/
|
770
|
+
function isStaticPositioned(element) {
|
771
|
+
return (getStyle(element, "position") || 'static' ) === 'static';
|
772
|
+
}
|
773
|
+
|
774
|
+
/**
|
775
|
+
* returns the closest, non-statically positioned parentOffset of a given element
|
776
|
+
* @param element
|
777
|
+
*/
|
778
|
+
var parentOffsetEl = function (element) {
|
779
|
+
var docDomEl = $document[0];
|
780
|
+
var offsetParent = element.offsetParent || docDomEl;
|
781
|
+
while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
|
782
|
+
offsetParent = offsetParent.offsetParent;
|
783
|
+
}
|
784
|
+
return offsetParent || docDomEl;
|
785
|
+
};
|
786
|
+
|
787
|
+
return {
|
788
|
+
/**
|
789
|
+
* Provides read-only equivalent of jQuery's position function:
|
790
|
+
* http://api.jquery.com/position/
|
791
|
+
*/
|
792
|
+
position: function (element) {
|
793
|
+
var elBCR = this.offset(element);
|
794
|
+
var offsetParentBCR = { top: 0, left: 0 };
|
795
|
+
var offsetParentEl = parentOffsetEl(element[0]);
|
796
|
+
if (offsetParentEl != $document[0]) {
|
797
|
+
offsetParentBCR = this.offset(angular.element(offsetParentEl));
|
798
|
+
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
|
799
|
+
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
|
800
|
+
}
|
801
|
+
|
802
|
+
return {
|
803
|
+
width: element.prop('offsetWidth'),
|
804
|
+
height: element.prop('offsetHeight'),
|
805
|
+
top: elBCR.top - offsetParentBCR.top,
|
806
|
+
left: elBCR.left - offsetParentBCR.left
|
807
|
+
};
|
808
|
+
},
|
809
|
+
|
810
|
+
/**
|
811
|
+
* Provides read-only equivalent of jQuery's offset function:
|
812
|
+
* http://api.jquery.com/offset/
|
813
|
+
*/
|
814
|
+
offset: function (element) {
|
815
|
+
var boundingClientRect = element[0].getBoundingClientRect();
|
816
|
+
return {
|
817
|
+
width: element.prop('offsetWidth'),
|
818
|
+
height: element.prop('offsetHeight'),
|
819
|
+
top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop),
|
820
|
+
left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft)
|
821
|
+
};
|
822
|
+
},
|
823
|
+
|
824
|
+
/**
|
825
|
+
* Provides the coordinates of the mouse
|
826
|
+
*/
|
827
|
+
mouse: function () {
|
828
|
+
return {x: mouseX, y: mouseY};
|
829
|
+
}
|
830
|
+
};
|
831
|
+
}]);
|
832
|
+
|
833
|
+
angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.position'])
|
3656
834
|
|
3657
835
|
.constant('datepickerConfig', {
|
3658
836
|
dayFormat: 'dd',
|
@@ -3663,31 +841,139 @@ angular.module('ui.bootstrap.datepicker', [])
|
|
3663
841
|
monthTitleFormat: 'yyyy',
|
3664
842
|
showWeeks: true,
|
3665
843
|
startingDay: 0,
|
3666
|
-
yearRange: 20
|
844
|
+
yearRange: 20,
|
845
|
+
minDate: null,
|
846
|
+
maxDate: null
|
3667
847
|
})
|
3668
848
|
|
3669
|
-
.
|
849
|
+
.controller('DatepickerController', ['$scope', '$attrs', 'dateFilter', 'datepickerConfig', function($scope, $attrs, dateFilter, dtConfig) {
|
850
|
+
var format = {
|
851
|
+
day: getValue($attrs.dayFormat, dtConfig.dayFormat),
|
852
|
+
month: getValue($attrs.monthFormat, dtConfig.monthFormat),
|
853
|
+
year: getValue($attrs.yearFormat, dtConfig.yearFormat),
|
854
|
+
dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat),
|
855
|
+
dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat),
|
856
|
+
monthTitle: getValue($attrs.monthTitleFormat, dtConfig.monthTitleFormat)
|
857
|
+
},
|
858
|
+
startingDay = getValue($attrs.startingDay, dtConfig.startingDay),
|
859
|
+
yearRange = getValue($attrs.yearRange, dtConfig.yearRange);
|
860
|
+
|
861
|
+
this.minDate = dtConfig.minDate ? new Date(dtConfig.minDate) : null;
|
862
|
+
this.maxDate = dtConfig.maxDate ? new Date(dtConfig.maxDate) : null;
|
863
|
+
|
864
|
+
function getValue(value, defaultValue) {
|
865
|
+
return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
|
866
|
+
}
|
867
|
+
|
868
|
+
function getDaysInMonth( year, month ) {
|
869
|
+
return new Date(year, month, 0).getDate();
|
870
|
+
}
|
871
|
+
|
872
|
+
function getDates(startDate, n) {
|
873
|
+
var dates = new Array(n);
|
874
|
+
var current = startDate, i = 0;
|
875
|
+
while (i < n) {
|
876
|
+
dates[i++] = new Date(current);
|
877
|
+
current.setDate( current.getDate() + 1 );
|
878
|
+
}
|
879
|
+
return dates;
|
880
|
+
}
|
881
|
+
|
882
|
+
function makeDate(date, format, isSelected, isSecondary) {
|
883
|
+
return { date: date, label: dateFilter(date, format), selected: !!isSelected, secondary: !!isSecondary };
|
884
|
+
}
|
885
|
+
|
886
|
+
this.modes = [
|
887
|
+
{
|
888
|
+
name: 'day',
|
889
|
+
getVisibleDates: function(date, selected) {
|
890
|
+
var year = date.getFullYear(), month = date.getMonth(), firstDayOfMonth = new Date(year, month, 1);
|
891
|
+
var difference = startingDay - firstDayOfMonth.getDay(),
|
892
|
+
numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
|
893
|
+
firstDate = new Date(firstDayOfMonth), numDates = 0;
|
894
|
+
|
895
|
+
if ( numDisplayedFromPreviousMonth > 0 ) {
|
896
|
+
firstDate.setDate( - numDisplayedFromPreviousMonth + 1 );
|
897
|
+
numDates += numDisplayedFromPreviousMonth; // Previous
|
898
|
+
}
|
899
|
+
numDates += getDaysInMonth(year, month + 1); // Current
|
900
|
+
numDates += (7 - numDates % 7) % 7; // Next
|
901
|
+
|
902
|
+
var days = getDates(firstDate, numDates), labels = new Array(7);
|
903
|
+
for (var i = 0; i < numDates; i ++) {
|
904
|
+
var dt = new Date(days[i]);
|
905
|
+
days[i] = makeDate(dt, format.day, (selected && selected.getDate() === dt.getDate() && selected.getMonth() === dt.getMonth() && selected.getFullYear() === dt.getFullYear()), dt.getMonth() !== month);
|
906
|
+
}
|
907
|
+
for (var j = 0; j < 7; j++) {
|
908
|
+
labels[j] = dateFilter(days[j].date, format.dayHeader);
|
909
|
+
}
|
910
|
+
return { objects: days, title: dateFilter(date, format.dayTitle), labels: labels };
|
911
|
+
},
|
912
|
+
compare: function(date1, date2) {
|
913
|
+
return (new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) - new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) );
|
914
|
+
},
|
915
|
+
split: 7,
|
916
|
+
step: { months: 1 }
|
917
|
+
},
|
918
|
+
{
|
919
|
+
name: 'month',
|
920
|
+
getVisibleDates: function(date, selected) {
|
921
|
+
var months = new Array(12), year = date.getFullYear();
|
922
|
+
for ( var i = 0; i < 12; i++ ) {
|
923
|
+
var dt = new Date(year, i, 1);
|
924
|
+
months[i] = makeDate(dt, format.month, (selected && selected.getMonth() === i && selected.getFullYear() === year));
|
925
|
+
}
|
926
|
+
return { objects: months, title: dateFilter(date, format.monthTitle) };
|
927
|
+
},
|
928
|
+
compare: function(date1, date2) {
|
929
|
+
return new Date( date1.getFullYear(), date1.getMonth() ) - new Date( date2.getFullYear(), date2.getMonth() );
|
930
|
+
},
|
931
|
+
split: 3,
|
932
|
+
step: { years: 1 }
|
933
|
+
},
|
934
|
+
{
|
935
|
+
name: 'year',
|
936
|
+
getVisibleDates: function(date, selected) {
|
937
|
+
var years = new Array(yearRange), year = date.getFullYear(), startYear = parseInt((year - 1) / yearRange, 10) * yearRange + 1;
|
938
|
+
for ( var i = 0; i < yearRange; i++ ) {
|
939
|
+
var dt = new Date(startYear + i, 0, 1);
|
940
|
+
years[i] = makeDate(dt, format.year, (selected && selected.getFullYear() === dt.getFullYear()));
|
941
|
+
}
|
942
|
+
return { objects: years, title: [years[0].label, years[yearRange - 1].label].join(' - ') };
|
943
|
+
},
|
944
|
+
compare: function(date1, date2) {
|
945
|
+
return date1.getFullYear() - date2.getFullYear();
|
946
|
+
},
|
947
|
+
split: 5,
|
948
|
+
step: { years: yearRange }
|
949
|
+
}
|
950
|
+
];
|
951
|
+
|
952
|
+
this.isDisabled = function(date, mode) {
|
953
|
+
var currentMode = this.modes[mode || 0];
|
954
|
+
return ((this.minDate && currentMode.compare(date, this.minDate) < 0) || (this.maxDate && currentMode.compare(date, this.maxDate) > 0) || ($scope.dateDisabled && $scope.dateDisabled({date: date, mode: currentMode.name})));
|
955
|
+
};
|
956
|
+
}])
|
957
|
+
|
958
|
+
.directive( 'datepicker', ['dateFilter', '$parse', 'datepickerConfig', '$log', function (dateFilter, $parse, datepickerConfig, $log) {
|
3670
959
|
return {
|
3671
960
|
restrict: 'EA',
|
3672
961
|
replace: true,
|
962
|
+
templateUrl: 'template/datepicker/datepicker.html',
|
3673
963
|
scope: {
|
3674
|
-
model: '=ngModel',
|
3675
964
|
dateDisabled: '&'
|
3676
965
|
},
|
3677
|
-
|
3678
|
-
|
3679
|
-
|
966
|
+
require: ['datepicker', '?^ngModel'],
|
967
|
+
controller: 'DatepickerController',
|
968
|
+
link: function(scope, element, attrs, ctrls) {
|
969
|
+
var datepickerCtrl = ctrls[0], ngModel = ctrls[1];
|
970
|
+
|
971
|
+
if (!ngModel) {
|
972
|
+
return; // do nothing if no ng-model
|
973
|
+
}
|
3680
974
|
|
3681
975
|
// Configuration parameters
|
3682
|
-
var selected = new Date(), showWeeks
|
3683
|
-
format.day = angular.isDefined(attrs.dayFormat) ? scope.$eval(attrs.dayFormat) : datepickerConfig.dayFormat;
|
3684
|
-
format.month = angular.isDefined(attrs.monthFormat) ? scope.$eval(attrs.monthFormat) : datepickerConfig.monthFormat;
|
3685
|
-
format.year = angular.isDefined(attrs.yearFormat) ? scope.$eval(attrs.yearFormat) : datepickerConfig.yearFormat;
|
3686
|
-
format.dayHeader = angular.isDefined(attrs.dayHeaderFormat) ? scope.$eval(attrs.dayHeaderFormat) : datepickerConfig.dayHeaderFormat;
|
3687
|
-
format.dayTitle = angular.isDefined(attrs.dayTitleFormat) ? scope.$eval(attrs.dayTitleFormat) : datepickerConfig.dayTitleFormat;
|
3688
|
-
format.monthTitle = angular.isDefined(attrs.monthTitleFormat) ? scope.$eval(attrs.monthTitleFormat) : datepickerConfig.monthTitleFormat;
|
3689
|
-
var startingDay = angular.isDefined(attrs.startingDay) ? scope.$eval(attrs.startingDay) : datepickerConfig.startingDay;
|
3690
|
-
var yearRange = angular.isDefined(attrs.yearRange) ? scope.$eval(attrs.yearRange) : datepickerConfig.yearRange;
|
976
|
+
var mode = 0, selected = new Date(), showWeeks = datepickerConfig.showWeeks;
|
3691
977
|
|
3692
978
|
if (attrs.showWeeks) {
|
3693
979
|
scope.$parent.$watch($parse(attrs.showWeeks), function(value) {
|
@@ -3695,174 +981,282 @@ angular.module('ui.bootstrap.datepicker', [])
|
|
3695
981
|
updateShowWeekNumbers();
|
3696
982
|
});
|
3697
983
|
} else {
|
3698
|
-
showWeeks = datepickerConfig.showWeeks;
|
3699
984
|
updateShowWeekNumbers();
|
3700
985
|
}
|
3701
986
|
|
3702
987
|
if (attrs.min) {
|
3703
988
|
scope.$parent.$watch($parse(attrs.min), function(value) {
|
3704
|
-
minDate = new Date(value);
|
989
|
+
datepickerCtrl.minDate = value ? new Date(value) : null;
|
3705
990
|
refill();
|
3706
991
|
});
|
3707
992
|
}
|
3708
993
|
if (attrs.max) {
|
3709
994
|
scope.$parent.$watch($parse(attrs.max), function(value) {
|
3710
|
-
maxDate = new Date(value);
|
995
|
+
datepickerCtrl.maxDate = value ? new Date(value) : null;
|
3711
996
|
refill();
|
3712
997
|
});
|
3713
998
|
}
|
3714
999
|
|
3715
|
-
function
|
3716
|
-
scope.
|
3717
|
-
scope.labels = labels;
|
3718
|
-
scope.title = title;
|
1000
|
+
function updateShowWeekNumbers() {
|
1001
|
+
scope.showWeekNumbers = mode === 0 && showWeeks;
|
3719
1002
|
}
|
3720
1003
|
|
3721
|
-
//
|
3722
|
-
function
|
3723
|
-
|
1004
|
+
// Split array into smaller arrays
|
1005
|
+
function split(arr, size) {
|
1006
|
+
var arrays = [];
|
1007
|
+
while (arr.length > 0) {
|
1008
|
+
arrays.push(arr.splice(0, size));
|
1009
|
+
}
|
1010
|
+
return arrays;
|
3724
1011
|
}
|
3725
1012
|
|
3726
|
-
function
|
3727
|
-
|
3728
|
-
|
3729
|
-
|
3730
|
-
|
3731
|
-
|
3732
|
-
|
1013
|
+
function refill( updateSelected ) {
|
1014
|
+
var date = null, valid = true;
|
1015
|
+
|
1016
|
+
if ( ngModel.$modelValue ) {
|
1017
|
+
date = new Date( ngModel.$modelValue );
|
1018
|
+
|
1019
|
+
if ( isNaN(date) ) {
|
1020
|
+
valid = false;
|
1021
|
+
$log.error('Datepicker 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.');
|
1022
|
+
} else if ( updateSelected ) {
|
1023
|
+
selected = date;
|
1024
|
+
}
|
3733
1025
|
}
|
1026
|
+
ngModel.$setValidity('date', valid);
|
1027
|
+
|
1028
|
+
var currentMode = datepickerCtrl.modes[mode], data = currentMode.getVisibleDates(selected, date);
|
1029
|
+
angular.forEach(data.objects, function(obj) {
|
1030
|
+
obj.disabled = datepickerCtrl.isDisabled(obj.date, mode);
|
1031
|
+
});
|
1032
|
+
|
1033
|
+
ngModel.$setValidity('date-disabled', (!date || !datepickerCtrl.isDisabled(date)));
|
1034
|
+
|
1035
|
+
scope.rows = split(data.objects, currentMode.split);
|
1036
|
+
scope.labels = data.labels || [];
|
1037
|
+
scope.title = data.title;
|
3734
1038
|
}
|
3735
1039
|
|
3736
|
-
function
|
3737
|
-
|
1040
|
+
function setMode(value) {
|
1041
|
+
mode = value;
|
1042
|
+
updateShowWeekNumbers();
|
1043
|
+
refill();
|
3738
1044
|
}
|
3739
1045
|
|
3740
|
-
|
3741
|
-
|
3742
|
-
|
3743
|
-
|
3744
|
-
|
1046
|
+
ngModel.$render = function() {
|
1047
|
+
refill( true );
|
1048
|
+
};
|
1049
|
+
|
1050
|
+
scope.select = function( date ) {
|
1051
|
+
if ( mode === 0 ) {
|
1052
|
+
var dt = new Date( ngModel.$modelValue );
|
1053
|
+
dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );
|
1054
|
+
ngModel.$setViewValue( dt );
|
1055
|
+
refill( true );
|
1056
|
+
} else {
|
1057
|
+
selected = date;
|
1058
|
+
setMode( mode - 1 );
|
3745
1059
|
}
|
3746
|
-
return arrays;
|
3747
1060
|
};
|
3748
|
-
|
3749
|
-
|
1061
|
+
scope.move = function(direction) {
|
1062
|
+
var step = datepickerCtrl.modes[mode].step;
|
1063
|
+
selected.setMonth( selected.getMonth() + direction * (step.months || 0) );
|
1064
|
+
selected.setFullYear( selected.getFullYear() + direction * (step.years || 0) );
|
1065
|
+
refill();
|
1066
|
+
};
|
1067
|
+
scope.toggleMode = function() {
|
1068
|
+
setMode( (mode + 1) % datepickerCtrl.modes.length );
|
1069
|
+
};
|
1070
|
+
scope.getWeekNumber = function(row) {
|
1071
|
+
return ( mode === 0 && scope.showWeekNumbers && row.length === 7 ) ? getISO8601WeekNumber(row[0].date) : null;
|
3750
1072
|
};
|
3751
1073
|
|
3752
|
-
|
3753
|
-
|
3754
|
-
|
1074
|
+
function getISO8601WeekNumber(date) {
|
1075
|
+
var checkDate = new Date(date);
|
1076
|
+
checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
|
1077
|
+
var time = checkDate.getTime();
|
1078
|
+
checkDate.setMonth(0); // Compare with Jan 1
|
1079
|
+
checkDate.setDate(1);
|
1080
|
+
return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
|
1081
|
+
}
|
1082
|
+
}
|
1083
|
+
};
|
1084
|
+
}])
|
3755
1085
|
|
3756
|
-
|
3757
|
-
|
3758
|
-
|
3759
|
-
|
3760
|
-
}
|
3761
|
-
lastDate = dt;
|
3762
|
-
}
|
1086
|
+
.constant('datepickerPopupConfig', {
|
1087
|
+
dateFormat: 'yyyy-MM-dd',
|
1088
|
+
closeOnDateSelection: true
|
1089
|
+
})
|
3763
1090
|
|
3764
|
-
|
3765
|
-
|
1091
|
+
.directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'datepickerPopupConfig',
|
1092
|
+
function ($compile, $parse, $document, $position, dateFilter, datepickerPopupConfig) {
|
1093
|
+
return {
|
1094
|
+
restrict: 'EA',
|
1095
|
+
require: 'ngModel',
|
1096
|
+
link: function(originalScope, element, attrs, ngModel) {
|
3766
1097
|
|
3767
|
-
|
3768
|
-
|
1098
|
+
var closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection;
|
1099
|
+
var dateFormat = attrs.datepickerPopup || datepickerPopupConfig.dateFormat;
|
3769
1100
|
|
3770
|
-
|
3771
|
-
|
3772
|
-
|
3773
|
-
|
3774
|
-
|
3775
|
-
addDays(lastDate, (7 - days.length % 7) % 7, false);
|
1101
|
+
// create a child scope for the datepicker directive so we are not polluting original scope
|
1102
|
+
var scope = originalScope.$new();
|
1103
|
+
originalScope.$on('$destroy', function() {
|
1104
|
+
scope.$destroy();
|
1105
|
+
});
|
3776
1106
|
|
3777
|
-
|
3778
|
-
|
3779
|
-
|
3780
|
-
|
3781
|
-
|
3782
|
-
|
3783
|
-
|
3784
|
-
|
3785
|
-
|
3786
|
-
|
3787
|
-
|
3788
|
-
}
|
3789
|
-
updateCalendar( split( months, 3 ), [], dateFilter(selected, format.monthTitle) );
|
3790
|
-
},
|
3791
|
-
year: function() {
|
3792
|
-
var years = [], year = parseInt((selected.getFullYear() - 1) / yearRange, 10) * yearRange + 1;
|
3793
|
-
for ( var i = 0; i < yearRange; i++ ) {
|
3794
|
-
var dt = new Date(year + i, 0, 1);
|
3795
|
-
years.push( {date: dt, isCurrent: true, isSelected: isSelected(dt), label: dateFilter(dt, format.year), disabled: isDisabled(dt)} );
|
1107
|
+
function formatDate(value) {
|
1108
|
+
return (value) ? dateFilter(value, dateFormat) : null;
|
1109
|
+
}
|
1110
|
+
ngModel.$formatters.push(formatDate);
|
1111
|
+
|
1112
|
+
// TODO: reverse from dateFilter string to Date object
|
1113
|
+
function parseDate(value) {
|
1114
|
+
if ( value ) {
|
1115
|
+
var date = new Date(value);
|
1116
|
+
if (!isNaN(date)) {
|
1117
|
+
return date;
|
3796
1118
|
}
|
3797
|
-
|
3798
|
-
|
1119
|
+
}
|
1120
|
+
return value;
|
1121
|
+
}
|
1122
|
+
ngModel.$parsers.push(parseDate);
|
1123
|
+
|
1124
|
+
var getIsOpen, setIsOpen;
|
1125
|
+
if ( attrs.open ) {
|
1126
|
+
getIsOpen = $parse(attrs.open);
|
1127
|
+
setIsOpen = getIsOpen.assign;
|
1128
|
+
|
1129
|
+
originalScope.$watch(getIsOpen, function updateOpen(value) {
|
1130
|
+
scope.isOpen = !! value;
|
1131
|
+
});
|
1132
|
+
}
|
1133
|
+
scope.isOpen = getIsOpen ? getIsOpen(originalScope) : false; // Initial state
|
1134
|
+
|
1135
|
+
function setOpen( value ) {
|
1136
|
+
if (setIsOpen) {
|
1137
|
+
setIsOpen(originalScope, !!value);
|
1138
|
+
} else {
|
1139
|
+
scope.isOpen = !!value;
|
1140
|
+
}
|
1141
|
+
}
|
1142
|
+
|
1143
|
+
var documentClickBind = function(event) {
|
1144
|
+
if (scope.isOpen && event.target !== element[0]) {
|
1145
|
+
scope.$apply(function() {
|
1146
|
+
setOpen(false);
|
1147
|
+
});
|
1148
|
+
}
|
1149
|
+
};
|
1150
|
+
|
1151
|
+
var elementFocusBind = function() {
|
1152
|
+
scope.$apply(function() {
|
1153
|
+
setOpen( true );
|
1154
|
+
});
|
1155
|
+
};
|
1156
|
+
|
1157
|
+
// popup element used to display calendar
|
1158
|
+
var popupEl = angular.element('<datepicker-popup-wrap><datepicker></datepicker></datepicker-popup-wrap>');
|
1159
|
+
popupEl.attr({
|
1160
|
+
'ng-model': 'date',
|
1161
|
+
'ng-change': 'dateSelection()'
|
1162
|
+
});
|
1163
|
+
var datepickerEl = popupEl.find('datepicker');
|
1164
|
+
if (attrs.datepickerOptions) {
|
1165
|
+
datepickerEl.attr(angular.extend({}, originalScope.$eval(attrs.datepickerOptions)));
|
1166
|
+
}
|
1167
|
+
|
1168
|
+
var $setModelValue = $parse(attrs.ngModel).assign;
|
1169
|
+
|
1170
|
+
// Inner change
|
1171
|
+
scope.dateSelection = function() {
|
1172
|
+
$setModelValue(originalScope, scope.date);
|
1173
|
+
if (closeOnDateSelection) {
|
1174
|
+
setOpen( false );
|
3799
1175
|
}
|
3800
1176
|
};
|
3801
|
-
|
3802
|
-
|
3803
|
-
|
3804
|
-
|
3805
|
-
|
3806
|
-
|
3807
|
-
|
3808
|
-
|
3809
|
-
if (
|
3810
|
-
|
1177
|
+
|
1178
|
+
// Outter change
|
1179
|
+
scope.$watch(function() {
|
1180
|
+
return ngModel.$modelValue;
|
1181
|
+
}, function(value) {
|
1182
|
+
if (angular.isString(value)) {
|
1183
|
+
var date = parseDate(value);
|
1184
|
+
|
1185
|
+
if (value && !date) {
|
1186
|
+
$setModelValue(originalScope, null);
|
1187
|
+
throw new Error(value + ' cannot be parsed to a date object.');
|
1188
|
+
} else {
|
1189
|
+
value = date;
|
3811
1190
|
}
|
3812
1191
|
}
|
3813
|
-
|
3814
|
-
|
1192
|
+
scope.date = value;
|
1193
|
+
updatePosition();
|
1194
|
+
});
|
3815
1195
|
|
3816
|
-
|
3817
|
-
if (
|
3818
|
-
|
1196
|
+
function addWatchableAttribute(attribute, scopeProperty, datepickerAttribute) {
|
1197
|
+
if (attribute) {
|
1198
|
+
originalScope.$watch($parse(attribute), function(value){
|
1199
|
+
scope[scopeProperty] = value;
|
1200
|
+
});
|
1201
|
+
datepickerEl.attr(datepickerAttribute || scopeProperty, scopeProperty);
|
3819
1202
|
}
|
1203
|
+
}
|
1204
|
+
addWatchableAttribute(attrs.min, 'min');
|
1205
|
+
addWatchableAttribute(attrs.max, 'max');
|
1206
|
+
if (attrs.showWeeks) {
|
1207
|
+
addWatchableAttribute(attrs.showWeeks, 'showWeeks', 'show-weeks');
|
1208
|
+
} else {
|
1209
|
+
scope.showWeeks = true;
|
1210
|
+
datepickerEl.attr('show-weeks', 'showWeeks');
|
1211
|
+
}
|
1212
|
+
if (attrs.dateDisabled) {
|
1213
|
+
datepickerEl.attr('date-disabled', attrs.dateDisabled);
|
1214
|
+
}
|
3820
1215
|
|
3821
|
-
|
3822
|
-
|
3823
|
-
|
3824
|
-
}
|
3825
|
-
scope.$watch('mode', function() {
|
3826
|
-
updateShowWeekNumbers();
|
3827
|
-
refill();
|
3828
|
-
});
|
1216
|
+
function updatePosition() {
|
1217
|
+
scope.position = $position.position(element);
|
1218
|
+
scope.position.top = scope.position.top + element.prop('offsetHeight');
|
1219
|
+
}
|
3829
1220
|
|
3830
|
-
scope
|
3831
|
-
|
3832
|
-
|
3833
|
-
|
3834
|
-
|
3835
|
-
|
3836
|
-
} else
|
3837
|
-
|
3838
|
-
|
3839
|
-
} else if ( scope.mode === 'day' ) {
|
3840
|
-
scope.model = new Date(selected);
|
1221
|
+
scope.$watch('isOpen', function(value) {
|
1222
|
+
if (value) {
|
1223
|
+
updatePosition();
|
1224
|
+
$document.bind('click', documentClickBind);
|
1225
|
+
element.unbind('focus', elementFocusBind);
|
1226
|
+
element.focus();
|
1227
|
+
} else {
|
1228
|
+
$document.unbind('click', documentClickBind);
|
1229
|
+
element.bind('focus', elementFocusBind);
|
3841
1230
|
}
|
3842
|
-
|
3843
|
-
|
3844
|
-
|
3845
|
-
selected.setMonth( selected.getMonth() + step );
|
3846
|
-
} else if (scope.mode === 'month') {
|
3847
|
-
selected.setFullYear( selected.getFullYear() + step );
|
3848
|
-
} else if (scope.mode === 'year') {
|
3849
|
-
selected.setFullYear( selected.getFullYear() + step * yearRange );
|
1231
|
+
|
1232
|
+
if ( setIsOpen ) {
|
1233
|
+
setIsOpen(originalScope, value);
|
3850
1234
|
}
|
3851
|
-
|
1235
|
+
});
|
1236
|
+
|
1237
|
+
scope.today = function() {
|
1238
|
+
$setModelValue(originalScope, new Date());
|
3852
1239
|
};
|
3853
|
-
scope.
|
3854
|
-
|
1240
|
+
scope.clear = function() {
|
1241
|
+
$setModelValue(originalScope, null);
|
3855
1242
|
};
|
3856
|
-
scope.getWeekNumber = function(row) {
|
3857
|
-
if ( scope.mode !== 'day' || ! scope.showWeekNumbers || row.length !== 7 ) {
|
3858
|
-
return;
|
3859
|
-
}
|
3860
1243
|
|
3861
|
-
|
3862
|
-
|
3863
|
-
|
3864
|
-
|
3865
|
-
|
1244
|
+
element.after($compile(popupEl)(scope));
|
1245
|
+
}
|
1246
|
+
};
|
1247
|
+
}])
|
1248
|
+
|
1249
|
+
.directive('datepickerPopupWrap', [function() {
|
1250
|
+
return {
|
1251
|
+
restrict:'E',
|
1252
|
+
replace: true,
|
1253
|
+
transclude: true,
|
1254
|
+
templateUrl: 'template/datepicker/popup.html',
|
1255
|
+
link:function (scope, element, attrs) {
|
1256
|
+
element.bind('click', function(event) {
|
1257
|
+
event.preventDefault();
|
1258
|
+
event.stopPropagation();
|
1259
|
+
});
|
3866
1260
|
}
|
3867
1261
|
};
|
3868
1262
|
}]);
|
@@ -3894,9 +1288,9 @@ dialogModule.provider("$dialog", function(){
|
|
3894
1288
|
keyboard: true, // close with esc key
|
3895
1289
|
backdropClick: true // only in conjunction with backdrop=true
|
3896
1290
|
/* other options: template, templateUrl, controller */
|
3897
|
-
|
1291
|
+
};
|
3898
1292
|
|
3899
|
-
|
1293
|
+
var globalOptions = {};
|
3900
1294
|
|
3901
1295
|
var activeBackdrops = {value : 0};
|
3902
1296
|
|
@@ -3906,21 +1300,21 @@ dialogModule.provider("$dialog", function(){
|
|
3906
1300
|
// // don't close dialog when backdrop is clicked by default
|
3907
1301
|
// $dialogProvider.options({backdropClick: false});
|
3908
1302
|
// });
|
3909
|
-
|
3910
|
-
|
3911
|
-
|
1303
|
+
this.options = function(value){
|
1304
|
+
globalOptions = value;
|
1305
|
+
};
|
3912
1306
|
|
3913
1307
|
// Returns the actual `$dialog` service that is injected in controllers
|
3914
|
-
|
1308
|
+
this.$get = ["$http", "$document", "$compile", "$rootScope", "$controller", "$templateCache", "$q", "$transition", "$injector",
|
3915
1309
|
function ($http, $document, $compile, $rootScope, $controller, $templateCache, $q, $transition, $injector) {
|
3916
1310
|
|
3917
|
-
|
1311
|
+
var body = $document.find('body');
|
3918
1312
|
|
3919
|
-
|
3920
|
-
|
3921
|
-
|
3922
|
-
|
3923
|
-
|
1313
|
+
function createElement(clazz) {
|
1314
|
+
var el = angular.element("<div>");
|
1315
|
+
el.addClass(clazz);
|
1316
|
+
return el;
|
1317
|
+
}
|
3924
1318
|
|
3925
1319
|
// The `Dialog` class represents a modal dialog. The dialog class can be invoked by providing an options object
|
3926
1320
|
// containing at lest template or templateUrl and controller:
|
@@ -3930,7 +1324,7 @@ dialogModule.provider("$dialog", function(){
|
|
3930
1324
|
// Dialogs can also be created using templateUrl and controller as distinct arguments:
|
3931
1325
|
//
|
3932
1326
|
// var d = new Dialog('path/to/dialog.html', MyDialogController);
|
3933
|
-
|
1327
|
+
function Dialog(opts) {
|
3934
1328
|
|
3935
1329
|
var self = this, options = this.options = angular.extend({}, defaults, globalOptions, opts);
|
3936
1330
|
this._open = false;
|
@@ -3960,10 +1354,6 @@ dialogModule.provider("$dialog", function(){
|
|
3960
1354
|
e.preventDefault();
|
3961
1355
|
self.$scope.$apply();
|
3962
1356
|
};
|
3963
|
-
|
3964
|
-
this.handleLocationChange = function() {
|
3965
|
-
self.close();
|
3966
|
-
};
|
3967
1357
|
}
|
3968
1358
|
|
3969
1359
|
// The `isOpen()` method returns wether the dialog is currently visible.
|
@@ -4070,9 +1460,9 @@ dialogModule.provider("$dialog", function(){
|
|
4070
1460
|
Dialog.prototype._addElementsToDom = function(){
|
4071
1461
|
body.append(this.modalEl);
|
4072
1462
|
|
4073
|
-
if(this.options.backdrop) {
|
1463
|
+
if(this.options.backdrop) {
|
4074
1464
|
if (activeBackdrops.value === 0) {
|
4075
|
-
body.append(this.backdropEl);
|
1465
|
+
body.append(this.backdropEl);
|
4076
1466
|
}
|
4077
1467
|
activeBackdrops.value++;
|
4078
1468
|
}
|
@@ -4083,10 +1473,10 @@ dialogModule.provider("$dialog", function(){
|
|
4083
1473
|
Dialog.prototype._removeElementsFromDom = function(){
|
4084
1474
|
this.modalEl.remove();
|
4085
1475
|
|
4086
|
-
if(this.options.backdrop) {
|
1476
|
+
if(this.options.backdrop) {
|
4087
1477
|
activeBackdrops.value--;
|
4088
1478
|
if (activeBackdrops.value === 0) {
|
4089
|
-
this.backdropEl.remove();
|
1479
|
+
this.backdropEl.remove();
|
4090
1480
|
}
|
4091
1481
|
}
|
4092
1482
|
this._open = false;
|
@@ -4216,7 +1606,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.dialog'])
|
|
4216
1606
|
// Create a dialog with the template as the contents of the directive
|
4217
1607
|
// Add the current scope as the resolve in order to make the directive scope as a dialog controller scope
|
4218
1608
|
opts = angular.extend(opts, {
|
4219
|
-
template: elm.html(),
|
1609
|
+
template: elm.html(),
|
4220
1610
|
resolve: { $scope: function() { return scope; } }
|
4221
1611
|
});
|
4222
1612
|
var dialog = $dialog.dialog(opts);
|
@@ -4228,9 +1618,9 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.dialog'])
|
|
4228
1618
|
$parse(attrs.close)(scope);
|
4229
1619
|
};
|
4230
1620
|
} else {
|
4231
|
-
setClosed = function() {
|
1621
|
+
setClosed = function() {
|
4232
1622
|
if (angular.isFunction($parse(shownExpr).assign)) {
|
4233
|
-
$parse(shownExpr).assign(scope, false);
|
1623
|
+
$parse(shownExpr).assign(scope, false);
|
4234
1624
|
}
|
4235
1625
|
};
|
4236
1626
|
}
|
@@ -4252,25 +1642,41 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.dialog'])
|
|
4252
1642
|
}]);
|
4253
1643
|
angular.module('ui.bootstrap.pagination', [])
|
4254
1644
|
|
4255
|
-
.controller('PaginationController', ['$scope', function (scope) {
|
1645
|
+
.controller('PaginationController', ['$scope', '$interpolate', function ($scope, $interpolate) {
|
1646
|
+
|
1647
|
+
this.currentPage = 1;
|
4256
1648
|
|
4257
|
-
|
4258
|
-
return
|
1649
|
+
this.noPrevious = function() {
|
1650
|
+
return this.currentPage === 1;
|
4259
1651
|
};
|
4260
|
-
|
4261
|
-
return
|
1652
|
+
this.noNext = function() {
|
1653
|
+
return this.currentPage === $scope.numPages;
|
4262
1654
|
};
|
4263
1655
|
|
4264
|
-
|
4265
|
-
return
|
1656
|
+
this.isActive = function(page) {
|
1657
|
+
return this.currentPage === page;
|
4266
1658
|
};
|
4267
1659
|
|
4268
|
-
|
4269
|
-
|
4270
|
-
|
4271
|
-
|
1660
|
+
this.reset = function() {
|
1661
|
+
$scope.pages = [];
|
1662
|
+
this.currentPage = parseInt($scope.currentPage, 10);
|
1663
|
+
|
1664
|
+
if ( this.currentPage > $scope.numPages ) {
|
1665
|
+
$scope.selectPage($scope.numPages);
|
1666
|
+
}
|
1667
|
+
};
|
1668
|
+
|
1669
|
+
var self = this;
|
1670
|
+
$scope.selectPage = function(page) {
|
1671
|
+
if ( ! self.isActive(page) && page > 0 && page <= $scope.numPages) {
|
1672
|
+
$scope.currentPage = page;
|
1673
|
+
$scope.onSelectPage({ page: page });
|
4272
1674
|
}
|
4273
1675
|
};
|
1676
|
+
|
1677
|
+
this.getAttributeValue = function(attribute, defaultValue, interpolate) {
|
1678
|
+
return angular.isDefined(attribute) ? (interpolate ? $interpolate(attribute)($scope.$parent) : $scope.$parent.$eval(attribute)) : defaultValue;
|
1679
|
+
};
|
4274
1680
|
}])
|
4275
1681
|
|
4276
1682
|
.constant('paginationConfig', {
|
@@ -4283,7 +1689,7 @@ angular.module('ui.bootstrap.pagination', [])
|
|
4283
1689
|
rotate: true
|
4284
1690
|
})
|
4285
1691
|
|
4286
|
-
.directive('pagination', ['paginationConfig', function(
|
1692
|
+
.directive('pagination', ['paginationConfig', function(config) {
|
4287
1693
|
return {
|
4288
1694
|
restrict: 'EA',
|
4289
1695
|
scope: {
|
@@ -4295,16 +1701,16 @@ angular.module('ui.bootstrap.pagination', [])
|
|
4295
1701
|
controller: 'PaginationController',
|
4296
1702
|
templateUrl: 'template/pagination/pagination.html',
|
4297
1703
|
replace: true,
|
4298
|
-
link: function(scope, element, attrs) {
|
1704
|
+
link: function(scope, element, attrs, paginationCtrl) {
|
4299
1705
|
|
4300
1706
|
// Setup configuration parameters
|
4301
|
-
var boundaryLinks =
|
4302
|
-
|
4303
|
-
|
4304
|
-
|
4305
|
-
|
4306
|
-
|
4307
|
-
|
1707
|
+
var boundaryLinks = paginationCtrl.getAttributeValue(attrs.boundaryLinks, config.boundaryLinks ),
|
1708
|
+
directionLinks = paginationCtrl.getAttributeValue(attrs.directionLinks, config.directionLinks ),
|
1709
|
+
firstText = paginationCtrl.getAttributeValue(attrs.firstText, config.firstText, true),
|
1710
|
+
previousText = paginationCtrl.getAttributeValue(attrs.previousText, config.previousText, true),
|
1711
|
+
nextText = paginationCtrl.getAttributeValue(attrs.nextText, config.nextText, true),
|
1712
|
+
lastText = paginationCtrl.getAttributeValue(attrs.lastText, config.lastText, true),
|
1713
|
+
rotate = paginationCtrl.getAttributeValue(attrs.rotate, config.rotate);
|
4308
1714
|
|
4309
1715
|
// Create page object used in template
|
4310
1716
|
function makePage(number, text, isActive, isDisabled) {
|
@@ -4317,7 +1723,7 @@ angular.module('ui.bootstrap.pagination', [])
|
|
4317
1723
|
}
|
4318
1724
|
|
4319
1725
|
scope.$watch('numPages + currentPage + maxSize', function() {
|
4320
|
-
|
1726
|
+
paginationCtrl.reset();
|
4321
1727
|
|
4322
1728
|
// Default page limits
|
4323
1729
|
var startPage = 1, endPage = scope.numPages;
|
@@ -4327,7 +1733,7 @@ angular.module('ui.bootstrap.pagination', [])
|
|
4327
1733
|
if ( isMaxSized ) {
|
4328
1734
|
if ( rotate ) {
|
4329
1735
|
// Current page is displayed in the middle of the visible ones
|
4330
|
-
startPage = Math.max(
|
1736
|
+
startPage = Math.max(paginationCtrl.currentPage - Math.floor(scope.maxSize/2), 1);
|
4331
1737
|
endPage = startPage + scope.maxSize - 1;
|
4332
1738
|
|
4333
1739
|
// Adjust if limit is exceeded
|
@@ -4337,7 +1743,7 @@ angular.module('ui.bootstrap.pagination', [])
|
|
4337
1743
|
}
|
4338
1744
|
} else {
|
4339
1745
|
// Visible pages are paginated with maxSize
|
4340
|
-
startPage = ((Math.ceil(
|
1746
|
+
startPage = ((Math.ceil(paginationCtrl.currentPage / scope.maxSize) - 1) * scope.maxSize) + 1;
|
4341
1747
|
|
4342
1748
|
// Adjust last page if limit is exceeded
|
4343
1749
|
endPage = Math.min(startPage + scope.maxSize - 1, scope.numPages);
|
@@ -4346,7 +1752,7 @@ angular.module('ui.bootstrap.pagination', [])
|
|
4346
1752
|
|
4347
1753
|
// Add page number links
|
4348
1754
|
for (var number = startPage; number <= endPage; number++) {
|
4349
|
-
var page = makePage(number, number,
|
1755
|
+
var page = makePage(number, number, paginationCtrl.isActive(number), false);
|
4350
1756
|
scope.pages.push(page);
|
4351
1757
|
}
|
4352
1758
|
|
@@ -4365,25 +1771,21 @@ angular.module('ui.bootstrap.pagination', [])
|
|
4365
1771
|
|
4366
1772
|
// Add previous & next links
|
4367
1773
|
if (directionLinks) {
|
4368
|
-
var previousPage = makePage(
|
1774
|
+
var previousPage = makePage(paginationCtrl.currentPage - 1, previousText, false, paginationCtrl.noPrevious());
|
4369
1775
|
scope.pages.unshift(previousPage);
|
4370
1776
|
|
4371
|
-
var nextPage = makePage(
|
1777
|
+
var nextPage = makePage(paginationCtrl.currentPage + 1, nextText, false, paginationCtrl.noNext());
|
4372
1778
|
scope.pages.push(nextPage);
|
4373
1779
|
}
|
4374
1780
|
|
4375
1781
|
// Add first & last links
|
4376
1782
|
if (boundaryLinks) {
|
4377
|
-
var firstPage = makePage(1, firstText, false,
|
1783
|
+
var firstPage = makePage(1, firstText, false, paginationCtrl.noPrevious());
|
4378
1784
|
scope.pages.unshift(firstPage);
|
4379
1785
|
|
4380
|
-
var lastPage = makePage(scope.numPages, lastText, false,
|
1786
|
+
var lastPage = makePage(scope.numPages, lastText, false, paginationCtrl.noNext());
|
4381
1787
|
scope.pages.push(lastPage);
|
4382
1788
|
}
|
4383
|
-
|
4384
|
-
if ( scope.currentPage > scope.numPages ) {
|
4385
|
-
scope.selectPage(scope.numPages);
|
4386
|
-
}
|
4387
1789
|
});
|
4388
1790
|
}
|
4389
1791
|
};
|
@@ -4409,9 +1811,9 @@ angular.module('ui.bootstrap.pagination', [])
|
|
4409
1811
|
link: function(scope, element, attrs, paginationCtrl) {
|
4410
1812
|
|
4411
1813
|
// Setup configuration parameters
|
4412
|
-
var previousText =
|
4413
|
-
|
4414
|
-
|
1814
|
+
var previousText = paginationCtrl.getAttributeValue(attrs.previousText, config.previousText, true),
|
1815
|
+
nextText = paginationCtrl.getAttributeValue(attrs.nextText, config.nextText, true),
|
1816
|
+
align = paginationCtrl.getAttributeValue(attrs.align, config.align);
|
4415
1817
|
|
4416
1818
|
// Create page object used in template
|
4417
1819
|
function makePage(number, text, isDisabled, isPrevious, isNext) {
|
@@ -4425,117 +1827,19 @@ angular.module('ui.bootstrap.pagination', [])
|
|
4425
1827
|
}
|
4426
1828
|
|
4427
1829
|
scope.$watch('numPages + currentPage', function() {
|
4428
|
-
|
1830
|
+
paginationCtrl.reset();
|
4429
1831
|
|
4430
1832
|
// Add previous & next links
|
4431
|
-
var previousPage = makePage(
|
1833
|
+
var previousPage = makePage(paginationCtrl.currentPage - 1, previousText, paginationCtrl.noPrevious(), true, false);
|
4432
1834
|
scope.pages.unshift(previousPage);
|
4433
1835
|
|
4434
|
-
var nextPage = makePage(
|
1836
|
+
var nextPage = makePage(paginationCtrl.currentPage + 1, nextText, paginationCtrl.noNext(), false, true);
|
4435
1837
|
scope.pages.push(nextPage);
|
4436
|
-
|
4437
|
-
if ( scope.currentPage > scope.numPages ) {
|
4438
|
-
scope.selectPage(scope.numPages);
|
4439
|
-
}
|
4440
1838
|
});
|
4441
1839
|
}
|
4442
1840
|
};
|
4443
1841
|
}]);
|
4444
1842
|
|
4445
|
-
angular.module('ui.bootstrap.position', [])
|
4446
|
-
|
4447
|
-
/**
|
4448
|
-
* A set of utility methods that can be use to retrieve position of DOM elements.
|
4449
|
-
* It is meant to be used where we need to absolute-position DOM elements in
|
4450
|
-
* relation to other, existing elements (this is the case for tooltips, popovers,
|
4451
|
-
* typeahead suggestions etc.).
|
4452
|
-
*/
|
4453
|
-
.factory('$position', ['$document', '$window', function ($document, $window) {
|
4454
|
-
|
4455
|
-
var mouseX, mouseY;
|
4456
|
-
|
4457
|
-
$document.bind('mousemove', function mouseMoved(event) {
|
4458
|
-
mouseX = event.pageX;
|
4459
|
-
mouseY = event.pageY;
|
4460
|
-
});
|
4461
|
-
|
4462
|
-
function getStyle(el, cssprop) {
|
4463
|
-
if (el.currentStyle) { //IE
|
4464
|
-
return el.currentStyle[cssprop];
|
4465
|
-
} else if ($window.getComputedStyle) {
|
4466
|
-
return $window.getComputedStyle(el)[cssprop];
|
4467
|
-
}
|
4468
|
-
// finally try and get inline style
|
4469
|
-
return el.style[cssprop];
|
4470
|
-
}
|
4471
|
-
|
4472
|
-
/**
|
4473
|
-
* Checks if a given element is statically positioned
|
4474
|
-
* @param element - raw DOM element
|
4475
|
-
*/
|
4476
|
-
function isStaticPositioned(element) {
|
4477
|
-
return (getStyle(element, "position") || 'static' ) === 'static';
|
4478
|
-
}
|
4479
|
-
|
4480
|
-
/**
|
4481
|
-
* returns the closest, non-statically positioned parentOffset of a given element
|
4482
|
-
* @param element
|
4483
|
-
*/
|
4484
|
-
var parentOffsetEl = function (element) {
|
4485
|
-
var docDomEl = $document[0];
|
4486
|
-
var offsetParent = element.offsetParent || docDomEl;
|
4487
|
-
while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
|
4488
|
-
offsetParent = offsetParent.offsetParent;
|
4489
|
-
}
|
4490
|
-
return offsetParent || docDomEl;
|
4491
|
-
};
|
4492
|
-
|
4493
|
-
return {
|
4494
|
-
/**
|
4495
|
-
* Provides read-only equivalent of jQuery's position function:
|
4496
|
-
* http://api.jquery.com/position/
|
4497
|
-
*/
|
4498
|
-
position: function (element) {
|
4499
|
-
var elBCR = this.offset(element);
|
4500
|
-
var offsetParentBCR = { top: 0, left: 0 };
|
4501
|
-
var offsetParentEl = parentOffsetEl(element[0]);
|
4502
|
-
if (offsetParentEl != $document[0]) {
|
4503
|
-
offsetParentBCR = this.offset(angular.element(offsetParentEl));
|
4504
|
-
offsetParentBCR.top += offsetParentEl.clientTop;
|
4505
|
-
offsetParentBCR.left += offsetParentEl.clientLeft;
|
4506
|
-
}
|
4507
|
-
|
4508
|
-
return {
|
4509
|
-
width: element.prop('offsetWidth'),
|
4510
|
-
height: element.prop('offsetHeight'),
|
4511
|
-
top: elBCR.top - offsetParentBCR.top,
|
4512
|
-
left: elBCR.left - offsetParentBCR.left
|
4513
|
-
};
|
4514
|
-
},
|
4515
|
-
|
4516
|
-
/**
|
4517
|
-
* Provides read-only equivalent of jQuery's offset function:
|
4518
|
-
* http://api.jquery.com/offset/
|
4519
|
-
*/
|
4520
|
-
offset: function (element) {
|
4521
|
-
var boundingClientRect = element[0].getBoundingClientRect();
|
4522
|
-
return {
|
4523
|
-
width: element.prop('offsetWidth'),
|
4524
|
-
height: element.prop('offsetHeight'),
|
4525
|
-
top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop),
|
4526
|
-
left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft)
|
4527
|
-
};
|
4528
|
-
},
|
4529
|
-
|
4530
|
-
/**
|
4531
|
-
* Provides the coordinates of the mouse
|
4532
|
-
*/
|
4533
|
-
mouse: function () {
|
4534
|
-
return {x: mouseX, y: mouseY};
|
4535
|
-
}
|
4536
|
-
};
|
4537
|
-
}]);
|
4538
|
-
|
4539
1843
|
/**
|
4540
1844
|
* The following features are still outstanding: animation as a
|
4541
1845
|
* function, placement as a function, inside, support for more triggers than
|
@@ -4564,7 +1868,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4564
1868
|
|
4565
1869
|
// The options specified to the provider globally.
|
4566
1870
|
var globalOptions = {};
|
4567
|
-
|
1871
|
+
|
4568
1872
|
/**
|
4569
1873
|
* `options({})` allows global configuration of all tooltips in the
|
4570
1874
|
* application.
|
@@ -4574,9 +1878,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4574
1878
|
* $tooltipProvider.options( { placement: 'left' } );
|
4575
1879
|
* });
|
4576
1880
|
*/
|
4577
|
-
|
4578
|
-
|
4579
|
-
|
1881
|
+
this.options = function( value ) {
|
1882
|
+
angular.extend( globalOptions, value );
|
1883
|
+
};
|
4580
1884
|
|
4581
1885
|
/**
|
4582
1886
|
* This allows you to extend the set of trigger mappings available. E.g.:
|
@@ -4620,16 +1924,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4620
1924
|
* undefined; otherwise, it uses the `triggerMap` value of the show
|
4621
1925
|
* trigger; else it will just use the show trigger.
|
4622
1926
|
*/
|
4623
|
-
function
|
4624
|
-
var show
|
4625
|
-
|
4626
|
-
show = trigger || options.trigger || defaultTriggerShow;
|
4627
|
-
if ( angular.isDefined ( options.trigger ) ) {
|
4628
|
-
hide = triggerMap[options.trigger] || show;
|
4629
|
-
} else {
|
4630
|
-
hide = triggerMap[show] || show;
|
4631
|
-
}
|
4632
|
-
|
1927
|
+
function getTriggers ( trigger ) {
|
1928
|
+
var show = trigger || options.trigger || defaultTriggerShow;
|
1929
|
+
var hide = triggerMap[show] || show;
|
4633
1930
|
return {
|
4634
1931
|
show: show,
|
4635
1932
|
hide: hide
|
@@ -4637,11 +1934,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4637
1934
|
}
|
4638
1935
|
|
4639
1936
|
var directiveName = snake_case( type );
|
4640
|
-
var triggers = setTriggers( undefined );
|
4641
1937
|
|
4642
1938
|
var startSym = $interpolate.startSymbol();
|
4643
1939
|
var endSym = $interpolate.endSymbol();
|
4644
|
-
var template =
|
1940
|
+
var template =
|
4645
1941
|
'<'+ directiveName +'-popup '+
|
4646
1942
|
'title="'+startSym+'tt_title'+endSym+'" '+
|
4647
1943
|
'content="'+startSym+'tt_content'+endSym+'" '+
|
@@ -4660,6 +1956,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4660
1956
|
var popupTimeout;
|
4661
1957
|
var $body;
|
4662
1958
|
var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;
|
1959
|
+
var triggers = getTriggers( undefined );
|
1960
|
+
var hasRegisteredTriggers = false;
|
4663
1961
|
|
4664
1962
|
// By default, the tooltip is not open.
|
4665
1963
|
// TODO add ability to start tooltip opened
|
@@ -4672,7 +1970,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4672
1970
|
hideTooltipBind();
|
4673
1971
|
}
|
4674
1972
|
}
|
4675
|
-
|
1973
|
+
|
4676
1974
|
// Show the tooltip with delay if specified, otherwise show it immediately
|
4677
1975
|
function showTooltipBind() {
|
4678
1976
|
if ( scope.tt_popupDelay ) {
|
@@ -4687,7 +1985,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4687
1985
|
hide();
|
4688
1986
|
});
|
4689
1987
|
}
|
4690
|
-
|
1988
|
+
|
4691
1989
|
// Show the tooltip popup element.
|
4692
1990
|
function show() {
|
4693
1991
|
var position,
|
@@ -4705,11 +2003,11 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4705
2003
|
if ( transitionTimeout ) {
|
4706
2004
|
$timeout.cancel( transitionTimeout );
|
4707
2005
|
}
|
4708
|
-
|
2006
|
+
|
4709
2007
|
// Set the initial positioning.
|
4710
2008
|
tooltip.css({ top: 0, left: 0, display: 'block' });
|
4711
|
-
|
4712
|
-
// Now we add it to the DOM because need some info about it. But it's not
|
2009
|
+
|
2010
|
+
// Now we add it to the DOM because need some info about it. But it's not
|
4713
2011
|
// visible yet anyway.
|
4714
2012
|
if ( appendToBody ) {
|
4715
2013
|
$body = $body || $document.find( 'body' );
|
@@ -4719,12 +2017,12 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4719
2017
|
}
|
4720
2018
|
|
4721
2019
|
// Get the position of the directive element.
|
4722
|
-
position =
|
2020
|
+
position = appendToBody ? $position.offset( element ) : $position.position( element );
|
4723
2021
|
|
4724
2022
|
// Get the height and width of the tooltip so we can center it.
|
4725
2023
|
ttWidth = tooltip.prop( 'offsetWidth' );
|
4726
2024
|
ttHeight = tooltip.prop( 'offsetHeight' );
|
4727
|
-
|
2025
|
+
|
4728
2026
|
// Calculate the tooltip's top and left coordinates to center it with
|
4729
2027
|
// this directive.
|
4730
2028
|
switch ( scope.tt_placement ) {
|
@@ -4766,11 +2064,11 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4766
2064
|
|
4767
2065
|
// Now set the calculated positioning.
|
4768
2066
|
tooltip.css( ttPosition );
|
4769
|
-
|
2067
|
+
|
4770
2068
|
// And show the tooltip.
|
4771
2069
|
scope.tt_isOpen = true;
|
4772
2070
|
}
|
4773
|
-
|
2071
|
+
|
4774
2072
|
// Hide the tooltip popup element.
|
4775
2073
|
function hide() {
|
4776
2074
|
// First things first: we don't show it anymore.
|
@@ -4778,8 +2076,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4778
2076
|
|
4779
2077
|
//if tooltip is going to be shown after delay, we must cancel this
|
4780
2078
|
$timeout.cancel( popupTimeout );
|
4781
|
-
|
4782
|
-
// And now we remove it from the DOM. However, if we have animation, we
|
2079
|
+
|
2080
|
+
// And now we remove it from the DOM. However, if we have animation, we
|
4783
2081
|
// need to wait for it to expire beforehand.
|
4784
2082
|
// FIXME: this is a placeholder for a port of the transitions library.
|
4785
2083
|
if ( angular.isDefined( scope.tt_animation ) && scope.tt_animation() ) {
|
@@ -4814,10 +2112,13 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4814
2112
|
});
|
4815
2113
|
|
4816
2114
|
attrs.$observe( prefix+'Trigger', function ( val ) {
|
4817
|
-
element.unbind( triggers.show );
|
4818
|
-
element.unbind( triggers.hide );
|
4819
2115
|
|
4820
|
-
|
2116
|
+
if (hasRegisteredTriggers) {
|
2117
|
+
element.unbind( triggers.show, showTooltipBind );
|
2118
|
+
element.unbind( triggers.hide, hideTooltipBind );
|
2119
|
+
}
|
2120
|
+
|
2121
|
+
triggers = getTriggers( val );
|
4821
2122
|
|
4822
2123
|
if ( triggers.show === triggers.hide ) {
|
4823
2124
|
element.bind( triggers.show, toggleTooltipBind );
|
@@ -4825,6 +2126,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
|
4825
2126
|
element.bind( triggers.show, showTooltipBind );
|
4826
2127
|
element.bind( triggers.hide, hideTooltipBind );
|
4827
2128
|
}
|
2129
|
+
|
2130
|
+
hasRegisteredTriggers = true;
|
4828
2131
|
});
|
4829
2132
|
|
4830
2133
|
attrs.$observe( prefix+'AppendToBody', function ( val ) {
|
@@ -5017,13 +2320,15 @@ angular.module('ui.bootstrap.rating', [])
|
|
5017
2320
|
return {
|
5018
2321
|
restrict: 'EA',
|
5019
2322
|
scope: {
|
5020
|
-
value: '='
|
2323
|
+
value: '=',
|
2324
|
+
onHover: '&',
|
2325
|
+
onLeave: '&'
|
5021
2326
|
},
|
5022
2327
|
templateUrl: 'template/rating/rating.html',
|
5023
2328
|
replace: true,
|
5024
2329
|
link: function(scope, element, attrs) {
|
5025
2330
|
|
5026
|
-
var maxRange = angular.isDefined(attrs.max) ? scope.$eval(attrs.max) : ratingConfig.max;
|
2331
|
+
var maxRange = angular.isDefined(attrs.max) ? scope.$parent.$eval(attrs.max) : ratingConfig.max;
|
5027
2332
|
|
5028
2333
|
scope.range = [];
|
5029
2334
|
for (var i = 1; i <= maxRange; i++) {
|
@@ -5040,10 +2345,12 @@ angular.module('ui.bootstrap.rating', [])
|
|
5040
2345
|
if ( ! scope.readonly ) {
|
5041
2346
|
scope.val = value;
|
5042
2347
|
}
|
2348
|
+
scope.onHover({value: value});
|
5043
2349
|
};
|
5044
2350
|
|
5045
2351
|
scope.reset = function() {
|
5046
2352
|
scope.val = angular.copy(scope.value);
|
2353
|
+
scope.onLeave();
|
5047
2354
|
};
|
5048
2355
|
scope.reset();
|
5049
2356
|
|
@@ -5079,6 +2386,7 @@ angular.module('ui.bootstrap.tabs', [])
|
|
5079
2386
|
|
5080
2387
|
.controller('TabsetController', ['$scope', '$element',
|
5081
2388
|
function TabsetCtrl($scope, $element) {
|
2389
|
+
|
5082
2390
|
var ctrl = this,
|
5083
2391
|
tabs = ctrl.tabs = $scope.tabs = [];
|
5084
2392
|
|
@@ -5091,7 +2399,7 @@ function TabsetCtrl($scope, $element) {
|
|
5091
2399
|
|
5092
2400
|
ctrl.addTab = function addTab(tab) {
|
5093
2401
|
tabs.push(tab);
|
5094
|
-
if (tabs.length
|
2402
|
+
if (tabs.length === 1 || tab.active) {
|
5095
2403
|
ctrl.select(tab);
|
5096
2404
|
}
|
5097
2405
|
};
|
@@ -5117,6 +2425,8 @@ function TabsetCtrl($scope, $element) {
|
|
5117
2425
|
* Tabset is the outer container for the tabs directive
|
5118
2426
|
*
|
5119
2427
|
* @param {boolean=} vertical Whether or not to use vertical styling for the tabs.
|
2428
|
+
* @param {string=} direction What direction the tabs should be rendered. Available:
|
2429
|
+
* 'right', 'left', 'below'.
|
5120
2430
|
*
|
5121
2431
|
* @example
|
5122
2432
|
<example module="ui.bootstrap">
|
@@ -5137,12 +2447,20 @@ function TabsetCtrl($scope, $element) {
|
|
5137
2447
|
return {
|
5138
2448
|
restrict: 'EA',
|
5139
2449
|
transclude: true,
|
2450
|
+
replace: true,
|
2451
|
+
require: '^tabset',
|
5140
2452
|
scope: {},
|
5141
2453
|
controller: 'TabsetController',
|
5142
2454
|
templateUrl: 'template/tabs/tabset.html',
|
5143
|
-
|
5144
|
-
scope
|
5145
|
-
|
2455
|
+
compile: function(elm, attrs, transclude) {
|
2456
|
+
return function(scope, element, attrs, tabsetCtrl) {
|
2457
|
+
scope.vertical = angular.isDefined(attrs.vertical) ? scope.$eval(attrs.vertical) : false;
|
2458
|
+
scope.type = angular.isDefined(attrs.type) ? scope.$parent.$eval(attrs.type) : 'tabs';
|
2459
|
+
scope.direction = angular.isDefined(attrs.direction) ? scope.$parent.$eval(attrs.direction) : 'top';
|
2460
|
+
scope.tabsAbove = (scope.direction != 'below');
|
2461
|
+
tabsetCtrl.$scope = scope;
|
2462
|
+
tabsetCtrl.$transcludeFn = transclude;
|
2463
|
+
};
|
5146
2464
|
}
|
5147
2465
|
};
|
5148
2466
|
})
|
@@ -5237,8 +2555,9 @@ function($parse, $http, $templateCache, $compile) {
|
|
5237
2555
|
transclude: true,
|
5238
2556
|
scope: {
|
5239
2557
|
heading: '@',
|
5240
|
-
onSelect: '&select' //This callback is called in contentHeadingTransclude
|
2558
|
+
onSelect: '&select', //This callback is called in contentHeadingTransclude
|
5241
2559
|
//once it inserts the tab's content into the dom
|
2560
|
+
onDeselect: '&deselect'
|
5242
2561
|
},
|
5243
2562
|
controller: function() {
|
5244
2563
|
//Empty controller so other directives can require being 'under' a tab
|
@@ -5246,17 +2565,13 @@ function($parse, $http, $templateCache, $compile) {
|
|
5246
2565
|
compile: function(elm, attrs, transclude) {
|
5247
2566
|
return function postLink(scope, elm, attrs, tabsetCtrl) {
|
5248
2567
|
var getActive, setActive;
|
5249
|
-
scope.active = false; // default value
|
5250
2568
|
if (attrs.active) {
|
5251
2569
|
getActive = $parse(attrs.active);
|
5252
2570
|
setActive = getActive.assign;
|
5253
2571
|
scope.$parent.$watch(getActive, function updateActive(value) {
|
5254
|
-
|
5255
|
-
setActive(scope.$parent, false); // Prevent active assignment
|
5256
|
-
} else {
|
5257
|
-
scope.active = !!value;
|
5258
|
-
}
|
2572
|
+
scope.active = !!value;
|
5259
2573
|
});
|
2574
|
+
scope.active = getActive(scope.$parent);
|
5260
2575
|
} else {
|
5261
2576
|
setActive = getActive = angular.noop;
|
5262
2577
|
}
|
@@ -5266,6 +2581,8 @@ function($parse, $http, $templateCache, $compile) {
|
|
5266
2581
|
if (active) {
|
5267
2582
|
tabsetCtrl.select(scope);
|
5268
2583
|
scope.onSelect();
|
2584
|
+
} else {
|
2585
|
+
scope.onDeselect();
|
5269
2586
|
}
|
5270
2587
|
});
|
5271
2588
|
|
@@ -5286,42 +2603,14 @@ function($parse, $http, $templateCache, $compile) {
|
|
5286
2603
|
scope.$on('$destroy', function() {
|
5287
2604
|
tabsetCtrl.removeTab(scope);
|
5288
2605
|
});
|
5289
|
-
//If the tabset sets this tab to active, set the parent scope's active
|
5290
|
-
//binding too. We do this so the watch for the parent's initial active
|
5291
|
-
//value won't overwrite what is initially set by the tabset
|
5292
2606
|
if (scope.active) {
|
5293
2607
|
setActive(scope.$parent, true);
|
5294
2608
|
}
|
5295
2609
|
|
5296
|
-
|
5297
|
-
//
|
5298
|
-
//
|
5299
|
-
|
5300
|
-
//Look at every element in the clone collection. If it's tab-heading,
|
5301
|
-
//mark it as that. If it's not tab-heading, mark it as tab contents
|
5302
|
-
var contents = [], heading;
|
5303
|
-
angular.forEach(clone, function(el) {
|
5304
|
-
//See if it's a tab-heading attr or element directive
|
5305
|
-
//First make sure it's a normal element, one that has a tagName
|
5306
|
-
if (el.tagName &&
|
5307
|
-
(el.hasAttribute("tab-heading") ||
|
5308
|
-
el.hasAttribute("data-tab-heading") ||
|
5309
|
-
el.tagName.toLowerCase() == "tab-heading" ||
|
5310
|
-
el.tagName.toLowerCase() == "data-tab-heading"
|
5311
|
-
)) {
|
5312
|
-
heading = el;
|
5313
|
-
} else {
|
5314
|
-
contents.push(el);
|
5315
|
-
}
|
5316
|
-
});
|
5317
|
-
//Share what we found on the scope, so our tabHeadingTransclude and
|
5318
|
-
//tabContentTransclude directives can find out what the heading and
|
5319
|
-
//contents are.
|
5320
|
-
if (heading) {
|
5321
|
-
scope.headingElement = angular.element(heading);
|
5322
|
-
}
|
5323
|
-
scope.contentElement = angular.element(contents);
|
5324
|
-
});
|
2610
|
+
|
2611
|
+
//We need to transclude later, once the content container is ready.
|
2612
|
+
//when this link happens, we're inside a tab heading.
|
2613
|
+
scope.$transcludeFn = transclude;
|
5325
2614
|
};
|
5326
2615
|
}
|
5327
2616
|
};
|
@@ -5342,21 +2631,56 @@ function($parse, $http, $templateCache, $compile) {
|
|
5342
2631
|
};
|
5343
2632
|
}])
|
5344
2633
|
|
5345
|
-
.directive('tabContentTransclude', ['$parse', function($parse) {
|
2634
|
+
.directive('tabContentTransclude', ['$compile', '$parse', function($compile, $parse) {
|
5346
2635
|
return {
|
5347
2636
|
restrict: 'A',
|
5348
2637
|
require: '^tabset',
|
5349
|
-
link: function(scope, elm, attrs
|
5350
|
-
scope.$
|
5351
|
-
|
5352
|
-
|
5353
|
-
|
5354
|
-
|
2638
|
+
link: function(scope, elm, attrs) {
|
2639
|
+
var tab = scope.$eval(attrs.tabContentTransclude);
|
2640
|
+
|
2641
|
+
//Now our tab is ready to be transcluded: both the tab heading area
|
2642
|
+
//and the tab content area are loaded. Transclude 'em both.
|
2643
|
+
tab.$transcludeFn(tab.$parent, function(contents) {
|
2644
|
+
angular.forEach(contents, function(node) {
|
2645
|
+
if (isTabHeading(node)) {
|
2646
|
+
//Let tabHeadingTransclude know.
|
2647
|
+
tab.headingElement = node;
|
2648
|
+
} else {
|
2649
|
+
elm.append(node);
|
2650
|
+
}
|
2651
|
+
});
|
5355
2652
|
});
|
5356
2653
|
}
|
5357
2654
|
};
|
2655
|
+
function isTabHeading(node) {
|
2656
|
+
return node.tagName && (
|
2657
|
+
node.hasAttribute('tab-heading') ||
|
2658
|
+
node.hasAttribute('data-tab-heading') ||
|
2659
|
+
node.tagName.toLowerCase() === 'tab-heading' ||
|
2660
|
+
node.tagName.toLowerCase() === 'data-tab-heading'
|
2661
|
+
);
|
2662
|
+
}
|
5358
2663
|
}])
|
5359
2664
|
|
2665
|
+
.directive('tabsetTitles', function($http) {
|
2666
|
+
return {
|
2667
|
+
restrict: 'A',
|
2668
|
+
require: '^tabset',
|
2669
|
+
templateUrl: 'template/tabs/tabset-titles.html',
|
2670
|
+
replace: true,
|
2671
|
+
link: function(scope, elm, attrs, tabsetCtrl) {
|
2672
|
+
if (!scope.$eval(attrs.tabsetTitles)) {
|
2673
|
+
elm.remove();
|
2674
|
+
} else {
|
2675
|
+
//now that tabs location has been decided, transclude the tab titles in
|
2676
|
+
tabsetCtrl.$transcludeFn(tabsetCtrl.$scope.$parent, function(node) {
|
2677
|
+
elm.append(node);
|
2678
|
+
});
|
2679
|
+
}
|
2680
|
+
}
|
2681
|
+
};
|
2682
|
+
})
|
2683
|
+
|
5360
2684
|
;
|
5361
2685
|
|
5362
2686
|
|
@@ -5452,20 +2776,22 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
5452
2776
|
// Respond on mousewheel spin
|
5453
2777
|
var mousewheel = (angular.isDefined(attrs.mousewheel)) ? scope.$eval(attrs.mousewheel) : timepickerConfig.mousewheel;
|
5454
2778
|
if ( mousewheel ) {
|
5455
|
-
|
2779
|
+
|
5456
2780
|
var isScrollingUp = function(e) {
|
5457
2781
|
if (e.originalEvent) {
|
5458
2782
|
e = e.originalEvent;
|
5459
2783
|
}
|
5460
|
-
|
2784
|
+
//pick correct delta variable depending on event
|
2785
|
+
var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY;
|
2786
|
+
return (e.detail || delta > 0);
|
5461
2787
|
};
|
5462
|
-
|
5463
|
-
hoursInputEl.bind('mousewheel', function(e) {
|
2788
|
+
|
2789
|
+
hoursInputEl.bind('mousewheel wheel', function(e) {
|
5464
2790
|
scope.$apply( (isScrollingUp(e)) ? scope.incrementHours() : scope.decrementHours() );
|
5465
2791
|
e.preventDefault();
|
5466
2792
|
});
|
5467
2793
|
|
5468
|
-
minutesInputEl.bind('mousewheel', function(e) {
|
2794
|
+
minutesInputEl.bind('mousewheel wheel', function(e) {
|
5469
2795
|
scope.$apply( (isScrollingUp(e)) ? scope.incrementMinutes() : scope.decrementMinutes() );
|
5470
2796
|
e.preventDefault();
|
5471
2797
|
});
|
@@ -5552,10 +2878,8 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
5552
2878
|
|
5553
2879
|
function addMinutes( minutes ) {
|
5554
2880
|
var dt = new Date( selected.getTime() + minutes * 60000 );
|
5555
|
-
|
5556
|
-
|
5557
|
-
}
|
5558
|
-
selected.setTime( dt.getTime() );
|
2881
|
+
selected.setHours( dt.getHours() );
|
2882
|
+
selected.setMinutes( dt.getMinutes() );
|
5559
2883
|
scope.model = new Date( selected );
|
5560
2884
|
}
|
5561
2885
|
|
@@ -5577,6 +2901,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
5577
2901
|
}
|
5578
2902
|
};
|
5579
2903
|
}]);
|
2904
|
+
|
5580
2905
|
angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
|
5581
2906
|
|
5582
2907
|
/**
|
@@ -5616,7 +2941,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
|
|
5616
2941
|
require:'ngModel',
|
5617
2942
|
link:function (originalScope, element, attrs, modelCtrl) {
|
5618
2943
|
|
5619
|
-
|
2944
|
+
//SUPPORTED ATTRIBUTES (OPTIONS)
|
5620
2945
|
|
5621
2946
|
//minimal no of characters that needs to be entered before typeahead kicks-in
|
5622
2947
|
var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
|
@@ -5624,16 +2949,26 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
|
|
5624
2949
|
//minimal wait time after last character typed before typehead kicks-in
|
5625
2950
|
var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
|
5626
2951
|
|
5627
|
-
//expressions used by typeahead
|
5628
|
-
var parserResult = typeaheadParser.parse(attrs.typeahead);
|
5629
|
-
|
5630
2952
|
//should it restrict model values to the ones selected from the popup only?
|
5631
2953
|
var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
|
5632
2954
|
|
2955
|
+
//binding to a variable that indicates if matches are being retrieved asynchronously
|
5633
2956
|
var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
|
5634
2957
|
|
2958
|
+
//a callback executed when a match is selected
|
5635
2959
|
var onSelectCallback = $parse(attrs.typeaheadOnSelect);
|
5636
2960
|
|
2961
|
+
var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
|
2962
|
+
|
2963
|
+
//INTERNAL VARIABLES
|
2964
|
+
|
2965
|
+
//model setter executed upon match selection
|
2966
|
+
var $setModelValue = $parse(attrs.ngModel).assign;
|
2967
|
+
|
2968
|
+
//expressions used by typeahead
|
2969
|
+
var parserResult = typeaheadParser.parse(attrs.typeahead);
|
2970
|
+
|
2971
|
+
|
5637
2972
|
//pop-up element used to display matches
|
5638
2973
|
var popUpEl = angular.element('<typeahead-popup></typeahead-popup>');
|
5639
2974
|
popUpEl.attr({
|
@@ -5643,6 +2978,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
|
|
5643
2978
|
query: 'query',
|
5644
2979
|
position: 'position'
|
5645
2980
|
});
|
2981
|
+
//custom item template
|
2982
|
+
if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
|
2983
|
+
popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
|
2984
|
+
}
|
5646
2985
|
|
5647
2986
|
//create a child scope for the typeahead directive so we are not polluting original scope
|
5648
2987
|
//with typeahead-specific data (matches, query etc.)
|
@@ -5702,55 +3041,69 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
|
|
5702
3041
|
//we need to propagate user's query so we can higlight matches
|
5703
3042
|
scope.query = undefined;
|
5704
3043
|
|
3044
|
+
//Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
|
3045
|
+
var timeoutPromise;
|
3046
|
+
|
5705
3047
|
//plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
|
5706
3048
|
//$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
|
5707
3049
|
modelCtrl.$parsers.push(function (inputValue) {
|
5708
3050
|
|
5709
|
-
var timeoutId;
|
5710
|
-
|
5711
3051
|
resetMatches();
|
5712
|
-
if (
|
5713
|
-
|
5714
|
-
|
5715
|
-
|
5716
|
-
if (waitTime > 0) {
|
5717
|
-
if (timeoutId) {
|
5718
|
-
$timeout.cancel(timeoutId);//cancel previous timeout
|
5719
|
-
}
|
5720
|
-
timeoutId = $timeout(function () {
|
5721
|
-
getMatchesAsync(inputValue);
|
5722
|
-
}, waitTime);
|
5723
|
-
} else {
|
5724
|
-
getMatchesAsync(inputValue);
|
3052
|
+
if (inputValue && inputValue.length >= minSearch) {
|
3053
|
+
if (waitTime > 0) {
|
3054
|
+
if (timeoutPromise) {
|
3055
|
+
$timeout.cancel(timeoutPromise);//cancel previous timeout
|
5725
3056
|
}
|
3057
|
+
timeoutPromise = $timeout(function () {
|
3058
|
+
getMatchesAsync(inputValue);
|
3059
|
+
}, waitTime);
|
3060
|
+
} else {
|
3061
|
+
getMatchesAsync(inputValue);
|
5726
3062
|
}
|
5727
3063
|
}
|
5728
3064
|
|
5729
3065
|
return isEditable ? inputValue : undefined;
|
5730
3066
|
});
|
5731
3067
|
|
5732
|
-
modelCtrl.$
|
3068
|
+
modelCtrl.$formatters.push(function (modelValue) {
|
3069
|
+
|
3070
|
+
var candidateViewValue, emptyViewValue;
|
5733
3071
|
var locals = {};
|
5734
|
-
|
5735
|
-
|
5736
|
-
|
5737
|
-
|
3072
|
+
|
3073
|
+
if (inputFormatter) {
|
3074
|
+
|
3075
|
+
locals['$model'] = modelValue;
|
3076
|
+
return inputFormatter(originalScope, locals);
|
3077
|
+
|
3078
|
+
} else {
|
3079
|
+
locals[parserResult.itemName] = modelValue;
|
3080
|
+
|
3081
|
+
//it might happen that we don't have enough info to properly render input value
|
3082
|
+
//we need to check for this situation and simply return model value if we can't apply custom formatting
|
3083
|
+
candidateViewValue = parserResult.viewMapper(originalScope, locals);
|
3084
|
+
emptyViewValue = parserResult.viewMapper(originalScope, {});
|
3085
|
+
|
3086
|
+
return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue;
|
3087
|
+
}
|
3088
|
+
});
|
5738
3089
|
|
5739
3090
|
scope.select = function (activeIdx) {
|
5740
3091
|
//called from within the $digest() cycle
|
5741
3092
|
var locals = {};
|
5742
3093
|
var model, item;
|
5743
|
-
locals[parserResult.itemName] = item = selected = scope.matches[activeIdx].model;
|
5744
3094
|
|
5745
|
-
|
5746
|
-
|
5747
|
-
|
5748
|
-
|
3095
|
+
locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
|
3096
|
+
model = parserResult.modelMapper(originalScope, locals);
|
3097
|
+
$setModelValue(originalScope, model);
|
3098
|
+
|
3099
|
+
onSelectCallback(originalScope, {
|
5749
3100
|
$item: item,
|
5750
3101
|
$model: model,
|
5751
|
-
$label: parserResult.viewMapper(
|
3102
|
+
$label: parserResult.viewMapper(originalScope, locals)
|
5752
3103
|
});
|
5753
3104
|
|
3105
|
+
//return focus to the input element if a mach was selected via a mouse click event
|
3106
|
+
resetMatches();
|
5754
3107
|
element[0].focus();
|
5755
3108
|
};
|
5756
3109
|
|
@@ -5807,9 +3160,11 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
|
|
5807
3160
|
select:'&'
|
5808
3161
|
},
|
5809
3162
|
replace:true,
|
5810
|
-
templateUrl:'template/typeahead/typeahead.html',
|
3163
|
+
templateUrl:'template/typeahead/typeahead-popup.html',
|
5811
3164
|
link:function (scope, element, attrs) {
|
5812
3165
|
|
3166
|
+
scope.templateUrl = attrs.templateUrl;
|
3167
|
+
|
5813
3168
|
scope.isOpen = function () {
|
5814
3169
|
return scope.matches.length > 0;
|
5815
3170
|
};
|
@@ -5829,6 +3184,23 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
|
|
5829
3184
|
};
|
5830
3185
|
})
|
5831
3186
|
|
3187
|
+
.directive('typeaheadMatch', ['$http', '$templateCache', '$compile', '$parse', function ($http, $templateCache, $compile, $parse) {
|
3188
|
+
return {
|
3189
|
+
restrict:'E',
|
3190
|
+
scope:{
|
3191
|
+
index:'=',
|
3192
|
+
match:'=',
|
3193
|
+
query:'='
|
3194
|
+
},
|
3195
|
+
link:function (scope, element, attrs) {
|
3196
|
+
var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html';
|
3197
|
+
$http.get(tplUrl, {cache: $templateCache}).success(function(tplContent){
|
3198
|
+
element.replaceWith($compile(tplContent.trim())(scope));
|
3199
|
+
});
|
3200
|
+
}
|
3201
|
+
};
|
3202
|
+
}])
|
3203
|
+
|
5832
3204
|
.filter('typeaheadHighlight', function() {
|
5833
3205
|
|
5834
3206
|
function escapeRegexp(queryToEscape) {
|