collapse.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /**
  2. * angular-strap
  3. * @version v2.3.5 - 2015-10-29
  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) {
  58. isActive(value) ? deactivateItem(value) : activateItem(value);
  59. } else {
  60. activateItem(value);
  61. }
  62. self.$viewChangeListeners.forEach(function(fn) {
  63. fn();
  64. });
  65. };
  66. self.$activeIndexes = function() {
  67. return self.$options.allowMultiple ? self.$targets.$active : self.$targets.$active.length === 1 ? self.$targets.$active[0] : -1;
  68. };
  69. function fixActiveItemIndexes(index) {
  70. var activeIndexes = self.$targets.$active;
  71. for (var i = 0; i < activeIndexes.length; i++) {
  72. if (index < activeIndexes[i]) {
  73. activeIndexes[i] = activeIndexes[i] - 1;
  74. }
  75. if (activeIndexes[i] === self.$targets.length) {
  76. activeIndexes[i] = self.$targets.length - 1;
  77. }
  78. }
  79. }
  80. function isActive(value) {
  81. var activeItems = self.$targets.$active;
  82. return activeItems.indexOf(value) === -1 ? false : true;
  83. }
  84. function deactivateItem(value) {
  85. var index = self.$targets.$active.indexOf(value);
  86. if (index !== -1) {
  87. self.$targets.$active.splice(index, 1);
  88. }
  89. }
  90. function activateItem(value) {
  91. if (!self.$options.allowMultiple) {
  92. self.$targets.$active.splice(0, 1);
  93. }
  94. if (self.$targets.$active.indexOf(value) === -1) {
  95. self.$targets.$active.push(value);
  96. }
  97. }
  98. };
  99. this.$get = function() {
  100. var $collapse = {};
  101. $collapse.defaults = defaults;
  102. $collapse.controller = controller;
  103. return $collapse;
  104. };
  105. }).directive('bsCollapse', [ '$window', '$animate', '$collapse', function($window, $animate, $collapse) {
  106. var defaults = $collapse.defaults;
  107. return {
  108. require: [ '?ngModel', 'bsCollapse' ],
  109. controller: [ '$scope', '$element', '$attrs', $collapse.controller ],
  110. link: function postLink(scope, element, attrs, controllers) {
  111. var ngModelCtrl = controllers[0];
  112. var bsCollapseCtrl = controllers[1];
  113. if (ngModelCtrl) {
  114. bsCollapseCtrl.$viewChangeListeners.push(function() {
  115. ngModelCtrl.$setViewValue(bsCollapseCtrl.$activeIndexes());
  116. });
  117. ngModelCtrl.$formatters.push(function(modelValue) {
  118. if (angular.isArray(modelValue)) {
  119. bsCollapseCtrl.$setActive(modelValue);
  120. } else {
  121. var activeIndexes = bsCollapseCtrl.$activeIndexes();
  122. if (angular.isArray(activeIndexes)) {
  123. if (activeIndexes.indexOf(modelValue * 1) === -1) {
  124. bsCollapseCtrl.$setActive(modelValue * 1);
  125. }
  126. } else if (activeIndexes !== modelValue * 1) {
  127. bsCollapseCtrl.$setActive(modelValue * 1);
  128. }
  129. }
  130. return modelValue;
  131. });
  132. }
  133. }
  134. };
  135. } ]).directive('bsCollapseToggle', function() {
  136. return {
  137. require: [ '^?ngModel', '^bsCollapse' ],
  138. link: function postLink(scope, element, attrs, controllers) {
  139. var ngModelCtrl = controllers[0];
  140. var bsCollapseCtrl = controllers[1];
  141. element.attr('data-toggle', 'collapse');
  142. bsCollapseCtrl.$registerToggle(element);
  143. scope.$on('$destroy', function() {
  144. bsCollapseCtrl.$unregisterToggle(element);
  145. });
  146. element.on('click', function() {
  147. var index = attrs.bsCollapseToggle && attrs.bsCollapseToggle !== 'bs-collapse-toggle' ? attrs.bsCollapseToggle : bsCollapseCtrl.$toggles.indexOf(element);
  148. bsCollapseCtrl.$setActive(index * 1);
  149. scope.$apply();
  150. });
  151. }
  152. };
  153. }).directive('bsCollapseTarget', [ '$animate', function($animate) {
  154. return {
  155. require: [ '^?ngModel', '^bsCollapse' ],
  156. link: function postLink(scope, element, attrs, controllers) {
  157. var ngModelCtrl = controllers[0];
  158. var bsCollapseCtrl = controllers[1];
  159. element.addClass('collapse');
  160. if (bsCollapseCtrl.$options.animation) {
  161. element.addClass(bsCollapseCtrl.$options.animation);
  162. }
  163. bsCollapseCtrl.$registerTarget(element);
  164. scope.$on('$destroy', function() {
  165. bsCollapseCtrl.$unregisterTarget(element);
  166. });
  167. function render() {
  168. var index = bsCollapseCtrl.$targets.indexOf(element);
  169. var active = bsCollapseCtrl.$activeIndexes();
  170. var action = 'removeClass';
  171. if (angular.isArray(active)) {
  172. if (active.indexOf(index) !== -1) {
  173. action = 'addClass';
  174. }
  175. } else if (index === active) {
  176. action = 'addClass';
  177. }
  178. $animate[action](element, bsCollapseCtrl.$options.activeClass);
  179. }
  180. bsCollapseCtrl.$viewChangeListeners.push(function() {
  181. render();
  182. });
  183. render();
  184. }
  185. };
  186. } ]);