angularstrap-rails 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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));