/client/assets/js/angular-accordion.js

https://gitlab.com/kapilp/Seeko · JavaScript · 158 lines · 129 code · 20 blank · 9 comment · 10 complexity · c45f7c46f9751d0b29a1d5555d4dfa93 MD5 · raw file

  1. angular.module('angular-accordion', [])
  2. .directive('angularAccordion', function() {
  3. return {
  4. restrict: 'C',
  5. transclude: true,
  6. replace: true,
  7. template: '<div ng-transclude class="angular-accordion-container"></div>',
  8. controller: ['$scope', function($scope) {
  9. var panes = [];
  10. this.expandPane = function(paneToExpand) {
  11. angular.forEach(panes, function(iteratedPane) {
  12. if(paneToExpand !== iteratedPane) {
  13. iteratedPane.expanded = false;
  14. }
  15. });
  16. };
  17. this.addPane = function(pane) {
  18. panes.push(pane);
  19. };
  20. }],
  21. scope: {}
  22. };
  23. })
  24. .directive('pane', function() {
  25. return {
  26. restrict: 'C',
  27. transclude: true,
  28. replace: true,
  29. template: '<div ng-transclude class="angular-accordion-pane"></div>'
  30. };
  31. })
  32. .directive('paneHeader', ['$window', 'Debounce', function($window, Debounce) {
  33. return {
  34. restrict: 'C',
  35. require: '^angularAccordion',
  36. transclude: true,
  37. replace: true,
  38. link: function(scope, iElement, iAttrs, controller) {
  39. scope.expanded = false;
  40. scope.passOnExpand = iAttrs.passOnExpand;
  41. scope.disabled = iAttrs.disabled;
  42. controller.addPane(scope);
  43. // TODO: figure out how to trigger this without interpolation in the template
  44. iAttrs.$observe('disabled', function(value) {
  45. // attributes always get passed as strings
  46. if(value === 'true') {
  47. scope.disabled = true;
  48. } else {
  49. scope.disabled = false;
  50. }
  51. });
  52. var computed = function(rawDomElement, property) {
  53. var computedValueAsString = $window.getComputedStyle(rawDomElement).getPropertyValue(property).replace('px', '');
  54. return parseFloat(computedValueAsString);
  55. };
  56. var computeExpandedPaneHeight = function() {
  57. var parentContainer = iElement.parent().parent()[0];
  58. var header = iElement[0];
  59. var paneWrapper = iElement.parent()[0];
  60. var contentPane = iElement.next()[0];
  61. var headerCount = iElement.parent().parent().children().length;
  62. var containerHeight = computed(parentContainer, 'height');
  63. var headersHeight = ((computed(header, 'height') + computed(header, 'padding-top') + computed(header, 'padding-bottom') +
  64. computed(header, 'margin-top') + computed(header, 'margin-bottom') + computed(header, 'border-top') + computed(header, 'border-bottom') +
  65. computed(paneWrapper, 'padding-top') + computed(paneWrapper, 'padding-bottom') + computed(paneWrapper, 'margin-top') +
  66. computed(paneWrapper, 'margin-bottom') + computed(paneWrapper, 'border-top') + computed(paneWrapper, 'border-bottom')) * headerCount) +
  67. (computed(contentPane, 'padding-top') + computed(contentPane, 'padding-bottom') + computed(contentPane, 'margin-top') +
  68. computed(contentPane, 'margin-bottom') + computed(contentPane, 'border-top') + computed(contentPane, 'border-bottom'));
  69. return containerHeight - headersHeight;
  70. }
  71. scope.toggle = function() {
  72. if(!scope.disabled) {
  73. scope.expanded = !scope.expanded;
  74. if(scope.expanded) {
  75. iElement.next().css('height', computeExpandedPaneHeight() + 'px');
  76. scope.$emit('angular-accordion-expand', scope.passOnExpand);
  77. }
  78. controller.expandPane(scope);
  79. }
  80. };
  81. angular.element($window).bind('resize', Debounce.debounce(function() {
  82. // must apply since the browser resize event is not seen by the digest process
  83. scope.$apply(function() {
  84. iElement.next().css('height', computeExpandedPaneHeight() + 'px');
  85. });
  86. }, 50));
  87. scope.$on('expand', function(event, eventArguments) {
  88. if(eventArguments === scope.passOnExpand) {
  89. // only toggle if we are loading a deeplinked route
  90. if(!scope.expanded) {
  91. scope.toggle();
  92. }
  93. }
  94. });
  95. },
  96. template: '<div ng-transclude class="angular-accordion-header" ng-click="toggle()" ' +
  97. 'ng-class="{ angularaccordionheaderselected: expanded, angulardisabledpane: disabled }"></div>'
  98. };
  99. }])
  100. .directive('paneContent', function() {
  101. return {
  102. restrict: 'C',
  103. require: '^paneHeader',
  104. transclude: true,
  105. replace: true,
  106. template: '<div ng-transclude class="angular-accordion-pane-content" ng-show="expanded"></div>'
  107. };
  108. })
  109. .service('Debounce', function() {
  110. var self = this;
  111. // debounce() method is slightly modified version of:
  112. // Underscore.js 1.4.4
  113. // http://underscorejs.org
  114. // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
  115. // Underscore may be freely distributed under the MIT license.
  116. self.debounce = function(func, wait, immediate) {
  117. var timeout,
  118. result;
  119. return function() {
  120. var context = this,
  121. args = arguments,
  122. callNow = immediate && !timeout;
  123. var later = function() {
  124. timeout = null;
  125. if (!immediate) {
  126. result = func.apply(context, args);
  127. }
  128. };
  129. clearTimeout(timeout);
  130. timeout = setTimeout(later, wait);
  131. if (callNow) {
  132. result = func.apply(context, args);
  133. }
  134. return result;
  135. };
  136. };
  137. return self;
  138. });