1 | /**
|
---|
2 | * AngularStrap - Twitter Bootstrap directives for AngularJS
|
---|
3 | * @version v0.7.5 - 2013-07-31
|
---|
4 | * @link http://mgcrea.github.com/angular-strap
|
---|
5 | * @author Olivier Louvignes <[email protected]>
|
---|
6 | * @license MIT License, http://www.opensource.org/licenses/MIT
|
---|
7 | */
|
---|
8 | angular.module('$strap.config', []).value('$strapConfig', {});
|
---|
9 | angular.module('$strap.filters', ['$strap.config']);
|
---|
10 | angular.module('$strap.directives', ['$strap.config']);
|
---|
11 | angular.module('$strap', [
|
---|
12 | '$strap.filters',
|
---|
13 | '$strap.directives',
|
---|
14 | '$strap.config'
|
---|
15 | ]);
|
---|
16 | 'use strict';
|
---|
17 | angular.module('$strap.directives').directive('bsAlert', [
|
---|
18 | '$parse',
|
---|
19 | '$timeout',
|
---|
20 | '$compile',
|
---|
21 | function ($parse, $timeout, $compile) {
|
---|
22 | return {
|
---|
23 | restrict: 'A',
|
---|
24 | link: function postLink(scope, element, attrs) {
|
---|
25 | var getter = $parse(attrs.bsAlert), setter = getter.assign, value = getter(scope);
|
---|
26 | var closeAlert = function closeAlertFn(delay) {
|
---|
27 | $timeout(function () {
|
---|
28 | element.alert('close');
|
---|
29 | }, delay * 1);
|
---|
30 | };
|
---|
31 | if (!attrs.bsAlert) {
|
---|
32 | if (angular.isUndefined(attrs.closeButton) || attrs.closeButton !== '0' && attrs.closeButton !== 'false') {
|
---|
33 | element.prepend('<button type="button" class="close" data-dismiss="alert">×</button>');
|
---|
34 | }
|
---|
35 | if (attrs.closeAfter)
|
---|
36 | closeAlert(attrs.closeAfter);
|
---|
37 | } else {
|
---|
38 | scope.$watch(attrs.bsAlert, function (newValue, oldValue) {
|
---|
39 | value = newValue;
|
---|
40 | element.html((newValue.title ? '<strong>' + newValue.title + '</strong> ' : '') + newValue.content || '');
|
---|
41 | if (!!newValue.closed) {
|
---|
42 | element.hide();
|
---|
43 | }
|
---|
44 | $compile(element.contents())(scope);
|
---|
45 | if (newValue.type || oldValue.type) {
|
---|
46 | oldValue.type && element.removeClass('alert-' + oldValue.type);
|
---|
47 | newValue.type && element.addClass('alert-' + newValue.type);
|
---|
48 | }
|
---|
49 | if (angular.isDefined(newValue.closeAfter))
|
---|
50 | closeAlert(newValue.closeAfter);
|
---|
51 | else if (attrs.closeAfter)
|
---|
52 | closeAlert(attrs.closeAfter);
|
---|
53 | if (angular.isUndefined(attrs.closeButton) || attrs.closeButton !== '0' && attrs.closeButton !== 'false') {
|
---|
54 | element.prepend('<button type="button" class="close" data-dismiss="alert">×</button>');
|
---|
55 | }
|
---|
56 | }, true);
|
---|
57 | }
|
---|
58 | element.addClass('alert').alert();
|
---|
59 | if (element.hasClass('fade')) {
|
---|
60 | element.removeClass('in');
|
---|
61 | setTimeout(function () {
|
---|
62 | element.addClass('in');
|
---|
63 | });
|
---|
64 | }
|
---|
65 | var parentArray = attrs.ngRepeat && attrs.ngRepeat.split(' in ').pop();
|
---|
66 | element.on('close', function (ev) {
|
---|
67 | var removeElement;
|
---|
68 | if (parentArray) {
|
---|
69 | ev.preventDefault();
|
---|
70 | element.removeClass('in');
|
---|
71 | removeElement = function () {
|
---|
72 | element.trigger('closed');
|
---|
73 | if (scope.$parent) {
|
---|
74 | scope.$parent.$apply(function () {
|
---|
75 | var path = parentArray.split('.');
|
---|
76 | var curr = scope.$parent;
|
---|
77 | for (var i = 0; i < path.length; ++i) {
|
---|
78 | if (curr) {
|
---|
79 | curr = curr[path[i]];
|
---|
80 | }
|
---|
81 | }
|
---|
82 | if (curr) {
|
---|
83 | curr.splice(scope.$index, 1);
|
---|
84 | }
|
---|
85 | });
|
---|
86 | }
|
---|
87 | };
|
---|
88 | $.support.transition && element.hasClass('fade') ? element.on($.support.transition.end, removeElement) : removeElement();
|
---|
89 | } else if (value) {
|
---|
90 | ev.preventDefault();
|
---|
91 | element.removeClass('in');
|
---|
92 | removeElement = function () {
|
---|
93 | element.trigger('closed');
|
---|
94 | scope.$apply(function () {
|
---|
95 | value.closed = true;
|
---|
96 | });
|
---|
97 | };
|
---|
98 | $.support.transition && element.hasClass('fade') ? element.on($.support.transition.end, removeElement) : removeElement();
|
---|
99 | } else {
|
---|
100 | }
|
---|
101 | });
|
---|
102 | }
|
---|
103 | };
|
---|
104 | }
|
---|
105 | ]);
|
---|
106 | 'use strict';
|
---|
107 | angular.module('$strap.directives').directive('bsButton', [
|
---|
108 | '$parse',
|
---|
109 | '$timeout',
|
---|
110 | function ($parse, $timeout) {
|
---|
111 | return {
|
---|
112 | restrict: 'A',
|
---|
113 | require: '?ngModel',
|
---|
114 | link: function postLink(scope, element, attrs, controller) {
|
---|
115 | if (controller) {
|
---|
116 | if (!element.parent('[data-toggle="buttons-checkbox"], [data-toggle="buttons-radio"]').length) {
|
---|
117 | element.attr('data-toggle', 'button');
|
---|
118 | }
|
---|
119 | var startValue = !!scope.$eval(attrs.ngModel);
|
---|
120 | if (startValue) {
|
---|
121 | element.addClass('active');
|
---|
122 | }
|
---|
123 | scope.$watch(attrs.ngModel, function (newValue, oldValue) {
|
---|
124 | var bNew = !!newValue, bOld = !!oldValue;
|
---|
125 | if (bNew !== bOld) {
|
---|
126 | $.fn.button.Constructor.prototype.toggle.call(button);
|
---|
127 | } else if (bNew && !startValue) {
|
---|
128 | element.addClass('active');
|
---|
129 | }
|
---|
130 | });
|
---|
131 | }
|
---|
132 | if (!element.hasClass('btn')) {
|
---|
133 | element.on('click.button.data-api', function (ev) {
|
---|
134 | element.button('toggle');
|
---|
135 | });
|
---|
136 | }
|
---|
137 | element.button();
|
---|
138 | var button = element.data('button');
|
---|
139 | button.toggle = function () {
|
---|
140 | if (!controller) {
|
---|
141 | return $.fn.button.Constructor.prototype.toggle.call(this);
|
---|
142 | }
|
---|
143 | var $parent = element.parent('[data-toggle="buttons-radio"]');
|
---|
144 | if ($parent.length) {
|
---|
145 | element.siblings('[ng-model]').each(function (k, v) {
|
---|
146 | $parse($(v).attr('ng-model')).assign(scope, false);
|
---|
147 | });
|
---|
148 | scope.$digest();
|
---|
149 | if (!controller.$modelValue) {
|
---|
150 | controller.$setViewValue(!controller.$modelValue);
|
---|
151 | scope.$digest();
|
---|
152 | }
|
---|
153 | } else {
|
---|
154 | scope.$apply(function () {
|
---|
155 | controller.$setViewValue(!controller.$modelValue);
|
---|
156 | });
|
---|
157 | }
|
---|
158 | };
|
---|
159 | }
|
---|
160 | };
|
---|
161 | }
|
---|
162 | ]).directive('bsButtonsCheckbox', [
|
---|
163 | '$parse',
|
---|
164 | function ($parse) {
|
---|
165 | return {
|
---|
166 | restrict: 'A',
|
---|
167 | require: '?ngModel',
|
---|
168 | compile: function compile(tElement, tAttrs, transclude) {
|
---|
169 | tElement.attr('data-toggle', 'buttons-checkbox').find('a, button').each(function (k, v) {
|
---|
170 | $(v).attr('bs-button', '');
|
---|
171 | });
|
---|
172 | }
|
---|
173 | };
|
---|
174 | }
|
---|
175 | ]).directive('bsButtonsRadio', [
|
---|
176 | '$timeout',
|
---|
177 | function ($timeout) {
|
---|
178 | return {
|
---|
179 | restrict: 'A',
|
---|
180 | require: '?ngModel',
|
---|
181 | compile: function compile(tElement, tAttrs, transclude) {
|
---|
182 | tElement.attr('data-toggle', 'buttons-radio');
|
---|
183 | if (!tAttrs.ngModel) {
|
---|
184 | tElement.find('a, button').each(function (k, v) {
|
---|
185 | $(v).attr('bs-button', '');
|
---|
186 | });
|
---|
187 | }
|
---|
188 | return function postLink(scope, iElement, iAttrs, controller) {
|
---|
189 | if (controller) {
|
---|
190 | $timeout(function () {
|
---|
191 | iElement.find('[value]').button().filter('[value="' + controller.$viewValue + '"]').addClass('active');
|
---|
192 | });
|
---|
193 | iElement.on('click.button.data-api', function (ev) {
|
---|
194 | scope.$apply(function () {
|
---|
195 | controller.$setViewValue($(ev.target).closest('button').attr('value'));
|
---|
196 | });
|
---|
197 | });
|
---|
198 | scope.$watch(iAttrs.ngModel, function (newValue, oldValue) {
|
---|
199 | if (newValue !== oldValue) {
|
---|
200 | var $btn = iElement.find('[value="' + scope.$eval(iAttrs.ngModel) + '"]');
|
---|
201 | if ($btn.length) {
|
---|
202 | $btn.button('toggle');
|
---|
203 | }
|
---|
204 | }
|
---|
205 | });
|
---|
206 | }
|
---|
207 | };
|
---|
208 | }
|
---|
209 | };
|
---|
210 | }
|
---|
211 | ]);
|
---|
212 | 'use strict';
|
---|
213 | angular.module('$strap.directives').directive('bsButtonSelect', [
|
---|
214 | '$parse',
|
---|
215 | '$timeout',
|
---|
216 | function ($parse, $timeout) {
|
---|
217 | return {
|
---|
218 | restrict: 'A',
|
---|
219 | require: '?ngModel',
|
---|
220 | link: function postLink(scope, element, attrs, ctrl) {
|
---|
221 | var getter = $parse(attrs.bsButtonSelect), setter = getter.assign;
|
---|
222 | if (ctrl) {
|
---|
223 | element.text(scope.$eval(attrs.ngModel));
|
---|
224 | scope.$watch(attrs.ngModel, function (newValue, oldValue) {
|
---|
225 | element.text(newValue);
|
---|
226 | });
|
---|
227 | }
|
---|
228 | var values, value, index, newValue;
|
---|
229 | element.bind('click', function (ev) {
|
---|
230 | values = getter(scope);
|
---|
231 | value = ctrl ? scope.$eval(attrs.ngModel) : element.text();
|
---|
232 | index = values.indexOf(value);
|
---|
233 | newValue = index > values.length - 2 ? values[0] : values[index + 1];
|
---|
234 | scope.$apply(function () {
|
---|
235 | element.text(newValue);
|
---|
236 | if (ctrl) {
|
---|
237 | ctrl.$setViewValue(newValue);
|
---|
238 | }
|
---|
239 | });
|
---|
240 | });
|
---|
241 | }
|
---|
242 | };
|
---|
243 | }
|
---|
244 | ]);
|
---|
245 | 'use strict';
|
---|
246 | angular.module('$strap.directives').directive('bsDatepicker', [
|
---|
247 | '$timeout',
|
---|
248 | '$strapConfig',
|
---|
249 | function ($timeout, $strapConfig) {
|
---|
250 | var isAppleTouch = /(iP(a|o)d|iPhone)/g.test(navigator.userAgent);
|
---|
251 | var regexpMap = function regexpMapFn(language) {
|
---|
252 | language = language || 'en';
|
---|
253 | return {
|
---|
254 | '/': '[\\/]',
|
---|
255 | '-': '[-]',
|
---|
256 | '.': '[.]',
|
---|
257 | ' ': '[\\s]',
|
---|
258 | 'dd': '(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))',
|
---|
259 | 'd': '(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))',
|
---|
260 | 'mm': '(?:[0]?[1-9]|[1][012])',
|
---|
261 | 'm': '(?:[0]?[1-9]|[1][012])',
|
---|
262 | 'DD': '(?:' + $.fn.datepicker.dates[language].days.join('|') + ')',
|
---|
263 | 'D': '(?:' + $.fn.datepicker.dates[language].daysShort.join('|') + ')',
|
---|
264 | 'MM': '(?:' + $.fn.datepicker.dates[language].months.join('|') + ')',
|
---|
265 | 'M': '(?:' + $.fn.datepicker.dates[language].monthsShort.join('|') + ')',
|
---|
266 | 'yyyy': '(?:(?:[1]{1}[0-9]{1}[0-9]{1}[0-9]{1})|(?:[2]{1}[0-9]{3}))(?![[0-9]])',
|
---|
267 | 'yy': '(?:(?:[0-9]{1}[0-9]{1}))(?![[0-9]])'
|
---|
268 | };
|
---|
269 | };
|
---|
270 | var regexpForDateFormat = function regexpForDateFormatFn(format, language) {
|
---|
271 | var re = format, map = regexpMap(language), i;
|
---|
272 | i = 0;
|
---|
273 | angular.forEach(map, function (v, k) {
|
---|
274 | re = re.split(k).join('${' + i + '}');
|
---|
275 | i++;
|
---|
276 | });
|
---|
277 | i = 0;
|
---|
278 | angular.forEach(map, function (v, k) {
|
---|
279 | re = re.split('${' + i + '}').join(v);
|
---|
280 | i++;
|
---|
281 | });
|
---|
282 | return new RegExp('^' + re + '$', ['i']);
|
---|
283 | };
|
---|
284 | return {
|
---|
285 | restrict: 'A',
|
---|
286 | require: '?ngModel',
|
---|
287 | link: function postLink(scope, element, attrs, controller) {
|
---|
288 | var options = angular.extend({ autoclose: true }, $strapConfig.datepicker || {}), type = attrs.dateType || options.type || 'date';
|
---|
289 | angular.forEach([
|
---|
290 | 'format',
|
---|
291 | 'weekStart',
|
---|
292 | 'calendarWeeks',
|
---|
293 | 'startDate',
|
---|
294 | 'endDate',
|
---|
295 | 'daysOfWeekDisabled',
|
---|
296 | 'autoclose',
|
---|
297 | 'startView',
|
---|
298 | 'minViewMode',
|
---|
299 | 'todayBtn',
|
---|
300 | 'todayHighlight',
|
---|
301 | 'keyboardNavigation',
|
---|
302 | 'language',
|
---|
303 | 'forceParse'
|
---|
304 | ], function (key) {
|
---|
305 | if (angular.isDefined(attrs[key]))
|
---|
306 | options[key] = attrs[key];
|
---|
307 | });
|
---|
308 | var language = options.language || 'en', readFormat = attrs.dateFormat || options.format || $.fn.datepicker.dates[language] && $.fn.datepicker.dates[language].format || 'mm/dd/yyyy', format = isAppleTouch ? 'yyyy-mm-dd' : readFormat, dateFormatRegexp = regexpForDateFormat(format, language);
|
---|
309 | if (controller) {
|
---|
310 | controller.$formatters.unshift(function (modelValue) {
|
---|
311 | return type === 'date' && angular.isString(modelValue) && modelValue ? $.fn.datepicker.DPGlobal.parseDate(modelValue, $.fn.datepicker.DPGlobal.parseFormat(readFormat), language) : modelValue;
|
---|
312 | });
|
---|
313 | controller.$parsers.unshift(function (viewValue) {
|
---|
314 | if (!viewValue) {
|
---|
315 | controller.$setValidity('date', true);
|
---|
316 | return null;
|
---|
317 | } else if (type === 'date' && angular.isDate(viewValue)) {
|
---|
318 | controller.$setValidity('date', true);
|
---|
319 | return viewValue;
|
---|
320 | } else if (angular.isString(viewValue) && dateFormatRegexp.test(viewValue)) {
|
---|
321 | controller.$setValidity('date', true);
|
---|
322 | if (isAppleTouch)
|
---|
323 | return new Date(viewValue);
|
---|
324 | return type === 'string' ? viewValue : $.fn.datepicker.DPGlobal.parseDate(viewValue, $.fn.datepicker.DPGlobal.parseFormat(format), language);
|
---|
325 | } else {
|
---|
326 | controller.$setValidity('date', false);
|
---|
327 | return undefined;
|
---|
328 | }
|
---|
329 | });
|
---|
330 | controller.$render = function ngModelRender() {
|
---|
331 | if (isAppleTouch) {
|
---|
332 | var date = controller.$viewValue ? $.fn.datepicker.DPGlobal.formatDate(controller.$viewValue, $.fn.datepicker.DPGlobal.parseFormat(format), language) : '';
|
---|
333 | element.val(date);
|
---|
334 | return date;
|
---|
335 | }
|
---|
336 | if (!controller.$viewValue)
|
---|
337 | element.val('');
|
---|
338 | return element.datepicker('update', controller.$viewValue);
|
---|
339 | };
|
---|
340 | }
|
---|
341 | if (isAppleTouch) {
|
---|
342 | element.prop('type', 'date').css('-webkit-appearance', 'textfield');
|
---|
343 | } else {
|
---|
344 | if (controller) {
|
---|
345 | element.on('changeDate', function (ev) {
|
---|
346 | scope.$apply(function () {
|
---|
347 | controller.$setViewValue(type === 'string' ? element.val() : ev.date);
|
---|
348 | });
|
---|
349 | });
|
---|
350 | }
|
---|
351 | element.datepicker(angular.extend(options, {
|
---|
352 | format: format,
|
---|
353 | language: language
|
---|
354 | }));
|
---|
355 | scope.$on('$destroy', function () {
|
---|
356 | var datepicker = element.data('datepicker');
|
---|
357 | if (datepicker) {
|
---|
358 | datepicker.picker.remove();
|
---|
359 | element.data('datepicker', null);
|
---|
360 | }
|
---|
361 | });
|
---|
362 | attrs.$observe('startDate', function (value) {
|
---|
363 | element.datepicker('setStartDate', value);
|
---|
364 | });
|
---|
365 | attrs.$observe('endDate', function (value) {
|
---|
366 | element.datepicker('setEndDate', value);
|
---|
367 | });
|
---|
368 | }
|
---|
369 | var component = element.siblings('[data-toggle="datepicker"]');
|
---|
370 | if (component.length) {
|
---|
371 | component.on('click', function () {
|
---|
372 | if (!element.prop('disabled')) {
|
---|
373 | element.trigger('focus');
|
---|
374 | }
|
---|
375 | });
|
---|
376 | }
|
---|
377 | }
|
---|
378 | };
|
---|
379 | }
|
---|
380 | ]);
|
---|
381 | 'use strict';
|
---|
382 | angular.module('$strap.directives').directive('bsDropdown', [
|
---|
383 | '$parse',
|
---|
384 | '$compile',
|
---|
385 | '$timeout',
|
---|
386 | function ($parse, $compile, $timeout) {
|
---|
387 | var buildTemplate = function (items, ul) {
|
---|
388 | if (!ul)
|
---|
389 | ul = [
|
---|
390 | '<ul class="dropdown-menu" role="menu" aria-labelledby="drop1">',
|
---|
391 | '</ul>'
|
---|
392 | ];
|
---|
393 | angular.forEach(items, function (item, index) {
|
---|
394 | if (item.divider)
|
---|
395 | return ul.splice(index + 1, 0, '<li class="divider"></li>');
|
---|
396 | var li = '<li' + (item.submenu && item.submenu.length ? ' class="dropdown-submenu"' : '') + '>' + '<a tabindex="-1" ng-href="' + (item.href || '') + '"' + (item.click ? '" ng-click="' + item.click + '"' : '') + (item.target ? '" target="' + item.target + '"' : '') + (item.method ? '" data-method="' + item.method + '"' : '') + '>' + (item.text || '') + '</a>';
|
---|
397 | if (item.submenu && item.submenu.length)
|
---|
398 | li += buildTemplate(item.submenu).join('\n');
|
---|
399 | li += '</li>';
|
---|
400 | ul.splice(index + 1, 0, li);
|
---|
401 | });
|
---|
402 | return ul;
|
---|
403 | };
|
---|
404 | return {
|
---|
405 | restrict: 'EA',
|
---|
406 | scope: true,
|
---|
407 | link: function postLink(scope, iElement, iAttrs) {
|
---|
408 | var getter = $parse(iAttrs.bsDropdown), items = getter(scope);
|
---|
409 | $timeout(function () {
|
---|
410 | if (!angular.isArray(items)) {
|
---|
411 | }
|
---|
412 | var dropdown = angular.element(buildTemplate(items).join(''));
|
---|
413 | dropdown.insertAfter(iElement);
|
---|
414 | $compile(iElement.next('ul.dropdown-menu'))(scope);
|
---|
415 | });
|
---|
416 | iElement.addClass('dropdown-toggle').attr('data-toggle', 'dropdown');
|
---|
417 | }
|
---|
418 | };
|
---|
419 | }
|
---|
420 | ]);
|
---|
421 | 'use strict';
|
---|
422 | angular.module('$strap.directives').factory('$modal', [
|
---|
423 | '$rootScope',
|
---|
424 | '$compile',
|
---|
425 | '$http',
|
---|
426 | '$timeout',
|
---|
427 | '$q',
|
---|
428 | '$templateCache',
|
---|
429 | '$strapConfig',
|
---|
430 | function ($rootScope, $compile, $http, $timeout, $q, $templateCache, $strapConfig) {
|
---|
431 | var ModalFactory = function ModalFactoryFn(config) {
|
---|
432 | function Modal(config) {
|
---|
433 | var options = angular.extend({ show: true }, $strapConfig.modal, config), scope = options.scope ? options.scope : $rootScope.$new(), templateUrl = options.template;
|
---|
434 | return $q.when($templateCache.get(templateUrl) || $http.get(templateUrl, { cache: true }).then(function (res) {
|
---|
435 | return res.data;
|
---|
436 | })).then(function onSuccess(template) {
|
---|
437 | var id = templateUrl.replace('.html', '').replace(/[\/|\.|:]/g, '-') + '-' + scope.$id;
|
---|
438 | var $modal = $('<div class="modal hide" tabindex="-1"></div>').attr('id', id).addClass('fade').html(template);
|
---|
439 | if (options.modalClass)
|
---|
440 | $modal.addClass(options.modalClass);
|
---|
441 | $('body').append($modal);
|
---|
442 | $timeout(function () {
|
---|
443 | $compile($modal)(scope);
|
---|
444 | });
|
---|
445 | scope.$modal = function (name) {
|
---|
446 | $modal.modal(name);
|
---|
447 | };
|
---|
448 | angular.forEach([
|
---|
449 | 'show',
|
---|
450 | 'hide'
|
---|
451 | ], function (name) {
|
---|
452 | scope[name] = function () {
|
---|
453 | $modal.modal(name);
|
---|
454 | };
|
---|
455 | });
|
---|
456 | scope.dismiss = scope.hide;
|
---|
457 | angular.forEach([
|
---|
458 | 'show',
|
---|
459 | 'shown',
|
---|
460 | 'hide',
|
---|
461 | 'hidden'
|
---|
462 | ], function (name) {
|
---|
463 | $modal.on(name, function (ev) {
|
---|
464 | scope.$emit('modal-' + name, ev);
|
---|
465 | });
|
---|
466 | });
|
---|
467 | $modal.on('shown', function (ev) {
|
---|
468 | $('input[autofocus], textarea[autofocus]', $modal).first().trigger('focus');
|
---|
469 | });
|
---|
470 | $modal.on('hidden', function (ev) {
|
---|
471 | if (!options.persist)
|
---|
472 | scope.$destroy();
|
---|
473 | });
|
---|
474 | scope.$on('$destroy', function () {
|
---|
475 | $modal.remove();
|
---|
476 | });
|
---|
477 | $modal.modal(options);
|
---|
478 | return $modal;
|
---|
479 | });
|
---|
480 | }
|
---|
481 | return new Modal(config);
|
---|
482 | };
|
---|
483 | return ModalFactory;
|
---|
484 | }
|
---|
485 | ]).directive('bsModal', [
|
---|
486 | '$q',
|
---|
487 | '$modal',
|
---|
488 | function ($q, $modal) {
|
---|
489 | return {
|
---|
490 | restrict: 'A',
|
---|
491 | scope: true,
|
---|
492 | link: function postLink(scope, iElement, iAttrs, controller) {
|
---|
493 | var options = {
|
---|
494 | template: scope.$eval(iAttrs.bsModal),
|
---|
495 | persist: true,
|
---|
496 | show: false,
|
---|
497 | scope: scope
|
---|
498 | };
|
---|
499 | angular.forEach([
|
---|
500 | 'modalClass',
|
---|
501 | 'backdrop',
|
---|
502 | 'keyboard'
|
---|
503 | ], function (key) {
|
---|
504 | if (angular.isDefined(iAttrs[key]))
|
---|
505 | options[key] = iAttrs[key];
|
---|
506 | });
|
---|
507 | $q.when($modal(options)).then(function onSuccess(modal) {
|
---|
508 | iElement.attr('data-target', '#' + modal.attr('id')).attr('data-toggle', 'modal');
|
---|
509 | });
|
---|
510 | }
|
---|
511 | };
|
---|
512 | }
|
---|
513 | ]);
|
---|
514 | 'use strict';
|
---|
515 | angular.module('$strap.directives').directive('bsNavbar', [
|
---|
516 | '$location',
|
---|
517 | function ($location) {
|
---|
518 | return {
|
---|
519 | restrict: 'A',
|
---|
520 | link: function postLink(scope, element, attrs, controller) {
|
---|
521 | scope.$watch(function () {
|
---|
522 | return $location.path();
|
---|
523 | }, function (newValue, oldValue) {
|
---|
524 | $('li[data-match-route]', element).each(function (k, li) {
|
---|
525 | var $li = angular.element(li), pattern = $li.attr('data-match-route'), regexp = new RegExp('^' + pattern + '$', ['i']);
|
---|
526 | if (regexp.test(newValue)) {
|
---|
527 | $li.addClass('active').find('.collapse.in').collapse('hide');
|
---|
528 | } else {
|
---|
529 | $li.removeClass('active');
|
---|
530 | }
|
---|
531 | });
|
---|
532 | });
|
---|
533 | }
|
---|
534 | };
|
---|
535 | }
|
---|
536 | ]);
|
---|
537 | 'use strict';
|
---|
538 | angular.module('$strap.directives').directive('bsPopover', [
|
---|
539 | '$parse',
|
---|
540 | '$compile',
|
---|
541 | '$http',
|
---|
542 | '$timeout',
|
---|
543 | '$q',
|
---|
544 | '$templateCache',
|
---|
545 | function ($parse, $compile, $http, $timeout, $q, $templateCache) {
|
---|
546 | $('body').on('keyup', function (ev) {
|
---|
547 | if (ev.keyCode === 27) {
|
---|
548 | $('.popover.in').each(function () {
|
---|
549 | $(this).popover('hide');
|
---|
550 | });
|
---|
551 | }
|
---|
552 | });
|
---|
553 | return {
|
---|
554 | restrict: 'A',
|
---|
555 | scope: true,
|
---|
556 | link: function postLink(scope, element, attr, ctrl) {
|
---|
557 | var getter = $parse(attr.bsPopover), setter = getter.assign, value = getter(scope), options = {};
|
---|
558 | if (angular.isObject(value)) {
|
---|
559 | options = value;
|
---|
560 | }
|
---|
561 | $q.when(options.content || $templateCache.get(value) || $http.get(value, { cache: true })).then(function onSuccess(template) {
|
---|
562 | if (angular.isObject(template)) {
|
---|
563 | template = template.data;
|
---|
564 | }
|
---|
565 | if (!!attr.unique) {
|
---|
566 | element.on('show', function (ev) {
|
---|
567 | $('.popover.in').each(function () {
|
---|
568 | var $this = $(this), popover = $this.data('popover');
|
---|
569 | if (popover && !popover.$element.is(element)) {
|
---|
570 | $this.popover('hide');
|
---|
571 | }
|
---|
572 | });
|
---|
573 | });
|
---|
574 | }
|
---|
575 | if (!!attr.hide) {
|
---|
576 | scope.$watch(attr.hide, function (newValue, oldValue) {
|
---|
577 | if (!!newValue) {
|
---|
578 | popover.hide();
|
---|
579 | } else if (newValue !== oldValue) {
|
---|
580 | popover.show();
|
---|
581 | }
|
---|
582 | });
|
---|
583 | }
|
---|
584 | if (!!attr.show) {
|
---|
585 | scope.$watch(attr.show, function (newValue, oldValue) {
|
---|
586 | if (!!newValue) {
|
---|
587 | $timeout(function () {
|
---|
588 | popover.show();
|
---|
589 | });
|
---|
590 | } else if (newValue !== oldValue) {
|
---|
591 | popover.hide();
|
---|
592 | }
|
---|
593 | });
|
---|
594 | }
|
---|
595 | element.popover(angular.extend({}, options, {
|
---|
596 | content: template,
|
---|
597 | html: true
|
---|
598 | }));
|
---|
599 | var popover = element.data('popover');
|
---|
600 | popover.hasContent = function () {
|
---|
601 | return this.getTitle() || template;
|
---|
602 | };
|
---|
603 | popover.getPosition = function () {
|
---|
604 | var r = $.fn.popover.Constructor.prototype.getPosition.apply(this, arguments);
|
---|
605 | $compile(this.$tip)(scope);
|
---|
606 | scope.$digest();
|
---|
607 | this.$tip.data('popover', this);
|
---|
608 | return r;
|
---|
609 | };
|
---|
610 | scope.$popover = function (name) {
|
---|
611 | popover(name);
|
---|
612 | };
|
---|
613 | angular.forEach([
|
---|
614 | 'show',
|
---|
615 | 'hide'
|
---|
616 | ], function (name) {
|
---|
617 | scope[name] = function () {
|
---|
618 | popover[name]();
|
---|
619 | };
|
---|
620 | });
|
---|
621 | scope.dismiss = scope.hide;
|
---|
622 | angular.forEach([
|
---|
623 | 'show',
|
---|
624 | 'shown',
|
---|
625 | 'hide',
|
---|
626 | 'hidden'
|
---|
627 | ], function (name) {
|
---|
628 | element.on(name, function (ev) {
|
---|
629 | scope.$emit('popover-' + name, ev);
|
---|
630 | });
|
---|
631 | });
|
---|
632 | });
|
---|
633 | }
|
---|
634 | };
|
---|
635 | }
|
---|
636 | ]);
|
---|
637 | 'use strict';
|
---|
638 | angular.module('$strap.directives').directive('bsSelect', [
|
---|
639 | '$timeout',
|
---|
640 | function ($timeout) {
|
---|
641 | var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/;
|
---|
642 | return {
|
---|
643 | restrict: 'A',
|
---|
644 | require: '?ngModel',
|
---|
645 | link: function postLink(scope, element, attrs, controller) {
|
---|
646 | var options = scope.$eval(attrs.bsSelect) || {};
|
---|
647 | $timeout(function () {
|
---|
648 | element.selectpicker(options);
|
---|
649 | element.next().removeClass('ng-scope');
|
---|
650 | });
|
---|
651 | if (controller) {
|
---|
652 | scope.$watch(attrs.ngModel, function (newValue, oldValue) {
|
---|
653 | if (!angular.equals(newValue, oldValue)) {
|
---|
654 | element.selectpicker('refresh');
|
---|
655 | }
|
---|
656 | });
|
---|
657 | }
|
---|
658 | }
|
---|
659 | };
|
---|
660 | }
|
---|
661 | ]);
|
---|
662 | 'use strict';
|
---|
663 | angular.module('$strap.directives').directive('bsTabs', [
|
---|
664 | '$parse',
|
---|
665 | '$compile',
|
---|
666 | '$timeout',
|
---|
667 | function ($parse, $compile, $timeout) {
|
---|
668 | var template = '<div class="tabs">' + '<ul class="nav nav-tabs">' + '<li ng-repeat="pane in panes" ng-class="{active:pane.active}">' + '<a data-target="#{{pane.id}}" data-index="{{$index}}" data-toggle="tab">{{pane.title}}</a>' + '</li>' + '</ul>' + '<div class="tab-content" ng-transclude>' + '</div>';
|
---|
669 | return {
|
---|
670 | restrict: 'A',
|
---|
671 | require: '?ngModel',
|
---|
672 | priority: 0,
|
---|
673 | scope: true,
|
---|
674 | template: template,
|
---|
675 | replace: true,
|
---|
676 | transclude: true,
|
---|
677 | compile: function compile(tElement, tAttrs, transclude) {
|
---|
678 | return function postLink(scope, iElement, iAttrs, controller) {
|
---|
679 | var getter = $parse(iAttrs.bsTabs), setter = getter.assign, value = getter(scope);
|
---|
680 | scope.panes = [];
|
---|
681 | var $tabs = iElement.find('ul.nav-tabs');
|
---|
682 | var $panes = iElement.find('div.tab-content');
|
---|
683 | var activeTab = 0, id, title, active;
|
---|
684 | $timeout(function () {
|
---|
685 | $panes.find('[data-title], [data-tab]').each(function (index) {
|
---|
686 | var $this = angular.element(this);
|
---|
687 | id = 'tab-' + scope.$id + '-' + index;
|
---|
688 | title = $this.data('title') || $this.data('tab');
|
---|
689 | active = !active && $this.hasClass('active');
|
---|
690 | $this.attr('id', id).addClass('tab-pane');
|
---|
691 | if (iAttrs.fade)
|
---|
692 | $this.addClass('fade');
|
---|
693 | scope.panes.push({
|
---|
694 | id: id,
|
---|
695 | title: title,
|
---|
696 | content: this.innerHTML,
|
---|
697 | active: active
|
---|
698 | });
|
---|
699 | });
|
---|
700 | if (scope.panes.length && !active) {
|
---|
701 | $panes.find('.tab-pane:first-child').addClass('active' + (iAttrs.fade ? ' in' : ''));
|
---|
702 | scope.panes[0].active = true;
|
---|
703 | }
|
---|
704 | });
|
---|
705 | if (controller) {
|
---|
706 | iElement.on('show', function (ev) {
|
---|
707 | var $target = $(ev.target);
|
---|
708 | scope.$apply(function () {
|
---|
709 | controller.$setViewValue($target.data('index'));
|
---|
710 | });
|
---|
711 | });
|
---|
712 | scope.$watch(iAttrs.ngModel, function (newValue, oldValue) {
|
---|
713 | if (angular.isUndefined(newValue))
|
---|
714 | return;
|
---|
715 | activeTab = newValue;
|
---|
716 | setTimeout(function () {
|
---|
717 | var $next = $($tabs[0].querySelectorAll('li')[newValue * 1]);
|
---|
718 | if (!$next.hasClass('active')) {
|
---|
719 | $next.children('a').tab('show');
|
---|
720 | }
|
---|
721 | });
|
---|
722 | });
|
---|
723 | }
|
---|
724 | };
|
---|
725 | }
|
---|
726 | };
|
---|
727 | }
|
---|
728 | ]);
|
---|
729 | 'use strict';
|
---|
730 | angular.module('$strap.directives').directive('bsTimepicker', [
|
---|
731 | '$timeout',
|
---|
732 | '$strapConfig',
|
---|
733 | function ($timeout, $strapConfig) {
|
---|
734 | var TIME_REGEXP = '((?:(?:[0-1][0-9])|(?:[2][0-3])|(?:[0-9])):(?:[0-5][0-9])(?::[0-5][0-9])?(?:\\s?(?:am|AM|pm|PM))?)';
|
---|
735 | return {
|
---|
736 | restrict: 'A',
|
---|
737 | require: '?ngModel',
|
---|
738 | link: function postLink(scope, element, attrs, controller) {
|
---|
739 | if (controller) {
|
---|
740 | element.on('changeTime.timepicker', function (ev) {
|
---|
741 | $timeout(function () {
|
---|
742 | controller.$setViewValue(element.val());
|
---|
743 | });
|
---|
744 | });
|
---|
745 | var timeRegExp = new RegExp('^' + TIME_REGEXP + '$', ['i']);
|
---|
746 | controller.$parsers.unshift(function (viewValue) {
|
---|
747 | if (!viewValue || timeRegExp.test(viewValue)) {
|
---|
748 | controller.$setValidity('time', true);
|
---|
749 | return viewValue;
|
---|
750 | } else {
|
---|
751 | controller.$setValidity('time', false);
|
---|
752 | return;
|
---|
753 | }
|
---|
754 | });
|
---|
755 | }
|
---|
756 | element.attr('data-toggle', 'timepicker');
|
---|
757 | element.parent().addClass('bootstrap-timepicker');
|
---|
758 | element.timepicker($strapConfig.timepicker || {});
|
---|
759 | var timepicker = element.data('timepicker');
|
---|
760 | var component = element.siblings('[data-toggle="timepicker"]');
|
---|
761 | if (component.length) {
|
---|
762 | component.on('click', $.proxy(timepicker.showWidget, timepicker));
|
---|
763 | }
|
---|
764 | }
|
---|
765 | };
|
---|
766 | }
|
---|
767 | ]);
|
---|
768 | 'use strict';
|
---|
769 | angular.module('$strap.directives').directive('bsTooltip', [
|
---|
770 | '$parse',
|
---|
771 | '$compile',
|
---|
772 | function ($parse, $compile) {
|
---|
773 | return {
|
---|
774 | restrict: 'A',
|
---|
775 | scope: true,
|
---|
776 | link: function postLink(scope, element, attrs, ctrl) {
|
---|
777 | var getter = $parse(attrs.bsTooltip), setter = getter.assign, value = getter(scope);
|
---|
778 | scope.$watch(attrs.bsTooltip, function (newValue, oldValue) {
|
---|
779 | if (newValue !== oldValue) {
|
---|
780 | value = newValue;
|
---|
781 | }
|
---|
782 | });
|
---|
783 | if (!!attrs.unique) {
|
---|
784 | element.on('show', function (ev) {
|
---|
785 | $('.tooltip.in').each(function () {
|
---|
786 | var $this = $(this), tooltip = $this.data('tooltip');
|
---|
787 | if (tooltip && !tooltip.$element.is(element)) {
|
---|
788 | $this.tooltip('hide');
|
---|
789 | }
|
---|
790 | });
|
---|
791 | });
|
---|
792 | }
|
---|
793 | element.tooltip({
|
---|
794 | title: function () {
|
---|
795 | return angular.isFunction(value) ? value.apply(null, arguments) : value;
|
---|
796 | },
|
---|
797 | html: true
|
---|
798 | });
|
---|
799 | var tooltip = element.data('tooltip');
|
---|
800 | tooltip.show = function () {
|
---|
801 | var r = $.fn.tooltip.Constructor.prototype.show.apply(this, arguments);
|
---|
802 | this.tip().data('tooltip', this);
|
---|
803 | return r;
|
---|
804 | };
|
---|
805 | scope._tooltip = function (event) {
|
---|
806 | element.tooltip(event);
|
---|
807 | };
|
---|
808 | scope.hide = function () {
|
---|
809 | element.tooltip('hide');
|
---|
810 | };
|
---|
811 | scope.show = function () {
|
---|
812 | element.tooltip('show');
|
---|
813 | };
|
---|
814 | scope.dismiss = scope.hide;
|
---|
815 | }
|
---|
816 | };
|
---|
817 | }
|
---|
818 | ]);
|
---|
819 | 'use strict';
|
---|
820 | angular.module('$strap.directives').directive('bsTypeahead', [
|
---|
821 | '$parse',
|
---|
822 | function ($parse) {
|
---|
823 | return {
|
---|
824 | restrict: 'A',
|
---|
825 | require: '?ngModel',
|
---|
826 | link: function postLink(scope, element, attrs, controller) {
|
---|
827 | var getter = $parse(attrs.bsTypeahead), setter = getter.assign, value = getter(scope);
|
---|
828 | scope.$watch(attrs.bsTypeahead, function (newValue, oldValue) {
|
---|
829 | if (newValue !== oldValue) {
|
---|
830 | value = newValue;
|
---|
831 | }
|
---|
832 | });
|
---|
833 | element.attr('data-provide', 'typeahead');
|
---|
834 | element.typeahead({
|
---|
835 | source: function (query) {
|
---|
836 | return angular.isFunction(value) ? value.apply(null, arguments) : value;
|
---|
837 | },
|
---|
838 | minLength: attrs.minLength || 1,
|
---|
839 | items: attrs.items,
|
---|
840 | updater: function (value) {
|
---|
841 | if (controller) {
|
---|
842 | scope.$apply(function () {
|
---|
843 | controller.$setViewValue(value);
|
---|
844 | });
|
---|
845 | }
|
---|
846 | scope.$emit('typeahead-updated', value);
|
---|
847 | return value;
|
---|
848 | }
|
---|
849 | });
|
---|
850 | var typeahead = element.data('typeahead');
|
---|
851 | typeahead.lookup = function (ev) {
|
---|
852 | var items;
|
---|
853 | this.query = this.$element.val() || '';
|
---|
854 | if (this.query.length < this.options.minLength) {
|
---|
855 | return this.shown ? this.hide() : this;
|
---|
856 | }
|
---|
857 | items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source;
|
---|
858 | return items ? this.process(items) : this;
|
---|
859 | };
|
---|
860 | if (!!attrs.matchAll) {
|
---|
861 | typeahead.matcher = function (item) {
|
---|
862 | return true;
|
---|
863 | };
|
---|
864 | }
|
---|
865 | if (attrs.minLength === '0') {
|
---|
866 | setTimeout(function () {
|
---|
867 | element.on('focus', function () {
|
---|
868 | element.val().length === 0 && setTimeout(element.typeahead.bind(element, 'lookup'), 200);
|
---|
869 | });
|
---|
870 | });
|
---|
871 | }
|
---|
872 | }
|
---|
873 | };
|
---|
874 | }
|
---|
875 | ]); |
---|