angular-strap-rails 2.0.1

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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +17 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +45 -0
  7. data/Rakefile +1 -0
  8. data/angular-strap-rails.gemspec +15 -0
  9. data/lib/angular-strap-rails.rb +8 -0
  10. data/lib/angular-strap-rails/version.rb +5 -0
  11. data/vendor/.DS_Store +0 -0
  12. data/vendor/assets/.DS_Store +0 -0
  13. data/vendor/assets/javascripts/.DS_Store +0 -0
  14. data/vendor/assets/javascripts/angular-strap.coffee +0 -0
  15. data/vendor/assets/javascripts/angular-strap/.DS_Store +0 -0
  16. data/vendor/assets/javascripts/angular-strap/datepicker.coffee +11 -0
  17. data/vendor/assets/javascripts/angular-strap/modal.coffee +9 -0
  18. data/vendor/assets/javascripts/dist/angular-strap.js +3682 -0
  19. data/vendor/assets/javascripts/dist/angular-strap.tpl.js +100 -0
  20. data/vendor/assets/javascripts/dist/modules/affix.js +191 -0
  21. data/vendor/assets/javascripts/dist/modules/alert.js +114 -0
  22. data/vendor/assets/javascripts/dist/modules/alert.tpl.js +14 -0
  23. data/vendor/assets/javascripts/dist/modules/aside.js +96 -0
  24. data/vendor/assets/javascripts/dist/modules/aside.tpl.js +14 -0
  25. data/vendor/assets/javascripts/dist/modules/button.js +141 -0
  26. data/vendor/assets/javascripts/dist/modules/date-parser.js +150 -0
  27. data/vendor/assets/javascripts/dist/modules/datepicker.js +583 -0
  28. data/vendor/assets/javascripts/dist/modules/datepicker.tpl.js +14 -0
  29. data/vendor/assets/javascripts/dist/modules/debounce.js +60 -0
  30. data/vendor/assets/javascripts/dist/modules/dimensions.js +142 -0
  31. data/vendor/assets/javascripts/dist/modules/dropdown.js +124 -0
  32. data/vendor/assets/javascripts/dist/modules/dropdown.tpl.js +14 -0
  33. data/vendor/assets/javascripts/dist/modules/modal.js +282 -0
  34. data/vendor/assets/javascripts/dist/modules/modal.tpl.js +14 -0
  35. data/vendor/assets/javascripts/dist/modules/navbar.js +55 -0
  36. data/vendor/assets/javascripts/dist/modules/parse-options.js +51 -0
  37. data/vendor/assets/javascripts/dist/modules/popover.js +100 -0
  38. data/vendor/assets/javascripts/dist/modules/popover.tpl.js +14 -0
  39. data/vendor/assets/javascripts/dist/modules/raf.js +45 -0
  40. data/vendor/assets/javascripts/dist/modules/scrollspy.js +229 -0
  41. data/vendor/assets/javascripts/dist/modules/select.js +281 -0
  42. data/vendor/assets/javascripts/dist/modules/select.tpl.js +14 -0
  43. data/vendor/assets/javascripts/dist/modules/tab.js +69 -0
  44. data/vendor/assets/javascripts/dist/modules/tab.tpl.js +14 -0
  45. data/vendor/assets/javascripts/dist/modules/timepicker.js +430 -0
  46. data/vendor/assets/javascripts/dist/modules/timepicker.tpl.js +14 -0
  47. data/vendor/assets/javascripts/dist/modules/tooltip.js +405 -0
  48. data/vendor/assets/javascripts/dist/modules/tooltip.tpl.js +14 -0
  49. data/vendor/assets/javascripts/dist/modules/typeahead.js +225 -0
  50. data/vendor/assets/javascripts/dist/modules/typeahead.tpl.js +14 -0
  51. data/vendor/assets/stylesheets/angular-strap.css +564 -0
  52. metadata +94 -0
@@ -0,0 +1,14 @@
1
+ /**
2
+ * angular-strap
3
+ * @version v2.0.1 - 2014-04-10
4
+ * @link http://mgcrea.github.io/angular-strap
5
+ * @author Olivier Louvignes (olivier@mg-crea.com)
6
+ * @license MIT License, http://www.opensource.org/licenses/MIT
7
+ */
8
+ 'use strict';
9
+ angular.module('mgcrea.ngStrap.datepicker').run([
10
+ '$templateCache',
11
+ function ($templateCache) {
12
+ $templateCache.put('datepicker/datepicker.tpl.html', '<div class="dropdown-menu datepicker" ng-class="\'datepicker-mode-\' + $mode" style="max-width: 320px"><table style="table-layout: fixed; height: 100%; width: 100%"><thead><tr class="text-center"><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$selectPane(-1)"><i class="glyphicon glyphicon-chevron-left"></i></button></th><th colspan="{{ rows[0].length - 2 }}"><button tabindex="-1" type="button" class="btn btn-default btn-block text-strong" ng-click="$toggleMode()"><strong style="text-transform: capitalize" ng-bind="title"></strong></button></th><th><button tabindex="-1" type="button" class="btn btn-default pull-right" ng-click="$selectPane(+1)"><i class="glyphicon glyphicon-chevron-right"></i></button></th></tr><tr ng-show="labels" ng-bind-html="labels"></tr></thead><tbody><tr ng-repeat="(i, row) in rows" height="{{ 100 / rows.length }}%"><td class="text-center" ng-repeat="(j, el) in row"><button tabindex="-1" type="button" class="btn btn-default" style="width: 100%" ng-class="{\'btn-primary\': el.selected}" ng-click="$select(el.date)" ng-disabled="el.disabled"><span ng-class="{\'text-muted\': el.muted}" ng-bind="el.label"></span></button></td></tr></tbody></table></div>');
13
+ }
14
+ ]);
@@ -0,0 +1,60 @@
1
+ /**
2
+ * angular-strap
3
+ * @version v2.0.1 - 2014-04-10
4
+ * @link http://mgcrea.github.io/angular-strap
5
+ * @author Olivier Louvignes (olivier@mg-crea.com)
6
+ * @license MIT License, http://www.opensource.org/licenses/MIT
7
+ */
8
+ 'use strict';
9
+ angular.module('mgcrea.ngStrap.helpers.debounce', []).constant('debounce', function (func, wait, immediate) {
10
+ var timeout, args, context, timestamp, result;
11
+ return function () {
12
+ context = this;
13
+ args = arguments;
14
+ timestamp = new Date();
15
+ var later = function () {
16
+ var last = new Date() - timestamp;
17
+ if (last < wait) {
18
+ timeout = setTimeout(later, wait - last);
19
+ } else {
20
+ timeout = null;
21
+ if (!immediate)
22
+ result = func.apply(context, args);
23
+ }
24
+ };
25
+ var callNow = immediate && !timeout;
26
+ if (!timeout) {
27
+ timeout = setTimeout(later, wait);
28
+ }
29
+ if (callNow)
30
+ result = func.apply(context, args);
31
+ return result;
32
+ };
33
+ }).constant('throttle', function (func, wait, options) {
34
+ var context, args, result;
35
+ var timeout = null;
36
+ var previous = 0;
37
+ options || (options = {});
38
+ var later = function () {
39
+ previous = options.leading === false ? 0 : new Date();
40
+ timeout = null;
41
+ result = func.apply(context, args);
42
+ };
43
+ return function () {
44
+ var now = new Date();
45
+ if (!previous && options.leading === false)
46
+ previous = now;
47
+ var remaining = wait - (now - previous);
48
+ context = this;
49
+ args = arguments;
50
+ if (remaining <= 0) {
51
+ clearTimeout(timeout);
52
+ timeout = null;
53
+ previous = now;
54
+ result = func.apply(context, args);
55
+ } else if (!timeout && options.trailing !== false) {
56
+ timeout = setTimeout(later, remaining);
57
+ }
58
+ return result;
59
+ };
60
+ });
@@ -0,0 +1,142 @@
1
+ /**
2
+ * angular-strap
3
+ * @version v2.0.1 - 2014-04-10
4
+ * @link http://mgcrea.github.io/angular-strap
5
+ * @author Olivier Louvignes (olivier@mg-crea.com)
6
+ * @license MIT License, http://www.opensource.org/licenses/MIT
7
+ */
8
+ 'use strict';
9
+ angular.module('mgcrea.ngStrap.helpers.dimensions', []).factory('dimensions', [
10
+ '$document',
11
+ '$window',
12
+ function ($document, $window) {
13
+ var jqLite = angular.element;
14
+ var fn = {};
15
+ /**
16
+ * Test the element nodeName
17
+ * @param element
18
+ * @param name
19
+ */
20
+ var nodeName = fn.nodeName = function (element, name) {
21
+ return element.nodeName && element.nodeName.toLowerCase() === name.toLowerCase();
22
+ };
23
+ /**
24
+ * Returns the element computed style
25
+ * @param element
26
+ * @param prop
27
+ * @param extra
28
+ */
29
+ fn.css = function (element, prop, extra) {
30
+ var value;
31
+ if (element.currentStyle) {
32
+ //IE
33
+ value = element.currentStyle[prop];
34
+ } else if (window.getComputedStyle) {
35
+ value = window.getComputedStyle(element)[prop];
36
+ } else {
37
+ value = element.style[prop];
38
+ }
39
+ return extra === true ? parseFloat(value) || 0 : value;
40
+ };
41
+ /**
42
+ * Provides read-only equivalent of jQuery's offset function:
43
+ * @required-by bootstrap-tooltip, bootstrap-affix
44
+ * @url http://api.jquery.com/offset/
45
+ * @param element
46
+ */
47
+ fn.offset = function (element) {
48
+ var boxRect = element.getBoundingClientRect();
49
+ var docElement = element.ownerDocument;
50
+ return {
51
+ width: element.offsetWidth,
52
+ height: element.offsetHeight,
53
+ top: boxRect.top + (window.pageYOffset || docElement.documentElement.scrollTop) - (docElement.documentElement.clientTop || 0),
54
+ left: boxRect.left + (window.pageXOffset || docElement.documentElement.scrollLeft) - (docElement.documentElement.clientLeft || 0)
55
+ };
56
+ };
57
+ /**
58
+ * Provides read-only equivalent of jQuery's position function
59
+ * @required-by bootstrap-tooltip, bootstrap-affix
60
+ * @url http://api.jquery.com/offset/
61
+ * @param element
62
+ */
63
+ fn.position = function (element) {
64
+ var offsetParentRect = {
65
+ top: 0,
66
+ left: 0
67
+ }, offsetParentElement, offset;
68
+ // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
69
+ if (fn.css(element, 'position') === 'fixed') {
70
+ // We assume that getBoundingClientRect is available when computed position is fixed
71
+ offset = element.getBoundingClientRect();
72
+ } else {
73
+ // Get *real* offsetParentElement
74
+ offsetParentElement = offsetParent(element);
75
+ offset = fn.offset(element);
76
+ // Get correct offsets
77
+ offset = fn.offset(element);
78
+ if (!nodeName(offsetParentElement, 'html')) {
79
+ offsetParentRect = fn.offset(offsetParentElement);
80
+ }
81
+ // Add offsetParent borders
82
+ offsetParentRect.top += fn.css(offsetParentElement, 'borderTopWidth', true);
83
+ offsetParentRect.left += fn.css(offsetParentElement, 'borderLeftWidth', true);
84
+ }
85
+ // Subtract parent offsets and element margins
86
+ return {
87
+ width: element.offsetWidth,
88
+ height: element.offsetHeight,
89
+ top: offset.top - offsetParentRect.top - fn.css(element, 'marginTop', true),
90
+ left: offset.left - offsetParentRect.left - fn.css(element, 'marginLeft', true)
91
+ };
92
+ };
93
+ /**
94
+ * Returns the closest, non-statically positioned offsetParent of a given element
95
+ * @required-by fn.position
96
+ * @param element
97
+ */
98
+ var offsetParent = function offsetParentElement(element) {
99
+ var docElement = element.ownerDocument;
100
+ var offsetParent = element.offsetParent || docElement;
101
+ if (nodeName(offsetParent, '#document'))
102
+ return docElement.documentElement;
103
+ while (offsetParent && !nodeName(offsetParent, 'html') && fn.css(offsetParent, 'position') === 'static') {
104
+ offsetParent = offsetParent.offsetParent;
105
+ }
106
+ return offsetParent || docElement.documentElement;
107
+ };
108
+ /**
109
+ * Provides equivalent of jQuery's height function
110
+ * @required-by bootstrap-affix
111
+ * @url http://api.jquery.com/height/
112
+ * @param element
113
+ * @param outer
114
+ */
115
+ fn.height = function (element, outer) {
116
+ var value = element.offsetHeight;
117
+ if (outer) {
118
+ value += fn.css(element, 'marginTop', true) + fn.css(element, 'marginBottom', true);
119
+ } else {
120
+ value -= fn.css(element, 'paddingTop', true) + fn.css(element, 'paddingBottom', true) + fn.css(element, 'borderTopWidth', true) + fn.css(element, 'borderBottomWidth', true);
121
+ }
122
+ return value;
123
+ };
124
+ /**
125
+ * Provides equivalent of jQuery's height function
126
+ * @required-by bootstrap-affix
127
+ * @url http://api.jquery.com/width/
128
+ * @param element
129
+ * @param outer
130
+ */
131
+ fn.width = function (element, outer) {
132
+ var value = element.offsetWidth;
133
+ if (outer) {
134
+ value += fn.css(element, 'marginLeft', true) + fn.css(element, 'marginRight', true);
135
+ } else {
136
+ value -= fn.css(element, 'paddingLeft', true) + fn.css(element, 'paddingRight', true) + fn.css(element, 'borderLeftWidth', true) + fn.css(element, 'borderRightWidth', true);
137
+ }
138
+ return value;
139
+ };
140
+ return fn;
141
+ }
142
+ ]);
@@ -0,0 +1,124 @@
1
+ /**
2
+ * angular-strap
3
+ * @version v2.0.1 - 2014-04-10
4
+ * @link http://mgcrea.github.io/angular-strap
5
+ * @author Olivier Louvignes (olivier@mg-crea.com)
6
+ * @license MIT License, http://www.opensource.org/licenses/MIT
7
+ */
8
+ 'use strict';
9
+ angular.module('mgcrea.ngStrap.dropdown', ['mgcrea.ngStrap.tooltip']).provider('$dropdown', function () {
10
+ var defaults = this.defaults = {
11
+ animation: 'am-fade',
12
+ prefixClass: 'dropdown',
13
+ placement: 'bottom-left',
14
+ template: 'dropdown/dropdown.tpl.html',
15
+ trigger: 'click',
16
+ container: false,
17
+ keyboard: true,
18
+ html: false,
19
+ delay: 0
20
+ };
21
+ this.$get = [
22
+ '$window',
23
+ '$rootScope',
24
+ '$tooltip',
25
+ function ($window, $rootScope, $tooltip) {
26
+ var bodyEl = angular.element($window.document.body);
27
+ var matchesSelector = Element.prototype.matchesSelector || Element.prototype.webkitMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector;
28
+ function DropdownFactory(element, config) {
29
+ var $dropdown = {};
30
+ // Common vars
31
+ var options = angular.extend({}, defaults, config);
32
+ var scope = $dropdown.$scope = options.scope && options.scope.$new() || $rootScope.$new();
33
+ $dropdown = $tooltip(element, options);
34
+ // Protected methods
35
+ $dropdown.$onKeyDown = function (evt) {
36
+ if (!/(38|40)/.test(evt.keyCode))
37
+ return;
38
+ evt.preventDefault();
39
+ evt.stopPropagation();
40
+ // Retrieve focused index
41
+ var items = angular.element($dropdown.$element[0].querySelectorAll('li:not(.divider) a'));
42
+ if (!items.length)
43
+ return;
44
+ var index;
45
+ angular.forEach(items, function (el, i) {
46
+ if (matchesSelector && matchesSelector.call(el, ':focus'))
47
+ index = i;
48
+ });
49
+ // Navigate with keyboard
50
+ if (evt.keyCode === 38 && index > 0)
51
+ index--;
52
+ else if (evt.keyCode === 40 && index < items.length - 1)
53
+ index++;
54
+ else if (angular.isUndefined(index))
55
+ index = 0;
56
+ items.eq(index)[0].focus();
57
+ };
58
+ // Overrides
59
+ var show = $dropdown.show;
60
+ $dropdown.show = function () {
61
+ show();
62
+ setTimeout(function () {
63
+ options.keyboard && $dropdown.$element.on('keydown', $dropdown.$onKeyDown);
64
+ bodyEl.on('click', onBodyClick);
65
+ });
66
+ };
67
+ var hide = $dropdown.hide;
68
+ $dropdown.hide = function () {
69
+ options.keyboard && $dropdown.$element.off('keydown', $dropdown.$onKeyDown);
70
+ bodyEl.off('click', onBodyClick);
71
+ hide();
72
+ };
73
+ // Private functions
74
+ function onBodyClick(evt) {
75
+ if (evt.target === element[0])
76
+ return;
77
+ return evt.target !== element[0] && $dropdown.hide();
78
+ }
79
+ return $dropdown;
80
+ }
81
+ return DropdownFactory;
82
+ }
83
+ ];
84
+ }).directive('bsDropdown', [
85
+ '$window',
86
+ '$location',
87
+ '$sce',
88
+ '$dropdown',
89
+ function ($window, $location, $sce, $dropdown) {
90
+ return {
91
+ restrict: 'EAC',
92
+ scope: true,
93
+ link: function postLink(scope, element, attr, transclusion) {
94
+ // Directive options
95
+ var options = { scope: scope };
96
+ angular.forEach([
97
+ 'placement',
98
+ 'container',
99
+ 'delay',
100
+ 'trigger',
101
+ 'keyboard',
102
+ 'html',
103
+ 'animation',
104
+ 'template'
105
+ ], function (key) {
106
+ if (angular.isDefined(attr[key]))
107
+ options[key] = attr[key];
108
+ });
109
+ // Support scope as an object
110
+ attr.bsDropdown && scope.$watch(attr.bsDropdown, function (newValue, oldValue) {
111
+ scope.content = newValue;
112
+ }, true);
113
+ // Initialize dropdown
114
+ var dropdown = $dropdown(element, options);
115
+ // Garbage collection
116
+ scope.$on('$destroy', function () {
117
+ dropdown.destroy();
118
+ options = null;
119
+ dropdown = null;
120
+ });
121
+ }
122
+ };
123
+ }
124
+ ]);
@@ -0,0 +1,14 @@
1
+ /**
2
+ * angular-strap
3
+ * @version v2.0.1 - 2014-04-10
4
+ * @link http://mgcrea.github.io/angular-strap
5
+ * @author Olivier Louvignes (olivier@mg-crea.com)
6
+ * @license MIT License, http://www.opensource.org/licenses/MIT
7
+ */
8
+ 'use strict';
9
+ angular.module('mgcrea.ngStrap.dropdown').run([
10
+ '$templateCache',
11
+ function ($templateCache) {
12
+ $templateCache.put('dropdown/dropdown.tpl.html', '<ul tabindex="-1" class="dropdown-menu" role="menu"><li role="presentation" ng-class="{divider: item.divider}" ng-repeat="item in content"><a role="menuitem" tabindex="-1" ng-href="{{item.href}}" ng-if="!item.divider && item.href" ng-bind="item.text"></a> <a role="menuitem" tabindex="-1" href="javascript:void(0)" ng-if="!item.divider && item.click" ng-click="$eval(item.click);$hide()" ng-bind="item.text"></a></li></ul>');
13
+ }
14
+ ]);
@@ -0,0 +1,282 @@
1
+ /**
2
+ * angular-strap
3
+ * @version v2.0.1 - 2014-04-10
4
+ * @link http://mgcrea.github.io/angular-strap
5
+ * @author Olivier Louvignes (olivier@mg-crea.com)
6
+ * @license MIT License, http://www.opensource.org/licenses/MIT
7
+ */
8
+ 'use strict';
9
+ angular.module('mgcrea.ngStrap.modal', ['mgcrea.ngStrap.helpers.dimensions']).provider('$modal', function () {
10
+ var defaults = this.defaults = {
11
+ animation: 'am-fade',
12
+ backdropAnimation: 'am-fade',
13
+ prefixClass: 'modal',
14
+ prefixEvent: 'modal',
15
+ placement: 'top',
16
+ template: 'modal/modal.tpl.html',
17
+ contentTemplate: false,
18
+ container: false,
19
+ element: null,
20
+ backdrop: true,
21
+ keyboard: true,
22
+ html: false,
23
+ show: true
24
+ };
25
+ this.$get = [
26
+ '$window',
27
+ '$rootScope',
28
+ '$compile',
29
+ '$q',
30
+ '$templateCache',
31
+ '$http',
32
+ '$animate',
33
+ '$timeout',
34
+ '$sce',
35
+ 'dimensions',
36
+ function ($window, $rootScope, $compile, $q, $templateCache, $http, $animate, $timeout, $sce, dimensions) {
37
+ var forEach = angular.forEach;
38
+ var trim = String.prototype.trim;
39
+ var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
40
+ var bodyElement = angular.element($window.document.body);
41
+ var htmlReplaceRegExp = /ng-bind="/gi;
42
+ function ModalFactory(config) {
43
+ var $modal = {};
44
+ // Common vars
45
+ var options = $modal.$options = angular.extend({}, defaults, config);
46
+ $modal.$promise = fetchTemplate(options.template);
47
+ var scope = $modal.$scope = options.scope && options.scope.$new() || $rootScope.$new();
48
+ if (!options.element && !options.container) {
49
+ options.container = 'body';
50
+ }
51
+ // Support scope as string options
52
+ forEach([
53
+ 'title',
54
+ 'content'
55
+ ], function (key) {
56
+ if (options[key])
57
+ scope[key] = options[key]; //$sce.trustAsHtml(options[key]);
58
+ });
59
+ // Provide scope helpers
60
+ scope.$hide = function () {
61
+ scope.$$postDigest(function () {
62
+ $modal.hide();
63
+ });
64
+ };
65
+ scope.$show = function () {
66
+ scope.$$postDigest(function () {
67
+ $modal.show();
68
+ });
69
+ };
70
+ scope.$toggle = function () {
71
+ scope.$$postDigest(function () {
72
+ $modal.toggle();
73
+ });
74
+ };
75
+ // Support contentTemplate option
76
+ if (options.contentTemplate) {
77
+ $modal.$promise = $modal.$promise.then(function (template) {
78
+ var templateEl = angular.element(template);
79
+ return fetchTemplate(options.contentTemplate).then(function (contentTemplate) {
80
+ var contentEl = findElement('[ng-bind="content"]', templateEl[0]).removeAttr('ng-bind').html(contentTemplate);
81
+ // Drop the default footer as you probably don't want it if you use a custom contentTemplate
82
+ if (!config.template)
83
+ contentEl.next().remove();
84
+ return templateEl[0].outerHTML;
85
+ });
86
+ });
87
+ }
88
+ // Fetch, compile then initialize modal
89
+ var modalLinker, modalElement;
90
+ var backdropElement = angular.element('<div class="' + options.prefixClass + '-backdrop"/>');
91
+ $modal.$promise.then(function (template) {
92
+ if (angular.isObject(template))
93
+ template = template.data;
94
+ if (options.html)
95
+ template = template.replace(htmlReplaceRegExp, 'ng-bind-html="');
96
+ template = trim.apply(template);
97
+ modalLinker = $compile(template);
98
+ $modal.init();
99
+ });
100
+ $modal.init = function () {
101
+ // Options: show
102
+ if (options.show) {
103
+ scope.$$postDigest(function () {
104
+ $modal.show();
105
+ });
106
+ }
107
+ };
108
+ $modal.destroy = function () {
109
+ // Remove element
110
+ if (modalElement) {
111
+ modalElement.remove();
112
+ modalElement = null;
113
+ }
114
+ if (backdropElement) {
115
+ backdropElement.remove();
116
+ backdropElement = null;
117
+ }
118
+ // Destroy scope
119
+ scope.$destroy();
120
+ };
121
+ $modal.show = function () {
122
+ scope.$emit(options.prefixEvent + '.show.before', $modal);
123
+ var parent = options.container ? findElement(options.container) : null;
124
+ var after = options.container ? null : options.element;
125
+ // Fetch a cloned element linked from template
126
+ modalElement = $modal.$element = modalLinker(scope, function (clonedElement, scope) {
127
+ });
128
+ // Set the initial positioning.
129
+ modalElement.css({ display: 'block' }).addClass(options.placement);
130
+ // Options: animation
131
+ if (options.animation) {
132
+ if (options.backdrop) {
133
+ backdropElement.addClass(options.backdropAnimation);
134
+ }
135
+ modalElement.addClass(options.animation);
136
+ }
137
+ if (options.backdrop) {
138
+ $animate.enter(backdropElement, bodyElement, null, function () {
139
+ });
140
+ }
141
+ $animate.enter(modalElement, parent, after, function () {
142
+ scope.$emit(options.prefixEvent + '.show', $modal);
143
+ });
144
+ scope.$isShown = true;
145
+ scope.$$phase || scope.$root.$$phase || scope.$digest();
146
+ // Focus once the enter-animation has started
147
+ // Weird PhantomJS bug hack
148
+ var el = modalElement[0];
149
+ requestAnimationFrame(function () {
150
+ el.focus();
151
+ });
152
+ bodyElement.addClass(options.prefixClass + '-open');
153
+ if (options.animation) {
154
+ bodyElement.addClass(options.prefixClass + '-with-' + options.animation);
155
+ }
156
+ // Bind events
157
+ if (options.backdrop) {
158
+ modalElement.on('click', hideOnBackdropClick);
159
+ backdropElement.on('click', hideOnBackdropClick);
160
+ }
161
+ if (options.keyboard) {
162
+ modalElement.on('keyup', $modal.$onKeyUp);
163
+ }
164
+ };
165
+ $modal.hide = function () {
166
+ scope.$emit(options.prefixEvent + '.hide.before', $modal);
167
+ $animate.leave(modalElement, function () {
168
+ scope.$emit(options.prefixEvent + '.hide', $modal);
169
+ bodyElement.removeClass(options.prefixClass + '-open');
170
+ if (options.animation) {
171
+ bodyElement.addClass(options.prefixClass + '-with-' + options.animation);
172
+ }
173
+ });
174
+ if (options.backdrop) {
175
+ $animate.leave(backdropElement, function () {
176
+ });
177
+ }
178
+ scope.$isShown = false;
179
+ scope.$$phase || scope.$root.$$phase || scope.$digest();
180
+ // Unbind events
181
+ if (options.backdrop) {
182
+ modalElement.off('click', hideOnBackdropClick);
183
+ backdropElement.off('click', hideOnBackdropClick);
184
+ }
185
+ if (options.keyboard) {
186
+ modalElement.off('keyup', $modal.$onKeyUp);
187
+ }
188
+ };
189
+ $modal.toggle = function () {
190
+ scope.$isShown ? $modal.hide() : $modal.show();
191
+ };
192
+ $modal.focus = function () {
193
+ modalElement[0].focus();
194
+ };
195
+ // Protected methods
196
+ $modal.$onKeyUp = function (evt) {
197
+ evt.which === 27 && $modal.hide();
198
+ };
199
+ // Private methods
200
+ function hideOnBackdropClick(evt) {
201
+ if (evt.target !== evt.currentTarget)
202
+ return;
203
+ options.backdrop === 'static' ? $modal.focus() : $modal.hide();
204
+ }
205
+ return $modal;
206
+ }
207
+ // Helper functions
208
+ function findElement(query, element) {
209
+ return angular.element((element || document).querySelectorAll(query));
210
+ }
211
+ function fetchTemplate(template) {
212
+ return $q.when($templateCache.get(template) || $http.get(template)).then(function (res) {
213
+ if (angular.isObject(res)) {
214
+ $templateCache.put(template, res.data);
215
+ return res.data;
216
+ }
217
+ return res;
218
+ });
219
+ }
220
+ return ModalFactory;
221
+ }
222
+ ];
223
+ }).directive('bsModal', [
224
+ '$window',
225
+ '$location',
226
+ '$sce',
227
+ '$modal',
228
+ function ($window, $location, $sce, $modal) {
229
+ return {
230
+ restrict: 'EAC',
231
+ scope: true,
232
+ link: function postLink(scope, element, attr, transclusion) {
233
+ // Directive options
234
+ var options = {
235
+ scope: scope,
236
+ element: element,
237
+ show: false
238
+ };
239
+ angular.forEach([
240
+ 'template',
241
+ 'contentTemplate',
242
+ 'placement',
243
+ 'backdrop',
244
+ 'keyboard',
245
+ 'html',
246
+ 'container',
247
+ 'animation'
248
+ ], function (key) {
249
+ if (angular.isDefined(attr[key]))
250
+ options[key] = attr[key];
251
+ });
252
+ // Support scope as data-attrs
253
+ angular.forEach([
254
+ 'title',
255
+ 'content'
256
+ ], function (key) {
257
+ attr[key] && attr.$observe(key, function (newValue, oldValue) {
258
+ scope[key] = $sce.trustAsHtml(newValue);
259
+ });
260
+ });
261
+ // Support scope as an object
262
+ attr.bsModal && scope.$watch(attr.bsModal, function (newValue, oldValue) {
263
+ if (angular.isObject(newValue)) {
264
+ angular.extend(scope, newValue);
265
+ } else {
266
+ scope.content = newValue;
267
+ }
268
+ }, true);
269
+ // Initialize modal
270
+ var modal = $modal(options);
271
+ // Trigger
272
+ element.on(attr.trigger || 'click', modal.toggle);
273
+ // Garbage collection
274
+ scope.$on('$destroy', function () {
275
+ modal.destroy();
276
+ options = null;
277
+ modal = null;
278
+ });
279
+ }
280
+ };
281
+ }
282
+ ]);