Summary Table

Categories Total Count
PII 0
URL 0
DNS 0
EKL 0
IP 0
PORT 0
VsID 0
CF 0
AI 0
VPD 0
PL 0
Other 0

File Content

define(['angular'], function (angular) {
"use strict";

angular.module('AccessibleAccordion', ['accordCollapse'])

.constant('accordionConfig', {
closeOthers: true
})

.controller('AccessibleAccordionController', function ($scope, $attrs, accordionConfig) {

// This array keeps track of the accordion groups
this.groups = [];

// Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
this.closeOthers = function(openGroup) {
var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
if ( closeOthers ) {
angular.forEach(this.groups, function (group) {
if ( group !== openGroup ) {
group.isOpen = false;
}
});
}
};

// This is called from the accordion-group directive to add itself to the accordion
this.addGroup = function(groupScope) {
var that = this;
this.groups.push(groupScope);

groupScope.$on('$destroy', function (event) {
that.removeGroup(groupScope);
});
};

// This is called from the accordion-group directive when to remove itself
this.removeGroup = function(group) {
var index = this.groups.indexOf(group);
if ( index !== -1 ) {
this.groups.splice(index, 1);
}
};

})

// The accordion directive simply sets up the directive controller
// and adds an accordion CSS class to itself element.
.directive('accessibleAccordion', function () {
return {
restrict:'EA',
controller:'AccessibleAccordionController',
transclude: true,
replace: false,
templateUrl: 'modules/ui-components/accordion/accessibleaccordion_template.html'
};
})

// The accordion-group directive indicates a block of html that will expand and collapse in an accordion
.directive('accessibleAccordionGroup', function($timeout) {
return {
require:'^accessibleAccordion', // We need this directive to be inside an accordion
restrict:'EA',
transclude:true, // It transcludes the contents of the directive into the template
replace: true, // The element containing the directive will be replaced with the template
templateUrl:'modules/ui-components/accordion/accessibleaccordiongroup_template.html',
scope: {
heading: '@', // Interpolate the heading attribute onto this scope
subtext: '@',
groupId: '@',
isOpen: '=?',
isDisabled: '=?'
},
controller: function() {
this.setHeading = function(element) {
this.heading = element;
};
},
link: function(scope, element, attrs, accessibleAccordionCtrl) {
accessibleAccordionCtrl.addGroup(scope);

scope.$watch('isOpen', function(value) {
if ( value ) {
accessibleAccordionCtrl.closeOthers(scope);

$timeout(function () {
var headerHeight = angular.element('nav.main-header').outerHeight() + 5,
currentScroll = angular.element('div[ui-view=primary]').scrollTop(),
updatedPosition = angular.element('div[group-id=' + scope.groupId + ']').offset().top;

if (updatedPosition < 0) {
$('div[ui-view=primary]').scrollTop(currentScroll + updatedPosition - headerHeight);
}
}, 700);
}
});

scope.toggleOpen = function() {
if ( !scope.isDisabled ) {
scope.isOpen = !scope.isOpen;
}
};
}
};
})

// Use accordion-heading below an accordion-group to provide a heading containing HTML
// <accordion-group>
// <accordion-heading>Heading containing HTML - <img src="..."></accordion-heading>
// </accordion-group>
.directive('accessibleAccordionHeading', function() {
return {
restrict: 'EA',
transclude: true, // Grab the contents to be used as the heading
template: '', // In effect remove this element!
replace: true,
require: '^accessibleAccordionGroup',
link: function(scope, element, attr, accessibleAccordionGroupCtrl, transclude) {
// Pass the heading to the accordion-group controller
// so that it can be transcluded into the right place in the template
// [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
accessibleAccordionGroupCtrl.setHeading(transclude(scope, function() {}));
}
};
})

// Use in the accordion-group template to indicate where you want the heading to be transcluded
// You must provide the property on the accordion-group controller that will hold the transcluded element
// <div class="accordion-group">
// <div class="accordion-heading" ><a ... accordion-transclude="heading">...</a></div>
// ...
// </div>
.directive('accessibleAccordionTransclude', function() {
return {
require: '^accessibleAccordionGroup',
link: function(scope, element, attr, controller) {
scope.$watch(function() { return controller[attr.accessibleAccordionTransclude]; }, function(heading) {
if ( heading ) {
element.html('');
element.append(heading);
}
});
}
};
})

.directive('ngEnterAccordion', function () {
return function (scope, element, attrs) {
element.bind("keydown keypress", function (event) {
if(event.which === 13 || event.which === 32) {
scope.$apply(function (){
scope.$eval(attrs.ngEnterAccordion);
});
event.preventDefault();
}
});
};
});

/**
* $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
* @param {DOMElement} element The DOMElement that will be animated.
* @param {string|object|function} trigger The thing that will cause the transition to start:
* - As a string, it represents the css class to be added to the element.
* - As an object, it represents a hash of style attributes to be applied to the element.
* - As a function, it represents a function to be called that will cause the transition to occur.
* @return {Promise} A promise that is resolved when the transition finishes.
*/
angular.module('accordTransition', []).factory('$transition', function($q, $timeout, $rootScope) {
var $transition = function(element, trigger, options) {
options = options || {};
var deferred = $q.defer();
var endEventName = $transition[options.animation ? 'animationEndEventName' : 'transitionEndEventName'];

var transitionEndHandler = function(event) {
$rootScope.$apply(function() {
element.unbind(endEventName, transitionEndHandler);
deferred.resolve(element);
});
};

if (endEventName) {
element.bind(endEventName, transitionEndHandler);
}

// Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
$timeout(function() {
if ( angular.isString(trigger) ) {
element.addClass(trigger);
} else if ( angular.isFunction(trigger) ) {
trigger(element);
} else if ( angular.isObject(trigger) ) {
element.css(trigger);
}
//If browser does not support transitions, instantly resolve
if ( !endEventName ) {
deferred.resolve(element);
}
});

// Add our custom cancel function to the promise that is returned
// We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
// i.e. it will therefore never raise a transitionEnd event for that transition
deferred.promise.cancel = function() {
if ( endEventName ) {
element.unbind(endEventName, transitionEndHandler);
}
deferred.reject('Transition cancelled');
};

return deferred.promise;
};

// Work out the name of the transitionEnd event
var transElement = document.createElement('trans');
var transitionEndEventNames = {
'WebkitTransition': 'webkitTransitionEnd',
'MozTransition': 'transitionend',
'OTransition': 'oTransitionEnd',
'transition': 'transitionend'
};
var animationEndEventNames = {
'WebkitTransition': 'webkitAnimationEnd',
'MozTransition': 'animationend',
'OTransition': 'oAnimationEnd',
'transition': 'animationend'
};
function findEndEventName(endEventNames) {
for (var name in endEventNames){
if (transElement.style[name] !== undefined) {
return endEventNames[name];
}
}
}
$transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
$transition.animationEndEventName = findEndEventName(animationEndEventNames);
return $transition;
});

angular.module('accordCollapse', ['accordTransition'])
.directive('collapse', function ($transition) {

return {
link: function (scope, element, attrs) {

var initialAnimSkip = true;
var currentTransition;

function doTransition(change) {
var newTransition = $transition(element, change);
if (currentTransition) {
currentTransition.cancel();
}
currentTransition = newTransition;
newTransition.then(newTransitionDone, newTransitionDone);
return newTransition;

function newTransitionDone() {
// Make sure it's this transition, otherwise, leave it alone.
if (currentTransition === newTransition) {
currentTransition = undefined;
}
}
}

function expand() {
if (initialAnimSkip) {
initialAnimSkip = false;
expandDone();
} else {
element.removeClass('collapse').addClass('collapsing');
doTransition({ height: element[0].scrollHeight + 'px' }).then(expandDone);
}
}

function expandDone() {
element.removeClass('collapsing');
element.addClass('collapse in');
element.css({height: 'auto'});
}

function collapse() {
if (initialAnimSkip) {
initialAnimSkip = false;
collapseDone();
element.css({height: 0});
} else {
// CSS transitions don't work with height: auto, so we have to manually change the height to a specific value
element.css({ height: element[0].scrollHeight + 'px' });
//trigger reflow so a browser realizes that height was updated from auto to a specific value
var x = element[0].offsetWidth;

element.removeClass('collapse in').addClass('collapsing');

doTransition({ height: 0 }).then(collapseDone);
}
}

function collapseDone() {
element.removeClass('collapsing');
element.addClass('collapse');
}

scope.$watch(attrs.collapse, function (shouldCollapse) {
if (shouldCollapse) {
collapse();
} else {
expand();
}
});
}
};
})
});