collapse.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /**
  2. * angular-strap
  3. * @version v2.3.9 - 2016-06-10
  4. * @link http://mgcrea.github.io/angular-strap
  5. * @author Olivier Louvignes <olivier@mg-crea.com> (https://github.com/mgcrea)
  6. * @license MIT License, http://www.opensource.org/licenses/MIT
  7. */
  8. 'use strict';
  9. angular.module('mgcrea.ngStrap.collapse', []).provider('$collapse', function() {
  10. var defaults = this.defaults = {
  11. animation: 'am-collapse',
  12. disallowToggle: false,
  13. activeClass: 'in',
  14. startCollapsed: false,
  15. allowMultiple: false
  16. };
  17. var controller = this.controller = function($scope, $element, $attrs) {
  18. var self = this;
  19. self.$options = angular.copy(defaults);
  20. angular.forEach([ 'animation', 'disallowToggle', 'activeClass', 'startCollapsed', 'allowMultiple' ], function(key) {
  21. if (angular.isDefined($attrs[key])) self.$options[key] = $attrs[key];
  22. });
  23. var falseValueRegExp = /^(false|0|)$/i;
  24. angular.forEach([ 'disallowToggle', 'startCollapsed', 'allowMultiple' ], function(key) {
  25. if (angular.isDefined($attrs[key]) && falseValueRegExp.test($attrs[key])) {
  26. self.$options[key] = false;
  27. }
  28. });
  29. self.$toggles = [];
  30. self.$targets = [];
  31. self.$viewChangeListeners = [];
  32. self.$registerToggle = function(element) {
  33. self.$toggles.push(element);
  34. };
  35. self.$registerTarget = function(element) {
  36. self.$targets.push(element);
  37. };
  38. self.$unregisterToggle = function(element) {
  39. var index = self.$toggles.indexOf(element);
  40. self.$toggles.splice(index, 1);
  41. };
  42. self.$unregisterTarget = function(element) {
  43. var index = self.$targets.indexOf(element);
  44. self.$targets.splice(index, 1);
  45. if (self.$options.allowMultiple) {
  46. deactivateItem(element);
  47. }
  48. fixActiveItemIndexes(index);
  49. self.$viewChangeListeners.forEach(function(fn) {
  50. fn();
  51. });
  52. };
  53. self.$targets.$active = !self.$options.startCollapsed ? [ 0 ] : [];
  54. self.$setActive = $scope.$setActive = function(value) {
  55. if (angular.isArray(value)) {
  56. self.$targets.$active = value;
  57. } else if (!self.$options.disallowToggle && isActive(value)) {
  58. deactivateItem(value);
  59. } else {
  60. activateItem(value);
  61. }
  62. self.$viewChangeListeners.forEach(function(fn) {
  63. fn();
  64. });
  65. };
  66. self.$activeIndexes = function() {
  67. if (self.$options.allowMultiple) {
  68. return self.$targets.$active;
  69. }
  70. return self.$targets.$active.length === 1 ? self.$targets.$active[0] : -1;
  71. };
  72. function fixActiveItemIndexes(index) {
  73. var activeIndexes = self.$targets.$active;
  74. for (var i = 0; i < activeIndexes.length; i++) {
  75. if (index < activeIndexes[i]) {
  76. activeIndexes[i] = activeIndexes[i] - 1;
  77. }
  78. if (activeIndexes[i] === self.$targets.length) {
  79. activeIndexes[i] = self.$targets.length - 1;
  80. }
  81. }
  82. }
  83. function isActive(value) {
  84. var activeItems = self.$targets.$active;
  85. return activeItems.indexOf(value) !== -1;
  86. }
  87. function deactivateItem(value) {
  88. var index = self.$targets.$active.indexOf(value);
  89. if (index !== -1) {
  90. self.$targets.$active.splice(index, 1);
  91. }
  92. }
  93. function activateItem(value) {
  94. if (!self.$options.allowMultiple) {
  95. self.$targets.$active.splice(0, 1);
  96. }
  97. if (self.$targets.$active.indexOf(value) === -1) {
  98. self.$targets.$active.push(value);
  99. }
  100. }
  101. };
  102. this.$get = function() {
  103. var $collapse = {};
  104. $collapse.defaults = defaults;
  105. $collapse.controller = controller;
  106. return $collapse;
  107. };
  108. }).directive('bsCollapse', [ '$window', '$animate', '$collapse', function($window, $animate, $collapse) {
  109. return {
  110. require: [ '?ngModel', 'bsCollapse' ],
  111. controller: [ '$scope', '$element', '$attrs', $collapse.controller ],
  112. link: function postLink(scope, element, attrs, controllers) {
  113. var ngModelCtrl = controllers[0];
  114. var bsCollapseCtrl = controllers[1];
  115. if (ngModelCtrl) {
  116. bsCollapseCtrl.$viewChangeListeners.push(function() {
  117. ngModelCtrl.$setViewValue(bsCollapseCtrl.$activeIndexes());
  118. });
  119. ngModelCtrl.$formatters.push(function(modelValue) {
  120. if (angular.isArray(modelValue)) {
  121. bsCollapseCtrl.$setActive(modelValue);
  122. } else {
  123. var activeIndexes = bsCollapseCtrl.$activeIndexes();
  124. if (angular.isArray(activeIndexes)) {
  125. if (activeIndexes.indexOf(modelValue * 1) === -1) {
  126. bsCollapseCtrl.$setActive(modelValue * 1);
  127. }
  128. } else if (activeIndexes !== modelValue * 1) {
  129. bsCollapseCtrl.$setActive(modelValue * 1);
  130. }
  131. }
  132. return modelValue;
  133. });
  134. }
  135. }
  136. };
  137. } ]).directive('bsCollapseToggle', function() {
  138. return {
  139. require: [ '^?ngModel', '^bsCollapse' ],
  140. link: function postLink(scope, element, attrs, controllers) {
  141. var bsCollapseCtrl = controllers[1];
  142. element.attr('data-toggle', 'collapse');
  143. bsCollapseCtrl.$registerToggle(element);
  144. scope.$on('$destroy', function() {
  145. bsCollapseCtrl.$unregisterToggle(element);
  146. });
  147. element.on('click', function() {
  148. if (!attrs.disabled) {
  149. var index = attrs.bsCollapseToggle && attrs.bsCollapseToggle !== 'bs-collapse-toggle' ? attrs.bsCollapseToggle : bsCollapseCtrl.$toggles.indexOf(element);
  150. bsCollapseCtrl.$setActive(index * 1);
  151. scope.$apply();
  152. }
  153. });
  154. }
  155. };
  156. }).directive('bsCollapseTarget', [ '$animate', function($animate) {
  157. return {
  158. require: [ '^?ngModel', '^bsCollapse' ],
  159. link: function postLink(scope, element, attrs, controllers) {
  160. var bsCollapseCtrl = controllers[1];
  161. element.addClass('collapse');
  162. if (bsCollapseCtrl.$options.animation) {
  163. element.addClass(bsCollapseCtrl.$options.animation);
  164. }
  165. bsCollapseCtrl.$registerTarget(element);
  166. scope.$on('$destroy', function() {
  167. bsCollapseCtrl.$unregisterTarget(element);
  168. });
  169. function render() {
  170. var index = bsCollapseCtrl.$targets.indexOf(element);
  171. var active = bsCollapseCtrl.$activeIndexes();
  172. var action = 'removeClass';
  173. if (angular.isArray(active)) {
  174. if (active.indexOf(index) !== -1) {
  175. action = 'addClass';
  176. }
  177. } else if (index === active) {
  178. action = 'addClass';
  179. }
  180. $animate[action](element, bsCollapseCtrl.$options.activeClass);
  181. }
  182. bsCollapseCtrl.$viewChangeListeners.push(function() {
  183. render();
  184. });
  185. render();
  186. }
  187. };
  188. } ]);