angularstrap-rails 0.7.6

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.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in angular-strap.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 AkaiBureido
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,41 @@
1
+ # AngularStrap-Rails
2
+
3
+ angularstrap-rails adds [AngularStrap](http://mgcrea.github.io/angular-strap/)
4
+ set of angular directives to your rails asset pipeline.
5
+
6
+ ## Usage
7
+
8
+ To install put this in your Gemfile
9
+
10
+ ```Gemfile
11
+ gem 'angularstrap-rails'
12
+ ```
13
+
14
+ Then add the following to your application.js
15
+ ```Javascript
16
+ //= require angular-strap
17
+ ```
18
+
19
+ You may also need to include the following in application.js
20
+ ```Javascript
21
+ //= require angular-strap/bootstrap-datepicker
22
+ //= require angular-strap/bootstrap-select
23
+ //= require angular-strap/bootstrap-datepicker
24
+ ```
25
+
26
+ And this in your aplication.css.sass
27
+ ```Sass
28
+ # require angular-strap/bootstrap-datepicker
29
+ # require angular-strap/bootstrap-select
30
+ # require angular-strap/bootstrap-datepicker
31
+ ```
32
+
33
+ For further information on how to use it refer to [AngularStrap](http://mgcrea.github.io/angular-strap/)
34
+
35
+ ## Contributing
36
+
37
+ 1. Fork it
38
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
39
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
40
+ 4. Push to the branch (`git push origin my-new-feature`)
41
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,15 @@
1
+ # coding: utf-8
2
+ require File.expand_path('../lib/angularstrap-rails/version', __FILE__)
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "angularstrap-rails"
6
+ spec.version = AngularStrap::Rails::VERSION
7
+ spec.authors = ["AkaiBureido"]
8
+ spec.email = ["utkin.oleg@me.com"]
9
+ spec.description = %q{AngularStrap for rails.}
10
+ spec.summary = %q{This gem adds AngularStrap to your rails asset pipeline.}
11
+ spec.homepage = "http://github.com/AkaiBureido/angularstrap-rails"
12
+ spec.license = "MIT"
13
+
14
+ spec.files = `git ls-files`.split($/)
15
+ end
@@ -0,0 +1,8 @@
1
+ require "angularstrap-rails/version"
2
+
3
+ module AngularStrap
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module AngularStrap
2
+ module Rails
3
+ VERSION = "0.7.6"
4
+ end
5
+ end
@@ -0,0 +1,975 @@
1
+ /**
2
+ * AngularStrap - Twitter Bootstrap directives for AngularJS
3
+ * @version v0.7.6 - 2013-08-21
4
+ * @link http://mgcrea.github.com/angular-strap
5
+ * @author Olivier Louvignes <olivier@mg-crea.com>
6
+ * @license MIT License, http://www.opensource.org/licenses/MIT
7
+ */
8
+ (function (window, document, undefined) {
9
+ 'use strict';
10
+ angular.module('$strap.config', []).value('$strapConfig', {});
11
+ angular.module('$strap.filters', ['$strap.config']);
12
+ angular.module('$strap.directives', ['$strap.config']);
13
+ angular.module('$strap', [
14
+ '$strap.filters',
15
+ '$strap.directives',
16
+ '$strap.config'
17
+ ]);
18
+ angular.module('$strap.directives').directive('bsAlert', [
19
+ '$parse',
20
+ '$timeout',
21
+ '$compile',
22
+ function ($parse, $timeout, $compile) {
23
+ return {
24
+ restrict: 'A',
25
+ link: function postLink(scope, element, attrs) {
26
+ var getter = $parse(attrs.bsAlert), setter = getter.assign, value = getter(scope);
27
+ var closeAlert = function closeAlertFn(delay) {
28
+ $timeout(function () {
29
+ element.alert('close');
30
+ }, delay * 1);
31
+ };
32
+ if (!attrs.bsAlert) {
33
+ if (angular.isUndefined(attrs.closeButton) || attrs.closeButton !== '0' && attrs.closeButton !== 'false') {
34
+ element.prepend('<button type="button" class="close" data-dismiss="alert">&times;</button>');
35
+ }
36
+ if (attrs.closeAfter)
37
+ closeAlert(attrs.closeAfter);
38
+ } else {
39
+ scope.$watch(attrs.bsAlert, function (newValue, oldValue) {
40
+ value = newValue;
41
+ element.html((newValue.title ? '<strong>' + newValue.title + '</strong>&nbsp;' : '') + newValue.content || '');
42
+ if (!!newValue.closed) {
43
+ element.hide();
44
+ }
45
+ $compile(element.contents())(scope);
46
+ if (newValue.type || oldValue.type) {
47
+ oldValue.type && element.removeClass('alert-' + oldValue.type);
48
+ newValue.type && element.addClass('alert-' + newValue.type);
49
+ }
50
+ if (angular.isDefined(newValue.closeAfter))
51
+ closeAlert(newValue.closeAfter);
52
+ else if (attrs.closeAfter)
53
+ closeAlert(attrs.closeAfter);
54
+ if (angular.isUndefined(attrs.closeButton) || attrs.closeButton !== '0' && attrs.closeButton !== 'false') {
55
+ element.prepend('<button type="button" class="close" data-dismiss="alert">&times;</button>');
56
+ }
57
+ }, true);
58
+ }
59
+ element.addClass('alert').alert();
60
+ if (element.hasClass('fade')) {
61
+ element.removeClass('in');
62
+ setTimeout(function () {
63
+ element.addClass('in');
64
+ });
65
+ }
66
+ var parentArray = attrs.ngRepeat && attrs.ngRepeat.split(' in ').pop();
67
+ element.on('close', function (ev) {
68
+ var removeElement;
69
+ if (parentArray) {
70
+ ev.preventDefault();
71
+ element.removeClass('in');
72
+ removeElement = function () {
73
+ element.trigger('closed');
74
+ if (scope.$parent) {
75
+ scope.$parent.$apply(function () {
76
+ var path = parentArray.split('.');
77
+ var curr = scope.$parent;
78
+ for (var i = 0; i < path.length; ++i) {
79
+ if (curr) {
80
+ curr = curr[path[i]];
81
+ }
82
+ }
83
+ if (curr) {
84
+ curr.splice(scope.$index, 1);
85
+ }
86
+ });
87
+ }
88
+ };
89
+ $.support.transition && element.hasClass('fade') ? element.on($.support.transition.end, removeElement) : removeElement();
90
+ } else if (value) {
91
+ ev.preventDefault();
92
+ element.removeClass('in');
93
+ removeElement = function () {
94
+ element.trigger('closed');
95
+ scope.$apply(function () {
96
+ value.closed = true;
97
+ });
98
+ };
99
+ $.support.transition && element.hasClass('fade') ? element.on($.support.transition.end, removeElement) : removeElement();
100
+ } else {
101
+ }
102
+ });
103
+ }
104
+ };
105
+ }
106
+ ]);
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
+ angular.module('$strap.directives').directive('bsButtonSelect', [
213
+ '$parse',
214
+ '$timeout',
215
+ function ($parse, $timeout) {
216
+ return {
217
+ restrict: 'A',
218
+ require: '?ngModel',
219
+ link: function postLink(scope, element, attrs, ctrl) {
220
+ var getter = $parse(attrs.bsButtonSelect), setter = getter.assign;
221
+ if (ctrl) {
222
+ element.text(scope.$eval(attrs.ngModel));
223
+ scope.$watch(attrs.ngModel, function (newValue, oldValue) {
224
+ element.text(newValue);
225
+ });
226
+ }
227
+ var values, value, index, newValue;
228
+ element.bind('click', function (ev) {
229
+ values = getter(scope);
230
+ value = ctrl ? scope.$eval(attrs.ngModel) : element.text();
231
+ index = values.indexOf(value);
232
+ newValue = index > values.length - 2 ? values[0] : values[index + 1];
233
+ scope.$apply(function () {
234
+ element.text(newValue);
235
+ if (ctrl) {
236
+ ctrl.$setViewValue(newValue);
237
+ }
238
+ });
239
+ });
240
+ }
241
+ };
242
+ }
243
+ ]);
244
+ angular.module('$strap.directives').directive('bsDatepicker', [
245
+ '$timeout',
246
+ '$strapConfig',
247
+ function ($timeout, $strapConfig) {
248
+ var isAppleTouch = /(iP(a|o)d|iPhone)/g.test(navigator.userAgent);
249
+ var regexpMap = function regexpMapFn(language) {
250
+ language = language || 'en';
251
+ return {
252
+ '/': '[\\/]',
253
+ '-': '[-]',
254
+ '.': '[.]',
255
+ ' ': '[\\s]',
256
+ 'dd': '(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))',
257
+ 'd': '(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))',
258
+ 'mm': '(?:[0]?[1-9]|[1][012])',
259
+ 'm': '(?:[0]?[1-9]|[1][012])',
260
+ 'DD': '(?:' + $.fn.datepicker.dates[language].days.join('|') + ')',
261
+ 'D': '(?:' + $.fn.datepicker.dates[language].daysShort.join('|') + ')',
262
+ 'MM': '(?:' + $.fn.datepicker.dates[language].months.join('|') + ')',
263
+ 'M': '(?:' + $.fn.datepicker.dates[language].monthsShort.join('|') + ')',
264
+ 'yyyy': '(?:(?:[1]{1}[0-9]{1}[0-9]{1}[0-9]{1})|(?:[2]{1}[0-9]{3}))(?![[0-9]])',
265
+ 'yy': '(?:(?:[0-9]{1}[0-9]{1}))(?![[0-9]])'
266
+ };
267
+ };
268
+ var regexpForDateFormat = function regexpForDateFormatFn(format, language) {
269
+ var re = format, map = regexpMap(language), i;
270
+ i = 0;
271
+ angular.forEach(map, function (v, k) {
272
+ re = re.split(k).join('${' + i + '}');
273
+ i++;
274
+ });
275
+ i = 0;
276
+ angular.forEach(map, function (v, k) {
277
+ re = re.split('${' + i + '}').join(v);
278
+ i++;
279
+ });
280
+ return new RegExp('^' + re + '$', ['i']);
281
+ };
282
+ return {
283
+ restrict: 'A',
284
+ require: '?ngModel',
285
+ link: function postLink(scope, element, attrs, controller) {
286
+ var options = angular.extend({ autoclose: true }, $strapConfig.datepicker || {}), type = attrs.dateType || options.type || 'date';
287
+ angular.forEach([
288
+ 'format',
289
+ 'weekStart',
290
+ 'calendarWeeks',
291
+ 'startDate',
292
+ 'endDate',
293
+ 'daysOfWeekDisabled',
294
+ 'autoclose',
295
+ 'startView',
296
+ 'minViewMode',
297
+ 'todayBtn',
298
+ 'todayHighlight',
299
+ 'keyboardNavigation',
300
+ 'language',
301
+ 'forceParse'
302
+ ], function (key) {
303
+ if (angular.isDefined(attrs[key]))
304
+ options[key] = attrs[key];
305
+ });
306
+ 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), ISODateRegexp = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
307
+ if (controller) {
308
+ controller.$formatters.unshift(function (modelValue) {
309
+ if (modelValue && type === 'iso' && ISODateRegexp.test(modelValue)) {
310
+ return $.fn.datepicker.DPGlobal.parseDate(new Date(modelValue), $.fn.datepicker.DPGlobal.parseFormat(readFormat), language);
311
+ } else if (modelValue && type === 'date' && angular.isString(modelValue)) {
312
+ return $.fn.datepicker.DPGlobal.parseDate(modelValue, $.fn.datepicker.DPGlobal.parseFormat(readFormat), language);
313
+ } else {
314
+ return modelValue;
315
+ }
316
+ });
317
+ controller.$parsers.unshift(function (viewValue) {
318
+ if (!viewValue) {
319
+ controller.$setValidity('date', true);
320
+ return null;
321
+ } else if ((type === 'date' || type === 'iso') && angular.isDate(viewValue)) {
322
+ controller.$setValidity('date', true);
323
+ return viewValue;
324
+ } else if (angular.isString(viewValue) && dateFormatRegexp.test(viewValue)) {
325
+ controller.$setValidity('date', true);
326
+ if (isAppleTouch)
327
+ return new Date(viewValue);
328
+ return type === 'string' ? viewValue : $.fn.datepicker.DPGlobal.parseDate(viewValue, $.fn.datepicker.DPGlobal.parseFormat(format), language);
329
+ } else {
330
+ controller.$setValidity('date', false);
331
+ return undefined;
332
+ }
333
+ });
334
+ controller.$render = function ngModelRender() {
335
+ if (isAppleTouch) {
336
+ var date = controller.$viewValue ? $.fn.datepicker.DPGlobal.formatDate(controller.$viewValue, $.fn.datepicker.DPGlobal.parseFormat(format), language) : '';
337
+ element.val(date);
338
+ return date;
339
+ }
340
+ if (!controller.$viewValue)
341
+ element.val('');
342
+ return element.datepicker('update', controller.$viewValue);
343
+ };
344
+ }
345
+ if (isAppleTouch) {
346
+ element.prop('type', 'date').css('-webkit-appearance', 'textfield');
347
+ } else {
348
+ if (controller) {
349
+ element.on('changeDate', function (ev) {
350
+ scope.$apply(function () {
351
+ controller.$setViewValue(type === 'string' ? element.val() : ev.date);
352
+ });
353
+ });
354
+ }
355
+ element.datepicker(angular.extend(options, {
356
+ format: format,
357
+ language: language
358
+ }));
359
+ scope.$on('$destroy', function () {
360
+ var datepicker = element.data('datepicker');
361
+ if (datepicker) {
362
+ datepicker.picker.remove();
363
+ element.data('datepicker', null);
364
+ }
365
+ });
366
+ attrs.$observe('startDate', function (value) {
367
+ element.datepicker('setStartDate', value);
368
+ });
369
+ attrs.$observe('endDate', function (value) {
370
+ element.datepicker('setEndDate', value);
371
+ });
372
+ }
373
+ var component = element.siblings('[data-toggle="datepicker"]');
374
+ if (component.length) {
375
+ component.on('click', function () {
376
+ if (!element.prop('disabled')) {
377
+ element.trigger('focus');
378
+ }
379
+ });
380
+ }
381
+ }
382
+ };
383
+ }
384
+ ]);
385
+ angular.module('$strap.directives').directive('bsDropdown', [
386
+ '$parse',
387
+ '$compile',
388
+ '$timeout',
389
+ function ($parse, $compile, $timeout) {
390
+ var buildTemplate = function (items, ul) {
391
+ if (!ul)
392
+ ul = [
393
+ '<ul class="dropdown-menu" role="menu" aria-labelledby="drop1">',
394
+ '</ul>'
395
+ ];
396
+ angular.forEach(items, function (item, index) {
397
+ if (item.divider)
398
+ return ul.splice(index + 1, 0, '<li class="divider"></li>');
399
+ 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.icon && '<i class="' + item.icon + '"></i>&nbsp;' || '') + (item.text || '') + '</a>';
400
+ if (item.submenu && item.submenu.length)
401
+ li += buildTemplate(item.submenu).join('\n');
402
+ li += '</li>';
403
+ ul.splice(index + 1, 0, li);
404
+ });
405
+ return ul;
406
+ };
407
+ return {
408
+ restrict: 'EA',
409
+ scope: true,
410
+ link: function postLink(scope, iElement, iAttrs) {
411
+ var getter = $parse(iAttrs.bsDropdown), items = getter(scope);
412
+ $timeout(function () {
413
+ if (!angular.isArray(items)) {
414
+ }
415
+ var dropdown = angular.element(buildTemplate(items).join(''));
416
+ dropdown.insertAfter(iElement);
417
+ $compile(iElement.next('ul.dropdown-menu'))(scope);
418
+ });
419
+ iElement.addClass('dropdown-toggle').attr('data-toggle', 'dropdown');
420
+ }
421
+ };
422
+ }
423
+ ]);
424
+ angular.module('$strap.directives').factory('$modal', [
425
+ '$rootScope',
426
+ '$compile',
427
+ '$http',
428
+ '$timeout',
429
+ '$q',
430
+ '$templateCache',
431
+ '$strapConfig',
432
+ function ($rootScope, $compile, $http, $timeout, $q, $templateCache, $strapConfig) {
433
+ var ModalFactory = function ModalFactoryFn(config) {
434
+ function Modal(config) {
435
+ var options = angular.extend({ show: true }, $strapConfig.modal, config), scope = options.scope ? options.scope : $rootScope.$new(), templateUrl = options.template;
436
+ return $q.when($templateCache.get(templateUrl) || $http.get(templateUrl, { cache: true }).then(function (res) {
437
+ return res.data;
438
+ })).then(function onSuccess(template) {
439
+ var id = templateUrl.replace('.html', '').replace(/[\/|\.|:]/g, '-') + '-' + scope.$id;
440
+ var $modal = $('<div class="modal hide" tabindex="-1"></div>').attr('id', id).addClass('fade').html(template);
441
+ if (options.modalClass)
442
+ $modal.addClass(options.modalClass);
443
+ $('body').append($modal);
444
+ $timeout(function () {
445
+ $compile($modal)(scope);
446
+ });
447
+ scope.$modal = function (name) {
448
+ $modal.modal(name);
449
+ };
450
+ angular.forEach([
451
+ 'show',
452
+ 'hide'
453
+ ], function (name) {
454
+ scope[name] = function () {
455
+ $modal.modal(name);
456
+ };
457
+ });
458
+ scope.dismiss = scope.hide;
459
+ angular.forEach([
460
+ 'show',
461
+ 'shown',
462
+ 'hide',
463
+ 'hidden'
464
+ ], function (name) {
465
+ $modal.on(name, function (ev) {
466
+ scope.$emit('modal-' + name, ev);
467
+ });
468
+ });
469
+ $modal.on('shown', function (ev) {
470
+ $('input[autofocus], textarea[autofocus]', $modal).first().trigger('focus');
471
+ });
472
+ $modal.on('hidden', function (ev) {
473
+ if (!options.persist)
474
+ scope.$destroy();
475
+ });
476
+ scope.$on('$destroy', function () {
477
+ $modal.remove();
478
+ });
479
+ $modal.modal(options);
480
+ return $modal;
481
+ });
482
+ }
483
+ return new Modal(config);
484
+ };
485
+ return ModalFactory;
486
+ }
487
+ ]).directive('bsModal', [
488
+ '$q',
489
+ '$modal',
490
+ function ($q, $modal) {
491
+ return {
492
+ restrict: 'A',
493
+ scope: true,
494
+ link: function postLink(scope, iElement, iAttrs, controller) {
495
+ var options = {
496
+ template: scope.$eval(iAttrs.bsModal),
497
+ persist: true,
498
+ show: false,
499
+ scope: scope
500
+ };
501
+ angular.forEach([
502
+ 'modalClass',
503
+ 'backdrop',
504
+ 'keyboard'
505
+ ], function (key) {
506
+ if (angular.isDefined(iAttrs[key]))
507
+ options[key] = iAttrs[key];
508
+ });
509
+ $q.when($modal(options)).then(function onSuccess(modal) {
510
+ iElement.attr('data-target', '#' + modal.attr('id')).attr('data-toggle', 'modal');
511
+ });
512
+ }
513
+ };
514
+ }
515
+ ]);
516
+ angular.module('$strap.directives').directive('bsNavbar', [
517
+ '$location',
518
+ function ($location) {
519
+ return {
520
+ restrict: 'A',
521
+ link: function postLink(scope, element, attrs, controller) {
522
+ scope.$watch(function () {
523
+ return $location.path();
524
+ }, function (newValue, oldValue) {
525
+ $('li[data-match-route]', element).each(function (k, li) {
526
+ var $li = angular.element(li), pattern = $li.attr('data-match-route'), regexp = new RegExp('^' + pattern + '$', ['i']);
527
+ if (regexp.test(newValue)) {
528
+ $li.addClass('active').find('.collapse.in').collapse('hide');
529
+ } else {
530
+ $li.removeClass('active');
531
+ }
532
+ });
533
+ });
534
+ }
535
+ };
536
+ }
537
+ ]);
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
+ angular.module('$strap.directives').directive('bsSelect', [
638
+ '$timeout',
639
+ function ($timeout) {
640
+ 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+(.*)$/;
641
+ return {
642
+ restrict: 'A',
643
+ require: '?ngModel',
644
+ link: function postLink(scope, element, attrs, controller) {
645
+ var options = scope.$eval(attrs.bsSelect) || {};
646
+ $timeout(function () {
647
+ element.selectpicker(options);
648
+ element.next().removeClass('ng-scope');
649
+ });
650
+ if (controller) {
651
+ scope.$watch(attrs.ngModel, function (newValue, oldValue) {
652
+ if (!angular.equals(newValue, oldValue)) {
653
+ element.selectpicker('refresh');
654
+ }
655
+ });
656
+ }
657
+ }
658
+ };
659
+ }
660
+ ]);
661
+ angular.module('$strap.directives').directive('bsTabs', [
662
+ '$parse',
663
+ '$compile',
664
+ '$timeout',
665
+ function ($parse, $compile, $timeout) {
666
+ 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>';
667
+ return {
668
+ restrict: 'A',
669
+ require: '?ngModel',
670
+ priority: 0,
671
+ scope: true,
672
+ template: template,
673
+ replace: true,
674
+ transclude: true,
675
+ compile: function compile(tElement, tAttrs, transclude) {
676
+ return function postLink(scope, iElement, iAttrs, controller) {
677
+ var getter = $parse(iAttrs.bsTabs), setter = getter.assign, value = getter(scope);
678
+ scope.panes = [];
679
+ var $tabs = iElement.find('ul.nav-tabs');
680
+ var $panes = iElement.find('div.tab-content');
681
+ var activeTab = 0, id, title, active;
682
+ $timeout(function () {
683
+ $panes.find('[data-title], [data-tab]').each(function (index) {
684
+ var $this = angular.element(this);
685
+ id = 'tab-' + scope.$id + '-' + index;
686
+ title = $this.data('title') || $this.data('tab');
687
+ active = !active && $this.hasClass('active');
688
+ $this.attr('id', id).addClass('tab-pane');
689
+ if (iAttrs.fade)
690
+ $this.addClass('fade');
691
+ scope.panes.push({
692
+ id: id,
693
+ title: title,
694
+ content: this.innerHTML,
695
+ active: active
696
+ });
697
+ });
698
+ if (scope.panes.length && !active) {
699
+ $panes.find('.tab-pane:first-child').addClass('active' + (iAttrs.fade ? ' in' : ''));
700
+ scope.panes[0].active = true;
701
+ }
702
+ });
703
+ if (controller) {
704
+ iElement.on('show', function (ev) {
705
+ var $target = $(ev.target);
706
+ scope.$apply(function () {
707
+ controller.$setViewValue($target.data('index'));
708
+ });
709
+ });
710
+ scope.$watch(iAttrs.ngModel, function (newValue, oldValue) {
711
+ if (angular.isUndefined(newValue))
712
+ return;
713
+ activeTab = newValue;
714
+ setTimeout(function () {
715
+ var $next = $($tabs[0].querySelectorAll('li')[newValue * 1]);
716
+ if (!$next.hasClass('active')) {
717
+ $next.children('a').tab('show');
718
+ }
719
+ });
720
+ });
721
+ }
722
+ };
723
+ }
724
+ };
725
+ }
726
+ ]);
727
+ angular.module('$strap.directives').directive('bsTimepicker', [
728
+ '$timeout',
729
+ '$strapConfig',
730
+ function ($timeout, $strapConfig) {
731
+ 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))?)';
732
+ return {
733
+ restrict: 'A',
734
+ require: '?ngModel',
735
+ link: function postLink(scope, element, attrs, controller) {
736
+ var pad = function (number) {
737
+ var r = String(number);
738
+ if (r.length === 1) {
739
+ r = '0' + r;
740
+ }
741
+ return r;
742
+ };
743
+ var options = angular.extend({ openOnFocus: false }, $strapConfig.timepicker);
744
+ var defaultTime = attrs.defaultTime || options.defaultTime || false;
745
+ options.defaultTime = false;
746
+ var type = attrs.timeType || options.timeType || 'string';
747
+ angular.forEach([
748
+ 'template',
749
+ 'minuteStep',
750
+ 'showSeconds',
751
+ 'secondStep',
752
+ 'showMeridian',
753
+ 'showInputs',
754
+ 'disableFocus',
755
+ 'modalBackdrop',
756
+ 'openOnFocus'
757
+ ], function (key) {
758
+ if (angular.isDefined(attrs[key])) {
759
+ if (attrs[key] === 'true' || attrs[key] === 'false') {
760
+ options[key] = attrs[key] === 'true';
761
+ } else if (/^\-?([0-9]+|Infinity)$/.test(attrs[key])) {
762
+ options[key] = parseInt(attrs[key], 10);
763
+ } else {
764
+ options[key] = attrs[key];
765
+ }
766
+ }
767
+ });
768
+ if (controller) {
769
+ element.on('changeTime.timepicker', function (ev) {
770
+ $timeout(function () {
771
+ if (type === 'string') {
772
+ controller.$setViewValue(element.val());
773
+ } else if (type === 'date') {
774
+ var date = new Date();
775
+ date.setHours(ev.time.hours);
776
+ date.setMinutes(ev.time.minutes);
777
+ controller.$setViewValue(date);
778
+ }
779
+ });
780
+ });
781
+ var timeRegExp = new RegExp('^' + TIME_REGEXP + '$', ['i']);
782
+ controller.$formatters.unshift(function (modelValue) {
783
+ if (modelValue) {
784
+ if (type === 'date') {
785
+ controller.$setViewValue(new Date(modelValue));
786
+ element.val(new Date(modelValue));
787
+ return new Date(modelValue);
788
+ }
789
+ controller.$setViewValue(modelValue);
790
+ element.val(modelValue);
791
+ return modelValue;
792
+ } else if (defaultTime) {
793
+ if (defaultTime === 'current') {
794
+ if (type === 'date') {
795
+ controller.$setViewValue(new Date());
796
+ element.val(new Date());
797
+ return new Date();
798
+ } else {
799
+ var currentDate = new Date();
800
+ var currentTime = pad(currentDate.getHours()) + ':' + pad(currentDate.getMinutes());
801
+ controller.$setViewValue(currentDate);
802
+ element.val(currentDate);
803
+ return currentDate;
804
+ }
805
+ } else {
806
+ controller.$setViewValue(defaultTime);
807
+ element.val(defaultTime);
808
+ return defaultTime;
809
+ }
810
+ }
811
+ controller.$setViewValue(element.val());
812
+ return element.val();
813
+ });
814
+ controller.$render = function ngModelRender() {
815
+ if (controller.$viewValue) {
816
+ if (type === 'date') {
817
+ var renderedDate;
818
+ if (timeRegExp.test(controller.$viewValue)) {
819
+ renderedDate = new Date();
820
+ var dateParts = controller.$viewValue.split(':');
821
+ var hours = !isNaN(parseInt(dateParts[0], 10)) ? parseInt(dateParts[0], 10) : '0';
822
+ var minutes = !isNaN(parseInt(dateParts[1], 10)) ? parseInt(dateParts[1], 10) : '0';
823
+ renderedDate.setHours(hours);
824
+ renderedDate.setMinutes(minutes);
825
+ element.val(controller.$viewValue);
826
+ controller.$setViewValue(renderedDate);
827
+ return renderedDate;
828
+ } else {
829
+ renderedDate = new Date(controller.$viewValue);
830
+ var modelRender = renderedDate !== 'Invalid Date' ? pad(renderedDate.getHours()) + ':' + pad(renderedDate.getMinutes()) : '';
831
+ element.val(modelRender);
832
+ controller.$setViewValue(renderedDate);
833
+ return renderedDate;
834
+ }
835
+ }
836
+ element.val(controller.$viewValue);
837
+ controller.$setViewValue(controller.$viewValue);
838
+ return controller.$viewValue;
839
+ }
840
+ element.val('');
841
+ return '';
842
+ };
843
+ controller.$parsers.unshift(function (viewValue) {
844
+ if (!viewValue || type === 'string' && timeRegExp.test(viewValue)) {
845
+ controller.$setValidity('time', true);
846
+ return viewValue;
847
+ } else if (type === 'date' && typeof viewValue === 'object' && viewValue.toString() !== 'Invalid Date') {
848
+ controller.$setValidity('time', true);
849
+ return viewValue;
850
+ } else {
851
+ controller.$setValidity('time', false);
852
+ return;
853
+ }
854
+ });
855
+ }
856
+ element.attr('data-toggle', 'timepicker');
857
+ element.parent().addClass('bootstrap-timepicker');
858
+ element.timepicker(options || {});
859
+ var timepicker = element.data('timepicker');
860
+ var component = element.siblings('[data-toggle="timepicker"]');
861
+ if (component.length) {
862
+ component.on('click', $.proxy(timepicker.showWidget, timepicker));
863
+ }
864
+ if (options.openOnFocus) {
865
+ element.on('focus', $.proxy(timepicker.showWidget, timepicker));
866
+ }
867
+ }
868
+ };
869
+ }
870
+ ]);
871
+ angular.module('$strap.directives').directive('bsTooltip', [
872
+ '$parse',
873
+ '$compile',
874
+ function ($parse, $compile) {
875
+ return {
876
+ restrict: 'A',
877
+ scope: true,
878
+ link: function postLink(scope, element, attrs, ctrl) {
879
+ var getter = $parse(attrs.bsTooltip), setter = getter.assign, value = getter(scope);
880
+ scope.$watch(attrs.bsTooltip, function (newValue, oldValue) {
881
+ value = newValue;
882
+ });
883
+ if (!!attrs.unique) {
884
+ element.on('show', function (ev) {
885
+ $('.tooltip.in').each(function () {
886
+ var $this = $(this), tooltip = $this.data('tooltip');
887
+ if (tooltip && !tooltip.$element.is(element)) {
888
+ $this.tooltip('hide');
889
+ }
890
+ });
891
+ });
892
+ }
893
+ element.tooltip({
894
+ title: function () {
895
+ return angular.isFunction(value) ? value.apply(null, arguments) : value;
896
+ },
897
+ html: true
898
+ });
899
+ var tooltip = element.data('tooltip');
900
+ tooltip.show = function () {
901
+ var r = $.fn.tooltip.Constructor.prototype.show.apply(this, arguments);
902
+ this.tip().data('tooltip', this);
903
+ return r;
904
+ };
905
+ scope._tooltip = function (event) {
906
+ element.tooltip(event);
907
+ };
908
+ scope.hide = function () {
909
+ element.tooltip('hide');
910
+ };
911
+ scope.show = function () {
912
+ element.tooltip('show');
913
+ };
914
+ scope.dismiss = scope.hide;
915
+ }
916
+ };
917
+ }
918
+ ]);
919
+ angular.module('$strap.directives').directive('bsTypeahead', [
920
+ '$parse',
921
+ function ($parse) {
922
+ return {
923
+ restrict: 'A',
924
+ require: '?ngModel',
925
+ link: function postLink(scope, element, attrs, controller) {
926
+ var getter = $parse(attrs.bsTypeahead), setter = getter.assign, value = getter(scope);
927
+ scope.$watch(attrs.bsTypeahead, function (newValue, oldValue) {
928
+ if (newValue !== oldValue) {
929
+ value = newValue;
930
+ }
931
+ });
932
+ element.attr('data-provide', 'typeahead');
933
+ element.typeahead({
934
+ source: function (query) {
935
+ return angular.isFunction(value) ? value.apply(null, arguments) : value;
936
+ },
937
+ minLength: attrs.minLength || 1,
938
+ items: attrs.items,
939
+ updater: function (value) {
940
+ if (controller) {
941
+ scope.$apply(function () {
942
+ controller.$setViewValue(value);
943
+ });
944
+ }
945
+ scope.$emit('typeahead-updated', value, attrs.id);
946
+ return value;
947
+ }
948
+ });
949
+ var typeahead = element.data('typeahead');
950
+ typeahead.lookup = function (ev) {
951
+ var items;
952
+ this.query = this.$element.val() || '';
953
+ if (this.query.length < this.options.minLength) {
954
+ return this.shown ? this.hide() : this;
955
+ }
956
+ items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source;
957
+ return items ? this.process(items) : this;
958
+ };
959
+ if (!!attrs.matchAll) {
960
+ typeahead.matcher = function (item) {
961
+ return true;
962
+ };
963
+ }
964
+ if (attrs.minLength === '0') {
965
+ setTimeout(function () {
966
+ element.on('focus', function () {
967
+ element.val().length === 0 && setTimeout(element.typeahead.bind(element, 'lookup'), 200);
968
+ });
969
+ });
970
+ }
971
+ }
972
+ };
973
+ }
974
+ ]);
975
+ }(window, document));