1 /*! b2b-angular-library - v1.0.1 - Last updated: 2017-03-02. Copyright (c) 2016 AT&T Services, Inc. */
\r
2 angular.module("b2b.att.tpls", ['b2bTemplate/audioPlayer/audioPlayer.html', 'b2bTemplate/audioRecorder/audioRecorder.html', 'b2bTemplate/backToTop/backToTop.html', 'b2bTemplate/boardstrip/b2bAddBoard.html', 'b2bTemplate/boardstrip/b2bBoard.html', 'b2bTemplate/boardstrip/b2bBoardstrip.html', 'b2bTemplate/calendar/datepicker-popup.html', 'b2bTemplate/calendar/datepicker.html', 'b2bTemplate/coachmark/coachmark.html', 'b2bTemplate/dropdowns/b2bDropdownDesktop.html', 'b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html', 'b2bTemplate/dropdowns/b2bDropdownListDesktop.html', 'b2bTemplate/fileUpload/fileUpload.html', 'b2bTemplate/flyout/flyout.html', 'b2bTemplate/flyout/flyoutContent.html', 'b2bTemplate/footer/footer_column_switch_tpl.html', 'b2bTemplate/horizontalTable/horizontalTable.html', 'b2bTemplate/hourPicker/b2bHourpicker.html', 'b2bTemplate/hourPicker/b2bHourpickerPanel.html', 'b2bTemplate/hourPicker/b2bHourpickerValue.html', 'b2bTemplate/leftNavigation/leftNavigation.html', 'b2bTemplate/listbox/listbox.html', 'b2bTemplate/modalsAndAlerts/b2b-backdrop.html', 'b2bTemplate/modalsAndAlerts/b2b-window.html', 'b2bTemplate/monthSelector/monthSelector-popup.html', 'b2bTemplate/monthSelector/monthSelector.html', 'b2bTemplate/monthSelector/monthSelectorLink.html', 'b2bTemplate/pagination/b2b-pagination.html', 'b2bTemplate/paneSelector/paneSelector.html', 'b2bTemplate/paneSelector/paneSelectorPane.html', 'b2bTemplate/profileCard/profileCard-addUser.html', 'b2bTemplate/profileCard/profileCard.html', 'b2bTemplate/searchField/searchField.html', 'b2bTemplate/seekBar/seekBar.html', 'b2bTemplate/slider/slider.html', 'b2bTemplate/spinButton/spinButton.html', 'b2bTemplate/statusTracker/statusTracker.html', 'b2bTemplate/stepTracker/stepTracker.html', 'b2bTemplate/switches/switches-spanish.html', 'b2bTemplate/switches/switches.html', 'b2bTemplate/tableMessages/tableMessage.html', 'b2bTemplate/tables/b2bTable.html', 'b2bTemplate/tables/b2bTableBody.html', 'b2bTemplate/tables/b2bTableHeaderSortable.html', 'b2bTemplate/tables/b2bTableHeaderUnsortable.html', 'b2bTemplate/tableScrollbar/tableScrollbar.html', 'b2bTemplate/tabs/b2bTab.html', 'b2bTemplate/tabs/b2bTabset.html', 'b2bTemplate/treeNav/groupedTree.html', 'b2bTemplate/treeNav/treeMember.html', 'b2bTemplate/treeNav/ungroupedTree.html', 'b2bTemplate/treeNodeCheckbox/groupedTree.html', 'b2bTemplate/treeNodeCheckbox/treeMember.html', 'b2bTemplate/treeNodeCheckbox/ungroupedTree.html']);angular.module("b2b.att", ["b2b.att.tpls", 'b2b.att.addressInputTemplate','b2b.att.arrows','b2b.att.audioPlayer','b2b.att.audioRecorder','b2b.att.backToTop','b2b.att.badgesForAlerts','b2b.att.boardstrip','b2b.att.breadcrumbs','b2b.att.buttonGroups','b2b.att.buttons','b2b.att.calendar','b2b.att.checkboxes','b2b.att.coachmark','b2b.att.configurationSection','b2b.att.directoryListingTemplate','b2b.att.dropdowns','b2b.att.fileUpload','b2b.att.filters','b2b.att.flyout','b2b.att.footer','b2b.att.header','b2b.att.headings','b2b.att.horizontalTable','b2b.att.hourPicker','b2b.att.inputTemplate','b2b.att.leftNavigation','b2b.att.links','b2b.att.listbox','b2b.att.loaderAnimation','b2b.att.messageWrapper','b2b.att.modalsAndAlerts','b2b.att.monthSelector','b2b.att.multiLevelNavigation','b2b.att.multipurposeExpander','b2b.att.notesMessagesAndErrors','b2b.att.notificationCardTemplate','b2b.att.orderConfirmationTemplate','b2b.att.pagination','b2b.att.paneSelector','b2b.att.phoneNumberInput','b2b.att.profileBlockTemplate','b2b.att.profileCard','b2b.att.radios','b2b.att.searchField','b2b.att.seekBar','b2b.att.selectorModule','b2b.att.separators','b2b.att.slider','b2b.att.spinButton','b2b.att.staticRouteTemplate','b2b.att.statusTracker','b2b.att.stepTracker','b2b.att.switches','b2b.att.tableMessages','b2b.att.tables','b2b.att.tableScrollbar','b2b.att.tabs','b2b.att.tagBadges','b2b.att.textArea','b2b.att.tooltipsForForms','b2b.att.treeNav','b2b.att.treeNodeCheckbox','b2b.att.utilities']);/**
\r
4 * @name Template.att:Address Input
\r
7 * <file src="src/addressInputTemplate/docs/readme.md" />
\r
13 * <section id="code">
\r
14 <example module="b2b.att">
\r
15 <file src="src/addressInputTemplate/docs/demo.html" />
\r
16 <file src="src/addressInputTemplate/docs/demo.js" />
\r
21 angular.module('b2b.att.addressInputTemplate', ['ngMessages']);
\r
24 * @name Buttons, links & UI controls.att:arrows
\r
27 * <file src="src/arrows/docs/readme.md" />
\r
30 * Please refer demo.html tab in Example section below.
\r
33 * <section id="code">
\r
34 <example module="b2b.att">
\r
35 <file src="src/arrows/docs/demo.html" />
\r
36 <file src="src/arrows/docs/demo.js" />
\r
41 angular.module('b2b.att.arrows', []);
\r
44 * @name Videos, audio & animation.att:Audio Player
\r
46 * @param {string} audioSrcUrl - MP3 audio source URL or Blob URL
\r
48 * <file src="src/audioPlayer/docs/readme.md" />
\r
52 <div b2b-audio audio-src-url='audioSrcUrl'></div>
\r
55 * <section id="code">
\r
56 <example module="b2b.att">
\r
57 <file src="src/audioPlayer/docs/demo.html" />
\r
58 <file src="src/audioPlayer/docs/demo.js" />
\r
64 angular.module('b2b.att.audioPlayer', ['b2b.att.utilities', 'b2b.att.seekBar'])
\r
65 .constant('AudioPlayerConfig', {
\r
66 'defaultVolume': 50,
\r
67 'timeShiftInSeconds': 5
\r
69 .filter('trustedAudioUrl', ['$sce', function ($sce) {
\r
70 return function (audioFileFullPath) {
\r
71 return audioFileFullPath ? $sce.trustAsResourceUrl(audioFileFullPath) : 'undefined';
\r
74 .directive('b2bAudio', ['$log', '$timeout', 'AudioPlayerConfig', '$compile', 'events', function ($log, $timeout, AudioPlayerConfig, $compile, events) {
\r
81 templateUrl: 'b2bTemplate/audioPlayer/audioPlayer.html',
\r
82 controller: function ($scope) {
\r
86 if (!angular.isDefined($scope.audioSrcUrl)) {
\r
87 $log.warn('b2b-audio : audio-src-url undefined');
\r
88 $scope.audioSrcUrl = undefined;
\r
89 $scope.audio.mp3 = undefined;
\r
93 link: function (scope, element) {
\r
94 var audioElement = angular.element(element[0].querySelector('audio'))[0];
\r
95 scope.audio.audioElement = audioElement;
\r
97 function setAttributes(element, attributes) {
\r
98 Object.keys(attributes).forEach(function (name) {
\r
99 element.setAttribute(name, attributes[name]);
\r
103 $timeout(function () {
\r
104 // TODO: Replace with DDA Tooltip
\r
105 var seekBarKnob = element[0].querySelector('.b2b-seek-bar-knob');
\r
106 var tooltipObject = {
\r
107 'tooltip': '{{timeFormatter(audio.currentTime)}}',
\r
108 'tooltip-placement': 'above',
\r
109 'tooltip-style': 'blue',
\r
110 'tooltip-trigger': 'mousedown',
\r
111 'tooltip-append-to-body': 'false',
\r
112 'tooltip-offset': '-10',
\r
113 'refer-by': 'seek-bar-tooltip'
\r
115 setAttributes(seekBarKnob, tooltipObject);
\r
116 $compile(seekBarKnob)(scope);
\r
119 if (angular.isDefined(scope.audioSrcUrl)) {
\r
120 scope.audio.mp3 = scope.audioSrcUrl;
\r
123 scope.audio.currentTime = 0;
\r
124 scope.audio.currentVolume = AudioPlayerConfig.defaultVolume;
\r
125 scope.audio.timeShiftInSeconds = AudioPlayerConfig.timeShiftInSeconds;
\r
126 scope.isPlayInProgress = false;
\r
127 scope.isReady = false;
\r
128 scope.isAudioDragging = false;
\r
130 $timeout(function () {
\r
131 audioElement.load();
\r
132 audioElement.volume = scope.audio.currentVolume / 100;
\r
135 scope.$watch('audioSrcUrl', function (newVal, oldVal) {
\r
136 if (newVal !== oldVal) {
\r
138 $log.warn('b2b-audio : audio-src-url undefined. Please provide a valid URL');
\r
141 scope.audio.mp3 = newVal;
\r
142 $timeout(function () {
\r
143 audioElement.load();
\r
148 scope.playAudio = function () {
\r
149 if (scope.isReady) {
\r
150 audioElement.play();
\r
154 audioElement.onplay = function () {
\r
155 scope.isPlayInProgress = true;
\r
159 scope.pauseAudio = function () {
\r
160 audioElement.pause();
\r
163 audioElement.onpause = function () {
\r
164 scope.isPlayInProgress = false;
\r
168 scope.toggleAudio = function () {
\r
169 if (audioElement.paused) {
\r
172 scope.pauseAudio();
\r
176 scope.volumeUp = function (delta) {
\r
180 delta = delta / 100;
\r
182 audioElement.muted = false;
\r
183 if (audioElement.volume < 1) {
\r
184 audioElement.volume = Math.min((Math.round((audioElement.volume + delta) * 100) / 100), 1);
\r
186 scope.audio.currentVolume = audioElement.volume * 100;
\r
187 return audioElement.volume;
\r
190 scope.volumeDown = function (delta) {
\r
194 delta = delta / 100;
\r
196 audioElement.muted = false;
\r
197 if (audioElement.volume > 0) {
\r
198 audioElement.volume = Math.max((Math.round((audioElement.volume - delta) * 100) / 100), 0);
\r
200 scope.audio.currentVolume = audioElement.volume * 100;
\r
201 return audioElement.volume;
\r
204 var volumeHandler = function (e) {
\r
205 events.preventDefault(e);
\r
206 if ((e.wheelDelta && e.wheelDelta > 0) || (e.detail && e.detail < 0)) {
\r
209 scope.volumeDown();
\r
216 scope.$watch('audio.currentVolume', function (newVal, oldVal) {
\r
217 if (newVal !== oldVal) {
\r
218 audioElement.volume = newVal / 100;
\r
222 scope.setCurrentTime = function (timeInSec) {
\r
223 audioElement.currentTime = timeInSec;
\r
226 scope.setAudioPosition = function (val) {
\r
227 if (scope.isReady) {
\r
228 scope.setCurrentTime(val);
\r
229 scope.isAudioDragging = false;
\r
233 function getTimestampArray(timestamp) {
\r
234 var d = Math.abs(timestamp) / 1000; // delta
\r
235 var r = {}; // result
\r
236 var s = { // structure
\r
243 Object.keys(s).forEach(function (key) {
\r
244 r[key] = Math.floor(d / s[key]);
\r
245 d -= r[key] * s[key];
\r
251 scope.timeFormatter = function (timeInSec) {
\r
252 var formattedTime = '00:00';
\r
254 if (!timeInSec || timeInSec < 1) {
\r
255 return formattedTime;
\r
258 if (typeof timeInSec === 'string') {
\r
262 var dateArray = getTimestampArray(timeInSec * 1000);
\r
263 Object.keys(dateArray).forEach(function (key) {
\r
264 if (dateArray[key] === 0) {
\r
265 dateArray[key] = '00';
\r
266 } else if (dateArray[key] < 10) {
\r
267 dateArray[key] = '0' + dateArray[key];
\r
271 formattedTime = dateArray['minute'] + ':' + dateArray['second'];
\r
273 if (dateArray['hour'] !== '00') {
\r
274 formattedTime = dateArray['hour'] + ':' + formattedTime;
\r
277 if (dateArray['day'] !== '00') {
\r
278 formattedTime = dateArray['day'] + ':' + formattedTime;
\r
281 return formattedTime;
\r
284 audioElement.onloadedmetadata = function () {
\r
285 scope.audio.duration = audioElement.duration;
\r
289 audioElement.ontimeupdate = function () {
\r
290 if (!scope.isAudioDragging) {
\r
291 scope.audio.currentTime = audioElement.currentTime;
\r
296 audioElement.onended = function () {
\r
297 scope.setCurrentTime(0);
\r
298 scope.audio.currentTime = 0;
\r
299 if (!audioElement.paused) {
\r
300 scope.pauseAudio();
\r
305 audioElement.oncanplay = function () {
\r
306 scope.isReady = true;
\r
307 scope.isPlayInProgress = !audioElement.paused;
\r
311 var onloadstart = function () {
\r
312 scope.isReady = false;
\r
313 scope.isPlayInProgress = !audioElement.paused;
\r
314 scope.audio.currentTime = 0;
\r
315 scope.audio.duration = 0;
\r
318 audioElement.addEventListener("loadstart", onloadstart);
\r
324 * @name Videos, audio & animation.att:Audio Recorder
\r
326 * @param {function} callback - A callback to handle the WAV blob
\r
327 * @param {object} config - A config object with properties startRecordingMessage & whileRecordingMessage
\r
329 * <file src="src/audioRecorder/docs/readme.md" />
\r
333 * <section id="code">
\r
334 <example module="b2b.att">
\r
335 <file src="src/audioRecorder/docs/demo.html" />
\r
336 <file src="src/audioRecorder/docs/demo.js" />
\r
341 angular.module('b2b.att.audioRecorder', ['b2b.att.utilities'])
\r
342 .constant('AudioRecorderConfig', {
\r
343 'startRecordingMessage': 'Click on REC icon to being recording',
\r
344 'whileRecordingMessage': 'Recording...'
\r
346 .directive('b2bAudioRecorder', ['$interval', 'AudioRecorderConfig', 'b2bUserAgent', 'b2bRecorder', function($interval, AudioRecorderConfig, b2bUserAgent, b2bRecorder) {
\r
353 templateUrl: 'b2bTemplate/audioRecorder/audioRecorder.html',
\r
354 controller: function($scope) {
\r
356 function hasGetUserMedia() {
\r
357 return !!(navigator.getUserMedia || navigator.webkitGetUserMedia ||
\r
358 navigator.mozGetUserMedia || navigator.msGetUserMedia);
\r
361 if (!hasGetUserMedia()) {
\r
362 throw new Error('Your broswer does not support MediaRecorder API');
\r
365 if (!(b2bUserAgent.isFF() || b2bUserAgent.isChrome())) {
\r
366 throw new Error('b2bAudioRecorder does not support this browser!');
\r
370 link: function(scope, element) {
\r
371 scope.elapsedTime = 0;
\r
372 scope.isRecording = false;
\r
374 scope.config.startRecordingMessage = AudioRecorderConfig.startRecordingMessage;
\r
375 scope.config.whileRecordingMessage = AudioRecorderConfig.whileRecordingMessage;
\r
378 var timer = undefined; // Interval promise
\r
379 navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
\r
381 var audio = angular.element(element[0].querySelector('audio'))[0];
\r
382 var recorder = undefined;
\r
384 function startRecording() {
\r
385 scope.isRecording = true;
\r
386 navigator.mediaDevices.getUserMedia({
\r
388 }).then(function(stream) {
\r
389 //create the MediaStreamAudioSourceNode
\r
390 context = new AudioContext();
\r
391 source = context.createMediaStreamSource(stream);
\r
392 recorder = new b2bRecorder(source);
\r
395 timer = $interval(function() {
\r
396 scope.elapsedTime += 1;
\r
398 }).catch(function(err) {
\r
404 function stopRecording() {
\r
405 scope.isRecording = false;
\r
408 recorder.exportWAV(function(s) {
\r
409 audio.src = window.URL.createObjectURL(s);
\r
410 context.close().then(function() {
\r
412 $interval.cancel(timer);
\r
414 scope.elapsedTime = 0;
\r
417 recorder = undefined;
\r
419 if (angular.isFunction(scope.callback)){
\r
420 scope.callback({'data': audio});
\r
427 scope.toggleRecording = function() {
\r
428 if (scope.isRecording) {
\r
437 //TODO: Move this into utilities
\r
438 function getTimestampArray(timestamp) {
\r
439 var d = Math.abs(timestamp) / 1000; // delta
\r
440 var r = {}; // result
\r
441 var s = { // structure
\r
448 Object.keys(s).forEach(function(key) {
\r
449 r[key] = Math.floor(d / s[key]);
\r
450 d -= r[key] * s[key];
\r
455 scope.timeFormatter = function(timeInSec) {
\r
456 var formattedTime = '00:00';
\r
458 if (!timeInSec || timeInSec < 1) {
\r
459 return formattedTime;
\r
462 if (typeof timeInSec === 'string') {
\r
466 var dateArray = getTimestampArray(timeInSec * 1000);
\r
467 Object.keys(dateArray).forEach(function(key) {
\r
468 if (dateArray[key] === 0) {
\r
469 dateArray[key] = '00';
\r
470 } else if (dateArray[key] < 10) {
\r
471 dateArray[key] = '0' + dateArray[key];
\r
475 formattedTime = dateArray['minute'] + ':' + dateArray['second'];
\r
477 if (dateArray['hour'] !== '00') {
\r
478 formattedTime = dateArray['hour'] + ':' + formattedTime;
\r
481 if (dateArray['day'] !== '00') {
\r
482 formattedTime = dateArray['day'] + ':' + formattedTime;
\r
485 return formattedTime;
\r
488 scope.$on('$destroy', function() {
\r
490 $interval.cancel(timer);
\r
499 * @name Navigation.att:Back To Top
\r
502 * <file src="src/backToTop/docs/readme.md" />
\r
503 * @param {integer} scrollSpeed - Scroll speed in seconds, default is 1
\r
507 <div ng-controller="backToTopController">
\r
508 <div b2b-backtotop></div>
\r
512 * <section id="code">
\r
513 <example module="b2b.att">
\r
514 <file src="src/backToTop/docs/demo.html" />
\r
515 <file src="src/backToTop/docs/demo.js" />
\r
521 angular.module('b2b.att.backToTop', ['b2b.att.utilities','b2b.att.position'])
\r
522 .directive('b2bBacktotopButton', [function () {
\r
526 templateUrl: 'b2bTemplate/backToTop/backToTop.html',
\r
527 link: function (scope, elem, attr) {
\r
528 elem.bind('click', function(evt) {
\r
529 var scrollSpeed = parseInt(attr.scrollSpeed) || 1;
\r
530 TweenLite.to(window, scrollSpeed, {scrollTo:{x: 0, y: 0}});
\r
537 * @name Messages, modals & alerts.att:badgesForAlerts
\r
540 * <file src="src/badgesForAlerts/docs/readme.md" />
\r
542 * <section id="code">
\r
543 <example module="b2b.att">
\r
544 <file src="src/badgesForAlerts/docs/demo.html" />
\r
545 <file src="src/badgesForAlerts/docs/demo.js" />
\r
550 angular.module('b2b.att.badgesForAlerts', []);
\r
553 * @name Misc.att:boardstrip
\r
556 * <file src="src/boardstrip/docs/readme.md" />
\r
561 <section id="code">
\r
562 <b>HTML + AngularJS</b>
\r
563 <example module="b2b.att">
\r
564 <file src="src/boardstrip/docs/demo.html" />
\r
565 <file src="src/boardstrip/docs/demo.js" />
\r
569 angular.module('b2b.att.boardstrip', ['b2b.att.utilities'])
\r
570 .constant('BoardStripConfig', {
\r
571 'maxVisibleBoards': 4,
\r
572 'boardsToScroll': 1,
\r
573 /* These parameters are non-configurable and remain unaltered, until there is a change in corresponding CSS */
\r
574 'boardLength': 140,
\r
577 .directive('b2bBoard', [function () {
\r
582 require: '^b2bBoardStrip',
\r
587 templateUrl: 'b2bTemplate/boardstrip/b2bBoard.html',
\r
588 link: function (scope, element, attrs, ctrls) {
\r
590 var parentCtrl = ctrls;
\r
592 scope.getCurrentIndex = function () {
\r
593 return parentCtrl.getCurrentIndex();
\r
595 scope.selectBoard = function (boardIndex) {
\r
596 if (!isNaN(boardIndex)) {
\r
597 parentCtrl.setCurrentIndex(boardIndex);
\r
603 .directive('b2bBoardStrip', ['BoardStripConfig', '$timeout', function (BoardStripConfig, $timeout) {
\r
608 require: ['?ngModel', 'b2bBoardStrip'],
\r
610 boardsMasterArray: '=',
\r
613 templateUrl: 'b2bTemplate/boardstrip/b2bBoardstrip.html',
\r
614 controller: function ($scope) {
\r
615 if (!angular.isDefined($scope.boardsMasterArray)) {
\r
616 $scope.boardsMasterArray = [];
\r
619 this.rectifyMaxVisibleBoards = function () {
\r
620 if (this.maxVisibleIndex >= $scope.boardsMasterArray.length) {
\r
621 this.maxVisibleIndex = $scope.boardsMasterArray.length - 1;
\r
624 if (this.maxVisibleIndex < 0) {
\r
625 this.maxVisibleIndex = 0;
\r
629 this.resetBoardStrip = function () {
\r
630 $scope.currentIndex = 0;
\r
632 this.maxVisibleIndex = BoardStripConfig.maxVisibleBoards - 1;
\r
633 this.minVisibleIndex = 0;
\r
635 this.rectifyMaxVisibleBoards();
\r
638 this.getCurrentIndex = function () {
\r
639 return $scope.currentIndex;
\r
641 this.setCurrentIndex = function (indx) {
\r
642 $scope.currentIndex = indx;
\r
645 this.getBoardsMasterArrayLength = function () {
\r
646 return $scope.boardsMasterArray.length;
\r
649 $scope.addBoardPressedFlag = false;
\r
650 this.getAddBoardPressedFlag = function () {
\r
651 return $scope.addBoardPressedFlag;
\r
653 this.setAddBoardPressedFlag = function (booleanValue) {
\r
654 $scope.addBoardPressedFlag = booleanValue;
\r
658 link: function (scope, element, attrs, ctrls) {
\r
660 var ngModelCtrl = ctrls[0];
\r
661 var ctrl = ctrls[1];
\r
664 var animationTimeout = 1000;
\r
666 var getBoardViewportWidth = function (numberOfVisibleBoards) {
\r
667 return numberOfVisibleBoards * (BoardStripConfig.boardLength + BoardStripConfig.boardMargin);
\r
669 if (element[0].querySelector(".board-viewport")) {
\r
670 angular.element(element[0].querySelector(".board-viewport")).css({
\r
671 "width": getBoardViewportWidth(BoardStripConfig.maxVisibleBoards) + "px"
\r
675 var getBoardstripContainerWidth = function (totalNumberOfBoards) {
\r
676 return totalNumberOfBoards * (BoardStripConfig.boardLength + BoardStripConfig.boardMargin);
\r
678 if (element[0].querySelector(".boardstrip-container")) {
\r
679 angular.element(element[0].querySelector(".boardstrip-container")).css({
\r
680 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
\r
682 angular.element(element[0].querySelector(".boardstrip-container")).css({
\r
687 var calculateAndGetBoardstripContainerAdjustment = function () {
\r
689 var calculatedAdjustmentValue;
\r
691 if (ctrl.getBoardsMasterArrayLength() <= BoardStripConfig.maxVisibleBoards) {
\r
692 calculatedAdjustmentValue = 0;
\r
694 calculatedAdjustmentValue = (ctrl.minVisibleIndex * (BoardStripConfig.boardLength + BoardStripConfig.boardMargin)) * -1;
\r
697 return calculatedAdjustmentValue;
\r
700 var animateBoardstripContainerAdjustment = function (elementToFocusAfterAnimation) {
\r
701 var oldContainerAdjustment = angular.element(element[0].querySelector(".boardstrip-container"))[0].style.left;
\r
702 var containerAdjustment = calculateAndGetBoardstripContainerAdjustment();
\r
703 if (oldContainerAdjustment !== containerAdjustment + 'px') {
\r
704 angular.element(element[0].querySelector(".boardstrip-container")).css({
\r
705 "left": containerAdjustment + "px"
\r
708 $timeout.cancel(oldTimeout);
\r
709 oldTimeout = $timeout(function () {
\r
710 elementToFocusAfterAnimation.focus();
\r
711 }, animationTimeout);
\r
713 elementToFocusAfterAnimation.focus();
\r
717 var updateBoardsTabIndex = function (boardArray, minViewIndex, maxViewIndex) {
\r
718 for (var i = 0; i < boardArray.length; i++) {
\r
719 angular.element(boardArray[i]).attr('tabindex', '-1');
\r
721 for (var j = minViewIndex; j <= maxViewIndex; j++) {
\r
722 angular.element(boardArray[j]).attr('tabindex', '0');
\r
726 $timeout(function () {
\r
727 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
\r
728 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
\r
731 scope.$watchCollection('boardsMasterArray', function (newVal, oldVal) {
\r
732 if (newVal !== oldVal) {
\r
733 /* When a board is removed */
\r
734 if (newVal.length < oldVal.length) {
\r
735 ctrl.resetBoardStrip();
\r
736 $timeout(function () {
\r
738 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
\r
739 if (currentBoardArray.length !== 0) {
\r
740 animateBoardstripContainerAdjustment(currentBoardArray[0]);
\r
742 element[0].querySelector('div.boardstrip-item--add').focus();
\r
745 angular.element(element[0].querySelector(".boardstrip-container")).css({
\r
746 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
\r
748 /* Update tabindecies to ensure keyboard navigation behaves correctly */
\r
749 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
\r
752 /* When a board is added */
\r
754 if (ctrl.getAddBoardPressedFlag()) {
\r
755 ctrl.maxVisibleIndex = ctrl.getBoardsMasterArrayLength() - 1;
\r
756 ctrl.minVisibleIndex = Math.max(ctrl.maxVisibleIndex - BoardStripConfig.maxVisibleBoards + 1, 0);
\r
758 ctrl.setCurrentIndex(ctrl.maxVisibleIndex);
\r
760 $timeout(function () {
\r
761 angular.element(element[0].querySelector(".boardstrip-container")).css({
\r
762 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
\r
765 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
\r
766 animateBoardstripContainerAdjustment(currentBoardArray[currentBoardArray.length - 1]);
\r
767 /* Update tabindecies to ensure keyboard navigation behaves correctly */
\r
768 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
\r
771 if (ctrl.minVisibleIndex === 0 && ctrl.getBoardsMasterArrayLength() < BoardStripConfig.maxVisibleBoards + 1) {
\r
772 ctrl.maxVisibleIndex = ctrl.getBoardsMasterArrayLength() - 1;
\r
773 ctrl.rectifyMaxVisibleBoards();
\r
776 $timeout(function () {
\r
777 angular.element(element[0].querySelector(".boardstrip-container")).css({
\r
778 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
\r
781 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
\r
782 /* Update tabindecies to ensure keyboard navigation behaves correctly */
\r
783 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
\r
787 ctrl.setAddBoardPressedFlag(false);
\r
792 scope.nextBoard = function () {
\r
793 ctrl.maxVisibleIndex += BoardStripConfig.boardsToScroll;
\r
794 ctrl.rectifyMaxVisibleBoards();
\r
795 ctrl.minVisibleIndex = ctrl.maxVisibleIndex - (BoardStripConfig.maxVisibleBoards - 1);
\r
797 $timeout.cancel(oldTimeout);
\r
798 angular.element(element[0].querySelector(".boardstrip-container")).css({
\r
799 "left": calculateAndGetBoardstripContainerAdjustment() + "px"
\r
802 $timeout(function () {
\r
803 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
\r
805 /* Remove tabindex from non-visible boards */
\r
806 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
\r
808 if (!(scope.isNextBoard())) {
\r
810 currentBoardArray[currentBoardArray.length - 1].focus();
\r
811 } catch (e) { /* IE8 may throw exception */ }
\r
813 }, animationTimeout);
\r
815 scope.prevBoard = function () {
\r
817 ctrl.minVisibleIndex -= BoardStripConfig.boardsToScroll;
\r
818 if (ctrl.minVisibleIndex < 0) {
\r
819 ctrl.minVisibleIndex = 0;
\r
822 ctrl.maxVisibleIndex = ctrl.minVisibleIndex + BoardStripConfig.maxVisibleBoards - 1;
\r
823 ctrl.rectifyMaxVisibleBoards();
\r
825 $timeout.cancel(oldTimeout);
\r
826 angular.element(element[0].querySelector(".boardstrip-container")).css({
\r
827 "left": calculateAndGetBoardstripContainerAdjustment() + "px"
\r
830 $timeout(function () {
\r
831 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
\r
833 /* Remove tabindex from non-visible boards */
\r
834 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
\r
836 if (ctrl.minVisibleIndex === 0) {
\r
838 element[0].querySelector('div.boardstrip-item--add').focus();
\r
839 } catch (e) { /* IE8 may throw exception */ }
\r
844 scope.isPrevBoard = function () {
\r
845 return (ctrl.minVisibleIndex > 0);
\r
847 scope.isNextBoard = function () {
\r
848 return (ctrl.getBoardsMasterArrayLength() - 1 > ctrl.maxVisibleIndex);
\r
851 ngModelCtrl.$render = function () {
\r
852 if (ngModelCtrl.$viewValue || ngModelCtrl.$viewValue === 0) {
\r
853 var newCurrentIndex = ngModelCtrl.$viewValue;
\r
855 if (!(newCurrentIndex = parseInt(newCurrentIndex, 10))) {
\r
856 newCurrentIndex = 0;
\r
859 if (newCurrentIndex <= 0) {
\r
860 ctrl.resetBoardStrip();
\r
861 newCurrentIndex = 0;
\r
863 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
\r
864 if (currentBoardArray.length !== 0) {
\r
865 animateBoardstripContainerAdjustment(currentBoardArray[0]);
\r
867 element[0].querySelector('div.boardstrip-item--add').focus();
\r
869 /* Update tabindecies to ensure keyboard navigation behaves correctly */
\r
870 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
\r
871 } else if (newCurrentIndex >= ctrl.getBoardsMasterArrayLength()) {
\r
872 ctrl.maxVisibleIndex = ctrl.getBoardsMasterArrayLength() - 1;
\r
873 ctrl.rectifyMaxVisibleBoards();
\r
874 ctrl.minVisibleIndex = Math.max(ctrl.maxVisibleIndex - BoardStripConfig.maxVisibleBoards + 1, 0);
\r
876 newCurrentIndex = ctrl.maxVisibleIndex;
\r
878 $timeout(function () {
\r
879 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
\r
880 animateBoardstripContainerAdjustment(currentBoardArray[newCurrentIndex]);
\r
881 /* Update tabindecies to ensure keyboard navigation behaves correctly */
\r
882 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
\r
886 if (!(newCurrentIndex >= ctrl.minVisibleIndex && newCurrentIndex <= ctrl.maxVisibleIndex)) {
\r
887 ctrl.minVisibleIndex = newCurrentIndex;
\r
888 ctrl.maxVisibleIndex = ctrl.minVisibleIndex + BoardStripConfig.maxVisibleBoards - 1;
\r
889 ctrl.rectifyMaxVisibleBoards();
\r
891 if (ctrl.getBoardsMasterArrayLength() < BoardStripConfig.maxVisibleBoards) {
\r
892 ctrl.minVisibleIndex = 0;
\r
894 ctrl.minVisibleIndex = Math.max(ctrl.maxVisibleIndex - BoardStripConfig.maxVisibleBoards + 1, 0);
\r
897 $timeout(function () {
\r
898 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
\r
899 animateBoardstripContainerAdjustment(currentBoardArray[newCurrentIndex]);
\r
900 /* Update tabindecies to ensure keyboard navigation behaves correctly */
\r
901 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
\r
905 scope.currentIndex = newCurrentIndex;
\r
906 ngModelCtrl.$setViewValue(newCurrentIndex);
\r
908 ctrl.resetBoardStrip();
\r
909 ngModelCtrl.$setViewValue(0);
\r
913 scope.$watch('currentIndex', function (newVal, oldVal) {
\r
914 if (newVal !== oldVal && ngModelCtrl && ngModelCtrl.$viewValue !== newVal) {
\r
915 ngModelCtrl.$setViewValue(newVal);
\r
921 .directive('b2bAddBoard', ['BoardStripConfig', '$parse', function (BoardStripConfig, $parse) {
\r
925 require: '^b2bBoardStrip',
\r
929 templateUrl: 'b2bTemplate/boardstrip/b2bAddBoard.html',
\r
930 link: function (scope, element, attrs, ctrl) {
\r
931 scope.addBoard = function () {
\r
932 if (attrs['onAddBoard']) {
\r
933 scope.onAddBoard = $parse(scope.onAddBoard);
\r
934 scope.onAddBoard();
\r
935 ctrl.setAddBoardPressedFlag(true);
\r
941 .directive('b2bBoardNavigation', ['keymap', 'events', function (keymap, events) {
\r
944 link: function (scope, elem) {
\r
946 var prevElem = keymap.KEY.LEFT;
\r
947 var nextElem = keymap.KEY.RIGHT;
\r
949 elem.bind('keydown', function (ev) {
\r
951 if (!(ev.keyCode)) {
\r
952 ev.keyCode = ev.which;
\r
955 switch (ev.keyCode) {
\r
957 events.preventDefault(ev);
\r
958 events.stopPropagation(ev);
\r
960 if (elem[0].nextElementSibling && parseInt(angular.element(elem[0].nextElementSibling).attr('tabindex')) >= 0) {
\r
961 angular.element(elem[0])[0].nextElementSibling.focus();
\r
964 var el = angular.element(elem[0])[0];
\r
966 if (el.nextSibling) {
\r
967 el = el.nextSibling;
\r
971 } while (el && el.tagName !== 'LI');
\r
973 if (el.tagName && el.tagName === 'LI' && parseInt(angular.element(el).attr('tabindex')) >= 0) {
\r
980 events.preventDefault(ev);
\r
981 events.stopPropagation(ev);
\r
983 if (elem[0].previousElementSibling && parseInt(angular.element(elem[0].previousElementSibling).attr('tabindex')) >= 0) {
\r
984 angular.element(elem[0])[0].previousElementSibling.focus();
\r
987 var el1 = angular.element(elem[0])[0];
\r
989 if (el1.previousSibling) {
\r
990 el1 = el1.previousSibling;
\r
994 } while (el1 && el1.tagName !== 'LI');
\r
996 if (el1.tagName && el1.tagName === 'LI' && parseInt(angular.element(el1).attr('tabindex')) >= 0) {
\r
1009 * @ngdoc directive
\r
1010 * @name Navigation.att:breadcrumbs
\r
1013 * <file src="src/breadcrumbs/docs/readme.md" />
\r
1015 <ul class="breadcrumb">
\r
1016 <li ng-repeat="link in breadCrumbsLink"><a tabindex="{{(idx==$index)?-1:0}}" href='javascript:void(0)' ng-click="clickActive($index)" ng-class="{'active':idx==$index, '': idx!=$index}">{{link.title}}</a></li>
\r
1019 <example module="b2b.att">
\r
1020 <file src="src/breadcrumbs/docs/demo.html" />
\r
1021 <file src="src/breadcrumbs/docs/demo.js" />
\r
1024 angular.module('b2b.att.breadcrumbs',[])
\r
1026 * @ngdoc directive
\r
1027 * @name Buttons, links & UI controls.att:buttonGroups
\r
1030 * <file src="src/buttonGroups/docs/readme.md" />
\r
1033 <h2>Radio Aproach</h2>
\r
1034 <div class="btn-group" b2b-key prev="37,38" next="39,40" circular-traversal role="radiogroup">
\r
1035 <button type="button" class="btn btn-secondary" b2b-key-item ng-focus="radioModel='Button 1'" ng-model="radioModel" b2b-btn-radio="'Button 1'" tabindex="{{(!radioModel || 'Button 1'===radioModel)?0:-1}}">Button 1</button>
\r
1036 <button type="button" class="btn btn-secondary" b2b-key-item ng-focus="radioModel='Button 2'" ng-model="radioModel" b2b-btn-radio="'Button 2'" tabindex="{{(!radioModel || 'Button 2'===radioModel)?0:-1}}">Button 2</button>
\r
1037 <button type="button" class="btn btn-secondary" b2b-key-item ng-focus="radioModel='Button 3'" ng-model="radioModel" b2b-btn-radio="'Button 3'" tabindex="{{(!radioModel || 'Button 3'===radioModel)?0:-1}}">Button 3</button>
\r
1040 <h2>Checkbox Aproach</h2>
\r
1041 <span b2b-button-group class="btn-group btn-fullwidth" role="group" max-select="3" ng-model="checkModel1">
\r
1042 <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button1" b2b-btn-checkbox>Button1</button>
\r
1043 <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button2" b2b-btn-checkbox>Button2</button>
\r
1044 <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button3" b2b-btn-checkbox>Button3</button>
\r
1045 <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button4" b2b-btn-checkbox>Button4</button>
\r
1046 <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button5" b2b-btn-checkbox>Button5</button>
\r
1050 * <section id="code">
\r
1051 <example module="b2b.att">
\r
1052 <file src="src/buttonGroups/docs/demo.html" />
\r
1053 <file src="src/buttonGroups/docs/demo.js" />
\r
1058 angular.module('b2b.att.buttonGroups', ['b2b.att.utilities'])
\r
1059 .constant('buttonConfig', {
\r
1060 activeClass: 'active',
\r
1061 toggleEvent: 'click'
\r
1063 .directive('b2bBtnRadio', ['buttonConfig', function (buttonConfig) {
\r
1064 var activeClass = buttonConfig.activeClass || 'active';
\r
1065 var toggleEvent = buttonConfig.toggleEvent || 'click';
\r
1068 require: 'ngModel',
\r
1069 link: function (scope, element, attrs, ngModelCtrl) {
\r
1070 var notMobile = !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
\r
1073 element.bind('focus', function () {
\r
1074 scope.$apply(function () {
\r
1075 ngModelCtrl.$setViewValue(scope.$eval(attrs.b2bBtnRadio));
\r
1076 ngModelCtrl.$render();
\r
1081 element.attr('role', 'radio');
\r
1084 ngModelCtrl.$render = function () {
\r
1085 element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.b2bBtnRadio)));
\r
1086 if (angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.b2bBtnRadio))) {
\r
1087 element.attr("aria-checked", true);
\r
1089 element.attr("aria-checked", false);
\r
1094 element.bind(toggleEvent, function () {
\r
1095 if (!element.hasClass(activeClass)) {
\r
1096 scope.$apply(function () {
\r
1097 ngModelCtrl.$setViewValue(scope.$eval(attrs.b2bBtnRadio));
\r
1098 ngModelCtrl.$render();
\r
1105 .directive('b2bBtnCheckbox', ['buttonConfig', function (buttonConfig) {
\r
1106 var activeClass = buttonConfig.activeClass || 'active';
\r
1107 var toggleEvent = buttonConfig.toggleEvent || 'click';
\r
1110 require: ['ngModel', '^^b2bButtonGroup'],
\r
1111 link: function (scope, element, attrs, ctrls) {
\r
1113 var ngModelCtrl = ctrls[0];
\r
1114 var parentCtrl = ctrls[1];
\r
1116 element.attr('role', 'checkbox');
\r
1117 element.attr('aria-describedby', parentCtrl.getStateDescriptionElemId());
\r
1119 function getTrueValue() {
\r
1120 var trueValue = scope.$eval(attrs.b2bBtnCheckboxTrue);
\r
1121 return angular.isDefined(trueValue) ? trueValue : true;
\r
1124 function getFalseValue() {
\r
1125 var falseValue = scope.$eval(attrs.b2bBtnCheckboxFalse);
\r
1126 return angular.isDefined(falseValue) ? falseValue : false;
\r
1130 ngModelCtrl.$render = function () {
\r
1131 element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
\r
1132 if ((angular.equals(ngModelCtrl.$modelValue, getTrueValue()))) {
\r
1133 element.attr("aria-checked", true);
\r
1135 element.attr("aria-checked", false);
\r
1140 element.bind(toggleEvent, function () {
\r
1141 scope.$apply(function () {
\r
1142 ngModelCtrl.$setViewValue(element.hasClass(activeClass) ? getFalseValue() : getTrueValue());
\r
1143 ngModelCtrl.$render();
\r
1149 .directive('b2bButtonGroup', ['$timeout', '$compile', function ($timeout, $compile) {
\r
1154 ngModelButtonState: '=ngModel'
\r
1156 controller: ['$scope', '$element', function ($scope, $element) {
\r
1159 var stateDescriptionElem = angular.element('<span id="b2b_button_group_' + $scope.$id + '" class="hide" aria-hidden="true">{{nSel}} of {{maxSelect}} options selected.</span>');
\r
1160 $compile(stateDescriptionElem)($scope);
\r
1161 $element.after(stateDescriptionElem);
\r
1163 this.getStateDescriptionElemId = function () {
\r
1164 return stateDescriptionElem.attr('id');
\r
1167 link: function (scope, element) {
\r
1170 var executeFxn = function () {
\r
1172 angular.forEach(scope.ngModelButtonState, function (value, key) {
\r
1173 if (value === true) {
\r
1178 if (scope.nSel >= scope.maxSelect) {
\r
1179 angular.forEach(element.children(), function (chd) {
\r
1180 if (chd.className.indexOf('active') < 0) {
\r
1181 chd.disabled = true;
\r
1182 chd.setAttribute('aria-disabled', true);
\r
1186 angular.forEach(element.children(), function (chd) {
\r
1187 chd.disabled = false;
\r
1188 chd.setAttribute('aria-disabled', false);
\r
1194 $timeout(function () {
\r
1197 element.bind('click', executeFxn);
\r
1202 * @ngdoc directive
\r
1203 * @name Buttons, links & UI controls.att:buttons
\r
1208 * <file src="src/buttons/docs/readme.md" />
\r
1212 <button class="btn" type="button">Button</button> button.btn (button shape only)
\r
1213 <button aria-label="Custom aria label" class="btn" type="button">Button</button> button.btn (button shape only) with custom aria label
\r
1214 <button aria-label="Click on button/Press enter" class="btn" type="button" onclick="javascript:alert('It works!');">Click on button/Press enter</button> button.btn with click functionality
\r
1215 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn" role="button">Button</a> a.btn (button shape only)
\r
1216 <button class="btn btn-primary">Button</button> .btn-primary
\r
1217 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn btn-primary" role="button">Button</a> a.btn-primary
\r
1220 <button class="btn btn-secondary">Button</button> .btn-secondary
\r
1221 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn btn-secondary" role="button">Button</a> a.btn-secondary
\r
1222 <button class="btn btn-alt">Button</button> .btn-alt
\r
1223 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn btn-alt" role="button">Button</a> a.btn-alt
\r
1224 <button class="btn btn-specialty">Button</button> .btn-specialty
\r
1225 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn btn-specialty" role="button">Button</a> a.btn-specialty
\r
1226 <button class="btn btn-specialty" disabled="">Button</button> disabled="disabled"
\r
1227 <a b2b-keyup-click="32" aria-disabled="true" href="javascript:void(0)" class="btn btn-primary disabled" role="button">Button</a> a.disabled
\r
1230 <button class="btn btn-secondary">Button</button> .btn is default and 46px height
\r
1231 <button class="btn btn-secondary btn-medium">Button</button> .btn-medium is 42px
\r
1232 <button class="btn btn-secondary btn-small">Button</button> .btn-small is 36px
\r
1234 .row-nowrap 2 up buttons
\r
1235 <div class="row-nowrap">
\r
1236 <button class="btn btn-secondary btn-fullwidth" type="button">Cancel</button>
\r
1237 <button class="btn btn-primary btn-fullwidth" type="button">Continue</button>
\r
1240 .row 2 up buttons (desktop) stacked (mobile) (different order)
\r
1241 <div class="row cta-button-group">
\r
1242 <button class="span btn btn-secondary btn-fullwidth hidden-phone" type="button">Cancel</button>
\r
1243 <button class="span btn btn-primary btn-fullwidth" type="button">Continue</button>
\r
1244 <button class="span btn btn-secondary btn-fullwidth visible-phone" type="button">Cancel</button>
\r
1248 * <section id="code">
\r
1249 <b>HTML + AngularJS</b>
\r
1250 * <example module="b2b.att">
\r
1251 * <file src="src/buttons/docs/demo.html" />
\r
1252 <file src="src/buttons/docs/demo.js" />
\r
1257 angular.module('b2b.att.buttons', ['b2b.att.utilities']);
\r
1259 * @ngdoc directive
\r
1260 * @name Forms.att:calendar
\r
1263 * <file src="src/calendar/docs/readme.md" />
\r
1265 * <input type="text" ng-model="dt" b2b-datepicker>
\r
1268 <section id="code">
\r
1269 <b>HTML + AngularJS</b>
\r
1270 <example module="b2b.att">
\r
1271 <file src="src/calendar/docs/demo.html" />
\r
1272 <file src="src/calendar/docs/demo.js" />
\r
1276 angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities'])
\r
1278 .constant('b2bDatepickerConfig', {
\r
1279 dateFormat: 'MM/dd/yyyy',
\r
1281 monthFormat: 'MMMM',
\r
1282 yearFormat: 'yyyy',
\r
1283 dayHeaderFormat: 'EEEE',
\r
1284 dayTitleFormat: 'MMMM yyyy',
\r
1285 disableWeekend: false,
\r
1286 disableSunday: false,
\r
1287 disableDates: null,
\r
1288 onSelectClose: null,
\r
1295 legendMessage: null,
\r
1296 calendarDisabled: false,
\r
1298 orientation: 'left',
\r
1300 helperText: 'The date you selected is $date. In case of mobile double tap to open calendar. Select a date to close the calendar.',
\r
1301 datepickerEvalAttributes: ['dateFormat', 'dayFormat', 'monthFormat', 'yearFormat', 'dayHeaderFormat', 'dayTitleFormat', 'disableWeekend', 'disableSunday', 'startingDay', 'collapseWait', 'orientation'],
\r
1302 datepickerWatchAttributes: ['min', 'max', 'due', 'from', 'legendIcon', 'legendMessage', 'ngDisabled'],
\r
1303 datepickerFunctionAttributes: ['disableDates', 'onSelectClose']
\r
1306 .factory('b2bDatepickerService', ['b2bDatepickerConfig', 'dateFilter', function (b2bDatepickerConfig, dateFilter) {
\r
1307 var setAttributes = function (attr, elem) {
\r
1308 if (angular.isDefined(attr) && attr !== null && angular.isDefined(elem) && elem !== null) {
\r
1309 var attributes = b2bDatepickerConfig.datepickerEvalAttributes.concat(b2bDatepickerConfig.datepickerWatchAttributes, b2bDatepickerConfig.datepickerFunctionAttributes);
\r
1310 for (var key in attr) {
\r
1311 var val = attr[key];
\r
1312 if (attributes.indexOf(key) !== -1 && angular.isDefined(val)) {
\r
1313 elem.attr(key.toSnakeCase(), key);
\r
1319 var bindScope = function (attr, scope) {
\r
1320 if (angular.isDefined(attr) && attr !== null && angular.isDefined(scope) && scope !== null) {
\r
1321 var evalFunction = function (key, val) {
\r
1322 scope[key] = scope.$parent.$eval(val);
\r
1325 var watchFunction = function (key, val) {
\r
1326 scope.$parent.$watch(val, function (value) {
\r
1327 scope[key] = value;
\r
1329 scope.$watch(key, function (value) {
\r
1330 scope.$parent[val] = value;
\r
1334 var evalAttributes = b2bDatepickerConfig.datepickerEvalAttributes;
\r
1335 var watchAttributes = b2bDatepickerConfig.datepickerWatchAttributes;
\r
1336 for (var key in attr) {
\r
1337 var val = attr[key];
\r
1338 if (evalAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
\r
1339 evalFunction(key, val);
\r
1340 } else if (watchAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
\r
1341 watchFunction(key, val);
\r
1348 setAttributes: setAttributes,
\r
1349 bindScope: bindScope
\r
1353 .controller('b2bDatepickerController', ['$scope', '$attrs', 'dateFilter', '$element', '$position', 'b2bDatepickerConfig', function ($scope, $attrs, dateFilter, $element, $position, dtConfig) {
\r
1355 date: getValue($attrs.dateFormat, dtConfig.dateFormat),
\r
1356 day: getValue($attrs.dayFormat, dtConfig.dayFormat),
\r
1357 month: getValue($attrs.monthFormat, dtConfig.monthFormat),
\r
1358 year: getValue($attrs.yearFormat, dtConfig.yearFormat),
\r
1359 dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat),
\r
1360 dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat),
\r
1361 disableWeekend: getValue($attrs.disableWeekend, dtConfig.disableWeekend),
\r
1362 disableSunday: getValue($attrs.disableSunday, dtConfig.disableSunday),
\r
1363 disableDates: getValue($attrs.disableDates, dtConfig.disableDates)
\r
1365 startingDay = getValue($attrs.startingDay, dtConfig.startingDay);
\r
1367 $scope.minDate = dtConfig.minDate ? $scope.resetTime(dtConfig.minDate) : null;
\r
1368 $scope.maxDate = dtConfig.maxDate ? $scope.resetTime(dtConfig.maxDate) : null;
\r
1369 $scope.dueDate = dtConfig.dueDate ? $scope.resetTime(dtConfig.dueDate) : null;
\r
1370 $scope.fromDate = dtConfig.fromDate ? $scope.resetTime(dtConfig.fromDate) : null;
\r
1371 $scope.legendIcon = dtConfig.legendIcon ? dtConfig.legendIcon : null;
\r
1372 $scope.legendMessage = dtConfig.legendMessage ? dtConfig.legendMessage : null;
\r
1373 $scope.ngDisabled = dtConfig.calendarDisabled ? dtConfig.calendarDisabled : null;
\r
1374 $scope.collapseWait = getValue($attrs.collapseWait, dtConfig.collapseWait);
\r
1375 $scope.orientation = getValue($attrs.orientation, dtConfig.orientation);
\r
1376 $scope.onSelectClose = getValue($attrs.onSelectClose, dtConfig.onSelectClose);
\r
1378 $scope.inline = $attrs.inline === 'true' ? true : dtConfig.inline;
\r
1380 function getValue(value, defaultValue) {
\r
1381 return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
\r
1384 function getDaysInMonth(year, month) {
\r
1385 return new Date(year, month, 0).getDate();
\r
1388 function getDates(startDate, n) {
\r
1389 var dates = new Array(n);
\r
1390 var current = startDate,
\r
1393 dates[i++] = new Date(current);
\r
1394 current.setDate(current.getDate() + 1);
\r
1399 this.updatePosition = function (b2bDatepickerPopupTemplate) {
\r
1400 $scope.position = $position.offset($element);
\r
1401 $scope.position.top = $scope.position.top + $element.find('input').prop('offsetHeight');
\r
1402 if ($scope.orientation === 'right') {
\r
1403 $scope.position.left = $scope.position.left - (((b2bDatepickerPopupTemplate && b2bDatepickerPopupTemplate.prop('offsetWidth')) || 290) - $element.find('input').prop('offsetWidth'));
\r
1407 function isSelected(dt) {
\r
1408 if (dt && angular.isDate($scope.currentDate) && compare(dt, $scope.currentDate) === 0) {
\r
1414 function isFromDate(dt) {
\r
1415 if (dt && angular.isDate($scope.fromDate) && compare(dt, $scope.fromDate) === 0) {
\r
1421 function isDateRange(dt) {
\r
1422 if (dt && $scope.fromDate && angular.isDate($scope.currentDate) && (compare(dt, $scope.fromDate) >= 0) && (compare(dt, $scope.currentDate) <= 0)) {
\r
1424 } else if (dt && $scope.fromDate && compare(dt, $scope.fromDate) === 0) {
\r
1430 function isOld(date, currentMonthDate) {
\r
1431 if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() < new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
\r
1438 function isNew(date, currentMonthDate) {
\r
1439 if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() > new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
\r
1446 function isPastDue(dt) {
\r
1447 if ($scope.dueDate) {
\r
1448 return (dt > $scope.dueDate);
\r
1453 function isDueDate(dt) {
\r
1454 if ($scope.dueDate) {
\r
1455 return (dt.getTime() === $scope.dueDate.getTime());
\r
1460 var isDisabled = function (date, currentMonthDate) {
\r
1461 if ($attrs.from && !angular.isDate($scope.fromDate)) {
\r
1464 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
\r
1467 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
\r
1470 if (isOld(date, currentMonthDate) || isNew(date, currentMonthDate)) {
\r
1473 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
\r
1478 var compare = function (date1, date2) {
\r
1479 return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
\r
1482 function isMinDateAvailable(startDate, endDate) {
\r
1483 if (($scope.minDate && $scope.minDate.getTime() >= startDate.getTime()) && ($scope.minDate.getTime() <= endDate.getTime())) {
\r
1484 $scope.disablePrev = true;
\r
1485 $scope.visibilityPrev = "hidden";
\r
1487 $scope.disablePrev = false;
\r
1488 $scope.visibilityPrev = "visible";
\r
1492 function isMaxDateAvailable(startDate, endDate) {
\r
1493 if (($scope.maxDate && $scope.maxDate.getTime() >= startDate.getTime()) && ($scope.maxDate.getTime() <= endDate.getTime())) {
\r
1494 $scope.disableNext = true;
\r
1495 $scope.visibilityNext = "hidden";
\r
1497 $scope.disableNext = false;
\r
1498 $scope.visibilityNext = "visible";
\r
1502 function getLabel(label) {
\r
1505 pre: label.substr(0, 1).toUpperCase(),
\r
1513 function makeDate(date, dayFormat, dayHeaderFormat, isSelected, isFromDate, isDateRange, isOld, isNew, isDisabled, dueDate, pastDue) {
\r
1516 label: dateFilter(date, dayFormat),
\r
1517 header: dateFilter(date, dayHeaderFormat),
\r
1518 selected: !!isSelected,
\r
1519 fromDate: !!isFromDate,
\r
1520 dateRange: !!isDateRange,
\r
1521 oldMonth: !!isOld,
\r
1522 nextMonth: !!isNew,
\r
1523 disabled: !!isDisabled,
\r
1524 dueDate: !!dueDate,
\r
1525 pastDue: !!pastDue,
\r
1526 focusable: !((isDisabled && !(isSelected || isDateRange)) || (isOld || isNew))
\r
1533 getVisibleDates: function (date) {
\r
1534 var year = date.getFullYear(),
\r
1535 month = date.getMonth(),
\r
1536 firstDayOfMonth = new Date(year, month, 1),
\r
1537 lastDayOfMonth = new Date(year, month + 1, 0);
\r
1538 var difference = startingDay - firstDayOfMonth.getDay(),
\r
1539 numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : -difference,
\r
1540 firstDate = new Date(firstDayOfMonth),
\r
1543 if (numDisplayedFromPreviousMonth > 0) {
\r
1544 firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
\r
1545 numDates += numDisplayedFromPreviousMonth; // Previous
\r
1547 numDates += getDaysInMonth(year, month + 1); // Current
\r
1548 numDates += (7 - numDates % 7) % 7; // Next
\r
1550 var days = getDates(firstDate, numDates),
\r
1551 labels = new Array(7);
\r
1552 for (var i = 0; i < numDates; i++) {
\r
1553 var dt = new Date(days[i]);
\r
1554 days[i] = makeDate(dt,
\r
1562 isDisabled(dt, date),
\r
1566 for (var j = 0; j < 7; j++) {
\r
1567 labels[j] = getLabel(dateFilter(days[j].date, format.dayHeader));
\r
1569 isMinDateAvailable(firstDayOfMonth, lastDayOfMonth);
\r
1570 isMaxDateAvailable(firstDayOfMonth, lastDayOfMonth);
\r
1573 title: dateFilter(date, format.dayTitle),
\r
1585 .directive('b2bDatepickerPopup', ['$parse', '$log', '$timeout', '$document', '$documentBind', '$isElement', '$templateCache', '$compile', 'trapFocusInElement', '$position', '$window', function ($parse, $log, $timeout, $document, $documentBind, $isElement, $templateCache, $compile, trapFocusInElement, $position, $window) {
\r
1590 templateUrl: function (elem, attr) {
\r
1591 if (attr.inline === 'true') {
\r
1592 return 'b2bTemplate/calendar/datepicker-popup.html';
\r
1594 return 'b2bTemplate/calendar/datepicker.html';
\r
1598 require: ['b2bDatepickerPopup', 'ngModel', '?^b2bDatepickerGroup'],
\r
1599 controller: 'b2bDatepickerController',
\r
1600 link: function (scope, element, attrs, ctrls) {
\r
1601 var datepickerCtrl = ctrls[0],
\r
1602 ngModel = ctrls[1],
\r
1603 b2bDatepickerGroupCtrl = ctrls[2];
\r
1604 var b2bDatepickerPopupTemplate;
\r
1607 $log.error("ng-model is required.");
\r
1608 return; // do nothing if no ng-model
\r
1611 // Configuration parameters
\r
1614 scope.isOpen = false;
\r
1616 scope.headers = [];
\r
1617 scope.footers = [];
\r
1619 if (b2bDatepickerGroupCtrl) {
\r
1620 b2bDatepickerGroupCtrl.registerDatepickerScope(scope);
\r
1623 element.find('button').bind('click', function () {
\r
1624 element.find('input')[0].click();
\r
1627 element.find('input').bind('click', function () {
\r
1628 if (!scope.ngDisabled) {
\r
1629 scope.isOpen = !scope.isOpen;
\r
1630 toggleCalendar(scope.isOpen);
\r
1632 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
\r
1633 $timeout(function () {
\r
1634 angular.element(element[0].querySelector('.datepicker-input')).scrollTop=0;
\r
1638 var toggleCalendar = function (flag) {
\r
1639 if (!scope.inline) {
\r
1641 b2bDatepickerPopupTemplate = angular.element($templateCache.get('b2bTemplate/calendar/datepicker-popup.html'));
\r
1642 b2bDatepickerPopupTemplate = $compile(b2bDatepickerPopupTemplate)(scope);
\r
1643 $document.find('body').append(b2bDatepickerPopupTemplate);
\r
1644 b2bDatepickerPopupTemplate.bind('keydown', keyPress);
\r
1645 $timeout(function () {
\r
1646 scope.getFocus = true;
\r
1647 trapFocusInElement(flag, b2bDatepickerPopupTemplate);
\r
1649 $timeout(function () {
\r
1650 scope.getFocus = false;
\r
1655 b2bDatepickerPopupTemplate.unbind('keydown', keyPress);
\r
1656 b2bDatepickerPopupTemplate.remove();
\r
1657 element.find('button')[0].focus();
\r
1658 scope.getFocus = false;
\r
1659 trapFocusInElement(flag, b2bDatepickerPopupTemplate);
\r
1664 var outsideClick = function (e) {
\r
1665 var isElement = $isElement(angular.element(e.target), element, $document);
\r
1666 var isb2bDatepickerPopupTemplate = $isElement(angular.element(e.target), b2bDatepickerPopupTemplate, $document);
\r
1667 if (!(isElement || isb2bDatepickerPopupTemplate)) {
\r
1668 scope.isOpen = false;
\r
1669 toggleCalendar(scope.isOpen);
\r
1674 var keyPress = function (ev) {
\r
1675 if (!ev.keyCode) {
\r
1677 ev.keyCode = ev.which;
\r
1678 } else if (ev.charCode) {
\r
1679 ev.keyCode = ev.charCode;
\r
1683 if (ev.keyCode === 27) {
\r
1684 scope.isOpen = false;
\r
1685 toggleCalendar(scope.isOpen);
\r
1686 ev.preventDefault();
\r
1687 ev.stopPropagation();
\r
1688 } else if (ev.keyCode === 33) {
\r
1689 !scope.disablePrev && scope.move(-1);
\r
1690 $timeout(function () {
\r
1691 scope.getFocus = true;
\r
1693 $timeout(function () {
\r
1694 scope.getFocus = false;
\r
1698 ev.preventDefault();
\r
1699 ev.stopPropagation();
\r
1700 } else if (ev.keyCode === 34) {
\r
1701 !scope.disableNext && scope.move(1);
\r
1702 $timeout(function () {
\r
1703 scope.getFocus = true;
\r
1705 $timeout(function () {
\r
1706 scope.getFocus = false;
\r
1710 ev.preventDefault();
\r
1711 ev.stopPropagation();
\r
1717 $documentBind.click('isOpen', outsideClick, scope);
\r
1719 var modalContainer = angular.element(document.querySelector('.modalwrapper'));
\r
1720 var modalBodyContainer = angular.element(document.querySelector('.modal-body'));
\r
1721 if (modalContainer) {
\r
1722 modalContainer.bind('scroll', function () {
\r
1723 if (b2bDatepickerPopupTemplate) {
\r
1724 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
\r
1729 if (modalBodyContainer) {
\r
1730 modalBodyContainer.bind('scroll', function () {
\r
1731 if (b2bDatepickerPopupTemplate) {
\r
1732 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
\r
1733 var datepickerTextfield = $position.offset(element.find('input'));
\r
1734 var modalBodyPosition = $position.offset(modalBodyContainer);
\r
1736 if (((datepickerTextfield.top + datepickerTextfield.height) < modalBodyPosition.top || datepickerTextfield.top > (modalBodyPosition.top + modalBodyPosition.height)) && scope.isOpen) {
\r
1737 scope.isOpen = false;
\r
1738 toggleCalendar(scope.isOpen);
\r
1744 var window = angular.element($window);
\r
1745 window.bind('resize', function () {
\r
1746 if (b2bDatepickerPopupTemplate) {
\r
1747 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
\r
1752 scope.$on('$destroy', function () {
\r
1753 if (scope.isOpen) {
\r
1754 scope.isOpen = false;
\r
1755 toggleCalendar(scope.isOpen);
\r
1759 scope.resetTime = function (date) {
\r
1760 if (typeof date === 'string') {
\r
1761 date = date + 'T12:00:00';
\r
1764 if (!isNaN(new Date(date))) {
\r
1765 dt = new Date(date);
\r
1769 return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
\r
1773 scope.$parent.$watch($parse(attrs.min), function (value) {
\r
1774 scope.minDate = value ? scope.resetTime(value) : null;
\r
1779 scope.$parent.$watch($parse(attrs.max), function (value) {
\r
1780 scope.maxDate = value ? scope.resetTime(value) : null;
\r
1785 scope.$parent.$watch($parse(attrs.due), function (value) {
\r
1786 scope.dueDate = value ? scope.resetTime(value) : null;
\r
1791 scope.$parent.$watch($parse(attrs.from), function (value) {
\r
1792 scope.fromDate = value ? scope.resetTime(value) : null;
\r
1797 if (attrs.legendIcon) {
\r
1798 scope.$parent.$watch(attrs.legendIcon, function (value) {
\r
1799 scope.legendIcon = value ? value : null;
\r
1803 if (attrs.legendMessage) {
\r
1804 scope.$parent.$watch(attrs.legendMessage, function (value) {
\r
1805 scope.legendMessage = value ? value : null;
\r
1809 if (attrs.ngDisabled) {
\r
1810 scope.$parent.$watch(attrs.ngDisabled, function (value) {
\r
1811 scope.ngDisabled = value ? value : null;
\r
1815 // Split array into smaller arrays
\r
1816 function split(arr, size) {
\r
1818 while (arr.length > 0) {
\r
1819 arrays.push(arr.splice(0, size));
\r
1824 function refill(date) {
\r
1825 if (angular.isDate(date) && !isNaN(date)) {
\r
1826 selected = new Date(date);
\r
1829 selected = new Date();
\r
1834 var currentMode = datepickerCtrl.modes[mode],
\r
1835 data = currentMode.getVisibleDates(selected);
\r
1836 scope.rows = split(data.objects, currentMode.split);
\r
1838 var startFlag = false;
\r
1839 var firstSelected = false;
\r
1840 for (var i = 0; i < scope.rows.length; i++) {
\r
1841 for (var j = 0; j < scope.rows[i].length; j++) {
\r
1843 if (scope.rows[i][j].label === "1" && !firstSelected) {
\r
1844 firstSelected = true;
\r
1845 var firstDay = scope.rows[i][j];
\r
1848 if (scope.rows[i][j].selected === true) {
\r
1858 firstDay.firstFocus = true;
\r
1861 scope.labels = data.labels || [];
\r
1862 scope.title = data.title;
\r
1864 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
\r
1868 scope.select = function (date) {
\r
1869 var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate());
\r
1870 if (!scope.onSelectClose || (scope.onSelectClose && scope.onSelectClose({
\r
1873 scope.currentDate = dt;
\r
1874 if (angular.isNumber(scope.collapseWait)) {
\r
1875 $timeout(function () {
\r
1876 scope.isOpen = false;
\r
1877 toggleCalendar(scope.isOpen);
\r
1878 }, scope.collapseWait);
\r
1880 scope.isOpen = false;
\r
1881 toggleCalendar(scope.isOpen);
\r
1886 scope.move = function (direction,$event) {
\r
1887 var step = datepickerCtrl.modes[mode].step;
\r
1888 selected.setDate(1);
\r
1889 selected.setMonth(selected.getMonth() + direction * (step.months || 0));
\r
1890 selected.setFullYear(selected.getFullYear() + direction * (step.years || 0));
\r
1893 $timeout(function () {
\r
1894 trapFocusInElement();
\r
1897 $event.preventDefault();
\r
1898 $event.stopPropagation();
\r
1901 scope.trapFocus = function () {
\r
1902 $timeout(function () {
\r
1903 trapFocusInElement();
\r
1907 scope.$watch('currentDate', function (value) {
\r
1908 if (angular.isDefined(value) && value !== null) {
\r
1913 ngModel.$setViewValue(value);
\r
1916 ngModel.$render = function () {
\r
1917 scope.currentDate = ngModel.$viewValue;
\r
1920 var stringToDate = function (value) {
\r
1921 if (!isNaN(new Date(value))) {
\r
1922 value = new Date(value);
\r
1926 ngModel.$formatters.unshift(stringToDate);
\r
1931 .directive('b2bDatepicker', ['$compile', '$log', 'b2bDatepickerConfig', 'b2bDatepickerService', function ($compile, $log, b2bDatepickerConfig, b2bDatepickerService) {
\r
1935 disableDates: '&',
\r
1936 onSelectClose: '&'
\r
1938 require: 'ngModel',
\r
1939 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
\r
1940 var dateFormatString = angular.isDefined(attr.dateFormat) ? scope.$parent.$eval(attr.dateFormat) : b2bDatepickerConfig.dateFormat;
\r
1941 var helperText = angular.isDefined(attr.helperText) ? scope.$parent.$eval(attr.helperText) : b2bDatepickerConfig.helperText;
\r
1942 helperText = helperText.replace('$date', '{{dt | date : \'' + dateFormatString + '\'}}');
\r
1944 var inline = false;
\r
1945 if (elem.prop('nodeName') !== 'INPUT') {
\r
1949 var calendarIcon = '<i class="icon-primary-calendar" aria-hidden="true"></i>'
\r
1950 var selectedDateMessage = '<button id="' + attr.btnId + '" type="button" class="span12 faux-input" ng-disabled="ngDisabled"><span class="hidden-spoken">' + helperText + '</span></button>';
\r
1952 elem.removeAttr('b2b-datepicker');
\r
1953 elem.removeAttr('ng-model');
\r
1954 elem.removeAttr('ng-disabled');
\r
1955 elem.addClass('datepicker-input');
\r
1956 elem.attr('ng-model', 'dt');
\r
1957 elem.attr('aria-describedby', 'datepicker');
\r
1958 elem.attr('aria-hidden', 'true');
\r
1959 elem.attr('tabindex', '-1');
\r
1960 elem.attr('readonly', 'true');
\r
1961 elem.attr('ng-disabled', 'ngDisabled');
\r
1962 elem.attr('b2b-format-date', dateFormatString);
\r
1964 var wrapperElement = angular.element('<div></div>');
\r
1965 wrapperElement.attr('b2b-datepicker-popup', '');
\r
1966 wrapperElement.attr('ng-model', 'dt');
\r
1968 wrapperElement.attr('inline', inline);
\r
1971 b2bDatepickerService.setAttributes(attr, wrapperElement);
\r
1972 b2bDatepickerService.bindScope(attr, scope);
\r
1974 wrapperElement.html('');
\r
1975 wrapperElement.append(calendarIcon);
\r
1976 wrapperElement.append(selectedDateMessage);
\r
1977 wrapperElement.append(elem.prop('outerHTML'));
\r
1979 var elm = wrapperElement.prop('outerHTML');
\r
1980 elm = $compile(elm)(scope);
\r
1981 elem.replaceWith(elm);
\r
1983 link: function (scope, elem, attr, ctrl) {
\r
1985 $log.error("ng-model is required.");
\r
1986 return; // do nothing if no ng-model
\r
1989 scope.$watch('dt', function (value) {
\r
1990 ctrl.$setViewValue(value);
\r
1992 ctrl.$render = function () {
\r
1993 scope.dt = ctrl.$viewValue;
\r
1999 .directive('b2bDatepickerGroup', [function () {
\r
2002 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
\r
2003 this.$$headers = [];
\r
2004 this.$$footers = [];
\r
2005 this.registerDatepickerScope = function (datepickerScope) {
\r
2006 datepickerScope.headers = this.$$headers;
\r
2007 datepickerScope.footers = this.$$footers;
\r
2010 link: function (scope, elem, attr, ctrl) {}
\r
2014 .directive('b2bFormatDate', ['dateFilter', function (dateFilter) {
\r
2017 require: 'ngModel',
\r
2018 link: function (scope, elem, attr, ctrl) {
\r
2019 var b2bFormatDate = "";
\r
2020 attr.$observe('b2bFormatDate', function (value) {
\r
2021 b2bFormatDate = value;
\r
2023 var dateToString = function (value) {
\r
2024 if (!isNaN(new Date(value))) {
\r
2025 return dateFilter(new Date(value), b2bFormatDate);
\r
2029 ctrl.$formatters.unshift(dateToString);
\r
2034 .directive('b2bDatepickerHeader', [function () {
\r
2037 require: '^b2bDatepickerGroup',
\r
2041 compile: function (elem, attr, transclude) {
\r
2042 return function link(scope, elem, attr, ctrl) {
\r
2044 ctrl.$$headers.push(transclude(scope, function () {}));
\r
2052 .directive('b2bDatepickerFooter', [function () {
\r
2055 require: '^b2bDatepickerGroup',
\r
2059 compile: function (elem, attr, transclude) {
\r
2060 return function link(scope, elem, attr, ctrl) {
\r
2062 ctrl.$$footers.push(transclude(scope, function () {}));
\r
2070 * @ngdoc directive
\r
2071 * @name Forms.att:checkboxes
\r
2074 * <file src="src/checkboxes/docs/readme.md" />
\r
2076 * See demo section
\r
2078 <example module="b2b.att">
\r
2079 <file src="src/checkboxes/docs/demo.html" />
\r
2080 <file src="src/checkboxes/docs/demo.js" />
\r
2083 angular.module('b2b.att.checkboxes', ['b2b.att.utilities'])
\r
2084 .directive('b2bSelectGroup', [function (){
\r
2087 require: 'ngModel',
\r
2091 link: function (scope, elem, attr, ctrl) {
\r
2092 elem.bind('change', function () {
\r
2093 var isChecked = elem.prop('checked');
\r
2094 angular.forEach(scope.checkboxes, function (item) {
\r
2095 item.isSelected = isChecked;
\r
2099 scope.$watch('checkboxes', function () {
\r
2101 if(scope.checkboxes === undefined) {
\r
2104 angular.forEach(scope.checkboxes, function (item) {
\r
2105 if (item.isSelected) {
\r
2109 elem.prop('indeterminate', false);
\r
2110 if ( scope.checkboxes !==undefined && setBoxes === scope.checkboxes.length && scope.checkboxes.length > 0) {
\r
2111 ctrl.$setViewValue(true);
\r
2112 elem.removeClass('indeterminate');
\r
2113 } else if (setBoxes === 0) {
\r
2114 ctrl.$setViewValue(false);
\r
2115 elem.removeClass('indeterminate');
\r
2117 ctrl.$setViewValue(false);
\r
2118 elem.addClass('indeterminate');
\r
2119 elem.prop('indeterminate', true);
\r
2127 * @ngdoc directive
\r
2128 * @name Misc.att:coachmark
\r
2131 * <file src="src/coachmark/docs/readme.md" />
\r
2135 <button b2b-coachmark start-coachmark-callback="startCoachmark()" end-coachmark-callback="endCoachmark()" action-coachmark-callback="actionCoachmark(action)" coachmark-index="coachmarkIndex" coachmarks="coachmarkElements" id="coachmark0" class="btn btn-alt">Initiate tour</button>
\r
2138 <section id="code">
\r
2139 <b>HTML + AngularJS</b>
\r
2140 <example module="b2b.att">
\r
2141 <file src="src/coachmark/docs/demo.html" />
\r
2142 <file src="src/coachmark/docs/demo.js" />
\r
2147 angular.module('b2b.att.coachmark', ['b2b.att.utilities','b2b.att.position'])
\r
2149 .directive('b2bCoachmark', ['$document', '$compile', '$position', '$timeout', function($document, $compile, $position, $timeout) {
\r
2154 coachmarkIndex: '=',
\r
2155 startCoachmarkCallback: '&',
\r
2156 endCoachmarkCallback: '&',
\r
2157 actionCoachmarkCallback: '&'
\r
2159 link: function (scope, element, attrs, ctrl) {
\r
2160 var coachmarkItems = scope.coachmarks;
\r
2161 var body = $document.find('body').eq(0);
\r
2162 var coackmarkJqContainer;
\r
2163 var coackmarkContainer;
\r
2164 var coachMarkElement;
\r
2165 var backdropjqLiteEl;
\r
2166 var coachmarkHighlight;
\r
2167 var initaitedCoachmark = false;
\r
2168 scope.coackmarkElPos ={
\r
2173 scope.currentCoachmark = {};
\r
2176 var coachmarkBackdrop = function(){
\r
2177 backdropjqLiteEl = angular.element('<div class="b2b-modal-backdrop fade in hidden-by-modal"></div>');
\r
2178 body.append(backdropjqLiteEl);
\r
2180 backdropjqLiteEl.bind('click', function() {
\r
2181 scope.closeCoachmark();
\r
2187 scope.closeButtonFocus = function(){
\r
2188 if(document.getElementsByClassName('b2b-coachmark-header').length >0){
\r
2189 document.getElementsByClassName('b2b-coachmark-header')[0].scrollLeft = 0;
\r
2190 document.getElementsByClassName('b2b-coachmark-header')[0].scrollTop = 0;
\r
2194 scope.actionCoachmark = function(action){
\r
2195 scope.actionCoachmarkCallback({
\r
2200 scope.closeCoachmark = function(){
\r
2201 initaitedCoachmark = false;
\r
2202 backdropjqLiteEl.remove();
\r
2203 coackmarkContainer.remove();
\r
2204 coachmarkHighlight.remove();
\r
2205 if(coachMarkElement !== undefined && coachMarkElement !==""){
\r
2206 coachMarkElement.removeClass('b2b-coachmark-label')
\r
2208 if (angular.isFunction(scope.endCoachmarkCallback)){
\r
2209 scope.endCoachmarkCallback();
\r
2211 element[0].focus();
\r
2214 function showCoachmark(targetElement) {
\r
2215 scope.currentCoachmark = targetElement;
\r
2216 if(coachMarkElement !== undefined && coachMarkElement !==""){
\r
2217 coachMarkElement.removeClass('b2b-coachmark-label')
\r
2218 coackmarkContainer.remove();
\r
2219 coachmarkHighlight.remove();
\r
2221 coachMarkElement = angular.element(document.querySelector(targetElement.elementId));
\r
2222 coachMarkElement.addClass('b2b-coachmark-label');
\r
2223 var elementPosition = $position.offset(coachMarkElement);
\r
2225 coachmarkHighlight = angular.element('<div class="b2b-coachmark-highlight"></div><div class="b2b-coachmark-highlight b2b-coachmark-highlight-mask"></div>');
\r
2226 coachmarkHighlight.css({
\r
2227 'width': (elementPosition.width + 20) +'px',
\r
2228 'top': (elementPosition.top -10) + 'px',
\r
2229 'left': (elementPosition.left - 10) + 'px',
\r
2230 'height': (elementPosition.height + 20) +'px'
\r
2232 body.append(coachmarkHighlight);
\r
2234 scope.coackmarkElPos.top = (elementPosition.top + elementPosition.height + 32) + 'px';
\r
2235 scope.coackmarkElPos.left = (elementPosition.left - 158 + elementPosition.width / 2 ) + 'px';
\r
2236 coackmarkJqContainer = angular.element('<div b2b-coachmark-container b2b-trap-focus-inside-element="true"></div>');
\r
2237 coackmarkContainer = $compile(coackmarkJqContainer)(scope);
\r
2238 body.append(coackmarkContainer);
\r
2240 $timeout(function () {
\r
2241 var currentCoachmarkContainer = document.getElementsByClassName('b2b-coachmark-container')[0];
\r
2242 currentCoachmarkContainer.focus();
\r
2243 var coachmarkHeight = window.getComputedStyle(currentCoachmarkContainer).height.split('px')[0];
\r
2244 var newOffsetHeight = (Math.round(elementPosition.top) - Math.round(coachmarkHeight));
\r
2246 // We need a slight offset to show the lightboxed item
\r
2247 TweenLite.to(window, 2, {scrollTo:{x: (scope.coackmarkElPos.left.split('px')[0]-100), y: newOffsetHeight}});
\r
2251 element.bind('click', function (e) {
\r
2252 initaitedCoachmark = true;
\r
2253 if(scope.coachmarkIndex === -1 || scope.coachmarkIndex >= coachmarkItems.length ){
\r
2254 scope.coachmarkIndex = 0;
\r
2256 scope.$watch('coachmarkIndex', function () {
\r
2257 if(initaitedCoachmark === true){
\r
2258 if(scope.coachmarkIndex === -1 || scope.coachmarkIndex >= coachmarkItems.length ){
\r
2259 scope.closeCoachmark();
\r
2261 showCoachmark(coachmarkItems[scope.coachmarkIndex]);
\r
2265 coachmarkBackdrop();
\r
2266 showCoachmark(coachmarkItems[scope.coachmarkIndex]);
\r
2267 if (angular.isFunction(scope.startCoachmarkCallback)){
\r
2268 scope.startCoachmarkCallback();
\r
2270 $document.bind('keydown', function (evt) {
\r
2271 if (evt.which === 27 && initaitedCoachmark) {
\r
2272 scope.closeCoachmark();
\r
2281 .directive('b2bCoachmarkContainer', ['$document', '$position', function($document, $position) {
\r
2286 templateUrl: 'b2bTemplate/coachmark/coachmark.html',
\r
2287 link: function (scope, element, attrs, ctrl) {
\r
2294 * @ngdoc directive
\r
2295 * @name Template.att:Configuration Section
\r
2298 * <file src="src/configurationSection/docs/readme.md" />
\r
2301 * <section id="code">
\r
2302 <b>HTML + AngularJS</b>
\r
2303 <example module="b2b.att">
\r
2304 <file src="src/configurationSection/docs/demo.html" />
\r
2305 <file src="src/configurationSection/docs/demo.js" />
\r
2310 angular.module('b2b.att.configurationSection', [])
\r
2313 * @ngdoc directive
\r
2314 * @name Template.att:Directory Listing
\r
2317 * <file src="src/directoryListingTemplate/docs/readme.md" />
\r
2320 * <section id="code">
\r
2321 <b>HTML + AngularJS</b>
\r
2322 <example module="b2b.att">
\r
2323 <file src="src/directoryListingTemplate/docs/demo.html" />
\r
2324 <file src="src/directoryListingTemplate/docs/demo.js" />
\r
2329 angular.module('b2b.att.directoryListingTemplate', [])
\r
2332 * @ngdoc directive
\r
2333 * @name Forms.att:dropdowns
\r
2336 * <file src="src/dropdowns/docs/readme.md" />
\r
2340 <section id="code">
\r
2341 <example module="b2b.att">
\r
2342 <file src="src/dropdowns/docs/demo.html" />
\r
2343 <file src="src/dropdowns/docs/demo.js" />
\r
2347 angular.module('b2b.att.dropdowns', ['b2b.att.utilities', 'b2b.att.position', 'ngSanitize'])
\r
2349 .constant('b2bDropdownConfig', {
\r
2352 menuKeyword: 'menu',
\r
2353 linkMenuKeyword: 'link-menu',
\r
2354 largeKeyword: 'large',
\r
2355 smallKeyword: 'small'
\r
2358 .directive("b2bDropdown", ['$timeout', '$compile', '$templateCache', 'b2bUserAgent', 'b2bDropdownConfig', '$position', function ($timeout, $compile, $templateCache, b2bUserAgent, b2bDropdownConfig, $position) {
\r
2362 require: 'ngModel',
\r
2363 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
\r
2364 scope.isInputDropdown = true;
\r
2365 scope.placeHoldertext = attr.placeholderText;
\r
2367 if (attr.type.indexOf(b2bDropdownConfig.menuKeyword) > -1 || attr.type.indexOf(b2bDropdownConfig.linkMenuKeyword) > -1) {
\r
2368 scope.isInputDropdown = false;
\r
2369 if (attr.type.indexOf(b2bDropdownConfig.linkMenuKeyword) > -1) {
\r
2370 scope.dropdownType = b2bDropdownConfig.linkMenuKeyword;
\r
2371 } else if (attr.type.indexOf(b2bDropdownConfig.menuKeyword) > -1) {
\r
2372 scope.dropdownType = b2bDropdownConfig.menuKeyword;
\r
2375 if (attr.type.indexOf(b2bDropdownConfig.largeKeyword) > -1) {
\r
2376 scope.dropdownSize = b2bDropdownConfig.largeKeyword;
\r
2377 } else if (attr.type.indexOf(b2bDropdownConfig.smallKeyword) > -1) {
\r
2378 scope.dropdownSize = b2bDropdownConfig.smallKeyword;
\r
2382 scope.labelText = attr.labelText;
\r
2384 scope.setBlur = function () {
\r
2385 scope.setTouched();
\r
2388 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
\r
2389 var formCtrl = elem.controller('form');
\r
2390 scope.setNgModelController = function (name, ngModelCtrl) {
\r
2391 if (name && formCtrl && ngModelCtrl) {
\r
2392 formCtrl[name] = ngModelCtrl;
\r
2395 scope.setOptionalCta = function (optionalCta) {
\r
2396 scope.optionalCta = optionalCta;
\r
2398 var innerHtml = angular.element('<div></div>').append(elem.html());
\r
2399 innerHtml = ($compile(innerHtml)(scope)).html();
\r
2400 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownDesktop.html'));
\r
2401 template.find('ul').eq(0).append(innerHtml);
\r
2402 template = $compile(template)(scope);
\r
2403 elem.replaceWith(template);
\r
2404 } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
\r
2407 'filter': 'alpha(opacity=0)'
\r
2409 elem.addClass('awd-select isWrapped');
\r
2410 elem.wrap('<span class="selectWrap"></span>');
\r
2411 var cover = angular.element('<span aria-hidden="true"><i class="icon-primary-down" aria-hidden="true"></i></span>');
\r
2412 elem.parent().append(cover);
\r
2413 elem.parent().append('<i class="icon-primary-down" aria-hidden="true"></i>');
\r
2414 var set = function () {
\r
2415 var sel = elem[0] ? elem[0] : elem;
\r
2416 var selectedText = "";
\r
2417 var selIndex = sel.selectedIndex;
\r
2418 if (typeof selIndex !== 'undefined') {
\r
2419 selectedText = sel.options[selIndex].text;
\r
2421 cover.text(selectedText).append('<i class="icon-primary-down" aria-hidden="true"></i>');
\r
2423 var update = function (value) {
\r
2424 $timeout(set, 100);
\r
2427 if (attr.ngModel) {
\r
2428 scope.$watch(attr.ngModel, function (newVal, oldVal) {
\r
2432 elem.bind('keyup', function (ev) {
\r
2433 if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
\r
2440 link: function (scope, elem, attr, ctrl) {
\r
2441 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
\r
2442 scope.updateModel = function () {
\r
2443 if (ctrl.$dirty) {
\r
2446 ctrl.$setViewValue(scope.currentSelected.value);
\r
2447 if (scope.dropdownRequired && scope.currentSelected.value === '') {
\r
2448 scope.setRequired(false);
\r
2450 scope.setRequired(true);
\r
2453 if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
\r
2454 $timeout(function () {
\r
2455 scope.appendCaretPositionStyle();
\r
2459 ctrl.$render = function () {
\r
2460 // if(ctrl.$dirty || ctrl.$pristine) {
\r
2461 $timeout(function () {
\r
2463 if ((angular.isUndefined(ctrl.$viewValue) || ctrl.$viewValue == '') && (angular.isUndefined(scope.placeHoldertext) || scope.placeHoldertext == '')) {
\r
2464 scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
\r
2465 } else if ((angular.isUndefined(scope.placeHoldertext) || scope.placeHoldertext == '') && ctrl.$viewValue !== '' ) {
\r
2466 scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
\r
2467 } else if ((angular.isUndefined(ctrl.$viewValue) || ctrl.$viewValue == '') && scope.placeHoldertext !== '' ) {
\r
2468 scope.currentSelected.text = scope.placeHoldertext;
\r
2470 scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
\r
2476 scope.disabled = false;
\r
2477 scope.dropdownName = attr.name;
\r
2478 scope.dropdownId = attr.id;
\r
2479 scope.labelId = attr.ariaLabelledby;
\r
2480 scope.dropdownDescribedBy = attr.ariaDescribedby;
\r
2481 if (attr.required) {
\r
2482 scope.dropdownRequired = true;
\r
2484 scope.dropdownRequired = false;
\r
2486 elem.removeAttr('name');
\r
2487 elem.removeAttr('id');
\r
2488 scope.$parent.$watch(attr.ngDisabled, function (val) {
\r
2489 scope.disabled = val;
\r
2496 .directive("b2bDropdownToggle", ['$document', '$documentBind', '$isElement', 'b2bDropdownConfig', 'keymap', 'b2bUtilitiesConfig', '$timeout', '$position', function ($document, $documentBind, $isElement, b2bDropdownConfig, keymap, b2bUtilitiesConfig, $timeout, $position) {
\r
2499 require: '?^b2bKey',
\r
2500 link: function (scope, elem, attr, ctrl) {
\r
2501 scope.appendCaretPositionStyle = function () {
\r
2502 while (document.querySelector('style.b2bDropdownCaret')) {
\r
2503 document.querySelector('style.b2bDropdownCaret').remove();
\r
2505 var caretPosition = $position.position(elem).width - 26;
\r
2506 if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
\r
2507 var template = angular.element('<style class="b2bDropdownCaret" type="text/css">.linkSelectorModule .active+.moduleWrapper:before {left: ' + caretPosition + 'px;}</style>');
\r
2508 $document.find('head').append(template);
\r
2512 if (scope.isInputDropdown && (scope.labelText !== undefined)) {
\r
2513 elem.attr('aria-label', scope.labelText);
\r
2516 scope.toggleFlag = false;
\r
2517 scope.dropdownLists = {};
\r
2518 scope.dropdownListValues = [];
\r
2519 scope.dropdown = {
\r
2522 scope.currentSelected = {
\r
2528 var searchString = '';
\r
2529 var searchElement = function (searchExp) {
\r
2530 var regex = new RegExp("\\b" + searchExp, "gi");
\r
2531 var position = scope.dropdownListValues.regexIndexOf(regex, scope.currentSelected.index + 1, true);
\r
2532 if (position > -1) {
\r
2537 var startTimer = function (time) {
\r
2538 if (searchString === '') {
\r
2539 $timeout(function () {
\r
2540 searchString = '';
\r
2544 scope.toggleDropdown = function (toggleFlag) {
\r
2545 if (!scope.disabled) {
\r
2546 if (angular.isDefined(toggleFlag)) {
\r
2547 scope.toggleFlag = toggleFlag;
\r
2549 scope.toggleFlag = !scope.toggleFlag;
\r
2551 if (!scope.toggleFlag) {
\r
2552 if (scope.isInputDropdown) {
\r
2553 elem.parent().find('input')[0].focus();
\r
2555 elem.parent().find('button')[0].focus();
\r
2558 scope.dropdown.highlightedValue = scope.currentSelected.value;
\r
2559 if (ctrl && ctrl.enableSearch) {
\r
2560 if (angular.isDefined(scope.dropdownLists[scope.currentSelected.value])) {
\r
2561 ctrl.resetCounter(scope.dropdownLists[scope.currentSelected.value][2]);
\r
2564 if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
\r
2565 scope.appendCaretPositionStyle();
\r
2571 elem.bind('keydown', function (ev) {
\r
2572 if (!ev.keyCode) {
\r
2574 ev.keyCode = ev.which;
\r
2575 } else if (ev.charCode) {
\r
2576 ev.keyCode = ev.charCode;
\r
2579 if (!scope.toggleFlag) {
\r
2581 var currentIndex = scope.currentSelected.index;
\r
2582 if (ev.altKey === true && ev.keyCode === keymap.KEY.DOWN) {
\r
2583 scope.toggleDropdown(true);
\r
2584 ev.preventDefault();
\r
2585 ev.stopPropagation();
\r
2586 } else if (b2bDropdownConfig.prev.split(',').indexOf(ev.keyCode.toString()) > -1) {
\r
2587 angular.isDefined(scope.dropdownListValues[currentIndex - 1]) ? scope.dropdownLists[scope.dropdownListValues[currentIndex - 1]][0].updateDropdownValue() : angular.noop();
\r
2588 ev.preventDefault();
\r
2589 ev.stopPropagation();
\r
2590 } else if (b2bDropdownConfig.next.split(',').indexOf(ev.keyCode.toString()) > -1) {
\r
2591 angular.isDefined(scope.dropdownListValues[currentIndex + 1]) ? scope.dropdownLists[scope.dropdownListValues[currentIndex + 1]][0].updateDropdownValue() : angular.noop();
\r
2592 ev.preventDefault();
\r
2593 ev.stopPropagation();
\r
2594 } else if (ev.keyCode >= 48 && ev.keyCode <= 105) {
\r
2595 startTimer(b2bUtilitiesConfig.searchTimer);
\r
2596 searchString = searchString + (keymap.MAP[ev.keyCode] || '');
\r
2597 var position = searchElement(searchString);
\r
2598 angular.isDefined(scope.dropdownListValues[position]) ? scope.dropdownLists[scope.dropdownListValues[position]][0].updateDropdownValue() : angular.noop();
\r
2599 ev.preventDefault();
\r
2600 ev.stopPropagation();
\r
2604 if (ev.altKey === true && ev.keyCode === keymap.KEY.UP) {
\r
2605 scope.toggleDropdown(false);
\r
2606 ev.preventDefault();
\r
2607 ev.stopPropagation();
\r
2608 } else if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
\r
2609 scope.toggleDropdown(false);
\r
2610 ev.preventDefault();
\r
2611 ev.stopPropagation();
\r
2614 scope.$apply(); // TODO: Move this into each block to avoid expensive digest cycles
\r
2616 var outsideClick = function (e) {
\r
2617 var isElement = $isElement(angular.element(e.target), elem.parent(), $document);
\r
2619 scope.toggleDropdown(false);
\r
2623 $documentBind.click('toggleFlag', outsideClick, scope);
\r
2628 .directive("b2bDropdownGroup", ['$compile', '$templateCache', 'b2bUserAgent', function ($compile, $templateCache, b2bUserAgent) {
\r
2631 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
\r
2632 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
\r
2633 var innerHtml = angular.element('<div></div>').append(elem.html());
\r
2634 innerHtml = ($compile(innerHtml)(scope)).html();
\r
2635 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html'));
\r
2636 template.attr('ng-repeat', attr.optGroupRepeat);
\r
2637 template.attr('label', elem.attr('label'));
\r
2638 template.find('ul').append(innerHtml);
\r
2639 elem.replaceWith(template);
\r
2640 } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
\r
2641 var template = angular.element(elem.prop('outerHTML'));
\r
2642 template.attr('ng-repeat', attr.optGroupRepeat);
\r
2643 template.removeAttr('b2b-dropdown-group');
\r
2644 template.removeAttr('opt-group-repeat');
\r
2645 template = $compile(template)(scope);
\r
2646 elem.replaceWith(template);
\r
2652 .directive("b2bDropdownGroupDesktop", [function () {
\r
2656 link: function (scope, elem, attr, ctrl) {
\r
2657 scope.groupHeader = attr.label;
\r
2662 .directive("b2bDropdownList", ['$compile', '$templateCache', 'b2bUserAgent', function ($compile, $templateCache, b2bUserAgent) {
\r
2665 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
\r
2666 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
\r
2667 var innerHtml = angular.element('<div></div>').append(elem.html());
\r
2668 innerHtml = ($compile(innerHtml)(scope)).html();
\r
2669 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownListDesktop.html'));
\r
2670 template.attr('ng-repeat', attr.optionRepeat);
\r
2671 template.attr('value', elem.attr('value'));
\r
2672 template.attr('search-key', elem.attr('value'));
\r
2673 if (elem.attr('aria-describedby')){
\r
2674 template.attr('aria-describedby', attr.ariaDescribedby);
\r
2676 if (elem.attr('imgsrc')) {
\r
2677 if (elem.attr('imgalt')) {
\r
2678 template.append('<img role="presentation" ng-src="' + elem.attr('imgsrc') + '" alt="' + elem.attr('imgalt') + '"/>');
\r
2680 template.append('<img role="presentation" ng-src="' + elem.attr('imgsrc') + '" alt=""/>');
\r
2683 template.append(innerHtml);
\r
2684 elem.replaceWith(template);
\r
2685 } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
\r
2686 var template = angular.element(elem.prop('outerHTML'));
\r
2687 template.attr('ng-repeat', attr.optionRepeat);
\r
2688 if (elem.attr('aria-describedby')){
\r
2689 template.attr('aria-describedby', attr.ariaDescribedby);
\r
2691 template.removeAttr('b2b-dropdown-list');
\r
2692 template.removeAttr('option-repeat');
\r
2693 template = $compile(template)(scope);
\r
2694 elem.replaceWith(template);
\r
2700 .directive("b2bDropdownListDesktop", ['$sce', 'keymap', 'b2bDropdownConfig', function ($sce, keymap, b2bDropdownConfig) {
\r
2704 link: function (scope, elem, attr, ctrl) {
\r
2705 var dropdownListValue = scope.dropdownListValue = attr.value;
\r
2706 scope.dropdown.totalIndex++;
\r
2707 var dropdownListIndex = scope.dropdown.totalIndex;
\r
2708 scope.dropdownListValues.push(dropdownListValue);
\r
2709 scope.dropdownLists[dropdownListValue] = [];
\r
2710 scope.dropdownLists[dropdownListValue][0] = scope;
\r
2711 scope.dropdownLists[dropdownListValue][1] = elem;
\r
2712 scope.dropdownLists[dropdownListValue][2] = dropdownListIndex;
\r
2713 scope.updateDropdownValue = function () {
\r
2714 scope.currentSelected.value = dropdownListValue;
\r
2715 if (scope.isInputDropdown) {
\r
2716 scope.currentSelected.text = elem.text();
\r
2717 } else if ((scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) || (scope.dropdownType === b2bDropdownConfig.menuKeyword && scope.dropdownSize === b2bDropdownConfig.smallKeyword)) {
\r
2718 scope.currentSelected.text = dropdownListValue;
\r
2719 } else if (scope.dropdownType === b2bDropdownConfig.menuKeyword) {
\r
2720 scope.currentSelected.text = $sce.trustAsHtml(elem.html());
\r
2722 if (scope.isInputDropdown) {
\r
2723 scope.currentSelected.label = elem.text();
\r
2724 } else if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
\r
2725 scope.currentSelected.label = dropdownListValue;
\r
2726 } else if (scope.dropdownType === b2bDropdownConfig.menuKeyword) {
\r
2727 scope.currentSelected.label = elem.text();
\r
2729 scope.currentSelected.index = dropdownListIndex;
\r
2730 scope.updateModel();
\r
2732 scope.selectDropdownItem = function () {
\r
2734 scope.updateDropdownValue();
\r
2735 scope.toggleDropdown(false);
\r
2737 scope.highlightDropdown = function () {
\r
2738 scope.dropdown.highlightedValue = dropdownListValue;
\r
2740 elem.bind('mouseover', function (ev) {
\r
2743 elem.bind('keydown', function (ev) {
\r
2744 if (!ev.keyCode) {
\r
2746 ev.keyCode = ev.which;
\r
2747 } else if (ev.charCode) {
\r
2748 ev.keyCode = ev.charCode;
\r
2751 if (ev.altKey === true && ev.keyCode === keymap.KEY.UP) {
\r
2752 scope.toggleDropdown(false);
\r
2753 ev.preventDefault();
\r
2754 ev.stopPropagation();
\r
2755 } else if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
\r
2756 scope.toggleDropdown(false);
\r
2757 ev.preventDefault();
\r
2758 ev.stopPropagation();
\r
2766 .directive("b2bDropdownRepeat", ['$compile', 'b2bUserAgent', function ($compile, b2bUserAgent) {
\r
2769 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
\r
2770 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
\r
2771 var innerHtml = angular.element('<div></div>').append(elem.html());
\r
2772 innerHtml = ($compile(innerHtml)(scope)).html();
\r
2773 var template = angular.element('<div></div>');
\r
2774 template.attr('ng-repeat', attr.b2bDropdownRepeat);
\r
2775 template.append(innerHtml);
\r
2776 elem.replaceWith(template);
\r
2777 } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
\r
2784 .directive("b2bDropdownValidation", ['$timeout', function ($timeout) {
\r
2787 require: 'ngModel',
\r
2788 link: function (scope, elem, attr, ctrl) {
\r
2789 $timeout(function () {
\r
2790 scope.setNgModelController(attr.name, ctrl);
\r
2792 scope.setDirty = function () {
\r
2793 if (ctrl.$dirty === false) {
\r
2794 ctrl.$dirty = true;
\r
2795 ctrl.$pristine = false;
\r
2798 scope.setTouched = function () {
\r
2799 if (ctrl.$touched === false) {
\r
2800 ctrl.$touched = true;
\r
2801 ctrl.$pristine = false;
\r
2804 scope.setRequired = function (flag) {
\r
2805 ctrl.$setValidity('required', flag);
\r
2811 .directive('b2bDropdownOptionalCta', [function () {
\r
2817 compile: function (elem, attr, transclude) {
\r
2818 return function link(scope, elem, attr, ctrl) {
\r
2819 if (scope.setOptionalCta) {
\r
2820 scope.setOptionalCta(transclude(scope, function () {}));
\r
2828 * @ngdoc directive
\r
2829 * @name Forms.att:File Upload
\r
2832 * <file src="src/fileUpload/docs/readme.md" />
\r
2836 <form id="dragDropFile">
\r
2837 <div b2b-file-drop file-model="fileModel" on-drop="triggerFileUpload()" align="center">
\r
2839 <br>To upload a file, drag & drop it here or
\r
2840 <span b2b-file-link file-model="fileModel" on-file-select="triggerFileUpload()" >
\r
2841 click here to select from your computer.
\r
2848 * <section id="code">
\r
2849 <example module="b2b.att">
\r
2850 <file src="src/fileUpload/docs/demo.html" />
\r
2851 <file src="src/fileUpload/docs/demo.js" />
\r
2856 angular.module('b2b.att.fileUpload', ['b2b.att.utilities'])
\r
2857 .directive('b2bFileDrop', [function() {
\r
2864 controller: ['$scope', '$attrs', function($scope, $attrs) {
\r
2865 this.onDrop = $scope.onDrop;
\r
2867 link: function(scope, element) {
\r
2868 element.addClass('b2b-dragdrop');
\r
2872 if (e.originalEvent) {
\r
2873 e.dataTransfer = e.originalEvent.dataTransfer;
\r
2875 e.dataTransfer.dropEffect = 'move';
\r
2876 // allows us to drop
\r
2877 if (e.preventDefault) {
\r
2878 e.preventDefault();
\r
2880 element.addClass('b2b-dragdrop-over');
\r
2887 // allows us to drop
\r
2888 if (e.preventDefault) {
\r
2889 e.preventDefault();
\r
2891 element.addClass('b2b-dragdrop-over');
\r
2898 element.removeClass('b2b-dragdrop-over');
\r
2905 // Stops some browsers from redirecting.
\r
2906 if (e.preventDefault) {
\r
2907 e.preventDefault();
\r
2909 if (e.stopPropagation) {
\r
2910 e.stopPropagation();
\r
2912 if (e.originalEvent) {
\r
2913 e.dataTransfer = e.originalEvent.dataTransfer;
\r
2915 element.removeClass('b2b-dragdrop-over');
\r
2916 if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
\r
2917 scope.fileModel = e.dataTransfer.files[0];
\r
2919 if (angular.isFunction(scope.onDrop)) {
\r
2929 .directive('b2bFileLink', [function() {
\r
2932 require: '^?b2bFileDrop',
\r
2935 templateUrl: 'b2bTemplate/fileUpload/fileUpload.html',
\r
2940 controller: ['$scope', function($scope) {
\r
2941 this.setFileModel = function(fileModel) {
\r
2942 if ($scope.takeFileModelFromParent) {
\r
2943 $scope.$parent.fileModel = fileModel;
\r
2944 $scope.$parent.$apply();
\r
2946 $scope.fileModel = fileModel;
\r
2950 this.callbackFunction = function() {
\r
2951 if (angular.isFunction($scope.onFileSelect)) {
\r
2952 $scope.onFileSelect();
\r
2957 link: function(scope, element, attr, b2bFileDropCtrl) {
\r
2958 scope.takeFileModelFromParent = false;
\r
2959 if (!(attr.fileModel) && b2bFileDropCtrl) {
\r
2960 scope.takeFileModelFromParent = true;
\r
2962 if (!(attr.onFileSelect) && b2bFileDropCtrl) {
\r
2963 scope.onFileSelect = b2bFileDropCtrl.onDrop;
\r
2968 .directive('b2bFileChange', ['$log', '$rootScope', function($log, $rootScope) {
\r
2971 require: '^b2bFileLink',
\r
2972 link: function(scope, element, attr, b2bFileLinkCtrl) {
\r
2973 element.bind('change', changeFileModel);
\r
2975 function changeFileModel(e) {
\r
2976 if (e.target.files && e.target.files.length > 0) {
\r
2977 b2bFileLinkCtrl.setFileModel(e.target.files[0]);
\r
2978 b2bFileLinkCtrl.callbackFunction();
\r
2980 var strFileName = e.target.value;
\r
2982 var objFSO = new ActiveXObject("Scripting.FileSystemObject");
\r
2983 b2bFileLinkCtrl.setFileModel(objFSO.getFile(strFileName));
\r
2984 b2bFileLinkCtrl.callbackFunction();
\r
2986 var errMsg = "There was an issue uploading " + strFileName + ". Please try again.";
\r
2987 $log.error(errMsg);
\r
2988 $rootScope.$broadcast('b2b-file-link-failure', errMsg);
\r
2996 * @ngdoc directive
\r
2997 * @name Navigation.att:filters
\r
3000 * <file src="src/filters/docs/readme.md" />
\r
3003 * <div b2b-filters></div>
\r
3006 * <section id="code">
\r
3007 <b>HTML + AngularJS</b>
\r
3008 <example module="b2b.att">
\r
3009 <file src="src/filters/docs/demo.html" />
\r
3010 <file src="src/filters/docs/demo.js" />
\r
3015 angular.module('b2b.att.filters', ['b2b.att.utilities', 'b2b.att.multipurposeExpander'])
\r
3016 .filter('filtersSelectedItemsFilter', [function () {
\r
3017 return function (listOfItemsArray) {
\r
3019 if (!listOfItemsArray) {
\r
3020 listOfItemsArray = [];
\r
3023 var returnArray = [];
\r
3025 for (var i = 0; i < listOfItemsArray.length; i++) {
\r
3026 for (var j = 0; j < listOfItemsArray[i].filterTypeItems.length; j++) {
\r
3027 if (listOfItemsArray[i].filterTypeItems[j].selected && !listOfItemsArray[i].filterTypeItems[j].inProgress) {
\r
3028 returnArray.push(listOfItemsArray[i].filterTypeItems[j]);
\r
3033 return returnArray;
\r
3037 * @ngdoc directive
\r
3038 * @name Messages, modals & alerts.att:flyout
\r
3041 * <file src="src/flyout/docs/readme.md" />
\r
3043 * <section id="code">
\r
3044 <example module="b2b.att">
\r
3045 <file src="src/flyout/docs/demo.html" />
\r
3046 <file src="src/flyout/docs/demo.js" />
\r
3051 angular.module('b2b.att.flyout', ['b2b.att.utilities', 'b2b.att.position'])
\r
3052 .directive('b2bFlyout', ['$timeout', 'b2bDOMHelper', 'keymap', 'events', function ($timeout, b2bDOMHelper, keymap, events) {
\r
3056 templateUrl: 'b2bTemplate/flyout/flyout.html',
\r
3057 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
\r
3058 scope.flyoutOpened = false;
\r
3059 var contentScope = '';
\r
3060 var togglerScope = '';
\r
3061 this.registerContentScope = function (scp) {
\r
3062 contentScope = scp;
\r
3064 this.registerTogglerScope = function (scp) {
\r
3065 togglerScope = scp;
\r
3068 this.toggleFlyoutState = function () {
\r
3069 if (contentScope) {
\r
3070 contentScope.toggleFlyout();
\r
3073 this.getTogglerDimensions = function () {
\r
3074 return togglerScope.getTogglerDimensions();
\r
3076 this.setTogglerFocus = function () {
\r
3077 return togglerScope.setTogglerFocus();
\r
3080 this.closeFlyout = function (e) {
\r
3081 contentScope.closeFromChild(e);
\r
3083 this.gotFocus = function () {
\r
3084 contentScope.gotFocus();
\r
3087 this.updateAriaModel = function (val) {
\r
3088 scope.flyoutOpened = val;
\r
3091 var firstTabableElement = undefined,
\r
3092 lastTabableElement = undefined;
\r
3094 var firstTabableElementKeyhandler = function (e) {
\r
3096 e.keyCode = e.which;
\r
3098 if (e.keyCode === keymap.KEY.TAB && e.shiftKey && scope.flyoutOpened) {
\r
3099 contentScope.gotFocus();
\r
3100 events.preventDefault(e);
\r
3101 events.stopPropagation(e);
\r
3105 var lastTabableElementKeyhandler = function (e) {
\r
3107 e.keyCode = e.which;
\r
3109 if (e.keyCode === keymap.KEY.TAB && !e.shiftKey) {
\r
3110 contentScope.gotFocus();
\r
3111 events.preventDefault(e);
\r
3112 events.stopPropagation(e);
\r
3115 this.associateTabEvent = function(){
\r
3116 $timeout(function () {
\r
3117 var element = elem[0].getElementsByClassName('b2b-flyout-container')[0];
\r
3118 firstTabableElement = b2bDOMHelper.firstTabableElement(element);
\r
3119 lastTabableElement = b2bDOMHelper.lastTabableElement(element);
\r
3120 if(angular.isUndefined(firstTabableElement)){
\r
3121 angular.element(element).css('display','block');
\r
3122 firstTabableElement = b2bDOMHelper.firstTabableElement(element);
\r
3123 lastTabableElement = b2bDOMHelper.lastTabableElement(element);
\r
3124 angular.element(element).css('display','none');
\r
3126 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
\r
3127 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
\r
3130 this.updateTabbableElements = function(){
\r
3131 $timeout(function () {
\r
3132 var element = elem[0].getElementsByClassName('b2b-flyout-container')[0];
\r
3133 angular.element(element).css('display','block');
\r
3134 firstTabableElement = b2bDOMHelper.firstTabableElement(element);
\r
3135 lastTabableElement = b2bDOMHelper.lastTabableElement(element);
\r
3136 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
\r
3137 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
\r
3138 angular.element(element).css('display','none');
\r
3141 this.unbindTabbaleEvents = function(){
\r
3142 if(angular.isDefined(firstTabableElement)){
\r
3143 angular.element(firstTabableElement).unbind('keydown', firstTabableElementKeyhandler);
\r
3146 if(angular.isDefined(lastTabableElement)){
\r
3147 angular.element(lastTabableElement).unbind('keydown', lastTabableElementKeyhandler);
\r
3151 link: function (scope, element, attrs, ctrl) {
\r
3156 .directive('b2bFlyoutToggler', [function () {
\r
3159 require: '^b2bFlyout',
\r
3160 link: function (scope, element, attrs, ctrl) {
\r
3161 element.bind('click', function (e) {
\r
3162 ctrl.toggleFlyoutState();
\r
3165 scope.getTogglerDimensions = function () {
\r
3166 return element[0].getBoundingClientRect();
\r
3169 scope.setTogglerFocus = function () {
\r
3170 element[0].focus();
\r
3173 ctrl.registerTogglerScope(scope);
\r
3177 .directive('b2bFlyoutContent', ['$position', '$timeout', '$documentBind', '$isElement', '$document', function ($position, $timeout, $documentBind, $isElement, $document) {
\r
3182 require: '^b2bFlyout',
\r
3184 horizontalPlacement: '@',
\r
3185 verticalPlacement: '@',
\r
3188 contentUpdated: "=?"
\r
3190 templateUrl: 'b2bTemplate/flyout/flyoutContent.html',
\r
3191 link: function (scope, element, attrs, ctrl) {
\r
3192 var flyoutStyleArray, eachCssProperty, cssPropertyKey, cssPropertyVal, temp;
\r
3193 scope.openFlyout = false;
\r
3194 if (!scope.horizontalPlacement) {
\r
3195 scope.horizontalPlacement = 'center';
\r
3197 if (!scope.verticalPlacement) {
\r
3198 scope.verticalPlacement = 'below';
\r
3201 scope.toggleFlyout = function () {
\r
3203 scope.openFlyout = !scope.openFlyout;
\r
3205 if (scope.openFlyout) {
\r
3207 if (angular.isDefined(scope.flyoutStyle) && scope.flyoutStyle != "") {
\r
3208 flyoutStyleArray = scope.flyoutStyle.split(";");
\r
3209 for (i = 0; i < flyoutStyleArray.length; i++) {
\r
3210 eachCssProperty = flyoutStyleArray[i].split(":");
\r
3211 if (eachCssProperty.length == 2) {
\r
3212 cssPropertyKey = eachCssProperty[0].trim();
\r
3213 cssPropertyVal = eachCssProperty[1].trim();
\r
3214 angular.element(element[0])[0].style[cssPropertyKey] = cssPropertyVal;
\r
3219 angular.element(element[0]).css({
\r
3221 'display': 'block'
\r
3224 var flyoutIcons = angular.element(document.querySelectorAll(".b2b-flyout-icon"));
\r
3225 angular.forEach(flyoutIcons, function (elm) {
\r
3226 angular.element(elm)[0].blur();
\r
3229 $timeout(function () {
\r
3230 ctrl.setTogglerFocus();
\r
3232 var togglerDimensions = ctrl.getTogglerDimensions();
\r
3233 var flyoutDimensions = element[0].getBoundingClientRect();
\r
3235 switch (scope.horizontalPlacement) {
\r
3237 angular.element(element[0]).css({
\r
3238 'left': ((togglerDimensions.width / 2) - 26) + 'px'
\r
3242 angular.element(element[0]).css({
\r
3243 'right': ((togglerDimensions.width / 2) - 23) + 'px'
\r
3247 case "centerLeft":
\r
3248 var marginLeft = 10-(flyoutDimensions.width)-20;
\r
3249 angular.element(element[0]).css({
\r
3250 'margin-left': marginLeft + 'px'
\r
3253 case "centerRight":
\r
3254 angular.element(element[0]).css({
\r
3255 'left': ((togglerDimensions.width + 9 )) + 'px'
\r
3260 var marginLeft = (togglerDimensions.width / 2) - (flyoutDimensions.width / 2) - 8;
\r
3261 angular.element(element[0]).css({
\r
3262 'margin-left': marginLeft + 'px'
\r
3266 switch (scope.verticalPlacement) {
\r
3268 angular.element(element[0]).css({
\r
3269 'top': -(flyoutDimensions.height + 13) + 'px'
\r
3272 case "centerLeft":
\r
3273 angular.element(element[0]).css({
\r
3274 'top': -((togglerDimensions.height-13))+ 'px'
\r
3277 case "centerRight":
\r
3278 angular.element(element[0]).css({
\r
3279 'top': -(flyoutDimensions.height - 23)+ 'px'
\r
3283 angular.element(element[0]).css({
\r
3284 'top': (togglerDimensions.height + 13) + 'px'
\r
3288 angular.element(element[0]).css({
\r
3293 scope.hideFlyout();
\r
3297 scope.gotFocus = function () {
\r
3298 scope.openFlyout = false;
\r
3299 scope.hideFlyout();
\r
3300 ctrl.setTogglerFocus();
\r
3304 scope.closeFromChild = function (e) {
\r
3305 scope.openFlyout = false;
\r
3306 scope.hideFlyout();
\r
3307 ctrl.setTogglerFocus();
\r
3311 scope.hideFlyout = function () {
\r
3312 angular.element(element[0]).css({
\r
3318 scope.closeFlyout = function (e) {
\r
3319 var isElement = $isElement(angular.element(e.target), element, $document);
\r
3320 if ((e.type === "keydown" && e.which === 27) || ((e.type === "click" || e.type==="touchend") && !isElement)) {
\r
3321 scope.openFlyout = false;
\r
3322 scope.hideFlyout();
\r
3323 ctrl.setTogglerFocus();
\r
3328 scope.$watch('openFlyout', function () {
\r
3329 ctrl.updateAriaModel(scope.openFlyout);
\r
3332 $documentBind.click('openFlyout', scope.closeFlyout, scope);
\r
3333 $documentBind.event('keydown', 'openFlyout', scope.closeFlyout, scope);
\r
3334 $documentBind.event('touchend', 'openFlyout', scope.closeFlyout, scope);
\r
3335 ctrl.registerContentScope(scope);
\r
3337 if (angular.isDefined(scope.contentUpdated) && scope.contentUpdated !== null) {
\r
3338 scope.$watch('contentUpdated', function (newVal, oldVal) {
\r
3340 if (newVal !== oldVal) {
\r
3341 ctrl.unbindTabbaleEvents();
\r
3342 ctrl.associateTabEvent();
\r
3344 scope.contentUpdated = false;
\r
3352 .directive('b2bCloseFlyout', [function () {
\r
3355 require: '^b2bFlyout',
\r
3359 link: function (scope, element, attrs, ctrl) {
\r
3360 element.bind('click', function (e) {
\r
3361 scope.closeFlyout(e);
\r
3362 ctrl.closeFlyout(e);
\r
3367 .directive('b2bFlyoutTrapFocusInside', [function () {
\r
3370 transclude: false,
\r
3371 require: '^b2bFlyout',
\r
3372 link: function (scope, elem, attr, ctrl) {
\r
3373 /* Before opening modal, find the focused element */
\r
3374 ctrl.updateTabbableElements();
\r
3379 * @ngdoc directive
\r
3380 * @name Layouts.att:footer
\r
3383 * <file src="src/footer/docs/readme.md" />
\r
3387 <footer class="b2b-footer-wrapper" role="contentinfo" aria-label="footer">
\r
3388 <div class="b2b-footer-container" b2b-column-switch-footer footer-link-items='footerItems'>
\r
3390 <div class="divider-bottom-footer">
\r
3391 <div class="span2 dispalyInline"> </div>
\r
3392 <div class="span6 dispalyInline">
\r
3393 <ul class="footer-nav-content">
\r
3394 <li><a href="Terms_of_use.html" title="Terms of use" id="foot0">Terms of use</a>|</li>
\r
3395 <li><a href="Privacy_policy.html" title="Privacy policy" id="foot1" class="active">Privacy policy</a>|</li>
\r
3396 <li><a href="Tollfree_directory_assistance.html" title="Tollfree directory assistance" id="foot2">Tollfree directory assistance</a>|</li>
\r
3397 <li><a href="compliance.html" title="Accessibility" id="foot3">Accessibility</a></li>
\r
3400 <p><a href="//www.att.com/gen/privacy-policy?pid=2587" target="_blank">© <span class="copyright">2016</span> AT&T Intellectual Property</a>. All rights reserved. AT&T,the AT&T Globe logo and all other AT&T marks contained herein are tardemarks of AT&T intellectual property and/or AT&T affiliated companines.
\r
3404 <div class="span3 footerLogo dispalyInline">
\r
3405 <a href="index.html" class="footer-logo">
\r
3406 <i class="icon-primary-att-globe"><span class="hidden-spoken">A T & T</span></i>
\r
3407 <h2 class="logo-title">AT&T</h2>
\r
3416 * <section id="code">
\r
3417 <example module="b2b.att">
\r
3418 <file src="src/footer/docs/demo.html" />
\r
3419 <file src="src/footer/docs/demo.js" />
\r
3424 angular.module('b2b.att.footer', ['b2b.att.utilities']).
\r
3425 directive('b2bColumnSwitchFooter', [function() {
\r
3430 footerLinkItems: "="
\r
3432 templateUrl: 'b2bTemplate/footer/footer_column_switch_tpl.html',
\r
3433 link: function(scope) {
\r
3434 var tempFooterColumns = scope.footerLinkItems.length;
\r
3435 scope.footerColumns = 3;
\r
3436 if ( (tempFooterColumns === 5) || (tempFooterColumns === 4) ) {
\r
3437 scope.footerColumns = tempFooterColumns;
\r
3447 * @ngdoc directive
\r
3448 * @name Layouts.att:header
\r
3451 * <file src="src/header/docs/readme.md" />
\r
3454 * <li b2b-header-menu class="header__item b2b-headermenu" ng-repeat="item in tabItems" role="presentation">
\r
3455 <a href="#" class="menu__item" role="menuitem">{{item.title}}</a>
\r
3456 <div class="header-secondary-wrapper">
\r
3457 <ul class="header-secondary" role="menu">
\r
3458 <li class="header-subitem" b2b-header-submenu ng-repeat="i in item.subitems" role="presentation">
\r
3459 <a href="#" class="menu__item" aria-haspopup="true" role="menuitem">{{i.value}}</a>
\r
3460 <div class="header-tertiary-wrapper" ng-if="i.links">
\r
3461 <ul class="header-tertiary" role="menu">
\r
3462 <li b2b-header-tertiarymenu ng-repeat="link in i.links" role="presentation">
\r
3463 <label>{{link.title}}</label>
\r
3464 <div b2b-tertiary-link ng-repeat="title in link.value">
\r
3465 <a href="{{link.href}}" class="header-tertiaryitem" ng-if="!title.subitems" aria-haspopup="false" role="menuitem"><span class="b2b-label-hide">{{link.title}}</span>{{title.title}}</a>
\r
3466 <a href="{{link.href}}" class="header-tertiaryitem" b2b-header-togglemenu ng-if="title.subitems" aria-haspopup="true" role="menuitem"><span class="b2b-label-hide">{{link.title}}</span>{{title.title}}</a>
\r
3467 <ul class="header-quarternary" role="menu" ng-if="title.subitems">
\r
3468 <li b2b-header-quarternarymenu role="presentation">
\r
3469 <a href="{{nav.href}}" ng-repeat="nav in title.subitems" role="menuitem" aria-haspopup="true">{{nav.title}}</a>
\r
3482 * <section id="code">
\r
3483 <example module="b2b.att.header">
\r
3484 <file src="src/header/docs/demo.html" />
\r
3485 <file src="src/header/docs/demo.js" />
\r
3490 angular.module('b2b.att.header', ['b2b.att.dropdowns','b2b.att.utilities'])
\r
3491 .directive('b2bHeaderMenu', ['keymap', '$documentBind', '$timeout', '$isElement', '$document', function (keymap, $documentBind, $timeout, $isElement, $document) {
\r
3494 controller:['$scope',function($scope){
\r
3495 this.nextSiblingFocus = function (elObj,flag) {
\r
3496 if (elObj.nextElementSibling) {
\r
3498 var nextmenuItem = this.getFirstElement(elObj.nextElementSibling,'a');
\r
3499 nextmenuItem.focus();
\r
3501 elObj.nextElementSibling.focus();
\r
3506 this.previousSiblingFocus = function (elObj,flag) {
\r
3507 if (elObj.previousElementSibling) {
\r
3509 var prevmenuItem = this.getFirstElement(elObj.previousElementSibling,'a');
\r
3510 prevmenuItem.focus();
\r
3512 elObj.previousElementSibling.focus();
\r
3517 this.getFirstElement = function(elmObj,selector){
\r
3518 return elmObj.querySelector(selector);
\r
3521 link: function (scope, elem,attr,ctrl) {
\r
3522 scope.showMenu = false;
\r
3523 var activeElm, subMenu, tertiaryMenu, el= angular.element(elem)[0],
\r
3524 menuItem = angular.element(elem[0].children[0]);
\r
3525 menuItem.bind('click', function () {
\r
3526 elem.parent().children().removeClass('active');
\r
3527 elem.addClass('active');
\r
3528 var elems= this.parentElement.parentElement.querySelectorAll('li[b2b-header-menu]>a');
\r
3529 for (var i=0; i<elems.length; i++) {
\r
3530 elems[i].setAttribute("aria-expanded",false);
\r
3532 scope.showMenu = true;
\r
3533 var elmTofocus = ctrl.getFirstElement(this.parentElement,'li[b2b-header-submenu]');
\r
3534 elmTofocus.firstElementChild.focus();
\r
3535 this.setAttribute('aria-expanded',true);
\r
3539 elem.bind('keydown', function (evt) {
\r
3540 activeElm = document.activeElement;
\r
3541 subMenu = ctrl.getFirstElement(activeElm.parentElement,'li[b2b-header-submenu]');
\r
3542 tertiaryMenu = ctrl.getFirstElement(activeElm.parentElement,'li[b2b-header-tertiarymenu]');
\r
3543 switch (evt.keyCode) {
\r
3544 case keymap.KEY.ENTER:
\r
3545 case keymap.KEY.SPACE:
\r
3548 case keymap.KEY.UP:
\r
3549 evt.stopPropagation();
\r
3550 evt.preventDefault();
\r
3551 if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
\r
3552 menuItem[0].focus();
\r
3555 case keymap.KEY.DOWN:
\r
3556 evt.stopPropagation();
\r
3557 evt.preventDefault();
\r
3559 subMenu.firstElementChild.focus();
\r
3560 } else if (tertiaryMenu) {
\r
3561 var firstSubitem = ctrl.getFirstElement(tertiaryMenu,'a.header-tertiaryitem');
\r
3562 firstSubitem.focus();
\r
3565 case keymap.KEY.RIGHT:
\r
3566 evt.stopPropagation();
\r
3567 evt.preventDefault();
\r
3568 if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
\r
3569 var elm = angular.element(activeElm.parentElement)[0];
\r
3570 ctrl.nextSiblingFocus(elm,true);
\r
3571 } else if (activeElm.parentElement.parentElement.hasAttribute('b2b-header-tertiarymenu')) {
\r
3572 var tertiaryLI = angular.element(activeElm.parentElement.parentElement)[0];
\r
3573 if (tertiaryLI.nextElementSibling) {
\r
3574 var nextElm = ctrl.getFirstElement(tertiaryLI.nextElementSibling,"a.header-tertiaryitem");
\r
3578 else if(activeElm.parentElement.hasAttribute('b2b-header-menu')){
\r
3579 ctrl.nextSiblingFocus(el,true);
\r
3582 case keymap.KEY.LEFT:
\r
3583 evt.stopPropagation();
\r
3584 evt.preventDefault();
\r
3585 if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
\r
3586 var previousElm = angular.element(activeElm.parentElement)[0];
\r
3587 ctrl.previousSiblingFocus(previousElm,true);
\r
3588 } else if (activeElm.parentElement.parentElement.hasAttribute('b2b-header-tertiarymenu')) {
\r
3589 var tertiaryLI = angular.element(activeElm.parentElement.parentElement)[0];
\r
3590 if (tertiaryLI.previousElementSibling) {
\r
3591 var prevElm = ctrl.getFirstElement(tertiaryLI.previousElementSibling,"a.header-tertiaryitem");
\r
3595 else if(activeElm.parentElement.hasAttribute('b2b-header-menu')) {
\r
3596 ctrl.previousSiblingFocus(el,true);
\r
3599 case keymap.KEY.ESC:
\r
3600 evt.stopPropagation();
\r
3601 evt.preventDefault();
\r
3602 scope.showMenu = false;
\r
3603 elem.removeClass('active');
\r
3604 menuItem.attr('aria-expanded',false);
\r
3605 $timeout(function(){
\r
3606 menuItem[0].focus();
\r
3614 var outsideClick = function (e) {
\r
3615 var isElement = $isElement(angular.element(e.target), elem, $document);
\r
3617 scope.showMenu = false;
\r
3618 elem.removeClass('active');
\r
3622 $documentBind.click('showMenu', outsideClick, scope);
\r
3625 }]).directive('b2bHeaderSubmenu', ['$timeout',function ($timeout) {
\r
3628 link: function (scope, elem) {
\r
3629 var caretSign = angular.element("<i class='menuCaret'></i>");
\r
3630 $timeout(function(){
\r
3631 var menuItem = angular.element(elem[0].children[0]);
\r
3632 menuItem.bind('focus mouseenter', function () {
\r
3633 elem.parent().children().removeClass('active');
\r
3634 elem.addClass('active');
\r
3635 if(elem[0].childElementCount > 1){ // > 1 has third level menu
\r
3636 menuItem.attr('aria-expanded',true);
\r
3637 menuItem.attr('aria-haspopup',true);
\r
3639 var caretLeft = (elem[0].offsetLeft + elem[0].offsetWidth/2) - 10;
\r
3640 caretSign.css({left: caretLeft + 'px'});
\r
3641 angular.element(caretSign);
\r
3642 var tertiaryItems = elem[0].querySelectorAll('[b2b-header-tertiarymenu]');
\r
3643 if(tertiaryItems.length >=1){
\r
3644 elem.append(caretSign);
\r
3647 menuItem.bind('blur', function () {
\r
3648 $timeout(function () {
\r
3649 var parentElm = document.activeElement.parentElement.parentElement;
\r
3651 if (!(parentElm.hasAttribute('b2b-header-tertiarymenu'))) {
\r
3652 elem.removeClass('active');
\r
3653 if(elem[0].childElementCount > 1){ // > 1 has third level menu
\r
3654 menuItem.attr('aria-expanded',false);
\r
3656 var caret = elem[0].querySelector('.menuCaret');
\r
3667 }]).directive('b2bHeaderTertiarymenu', ['$timeout','keymap', function ($timeout,keymap){
\r
3670 require:'^b2bHeaderMenu',
\r
3671 link: function (scope, elem,attr,ctrl) {
\r
3673 elem.bind('keydown', function (evt) {
\r
3674 var activeElm = document.activeElement;
\r
3675 var activeParentElm = activeElm.parentElement;
\r
3676 var activeParentObj = angular.element(activeParentElm)[0];
\r
3678 if(activeParentElm.hasAttribute('b2b-tertiary-link')){
\r
3679 var quarterNav = angular.element(activeParentElm)[0].querySelector('li[b2b-header-quarternarymenu]');
\r
3681 var links = ctrl.getFirstElement(angular.element(quarterNav)[0],'a');
\r
3684 var tertiaryMenu = activeElm.parentElement.parentElement.parentElement;
\r
3685 var tertiaryMenuFlag = tertiaryMenu.hasAttribute('b2b-tertiary-link');
\r
3687 switch (evt.keyCode) {
\r
3688 case keymap.KEY.DOWN:
\r
3689 evt.stopPropagation();
\r
3690 evt.preventDefault();
\r
3691 if (activeParentElm.hasAttribute('b2b-tertiary-link')) {
\r
3692 if(angular.element(quarterNav).hasClass('active')){
\r
3694 }else if(activeParentObj.nextElementSibling){
\r
3695 ctrl.nextSiblingFocus(activeParentObj,true);
\r
3698 else if(angular.element(activeParentElm).hasClass('active')){
\r
3699 ctrl.nextSiblingFocus(activeElm);
\r
3702 case keymap.KEY.UP:
\r
3703 evt.stopPropagation();
\r
3704 evt.preventDefault();
\r
3705 if(activeParentElm.hasAttribute('b2b-tertiary-link')){
\r
3706 if(activeParentObj.previousElementSibling.hasAttribute('b2b-tertiary-link')){
\r
3707 ctrl.previousSiblingFocus(activeParentObj,true);
\r
3709 var elm = angular.element(activeElm.parentElement.parentElement.parentElement.parentElement.parentElement)[0];
\r
3710 ctrl.getFirstElement(elm,"a").focus();
\r
3712 }else if(angular.element(activeParentElm).hasClass('active')){
\r
3713 if (activeElm.previousElementSibling) {
\r
3714 ctrl.previousSiblingFocus(activeElm);
\r
3715 }else if (tertiaryMenuFlag) {
\r
3716 var elm = angular.element(tertiaryMenu)[0];
\r
3717 ctrl.getFirstElement(elm,"a.header-tertiaryitem").focus();
\r
3727 }]).directive('b2bHeaderTogglemenu', ['$timeout', 'keymap', function ($timeout, keymap) {
\r
3730 require: '^b2bHeaderMenu',
\r
3731 link: function (scope, elem, attrs, ctrl) {
\r
3733 $timeout(function () {
\r
3734 quarterNav = angular.element(elem.parent())[0].querySelector('li[b2b-header-quarternarymenu]');
\r
3735 elem.bind('click', function () {
\r
3736 angular.element(quarterNav).toggleClass('active');
\r
3741 }]).directive('b2bHeaderResponsive', ['$timeout',function ($timeout) {
\r
3744 controller: function($scope){
\r
3745 this.applyMediaQueries = function(value){
\r
3746 document.querySelector('style').textContent +=
\r
3747 "@media screen and (max-width:950px) { \
\r
3748 .header__item.profile { right: " + value + "px; } \
\r
3751 this.arrangeResponsiveHeader = function(children){
\r
3753 * clientWidth of 1090 === max-width of 1100px
\r
3754 * clientWidth of 920 === max-width of 950px
\r
3755 * see b2b-angular.css for rest of responsive header CSS
\r
3757 if (document.documentElement.clientWidth <= 920) {
\r
3760 this.applyMediaQueries(200);
\r
3763 this.applyMediaQueries(200);
\r
3765 default: // anthing above 3, however, should not have more than 3 to date
\r
3766 this.applyMediaQueries(200);
\r
3771 link: function (scope, elem, attrs, ctrl) {
\r
3776 $timeout(function(){
\r
3777 profile = document.querySelector('li.header__item.profile');
\r
3778 children = angular.element(profile).children().length;
\r
3780 ctrl.arrangeResponsiveHeader(children); // shift right-side icon flyovers
\r
3783 // on screen resize
\r
3784 window.addEventListener('resize', function(event){ // caret adjustmet
\r
3785 var activeSubmenu = elem[0].querySelector('[b2b-header-menu] [b2b-header-submenu].active');
\r
3786 var activeSubmenuEl = angular.element(activeSubmenu);
\r
3787 if(activeSubmenu){
\r
3788 var caretSign = activeSubmenu.querySelector('i.menuCaret');
\r
3790 var caretSignEl = angular.element(caretSign);
\r
3791 var caretLeft = (activeSubmenu.offsetLeft + activeSubmenu.offsetWidth/2) - 10;
\r
3792 caretSignEl.css({left: caretLeft + 'px'});
\r
3796 ctrl.arrangeResponsiveHeader(children); // shift right-side icon flyovers
\r
3803 * @ngdoc directive
\r
3804 * @name Layouts.att:headings
\r
3807 * <file src="src/headings/docs/readme.md" />
\r
3810 <h1 class="heading-page">38px page heading</h1>
\r
3811 <h2 class="heading-major-section">30px major section heading</h2>
\r
3812 <h3 class="heading-sub-section">24px sub-section heading</h3>
\r
3813 <h2 class="heading-medium">20px medium heading</h2>
\r
3814 <h2 class="heading-medium-emphasis">20px medium emphasis heading</h2>
\r
3815 <h3 class="heading-small">18px small heading</h3>
\r
3816 <h3 class="heading-small-emphasis">18px small emphasis heading</h3>
\r
3817 <h3 class="heading-micro">13px micro heading</h3>
\r
3819 <h2 class="heading-group">Lead</h2>
\r
3820 <h1 class="heading-page">This is 38px heading</h1>
\r
3821 <h2 class="lead">This is lead text...The next big thing since the last big thing we announced.</h2>
\r
3822 <h2 class="heading-group">Eyebrow</h2>
\r
3823 <h3 class="eyebrow">EYEBROW TEXT</h3>
\r
3824 <h2 class="heading-major-section">This is a 30px heading</h2>
\r
3825 <h3 class="eyebrow">EYEBROW TEXT</h3>
\r
3826 <h3 class="heading-sub-section">24px sub-section heading</h3>
\r
3827 <h2 class="heading-group">Subheading</h2>
\r
3828 <h2 class="heading-major-section">This is a 30px heading</h2>
\r
3829 <h3 class="subheading">A subheading here to support what was said above</h3>
\r
3831 <section id="code">
\r
3832 <b>HTML + AngularJS</b>
\r
3833 <example module="b2b.att">
\r
3834 <file src="src/headings/docs/demo.html" />
\r
3839 var b2bLegalCopy = angular.module('b2b.att.headings', []);
\r
3841 * @ngdoc directive
\r
3842 * @name Tabs, tables & accordions.att:horizontalTable
\r
3845 * <file src="src/horizontalTable/docs/readme.md" />
\r
3848 * @param {int} sticky - Number of sticky columns to have. Maximum of 3.
\r
3849 * @param {boolean} refresh - A boolean that when set to true will force a re-render of table. Only use when using 'bulk mode'
\r
3851 * <section id="code">
\r
3852 <example module="b2b.att">
\r
3853 <file src="src/horizontalTable/docs/demo.html" />
\r
3854 <file src="src/horizontalTable/docs/demo.js" />
\r
3859 angular.module('b2b.att.horizontalTable', [])
\r
3860 .constant('b2bHorizontalTableConfig', {
\r
3861 'maxStickyColumns': 3
\r
3863 .directive('b2bHorizontalTable', ['$timeout', 'b2bHorizontalTableConfig', 'b2bDOMHelper', function ($timeout, b2bHorizontalTableConfig, b2bDOMHelper) {
\r
3869 numOfStickyCols: '=?sticky',
\r
3873 templateUrl: 'b2bTemplate/horizontalTable/horizontalTable.html',
\r
3874 link: function (scope, element, attrs, ctrl) {
\r
3875 scope.numOfStickyCols = scope.numOfStickyCols || 1;
\r
3876 scope.viewportIndex = scope.numOfStickyCols;
\r
3878 // JM520E: This is a temporary hack until I solve the ngRepeat issue
\r
3880 if (element.find('th').length < scope.numOfStickyCols) {
\r
3881 // DOM ngRepeat is not ready, let's check back in 10 ms
\r
3882 console.info('THs are not ready, trying again in 10ms');
\r
3883 $timeout(hack, 10, false);
\r
3890 if (attrs.refresh !== undefined && attrs.refresh !== '') {
\r
3891 scope.$watch('refresh', function(oldVal, newVal) {
\r
3892 if (scope.refresh) {
\r
3893 // From testing it takes about 30 ms before ngRepeat executes, so let's set initial timeout
\r
3894 // NOTE: May need to expose timeout to developers. Application is known to have digest cycle of 3-5k watches.
\r
3895 $timeout(init, 100, false);
\r
3896 scope.refresh = false;
\r
3901 var tableElement = element.find('table');
\r
3902 var thElements = element.find('th');
\r
3903 var innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container'));
\r
3904 var outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table'));
\r
3906 var tableColumns = [];
\r
3907 var tableRows = element.find('tr');
\r
3911 var totalWidth = element.children()[0].offsetWidth;
\r
3912 var lastVisibleColumn = 0;
\r
3913 var collectiveColumnWidth = [];
\r
3914 var collectiveRowHeight = [];
\r
3915 var columnSets = [];
\r
3916 var setIndex = 0;
\r
3917 var stickyPixels = 0;
\r
3919 var displayNoneCSS = {'display': 'none'};
\r
3920 var displayBlockCSS = {'display': 'table-cell'};
\r
3922 function calculateVisibleColumns(startingPoint) {
\r
3923 var usedWidth = 0,
\r
3924 visibleColumns = startingPoint || scope.numOfStickyCols;
\r
3926 while(usedWidth < stickyPixels && visibleColumns < collectiveColumnWidth.length) {
\r
3927 if (usedWidth+collectiveColumnWidth[visibleColumns] > stickyPixels) {
\r
3928 if (startingPoint === visibleColumns) {
\r
3929 return visibleColumns; // The next cell is too large to fit, it should be only thing to fit
\r
3932 return visibleColumns;
\r
3934 usedWidth += collectiveColumnWidth[visibleColumns];
\r
3938 if (usedWidth > stickyPixels) {
\r
3939 return --visibleColumns;
\r
3941 return visibleColumns;
\r
3944 function updateCellDisplay(set) {
\r
3945 for (var i = scope.numOfStickyCols; i < tableColumns.length; i++) {
\r
3946 angular.element(tableColumns[i]).css(displayNoneCSS);
\r
3949 for (var i = set[0]; i <= set[1]; i++) {
\r
3950 angular.element(tableColumns[i]).css(displayBlockCSS);
\r
3954 function forceDigest() {
\r
3955 if (!scope.$$phase) {
\r
3960 function findMax(arr, prop) {
\r
3965 for (var i = 0; i < arr.length; i++) {
\r
3967 prevDisplay = angular.element(item).css('display');
\r
3968 if (scope.$$phase) {
\r
3971 if (prop === 'width') {
\r
3972 localVal = Math.ceil(parseInt(window.getComputedStyle(item).width.split('px')[0], 10)) + 30; // 30 px is padding
\r
3973 } else if (prop === 'offsetWidth') {
\r
3974 localVal = item.offsetWidth;
\r
3975 } else if (prop === 'height') {
\r
3976 localVal = item.offsetHeight;
\r
3979 if (localVal >= max) {
\r
3988 // Reset this from a previous execution
\r
3989 tableColumns = [];
\r
3990 collectiveColumnWidth = [];
\r
3991 collectiveRowHeight = [];
\r
3994 lastVisibleColumn = 0;
\r
3997 visibleColumns = [];
\r
4000 tableElement = element.find('table');
\r
4001 thElements = element.find('th');
\r
4002 innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container'));
\r
4003 outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table'));
\r
4004 totalWidth = element.children()[0].offsetWidth;
\r
4007 tableRows = element.find('tr');
\r
4008 totalWidth = element.children()[0].offsetWidth;
\r
4010 scope.disableLeft = true;
\r
4011 scope.disableRight = false;
\r
4013 if (scope.numOfStickyCols > b2bHorizontalTableConfig.maxStickyColumns) {
\r
4014 throw new Error("Table can only support 3 sticky columns.");
\r
4017 angular.forEach(tableRows, function(row, rowIndex) {
\r
4018 collectiveRowHeight.push(findMax(row.children, 'height'));
\r
4019 for(var j = 0; j < row.children.length; j++) {
\r
4020 if (tableColumns[j] === undefined) {
\r
4021 tableColumns[j] = [];
\r
4023 tableColumns[j].push(row.children[j]);
\r
4027 // We need to reset all the displayNones from previous runs, if applicable
\r
4028 if (attrs.refresh !== undefined && attrs.refresh !== '') {
\r
4029 for (var i = scope.numOfStickyCols+1; i < tableColumns.length; i++) {
\r
4030 angular.element(tableColumns[i]).css(displayBlockCSS);
\r
4034 for (var i = 0; i < tableColumns.length; i++) {
\r
4035 collectiveColumnWidth.push(findMax(tableColumns[i], 'width')); //offsetWidth doesn't take into account custom css inside
\r
4037 for(var i = 0; i < scope.numOfStickyCols; i++) {
\r
4038 maxWidth += collectiveColumnWidth[i];
\r
4041 stickyPixels = totalWidth-maxWidth;
\r
4044 // At this point, for each tr, I need to set the properties (height) and each numOfStickyCols children
\r
4045 // should be set with sticky properties (margin-left and width)
\r
4046 var width = maxWidth;
\r
4047 for(var i = 0; i < scope.numOfStickyCols; i++) {
\r
4048 for (var j = 0; j < tableRows.length; j++) {
\r
4049 trObject = angular.element(tableRows[j].children[i]);
\r
4051 angular.element(trObject).css({
\r
4052 'margin-left': -(width + 2) + 'px',
\r
4053 'width': (collectiveColumnWidth[i] + 3) + 'px', // instead of taking the max width, grab max width for that column
\r
4054 'height': collectiveRowHeight[j] + 'px',
\r
4055 'position': 'absolute',
\r
4056 'background-color': 'lightgrey'
\r
4061 width -= collectiveColumnWidth[i];
\r
4064 innerContainer.css({
\r
4065 'padding-left': (maxWidth + 2) + 'px'
\r
4069 // Let's precompute all the (set) combinations beforehand
\r
4071 for (var i = scope.numOfStickyCols; i < tableColumns.length;) {
\r
4072 visibleColumns = calculateVisibleColumns(i);
\r
4073 columnSets.push([i, visibleColumns]);
\r
4074 i = visibleColumns + 1;
\r
4077 updateCellDisplay(columnSets[setIndex]);
\r
4078 checkScrollArrows();
\r
4080 scope.numOfCols = tableColumns.length;
\r
4082 console.log('Bulk Mode is ' + (attrs.bulkMode ? 'enabled': 'disabled'));
\r
4083 console.log('tableColumns', tableColumns);
\r
4084 console.log('collectiveColumnWidth: ', collectiveColumnWidth);
\r
4085 console.log('maxWidth: ', maxWidth);
\r
4088 function checkScrollArrows() {
\r
4089 scope.disableLeft = (setIndex === 0);
\r
4090 scope.disableRight = !(setIndex < columnSets.length-1);
\r
4094 scope.moveViewportLeft = function () {
\r
4096 updateCellDisplay(columnSets[setIndex]);
\r
4097 checkScrollArrows();
\r
4099 if (scope.disableLeft) {
\r
4100 element.find('span')[0].focus();
\r
4104 scope.moveViewportRight = function () {
\r
4106 updateCellDisplay(columnSets[setIndex]);
\r
4107 checkScrollArrows();
\r
4109 if (scope.disableRight) {
\r
4110 element.find('span')[0].focus();
\r
4114 scope.getColumnSet = function () {
\r
4115 return columnSets[setIndex];
\r
4118 innerContainer.bind('scroll', function () {
\r
4119 $timeout(function () {
\r
4120 checkScrollArrows();
\r
4128 * @ngdoc directive
\r
4129 * @name Forms.att:hourPicker
\r
4132 * <file src="src/hourPicker/docs/readme.md" />
\r
4135 * <div b2b-hourpicker ng-model="hourpickerValue.value"></div>
\r
4138 * <section id="code">
\r
4139 <example module="b2b.att">
\r
4140 <file src="src/hourPicker/docs/demo.html" />
\r
4141 <file src="src/hourPicker/docs/demo.js" />
\r
4146 angular.module('b2b.att.hourPicker', ['b2b.att.utilities'])
\r
4148 .constant('b2bHourpickerConfig', {
\r
4165 title: 'wednesday',
\r
4170 title: 'thursday',
\r
4180 title: 'saturday',
\r
4185 startTimeOptions: ['1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00', '12:00'],
\r
4186 startTimeDefaultOptionIndex: -1,
\r
4187 startTimeDefaultMeridiem: "am",
\r
4188 endTimeOptions: ['1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00', '12:00'],
\r
4189 endTimeDefaultOptionIndex: -1,
\r
4190 endTimeDefaultMeridiem: "pm",
\r
4191 sameDayOption: true
\r
4194 .factory('b2bNormalizeHourpickerValues', [function () {
\r
4195 var _normalize = function (hourpickerValues) {
\r
4196 if (angular.isDefined(hourpickerValues) && hourpickerValues != null) {
\r
4197 var finalHourpickerValues = [];
\r
4198 var hourpickerValue = {};
\r
4200 for (var i = 0; i < hourpickerValues.length; i++) {
\r
4201 days = hourpickerValues[i].days ? hourpickerValues[i].days : {};
\r
4202 hourpickerValue.startTime = hourpickerValues[i].startTime ? hourpickerValues[i].startTime : '';
\r
4203 hourpickerValue.startMeridiem = hourpickerValues[i].startMeridiem ? hourpickerValues[i].startMeridiem : '';
\r
4204 hourpickerValue.endTime = hourpickerValues[i].endTime ? hourpickerValues[i].endTime : '';
\r
4205 hourpickerValue.endMeridiem = hourpickerValues[i].endMeridiem ? hourpickerValues[i].endMeridiem : '';
\r
4206 hourpickerValue.days = [];
\r
4208 var retrieveDaysText = function (daysDetails) {
\r
4209 var daysTexts = [];
\r
4213 for (var i in days) {
\r
4214 if (days[i].value) {
\r
4215 daysTexts.push(i);
\r
4219 first = daysTexts[0];
\r
4220 last = daysTexts[0];
\r
4222 hourpickerValue.days[index] = days[first].caption;
\r
4223 if (daysTexts.length > 1) {
\r
4224 for (var i = 1; i < daysTexts.length; i++) {
\r
4225 if (daysTexts[i] - last === 1) {
\r
4226 last = daysTexts[i];
\r
4227 hourpickerValue.days[index] = days[first].caption + ' - ' + days[last].caption;
\r
4230 first = last = daysTexts[i];
\r
4231 hourpickerValue.days[index] = days[first].caption;
\r
4236 retrieveDaysText();
\r
4238 finalHourpickerValues.push(angular.copy(hourpickerValue));
\r
4241 return angular.copy(finalHourpickerValues);
\r
4246 normalize: _normalize
\r
4250 .directive('b2bHourpicker', ['b2bHourpickerConfig', 'b2bNormalizeHourpickerValues', function (b2bHourpickerConfig, b2bNormalizeHourpickerValues) {
\r
4255 require: 'ngModel',
\r
4256 templateUrl: 'b2bTemplate/hourPicker/b2bHourpicker.html',
\r
4257 controller: ['$scope', function (scope) {
\r
4260 link: function (scope, elem, attr, ctrl) {
\r
4261 scope.hourpicker = {};
\r
4262 scope.hourpicker.dayOptions = attr.dayOptions ? scope.$parent.$eval(attr.dayOptions) : b2bHourpickerConfig.dayOptions;
\r
4263 scope.hourpicker.startTimeOptions = attr.startTimeOptions ? scope.$parent.$eval(attr.startTimeOptions) : b2bHourpickerConfig.startTimeOptions;
\r
4264 scope.hourpicker.endTimeOptions = attr.endTimeOptions ? scope.$parent.$eval(attr.endTimeOptions) : b2bHourpickerConfig.endTimeOptions;
\r
4265 scope.hourpicker.startTimeDefaultOptionIndex = attr.startTimeDefaultOptionIndex ? scope.$parent.$eval(attr.startTimeDefaultOptionIndex) : b2bHourpickerConfig.startTimeDefaultOptionIndex;
\r
4266 scope.hourpicker.endTimeDefaultOptionIndex = attr.endTimeDefaultOptionIndex ? scope.$parent.$eval(attr.endTimeDefaultOptionIndex) : b2bHourpickerConfig.endTimeDefaultOptionIndex;
\r
4267 scope.hourpicker.startTimeDefaultMeridiem = attr.startTimeDefaultMeridiem ? scope.$parent.$eval(attr.startTimeDefaultMeridiem) : b2bHourpickerConfig.startTimeDefaultMeridiem;
\r
4268 scope.hourpicker.endTimeDefaultMeridiem = attr.endTimeDefaultMeridiem ? scope.$parent.$eval(attr.endTimeDefaultMeridiem) : b2bHourpickerConfig.endTimeDefaultMeridiem;
\r
4269 scope.hourpicker.sameDayOption = attr.sameDayOption ? scope.$parent.$eval(attr.sameDayOption) : b2bHourpickerConfig.sameDayOption;
\r
4270 scope.hourpicker.editMode = -1;
\r
4272 scope.hourpickerValues = [];
\r
4273 scope.finalHourpickerValues = [];
\r
4274 scope.addHourpickerValue = function (hourpickerPanelValue) {
\r
4275 if (hourpickerPanelValue) {
\r
4276 if (scope.hourpicker.editMode > -1) {
\r
4277 scope.hourpickerValues[scope.hourpicker.editMode] = hourpickerPanelValue;
\r
4278 scope.hourpicker.editMode = -1;
\r
4280 scope.hourpickerValues.push(hourpickerPanelValue);
\r
4283 scope.finalHourpickerValues = b2bNormalizeHourpickerValues.normalize(angular.copy(scope.hourpickerValues));
\r
4284 ctrl.$setViewValue(angular.copy(scope.hourpickerValues));
\r
4286 ctrl.$render = function () {
\r
4287 if (angular.isDefined(ctrl.$modelValue)) {
\r
4288 scope.hourpickerValues = angular.copy(ctrl.$modelValue);
\r
4289 scope.finalHourpickerValues = b2bNormalizeHourpickerValues.normalize(angular.copy(scope.hourpickerValues));
\r
4292 scope.editHourpickerValue = function (index) {
\r
4293 scope.hourpickerPanelValue = angular.copy(scope.hourpickerValues[index]);
\r
4294 scope.hourpicker.editMode = index;
\r
4296 scope.deleteHourpickerValue = function (index) {
\r
4297 scope.hourpickerValues.splice(index, 1);
\r
4298 scope.resetHourpickerPanelValue();
\r
4299 scope.addHourpickerValue();
\r
4302 scope.setValidity = function (errorType, errorValue) {
\r
4303 ctrl.$setValidity(errorType, errorValue);
\r
4309 .directive('b2bHourpickerPanel', [function () {
\r
4313 templateUrl: 'b2bTemplate/hourPicker/b2bHourpickerPanel.html',
\r
4314 controller: ['$scope', function (scope) {
\r
4317 link: function (scope, elem, attr, ctrl) {
\r
4318 var hourpickerPanelValueTemplate = {
\r
4321 startMeridiem: 'am',
\r
4325 for (var i = 0; i < scope.hourpicker.dayOptions.length; i++) {
\r
4326 hourpickerPanelValueTemplate.days[i] = {
\r
4328 title: scope.hourpicker.dayOptions[i].title,
\r
4329 caption: scope.hourpicker.dayOptions[i].caption
\r
4332 scope.hourpickerPanelValue = {};
\r
4333 scope.disableAddBtn = true;
\r
4335 scope.$watch('hourpickerPanelValue.days', function(){
\r
4336 for(var i in scope.hourpickerPanelValue.days)
\r
4338 if(scope.hourpickerPanelValue.days[i].value)
\r
4340 scope.disableAddBtn = false;
\r
4343 scope.disableAddBtn = true;
\r
4347 scope.resetHourpickerPanelValue = function () {
\r
4348 scope.hourpickerPanelValue = angular.copy(hourpickerPanelValueTemplate);
\r
4349 if (scope.hourpicker.startTimeDefaultOptionIndex > -1) {
\r
4350 scope.hourpickerPanelValue.startTime = scope.hourpicker.startTimeOptions[scope.hourpicker.startTimeDefaultOptionIndex];
\r
4352 if (scope.hourpicker.endTimeDefaultOptionIndex > -1) {
\r
4353 scope.hourpickerPanelValue.endTime = scope.hourpicker.endTimeOptions[scope.hourpicker.endTimeDefaultOptionIndex];
\r
4355 scope.hourpickerPanelValue.startMeridiem = scope.hourpicker.startTimeDefaultMeridiem;
\r
4356 scope.hourpickerPanelValue.endMeridiem = scope.hourpicker.endTimeDefaultMeridiem;
\r
4357 scope.hourpicker.editMode = -1;
\r
4358 scope.setValidity('invalidHourpickerData', true);
\r
4359 scope.setValidity('invalidHourpickerTimeRange', true);
\r
4361 scope.resetHourpickerPanelValue();
\r
4362 scope.updateHourpickerValue = function () {
\r
4363 if (scope.isFormValid() && !scope.isTimeOverlap()) {
\r
4364 scope.addHourpickerValue(angular.copy(scope.hourpickerPanelValue));
\r
4365 scope.resetHourpickerPanelValue();
\r
4369 scope.isFormValid = function () {
\r
4370 var isStartTimeAvailable = scope.hourpickerPanelValue.startTime ? true : false;
\r
4371 var isStartMeridiemAvailable = scope.hourpickerPanelValue.startMeridiem ? true : false;
\r
4372 var isEndTimeAvailable = scope.hourpickerPanelValue.endTime ? true : false;
\r
4373 var isEndMeridiemAvailable = scope.hourpickerPanelValue.endMeridiem ? true : false;
\r
4374 var currentStartTime = getTime(scope.hourpickerPanelValue.startTime, scope.hourpickerPanelValue.startMeridiem);
\r
4375 var currentEndTime = getTime(scope.hourpickerPanelValue.endTime, scope.hourpickerPanelValue.endMeridiem);
\r
4376 var isTimeInProperSequence = currentEndTime > currentStartTime;
\r
4377 var isDayChecked = false;
\r
4378 for (var i in scope.hourpickerPanelValue.days) {
\r
4379 if (scope.hourpickerPanelValue.days[i].value) {
\r
4380 isDayChecked = true;
\r
4385 if (isStartTimeAvailable && isStartMeridiemAvailable && isEndTimeAvailable && isEndMeridiemAvailable && isTimeInProperSequence && isDayChecked) {
\r
4386 scope.setValidity('invalidHourpickerData', true);
\r
4389 scope.setValidity('invalidHourpickerData', false);
\r
4393 scope.isTimeOverlap = function () {
\r
4394 var selectedDays = [];
\r
4395 for (var i in scope.hourpickerPanelValue.days) {
\r
4396 if (scope.hourpickerPanelValue.days[i].value) {
\r
4397 selectedDays.push(i);
\r
4401 var currentStartTime, currentEndTime, existingStartTime, existingEndTime;
\r
4402 currentStartTime = getTime(scope.hourpickerPanelValue.startTime, scope.hourpickerPanelValue.startMeridiem);
\r
4403 currentEndTime = getTime(scope.hourpickerPanelValue.endTime, scope.hourpickerPanelValue.endMeridiem);
\r
4404 for (var i = 0; i < scope.hourpickerValues.length; i++) {
\r
4406 if (i === scope.hourpicker.editMode) {
\r
4410 for (var j = 0; j < selectedDays.length; j++) {
\r
4411 existingStartTime = getTime(scope.hourpickerValues[i].startTime, scope.hourpickerValues[i].startMeridiem);
\r
4412 existingEndTime = getTime(scope.hourpickerValues[i].endTime, scope.hourpickerValues[i].endMeridiem);
\r
4413 if (scope.hourpickerValues[i].days[selectedDays[j]].value) {
\r
4414 if(!scope.hourpicker.sameDayOption){
\r
4415 scope.setValidity('dayAlreadySelected', false);
\r
4417 } else if ((currentStartTime > existingStartTime && currentStartTime < existingEndTime) || (currentEndTime > existingStartTime && currentEndTime < existingEndTime)) {
\r
4418 scope.setValidity('invalidHourpickerTimeRange', false);
\r
4420 } else if ((existingStartTime > currentStartTime && existingStartTime < currentEndTime) || (existingEndTime > currentStartTime && existingEndTime < currentEndTime)) {
\r
4421 scope.setValidity('invalidHourpickerTimeRange', false);
\r
4423 } else if ((currentStartTime === existingStartTime) && (currentEndTime === existingEndTime)) {
\r
4424 scope.setValidity('invalidHourpickerTimeRange', false);
\r
4431 scope.setValidity('dayAlreadySelected', true);
\r
4432 scope.setValidity('invalidHourpickerTimeRange', true);
\r
4435 var getTime = function (timeString, meridiem) {
\r
4436 var tempDate = new Date();
\r
4437 if (timeString && meridiem) {
\r
4438 var timeSplit = timeString.split(':');
\r
4439 var hour = ((meridiem === 'PM' || meridiem === 'pm') && timeSplit[0] !== '12') ? parseInt(timeSplit[0], 10) + 12 : parseInt(timeSplit[0], 10);
\r
4440 tempDate.setHours(hour, parseInt(timeSplit[1], 10), 0, 0);
\r
4443 return tempDate.getTime();
\r
4449 .directive('b2bHourpickerValue', [function () {
\r
4453 templateUrl: 'b2bTemplate/hourPicker/b2bHourpickerValue.html',
\r
4454 controller: ['$scope', function (scope) {
\r
4457 link: function (scope, elem, attr, ctrl) {
\r
4458 scope.hourpickerValue = {};
\r
4459 scope.hourpickerValue.startTime = attr.startTime ? scope.$eval(attr.startTime) : '';
\r
4460 scope.hourpickerValue.startMeridiem = attr.startMeridiem ? scope.$eval(attr.startMeridiem) : '';
\r
4461 scope.hourpickerValue.endTime = attr.endTime ? scope.$eval(attr.endTime) : '';
\r
4462 scope.hourpickerValue.endMeridiem = attr.endMeridiem ? scope.$eval(attr.endMeridiem) : '';
\r
4463 scope.hourpickerValue.days = attr.days ? scope.$eval(attr.days).join(', ') : '';
\r
4464 scope.hourpickerValue.index = attr.b2bHourpickerValue ? scope.$eval(attr.b2bHourpickerValue) : -1;
\r
4469 * @ngdoc directive
\r
4470 * @name Template.att:inputTemplate
\r
4473 * <file src="src/inputTemplate/docs/readme.md" />
\r
4476 * <input type="text" id="fieldId" placeholder="placholder text here" class="span12 input-enhanced" name="fieldName">
\r
4479 <section id="code">
\r
4480 <b>HTML + AngularJS</b>
\r
4481 <example module="b2b.att">
\r
4482 <file src="src/inputTemplate/docs/demo.html" />
\r
4483 <file src="src/inputTemplate/docs/demo.js" />
\r
4487 angular.module('b2b.att.inputTemplate', []);
\r
4490 * @ngdoc directive
\r
4491 * @name Navigation.att:leftNavigation
\r
4494 * <file src="src/leftNavigation/docs/readme.md" />
\r
4497 * <b2b-left-navigation data-menu="menuData"></b2b-left-navigation>
\r
4500 * <section id="code">
\r
4501 <example module="b2b.att">
\r
4502 <file src="src/leftNavigation/docs/demo.html" />
\r
4503 <file src="src/leftNavigation/docs/demo.js" />
\r
4508 angular.module('b2b.att.leftNavigation', [])
\r
4509 .directive('b2bLeftNavigation', [function () {
\r
4512 templateUrl: 'b2bTemplate/leftNavigation/leftNavigation.html',
\r
4516 link: function (scope, element, attrs, ctrl) {
\r
4518 scope.itemIdx = -1;
\r
4519 scope.navIdx = -1;
\r
4520 scope.toggleNav = function (val,link) {
\r
4521 if (val === scope.idx) {
\r
4527 /*New function for ECOMP sdk*/
\r
4528 scope.toggleDrawer = function(showmenu){
\r
4529 scope.idx=-1; /*hide the sunmenus*/
\r
4531 //scope.openList.length=0;
\r
4532 document.getElementById('page-content').style.marginLeft = "50px";
\r
4535 document.getElementById('page-content').style.marginLeft = "250px"; */
\r
4537 scope.liveLink = function (evt, val1, val2) {
\r
4538 scope.itemIdx = val1;
\r
4539 scope.navIdx = val2;
\r
4540 evt.stopPropagation();
\r
4546 * @ngdoc directive
\r
4547 * @name Buttons, links & UI controls.att:links
\r
4550 * <file src="src/links/docs/readme.md" />
\r
4552 * <!-- See below examples for link implementation -->
\r
4555 <section id="code">
\r
4556 <b>HTML + AngularJS</b>
\r
4557 <example module="b2b.att">
\r
4558 <file src="src/links/docs/demo.html" />
\r
4559 <file src="src/links/docs/demo.js" />
\r
4563 angular.module('b2b.att.links', []);
\r
4565 * @ngdoc directive
\r
4566 * @name Misc.att:listbox
\r
4569 * <file src="src/listbox/docs/readme.md" />
\r
4571 * @param {int} currentIndex - Current index of selected listbox item. Is not supported on multiselect listbox
\r
4572 * @param {Array} listboxData - Data of listbox items. Should include full data regardless if HTML will be filtered.
\r
4575 * <section id="code">
\r
4576 <example module="b2b.att">
\r
4577 <file src="src/listbox/docs/demo.html" />
\r
4578 <file src="src/listbox/docs/demo.js" />
\r
4583 angular.module('b2b.att.listbox', ['b2b.att.utilities'])
\r
4584 .directive('b2bListBox', ['keymap', 'b2bDOMHelper', '$rootScope', function(keymap, b2bDOMHelper, $rootScope) {
\r
4590 currentIndex: '=',
\r
4593 templateUrl: 'b2bTemplate/listbox/listbox.html',
\r
4594 link: function(scope, elem, attr) {
\r
4596 if (attr.ariaMultiselectable !== undefined || attr.ariaMultiselectable === 'true') {
\r
4597 scope.multiselectable = true;
\r
4599 scope.multiselectable = false;
\r
4602 var shiftKey = false;
\r
4603 var elements = [];
\r
4604 var prevDirection = undefined; // previous direction is used for an edge case when shifting
\r
4605 var shiftKeyPressed = false; // Used to handle shift clicking
\r
4606 var ctrlKeyPressed = false;
\r
4608 var currentIndexSet = {
\r
4609 'elementIndex': 0,
\r
4610 'listboxDataIndex': 0
\r
4613 /*scope.$watch('currentIndex', function(oldVal, newVal) {
\r
4614 if (angular.equals(oldVal, newVal)) return;
\r
4615 if (!scope.multiselectable) {
\r
4616 // This doesn't garuntee anything. index will update on focus based on rules
\r
4617 currentIndexSet.listboxDataIndex = scope.currentIndex;
\r
4618 // Should this occur?
\r
4619 //scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
\r
4621 // Update elementIndex
\r
4622 elements = elem.children();
\r
4623 var indecies = Array.prototype.map.call(elements, function(item) {
\r
4624 return parseInt(angular.element(item).attr('data-index'), 10);
\r
4625 }).filter(function(item) {
\r
4626 return item === scope.currentIndex;
\r
4628 currentIndex.elementIndex = indecies[0];
\r
4629 //focusOnElement(currentIndexSet.elementIndex); // This isn't shifting focus
\r
4630 if (!scope.$$phase) {
\r
4636 function isTrue(item) {
\r
4637 if (item.selected === true) {
\r
4642 function incrementIndex(elem) {
\r
4643 $rootScope.$apply();
\r
4645 var nextElem = elem.next();
\r
4646 if (!angular.isDefined(nextElem) || nextElem.length === 0) {
\r
4650 currentIndexSet.elementIndex += 1;
\r
4651 currentIndexSet.listboxDataIndex = parseInt(nextElem.attr('data-index'), 10);
\r
4652 scope.currentIndex = currentIndexSet.listboxDataIndex;
\r
4654 if (currentIndexSet.elementIndex >= elements.length - 1) {
\r
4655 currentIndexSet.elementIndex = elements.length-1;
\r
4659 function decrementIndex(elem) {
\r
4660 $rootScope.$apply();
\r
4661 var prevElem = angular.element(b2bDOMHelper.previousElement(elem));
\r
4662 if (!angular.isDefined(prevElem) || prevElem.length === 0) {
\r
4666 currentIndexSet.elementIndex -= 1;
\r
4667 currentIndexSet.listboxDataIndex = parseInt(prevElem.attr('data-index'), 10);
\r
4668 scope.currentIndex = currentIndexSet.listboxDataIndex;
\r
4670 if (currentIndexSet.elementIndex <= 0) {
\r
4671 currentIndexSet.elementIndex = 0;
\r
4675 var focusOnElement = function(index) {
\r
4677 elements[index].focus();
\r
4681 function selectItems(startIndex, endIndex, forceValue) {
\r
4682 for (var i = startIndex; i < endIndex; i++) {
\r
4683 if (forceValue === undefined) {
\r
4684 // We will flip the value
\r
4685 scope.listboxData[i].selected = !scope.listboxData[i].selected;
\r
4687 scope.listboxData[i].selected = forceValue;
\r
4691 if (!scope.$$phase) {
\r
4696 elem.bind('focus', function(evt) {
\r
4697 // If multiselectable or not and nothing is selected, put focus on first element
\r
4698 // If multiselectable and a range is set, put focus on first element of range
\r
4699 // If not multiselectable and something selected, put focus on element
\r
4700 elements = elem.children();
\r
4701 var selectedItems = scope.listboxData.filter(isTrue);
\r
4702 var elementsIndies = Array.prototype.map.call(elements, function(item) {
\r
4703 return parseInt(angular.element(item).attr('data-index'), 10);
\r
4706 if (selectedItems.length == 0) {
\r
4707 focusOnElement(0);
\r
4708 currentIndexSet.listboxDataIndex = 0;
\r
4709 } else if (attr.ariaMultiselectable) {
\r
4710 var index = scope.listboxData.indexOf(selectedItems[0]);
\r
4711 var indies = elementsIndies.filter(function(item) {
\r
4712 return (item === index);
\r
4715 if (indies.length === 0 || indies[0] != index) {
\r
4716 // Set focused on 0
\r
4717 currentIndexSet.elementIndex = elementsIndies[0];
\r
4718 currentIndexSet.listboxDataIndex = 0;
\r
4719 focusOnElement(currentIndexSet.elementIndex);
\r
4721 focusOnElement(indies[0]);
\r
4722 currentIndexSet.elementIndex = indies[0];
\r
4723 currentIndexSet.listboxDataIndex = index;
\r
4726 focusOnElement(currentIndexSet.elementIndex);
\r
4728 scope.currentIndex = currentIndexSet.listboxDataIndex;
\r
4730 if (!scope.$$phase) {
\r
4734 elem.bind('keyup', function(evt) {
\r
4735 if (evt.keyCode === keymap.KEY.SHIFT) {
\r
4736 shiftKeyPressed = false;
\r
4737 } else if (evt.keyCode === keymap.KEY.CTRL) {
\r
4738 ctrlKeyPressed = false;
\r
4742 elem.bind('keydown', function(evt) {
\r
4743 var keyCode = evt.keyCode;
\r
4744 elements = elem.children();
\r
4745 if (keyCode === keymap.KEY.SHIFT) {
\r
4746 shiftKeyPressed = true;
\r
4747 } else if (evt.keyCode === keymap.KEY.CTRL) {
\r
4748 ctrlKeyPressed = true;
\r
4754 if (scope.multiselectable && evt.ctrlKey) {
\r
4755 var arr = scope.listboxData.filter(isTrue);
\r
4756 var elementsIndies = Array.prototype.map.call(elements, function(item) {
\r
4757 return parseInt(angular.element(item).attr('data-index'), 10);
\r
4759 var val = !(arr.length === scope.listboxData.length);
\r
4760 for (var i = 0; i < elementsIndies.length; i++) {
\r
4761 scope.listboxData[elementsIndies[i]].selected = val;
\r
4764 if (!scope.$$phase) {
\r
4768 evt.preventDefault();
\r
4769 evt.stopPropagation();
\r
4773 case keymap.KEY.END:
\r
4775 if (scope.multiselectable && evt.ctrlKey && evt.shiftKey) {
\r
4776 var elementsIndies = Array.prototype.map.call(elements, function(item) {
\r
4777 return parseInt(angular.element(item).attr('data-index'), 10);
\r
4778 }).filter(function(item) {
\r
4779 return (item >= currentIndexSet.listboxDataIndex);
\r
4781 for (var i = 0; i < elementsIndies.length; i++) {
\r
4782 scope.listboxData[elementsIndies[i]].selected = true;
\r
4784 evt.preventDefault();
\r
4785 evt.stopPropagation();
\r
4787 if (!scope.$$phase) {
\r
4793 case keymap.KEY.HOME:
\r
4795 if (scope.multiselectable && evt.ctrlKey && evt.shiftKey) {
\r
4796 selectItems(0, currentIndexSet.listboxDataIndex+1, true); // currentIndex+1 is what is being focused on
\r
4797 evt.preventDefault();
\r
4798 evt.stopPropagation();
\r
4802 case keymap.KEY.LEFT:
\r
4803 case keymap.KEY.UP:
\r
4805 if (currentIndexSet.listboxDataIndex === 0) {
\r
4806 evt.preventDefault();
\r
4807 evt.stopPropagation();
\r
4811 decrementIndex(elements.eq(currentIndexSet.elementIndex));
\r
4812 if (scope.multiselectable && (evt.shiftKey || evt.ctrlKey)) {
\r
4813 if (evt.shiftKey) {
\r
4814 if (prevDirection === 'DOWN') {
\r
4815 scope.listboxData[currentIndexSet.listboxDataIndex+1].selected = !scope.listboxData[currentIndexSet.listboxDataIndex+1].selected;
\r
4817 scope.listboxData[currentIndexSet.listboxDataIndex].selected = !scope.listboxData[currentIndexSet.listboxDataIndex].selected;
\r
4819 prevDirection = 'UP';
\r
4821 // If no modifier keys are selected, all other items need to be unselected.
\r
4822 prevDirection = undefined;
\r
4823 selectItems(0, scope.listboxData.length, false);
\r
4824 scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
\r
4826 focusOnElement(currentIndexSet.elementIndex);
\r
4827 if(!scope.$$phase) {
\r
4830 evt.preventDefault();
\r
4831 evt.stopPropagation();
\r
4834 case keymap.KEY.RIGHT:
\r
4835 case keymap.KEY.DOWN:
\r
4837 if (currentIndexSet.listboxDataIndex === scope.listboxData.length-1) {
\r
4838 evt.preventDefault();
\r
4839 evt.stopPropagation();
\r
4843 incrementIndex(elements.eq(currentIndexSet.elementIndex));
\r
4845 if (scope.multiselectable && (evt.shiftKey || evt.ctrlKey)) {
\r
4846 if (evt.shiftKey) {
\r
4847 if (prevDirection === 'UP') {
\r
4848 scope.listboxData[currentIndexSet.listboxDataIndex-1].selected = !scope.listboxData[currentIndexSet.listboxDataIndex-1].selected;
\r
4851 scope.listboxData[currentIndexSet.listboxDataIndex].selected = !scope.listboxData[currentIndexSet.listboxDataIndex].selected;
\r
4853 prevDirection = 'DOWN';
\r
4855 // If no modifier keys are selected, all other items need to be unselected.
\r
4856 prevDirection = undefined;
\r
4857 selectItems(0, scope.listboxData.length, false);
\r
4858 scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
\r
4861 focusOnElement(currentIndexSet.elementIndex);
\r
4862 if(!scope.$$phase) {
\r
4865 evt.preventDefault();
\r
4866 evt.stopPropagation();
\r
4869 case keymap.KEY.TAB:
\r
4870 if(evt.shiftKey) {
\r
4871 var previousElement = b2bDOMHelper.previousElement(elem.parent().parent(), true);
\r
4872 evt.preventDefault();
\r
4873 previousElement.focus();
\r
4881 elem.bind('click', function(evt) {
\r
4882 var index = parseInt(evt.target.dataset.index, 10);
\r
4883 if (index === undefined || isNaN(index)) {
\r
4886 if (scope.multiselectable && currentIndexSet.listboxDataIndex !== undefined) {
\r
4887 if (shiftKeyPressed) {
\r
4888 var min = Math.min(index, currentIndexSet.listboxDataIndex);
\r
4889 var max = Math.max(index, currentIndexSet.listboxDataIndex);
\r
4891 if (index === min) { // clicking up
\r
4892 var firstIndex = scope.listboxData.findIndex(function(item) { return item.selected === true;});
\r
4893 // Given the firstIndex, let's find the matching element to get proper element match
\r
4894 elements = elem.children();
\r
4895 elements.eq(firstIndex)
\r
4896 var elementsThatMatch = Array.prototype.filter.call(elements, function(item) {
\r
4897 if (parseInt(angular.element(item).attr('data-index'), 10) === firstIndex) {
\r
4901 firstIndex = parseInt(angular.element(elementsThatMatch).attr('data-index'), 10);
\r
4903 if (index <= firstIndex && scope.listboxData.filter(isTrue).length > 1) {
\r
4904 // Break the selection into 2
\r
4905 selectItems(firstIndex + 1, max + 1, undefined); // + 1 needed because selectItems only selects up to MAX
\r
4906 selectItems(min, firstIndex, undefined);
\r
4907 } else if (scope.listboxData.filter(isTrue).length == 1){
\r
4908 selectItems(min, max, undefined);
\r
4910 selectItems(min + 1, max + 1, undefined);
\r
4912 } else { // clicking down
\r
4913 selectItems(min + 1, max + 1, scope.listboxData[min].selected);
\r
4915 } else if (ctrlKeyPressed) {
\r
4916 scope.listboxData[index].selected = !scope.listboxData[index].selected;
\r
4918 selectItems(0, scope.listboxData.length, false);
\r
4919 scope.listboxData[index].selected = !scope.listboxData[index].selected;
\r
4922 selectItems(0, scope.listboxData.length, false);
\r
4923 scope.listboxData[index].selected = !scope.listboxData[index].selected;
\r
4925 currentIndexSet.elementIndex = index;
\r
4926 currentIndexSet.listboxDataIndex = index;
\r
4927 scope.currentIndex = currentIndexSet.listboxDataIndex;
\r
4928 if (!scope.$$phase) {
\r
4931 focusOnElement(index);
\r
4937 * @ngdoc directive
\r
4938 * @name Videos, audio & animation.att:loaderAnimation
\r
4941 * <file src="src/loaderAnimation/docs/readme.md" />
\r
4944 * <!-- Below demo js shows-->
\r
4945 * Angular library uses Global.css's icon-primary-spinner.
\r
4948 * <section id="code">
\r
4949 <example module="b2b.att">
\r
4950 <file src="src/loaderAnimation/docs/demo.html" />
\r
4951 <file src="src/loaderAnimation/docs/demo.js" />
\r
4956 angular.module('b2b.att.loaderAnimation', [])
\r
4957 .constant('b2bSpinnerConfig', {
\r
4958 loadingText: 'Loading...',
\r
4959 startEvent: 'startButtonSpinner',
\r
4960 stopEvent: 'stopButtonSpinner'
\r
4962 .constant("progressTrackerConfig", {
\r
4963 loadingText: 'Loading...',
\r
4965 activationDelay: "",
\r
4966 minDurationPromise: "",
\r
4967 activationDelayPromise: ""
\r
4970 .provider('progressTracker', function () {
\r
4971 this.$get = ['$q', '$timeout', function ($q, $timeout) {
\r
4972 function cancelTimeout(promise) {
\r
4974 $timeout.cancel(promise);
\r
4977 return function ProgressTracker(options) {
\r
4978 //do new if user doesn't
\r
4979 if (!(this instanceof ProgressTracker)) {
\r
4980 return new ProgressTracker(options);
\r
4983 options = options || {};
\r
4984 //Array of promises being tracked
\r
4987 //Allow an optional "minimum duration" that the tracker has to stay active for.
\r
4988 var minDuration = options.minDuration;
\r
4989 //Allow a delay that will stop the tracker from activating until that time is reached
\r
4990 var activationDelay = options.activationDelay;
\r
4991 var minDurationPromise;
\r
4992 var activationDelayPromise;
\r
4993 self.active = function () {
\r
4994 //Even if we have a promise in our tracker, we aren't active until delay is elapsed
\r
4995 if (activationDelayPromise) {
\r
4998 return tracked.length > 0;
\r
5000 self.tracking = function () {
\r
5001 //Even if we aren't active, we could still have a promise in our tracker
\r
5002 return tracked.length > 0;
\r
5004 self.destroy = self.cancel = function () {
\r
5005 minDurationPromise = cancelTimeout(minDurationPromise);
\r
5006 activationDelayPromise = cancelTimeout(activationDelayPromise);
\r
5007 for (var i = tracked.length - 1; i >= 0; i--) {
\r
5008 tracked[i].resolve();
\r
5010 tracked.length = 0;
\r
5012 //Create a promise that will make our tracker active until it is resolved.
\r
5013 // @return deferred - our deferred object that is being tracked
\r
5014 self.createPromise = function () {
\r
5015 var deferred = $q.defer();
\r
5016 tracked.push(deferred);
\r
5017 //If the tracker was just inactive and this the first in the list of promises, we reset our delay and minDuration again.
\r
5018 if (tracked.length === 1) {
\r
5019 if (activationDelay) {
\r
5020 activationDelayPromise = $timeout(function () {
\r
5021 activationDelayPromise = cancelTimeout(activationDelayPromise);
\r
5022 startMinDuration();
\r
5023 }, activationDelay);
\r
5025 startMinDuration();
\r
5028 deferred.promise.then(onDone(false), onDone(true));
\r
5031 function startMinDuration() {
\r
5032 if (minDuration) {
\r
5033 minDurationPromise = $timeout(angular.noop, minDuration);
\r
5036 //Create a callback for when this promise is done. It will remove our tracked promise from the array if once minDuration is complete
\r
5037 function onDone() {
\r
5038 return function () {
\r
5039 (minDurationPromise || $q.when()).then(function () {
\r
5040 var index = tracked.indexOf(deferred);
\r
5041 tracked.splice(index, 1);
\r
5042 //If this is the last promise, cleanup the timeouts for activationDelay
\r
5043 if (tracked.length === 0) {
\r
5044 activationDelayPromise = cancelTimeout(activationDelayPromise);
\r
5050 self.addPromise = function (promise) {
\r
5052 // we cannot assign then function in other var and then add the resolve and reject
\r
5053 var thenFxn = promise && (promise.then || promise.$then || (promise.$promise && promise.$promise.then));
\r
5055 throw new Error("progressTracker expects a promise object :: Not found");
\r
5057 var deferred = self.createPromise();
\r
5058 //When given promise is done, resolve our created promise
\r
5059 //Allow $then for angular-resource objects
\r
5061 promise.then(function (value) {
\r
5062 deferred.resolve(value);
\r
5064 }, function (value) {
\r
5065 deferred.reject(value);
\r
5066 return $q.reject(value);
\r
5075 .config(['$httpProvider', function ($httpProvider) {
\r
5076 $httpProvider.interceptors.push(['$q', 'progressTracker', function ($q) {
\r
5078 request: function (config) {
\r
5079 if (config.tracker) {
\r
5080 if (!angular.isArray(config.tracker)) {
\r
5081 config.tracker = [config.tracker];
\r
5083 config.$promiseTrackerDeferred = config.$promiseTrackerDeferred || [];
\r
5085 angular.forEach(config.tracker, function (tracker) {
\r
5086 var deferred = tracker.createPromise();
\r
5087 config.$promiseTrackerDeferred.push(deferred);
\r
5090 return $q.when(config);
\r
5092 response: function (response) {
\r
5093 if (response.config && response.config.$promiseTrackerDeferred) {
\r
5094 angular.forEach(response.config.$promiseTrackerDeferred, function (deferred) {
\r
5095 deferred.resolve(response);
\r
5098 return $q.when(response);
\r
5100 responseError: function (response) {
\r
5101 if (response.config && response.config.$promiseTrackerDeferred) {
\r
5102 angular.forEach(response.config.$promiseTrackerDeferred, function (deferred) {
\r
5103 deferred.reject(response);
\r
5106 return $q.reject(response);
\r
5112 .directive('b2bClickSpin', ['$timeout', '$parse', '$rootScope', 'progressTracker', function ($timeout, $parse, $rootScope, progressTracker) {
\r
5115 link: function (scope, elm, attrs) {
\r
5116 var fn = $parse(attrs.b2bClickSpin);
\r
5117 elm.on('click', function (event) {
\r
5118 var promise = $timeout(function () {console.log("inside Promise")}, 5000);
\r
5119 scope.$apply(function () {
\r
5124 //comment this line if not running unit test
\r
5125 $rootScope.loadingTracker = progressTracker({
\r
5128 $rootScope.loadingTracker.addPromise(promise);
\r
5129 angular.forEach("$routeChangeSuccess $viewContentLoaded $locationChangeSuccess".split(" "), function (event) {
\r
5130 $rootScope.$on(event, function () {
\r
5132 $timeout.cancel(promise);
\r
5140 .directive('b2bProgressTracker', ['progressTrackerConfig', function (ptc) {
\r
5144 template: '<div><div ng-show="loadingTracker.active()" style="width:100%; text-align:center"><i class=\"icon-primary-spinner\"></i></div><div ng-show="loadingTracker.active()" style="width:100%;margin-top:10px; text-align:center">'+ ptc.loadingText+'</div></div>'
\r
5148 .directive('b2bLoadButton', ['b2bSpinnerConfig', '$timeout', function (spinnerConfig, $timeout) {
\r
5149 var spinButton = function (state, element, data) {
\r
5151 var attr = element.html() ? 'html' : 'val';
\r
5152 state = state + 'Text';
\r
5153 if (state === 'loadingText') {
\r
5154 element[attr](data[state]);
\r
5155 element.attr("disabled",'disabled');
\r
5156 element.addClass('disabled');
\r
5157 } else if (state === 'resetText') {
\r
5158 element[attr](data[state]);
\r
5159 element.removeAttr("disabled");
\r
5160 element.removeClass('disabled');
\r
5168 promise: '=promise',
\r
5169 startEvent: '@startEvent',
\r
5170 stopEvent: '@stopEvent'
\r
5172 link: function (scope, element, attr) {
\r
5173 var validAttr = element.html() ? 'html' : 'val';
\r
5179 var updateLoadingText = function (val) {
\r
5180 var loadingText = val;
\r
5181 if (!angular.isDefined(loadingText) || loadingText === "") {
\r
5182 loadingText = spinnerConfig.loadingText;
\r
5184 data.loadingText = validAttr === 'html' ? "<i class=\"icon-primary-spinner small\"></i>" + loadingText : loadingText;
\r
5186 var updateResetText = function (val) {
\r
5187 data.resetText = val;
\r
5190 attr.$observe('b2bLoadButton', function (val) {
\r
5191 updateLoadingText(val);
\r
5193 $timeout(function () {
\r
5194 updateResetText(element[validAttr]());
\r
5197 if (!angular.isDefined(scope.startEvent) || scope.startEvent === "") {
\r
5198 scope.startEvent = spinnerConfig.startEvent;
\r
5201 if (!angular.isDefined(scope.stopEvent) || scope.stopEvent === "") {
\r
5202 scope.stopEvent = spinnerConfig.stopEvent;
\r
5205 scope.$watch('promise', function () {
\r
5206 if (angular.isDefined(scope.promise) && angular.isFunction(scope.promise.then)) {
\r
5207 spinButton('loading', element, data);
\r
5208 scope.promise.then(function () {
\r
5209 spinButton('reset', element, data);
\r
5211 spinButton('reset', element, data);
\r
5216 scope.$on(scope.startEvent, function () {
\r
5217 spinButton('loading', element, data);
\r
5218 scope.$on(scope.stopEvent, function () {
\r
5219 spinButton('reset', element, data);
\r
5229 * @ngdoc directive
\r
5230 * @name Misc.att:messageWrapper
\r
5232 * @param {boolean} trigger - A boolean that triggers directive to switch focus
\r
5233 * @param {integer} delay - Extra delay added to trigger code to allow for DOM to be ready. Default is 10ms.
\r
5234 * @param {string} noFocus - Attribute-based API to trigger whether first focusable element receives focus on trigger or whole message (assumes tabindex="-1" set on first child)
\r
5235 * @param {string} trapFocus - Attribute-based API to trap focus within the message. This should be enabled by default on all toast messages.
\r
5237 * <file src="src/messageWrapper/docs/readme.md" />
\r
5239 * <b2b-message-wrapper>Code that contains at least one focusable element and will be shown/hidden on some logic. This must have tabindex="-1".</b2b-message-wrapper>
\r
5242 * <section id="code">
\r
5243 <b>HTML + AngularJS</b>
\r
5244 <example module="b2b.att">
\r
5245 <file src="src/messageWrapper/docs/demo.html" />
\r
5246 <file src="src/messageWrapper/docs/demo.js" />
\r
5251 angular.module('b2b.att.messageWrapper', ['b2b.att.utilities'])
\r
5252 .directive('b2bMessageWrapper', ['b2bDOMHelper', '$compile', '$timeout', '$log', function(b2bDOMHelper, $compile, $timeout, $log) {
\r
5261 template: '<div ng-transclude></div>',
\r
5262 link: function(scope, elem, attrs) {
\r
5263 scope.delay = scope.delay || 10;
\r
5265 if (attrs.trapFocus != undefined && !elem.children().eq(0).attr('b2b-trap-focus-inside-element')) {
\r
5266 // Append b2bTrapFocusInsideElement onto first child and recompile
\r
5267 elem.children().eq(0).attr('b2b-trap-focus-inside-element', 'false');
\r
5268 elem.children().eq(0).attr('trigger', scope.trigger);
\r
5269 $compile(elem.contents())(scope);
\r
5272 var firstElement = undefined,
\r
5273 launchingElement = undefined;
\r
5275 scope.$watch('trigger', function(oldVal, newVal) {
\r
5276 if (oldVal === newVal) return;
\r
5277 if (!angular.isDefined(launchingElement)) {
\r
5278 launchingElement = document.activeElement;
\r
5280 $timeout(function() {
\r
5281 if (scope.trigger) {
\r
5283 if (attrs.noFocus === true || attrs.noFocus === "") {
\r
5284 elem.children()[0].focus();
\r
5286 firstElement = b2bDOMHelper.firstTabableElement(elem);
\r
5288 if (angular.isDefined(firstElement)) {
\r
5289 firstElement.focus();
\r
5294 if (angular.isDefined(launchingElement) && launchingElement.nodeName !== 'BODY') {
\r
5295 if (launchingElement === document.activeElement) {
\r
5299 if (b2bDOMHelper.isInDOM(launchingElement) && b2bDOMHelper.isTabable(launchingElement)) {
\r
5300 // At this point, launchingElement is still a valid element, but focus will fail and
\r
5301 // activeElement will become body, hence we want to apply custom logic and find previousElement
\r
5302 var prevLaunchingElement = launchingElement;
\r
5303 launchingElement.focus();
\r
5305 if (document.activeElement !== launchingElement || document.activeElement.nodeName === 'BODY') {
\r
5306 launchingElement = b2bDOMHelper.previousElement(angular.element(prevLaunchingElement), true);
\r
5307 launchingElement.focus();
\r
5310 launchingElement = b2bDOMHelper.previousElement(launchingElement, true);
\r
5311 launchingElement.focus();
\r
5321 * @ngdoc directive
\r
5322 * @name Messages, modals & alerts.att:modalsAndAlerts
\r
5325 * <file src="src/modalsAndAlerts/docs/readme.md" />
\r
5328 * <button class="btn" b2b-modal="b2bTemplate/modalsAndAlerts/demo_modal.html" modal-ok="ok()" modal-cancel="cancel()">Launch demo modal</button>
\r
5331 * <section id="code">
\r
5332 <example module="b2b.att">
\r
5333 <file src="src/modalsAndAlerts/docs/demo.html" />
\r
5334 <file src="src/modalsAndAlerts/docs/demo.js" />
\r
5339 angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transition', 'b2b.att.utilities'])
\r
5342 * A helper, internal data structure that acts as a map but also allows getting / removing
\r
5343 * elements in the LIFO order
\r
5345 .factory('$$stackedMap', function () {
\r
5347 createNew: function () {
\r
5351 add: function (key, value) {
\r
5357 get: function (key) {
\r
5358 for (var i = 0; i < stack.length; i++) {
\r
5359 if (key === stack[i].key) {
\r
5364 keys: function () {
\r
5366 for (var i = 0; i < stack.length; i++) {
\r
5367 keys.push(stack[i].key);
\r
5371 top: function () {
\r
5372 return stack[stack.length - 1];
\r
5374 remove: function (key) {
\r
5376 for (var i = 0; i < stack.length; i++) {
\r
5377 if (key === stack[i].key) {
\r
5382 return stack.splice(idx, 1)[0];
\r
5384 removeTop: function () {
\r
5385 return stack.splice(stack.length - 1, 1)[0];
\r
5387 length: function () {
\r
5388 return stack.length;
\r
5393 }).factory('trapFocusInElement', ['$document', '$isElement', 'DOMHelper', 'keymap', function ($document, $isElement, DOMHelper, keymap) {
\r
5394 var elementStack = [];
\r
5395 var stackHead = undefined;
\r
5396 var firstTabableElement, lastTabableElement;
\r
5398 var trapKeyboardFocusInFirstElement = function (e) {
\r
5400 e.keyCode = e.which;
\r
5403 if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
\r
5404 lastTabableElement[0].focus();
\r
5405 e.preventDefault(e);
\r
5406 e.stopPropagation(e);
\r
5411 var trapKeyboardFocusInLastElement = function (e) {
\r
5413 e.keyCode = e.which;
\r
5416 if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
\r
5417 firstTabableElement[0].focus();
\r
5418 e.preventDefault(e);
\r
5419 e.stopPropagation(e);
\r
5423 var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
\r
5424 var bodyElements = $document.find('body').children();
\r
5426 firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(DOMHelper.firstTabableElement(stackHead));
\r
5427 lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(DOMHelper.lastTabableElement(stackHead));
\r
5430 for (var i = 0; i < bodyElements.length; i++) {
\r
5431 if (bodyElements[i] !== stackHead[0]) {
\r
5432 bodyElements.eq(i).attr('aria-hidden', true);
\r
5435 firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
\r
5436 lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
\r
5438 for (var j = 0; j < bodyElements.length; j++) {
\r
5439 if (bodyElements[j] !== stackHead[0]) {
\r
5440 bodyElements.eq(j).removeAttr('aria-hidden');
\r
5443 firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
\r
5444 lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
\r
5447 var toggleTrapFocusInElement = function (flag, element) {
\r
5448 if (angular.isDefined(flag) && angular.isDefined(element)) {
\r
5449 if (angular.isUndefined(stackHead)) {
\r
5450 stackHead = element;
\r
5451 trapFocusInElement(flag);
\r
5454 trapFocusInElement(false);
\r
5455 elementStack.push(stackHead);
\r
5456 stackHead = element;
\r
5457 trapFocusInElement(true);
\r
5459 if (stackHead.prop('$$hashKey') === element.prop('$$hashKey')) {
\r
5460 trapFocusInElement(false);
\r
5461 stackHead = elementStack.pop();
\r
5462 if (angular.isDefined(stackHead)) {
\r
5463 trapFocusInElement(true);
\r
5469 if (angular.isDefined(stackHead)) {
\r
5470 trapFocusInElement(false, firstTabableElement, lastTabableElement);
\r
5471 trapFocusInElement(true);
\r
5476 return toggleTrapFocusInElement;
\r
5480 * A helper directive for the $modal service. It creates a backdrop element.
\r
5482 .directive('b2bModalBackdrop', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
\r
5486 templateUrl: 'b2bTemplate/modalsAndAlerts/b2b-backdrop.html',
\r
5487 link: function (scope, element, attrs) {
\r
5488 scope.close = function (evt) {
\r
5489 var modal = $modalStack.getTop();
\r
5490 if (modal && modal.value.backdrop && modal.value.backdrop !== 'static') {
\r
5491 evt.preventDefault();
\r
5492 evt.stopPropagation();
\r
5493 $modalStack.dismiss(modal.key, 'backdrop click');
\r
5500 .directive('b2bModalWindow', ['$timeout', 'windowOrientation', '$window', function ($timeout, windowOrientation, $window) {
\r
5508 templateUrl: 'b2bTemplate/modalsAndAlerts/b2b-window.html',
\r
5509 controller: ['$scope', '$element', '$attrs', function (scope, element, attrs) {
\r
5510 scope.windowClass = attrs.windowClass || '';
\r
5511 scope.sizeClass = attrs.sizeClass || '';
\r
5512 scope.isNotifDialog = false;
\r
5514 this.setTitle = function (title) {
\r
5515 scope.title = title;
\r
5517 this.setContent = function (content) {
\r
5518 scope.content = content;
\r
5519 scope.isNotifDialog = true;
\r
5521 this.isDockedModal = scope.windowClass.indexOf('modal-docked') > -1;
\r
5523 link: function (scope, element, attrs, ctrl) {
\r
5524 if (ctrl.isDockedModal) {
\r
5525 scope.isModalLandscape = false;
\r
5527 var window = angular.element($window);
\r
5528 scope.updateCss = function () {
\r
5529 if (windowOrientation.isPotrait()) { // Potrait Mode
\r
5530 scope.isModalLandscape = false;
\r
5531 } else if (windowOrientation.isLandscape()) { // Landscape Mode
\r
5532 scope.isModalLandscape = true;
\r
5536 $timeout(function () {
\r
5537 scope.updateCss();
\r
5540 window.bind('orientationchange', function () {
\r
5541 scope.updateCss();
\r
5544 window.bind('resize', function () {
\r
5545 scope.updateCss();
\r
5549 angular.element(element[0].querySelectorAll(".awd-select-list")).css({
\r
5550 "max-height": "200px"
\r
5554 var isIE = /msie|trident/i.test(navigator.userAgent);
\r
5556 if(angular.element(element[0].querySelector('.corner-button button.close')).length > 0){
\r
5557 angular.element(element[0].querySelector('.corner-button button.close')).bind('focus', function () {
\r
5558 angular.element(element[0].querySelector('.b2b-modal-header'))[0].scrollLeft = 0;
\r
5559 angular.element(element[0].querySelector('.b2b-modal-header'))[0].scrollTop = 0;
\r
5568 .directive('b2bModalTitle', [function () {
\r
5571 require: '^b2bModalWindow',
\r
5572 link: function (scope, elem, attr, ctrl) {
\r
5573 ctrl.setTitle(attr.id);
\r
5578 .directive('b2bModalContent', [function () {
\r
5581 require: '^b2bModalWindow',
\r
5582 link: function (scope, elem, attr, ctrl) {
\r
5583 ctrl.setContent(attr.id);
\r
5589 .directive('b2bModalBody', ['$timeout', '$position', '$document', '$window', 'windowOrientation', 'b2bAwdBreakpoints', function ($timeout, $position, $document, $window, windowOrientation, b2bAwdBreakpoints) {
\r
5595 require: '^b2bModalWindow',
\r
5596 link: function (scope, element, attrs, ctrl) {
\r
5597 var window = angular.element($window);
\r
5598 var body = $document.find('body').eq(0);
\r
5599 scope.setModalHeight = function () {
\r
5600 var modalHeaderHeight, modalFooterHeight, modalBodyHeight, windowHeight, windowWidth, modalHeight;
\r
5601 modalHeaderHeight = 0;
\r
5602 modalFooterHeight = 0;
\r
5603 windowHeight = $window.innerHeight;
\r
5604 windowWidth = $window.innerWidth;
\r
5606 'height': windowHeight + 'px'
\r
5609 if (ctrl.isDockedModal) {
\r
5610 var modalElements = element.parent().children();
\r
5611 for (var i = 0; i < modalElements.length; i++) {
\r
5612 if (modalElements.eq(i).hasClass('b2b-modal-header')) {
\r
5613 modalHeaderHeight = $position.position(modalElements.eq(i)).height;
\r
5614 } else if (modalElements.eq(i).hasClass('b2b-modal-footer')) {
\r
5615 modalFooterHeight = $position.position(modalElements.eq(i)).height;
\r
5619 modalHeight = $position.position(element.parent()).height;
\r
5621 modalBodyHeight = modalHeight - (modalHeaderHeight + modalFooterHeight) + 'px';
\r
5623 if (windowOrientation.isPotrait()) { // Potrait Mode
\r
5624 element.removeAttr('style').css({
\r
5625 height: modalBodyHeight
\r
5627 } else if (windowOrientation.isLandscape() && windowWidth < b2bAwdBreakpoints.breakpoints.mobile.max) { // Landscape Mode Mobile
\r
5628 element.removeAttr('style');
\r
5629 } else if (windowOrientation.isLandscape() && windowWidth >= b2bAwdBreakpoints.breakpoints.mobile.max) { // Landscape Mode Non-Mobile
\r
5630 element.removeAttr('style').css({
\r
5631 height: modalBodyHeight
\r
5637 $timeout(function () {
\r
5638 scope.setModalHeight();
\r
5641 window.bind('orientationchange', function () {
\r
5642 scope.setModalHeight();
\r
5645 window.bind('resize', function () {
\r
5646 scope.setModalHeight();
\r
5653 .directive('b2bModalFooter', ['windowOrientation', '$window', function (windowOrientation, $window) {
\r
5659 link: function (scope, element, attrs) {
\r
5665 .factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap', '$log', '$timeout', 'trapFocusInElement', function ($document, $compile, $rootScope, $$stackedMap, $log, $timeout, trapFocusInElement) {
\r
5666 var backdropjqLiteEl, backdropDomEl;
\r
5667 var backdropScope = $rootScope.$new(true);
\r
5668 var body = $document.find('body').eq(0);
\r
5669 var html = $document.find('html').eq(0);
\r
5670 var openedWindows = $$stackedMap.createNew();
\r
5671 var $modalStack = {};
\r
5673 function backdropIndex() {
\r
5674 var topBackdropIndex = -1;
\r
5675 var opened = openedWindows.keys();
\r
5676 for (var i = 0; i < opened.length; i++) {
\r
5677 if (openedWindows.get(opened[i]).value.backdrop) {
\r
5678 topBackdropIndex = i;
\r
5681 return topBackdropIndex;
\r
5684 $rootScope.$watch(backdropIndex, function (newBackdropIndex) {
\r
5685 backdropScope.index = newBackdropIndex;
\r
5688 function removeModalWindow(modalInstance) {
\r
5689 //background scroll fix
\r
5690 html.removeAttr('style');
\r
5691 body.removeAttr('style');
\r
5692 body.removeClass('styled-by-modal');
\r
5694 var modalWindow = openedWindows.get(modalInstance).value;
\r
5695 trapFocusInElement(false, modalWindow.modalDomEl);
\r
5697 //clean up the stack
\r
5698 openedWindows.remove(modalInstance);
\r
5700 //remove window DOM element
\r
5701 modalWindow.modalDomEl.remove();
\r
5703 //remove backdrop if no longer needed
\r
5704 if (backdropDomEl && backdropIndex() === -1) {
\r
5705 backdropDomEl.remove();
\r
5706 backdropDomEl = undefined;
\r
5710 modalWindow.modalScope.$destroy();
\r
5713 $document.bind('keydown', function (evt) {
\r
5716 if (evt.which === 27) {
\r
5717 modal = openedWindows.top();
\r
5718 if (modal && modal.value.keyboard) {
\r
5719 $rootScope.$apply(function () {
\r
5720 $modalStack.dismiss(modal.key);
\r
5726 $modalStack.open = function (modalInstance, modal) {
\r
5728 openedWindows.add(modalInstance, {
\r
5729 deferred: modal.deferred,
\r
5730 modalScope: modal.scope,
\r
5731 backdrop: modal.backdrop,
\r
5732 keyboard: modal.keyboard
\r
5735 var angularDomEl = angular.element('<div b2b-modal-window></div>');
\r
5736 angularDomEl.attr('window-class', modal.windowClass);
\r
5737 angularDomEl.attr('size-class', modal.sizeClass);
\r
5738 angularDomEl.attr('index', openedWindows.length() - 1);
\r
5739 angularDomEl.html(modal.content);
\r
5741 var modalDomEl = $compile(angularDomEl)(modal.scope);
\r
5742 openedWindows.top().value.modalDomEl = modalDomEl;
\r
5743 //background page scroll fix
\r
5745 'overflow-y': 'hidden'
\r
5748 'overflow-y': 'hidden',
\r
5750 'height': window.innerHeight + 'px'
\r
5752 body.addClass('styled-by-modal');
\r
5753 body.append(modalDomEl);
\r
5755 if (backdropIndex() >= 0 && !backdropDomEl) {
\r
5756 backdropjqLiteEl = angular.element('<div b2b-modal-backdrop></div>');
\r
5757 backdropDomEl = $compile(backdropjqLiteEl)(backdropScope);
\r
5758 body.append(backdropDomEl);
\r
5761 $timeout(function () {
\r
5763 if (modal.scope.$$childHead.isNotifDialog) {
\r
5764 angular.element(modalDomEl).find('button')[0].focus();
\r
5766 angular.element(modalDomEl)[0].focus();
\r
5768 trapFocusInElement(true, angular.element(modalDomEl).eq(0));
\r
5772 $modalStack.close = function (modalInstance, result) {
\r
5773 var modal = openedWindows.get(modalInstance);
\r
5775 modal.value.deferred.resolve(result);
\r
5776 removeModalWindow(modalInstance);
\r
5780 $modalStack.dismiss = function (modalInstance, reason) {
\r
5781 var modalWindow = openedWindows.get(modalInstance).value;
\r
5782 if (modalWindow) {
\r
5783 modalWindow.deferred.reject(reason);
\r
5784 removeModalWindow(modalInstance);
\r
5788 $modalStack.getTop = function () {
\r
5789 return openedWindows.top();
\r
5792 return $modalStack;
\r
5795 .provider('$modal', function () {
\r
5796 var $modalProvider = {
\r
5798 backdrop: true, //can be also false or 'static'
\r
5801 $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack', function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
\r
5804 function getTemplatePromise(options) {
\r
5805 return options.template ? $q.when(options.template) :
\r
5806 $http.get(options.templateUrl, {
\r
5807 cache: $templateCache
\r
5808 }).then(function (result) {
\r
5809 return result.data;
\r
5813 function getResolvePromises(resolves) {
\r
5814 var promisesArr = [];
\r
5815 angular.forEach(resolves, function (value, key) {
\r
5816 if (angular.isFunction(value) || angular.isArray(value)) {
\r
5817 promisesArr.push($q.when($injector.invoke(value)));
\r
5820 return promisesArr;
\r
5823 $modal.open = function (modalOptions) {
\r
5825 var modalResultDeferred = $q.defer();
\r
5826 var modalOpenedDeferred = $q.defer();
\r
5827 //prepare an instance of a modal to be injected into controllers and returned to a caller
\r
5828 var modalInstance = {
\r
5829 result: modalResultDeferred.promise,
\r
5830 opened: modalOpenedDeferred.promise,
\r
5831 close: function (result) {
\r
5832 $modalStack.close(modalInstance, result);
\r
5834 dismiss: function (reason) {
\r
5835 $modalStack.dismiss(modalInstance, reason);
\r
5839 //merge and clean up options
\r
5840 modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
\r
5841 modalOptions.resolve = modalOptions.resolve || {};
\r
5844 if (!modalOptions.template && !modalOptions.templateUrl) {
\r
5845 throw new Error('One of template or templateUrl options is required.');
\r
5848 var templateAndResolvePromise =
\r
5849 $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
\r
5852 templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
\r
5854 var modalScope = (modalOptions.scope || $rootScope).$new();
\r
5855 modalScope.$close = modalInstance.close;
\r
5856 modalScope.$dismiss = modalInstance.dismiss;
\r
5858 var ctrlInstance, ctrlLocals = {};
\r
5859 var resolveIter = 1;
\r
5862 if (modalOptions.controller) {
\r
5863 ctrlLocals.$scope = modalScope;
\r
5864 ctrlLocals.$modalInstance = modalInstance;
\r
5865 angular.forEach(modalOptions.resolve, function (value, key) {
\r
5866 ctrlLocals[key] = tplAndVars[resolveIter++];
\r
5869 ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
\r
5872 $modalStack.open(modalInstance, {
\r
5873 scope: modalScope,
\r
5874 deferred: modalResultDeferred,
\r
5875 content: tplAndVars[0],
\r
5876 backdrop: modalOptions.backdrop,
\r
5877 keyboard: modalOptions.keyboard,
\r
5878 windowClass: modalOptions.windowClass,
\r
5879 sizeClass: modalOptions.sizeClass
\r
5882 }, function resolveError(reason) {
\r
5883 modalResultDeferred.reject(reason);
\r
5886 templateAndResolvePromise.then(function () {
\r
5887 modalOpenedDeferred.resolve(true);
\r
5889 modalOpenedDeferred.reject(false);
\r
5892 return modalInstance;
\r
5899 return $modalProvider;
\r
5902 .directive("b2bModal", ["$modal", "$log", '$scrollTo', function ($modal, $log, $scrollTo) {
\r
5907 modalController: '@',
\r
5913 link: function (scope, elm, attr) {
\r
5914 elm.bind('click', function (ev) {
\r
5915 var currentPosition = ev.pageY - ev.clientY;
\r
5916 ev.preventDefault();
\r
5917 if (angular.isDefined(elm.attr("href")) && elm.attr("href") !== "") {
\r
5918 scope.b2bModal = elm.attr("href");
\r
5921 templateUrl: scope.b2bModal,
\r
5922 controller: scope.modalController,
\r
5923 windowClass: scope.windowClass,
\r
5924 sizeClass: scope.sizeClass
\r
5925 }).result.then(function (value) {
\r
5930 }, function (value) {
\r
5931 scope.modalCancel({
\r
5941 .directive("utilityFilter", ["$modal", "$log", '$scrollTo', function ($modal, $log, $scrollTo) {
\r
5945 utilityFilter: '@'
\r
5947 require: 'ngModel',
\r
5948 templateUrl: 'b2bTemplate/modal/u-filter.html',
\r
5949 link: function (scope, element, attribute, ctrl) {
\r
5950 //controller to be passed to $modal service
\r
5951 scope.options = angular.copy(scope.$parent.$eval(attribute.ngModel));
\r
5952 scope.$parent.$watch(attribute.ngModel, function (newVal, oldVal) {
\r
5953 if (newVal !== oldVal) {
\r
5954 scope.options = newVal;
\r
5957 var modalCtrl = function ($scope, options) {
\r
5958 $scope.options = angular.copy(options);
\r
5961 if (angular.isDefined(scope.utilityFilter)) {
\r
5962 scope.templateUrl = scope.utilityFilter;
\r
5964 scope.templateUrl = 'b2bTemplate/modal/u-filter-window.html';
\r
5966 element.bind('click', function (ev) {
\r
5967 var currentPosition = ev.pageY - ev.clientY;
\r
5969 templateUrl: scope.templateUrl,
\r
5970 controller: modalCtrl,
\r
5972 options: function () {
\r
5973 return scope.options;
\r
5976 }).result.then(function (value) {
\r
5977 ctrl.$setViewValue(value);
\r
5978 element[0].focus();
\r
5979 $scrollTo(0, currentPosition, 0);
\r
5981 element[0].focus();
\r
5982 $scrollTo(0, currentPosition, 0);
\r
5989 * @ngdoc directive
\r
5990 * @name Forms.att:monthSelector
\r
5993 * <file src="src/monthSelector/docs/readme.md" />
\r
5996 * <div b2b-monthpicker ng-model="dt" min="minDate" max="maxDate" mode="monthpicker"></div>
\r
5999 * <section id="code">
\r
6000 <example module="b2b.att">
\r
6001 <file src="src/monthSelector/docs/demo.html" />
\r
6002 <file src="src/monthSelector/docs/demo.js" />
\r
6007 angular.module('b2b.att.monthSelector', ['b2b.att.position', 'b2b.att.utilities'])
\r
6009 .constant('b2bMonthpickerConfig', {
\r
6010 dateFormat: 'MM/dd/yyyy',
\r
6012 monthFormat: 'MMMM',
\r
6013 yearFormat: 'yyyy',
\r
6014 dayHeaderFormat: 'EEEE',
\r
6015 dayTitleFormat: 'MMMM yyyy',
\r
6016 disableWeekend: false,
\r
6017 disableSunday: false,
\r
6018 disableDates: null,
\r
6019 onSelectClose: null,
\r
6026 legendMessage: null,
\r
6027 calendarDisabled: false,
\r
6029 orientation: 'left',
\r
6032 helperText: 'The date you selected is $date. Double tap to open calendar. Select a date to close the calendar.',
\r
6033 descriptionText: 'Use tab to navigate between previous button, next button and month. Use arrow keys to navigate between months. Use space or enter to select a month.',
\r
6034 MonthpickerEvalAttributes: ['dateFormat', 'dayFormat', 'monthFormat', 'yearFormat', 'dayHeaderFormat', 'dayTitleFormat', 'disableWeekend', 'disableSunday', 'startingDay', 'collapseWait', 'orientation','mode','id'],
\r
6035 MonthpickerWatchAttributes: ['min', 'max', 'due', 'from', 'legendIcon', 'legendMessage', 'ngDisabled'],
\r
6036 MonthpickerFunctionAttributes: ['disableDates', 'onSelectClose']
\r
6039 .factory('b2bMonthpickerService', ['b2bMonthpickerConfig', 'dateFilter', function (b2bMonthpickerConfig, dateFilter) {
\r
6040 var setAttributes = function (attr, elem) {
\r
6041 if (angular.isDefined(attr) && attr !== null && angular.isDefined(elem) && elem !== null) {
\r
6042 var attributes = b2bMonthpickerConfig.MonthpickerEvalAttributes.concat(b2bMonthpickerConfig.MonthpickerWatchAttributes, b2bMonthpickerConfig.MonthpickerFunctionAttributes);
\r
6043 for (var key in attr) {
\r
6044 var val = attr[key];
\r
6045 if (attributes.indexOf(key) !== -1 && angular.isDefined(val)) {
\r
6046 elem.attr(key.toSnakeCase(), key);
\r
6052 var bindScope = function (attr, scope) {
\r
6053 if (angular.isDefined(attr) && attr !== null && angular.isDefined(scope) && scope !== null) {
\r
6054 var evalFunction = function (key, val) {
\r
6055 scope[key] = scope.$parent.$eval(val);
\r
6058 var watchFunction = function (key, val) {
\r
6059 scope.$parent.$watch(val, function (value) {
\r
6060 scope[key] = value;
\r
6062 scope.$watch(key, function (value) {
\r
6063 scope.$parent[val] = value;
\r
6067 var evalAttributes = b2bMonthpickerConfig.MonthpickerEvalAttributes;
\r
6068 var watchAttributes = b2bMonthpickerConfig.MonthpickerWatchAttributes;
\r
6069 for (var key in attr) {
\r
6070 var val = attr[key];
\r
6071 if (evalAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
\r
6072 evalFunction(key, val);
\r
6073 } else if (watchAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
\r
6074 watchFunction(key, val);
\r
6081 setAttributes: setAttributes,
\r
6082 bindScope: bindScope
\r
6086 .controller('b2bMonthpickerController', ['$scope', '$attrs', 'dateFilter', '$element', '$position', 'b2bMonthpickerConfig', function ($scope, $attrs, dateFilter, $element, $position, dtConfig) {
\r
6088 date: getValue($attrs.dateFormat, dtConfig.dateFormat),
\r
6089 day: getValue($attrs.dayFormat, dtConfig.dayFormat),
\r
6090 month: getValue($attrs.monthFormat, dtConfig.monthFormat),
\r
6091 year: getValue($attrs.yearFormat, dtConfig.yearFormat),
\r
6092 dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat),
\r
6093 dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat),
\r
6094 disableWeekend: getValue($attrs.disableWeekend, dtConfig.disableWeekend),
\r
6095 disableSunday: getValue($attrs.disableSunday, dtConfig.disableSunday),
\r
6096 disableDates: getValue($attrs.disableDates, dtConfig.disableDates)
\r
6098 startingDay = getValue($attrs.startingDay, dtConfig.startingDay);
\r
6100 $scope.minDate = dtConfig.minDate ? $scope.resetTime(dtConfig.minDate) : null;
\r
6101 $scope.maxDate = dtConfig.maxDate ? $scope.resetTime(dtConfig.maxDate) : null;
\r
6102 $scope.dueDate = dtConfig.dueDate ? $scope.resetTime(dtConfig.dueDate) : null;
\r
6103 $scope.fromDate = dtConfig.fromDate ? $scope.resetTime(dtConfig.fromDate) : null;
\r
6104 $scope.legendIcon = dtConfig.legendIcon ? dtConfig.legendIcon : null;
\r
6105 $scope.legendMessage = dtConfig.legendMessage ? dtConfig.legendMessage : null;
\r
6106 $scope.ngDisabled = dtConfig.calendarDisabled ? dtConfig.calendarDisabled : null;
\r
6107 $scope.collapseWait = getValue($attrs.collapseWait, dtConfig.collapseWait);
\r
6108 $scope.orientation = getValue($attrs.orientation, dtConfig.orientation);
\r
6109 $scope.onSelectClose = getValue($attrs.onSelectClose, dtConfig.onSelectClose);
\r
6110 $scope.mode = getValue($attrs.mode, dtConfig.mode);
\r
6112 $scope.inline = $attrs.inline === 'true' ? true : dtConfig.inline;
\r
6114 function getValue(value, defaultValue) {
\r
6115 return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
\r
6118 function getDaysInMonth(year, month) {
\r
6119 return new Date(year, month, 0).getDate();
\r
6122 function getDates(startDate, n) {
\r
6123 var dates = new Array(n);
\r
6124 var current = startDate,
\r
6127 dates[i++] = new Date(current);
\r
6128 current.setDate(current.getDate() + 1);
\r
6133 this.updatePosition = function (b2bMonthpickerPopupTemplate) {
\r
6134 $scope.position = $position.offset($element);
\r
6135 if($element.find('input').length > 0 ){
\r
6136 $scope.position.top += $element.find('input').prop('offsetHeight');
\r
6138 $scope.position.top += $element.find('a').prop('offsetHeight');
\r
6141 if ($scope.orientation === 'right') {
\r
6142 $scope.position.left -= (((b2bMonthpickerPopupTemplate && b2bMonthpickerPopupTemplate.prop('offsetWidth')) || 290) - $element.find('input').prop('offsetWidth'));
\r
6146 function isSelected(dt) {
\r
6147 if (dt && angular.isDate($scope.currentDate) && compare(dt, $scope.currentDate) === 0) {
\r
6153 function isFromDate(dt) {
\r
6154 if (dt && angular.isDate($scope.fromDate) && compare(dt, $scope.fromDate) === 0) {
\r
6160 function isDateRange(dt) {
\r
6161 if (dt && $scope.fromDate && angular.isDate($scope.currentDate) && (compare(dt, $scope.fromDate) >= 0) && (compare(dt, $scope.currentDate) <= 0)) {
\r
6163 } else if (dt && $scope.fromDate && compare(dt, $scope.fromDate) === 0) {
\r
6169 function isOld(date, currentMonthDate) {
\r
6170 if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() < new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
\r
6177 function isNew(date, currentMonthDate) {
\r
6178 if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() > new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
\r
6185 function isPastDue(dt) {
\r
6186 if ($scope.dueDate) {
\r
6187 return (dt > $scope.dueDate);
\r
6192 function isDueDate(dt) {
\r
6193 if ($scope.dueDate) {
\r
6194 return (dt.getTime() === $scope.dueDate.getTime());
\r
6199 var isDisabled = function (date, currentMonthDate) {
\r
6200 if ($attrs.from && !angular.isDate($scope.fromDate)) {
\r
6203 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
\r
6206 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
\r
6209 if (isOld(date, currentMonthDate) || isNew(date, currentMonthDate)) {
\r
6212 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
\r
6217 var isDisabledMonth = function (date, currentMonthDate) {
\r
6218 if ($attrs.from && !angular.isDate($scope.fromDate)) {
\r
6221 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
\r
6224 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
\r
6227 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
\r
6232 var compare = function (date1, date2) {
\r
6233 return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
\r
6236 function isMinDateAvailable(startDate, endDate) {
\r
6237 if (($scope.minDate && $scope.minDate.getTime() >= startDate.getTime()) && ($scope.minDate.getTime() <= endDate.getTime())) {
\r
6238 $scope.disablePrev = true;
\r
6239 $scope.visibilityPrev = "hidden";
\r
6241 $scope.disablePrev = false;
\r
6242 $scope.visibilityPrev = "visible";
\r
6246 function isMaxDateAvailable(startDate, endDate) {
\r
6247 if (($scope.maxDate && $scope.maxDate.getTime() >= startDate.getTime()) && ($scope.maxDate.getTime() <= endDate.getTime())) {
\r
6248 $scope.disableNext = true;
\r
6249 $scope.visibilityNext = "hidden";
\r
6251 $scope.disableNext = false;
\r
6252 $scope.visibilityNext = "visible";
\r
6256 function isYearInRange(currentYear) {
\r
6258 if ($scope.minDate && currentYear === $scope.minDate.getFullYear()) {
\r
6259 $scope.disablePrev = true;
\r
6260 $scope.visibilityPrev = "hidden";
\r
6262 $scope.disablePrev = false;
\r
6263 $scope.visibilityPrev = "visible";
\r
6266 if ($scope.maxDate && currentYear === $scope.maxDate.getFullYear()) {
\r
6267 $scope.disableNext = true;
\r
6268 $scope.visibilityNext = "hidden";
\r
6270 $scope.disableNext = false;
\r
6271 $scope.visibilityNext = "visible";
\r
6276 this.focusNextPrev = function(b2bMonthpickerPopupTemplate,init){
\r
6278 if (!$scope.disablePrev){
\r
6279 b2bMonthpickerPopupTemplate[0].querySelector('th.prev').focus();
\r
6280 }else if (!$scope.disableNext){
\r
6281 b2bMonthpickerPopupTemplate[0].querySelector('th.next').focus();
\r
6283 b2bMonthpickerPopupTemplate[0].querySelector('th.b2b-monthSelector-label').focus();
\r
6286 if ($scope.disableNext || $scope.disablePrev){
\r
6287 b2bMonthpickerPopupTemplate[0].querySelector('th.b2b-monthSelector-label').focus();
\r
6292 function getLabel(label) {
\r
6295 pre: label.substr(0, 1).toUpperCase(),
\r
6303 function makeDate(date, dayFormat, dayHeaderFormat, isSelected, isFromDate, isDateRange, isOld, isNew, isDisabled, dueDate, pastDue) {
\r
6306 label: dateFilter(date, dayFormat),
\r
6307 header: dateFilter(date, dayHeaderFormat),
\r
6308 selected: !!isSelected,
\r
6309 fromDate: !!isFromDate,
\r
6310 dateRange: !!isDateRange,
\r
6311 oldMonth: !!isOld,
\r
6312 nextMonth: !!isNew,
\r
6313 disabled: !!isDisabled,
\r
6314 dueDate: !!dueDate,
\r
6315 pastDue: !!pastDue,
\r
6316 focusable: !((isDisabled && !(isSelected || isDateRange)) || (isOld || isNew))
\r
6323 getVisibleDates: function (date) {
\r
6324 var year = date.getFullYear(),
\r
6325 month = date.getMonth(),
\r
6326 firstDayOfMonth = new Date(year, month, 1),
\r
6327 lastDayOfMonth = new Date(year, month + 1, 0);
\r
6328 var difference = startingDay - firstDayOfMonth.getDay(),
\r
6329 numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : -difference,
\r
6330 firstDate = new Date(firstDayOfMonth),
\r
6333 if (numDisplayedFromPreviousMonth > 0) {
\r
6334 firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
\r
6335 numDates += numDisplayedFromPreviousMonth; // Previous
\r
6337 numDates += getDaysInMonth(year, month + 1); // Current
\r
6338 numDates += (7 - numDates % 7) % 7; // Next
\r
6340 var days = getDates(firstDate, numDates),
\r
6341 labels = new Array(7);
\r
6342 for (var i = 0; i < numDates; i++) {
\r
6343 var dt = new Date(days[i]);
\r
6344 days[i] = makeDate(dt,
\r
6352 isDisabled(dt, date),
\r
6356 for (var j = 0; j < 7; j++) {
\r
6357 labels[j] = getLabel(dateFilter(days[j].date, format.dayHeader));
\r
6359 isMinDateAvailable(firstDayOfMonth, lastDayOfMonth);
\r
6360 isMaxDateAvailable(firstDayOfMonth, lastDayOfMonth);
\r
6363 title: dateFilter(date, format.dayTitle),
\r
6374 getVisibleDates: function(date) {
\r
6377 year = date.getFullYear();
\r
6378 for (var i = 0; i < 12; i++) {
\r
6379 var dt = new Date(year,i,1);
\r
6380 months[i] = makeDate(dt,
\r
6388 isDisabledMonth(dt, date),
\r
6392 isYearInRange(year);
\r
6393 return {objects: months, title: dateFilter(date, format.year), labels: labels};
\r
6401 .directive('b2bMonthpickerPopup', ['$parse', '$log', '$timeout', '$document', '$documentBind', '$isElement', '$templateCache', '$compile','$interval', 'trapFocusInElement', 'keymap', function ($parse, $log, $timeout, $document, $documentBind, $isElement, $templateCache, $compile, $interval,trapFocusInElement, keymap) {
\r
6409 templateUrl: function (elem, attr) {
\r
6410 if (attr.inline === 'true') {
\r
6411 return 'b2bTemplate/monthSelector/monthSelector-popup.html';
\r
6412 }else if (attr.link === 'true') {
\r
6413 return 'b2bTemplate/monthSelector/monthSelectorLink.html';
\r
6415 return 'b2bTemplate/monthSelector/monthSelector.html';
\r
6419 require: ['b2bMonthpickerPopup', 'ngModel', '?^b2bMonthpickerGroup'],
\r
6420 controller: 'b2bMonthpickerController',
\r
6421 link: function (scope, element, attrs, ctrls) {
\r
6422 var MonthpickerCtrl = ctrls[0],
\r
6423 ngModel = ctrls[1],
\r
6424 b2bMonthpickerGroupCtrl = ctrls[2];
\r
6425 var b2bMonthpickerPopupTemplate;
\r
6428 $log.error("ng-model is required.");
\r
6429 return; // do nothing if no ng-model
\r
6432 // Configuration parameters
\r
6433 var mode = scope.mode,
\r
6435 scope.isOpen = false;
\r
6437 scope.headers = [];
\r
6438 scope.footers = [];
\r
6439 scope.triggerInterval=undefined;
\r
6442 if (b2bMonthpickerGroupCtrl) {
\r
6443 b2bMonthpickerGroupCtrl.registerMonthpickerScope(scope);
\r
6446 element.bind('keydown', function (ev) {
\r
6447 if (!ev.keyCode) {
\r
6449 ev.keyCode = ev.which;
\r
6450 } else if (ev.charCode) {
\r
6451 ev.keyCode = ev.charCode;
\r
6454 if(ev.keyCode === keymap.KEY.ESC)
\r
6456 scope.isOpen = false;
\r
6457 toggleCalendar(scope.isOpen);
\r
6462 element.find('button').bind('click', function () {
\r
6466 element.find('a').bind('click', function () {
\r
6471 element.find('input').bind('click', function () {
\r
6475 var onClicked = function() {
\r
6476 if (!scope.ngDisabled) {
\r
6477 scope.isOpen = !scope.isOpen;
\r
6478 toggleCalendar(scope.isOpen);
\r
6479 MonthpickerCtrl.updatePosition(b2bMonthpickerPopupTemplate);
\r
6484 var toggleCalendar = function (flag) {
\r
6485 if (!scope.inline) {
\r
6487 b2bMonthpickerPopupTemplate = angular.element($templateCache.get('b2bTemplate/monthSelector/monthSelector-popup.html'));
\r
6488 b2bMonthpickerPopupTemplate.attr('b2b-trap-focus-inside-element', 'false');
\r
6489 b2bMonthpickerPopupTemplate.attr('trigger', 'true');
\r
6490 b2bMonthpickerPopupTemplate = $compile(b2bMonthpickerPopupTemplate)(scope);
\r
6491 $document.find('body').append(b2bMonthpickerPopupTemplate);
\r
6492 b2bMonthpickerPopupTemplate.bind('keydown', escPress);
\r
6493 $timeout(function () {
\r
6494 scope.getFocus = true;
\r
6497 $timeout(function () {
\r
6498 scope.getFocus = false;
\r
6500 MonthpickerCtrl.focusNextPrev(b2bMonthpickerPopupTemplate,true);
\r
6503 scope.triggerInterval = $interval(function () {
\r
6504 //This value is updated to trigger init() function of directive on year change.
\r
6505 scope.trigger=(scope.trigger === 0 ? 1 : 0);
\r
6509 b2bMonthpickerPopupTemplate.unbind('keydown', escPress);
\r
6510 if(scope.triggerInterval)
\r
6512 $interval.cancel(scope.triggerInterval);
\r
6513 scope.triggerInterval=undefined;
\r
6515 b2bMonthpickerPopupTemplate.remove();
\r
6516 if(element.find('button').length > 0){
\r
6517 element.find('button')[0].focus();
\r
6519 element.find('a')[0].focus();
\r
6522 scope.getFocus = false;
\r
6527 var outsideClick = function (e) {
\r
6528 var isElement = $isElement(angular.element(e.target), element, $document);
\r
6529 var isb2bMonthpickerPopupTemplate = $isElement(angular.element(e.target), b2bMonthpickerPopupTemplate, $document);
\r
6530 if (!(isElement || isb2bMonthpickerPopupTemplate)) {
\r
6531 scope.isOpen = false;
\r
6532 toggleCalendar(scope.isOpen);
\r
6537 var escPress = function (ev) {
\r
6538 if (!ev.keyCode) {
\r
6540 ev.keyCode = ev.which;
\r
6541 } else if (ev.charCode) {
\r
6542 ev.keyCode = ev.charCode;
\r
6546 if (ev.keyCode === keymap.KEY.ESC) {
\r
6547 scope.isOpen = false;
\r
6548 toggleCalendar(scope.isOpen);
\r
6549 ev.preventDefault();
\r
6550 ev.stopPropagation();
\r
6551 } else if (ev.keyCode === 33) {
\r
6552 !scope.disablePrev && scope.move(-1);
\r
6553 $timeout(function () {
\r
6554 scope.getFocus = true;
\r
6556 $timeout(function () {
\r
6557 scope.getFocus = false;
\r
6561 ev.preventDefault();
\r
6562 ev.stopPropagation();
\r
6563 } else if (ev.keyCode === 34) {
\r
6564 !scope.disableNext && scope.move(1);
\r
6565 $timeout(function () {
\r
6566 scope.getFocus = true;
\r
6568 $timeout(function () {
\r
6569 scope.getFocus = false;
\r
6573 ev.preventDefault();
\r
6574 ev.stopPropagation();
\r
6580 $documentBind.click('isOpen', outsideClick, scope);
\r
6582 scope.$on('$destroy', function () {
\r
6583 if (scope.isOpen) {
\r
6584 scope.isOpen = false;
\r
6585 toggleCalendar(scope.isOpen);
\r
6589 scope.resetTime = function (date) {
\r
6590 if (typeof date === 'string') {
\r
6591 date = date + 'T12:00:00';
\r
6594 if (!isNaN(new Date(date))) {
\r
6595 dt = new Date(date);
\r
6596 if(scope.mode === 1){
\r
6597 dt = new Date(dt.getFullYear(), dt.getMonth());
\r
6599 dt = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
\r
6604 return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
\r
6608 scope.$parent.$watch($parse(attrs.min), function (value) {
\r
6609 scope.minDate = value ? scope.resetTime(value) : null;
\r
6614 scope.$parent.$watch($parse(attrs.max), function (value) {
\r
6615 scope.maxDate = value ? scope.resetTime(value) : null;
\r
6620 scope.$parent.$watch($parse(attrs.due), function (value) {
\r
6621 scope.dueDate = value ? scope.resetTime(value) : null;
\r
6626 scope.$parent.$watch($parse(attrs.from), function (value) {
\r
6627 scope.fromDate = value ? scope.resetTime(value) : null;
\r
6632 if (attrs.legendIcon) {
\r
6633 scope.$parent.$watch(attrs.legendIcon, function (value) {
\r
6634 scope.legendIcon = value ? value : null;
\r
6638 if (attrs.legendMessage) {
\r
6639 scope.$parent.$watch(attrs.legendMessage, function (value) {
\r
6640 scope.legendMessage = value ? value : null;
\r
6644 if (attrs.ngDisabled) {
\r
6645 scope.$parent.$watch(attrs.ngDisabled, function (value) {
\r
6646 scope.ngDisabled = value ? value : null;
\r
6651 // Split array into smaller arrays
\r
6652 function split(arr, size) {
\r
6654 while (arr.length > 0) {
\r
6655 arrays.push(arr.splice(0, size));
\r
6660 var moveMonth = function(selectedDate, direction) {
\r
6661 var step = MonthpickerCtrl.modes[scope.mode].step;
\r
6662 selectedDate.setDate(1);
\r
6663 selectedDate.setMonth(selectedDate.getMonth() + direction * (step.months || 0));
\r
6664 selectedDate.setFullYear(selectedDate.getFullYear() + direction * (step.years || 0));
\r
6666 return selectedDate;
\r
6669 function refill(date) {
\r
6670 if (angular.isDate(date) && !isNaN(date)) {
\r
6671 selected = new Date(date);
\r
6674 selected = new Date();
\r
6679 var selectedCalendar;
\r
6680 if(scope.mode === 1){
\r
6681 if(!angular.isDate(selected))
\r
6683 selected = new Date();
\r
6685 selectedCalendar = moveMonth(angular.copy(selected), -1);
\r
6687 selectedCalendar = angular.copy(selected);
\r
6690 var currentMode = MonthpickerCtrl.modes[mode],
\r
6691 data = currentMode.getVisibleDates(selected);
\r
6693 scope.rows = split(data.objects, currentMode.split);
\r
6696 var startFlag=false;
\r
6697 var firstSelected = false;
\r
6698 for(var i=0; i<scope.rows.length; i++)
\r
6700 for(var j=0; j<scope.rows[i].length; j++)
\r
6702 if(!scope.rows[i][j].disabled && !firstSelected)
\r
6704 firstSelected=true;
\r
6705 var firstDay = scope.rows[i][j];
\r
6708 if(scope.rows[i][j].selected)
\r
6719 if(!flag && firstSelected)
\r
6721 firstDay.firstFocus=true;
\r
6724 scope.labels = data.labels || [];
\r
6725 scope.title = data.title;
\r
6729 scope.select = function (date,$event) {
\r
6730 var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate());
\r
6731 scope.currentDate = dt;
\r
6732 if (!scope.onSelectClose || (scope.onSelectClose && scope.onSelectClose({
\r
6735 if (angular.isNumber(scope.collapseWait)) {
\r
6736 $timeout(function () {
\r
6737 scope.isOpen = false;
\r
6738 toggleCalendar(scope.isOpen);
\r
6739 }, scope.collapseWait);
\r
6741 scope.isOpen = false;
\r
6742 toggleCalendar(scope.isOpen);
\r
6747 scope.move = function (direction,$event) {
\r
6748 var step = MonthpickerCtrl.modes[mode].step;
\r
6749 selected.setDate(1);
\r
6750 selected.setMonth(selected.getMonth() + direction * (step.months || 0));
\r
6751 selected.setFullYear(selected.getFullYear() + direction * (step.years || 0));
\r
6753 scope.getFocus = true;
\r
6754 $timeout(function () {
\r
6755 if (attrs.inline === 'true') {
\r
6756 MonthpickerCtrl.focusNextPrev(element,false);
\r
6758 MonthpickerCtrl.focusNextPrev(b2bMonthpickerPopupTemplate,false);
\r
6761 $event.preventDefault();
\r
6762 $event.stopPropagation();
\r
6765 scope.$watch('currentDate', function (value) {
\r
6766 if (angular.isDefined(value) && value !== null) {
\r
6771 ngModel.$setViewValue(value);
\r
6774 ngModel.$render = function () {
\r
6775 scope.currentDate = ngModel.$viewValue;
\r
6778 var stringToDate = function (value) {
\r
6779 if (!isNaN(new Date(value))) {
\r
6780 value = new Date(value);
\r
6784 ngModel.$formatters.unshift(stringToDate);
\r
6789 .directive('b2bMonthpicker', ['$compile', '$log', 'b2bMonthpickerConfig', 'b2bMonthpickerService', function ($compile, $log, b2bMonthpickerConfig, b2bMonthpickerService) {
\r
6793 disableDates: '&',
\r
6794 onSelectClose: '&'
\r
6796 require: 'ngModel',
\r
6797 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
\r
6798 var dateFormatString = angular.isDefined(attr.dateFormat) ? scope.$parent.$eval(attr.dateFormat) : b2bMonthpickerConfig.dateFormat;
\r
6799 var helperText = angular.isDefined(attr.helperText) ? scope.$parent.$eval(attr.helperText) : b2bMonthpickerConfig.helperText;
\r
6800 helperText = helperText.replace('$date', '{{dt | date : \'' + dateFormatString + '\'}}');
\r
6802 var descriptionText = angular.isDefined(attr.descriptionText) ? scope.$parent.$eval(attr.descriptionText) : b2bMonthpickerConfig.descriptionText;
\r
6805 var inline = false;
\r
6806 if (elem.prop('nodeName') !== 'INPUT' && elem.prop('nodeName') !== 'A') {
\r
6810 var selectedDateMessage = "";
\r
6812 if (elem.prop('nodeName') !== 'A'){
\r
6813 selectedDateMessage = '<button type="button" class="span12 faux-input" ng-disabled="ngDisabled" aria-describedby="monthpicker-description'+scope.$id+'"><span class="hidden-spoken" aria-live="assertive" aria-atomic="false">' + helperText + '</span></button>';
\r
6814 elem.attr('tabindex', '-1');
\r
6815 elem.attr('aria-hidden', 'true');
\r
6816 elem.attr('readonly', 'true');
\r
6818 selectedDateMessage = ''
\r
6819 elem.attr('aria-label', helperText);
\r
6822 var descriptionTextSpan = '<span class="offscreen-text" id="monthpicker-description'+scope.$id+'">'+descriptionText+'</span>';
\r
6823 elem.removeAttr('b2b-Monthpicker');
\r
6824 elem.removeAttr('ng-model');
\r
6825 elem.removeAttr('ng-disabled');
\r
6826 elem.addClass('Monthpicker-input');
\r
6827 elem.attr('ng-model', 'dt');
\r
6828 elem.attr('aria-describedby', 'monthpicker-description'+scope.$id);
\r
6832 elem.attr('ng-disabled', 'ngDisabled');
\r
6833 elem.attr('b2b-format-date', dateFormatString);
\r
6835 var wrapperElement = angular.element('<div></div>');
\r
6836 wrapperElement.attr('b2b-Monthpicker-popup', '');
\r
6837 wrapperElement.attr('ng-model', 'dt');
\r
6839 wrapperElement.attr('inline', inline);
\r
6841 if (elem.prop('nodeName') === 'A'){
\r
6842 wrapperElement.attr('link', true);
\r
6844 b2bMonthpickerService.setAttributes(attr, wrapperElement);
\r
6845 b2bMonthpickerService.bindScope(attr, scope);
\r
6847 wrapperElement.html('');
\r
6848 wrapperElement.append(selectedDateMessage);
\r
6849 wrapperElement.append('');
\r
6850 wrapperElement.append(descriptionTextSpan);
\r
6851 wrapperElement.append('');
\r
6852 wrapperElement.append(elem.prop('outerHTML'));
\r
6854 var elm = wrapperElement.prop('outerHTML');
\r
6855 elm = $compile(elm)(scope);
\r
6856 elem.replaceWith(elm);
\r
6858 link: function (scope, elem, attr, ctrl) {
\r
6860 $log.error("ng-model is required.");
\r
6861 return; // do nothing if no ng-model
\r
6864 scope.$watch('dt', function (value) {
\r
6865 ctrl.$setViewValue(value);
\r
6867 ctrl.$render = function () {
\r
6868 scope.dt = ctrl.$viewValue;
\r
6874 .directive('b2bMonthpickerGroup', [function () {
\r
6877 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
\r
6878 this.$$headers = [];
\r
6879 this.$$footers = [];
\r
6880 this.registerMonthpickerScope = function (MonthpickerScope) {
\r
6881 MonthpickerScope.headers = this.$$headers;
\r
6882 MonthpickerScope.footers = this.$$footers;
\r
6885 link: function (scope, elem, attr, ctrl) {}
\r
6889 .directive('b2bFormatDate', ['dateFilter', function (dateFilter) {
\r
6892 require: 'ngModel',
\r
6893 link: function (scope, elem, attr, ctrl) {
\r
6894 var b2bFormatDate = "";
\r
6895 attr.$observe('b2bFormatDate', function (value) {
\r
6896 b2bFormatDate = value;
\r
6898 var dateToString = function (value) {
\r
6899 if (!isNaN(new Date(value))) {
\r
6900 return dateFilter(new Date(value), b2bFormatDate);
\r
6904 ctrl.$formatters.unshift(dateToString);
\r
6909 .directive('b2bMonthpickerHeader', [function () {
\r
6912 require: '^b2bMonthpickerGroup',
\r
6916 compile: function (elem, attr, transclude) {
\r
6917 return function link(scope, elem, attr, ctrl) {
\r
6919 ctrl.$$headers.push(transclude(scope, function () {}));
\r
6927 .directive('b2bMonthpickerFooter', [function () {
\r
6930 require: '^b2bMonthpickerGroup',
\r
6934 compile: function (elem, attr, transclude) {
\r
6935 return function link(scope, elem, attr, ctrl) {
\r
6937 ctrl.$$footers.push(transclude(scope, function () {}));
\r
6945 * @ngdoc directive
\r
6946 * @name Navigation.att:multiLevelNavigation
\r
6949 * <file src="src/multiLevelNavigation/docs/readme.md" />
\r
6952 * <div class="b2b-ml-nav">
\r
6953 * <ul role="tree">
\r
6954 * <li aria-label="{{child.name}}" tabindex="-1" b2b-ml-nav="{{child.type}}" role="treeitem" ng-repeat="child in treeStructure">
\r
6955 * <a tabindex="-1" href="javascript:void(0);" title="{{child.name}}">{{child.name}}<span><i class="{{child.type=='endNode'?'icon-primary-circle':'icon-primary-collapsed'}}"></i></span></a>
\r
6956 * <!-- Below UL tag is RECURSIVE to generate n-childs -->
\r
6957 * <ul role="group" ng-if="child.child">
\r
6958 * <li aria-label="{{child.name}}" b2b-ml-nav="{{child.type}}" tabindex="-1" role="treeitem" ng-repeat="child in child.child">
\r
6959 * <a tabindex="-1" href="javascript:void(0);" title="{{child.name}}">{{child.name}}<span><i class="{{child.type=='endNode'?'icon-primary-circle':'icon-primary-collapsed'}}"></i></span></a>
\r
6960 * <!-- RECURSIVE UL tag goes here -->
\r
6968 * <section id="code">
\r
6969 <example module="b2b.att">
\r
6970 <file src="src/multiLevelNavigation/docs/demo.html" />
\r
6971 <file src="src/multiLevelNavigation/docs/demo.js" />
\r
6976 angular.module('b2b.att.multiLevelNavigation', ['b2b.att.utilities'])
\r
6977 //directive b2bMlNav Test coverage 100% on 5/13
\r
6978 .directive('b2bMlNav', ['keymap', function (keymap) {
\r
6981 link: function (scope, element) {
\r
6982 var rootE, parentE, upE, downE, lastE, homeE, endE;
\r
6983 //default root tree element tabindex set zero
\r
6984 if (element.parent().parent().hasClass('b2b-ml-nav') && (element[0].previousElementSibling === null)) {
\r
6985 element.attr('tabindex', 0);
\r
6987 //check root via class
\r
6988 var isRoot = function (elem) {
\r
6989 if (elem.parent().parent().eq(0).hasClass('b2b-ml-nav')) {
\r
6996 //for any expandable tree item on click
\r
6997 var toggleState = function (e) {
\r
6998 if (angular.element(e.target).attr("b2b-ml-nav") !== "endNode") {
\r
6999 var eLink = element.find('a').eq(0);
\r
7000 if (eLink.hasClass('active')) {
\r
7001 eLink.removeClass('active');
\r
7002 eLink.parent().attr("aria-expanded", "false");
\r
7003 eLink.find('i').eq(0).removeClass('icon-primary-expanded');
\r
7004 eLink.find('i').eq(0).addClass('icon-primary-collapsed');
\r
7006 eLink.addClass('active');
\r
7007 eLink.parent().attr("aria-expanded", "true");
\r
7008 eLink.find('i').eq(0).removeClass('icon-primary-collapsed');
\r
7009 eLink.find('i').eq(0).addClass('icon-primary-expanded');
\r
7013 //function finds the main root-item from particular tree-group
\r
7014 var findRoot = function (elem) {
\r
7015 if (isRoot(elem)) {
\r
7019 if (elem.attr("b2b-ml-nav") === "middleNode" || elem.attr("b2b-ml-nav") === "endNode") {
\r
7020 parentE = elem.parent().parent();
\r
7024 if (parentE.attr("b2b-ml-nav") === "rootNode") {
\r
7027 findRoot(parentE);
\r
7030 //finds the last visible node of the previous tree-group
\r
7031 var findPreActive = function (elem) {
\r
7032 if (!(elem.hasClass("active"))) {
\r
7035 var childElems = angular.element(elem[0].nextElementSibling.children);
\r
7036 lastE = angular.element(childElems[childElems.length - 1]);
\r
7037 if (lastE.attr("b2b-ml-nav") === "middleNode" && lastE.find('a').eq(0).hasClass('active')) {
\r
7038 findPreActive(lastE.find('a').eq(0));
\r
7043 //find above visible link
\r
7044 var findUp = function (elem) {
\r
7045 if (elem[0].previousElementSibling !== null) {
\r
7046 upE = angular.element(elem[0].previousElementSibling);
\r
7048 upE = elem.parent().parent();
\r
7050 if (isRoot(elem) || (upE.attr('b2b-ml-nav') === "middleNode" && upE[0] !== elem.parent().parent()[0])) {
\r
7051 findPreActive(upE.find('a').eq(0));
\r
7054 //find below visible link
\r
7055 var findDown = function (elem) {
\r
7056 if (elem.hasClass('active')) {
\r
7057 downE = elem.next().find('li').eq(0);
\r
7059 if (elem.parent().next().length !== 0) {
\r
7060 downE = elem.parent().next().eq(0);
\r
7062 if (elem.parent().parent().parent().next().length !== 0) {
\r
7063 downE = elem.parent().parent().parent().next().eq(0);
\r
7066 downE = elem.parent().eq(0);
\r
7070 //finds last root-group element of the tree
\r
7071 var findEnd = function (elem) {
\r
7073 endE = angular.element(rootE.parent()[0].children[rootE.parent()[0].children.length - 1]);
\r
7075 //finds first root element of tree
\r
7076 var findHome = function (elem) {
\r
7078 homeE = angular.element(rootE.parent()[0].children[0]);
\r
7080 element.bind('click', function (e) {
\r
7081 if(element.attr("b2b-ml-nav") !== "endNode") {
\r
7084 e.stopPropagation();
\r
7086 element.bind('focus', function (e) {
\r
7087 if(element.attr("b2b-ml-nav") !== "endNode") {
\r
7088 if(element.find('a').eq(0).hasClass('active')) {
\r
7089 element.attr("aria-expanded", true);
\r
7092 element.attr("aria-expanded", false);
\r
7097 //Keyboard functionality approach:
\r
7099 //set set tabindex -1 on the current focus element
\r
7100 //find the next element to be focussed, set tabindex 0 and throw focus
\r
7101 element.bind('keydown', function (evt) {
\r
7102 switch (evt.keyCode) {
\r
7103 case keymap.KEY.ENTER:
\r
7104 case keymap.KEY.SPACE:
\r
7105 element.triggerHandler('click');
\r
7106 evt.stopPropagation();
\r
7107 evt.preventDefault();
\r
7109 case keymap.KEY.END:
\r
7110 evt.preventDefault();
\r
7111 element.attr('tabindex', -1);
\r
7113 endE.eq(0).attr('tabindex', 0);
\r
7115 evt.stopPropagation();
\r
7117 case keymap.KEY.HOME:
\r
7118 evt.preventDefault();
\r
7119 element.attr('tabindex', -1);
\r
7120 findHome(element);
\r
7121 homeE.eq(0).attr('tabindex', 0);
\r
7123 evt.stopPropagation();
\r
7125 case keymap.KEY.LEFT:
\r
7126 evt.preventDefault();
\r
7127 if (!isRoot(element)) {
\r
7128 element.attr('tabindex', -1);
\r
7129 parentE = element.parent().parent();
\r
7130 parentE.eq(0).attr('tabindex', 0);
\r
7131 parentE[0].focus();
\r
7132 parentE.eq(0).triggerHandler('click');
\r
7134 if (element.find('a').eq(0).hasClass('active')) {
\r
7135 element.triggerHandler('click');
\r
7138 evt.stopPropagation();
\r
7140 case keymap.KEY.UP:
\r
7141 evt.preventDefault();
\r
7142 if (!(isRoot(element) && element[0].previousElementSibling === null)) {
\r
7143 element.attr('tabindex', -1);
\r
7145 upE.eq(0).attr('tabindex', 0);
\r
7148 evt.stopPropagation();
\r
7150 case keymap.KEY.RIGHT:
\r
7151 evt.preventDefault();
\r
7152 if (element.attr("b2b-ml-nav") !== "endNode") {
\r
7153 if (!element.find('a').eq(0).hasClass('active')) {
\r
7154 element.triggerHandler('click');
\r
7156 element.attr('tabindex', -1);
\r
7157 findDown(element.find('a').eq(0));
\r
7158 downE.eq(0).attr('tabindex', 0);
\r
7161 evt.stopPropagation();
\r
7163 case keymap.KEY.DOWN:
\r
7164 evt.preventDefault();
\r
7165 element.attr('tabindex', -1);
\r
7166 if (!(element.attr("b2b-ml-nav") === "middleNode" && element.find('a').eq(0).hasClass('active')) && (element.next().length === 0)) {
\r
7167 if(element.parent().parent().attr("b2b-ml-nav") !== "rootNode" && element.parent().parent()[0].nextElementSibling !== null)
\r
7169 findDown(element.find('a').eq(0));
\r
7170 downE.eq(0).attr('tabindex', 0);
\r
7172 evt.stopPropagation();
\r
7175 findRoot(element);
\r
7176 if (!(rootE.next().length === 0)) {
\r
7177 rootE.next().eq(0).attr('tabindex', 0);
\r
7178 rootE.next()[0].focus();
\r
7180 rootE.eq(0).attr('tabindex', 0);
\r
7183 evt.stopPropagation();
\r
7186 findDown(element.find('a').eq(0));
\r
7187 downE.eq(0).attr('tabindex', 0);
\r
7189 evt.stopPropagation();
\r
7199 * @ngdoc directive
\r
7200 * @name Tabs, tables & accordions.att:multipurposeExpander
\r
7203 * <file src="src/multipurposeExpander/docs/readme.md" />
\r
7206 * <!--With Close Other -->
\r
7207 * <b2b-expander-group close-others="true">
\r
7208 * <b2b-expanders class="mpc-expanders" is-open="testmpc">
\r
7209 * <b2b-expander-heading ng-class=" { 'b2b-toggle-header-active': !testmpc, 'b2b-toggle-header-inactive': testmpc } ">Heading content goes here</b2b-expander-heading>
\r
7210 * <b2b-expander-body>
\r
7211 <p>body content goes here</p>
\r
7212 </b2b-expander-body>
\r
7213 * </b2b-expanders>
\r
7214 * </b2b-expander-group>
\r
7216 * <!-- Without Close Other -->
\r
7217 * <b2b-expanders class="mpc-expanders" is-open="testmpc2">
\r
7218 * <b2b-expander-heading ng-class=" { 'b2b-toggle-header-active': !testmpc2, 'b2b-toggle-header-inactive': testmpc2 } ">Heading content goes here</b2b-expander-heading>
\r
7219 * <b2b-expander-body>
\r
7220 <p>body content goes here</p>
\r
7221 </b2b-expander-body>
\r
7222 * </b2b-expanders>
\r
7225 * <section id="code">
\r
7226 <example module="b2b.att.multipurposeExpander">
\r
7227 <file src="src/multipurposeExpander/docs/demo.html" />
\r
7228 <file src="src/multipurposeExpander/docs/demo.js" />
\r
7234 angular.module('b2b.att.multipurposeExpander', ['b2b.att', 'b2b.att.collapse'])
\r
7235 .directive('b2bExpanderGroup', function () {
\r
7239 template: "<ng-transclude></ng-transclude>",
\r
7240 controller:['$scope','$attrs', function($scope,$attrs){
\r
7243 this.scope = $scope;
\r
7245 this.addGroup = function (groupScope) {
\r
7247 groupScope.index = this.groups.length;
\r
7248 this.groups.push(groupScope);
\r
7249 if(this.groups.length > 0){
\r
7252 groupScope.$on('$destroy', function () {
\r
7253 that.removeGroup(groupScope);
\r
7257 this.closeOthers = function (openGroup) {
\r
7258 var closeOthers = angular.isDefined($attrs.closeOthers);
\r
7259 if (closeOthers && !$scope.forceExpand) {
\r
7260 angular.forEach(this.groups, function (group) {
\r
7261 if (group !== openGroup) {
\r
7262 group.isOpen = false;
\r
7266 if (this.groups.indexOf(openGroup) === (this.groups.length - 1) && $scope.forceExpand) {
\r
7267 $scope.forceExpand = false;
\r
7270 this.removeGroup = function (group) {
\r
7271 var index = this.groups.indexOf(group);
\r
7272 if (index !== -1) {
\r
7273 this.groups.splice(this.groups.indexOf(group), 1);
\r
7281 .directive('b2bExpanders', function () {
\r
7285 require:['b2bExpanders','?^b2bExpanderGroup'],
\r
7287 scope:{isOpen:'=?'},
\r
7288 template: "<div ng-transclude></div>",
\r
7289 controller: ['$scope', function ($scope){
\r
7290 var bodyScope = null;
\r
7291 var expanderScope = null;
\r
7292 this.isOpened = function(){
\r
7301 this.setScope = function (scope) {
\r
7302 bodyScope = scope;
\r
7303 bodyScope.isOpen = $scope.isOpen;
\r
7305 this.setExpanderScope = function (scope) {
\r
7306 expanderScope = scope;
\r
7308 this.toggle = function () {
\r
7309 $scope.isOpen = bodyScope.isOpen = !bodyScope.isOpen;
\r
7310 return bodyScope.isOpen;
\r
7313 this.watchToggle = function(io){
\r
7314 bodyScope.isOpen = io;
\r
7315 expanderScope.updateIcons(io);
\r
7318 link: function (scope, elem, attr, myCtrl)
\r
7320 //scope.isOpen = false;
\r
7322 myCtrl[1].addGroup(scope);
\r
7324 scope.$watch('isOpen', function(val){
\r
7325 myCtrl[0].watchToggle(scope.isOpen);
\r
7326 if(val && myCtrl[1]){
\r
7327 myCtrl[1].closeOthers(scope);
\r
7334 .directive('b2bExpanderHeading', function () {
\r
7336 require: "^b2bExpanders",
\r
7341 template: "<div style='padding:10px 10px 10px 0 !important' ng-transclude></div>"
\r
7345 .directive('b2bExpanderBody', function () {
\r
7348 require: "^b2bExpanders",
\r
7352 template: "<div b2b-collapse='!isOpen' ><div ng-transclude></div></div>",
\r
7353 link: function (scope, elem, attr, myCtrl) {
\r
7354 scope.isOpen = false;
\r
7355 myCtrl.setScope(scope);
\r
7360 .directive('b2bExpanderToggle', function () {
\r
7363 require: "^b2bExpanders",
\r
7369 link: function (scope, element, attr, myCtrl)
\r
7371 myCtrl.setExpanderScope(scope);
\r
7372 var isOpen = myCtrl.isOpened();
\r
7374 scope.setIcon = function () {
\r
7375 element.attr("role", "tab");
\r
7377 if (scope.expandIcon && scope.collapseIcon)
\r
7380 element.removeClass(scope.expandIcon);
\r
7381 element.addClass(scope.collapseIcon);
\r
7383 element.attr("aria-expanded", "true");
\r
7386 element.removeClass(scope.collapseIcon);
\r
7387 element.addClass(scope.expandIcon);
\r
7389 element.attr("aria-expanded", "false");
\r
7394 element.bind('click', function (){
\r
7397 scope.updateIcons = function(nStat){
\r
7401 scope.toggleit = function (){
\r
7402 isOpen = myCtrl.toggle();
\r
7411 * @ngdoc directive
\r
7412 * @name Messages, modals & alerts.att:notesMessagesAndErrors
\r
7415 * <file src="src/notesMessagesAndErrors/docs/readme.md" />
\r
7421 * <section id="code">
\r
7422 <example module="b2b.att">
\r
7423 <file src="src/notesMessagesAndErrors/docs/demo.html" />
\r
7424 <file src="src/notesMessagesAndErrors/docs/demo.js" />
\r
7429 angular.module('b2b.att.notesMessagesAndErrors', []);
\r
7431 * @ngdoc directive
\r
7432 * @name Template.att:Notification Card
\r
7435 * <file src="src/notificationCardTemplate/docs/readme.md" />
\r
7438 * <section id="code">
\r
7439 <b>HTML + AngularJS</b>
\r
7440 <example module="b2b.att">
\r
7441 <file src="src/notificationCardTemplate/docs/demo.html" />
\r
7442 <file src="src/notificationCardTemplate/docs/demo.js" />
\r
7447 angular.module('b2b.att.notificationCardTemplate', [])
\r
7450 * @ngdoc directive
\r
7451 * @name Template.att:Order Confirmation Template
\r
7454 * <file src="src/orderConfirmationTemplate/docs/readme.md" />
\r
7457 * <section id="code">
\r
7458 <b>HTML + AngularJS</b>
\r
7459 <example module="b2b.att">
\r
7460 <file src="src/orderConfirmationTemplate/docs/demo.html" />
\r
7461 <file src="src/orderConfirmationTemplate/docs/demo.js" />
\r
7466 angular.module('b2b.att.orderConfirmationTemplate', []);
\r
7469 * @ngdoc directive
\r
7470 * @name Navigation.att:pagination
\r
7473 * <file src="src/pagination/docs/readme.md" />
\r
7476 * <div b2b-pagination="" input-id="goto-page-2" total-pages="totalPages1" current-page="currentPage1" click-handler="customHandler" show-input="showInput"></div>
\r
7479 * <section id="code">
\r
7480 <example module="b2b.att">
\r
7481 <file src="src/pagination/docs/demo.html" />
\r
7482 <file src="src/pagination/docs/demo.js" />
\r
7487 angular.module('b2b.att.pagination', ['b2b.att.utilities', 'ngTouch'])
\r
7488 .directive('b2bPagination', ['b2bUserAgent', 'keymap', '$window', '$timeout', function (b2bUserAgent, keymap, $window, $timeout) {
\r
7495 clickHandler: '=?',
\r
7499 templateUrl: 'b2bTemplate/pagination/b2b-pagination.html',
\r
7500 link: function (scope, elem) {
\r
7501 scope.isMobile = b2bUserAgent.isMobile();
\r
7502 scope.notMobile = b2bUserAgent.notMobile();
\r
7503 scope.focusedPage;
\r
7504 scope.meanVal = 3;
\r
7505 scope.$watch('totalPages', function (value) {
\r
7506 if (angular.isDefined(value) && value !== null) {
\r
7509 scope.totalPages = 1;
\r
7512 if (value <= 10) {
\r
7513 for (var i = 1; i <= value; i++) {
\r
7514 scope.pages.push(i);
\r
7516 } else if (value > 10) {
\r
7517 var midVal = Math.ceil(value / 2);
\r
7518 scope.pages = [midVal - 2, midVal - 1, midVal, midVal + 1, midVal + 2];
\r
7520 if(scope.currentPage === undefined || scope.currentPage === 1)
\r
7522 currentPageChanged(1);
\r
7526 scope.$watch('currentPage', function (value) {
\r
7527 currentPageChanged(value);
\r
7528 callbackHandler(value);
\r
7530 var callbackHandler = function (num) {
\r
7531 if (angular.isFunction(scope.clickHandler)) {
\r
7532 scope.clickHandler(num);
\r
7536 function currentPageChanged(value) {
\r
7537 if (angular.isDefined(value) && value !== null) {
\r
7538 if (!value || value < 1) {
\r
7541 if (value > scope.totalPages) {
\r
7542 value = scope.totalPages;
\r
7544 if (scope.currentPage !== value) {
\r
7545 scope.currentPage = value;
\r
7546 callbackHandler(scope.currentPage);
\r
7548 if (scope.totalPages > 10) {
\r
7549 var val = parseInt(value);
\r
7551 scope.pages = [1, 2, 3, 4, 5, 6, 7, 8];
\r
7552 } else if (val > 6 && val <= scope.totalPages - 5) {
\r
7553 scope.pages = [val - 1, val, val + 1];
\r
7554 } else if (val >= scope.totalPages - 5) {
\r
7555 scope.pages = [scope.totalPages - 7, scope.totalPages - 6, scope.totalPages - 5, scope.totalPages - 4, scope.totalPages - 3, scope.totalPages - 2, scope.totalPages - 1, scope.totalPages];
\r
7558 if (scope.isMobile) {
\r
7559 var inWidth = $window.innerWidth;
\r
7560 var viewLimit = 7;
\r
7561 if (inWidth <= 400) {
\r
7563 } else if (inWidth > 400 && inWidth < 500) {
\r
7565 } else if (inWidth >= 500 && inWidth < 600) {
\r
7567 } else if (inWidth >= 600 && inWidth < 700) {
\r
7569 } else if (inWidth >= 700 && inWidth < 800) {
\r
7573 var val = parseInt(value);
\r
7575 scope.meanVal = Math.floor(viewLimit / 2);
\r
7576 var lowerLimit = (val - scope.meanVal) < 1 ? 1 : val - scope.meanVal;
\r
7577 var upperLimit = (lowerLimit + viewLimit - 1) > scope.totalPages ? scope.totalPages : lowerLimit + viewLimit - 1;
\r
7579 for (var i = lowerLimit; i <= upperLimit; i++) {
\r
7580 scope.pages.push(i);
\r
7585 scope.gotoKeyClick = function (keyEvent) {
\r
7586 if (keyEvent.which === keymap.KEY.ENTER) {
\r
7587 scope.gotoBtnClick()
\r
7590 scope.gotoBtnClick = function () {
\r
7591 currentPageChanged(parseInt(scope.gotoPage));
\r
7592 callbackHandler(scope.currentPage);
\r
7593 var qResult = elem[0].querySelector('button');
\r
7594 angular.element(qResult).attr('disabled','true');
\r
7595 $timeout(function(){
\r
7596 elem[0].querySelector('.b2b-pager__item--active').focus();
\r
7598 scope.gotoPage = null;
\r
7600 scope.onfocusIn = function(evt)
\r
7602 var qResult = elem[0].querySelector('button');
\r
7603 angular.element(qResult).removeAttr('disabled');
\r
7605 scope.onfocusOut = function(evt)
\r
7607 if(evt.target.value === "")
\r
7609 var qResult = elem[0].querySelector('button');
\r
7610 angular.element(qResult).attr('disabled','true');
\r
7613 scope.next = function (event) {
\r
7614 if (event != undefined) {
\r
7615 event.preventDefault();
\r
7617 if (scope.currentPage < scope.totalPages) {
\r
7618 scope.currentPage += 1;
\r
7619 callbackHandler(scope.currentPage);
\r
7622 scope.prev = function (event) {
\r
7623 if (event != undefined) {
\r
7624 event.preventDefault();
\r
7626 if (scope.currentPage > 1) {
\r
7627 scope.currentPage -= 1;
\r
7628 callbackHandler(scope.currentPage);
\r
7631 scope.selectPage = function (value, event) {
\r
7632 event.preventDefault();
\r
7633 scope.currentPage = value;
\r
7634 scope.focusedPage = value;
\r
7635 callbackHandler(scope.currentPage);
\r
7637 scope.checkSelectedPage = function (value) {
\r
7638 if (scope.currentPage === value) {
\r
7643 scope.isFocused = function (page) {
\r
7644 return scope.focusedPage === page;
\r
7650 * @ngdoc directive
\r
7651 * @name Navigation.att:paneSelector
\r
7654 * <file src="src/paneSelector/docs/readme.md" />
\r
7657 * Please refer demo.html tab in Example section below.
\r
7660 <section id="code">
\r
7661 <b>HTML + AngularJS</b>
\r
7662 <example module="b2b.att">
\r
7663 <file src="src/paneSelector/docs/demo.html" />
\r
7664 <file src="src/paneSelector/docs/demo.js" />
\r
7669 angular.module('b2b.att.paneSelector', ['b2b.att.tabs', 'b2b.att.utilities'])
\r
7671 .filter('paneSelectorSelectedItemsFilter', [function () {
\r
7672 return function (listOfItemsArray) {
\r
7674 if (!listOfItemsArray) {
\r
7675 listOfItemsArray = [];
\r
7678 var returnArray = [];
\r
7680 for (var i = 0; i < listOfItemsArray.length; i++) {
\r
7681 if (listOfItemsArray[i].isSelected) {
\r
7682 returnArray.push(listOfItemsArray[i]);
\r
7686 return returnArray;
\r
7690 .filter('paneSelectorFetchChildItemsFilter', [function () {
\r
7691 return function (listOfItemsArray) {
\r
7693 if (!listOfItemsArray) {
\r
7694 listOfItemsArray = [];
\r
7697 var returnArray = [];
\r
7699 for (var i = 0; i < listOfItemsArray.length; i++) {
\r
7700 for (var j = 0; j < listOfItemsArray[i].childItems.length; j++) {
\r
7701 returnArray.push(listOfItemsArray[i].childItems[j]);
\r
7705 return returnArray;
\r
7709 .directive('b2bPaneSelector', [function () {
\r
7713 templateUrl: 'b2bTemplate/paneSelector/paneSelector.html',
\r
7719 .directive('b2bPaneSelectorPane', [ function () {
\r
7723 templateUrl: 'b2bTemplate/paneSelector/paneSelectorPane.html',
\r
7729 .directive('b2bTabVertical', ['$timeout', 'keymap', function ($timeout, keymap) {
\r
7732 require: '^b2bTab',
\r
7733 link: function (scope, element, attr, b2bTabCtrl) {
\r
7735 if (!b2bTabCtrl) {
\r
7739 // retreive the isolateScope
\r
7740 var iScope = angular.element(element).isolateScope();
\r
7742 $timeout(function () {
\r
7743 angular.element(element[0].querySelector('a')).unbind('keydown');
\r
7744 angular.element(element[0].querySelector('a')).bind('keydown', function (evt) {
\r
7746 if (!(evt.keyCode)) {
\r
7747 evt.keyCode = evt.which;
\r
7750 switch (evt.keyCode) {
\r
7751 case keymap.KEY.DOWN:
\r
7752 evt.preventDefault();
\r
7756 case keymap.KEY.UP:
\r
7757 evt.preventDefault();
\r
7758 iScope.previousKey();
\r
7769 * @ngdoc directive
\r
7770 * @name Forms.att:phoneNumberInput
\r
7773 * <file src="src/phoneNumberInput/docs/readme.md" />
\r
7776 <form name="userForm1">
\r
7777 <div class="form-row" ng-class="{'error':!userForm1.text.$valid && userForm1.text.$dirty}">
\r
7778 <label>Phone Mask<span style="color:red">*</span>: (npa) nxx-line Model Value: {{mask.text}}</label>
\r
7780 <input b2b-phone-mask="phoneMask" name="text" ng-model="mask.text" type="text" placeholder="e.g. (123) 456-7890" title="Phone Number" class="b2b-phone-mask-input" required />
\r
7781 <div ng-messages="userForm1.text.$error" class="error-msg" aria-live="polite" aria-atomic="true">
\r
7782 <span ng-message="required" role="alert">This field is mandatory!</span>
\r
7783 <span ng-message="invalidPhoneNumber" role="alert">Please enter valid phone number!</span>
\r
7784 <span ng-message="mask" role="alert">Please enter valid phone number!</span>
\r
7791 * <section id="code">
\r
7792 <example module="b2b.att">
\r
7793 <file src="src/phoneNumberInput/docs/demo.html" />
\r
7794 <file src="src/phoneNumberInput/docs/demo.js" />
\r
7799 angular.module('b2b.att.phoneNumberInput', ['ngMessages', 'b2b.att.utilities'])
\r
7800 .constant("CoreFormsUiConfig", {
\r
7801 phoneMask: '(___) ___-____',
\r
7802 phoneMaskDot: '___.___.____',
\r
7803 phoneMaskHyphen: '___-___-____'
\r
7805 .directive('b2bPhoneMask', ['$parse', 'CoreFormsUiConfig', 'keymap', function ($parse, CoreFormsUiConfig, keymap) {
\r
7807 require: 'ngModel',
\r
7811 link: function (scope, iElement, iAttrs, ctrl) {
\r
7812 var navigatorAgent = navigator.userAgent.toLowerCase(),
\r
7813 isAndroid = navigatorAgent.indexOf("android") > -1,
\r
7814 oldIE = navigatorAgent.indexOf('msie 8.0') !== -1;
\r
7816 var validPhoneNumber = false;
\r
7817 var currentKey = '';
\r
7819 mask = "__________";
\r
7821 switch (iAttrs.b2bPhoneMask) {
\r
7823 mask = CoreFormsUiConfig.phoneMask;
\r
7825 case "phoneMaskDot":
\r
7826 mask = CoreFormsUiConfig.phoneMaskDot;
\r
7828 case "phoneMaskHyphen":
\r
7829 mask = CoreFormsUiConfig.phoneMaskHyphen;
\r
7832 mask = CoreFormsUiConfig.phoneMask;
\r
7835 iElement.attr("maxlength", mask.length);
\r
7836 var checkValidity = function (unmaskedValue, rawValue) {
\r
7837 var valid = false;
\r
7838 if (angular.isUndefined(rawValue) || rawValue === '') {
\r
7840 } else if (unmaskedValue) {
\r
7841 valid = (unmaskedValue.length === 10);
\r
7843 ctrl.$setValidity('invalidPhoneNumber', validPhoneNumber);
\r
7844 ctrl.$setValidity('mask', valid);
\r
7847 var handleKeyup = function (evt) {
\r
7849 if (evt && evt.keyCode === keymap.KEY.SHIFT) {
\r
7853 var index, formattedNumber;
\r
7854 if (ctrl.$modelValue) {
\r
7855 formattedNumber = ctrl.$modelValue;
\r
7857 formattedNumber = iElement.val();
\r
7859 if (!formattedNumber.length && currentKey === '') {
\r
7862 var maskLength, inputNumbers, maskArray, tempArray, maskArrayLength;
\r
7864 maskArray = mask.split("");
\r
7865 maskArrayLength = maskArray.length;
\r
7866 maskLength = formattedNumber.substring(0, mask.length);
\r
7867 inputNumbers = formattedNumber.replace(/[^0-9]/g, "").split("");
\r
7868 for (index = 0; index < maskArrayLength; index++) {
\r
7869 tempArray.push(maskArray[index] === "_" ? inputNumbers.shift() : maskArray[index]);
\r
7870 if (inputNumbers.length === 0) {
\r
7874 formattedNumber = tempArray.join("");
\r
7875 if (formattedNumber === '(') {
\r
7876 formattedNumber = '';
\r
7879 if ( (angular.isDefined(evt) && evt.which) && currentKey !== '') {
\r
7880 if (maskArray[0] === currentKey && formattedNumber === '') {
\r
7881 formattedNumber = '(';
\r
7882 } else if (evt.which === keymap.KEY.SPACE && currentKey === ' ') {
\r
7883 formattedNumber = formattedNumber + ') ';
\r
7884 } else if (maskArray[0] === currentKey && formattedNumber === '') {
\r
7885 formattedNumber = formattedNumber + currentKey;
\r
7886 } else if (maskArray[formattedNumber.length] === currentKey) {
\r
7887 formattedNumber = formattedNumber + currentKey;
\r
7892 ctrl.$setViewValue(formattedNumber);
\r
7894 return formattedNumber;
\r
7898 // since we are only allowing 0-9, why even let the keypress go forward?
\r
7899 // also added in delete... in case they want to delete :)
\r
7900 var handlePress = function (e) {
\r
7902 if ((e.which < 48 || e.which > 57) && (e.which < 96 || e.which > 105)) {
\r
7903 if (e.which !== keymap.KEY.BACKSPACE && e.which !== keymap.KEY.TAB && e.which !== keymap.KEY.DELETE && e.which !== keymap.KEY.ENTER && e.which !== keymap.KEY.LEFT && e.which !== keymap.KEY.RIGHT &&
\r
7904 // Allow: Ctrl+V/v
\r
7905 (!(e.ctrlKey) && (e.which !== '118' || e.which !== '86')) &&
\r
7906 // Allow: Ctrl+C/c
\r
7907 (!(e.ctrlKey) && (e.which !== '99' || e.which !== '67')) &&
\r
7908 // Allow: Ctrl+X/x
\r
7909 (!(e.ctrlKey) && (e.which !== '120' || e.which !== '88'))) {
\r
7910 e.preventDefault ? e.preventDefault() : e.returnValue = false;
\r
7911 iElement.attr("aria-label", "Only numbers are allowed");
\r
7912 validPhoneNumber = false;
\r
7915 iElement.removeAttr("aria-label");
\r
7916 validPhoneNumber = true;
\r
7923 // i moved this out because i thought i might need focus as well..
\r
7924 // to handle setting the model as the view changes
\r
7925 var parser = function (fromViewValue) {
\r
7926 var letters = /^[A-Za-z]+$/;
\r
7927 var numbers = /^[0-9]+$/;
\r
7928 if (angular.isUndefined(fromViewValue) || fromViewValue === '') {
\r
7929 validPhoneNumber = true;
\r
7931 if (fromViewValue.match(letters)) {
\r
7932 validPhoneNumber = false;
\r
7934 if (fromViewValue.match(numbers)) {
\r
7935 validPhoneNumber = true;
\r
7939 if (fromViewValue && fromViewValue.length > 0) {
\r
7940 clean = fromViewValue.replace(/[^0-9]/g, '');
\r
7942 checkValidity(clean, fromViewValue);
\r
7946 //to handle reading the model and formatting it
\r
7947 var formatter = function (fromModelView) {
\r
7949 checkValidity(fromModelView);
\r
7950 if (fromModelView) {
\r
7951 input = handleKeyup();
\r
7956 var setCurrentKey = function (e) {
\r
7957 switch (e.which) {
\r
7967 if (e.shiftKey === true) {
\r
7972 if (e.shiftKey === true) {
\r
7982 if (angular.isDefined(scope.ngModel)) {
\r
7983 parser(scope.ngModel);
\r
7986 ctrl.$parsers.push(parser);
\r
7987 ctrl.$formatters.push(formatter);
\r
7988 iElement.bind('keyup', handleKeyup);
\r
7989 iElement.bind('keydown', handlePress);
\r
7994 * @ngdoc directive
\r
7995 * @name Template.att:Profile Blocks
\r
7998 * <file src="src/profileBlockTemplate/docs/readme.md" />
\r
8000 * <section id="code">
\r
8001 <example module="b2b.att">
\r
8002 <file src="src/profileBlockTemplate/docs/demo.html" />
\r
8003 <file src="src/profileBlockTemplate/docs/demo.js" />
\r
8009 angular.module('b2b.att.profileBlockTemplate', [])
\r
8014 * @ngdoc directive
\r
8015 * @name Layouts.att:profileCard
\r
8018 * <file src="src/profileCard/docs/readme.md" />
\r
8021 * <b2b-profile-card></b2b-profile-card>
\r
8024 <section id="code">
\r
8025 <example module="b2b.att">
\r
8026 <file src="src/profileCard/docs/demo.html" />
\r
8027 <file src="src/profileCard/docs/demo.js" />
\r
8032 angular.module('b2b.att.profileCard', ['b2b.att'])
\r
8033 .constant('profileStatus',{
\r
8040 status: "Deactivated",
\r
8052 status: "Pending",
\r
8056 role: "COMPANY ADMINISTRATOR"
\r
8059 .directive('b2bProfileCard',['$http','$q','profileStatus', function($http,$q,profileStatus) {
\r
8063 templateUrl: function(element, attrs){
\r
8064 if(!attrs.addUser){
\r
8065 return 'b2bTemplate/profileCard/profileCard.html';
\r
8068 return 'b2bTemplate/profileCard/profileCard-addUser.html';
\r
8073 characterLimit: '@'
\r
8075 link: function(scope, elem, attr){
\r
8076 scope.characterLimit = parseInt(attr.characterLimit, 10) || 25;
\r
8077 scope.shouldClip = function(str) {
\r
8078 return str.length > scope.characterLimit;
\r
8081 scope.showEmailTooltip = false;
\r
8084 function isImage(src) {
\r
8085 var deferred = $q.defer();
\r
8086 var image = new Image();
\r
8087 image.onerror = function() {
\r
8088 deferred.reject(false);
\r
8090 image.onload = function() {
\r
8091 deferred.resolve(true);
\r
8093 if(src !== undefined && src.length>0 ){
\r
8096 deferred.reject(false);
\r
8098 return deferred.promise;
\r
8100 if(!attr.addUser){
\r
8101 scope.image=false;
\r
8102 isImage(scope.profile.img).then(function(img) {
\r
8105 var splitName=(scope.profile.name).split(' ');
\r
8106 scope.initials='';
\r
8107 for(var i=0;i<splitName.length;i++){
\r
8108 scope.initials += splitName[i][0];
\r
8110 if(scope.profile.role.toUpperCase() === profileStatus.role){
\r
8113 var profileState=profileStatus.status[scope.profile.state.toUpperCase()];
\r
8114 if(profileState) {
\r
8115 scope.profile.state=profileStatus.status[scope.profile.state.toUpperCase()].status;
\r
8116 scope.colorIcon=profileStatus.status[scope.profile.state.toUpperCase()].color;
\r
8117 if(scope.profile.state.toUpperCase()===profileStatus.status.PENDING.status.toUpperCase()||scope.profile.state.toUpperCase()===profileStatus.status.LOCKED.status.toUpperCase()){
\r
8118 scope.profile.lastLogin=scope.profile.state;
\r
8121 var today=new Date().getTime();
\r
8122 var lastlogin=new Date(scope.profile.lastLogin).getTime();
\r
8123 var diff=(today-lastlogin)/(1000*60*60*24);
\r
8125 scope.profile.lastLogin="Today";
\r
8128 scope.profile.lastLogin="Yesterday";
\r
8135 * @ngdoc directive
\r
8136 * @name Forms.att:radios
\r
8139 * <file src="src/radios/docs/readme.md" />
\r
8142 * See demo section
\r
8144 * @param {boolean} refreshRadioGroup - A trigger that recalculates and updates the accessibility roles on radios in a group.
\r
8147 <section id="code">
\r
8148 <b>HTML + AngularJS</b>
\r
8149 <example module="b2b.att">
\r
8150 <file src="src/radios/docs/demo.html" />
\r
8151 <file src="src/radios/docs/demo.js" />
\r
8155 angular.module('b2b.att.radios', ['b2b.att.utilities'])
\r
8156 .directive('b2bRadioGroupAccessibility', ['$timeout', 'b2bUserAgent', function($timeout, b2bUserAgent) {
\r
8160 refreshRadioGroup: "=",
\r
8162 link: function(scope, ele, attr) {
\r
8164 var roleRadioElement, radioProductSelectElement, radioInputTypeElement;
\r
8166 $timeout(calculateNumberOfRadio);
\r
8168 scope.$watch('refreshRadioGroup', function(value) {
\r
8169 if (value === true) {
\r
8170 addingRoleAttribute();
\r
8171 $timeout(calculateNumberOfRadio);
\r
8172 scope.refreshRadioGroup = false;
\r
8179 function calculateNumberOfRadio() {
\r
8180 roleRadioElement = ele[0].querySelectorAll('[role="radio"]');
\r
8182 radioProductSelectElement = ele[0].querySelectorAll('[role="radiogroup"] li.radio-box');
\r
8184 radioInputTypeElement = ele[0].querySelectorAll('input[type="radio"]');
\r
8186 for (var i = 0; i < radioInputTypeElement.length; i++) {
\r
8187 var isChecked = radioInputTypeElement[i].checked ? 'true' : 'false';
\r
8188 var isDisabled = radioInputTypeElement[i].disabled ? 'true' : 'false';
\r
8189 var numOfx = i + 1 + ' of ' + radioInputTypeElement.length;
\r
8190 angular.element(roleRadioElement[i]).attr({
\r
8191 'aria-checked': isChecked,
\r
8192 'aria-disabled': isDisabled,
\r
8193 'data-opNum': numOfx
\r
8195 if (b2bUserAgent.notMobile()) {
\r
8196 angular.element(roleRadioElement[i]).removeAttr("role");
\r
8199 if (radioProductSelectElement.length) {
\r
8200 isChecked === 'true' ? angular.element(radioProductSelectElement[i]).addClass('active') : angular.element(radioProductSelectElement[i]).removeClass('active');
\r
8203 if (/Android/i.test(navigator.userAgent)) {
\r
8204 angular.element(roleRadioElement[i]).append('<span class="hidden-spoken">. ' + numOfx + '.</span>');
\r
8208 angular.element(radioInputTypeElement[i]).bind('click', radioStateChangeonClick);
\r
8213 function addingRoleAttribute() {
\r
8214 for (var i = 0; i < radioInputTypeElement.length; i++) {
\r
8215 if (b2bUserAgent.notMobile()) {
\r
8216 angular.element(roleRadioElement[i]).attr("role", "radio");
\r
8221 function radioStateChangeonClick() {
\r
8222 for (var i = 0; i < radioInputTypeElement.length; i++) {
\r
8223 var isChecked = radioInputTypeElement[i].checked ? 'true' : 'false';
\r
8224 var isDisabled = radioInputTypeElement[i].disabled ? 'true' : 'false';
\r
8225 if (radioProductSelectElement.length) {
\r
8226 isChecked === 'true' ? angular.element(radioProductSelectElement[i]).addClass('active') : angular.element(radioProductSelectElement[i]).removeClass('active');
\r
8228 angular.element(roleRadioElement[i]).attr({
\r
8229 'aria-checked': isChecked,
\r
8230 'aria-disabled': isDisabled
\r
8241 * @ngdoc directive
\r
8242 * @name Forms.att:searchField
\r
8245 * <file src="src/searchField/docs/readme.md" />
\r
8248 * <div b2b-search-field dropdown-list="listdata" on-click-callback="clickFn(value)" class="span12" input-model='typedString' config-obj='keyConfigObj'></div>
\r
8251 <section id="code">
\r
8252 <example module="b2b.att">
\r
8253 <file src="src/searchField/docs/demo.html" />
\r
8254 <file src="src/searchField/docs/demo.js" />
\r
8259 angular.module('b2b.att.searchField', ['b2b.att.utilities', 'b2b.att.position'])
\r
8260 .filter('b2bFilterInput', [function() {
\r
8261 return function(list, str, keyArray, displayListKey, isContainsSearch, searchSeperator) {
\r
8264 var searchCondition;
\r
8265 var conditionCheck = function(searchSeperator, listItem, displayListKey, splitString) {
\r
8266 var displayTitle = null;
\r
8267 if (splitString) {
\r
8268 for (var i = 0; i < displayListKey.length; i++) {
\r
8270 displayTitle = listItem[displayListKey[i]].toLowerCase().indexOf(splitString[i].toLowerCase()) > -1;
\r
8272 displayTitle = (splitString[i]) ? displayTitle && listItem[displayListKey[i]].toLowerCase().indexOf(splitString[i].toLowerCase().trim()) > -1 : displayTitle;
\r
8276 angular.forEach(displayListKey, function(value) {
\r
8277 if (!displayTitle) {
\r
8278 displayTitle = listItem[value];
\r
8280 displayTitle = displayTitle + (listItem[value] ? searchSeperator + ' ' + listItem[value] : '');
\r
8284 return displayTitle;
\r
8286 angular.forEach(list, function(listItem) {
\r
8287 var splitString = str.indexOf(searchSeperator) > -1 ? str.split(searchSeperator) : false;
\r
8288 var displayList = conditionCheck(searchSeperator, listItem, displayListKey, splitString)
\r
8289 for (var i = 0; i < keyArray.length; i++) {
\r
8290 searchLabel = keyArray[i];
\r
8291 if (listItem[searchLabel]) {
\r
8292 if (isContainsSearch) {
\r
8293 var displaySearchList = listItem[searchLabel].toLowerCase().indexOf(str.toLowerCase()) > -1;
\r
8294 if (splitString.length > 1) {
\r
8295 displaySearchList = (splitString.length <= keyArray.length) ? displayList : false;
\r
8297 searchCondition = displaySearchList;
\r
8299 searchCondition = listItem[searchLabel].match(new RegExp('^' + str, 'gi'));
\r
8301 if (searchCondition) {
\r
8303 'title': conditionCheck(searchSeperator, listItem, displayListKey),
\r
8304 'valueObj': listItem
\r
8313 }]).directive('b2bSearchField', ['$filter', 'b2bFilterInputFilter', 'keymap', '$documentBind', '$isElement', '$document', 'events', '$timeout', function($filter, b2bFilterInput, keymap, $documentBind, $isElement, $document, events, $timeout) {
\r
8317 dataList: '=dropdownList',
\r
8318 onClickCallback: '&',
\r
8326 templateUrl: 'b2bTemplate/searchField/searchField.html',
\r
8327 controller: ['$scope', function($scope) {
\r
8328 this.searchKeyArray = [];
\r
8329 if ($scope.configObj.searchKeys) {
\r
8330 this.searchKeyArray = $scope.configObj.searchKeys;
\r
8332 if (angular.isUndefined($scope.disabled)) {
\r
8333 $scope.disabled = false;
\r
8335 this.triggerInput = function(searchString) {
\r
8336 $scope.originalInputModel = searchString;
\r
8337 if (searchString === '') {
\r
8338 $scope.currentIndex = -1;
\r
8339 $scope.filterList = [];
\r
8340 $scope.showListFlag = false;
\r
8341 } else if (searchString !== '' && !$scope.isFilterEnabled) {
\r
8342 $scope.filterList = $filter('b2bFilterInput')($scope.dataList, searchString, this.searchKeyArray, $scope.configObj.displayListDataKey, $scope.configObj.isContainsSearch, $scope.configObj.searchSeperator);
\r
8343 $scope.showListFlag = true;
\r
8346 this.denyRegex = function() {
\r
8347 return $scope.inputDeny;
\r
8350 link: function(scope, elem) {
\r
8351 scope.isFilterEnabled = false;
\r
8352 scope.showListFlag = false;
\r
8353 scope.currentIndex = -1;
\r
8354 scope.setCurrentIdx = function(idx) {
\r
8355 scope.currentIndex = idx;
\r
8357 scope.inputModel = scope.filterList[idx].title;
\r
8358 scope.objModel = scope.filterList[idx];
\r
8361 scope.isActive = function(index, dropdownLength) {
\r
8362 scope.dropdownLength = dropdownLength;
\r
8363 return scope.currentIndex === index;
\r
8365 scope.selectItem = function(idx) {
\r
8366 scope.setCurrentIdx(idx);
\r
8367 scope.onClickCallback({
\r
8368 value: scope.inputModel,
\r
8369 objValue: scope.objModel
\r
8371 scope.showListFlag = false;
\r
8372 $timeout(function() {
\r
8373 elem.find('div').find('input')[0].focus();
\r
8376 scope.startSearch = function() {
\r
8377 scope.onClickCallback({
\r
8378 value: scope.inputModel,
\r
8379 objValue: scope.objModel
\r
8382 scope.selectPrev = function() {
\r
8383 if (scope.currentIndex > 0 && scope.filterList.length > 0) {
\r
8384 scope.currentIndex = scope.currentIndex - 1;
\r
8385 scope.setCurrentIdx(scope.currentIndex);
\r
8386 } else if (scope.currentIndex === 0) {
\r
8387 scope.currentIndex = scope.currentIndex - 1;
\r
8388 scope.inputModel = scope.originalInputModel;
\r
8389 scope.isFilterEnabled = true;
\r
8392 scope.selectNext = function() {
\r
8393 if (scope.currentIndex < scope.configObj.noOfItemsDisplay - 1) {
\r
8394 if (scope.currentIndex < scope.filterList.length - 1) {
\r
8395 scope.currentIndex = scope.currentIndex + 1;
\r
8396 scope.setCurrentIdx(scope.currentIndex);
\r
8400 scope.selectCurrent = function() {
\r
8401 scope.selectItem(scope.currentIndex);
\r
8403 scope.selectionIndex = function(e) {
\r
8404 switch (e.keyCode) {
\r
8405 case keymap.KEY.DOWN:
\r
8406 events.preventDefault(e);
\r
8407 scope.isFilterEnabled = true;
\r
8408 scope.selectNext();
\r
8410 case keymap.KEY.UP:
\r
8411 events.preventDefault(e);
\r
8412 scope.isFilterEnabled = true;
\r
8413 scope.selectPrev();
\r
8415 case keymap.KEY.ENTER:
\r
8416 events.preventDefault(e);
\r
8417 scope.isFilterEnabled = true;
\r
8418 scope.selectCurrent();
\r
8420 case keymap.KEY.ESC:
\r
8421 events.preventDefault(e);
\r
8422 scope.isFilterEnabled = false;
\r
8423 scope.showListFlag = false;
\r
8424 scope.inputModel = '';
\r
8427 scope.isFilterEnabled = false;
\r
8430 if (elem[0].querySelector('.filtercontainer')) {
\r
8431 elem[0].querySelector('.filtercontainer').scrollTop = (scope.currentIndex - 1) * 35;
\r
8434 scope.$watch('filterList', function(newVal, oldVal) {
\r
8435 if (newVal !== oldVal) {
\r
8436 scope.currentIndex = -1;
\r
8439 scope.blurInput = function() {
\r
8440 $timeout(function() {
\r
8441 scope.showListFlag = false;
\r
8444 var outsideClick = function(e) {
\r
8445 var isElement = $isElement(angular.element(e.target), elem.find('ul').eq(0), $document);
\r
8446 if (!isElement && document.activeElement.tagName.toLowerCase() !== 'input') {
\r
8447 scope.showListFlag = false;
\r
8451 $documentBind.click('showListFlag', outsideClick, scope);
\r
8455 .directive('b2bSearchInput', [function() {
\r
8458 require: ['ngModel', '^b2bSearchField'],
\r
8459 link: function(scope, elem, attr, ctrl) {
\r
8460 var ngModelCtrl = ctrl[0];
\r
8461 var attSearchBarCtrl = ctrl[1];
\r
8462 var REGEX = ctrl[1].denyRegex();
\r
8463 var parser = function(viewValue) {
\r
8464 attSearchBarCtrl.triggerInput(viewValue);
\r
8467 ngModelCtrl.$parsers.push(parser);
\r
8469 if (REGEX !== undefined || REGEX !== '') {
\r
8470 elem.bind('input', function() {
\r
8471 var inputString = ngModelCtrl.$viewValue && ngModelCtrl.$viewValue.replace(REGEX, '');
\r
8472 if (inputString !== ngModelCtrl.$viewValue) {
\r
8473 ngModelCtrl.$setViewValue(inputString);
\r
8474 ngModelCtrl.$render();
\r
8484 * @ngdoc directive
\r
8485 * @name Buttons, links & UI controls.att:Seek bar
\r
8488 * <file src="src/seekBar/docs/readme.md" />
\r
8491 * Horizontal Seek Bar
\r
8492 * <b2b-seek-bar min="0" max="400" step="1" skip-interval="1" data-ng-model="horizontalSeekBarVal" style="width:180px; margin: auto;" on-drag-end="onDragEnd()" on-drag-init="onDragStart()"></b2b-seek-bar>
\r
8494 * Vertical Seek Bar
\r
8495 * <b2b-seek-bar min="0" max="1" step="0.01" skip-interval="0.1" vertical data-ng-model="verticalSeekBarVal" style=" width: 6px; height: 180px; margin: auto;"></b2b-seek-bar>
\r
8498 <section id="code">
\r
8499 <b>HTML + AngularJS</b>
\r
8500 <example module="b2b.att">
\r
8501 <file src="src/seekBar/docs/demo.html" />
\r
8502 <file src="src/seekBar/docs/demo.js" />
\r
8507 angular.module('b2b.att.seekBar', ['b2b.att.utilities','b2b.att.position'])
\r
8508 .constant('b2bSeekBarConfig', {
\r
8514 .directive('b2bSeekBar', ['$documentBind', 'events', 'b2bSeekBarConfig', 'keymap', '$compile', function($documentBind, events, b2bSeekBarConfig, keymap, $compile) {
\r
8518 require: 'ngModel',
\r
8519 templateUrl: 'b2bTemplate/seekBar/seekBar.html',
\r
8524 link: function(scope, elm, attr, ngModelCtrl) {
\r
8525 scope.isDragging = false;
\r
8526 scope.verticalSeekBar = false;
\r
8529 var step = b2bSeekBarConfig.step;
\r
8530 var skipInterval = b2bSeekBarConfig.skipInterval;
\r
8531 var knob = angular.element(elm[0].querySelector('.b2b-seek-bar-knob-container'));
\r
8532 var seekBarKnob = angular.element(knob[0].querySelector('.b2b-seek-bar-knob'));
\r
8533 var trackContainer = angular.element(elm[0].querySelector('.b2b-seek-bar-track-container'));
\r
8534 var trackFill = angular.element(elm[0].querySelector('.b2b-seek-bar-track-fill'));
\r
8535 var trackContainerRect = {};
\r
8537 var trackFillOrderPositioning;
\r
8539 if (angular.isDefined(attr.vertical)) {
\r
8540 scope.verticalSeekBar = true;
\r
8541 axisPosition = "clientY";
\r
8544 scope.verticalSeekBar = false;
\r
8545 axisPosition = "clientX";
\r
8547 var getValidStep = function(val) {
\r
8548 val = parseFloat(val);
\r
8549 // in case $modelValue came in string number
\r
8550 if (angular.isNumber(val)) {
\r
8551 val = Math.round((val - min) / step) * step + min;
\r
8552 return Math.round(val * 1000) / 1000;
\r
8556 var getPositionToPercent = function(x) {
\r
8557 if (scope.verticalSeekBar) {
\r
8558 return Math.max(0, Math.min(1, (trackContainerRect.bottom - x) / (trackFillOrderPositioning)));
\r
8561 return Math.max(0, Math.min(1, (x - trackContainerRect.left) / (trackFillOrderPositioning)));
\r
8565 var getPercentToValue = function(percent) {
\r
8566 return (min + percent * (max - min));
\r
8569 var getValueToPercent = function(val) {
\r
8570 return (val - min) / (max - min);
\r
8573 var getValidMinMax = function(val) {
\r
8574 return Math.max(min, Math.min(max, val));
\r
8577 var updateTrackContainerRect = function() {
\r
8578 trackContainerRect = trackContainer[0].getBoundingClientRect();
\r
8579 if (scope.verticalSeekBar) {
\r
8580 if (!trackContainerRect.height) {
\r
8581 trackFillOrderPositioning = trackContainer[0].scrollHeight;
\r
8583 trackFillOrderPositioning = trackContainerRect.height;
\r
8587 if (!trackContainerRect.width) {
\r
8588 trackFillOrderPositioning = trackContainer[0].scrollWidth;
\r
8590 trackFillOrderPositioning = trackContainerRect.width;
\r
8597 var updateKnobPosition = function(percent) {
\r
8598 var percentStr = (percent * 100) + '%';
\r
8599 if (scope.verticalSeekBar) {
\r
8600 knob.css('bottom', percentStr);
\r
8601 trackFill.css('height', percentStr);
\r
8604 knob.css('left', percentStr);
\r
8605 trackFill.css('width', percentStr);
\r
8609 var modelRenderer = function() {
\r
8610 if (isNaN(ngModelCtrl.$viewValue)) {
\r
8611 ngModelCtrl.$viewValue = ngModelCtrl.$modelValue || min;
\r
8614 var viewVal = ngModelCtrl.$viewValue;
\r
8615 scope.currentModelValue = viewVal;
\r
8617 //wait for min, max and step to be set then only update UI to avoid NaN on percent calculation
\r
8618 if ((min || min === 0) && max && step) {
\r
8619 updateKnobPosition(getValueToPercent(viewVal));
\r
8623 var setModelValue = function(val) {
\r
8624 scope.currentModelValue = getValidMinMax(getValidStep(val));
\r
8625 ngModelCtrl.$setViewValue(scope.currentModelValue);
\r
8628 var updateMin = function(val) {
\r
8629 min = parseFloat(val);
\r
8631 min = b2bSeekBarConfig.min;
\r
8636 var updateMax = function(val) {
\r
8637 max = parseFloat(val);
\r
8639 max = b2bSeekBarConfig.max;
\r
8644 var updateStep = function(val) {
\r
8645 step = parseFloat(val);
\r
8646 if (!attr['skipInterval']) {
\r
8647 skipInterval = step;
\r
8651 var updateSkipInterval = function(val) {
\r
8652 skipInterval = step * Math.ceil(val / (step!==0?step:1));
\r
8655 angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(b2bSeekBarConfig.min);
\r
8656 angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(b2bSeekBarConfig.max);
\r
8657 if (angular.isDefined(attr.step)) {
\r
8658 attr.$observe('step', updateStep);
\r
8660 if (angular.isDefined(attr.skipInterval)) {
\r
8661 attr.$observe('skipInterval', updateSkipInterval);
\r
8663 scope.currentModelValue = getValidMinMax(getValidStep(min));
\r
8664 var onMouseDown = function(e) {
\r
8665 switch (e.which) {
\r
8667 // left mouse button
\r
8671 // right or middle mouse button
\r
8676 scope.isDragging = true;
\r
8677 seekBarKnob[0].focus();
\r
8678 updateTrackContainerRect();
\r
8679 if (attr['onDragInit']) {
\r
8680 scope.onDragInit();
\r
8682 events.stopPropagation(e);
\r
8683 events.preventDefault(e);
\r
8684 scope.$apply(function() {
\r
8685 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
\r
8689 var onMouseUp = function() {
\r
8691 if (attr['onDragEnd']) {
\r
8692 scope.onDragEnd();
\r
8694 scope.isDragging = false;
\r
8698 var onMouseMove = function(e) {
\r
8699 if (scope.isDragging) {
\r
8700 events.stopPropagation(e);
\r
8701 events.preventDefault(e);
\r
8703 scope.$apply(function() {
\r
8704 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
\r
8709 function onKeyDown(e) {
\r
8710 if (!(e.keyCode)) {
\r
8711 e.keyCode = e.which;
\r
8714 switch (e.keyCode) {
\r
8715 case keymap.KEY.LEFT:
\r
8716 if (!scope.verticalSeekBar) {
\r
8717 updateStep = -skipInterval;
\r
8720 case keymap.KEY.RIGHT:
\r
8721 if (!scope.verticalSeekBar) {
\r
8722 updateStep = skipInterval;
\r
8725 case keymap.KEY.UP:
\r
8726 if (scope.verticalSeekBar) {
\r
8727 updateStep = skipInterval;
\r
8730 case keymap.KEY.DOWN:
\r
8731 if (scope.verticalSeekBar) {
\r
8732 updateStep = -skipInterval;
\r
8740 events.stopPropagation(e);
\r
8741 events.preventDefault(e);
\r
8742 scope.$apply(function() {
\r
8743 setModelValue(ngModelCtrl.$viewValue + updateStep);
\r
8745 if (attr['onDragEnd']) {
\r
8746 scope.onDragEnd();
\r
8751 elm.on('keydown', onKeyDown);
\r
8752 elm.on('mousedown', onMouseDown);
\r
8754 $documentBind.event('mousemove', 'isDragging', onMouseMove, scope, true, 0);
\r
8755 $documentBind.event('mouseup', 'isDragging', onMouseUp, scope, true, 0);
\r
8757 ngModelCtrl.$render = function() {
\r
8758 if (!scope.isDragging) {
\r
8762 ngModelCtrl.$viewChangeListeners.push(modelRenderer);
\r
8763 ngModelCtrl.$formatters.push(getValidMinMax);
\r
8764 ngModelCtrl.$formatters.push(getValidStep);
\r
8769 * @ngdoc directive
\r
8770 * @name Forms.att:selectorModule
\r
8773 * <file src="src/selectorModule/docs/readme.md" />
\r
8776 * <select name="myNameBig" type="large" b2b-dropdown ng-model="Controller Variable here">
\r
8777 * <option b2b-dropdown-list option-repeat="option data here" imgsrc="image path" value="value">List Text</option>
\r
8780 * <select name="myNameBig" type="large" b2b-dropdown ng-model="Controller Variable here">
\r
8781 * <option b2b-dropdown-list option-repeat="option data here" imgsrc="image path" value="value">List Text</option>
\r
8784 * <select name="myNameBig" b2b-dropdown ng-model="Controller Variable here">
\r
8785 * <optgroup b2b-dropdown-group label="Group Label here">
\r
8786 * <option b2b-dropdown-list option-repeat="option data here" imgsrc="image path" value="value">List Text</option>
\r
8791 * <section id="code">
\r
8792 <example module="b2b.att">
\r
8793 <file src="src/selectorModule/docs/demo.html" />
\r
8794 <file src="src/selectorModule/docs/demo.js" />
\r
8799 angular.module('b2b.att.selectorModule', ['b2b.att.dropdowns']);
\r
8801 * @ngdoc directive
\r
8802 * @name Layouts.att:separators
\r
8805 * <file src="src/separators/docs/readme.md" />
\r
8811 * <section id="code">
\r
8812 <b>HTML + AngularJS</b>
\r
8813 <example module="b2b.att">
\r
8814 <file src="src/separators/docs/demo.html" />
\r
8815 <file src="src/separators/docs/demo.js" />
\r
8821 angular.module('b2b.att.separators', []);
\r
8823 * @ngdoc directive
\r
8824 * @name Buttons, links & UI controls.att:slider
\r
8827 * <file src="src/slider/docs/readme.md" />
\r
8830 * <b2b-slider min="0" max="400" step="1" no-aria-label skip-interval="1" ng-model="horizontalSliderVal" style="width:180px; margin: auto;" on-drag-end="onDragEnd()" on-drag-init="onDragStart()" label-id="slider-label" post-aria-label="postAriaLabel"></b2b-slider>
\r
8833 <section id="code">
\r
8834 <b>HTML + AngularJS</b>
\r
8835 <example module="b2b.att">
\r
8836 <file src="src/slider/docs/demo.html" />
\r
8837 <file src="src/slider/docs/demo.js" />
\r
8842 angular.module('b2b.att.slider', ['b2b.att.utilities'])
\r
8843 .constant('SliderConfig', {
\r
8849 .directive('b2bSlider', ['$documentBind', 'SliderConfig', 'keymap', '$compile', '$log', function($documentBind, SliderConfig, keymap, $compile, $log) {
\r
8853 require: 'ngModel',
\r
8854 templateUrl: 'b2bTemplate/slider/slider.html',
\r
8858 trackFillColor: '=?',
\r
8859 preAriaLabel: '=?',
\r
8860 postAriaLabel: '=?',
\r
8861 onRenderEnd: '&?',
\r
8862 sliderSnapPoints: '=?',
\r
8863 customAriaLabel: '=?',
\r
8866 link: function(scope, elm, attr, ngModelCtrl) {
\r
8867 scope.isDragging = false;
\r
8868 scope.verticalSlider = false;
\r
8871 var step = SliderConfig.step;
\r
8872 var skipInterval = SliderConfig.skipInterval;
\r
8873 var knob = angular.element(elm[0].querySelector('.slider-knob-container'));
\r
8874 var sliderKnob = angular.element(knob[0].querySelector('.slider-knob'));
\r
8875 var trackContainer = angular.element(elm[0].querySelector('.slider-track-container'));
\r
8876 var trackFill = angular.element(elm[0].querySelector('.slider-track-fill'));
\r
8877 var trackContainerRect = {};
\r
8878 var axisPosition = "clientX";
\r
8879 var trackFillOrderPositioning;
\r
8881 //Forcefully disabling the vertical Slider code.
\r
8882 if (angular.isDefined(attr.vertical)) {
\r
8883 scope.verticalSlider = true;
\r
8884 axisPosition = "clientY";
\r
8887 if (angular.isDefined(scope.noAriaLabel) && scope.noAriaLabel !== '') {
\r
8888 $log.warn('no-aria-label has been deprecated. This will be removed in v0.6.0.');
\r
8890 if (angular.isDefined(scope.preAriaLabel) && scope.preAriaLabel !== '') {
\r
8891 $log.warn('pre-aria-label has been deprecated. Please use label-id instead. This will be removed in v0.6.0.');
\r
8893 if (angular.isDefined(scope.customAriaLabel) && scope.customAriaLabel !== '') {
\r
8894 $log.warn('custom-aria-label has been deprecated. Please use label-id and post-aria-label instead. This will be removed in v0.6.0.');
\r
8897 var binarySearchNearest = function (num, arr) {
\r
8900 var hi = arr.length - 1;
\r
8902 while (hi - lo > 1) {
\r
8903 mid = Math.floor((lo + hi) / 2);
\r
8904 if (arr[mid] < num) {
\r
8910 if (num - arr[lo] < arr[hi] - num) {
\r
8916 var getValidStep = function(val) {
\r
8917 val = parseFloat(val);
\r
8918 // in case $modelValue came in string number
\r
8919 if (!isNaN(val)) {
\r
8921 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
\r
8922 val = binarySearchNearest(val, scope.sliderSnapPoints);
\r
8925 val = Math.round((val - min) / step) * step + min;
\r
8928 return Math.round(val * 1000) / 1000;
\r
8932 var getPositionToPercent = function(x) {
\r
8933 if (scope.verticalSlider) {
\r
8934 return Math.max(0, Math.min(1, (trackContainerRect.bottom - x) / (trackFillOrderPositioning)));
\r
8937 return Math.max(0, Math.min(1, (x - trackContainerRect.left) / (trackFillOrderPositioning)));
\r
8941 var getPercentToValue = function(percent) {
\r
8942 return (min + percent * (max - min));
\r
8945 var getValueToPercent = function(val) {
\r
8946 return (val - min) / (max - min);
\r
8949 var getValidMinMax = function(val) {
\r
8950 return Math.max(min, Math.min(max, val));
\r
8953 var updateTrackContainerRect = function() {
\r
8954 trackContainerRect = trackContainer[0].getBoundingClientRect();
\r
8955 if (scope.verticalSlider) {
\r
8956 if (!trackContainerRect.height) {
\r
8957 trackFillOrderPositioning = trackContainer[0].scrollHeight;
\r
8959 trackFillOrderPositioning = trackContainerRect.height;
\r
8963 if (!trackContainerRect.width) {
\r
8964 trackFillOrderPositioning = trackContainer[0].scrollWidth;
\r
8966 trackFillOrderPositioning = trackContainerRect.width;
\r
8973 var updateKnobPosition = function(percent) {
\r
8974 var percentStr = (percent * 100) + '%';
\r
8975 if (scope.verticalSlider) {
\r
8976 knob.css('bottom', percentStr);
\r
8977 trackFill.css('height', percentStr);
\r
8980 knob.css('left', percentStr);
\r
8981 trackFill.css('width', percentStr);
\r
8985 var modelRenderer = function() {
\r
8987 if(attr['disabled']){
\r
8991 if (isNaN(ngModelCtrl.$viewValue)) {
\r
8992 ngModelCtrl.$viewValue = ngModelCtrl.$modelValue || min;
\r
8995 var viewVal = ngModelCtrl.$viewValue;
\r
8996 scope.currentModelValue = viewVal;
\r
8998 //wait for min, max and step to be set then only update UI to avoid NaN on percent calculation
\r
8999 if ((min || min === 0) && max && step) {
\r
9000 updateKnobPosition(getValueToPercent(viewVal));
\r
9004 var setModelValue = function(val) {
\r
9005 scope.currentModelValue = getValidMinMax(getValidStep(val));
\r
9006 ngModelCtrl.$setViewValue(scope.currentModelValue);
\r
9009 var updateMin = function(val) {
\r
9010 min = parseFloat(val);
\r
9012 min = SliderConfig.min;
\r
9018 var updateMax = function(val) {
\r
9019 max = parseFloat(val);
\r
9021 max = SliderConfig.max;
\r
9027 var updateStep = function(val) {
\r
9028 step = parseFloat(val);
\r
9029 if (!attr['skipInterval']) {
\r
9030 skipInterval = step;
\r
9034 var updateSkipInterval = function(val) {
\r
9035 skipInterval = step * Math.ceil(val / (step!==0?step:1));
\r
9038 angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(SliderConfig.min);
\r
9039 angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(SliderConfig.max);
\r
9040 if (angular.isDefined(attr.step)) {
\r
9041 attr.$observe('step', updateStep);
\r
9043 if (angular.isDefined(attr.skipInterval)) {
\r
9044 attr.$observe('skipInterval', updateSkipInterval);
\r
9046 scope.currentModelValue = getValidMinMax(getValidStep(min));
\r
9047 var onMouseDown = function(e) {
\r
9049 if(attr['disabled']){
\r
9053 switch (e.which) {
\r
9055 // left mouse button
\r
9059 // right or middle mouse button
\r
9063 scope.isDragging = true;
\r
9064 sliderKnob[0].focus();
\r
9065 updateTrackContainerRect();
\r
9066 if (attr['onDragInit']) {
\r
9067 scope.onDragInit();
\r
9069 e.stopPropagation();
\r
9070 e.preventDefault();
\r
9071 scope.$apply(function() {
\r
9072 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
\r
9076 var onMouseUp = function() {
\r
9078 if (attr['onDragEnd']) {
\r
9079 scope.onDragEnd();
\r
9081 scope.isDragging = false;
\r
9085 var onMouseMove = function(e) {
\r
9086 if (scope.isDragging) {
\r
9087 e.stopPropagation();
\r
9088 e.preventDefault();
\r
9090 scope.$apply(function() {
\r
9091 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
\r
9096 function onKeyDown(e) {
\r
9097 if (!(e.keyCode)) {
\r
9098 e.keyCode = e.which;
\r
9101 switch (e.keyCode) {
\r
9102 case keymap.KEY.DOWN:
\r
9103 case keymap.KEY.LEFT:
\r
9104 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
\r
9105 var currentIndex = scope.sliderSnapPoints.indexOf(ngModelCtrl.$viewValue);
\r
9106 if (currentIndex > 0) {
\r
9109 updateStep = scope.sliderSnapPoints[currentIndex];
\r
9112 updateStep = ngModelCtrl.$viewValue - skipInterval;
\r
9115 case keymap.KEY.UP:
\r
9116 case keymap.KEY.RIGHT:
\r
9117 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
\r
9118 var currentIndex = scope.sliderSnapPoints.indexOf(ngModelCtrl.$viewValue);
\r
9119 if (currentIndex < scope.sliderSnapPoints.length-1) {
\r
9122 updateStep = scope.sliderSnapPoints[currentIndex];
\r
9125 updateStep = ngModelCtrl.$viewValue + skipInterval;
\r
9128 case keymap.KEY.END:
\r
9129 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
\r
9130 currentIndex = scope.sliderSnapPoints.length-1;
\r
9131 updateStep = scope.sliderSnapPoints[currentIndex];
\r
9133 setModelValue(scope.max);
\r
9135 e.preventDefault();
\r
9136 e.stopPropagation();
\r
9138 case keymap.KEY.HOME:
\r
9139 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
\r
9141 updateStep = scope.sliderSnapPoints[currentIndex];
\r
9143 setModelValue(scope.min);
\r
9145 e.preventDefault();
\r
9146 e.stopPropagation();
\r
9152 if (angular.isNumber(updateStep) && !attr['disabled']) {
\r
9153 e.stopPropagation();
\r
9154 e.preventDefault();
\r
9155 scope.$apply(function() {
\r
9156 setModelValue(updateStep);
\r
9158 if (attr['onDragEnd']) {
\r
9159 scope.onDragEnd();
\r
9164 elm.on('keydown', onKeyDown);
\r
9165 elm.on('mousedown', onMouseDown);
\r
9167 $documentBind.event('mousemove', 'isDragging', onMouseMove, scope, true, 0);
\r
9168 $documentBind.event('mouseup', 'isDragging', onMouseUp, scope, true, 0);
\r
9170 attr.$observe('disabled', function (disabled) {
\r
9172 sliderKnob.removeAttr('tabindex');
\r
9174 sliderKnob.attr('tabindex', '0');
\r
9178 elm.toggleClass("slider-disabled", disabled);
\r
9180 if (angular.isDefined(attr.hideDisabledKnob)) {
\r
9181 scope.hideKnob = disabled;
\r
9185 ngModelCtrl.$render = function() {
\r
9186 if (!scope.isDragging) {
\r
9188 if (attr['onRenderEnd'] && !attr['disabled']) {
\r
9189 scope.onRenderEnd({currentModelValue: scope.currentModelValue});
\r
9193 ngModelCtrl.$viewChangeListeners.push(modelRenderer);
\r
9194 ngModelCtrl.$formatters.push(getValidMinMax);
\r
9195 ngModelCtrl.$formatters.push(getValidStep);
\r
9200 * @ngdoc directive
\r
9201 * @name Forms.att:spinButton
\r
9203 * @param {String} spin-button-id - An ID for the input field
\r
9204 * @param {Integer} min - Minimum value for the input
\r
9205 * @param {Integer} max - Maximum value for the input
\r
9206 * @param {Integer} step - Value by which input field increments or decrements on up/down arrow keys
\r
9207 * @param {Integer} page-step - Value by which input field increments or decrements on page up/down keys
\r
9208 * @param {boolean} input-model-key - Default value for input field
\r
9209 * @param {boolean} disabled-flag - A boolean that triggers directive once the min or max value has reached
\r
9212 * <file src="src/spinButton/docs/readme.md" />
\r
9215 * <section id="code">
\r
9216 <example module="b2b.att">
\r
9217 <file src="src/spinButton/docs/demo.html" />
\r
9218 <file src="src/spinButton/docs/demo.js" />
\r
9223 angular.module('b2b.att.spinButton', ['b2b.att.utilities'])
\r
9224 .constant('b2bSpinButtonConfig', {
\r
9229 inputModelKey: 'value',
\r
9230 disabledFlag: false
\r
9232 .directive('b2bSpinButton', ['keymap', 'b2bSpinButtonConfig', 'b2bUserAgent', function (keymap, b2bSpinButtonConfig, userAgent) {
\r
9235 require: '?ngModel',
\r
9236 transclude: false,
\r
9242 pageStep: '=pageStep',
\r
9243 spinButtonId: '@',
\r
9244 inputValue: '=ngModel',
\r
9245 inputModelKey: '@',
\r
9246 disabledFlag: "=?"
\r
9248 templateUrl: 'b2bTemplate/spinButton/spinButton.html',
\r
9249 controller: ['$scope', '$element', '$attrs', function (scope, element, attrs) {
\r
9251 scope.isMobile = userAgent.isMobile();
\r
9252 scope.notMobile = userAgent.notMobile();
\r
9254 scope.min = attrs.min ? scope.min : b2bSpinButtonConfig.min;
\r
9255 scope.max = attrs.max ? scope.max : b2bSpinButtonConfig.max;
\r
9256 scope.step = attrs.step ? scope.step : b2bSpinButtonConfig.step;
\r
9257 scope.pageStep = attrs.pageStep ? scope.pageStep : b2bSpinButtonConfig.pageStep;
\r
9258 scope.inputModelKey = attrs.inputModelKey ? scope.inputModelKey : b2bSpinButtonConfig.inputModelKey;
\r
9259 scope.disabledFlag = attrs.disabledFlag ? scope.disabledFlag : b2bSpinButtonConfig.disabledFlag;
\r
9261 if (scope.min < 0) {
\r
9264 if (scope.max > 999) {
\r
9268 scope.isPlusDisabled = function () {
\r
9269 return (scope.disabledFlag || scope.inputValue[scope.inputModelKey] >= scope.max);
\r
9271 scope.isMinusDisabled = function () {
\r
9272 return (scope.disabledFlag || scope.inputValue[scope.inputModelKey] <= scope.min);
\r
9275 scope.getValidateInputValue = function (value) {
\r
9276 if (value <= scope.min) {
\r
9278 } else if (value >= scope.max) {
\r
9285 scope.plus = function () {
\r
9286 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) + scope.step);
\r
9288 scope.minus = function () {
\r
9289 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) - scope.step);
\r
9291 scope.pagePlus = function () {
\r
9292 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) + scope.pageStep);
\r
9294 scope.pageMinus = function () {
\r
9295 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) - scope.pageStep);
\r
9299 link: function (scope, elem) {
\r
9301 if (scope.notMobile) {
\r
9302 angular.element(elem).find('input').attr('aria-live', 'off');
\r
9303 angular.element(elem).find('input').attr('role', 'spinbutton');
\r
9306 elem.find('input').bind('keydown', function (e) {
\r
9307 if (e.keyCode === keymap.KEY.UP) {
\r
9309 } else if (e.keyCode === keymap.KEY.DOWN){
\r
9311 } else if (e.keyCode === keymap.KEY.HOME) {
\r
9312 scope.inputValue[scope.inputModelKey] = parseInt(scope.min);
\r
9313 } else if (e.keyCode === keymap.KEY.END) {
\r
9314 scope.inputValue[scope.inputModelKey] = parseInt(scope.max);
\r
9315 } else if (e.keyCode === keymap.KEY.PAGE_UP) {
\r
9317 } else if (e.keyCode === keymap.KEY.PAGE_DOWN) {
\r
9318 scope.pageMinus();
\r
9323 elem.find('input').bind('keyup', function () {
\r
9324 if (scope.inputValue[scope.inputModelKey] === null ||
\r
9325 scope.inputValue[scope.inputModelKey] === '' ||
\r
9326 scope.inputValue[scope.inputModelKey] < scope.min) {
\r
9327 scope.inputValue[scope.inputModelKey] = scope.min;
\r
9329 } else if (angular.isUndefined(scope.inputValue[scope.inputModelKey]) ||
\r
9330 scope.inputValue[scope.inputModelKey] > scope.max) {
\r
9331 scope.inputValue[scope.inputModelKey] = scope.max;
\r
9336 scope.focusInputSpinButton = function (evt) {
\r
9337 evt.preventDefault();
\r
9338 if (scope.notMobile) {
\r
9339 elem[0].querySelector('input').focus();
\r
9347 * @ngdoc directive
\r
9348 * @name Template.att:Static Route
\r
9351 * <file src="src/staticRouteTemplate/docs/readme.md" />
\r
9354 * <section id="code">
\r
9355 <example module="b2b.att">
\r
9356 <file src="src/staticRouteTemplate/docs/demo.html" />
\r
9357 <file src="src/staticRouteTemplate/docs/demo.js" />
\r
9362 angular.module('b2b.att.staticRouteTemplate', ['b2b.att.utilities'])
\r
9365 * @ngdoc directive
\r
9366 * @name Progress & usage indicators.att:statusTracker
\r
9369 * @param {array} statuses - An array of status objects
\r
9371 * <file src="src/statusTracker/docs/readme.md" />
\r
9375 <div ng-controller="statusTrackerController">
\r
9376 <b2b-status-tracker statuses="statusObject"></b2b-status-tracker>
\r
9380 <section id="code">
\r
9381 <b>HTML + AngularJS</b>
\r
9382 <example module="b2b.att">
\r
9383 <file src="src/statusTracker/docs/demo.html" />
\r
9384 <file src="src/statusTracker/docs/demo.js" />
\r
9389 angular.module('b2b.att.statusTracker', ['b2b.att.utilities'])
\r
9390 .constant('b2bStatusTrackerConfig', {
\r
9393 .directive('b2bStatusTracker', ['b2bStatusTrackerConfig', function(b2bStatusTrackerConfig) {
\r
9396 transclude: false,
\r
9401 templateUrl: function(scope) {
\r
9402 return 'b2bTemplate/statusTracker/statusTracker.html';
\r
9404 link: function(scope, element, attr) {
\r
9405 scope.currentViewIndex = 0;
\r
9406 scope.b2bStatusTrackerConfig = b2bStatusTrackerConfig;
\r
9408 scope.nextStatus = function() {
\r
9409 if (scope.currentViewIndex+1 <= scope.statuses.length) {
\r
9410 scope.currentViewIndex++;
\r
9413 scope.previousStatus = function() {
\r
9414 if (scope.currentViewIndex-1 >= 0) {
\r
9415 scope.currentViewIndex--;
\r
9418 scope.isInViewport = function(index) {
\r
9419 return (index < scope.currentViewIndex+3 && index >= scope.currentViewIndex); // && index > scope.currentViewIndex-2
\r
9421 scope.currentStatus = function(index) {
\r
9422 if(index != undefined){
\r
9423 if(!scope.statuses[index].complete) {
\r
9424 if(index > 0 && scope.statuses[index-1].complete) {
\r
9426 } else if(index == 0 && !scope.statuses[index].complete){
\r
9438 * @ngdoc directive
\r
9439 * @name Progress & usage indicators.att:stepTracker
\r
9442 * @param {array} stepsItemsObject - An array of step objects
\r
9443 * @param {Integer} currenIindex - This indicates the current running step
\r
9444 * @param {Integer} viewportIndex - This is optional. This can used to start the view port rather than 1 item.
\r
9446 * <file src="src/stepTracker/docs/readme.md" />
\r
9450 * <b2b-step-tracker steps-items-object="stepsObject" current-index="currentStepIndex" step-indicator-heading="stepHeading"></b2b-step-tracker>
\r
9454 <section id="code">
\r
9455 <b>HTML + AngularJS</b>
\r
9456 <example module="b2b.att">
\r
9457 <file src="src/stepTracker/docs/demo.html" />
\r
9458 <file src="src/stepTracker/docs/demo.js" />
\r
9462 angular.module('b2b.att.stepTracker', ['b2b.att.utilities'])
\r
9463 .constant('b2bStepTrackerConfig', {
\r
9466 .directive('b2bStepTracker', ['b2bStepTrackerConfig', function(b2bStepTrackerConfig) {
\r
9471 stepsItemsObject:"=",
\r
9473 viewportIndex:"=?"
\r
9475 templateUrl: 'b2bTemplate/stepTracker/stepTracker.html',
\r
9476 link: function(scope, ele, attr) {
\r
9477 if (angular.isDefined(scope.viewportIndex)) {
\r
9478 scope.currentViewIndex = scope.viewportIndex - 1;
\r
9480 scope.currentViewIndex = 0;
\r
9483 scope.b2bStepTrackerConfig = b2bStepTrackerConfig;
\r
9484 scope.nextStatus = function() {
\r
9485 if (scope.currentViewIndex+1 <= scope.stepsItemsObject.length) {
\r
9486 scope.currentViewIndex++;
\r
9489 scope.previousStatus = function() {
\r
9490 if (scope.currentViewIndex-1 >= 0) {
\r
9491 scope.currentViewIndex--;
\r
9494 scope.isInViewport = function(index) {
\r
9495 return (index < scope.currentViewIndex+b2bStepTrackerConfig.maxViewItems && index >= scope.currentViewIndex);
\r
9502 * @ngdoc directive
\r
9503 * @name Buttons, links & UI controls.att:switches
\r
9506 * <file src="src/switches/docs/readme.md" />
\r
9510 * <!-- On / Off Toggle switch -->
\r
9511 * <label for="switch1" class="controlled-text-wrap"> This is ON
\r
9512 * <input type="checkbox" role="checkbox" b2b-switches id="switch1" ng-model="switch1.value">
\r
9515 * <!-- On / Off Toggle switch and DISABLED -->
\r
9516 * <label for="switch2" class="controlled-text-wrap"> This is ON (disabled)
\r
9517 * <input type="checkbox" role="checkbox" b2b-switches id="switch2" ng-model="switch1.value" ng-disabled="true" >
\r
9522 * <section id="code">
\r
9523 <b>HTML + AngularJS</b>
\r
9524 <example module="b2b.att">
\r
9525 <file src="src/switches/docs/demo.js" />
\r
9526 <file src="src/switches/docs/demo.html" />
\r
9530 angular.module('b2b.att.switches', ['b2b.att.utilities'])
\r
9531 .directive('b2bSwitches', ['$compile', '$templateCache', 'keymap', 'events', function ($compile, $templateCache, keymap, events) {
\r
9534 require: ['ngModel'],
\r
9535 link: function (scope, element, attrs, ctrl) {
\r
9536 var ngModelController = ctrl[0];
\r
9538 element.parent().bind("keydown", function (e) {
\r
9539 if (!attrs.disabled && (e.keyCode === keymap.KEY.ENTER || e.keyCode === keymap.KEY.SPACE)) {
\r
9540 events.preventDefault(e);
\r
9541 ngModelController.$setViewValue(!ngModelController.$viewValue);
\r
9542 element.prop("checked", ngModelController.$viewValue);
\r
9546 element.wrap('<div class="btn-switch">');
\r
9547 //element.attr("tabindex", -1);
\r
9548 if (navigator.userAgent.match(/iphone/i)){
\r
9549 element.attr("aria-live", "polite");
\r
9552 element.removeAttr('aria-live');
\r
9555 var templateSwitch = angular.element($templateCache.get("b2bTemplate/switches/switches.html"));
\r
9556 if (angular.isDefined(attrs.typeSpanish)) {
\r
9557 templateSwitch = angular.element($templateCache.get("b2bTemplate/switches/switches-spanish.html"));
\r
9560 templateSwitch = $compile(templateSwitch)(scope);
\r
9561 element.parent().append(templateSwitch);
\r
9563 element.bind("focus", function (e) {
\r
9564 element.parent().addClass('focused');
\r
9567 element.bind("blur", function (e) {
\r
9568 element.parent().removeClass('focused');
\r
9574 * @ngdoc directive
\r
9575 * @name Messages, modals & alerts.att:tableMessages
\r
9578 * <file src="src/tableMessages/docs/readme.md" />
\r
9581 <!-- no matching results -->
\r
9582 <b2b-table-message msg-type="'noMatchingResults'">
\r
9583 <p>No Matching Results</p>
\r
9584 </b2b-table-message>
\r
9586 <!-- info could not load -->
\r
9587 <b2b-table-message msg-type="'infoCouldNotLoad'" on-refresh-click="refreshClicked()">
\r
9588 </b2b-table-message>
\r
9590 <!-- magnify search -->
\r
9591 <b2b-table-message msg-type="'magnifySearch'">
\r
9592 </b2b-table-message>
\r
9594 <!-- loading data -->
\r
9595 <b2b-table-message msg-type="'loadingTable'">
\r
9596 <!-- custom html -->
\r
9597 <p>The data is currently loading...</p>
\r
9598 </b2b-table-message>
\r
9601 <section id="code">
\r
9602 <b>HTML + AngularJS</b>
\r
9603 <example module="b2b.att">
\r
9604 <file src="src/tableMessages/docs/demo.html" />
\r
9605 <file src="src/tableMessages/docs/demo.js" />
\r
9609 angular.module('b2b.att.tableMessages', [])
\r
9610 .directive('b2bTableMessage', [function() {
\r
9617 onRefreshClick: '&'
\r
9619 templateUrl: 'b2bTemplate/tableMessages/tableMessage.html',
\r
9620 link: function(scope) {
\r
9621 scope.refreshAction = function(evt) {
\r
9622 scope.onRefreshClick(evt);
\r
9629 * @ngdoc directive
\r
9630 * @name Tabs, tables & accordions.att:tableScrollbar
\r
9633 * <file src="src/tableScrollbar/docs/readme.md" />
\r
9637 <b2b-table-scrollbar>
\r
9639 <thead type="header">
\r
9641 <th role="columnheader" scope="col" key="Id" id="col1">Id</th>
\r
9645 <tbody type="body">
\r
9647 <td id="rowheader0" headers="col1">1002</td>
\r
9652 </b2b-table-scrollbar>
\r
9655 * <section id="code">
\r
9656 <example module="b2b.att">
\r
9657 <file src="src/tableScrollbar/docs/demo.html" />
\r
9658 <file src="src/tableScrollbar/docs/demo.js" />
\r
9663 angular.module('b2b.att.tableScrollbar', [])
\r
9664 .directive('b2bTableScrollbar', ['$timeout', function ($timeout) {
\r
9669 templateUrl: 'b2bTemplate/tableScrollbar/tableScrollbar.html',
\r
9670 link: function (scope, element, attrs, ctrl) {
\r
9671 var firstThWidth, firstTdWidth, firstColumnWidth, firstColumnHeight, trHeight = 0;
\r
9672 var pxToScroll = '';
\r
9673 var tableElement = element.find('table');
\r
9674 var thElements = element.find('th');
\r
9675 var tdElements = element.find('td');
\r
9676 var innerContainer = angular.element(element[0].querySelector('.b2b-table-inner-container'));
\r
9677 var outerContainer = angular.element(element[0].querySelector('.b2b-table-scrollbar'));
\r
9679 scope.disableLeft = true;
\r
9680 scope.disableRight = false;
\r
9682 if (angular.isDefined(thElements[0])) {
\r
9683 firstThWidth = thElements[0].offsetWidth;
\r
9685 if (angular.isDefined(tdElements[0])) {
\r
9686 firstTdWidth = tdElements[0].offsetWidth;
\r
9688 firstColumnWidth = (firstThWidth > firstTdWidth) ? firstThWidth : firstTdWidth;
\r
9690 innerContainer.css({
\r
9691 'padding-left': (firstColumnWidth + 2) + 'px'
\r
9694 angular.forEach(element.find('tr'), function (eachTr, index) {
\r
9695 trObject = angular.element(eachTr);
\r
9696 firstColumn = angular.element(trObject.children()[0]);
\r
9698 angular.element(firstColumn).css({
\r
9699 'margin-left': -(firstColumnWidth + 2) + 'px',
\r
9700 'width': (firstColumnWidth + 2) + 'px',
\r
9701 'position': 'absolute'
\r
9704 trHeight = trObject[0].offsetHeight;
\r
9705 firstColumnHeight = firstColumn[0].offsetHeight;
\r
9706 if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
\r
9707 firstColumnHeight += 1;
\r
9710 if (trHeight !== firstColumnHeight - 1) {
\r
9711 if (trHeight > firstColumnHeight) {
\r
9712 if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
\r
9715 angular.element(firstColumn).css({
\r
9716 'height': (trHeight + 1) + 'px'
\r
9719 angular.element(trObject).css({
\r
9720 'height': (firstColumnHeight - 1) + 'px'
\r
9727 pxToScroll = outerContainer[0].offsetWidth - firstColumnWidth;
\r
9729 scope.scrollLeft = function () {
\r
9730 innerContainer[0].scrollLeft = innerContainer[0].scrollLeft + 20 - pxToScroll;
\r
9733 scope.scrollRight = function () {
\r
9734 innerContainer[0].scrollLeft = innerContainer[0].scrollLeft + pxToScroll - 20;
\r
9737 scope.checkScrollArrows = function () {
\r
9738 if (innerContainer[0].scrollLeft == 0) {
\r
9739 scope.disableLeft = true;
\r
9741 scope.disableLeft = false;
\r
9744 if (((innerContainer[0].offsetWidth - firstColumnWidth) + innerContainer[0].scrollLeft) >= tableElement[0].offsetWidth) {
\r
9745 scope.disableRight = true;
\r
9747 scope.disableRight = false;
\r
9751 innerContainer.bind('scroll', function () {
\r
9752 $timeout(function () {
\r
9753 scope.checkScrollArrows();
\r
9761 * @ngdoc directive
\r
9762 * @name Tabs, tables & accordions.att:tables
\r
9765 * <file src="src/tables/docs/readme.md" />
\r
9770 <table b2b-table table-data="tableData" search-string="searchString">
\r
9771 <thead b2b-table-row type="header">
\r
9773 <th b2b-table-header key="requestId" default-sort="a" id="col1">Header 1</th>
\r
9774 <th b2b-table-header key="requestType" sortable="false" id="col2">Header 2</th>
\r
9777 <tbody b2b-table-row type="body" row-repeat="rowData in tableData">
\r
9779 <td b2b-table-body id="rowheader{{$index}}" headers="col1" ng-bind="rowData['requestId']"> </td>
\r
9780 <td b2b-table-body ng-bind="rowData['requestType']"></td>
\r
9786 * <section id="code">
\r
9787 <example module="b2b.att">
\r
9788 <file src="src/tables/docs/demo.html" />
\r
9789 <file src="src/tables/docs/demo.js" />
\r
9794 angular.module('b2b.att.tables', ['b2b.att.utilities'])
\r
9795 .constant('b2bTableConfig', {
\r
9796 defaultSortPattern: false, // true for descending & false for ascending
\r
9797 highlightSearchStringClass: 'tablesorter-search-highlight',
\r
9798 zebraStripCutOff: 6, // > zebraStripCutOff
\r
9799 tableBreakpoints: [ // breakpoints are >= min and < max
\r
9822 .directive('b2bTable', ['$filter', '$window', 'b2bTableConfig', '$timeout', function ($filter, $window, b2bTableConfig, $timeout) {
\r
9832 searchCategory: "=",
\r
9833 searchString: "=",
\r
9836 require: 'b2bTable',
\r
9837 templateUrl: 'b2bTemplate/tables/b2bTable.html',
\r
9838 controller: ['$scope', '$attrs', function ($scope, $attrs) {
\r
9839 this.headers = [];
\r
9840 this.currentSortIndex = null;
\r
9841 this.responsive = $scope.responsive = $attrs.responsive;
\r
9842 this.maxTableColumns = -1;
\r
9843 this.totalTableColums = 0;
\r
9844 this.active = $scope.active = false;
\r
9845 this.responsiveRowScopes = [];
\r
9846 this.hideColumnPriority = [];
\r
9847 this.hiddenColumn = [];
\r
9848 this.setIndex = function (headerScope, priority) {
\r
9849 this.headers.push(headerScope);
\r
9850 if (this.responsive) {
\r
9851 this.totalTableColums++;
\r
9852 if (!isNaN(priority)) {
\r
9853 this.hideColumnPriority[priority] = this.totalTableColums - 1;
\r
9855 this.hideColumnPriority[this.totalTableColums - 1] = this.totalTableColums - 1;
\r
9858 return this.totalTableColums - 1;
\r
9860 this.getIndex = function (headerName) {
\r
9861 for (var i = 0; i < this.headers.length; i++) {
\r
9862 if (this.headers[i].headerName === headerName) {
\r
9863 return this.headers[i].index;
\r
9868 this.setResponsiveRow = function (responsiveRowScope) {
\r
9869 this.responsiveRowScopes.push(responsiveRowScope);
\r
9871 $scope.nextSort = '';
\r
9872 this.sortData = function (columnIndex, reverse, externalSort) {
\r
9873 if ($scope.$parent && $scope.$parent !== undefined) {
\r
9874 $scope.$parent.columnIndex = columnIndex;
\r
9875 $scope.$parent.reverse = reverse;
\r
9877 this.currentSortIndex = columnIndex;
\r
9878 if (externalSort === true) {
\r
9880 $scope.nextSort = 'd'
\r
9882 $scope.nextSort = 'a'
\r
9885 $scope.currentPage = 1;
\r
9886 this.resetSortPattern();
\r
9888 this.getSearchString = function () {
\r
9889 return $scope.searchString;
\r
9891 this.resetSortPattern = function () {
\r
9892 for (var i = 0; i < this.headers.length; i++) {
\r
9893 var currentScope = this.headers[i];
\r
9894 if (currentScope.index !== this.currentSortIndex) {
\r
9895 currentScope.resetSortPattern();
\r
9900 $scope.$watch('nextSort', function (val) {
\r
9901 if ($scope.$parent && $scope.$parent !== undefined) {
\r
9902 $scope.$parent.nextSort = val;
\r
9907 link: function (scope, elem, attr, ctrl) {
\r
9908 scope.searchCriteria = {};
\r
9909 scope.tableBreakpoints = attr.tableConfig ? scope.$parent.$eval(attr.tableConfig) : angular.copy(b2bTableConfig.tableBreakpoints);
\r
9910 scope.$watchCollection('tableData', function (value) {
\r
9911 if (value && !isNaN(value.length)) {
\r
9912 scope.totalRows = value.length;
\r
9915 scope.$watch('currentPage', function (val) {
\r
9916 if (scope.$parent && scope.$parent !== undefined) {
\r
9917 scope.$parent.currentPage = val;
\r
9921 scope.$watch('viewPerPage', function (val) {
\r
9922 if (scope.$parent && scope.$parent !== undefined) {
\r
9923 scope.$parent.viewPerPage = val;
\r
9926 scope.$watch('totalRows', function (val) {
\r
9927 if (scope.$parent && scope.$parent !== undefined) {
\r
9928 if (val > b2bTableConfig.zebraStripCutOff) {
\r
9929 scope.$parent.zebraStripFlag = true;
\r
9931 scope.$parent.zebraStripFlag = false;
\r
9935 scope.$watch(function () {
\r
9936 return scope.totalRows / scope.viewPerPage;
\r
9937 }, function (value) {
\r
9938 if (!isNaN(value)) {
\r
9939 scope.totalPage = Math.ceil(value);
\r
9940 scope.currentPage = 1;
\r
9943 var searchValCheck = function (val) {
\r
9944 if (angular.isDefined(val) && val !== null && val !== "") {
\r
9948 var setSearchCriteria = function (v1, v2) {
\r
9949 if (searchValCheck(v1) && searchValCheck(v2)) {
\r
9950 var index = ctrl.getIndex(v2);
\r
9951 scope.searchCriteria = {};
\r
9952 if (index !== null) {
\r
9953 scope.searchCriteria[index] = v1;
\r
9955 } else if (searchValCheck(v1) && (!angular.isDefined(v2) || v2 === null || v2 === "")) {
\r
9956 scope.searchCriteria = {
\r
9960 scope.searchCriteria = {};
\r
9963 scope.$watch('searchCategory', function (newVal, oldVal) {
\r
9964 if (newVal !== oldVal) {
\r
9965 setSearchCriteria(scope.searchString, newVal);
\r
9968 scope.$watch('searchString', function (newVal, oldVal) {
\r
9969 if (newVal !== oldVal) {
\r
9970 setSearchCriteria(newVal, scope.searchCategory);
\r
9973 scope.$watchCollection('searchCriteria', function (val) {
\r
9974 if (scope.$parent && scope.$parent !== undefined) {
\r
9975 scope.$parent.searchCriteria = val;
\r
9977 scope.totalRows = scope.tableData && ($filter('filter')(scope.tableData, val, false)).length || 0;
\r
9978 scope.currentPage = 1;
\r
9980 var window = angular.element($window);
\r
9981 var findMaxTableColumns = function () {
\r
9983 windowWidth = $window.innerWidth;
\r
9984 ctrl.maxTableColumns = -1;
\r
9985 for (var i in scope.tableBreakpoints) {
\r
9986 if (windowWidth >= scope.tableBreakpoints[i].min && windowWidth < scope.tableBreakpoints[i].max) {
\r
9987 ctrl.maxTableColumns = scope.tableBreakpoints[i].columns;
\r
9991 if (ctrl.maxTableColumns > -1 && ctrl.totalTableColums > ctrl.maxTableColumns) {
\r
9992 ctrl.active = true;
\r
9994 ctrl.active = false;
\r
9996 for (var i in ctrl.responsiveRowScopes) {
\r
9997 ctrl.responsiveRowScopes[i].setActive(ctrl.active);
\r
10000 var findHiddenColumn = function () {
\r
10001 var columnDiffenence = ctrl.maxTableColumns > -1 ? ctrl.totalTableColums - ctrl.maxTableColumns : 0;
\r
10002 ctrl.hiddenColumn = [];
\r
10003 if (columnDiffenence > 0) {
\r
10004 var tempHideColumnPriority = angular.copy(ctrl.hideColumnPriority);
\r
10005 for (var i = 0; i < columnDiffenence; i++) {
\r
10006 ctrl.hiddenColumn.push(tempHideColumnPriority.pop());
\r
10010 var resizeListener = function () {
\r
10011 findMaxTableColumns();
\r
10012 findHiddenColumn();
\r
10014 if (ctrl.responsive) {
\r
10015 window.bind('resize', function () {
\r
10016 resizeListener();
\r
10019 $timeout(function () {
\r
10020 resizeListener();
\r
10026 .directive('b2bTableRow', [function () {
\r
10029 compile: function (elem, attr) {
\r
10030 if (attr.type === 'header') {
\r
10032 } else if (attr.type === 'body') {
\r
10033 var html = elem.children();
\r
10034 if (attr.rowRepeat) {
\r
10035 html.attr('ng-repeat', attr.rowRepeat.concat(" | orderBy : columnIndex : reverse | filter : searchCriteria : false "));
\r
10037 html.attr('ng-class', "{'odd': $odd && zebraStripFlag}");
\r
10038 html.attr('class', 'data-row');
\r
10039 html.attr('b2b-responsive-row', '{{$index}}');
\r
10040 elem.append(html);
\r
10045 .directive('b2bTableHeader', ['b2bTableConfig', function (b2bTableConfig) {
\r
10049 transclude: true,
\r
10052 defaultSort: '@',
\r
10055 require: '^b2bTable',
\r
10056 templateUrl: function (elem, attr) {
\r
10057 if (attr.sortable === 'false') {
\r
10058 return 'b2bTemplate/tables/b2bTableHeaderUnsortable.html';
\r
10060 return 'b2bTemplate/tables/b2bTableHeaderSortable.html';
\r
10063 link: function (scope, elem, attr, ctrl) {
\r
10064 var reverse = b2bTableConfig.defaultSortPattern;
\r
10065 scope.headerName = elem.text();
\r
10066 scope.headerId = elem.attr('id');
\r
10067 scope.sortPattern = null;
\r
10068 var priority = parseInt(attr.priority, 10);
\r
10069 scope.columnIndex = ctrl.setIndex(scope, priority);
\r
10071 scope.isHidden = function () {
\r
10072 return (ctrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
\r
10075 scope.$watch(function () {
\r
10076 return elem.text();
\r
10077 }, function (value) {
\r
10078 scope.headerName = value;
\r
10080 scope.sort = function (sortType) {
\r
10081 if (typeof sortType === 'boolean') {
\r
10082 reverse = sortType;
\r
10084 ctrl.sortData(scope.index, reverse, false);
\r
10085 scope.sortPattern = reverse ? 'descending' : 'ascending';
\r
10086 reverse = !reverse;
\r
10088 scope.$watch(function () {
\r
10089 return ctrl.currentSortIndex;
\r
10090 }, function (value) {
\r
10091 if (value !== scope.index) {
\r
10092 scope.sortPattern = null;
\r
10096 if (scope.sortable === undefined || scope.sortable === 'true' || scope.sortable === true) {
\r
10097 scope.sortable = 'true';
\r
10098 } else if (scope.sortable === false || scope.sortable === 'false') {
\r
10099 scope.sortable = 'false';
\r
10102 if (scope.sortable !== 'false') {
\r
10103 if (scope.defaultSort === 'A' || scope.defaultSort === 'a') {
\r
10104 scope.sort(false);
\r
10105 } else if (scope.defaultSort === 'D' || scope.defaultSort === 'd') {
\r
10106 scope.sort(true);
\r
10109 scope.resetSortPattern = function () {
\r
10110 reverse = b2bTableConfig.defaultSortPattern;
\r
10115 .directive('b2bResponsiveRow', ['$templateCache', '$timeout', '$compile', function ($templateCache, $timeout, $compile) {
\r
10118 require: '^b2bTable',
\r
10119 controller: ['$scope', function ($scope) {
\r
10120 this.rowValues = $scope.rowValues = [];
\r
10121 this.setRowValues = function (rowValue) {
\r
10122 this.rowValues.push(rowValue);
\r
10124 var columnIndexCounter = -1;
\r
10125 this.getIndex = function () {
\r
10126 columnIndexCounter++;
\r
10127 return columnIndexCounter;
\r
10130 link: function (scope, elem, attr, ctrl) {
\r
10131 if (ctrl.responsive) {
\r
10132 scope.rowIndex = attr.b2bResponsiveRow;
\r
10133 scope.active = false;
\r
10134 scope.expandFlag = false;
\r
10135 scope.headerValues = ctrl.headers;
\r
10136 ctrl.setResponsiveRow(scope);
\r
10137 var firstTd = elem.find('td').eq(0);
\r
10138 scope.setActive = function (activeFlag) {
\r
10139 scope.active = activeFlag;
\r
10140 if (scope.active) {
\r
10141 elem.addClass('has-button');
\r
10142 firstTd.attr('role', 'rowheader');
\r
10143 firstTd.parent().attr('role', 'row');
\r
10145 elem.removeClass('has-button');
\r
10146 firstTd.removeAttr('role');
\r
10147 firstTd.parent().removeAttr('role');
\r
10150 scope.toggleExpandFlag = function (expandFlag) {
\r
10151 if (angular.isDefined(expandFlag)) {
\r
10152 scope.expandFlag = expandFlag;
\r
10154 scope.expandFlag = !scope.expandFlag;
\r
10156 if (scope.expandFlag) {
\r
10157 elem.addClass('opened');
\r
10159 elem.removeClass('opened');
\r
10163 firstTd.attr('scope', 'row');
\r
10164 firstTd.addClass('col-1');
\r
10165 scope.$on('$destroy', function () {
\r
10166 elem.next().remove();
\r
10168 $timeout(function () {
\r
10169 scope.firstTdId = firstTd.attr('id');
\r
10170 var firstTdContent = firstTd.html();
\r
10171 var toggleButtonTemplate = '<span ng-show="!active">' + firstTdContent + '</span><button type="button" aria-describedby="sup-actNum{{$id}}" aria-expanded="{{expandFlag}}" ng-show="active" ng-click="toggleExpandFlag()"><i ng-class="{\'icon-primary-accordion-plus\': !expandFlag, \'icon-primary-accordion-minus\': expandFlag}" aria-hidden="true"></i>' + firstTdContent + '</button><span id="sup-actNum{{$id}}" style="display:none">{{expandFlag && "Hide row below." || "Show row below."}}</span>';
\r
10172 toggleButtonTemplate = $compile(toggleButtonTemplate)(scope);
\r
10173 firstTd.html('');
\r
10174 firstTd.prepend(toggleButtonTemplate);
\r
10176 var template = $templateCache.get('b2bTemplate/tables/b2bResponsiveRow.html');
\r
10177 template = $compile(template)(scope);
\r
10178 elem.after(template);
\r
10184 .directive('b2bResponsiveList', ['$templateCache', '$timeout', '$compile', function ($templateCache, $timeout, $compile) {
\r
10187 require: '^b2bTable',
\r
10188 link: function (scope, elem, attr, ctrl) {
\r
10189 scope.columnIndex = parseInt(attr.b2bResponsiveList, 10);
\r
10190 scope.isVisible = function () {
\r
10191 return (ctrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
\r
10196 .directive('b2bTableBody', ['$filter', '$timeout', 'b2bTableConfig', function ($filter, $timeout, b2bTableConfig) {
\r
10199 require: ['^b2bTable', '?^b2bResponsiveRow'],
\r
10202 transclude: true,
\r
10203 templateUrl: 'b2bTemplate/tables/b2bTableBody.html',
\r
10204 link: function (scope, elem, attr, ctrl) {
\r
10205 var b2bTableCtrl = ctrl[0];
\r
10206 var b2bResponsiveRowCtrl = ctrl[1];
\r
10207 var highlightSearchStringClass = b2bTableConfig.highlightSearchStringClass;
\r
10208 var searchString = "";
\r
10209 var wrapElement = function (elem) {
\r
10210 var text = elem.text();
\r
10211 elem.html($filter('b2bHighlight')(text, searchString, highlightSearchStringClass));
\r
10213 var traverse = function (elem) {
\r
10214 var innerHtml = elem.children();
\r
10215 if (innerHtml.length > 0) {
\r
10216 for (var i = 0; i < innerHtml.length; i++) {
\r
10217 traverse(innerHtml.eq(i));
\r
10220 wrapElement(elem);
\r
10224 var clearWrap = function (elem) {
\r
10225 var elems = elem.find('*');
\r
10226 for (var i = 0; i < elems.length; i++) {
\r
10227 if (elems.eq(i).attr('class') && elems.eq(i).attr('class').indexOf(highlightSearchStringClass) !== -1) {
\r
10228 var text = elems.eq(i).text();
\r
10229 elems.eq(i).replaceWith(text);
\r
10233 if (b2bResponsiveRowCtrl) {
\r
10234 scope.columnIndex = b2bResponsiveRowCtrl.getIndex();
\r
10235 scope.isHidden = function () {
\r
10236 return (b2bTableCtrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
\r
10239 $timeout(function () {
\r
10240 var actualHtml = elem.children();
\r
10241 scope.$watch(function () {
\r
10242 return b2bTableCtrl.getSearchString();
\r
10243 }, function (val) {
\r
10244 searchString = val;
\r
10246 if (actualHtml.length > 0) {
\r
10249 wrapElement(elem);
\r
10252 if (b2bResponsiveRowCtrl) {
\r
10253 b2bResponsiveRowCtrl.setRowValues(elem.html());
\r
10259 .directive('b2bTableSort', ['b2bTableConfig','$timeout', function (b2bTableConfig,$timeout) {
\r
10263 require: '^b2bTable',
\r
10264 link: function (scope, elem, attr, ctrl) {
\r
10265 var initialSort = '',
\r
10268 initialSort = attr.initialSort;
\r
10270 scope.sortTable = function (msg) {
\r
10271 $timeout(function(){
\r
10272 if (nextSort.length > 0) {
\r
10274 if (nextSort === 'd' || nextSort === 'D') {
\r
10275 tempsort = nextSort
\r
10276 ctrl.sortData(msg, true, true);
\r
10278 $timeout(function(){
\r
10279 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
\r
10280 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
\r
10285 tempsort = nextSort
\r
10286 ctrl.sortData(msg, false, true);
\r
10288 $timeout(function(){
\r
10289 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
\r
10290 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
\r
10294 } else if (initialSort.length > 0) {
\r
10296 if (initialSort === 'd' || initialSort === 'D') {
\r
10297 tempsort = nextSort
\r
10298 ctrl.sortData(msg, true, true);
\r
10300 $timeout(function(){
\r
10301 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
\r
10302 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
\r
10307 tempsort = nextSort
\r
10308 ctrl.sortData(msg, false, true);
\r
10310 $timeout(function(){
\r
10311 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
\r
10312 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
\r
10323 scope.sortDropdown = function(msg) {
\r
10325 if(tempsort==='') {
\r
10329 if(tempsort === 'd' || tempsort === 'D' ) {
\r
10330 ctrl.sortData(msg, true, false);
\r
10332 ctrl.sortData(msg, false, false);
\r
10340 * @ngdoc directive
\r
10341 * @name Tabs, tables & accordions.att:tabs
\r
10344 * <file src="src/tabs/docs/readme.md" />
\r
10347 * <b2b-tabset tab-id-selected="activeTabsId">
\r
10348 <b2b-tab ng-repeat="tab in gTabs" tab-item="tab"
\r
10349 id="{{tab.uniqueId}}" aria-controls="{{tab.tabPanelId}}"
\r
10350 ng-disabled="tab.disabled">
\r
10356 * <section id="code">
\r
10357 <example module="b2b.att">
\r
10358 <file src="src/tabs/docs/demo.html" />
\r
10359 <file src="src/tabs/docs/demo.js" />
\r
10365 angular.module('b2b.att.tabs', ['b2b.att.utilities'])
\r
10366 .directive('b2bTabset', function () {
\r
10369 transclude: true,
\r
10372 tabIdSelected: '='
\r
10374 templateUrl: 'b2bTemplate/tabs/b2bTabset.html',
\r
10375 controller: ['$scope', function ($scope) {
\r
10377 this.setTabIdSelected = function (tab) {
\r
10378 $scope.tabIdSelected = tab.id;
\r
10381 this.getTabIdSelected = function () {
\r
10382 return $scope.tabIdSelected;
\r
10387 .directive('b2bTab', ['keymap', function (keymap) {
\r
10390 transclude: true,
\r
10392 require: '^b2bTabset',
\r
10396 templateUrl: 'b2bTemplate/tabs/b2bTab.html',
\r
10397 controller: [function(){}],
\r
10398 link: function (scope, element, attr, b2bTabsetCtrl) {
\r
10400 if (scope.tabItem && !scope.tabItem.disabled) {
\r
10401 scope.tabItem.disabled = false;
\r
10404 scope.isTabActive = function () {
\r
10405 return (scope.tabItem.id === b2bTabsetCtrl.getTabIdSelected());
\r
10408 scope.clickTab = function () {
\r
10409 if (attr.disabled) {
\r
10412 b2bTabsetCtrl.setTabIdSelected(scope.tabItem);
\r
10415 scope.nextKey = function () {
\r
10416 var el = angular.element(element[0])[0];
\r
10417 var elementToFocus = null;
\r
10418 while (el && el.nextElementSibling) {
\r
10419 el = el.nextElementSibling;
\r
10420 if (!el.querySelector('a').disabled) {
\r
10421 elementToFocus = el.querySelector('a');
\r
10426 if (!elementToFocus) {
\r
10427 var childTabs = element.parent().children();
\r
10428 for (var i = 0; i < childTabs.length; i++) {
\r
10429 if (!childTabs[i].querySelector('a').disabled) {
\r
10430 elementToFocus = childTabs[i].querySelector('a');
\r
10436 if (elementToFocus) {
\r
10437 elementToFocus.focus();
\r
10441 scope.previousKey = function () {
\r
10442 var el = angular.element(element[0])[0];
\r
10443 var elementToFocus = null;
\r
10445 while (el && el.previousElementSibling) {
\r
10446 el = el.previousElementSibling;
\r
10447 if (!el.querySelector('a').disabled) {
\r
10448 elementToFocus = el.querySelector('a');
\r
10453 if (!elementToFocus) {
\r
10454 var childTabs = element.parent().children();
\r
10455 for (var i = childTabs.length - 1; i > 0; i--) {
\r
10456 if (!childTabs[i].querySelector('a').disabled) {
\r
10457 elementToFocus = childTabs[i].querySelector('a');
\r
10463 if (elementToFocus) {
\r
10464 elementToFocus.focus();
\r
10468 angular.element(element[0].querySelector('a')).bind('keydown', function (evt) {
\r
10470 if (!(evt.keyCode)) {
\r
10471 evt.keyCode = evt.which;
\r
10474 switch (evt.keyCode) {
\r
10475 case keymap.KEY.RIGHT:
\r
10476 evt.preventDefault();
\r
10480 case keymap.KEY.LEFT:
\r
10481 evt.preventDefault();
\r
10482 scope.previousKey();
\r
10492 * @ngdoc directive
\r
10493 * @name Messages, modals & alerts.att:tagBadges
\r
10496 * <file src="src/tagBadges/docs/readme.md" />
\r
10499 * <section id="code">
\r
10500 <example module="b2b.att">
\r
10501 <file src="src/tagBadges/docs/demo.html" />
\r
10502 <file src="src/tagBadges/docs/demo.js" />
\r
10507 angular.module('b2b.att.tagBadges', ['b2b.att.utilities'])
\r
10508 .directive('b2bTagBadge',['$timeout',function($timeout){
\r
10511 link: function(scope,elem,attr,ctrl){
\r
10512 elem.addClass('b2b-tags');
\r
10513 if(angular.element(elem[0].querySelector('.icon-primary-close')).length>0) {
\r
10514 var item = angular.element(elem[0].querySelector('.icon-primary-close'));
\r
10515 item.bind('click',function(){
\r
10516 elem.css({'height':'0','width':'0','padding':'0','border':'0'});
\r
10517 elem.attr('tabindex','0');
\r
10519 item.parent().remove();
\r
10520 elem[0].bind('blur',function(){
\r
10521 elem[0].remove();
\r
10533 * @ngdoc directive
\r
10534 * @name Forms.att:textArea
\r
10537 * <file src="src/textArea/docs/readme.md" />
\r
10540 * <textarea b2b-reset b2b-reset-textarea ng-model="textAreaModel" ng-disabled="disabled" ng-trim="false" placeholder="{{placeholderText}}" rows="{{textAreaRows}}" maxlength="{{textAreaMaxlength}}" role="textarea"></textarea>
\r
10543 <section id="code">
\r
10544 <b>HTML + AngularJS</b>
\r
10545 <example module="b2b.att">
\r
10546 <file src="src/textArea/docs/demo.html" />
\r
10547 <file src="src/textArea/docs/demo.js" />
\r
10551 angular.module('b2b.att.textArea', ['b2b.att.utilities'])
\r
10553 .directive('b2bResetTextarea', [ function () {
\r
10556 require: 'b2bReset',
\r
10557 link: function (scope, element, attrs, ctrl) {
\r
10559 var resetButton = ctrl.getResetButton();
\r
10561 var computeScrollbarAndAddClass = function () {
\r
10562 if (element.prop('scrollHeight') > element[0].clientHeight) {
\r
10563 element.addClass('hasScrollbar');
\r
10565 element.removeClass('hasScrollbar');
\r
10569 computeScrollbarAndAddClass();
\r
10571 element.on('focus keyup', function(){
\r
10572 computeScrollbarAndAddClass();
\r
10579 * @ngdoc directive
\r
10580 * @name Forms.att:tooltipsForForms
\r
10583 * <file src="src/tooltipsForForms/docs/readme.md" />
\r
10586 <example module="b2b.att">
\r
10587 <file src="src/tooltipsForForms/docs/demo.html" />
\r
10588 <file src="src/tooltipsForForms/docs/demo.js" />
\r
10591 angular.module('b2b.att.tooltipsForForms', ['b2b.att.utilities'])
\r
10592 .directive('b2bTooltip', ['$document', '$window', '$isElement', function ($document, $window, $isElement) {
\r
10595 link: function (scope, elem, attr, ctrl) {
\r
10596 var icon = elem[0].querySelector('a.tooltip-element');
\r
10597 var btnIcon = elem[0].querySelector('.btn.tooltip-element');
\r
10598 var tooltipText = elem[0].querySelector('.helpertext');
\r
10599 var tooltipWrapper = elem[0].querySelector('.tooltip-size-control');
\r
10600 if (elem.hasClass('tooltip-onfocus')) {
\r
10601 var inputElm = angular.element(elem[0].querySelector("input"));
\r
10602 var textAreaElm = angular.element(elem[0].querySelector("textarea"));
\r
10604 angular.element(icon).attr({'aria-expanded': false});
\r
10605 angular.element(btnIcon).attr({'aria-expanded': false});
\r
10606 var calcTooltip = function () {
\r
10607 if (!elem.hasClass('tooltip active')) {
\r
10608 if (elem.hasClass('tooltip-onfocus')) {
\r
10609 angular.element(elem[0].querySelector("input")).triggerHandler('focusout');
\r
10611 if (elem.hasClass('tooltip-onclick')) {
\r
10614 angular.element(icon).removeClass('active');
\r
10615 angular.element(icon).attr({'aria-expanded': true});
\r
10616 angular.element(icon).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
\r
10617 angular.element(tooltipText).attr({'aria-hidden': false});
\r
10618 elem.addClass('active');
\r
10620 var tooltipIconPos = angular.element(icon).prop('offsetLeft'),
\r
10621 tooltipPosition = angular.element(tooltipText).prop('offsetWidth') / 2,
\r
10622 tipOffset = (tooltipIconPos - 30) - tooltipPosition,
\r
10623 maxRightPos = (($window.innerWidth - 72) - (tooltipPosition * 2)) - 14.5;
\r
10625 if ($window.innerWidth >= '768') {
\r
10626 if (tipOffset < 0) {// if icon on far left side of page
\r
10629 else if (tooltipIconPos > maxRightPos) {// if icon is far right side of page
\r
10630 tipOffset = maxRightPos;
\r
10632 else {// if tooltip in the middle somewhere
\r
10633 tipOffset = tipOffset;
\r
10635 angular.element(tooltipWrapper).css({left: tipOffset + 'px'});
\r
10640 // TOOLTIP LINK ONCLICK AND FOCUS
\r
10641 angular.element(icon).on('click mouseover mouseout focus blur', function (e) {
\r
10642 if (e.type == 'mouseover') {
\r
10645 else if (e.type == 'mouseout' && elem.hasClass('active')) {
\r
10646 if (!elem.hasClass('activeClick')) {
\r
10647 angular.element(tooltipText).attr({
\r
10648 'aria-hidden': true,
\r
10651 elem.removeClass('active');
\r
10652 } else if (elem.hasClass('activeClick') && navigator.userAgent.match(/iphone/i)) {
\r
10653 elem.removeClass('active activeClick');
\r
10658 if (elem.hasClass('activeClick')) {
\r
10659 angular.element(icon).attr({'aria-expanded': false});
\r
10660 angular.element(tooltipText).attr({'aria-hidden': true});
\r
10661 angular.element(icon).removeAttr('aria-describedby');
\r
10662 elem.removeClass('activeClick active');
\r
10663 e.preventDefault();
\r
10665 else if (e.type == 'click') {
\r
10666 elem.addClass('activeClick');
\r
10668 e.preventDefault();
\r
10671 angular.element(icon).on('keydown', function (e) {
\r
10672 if (e.keyCode == '32') {
\r
10673 (elem.hasClass('active')) ? elem.removeClass('active') : elem.addClass('value');
\r
10674 angular.element(icon).triggerHandler('click');
\r
10675 e.preventDefault();
\r
10676 } else if (e.keyCode == '27') {
\r
10677 (elem.hasClass('active')) ? elem.removeClass('active activeClick') : elem.addClass('value');
\r
10680 e.preventDefault();
\r
10683 e.preventDefault();
\r
10686 // TOOLTIP BUTTON INSIDE A TEXT FIELD
\r
10687 angular.element(btnIcon).on('click', function (e) {
\r
10688 var $this = angular.element(this);
\r
10689 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
\r
10690 elem.removeClass('active');
\r
10691 $this.removeClass('active');
\r
10692 angular.element(tooltipText).removeAttr('aria-live');
\r
10693 $this.attr({'aria-expanded': 'false'});
\r
10694 $this.removeAttr('aria-describedby');
\r
10696 elem.addClass('active');
\r
10697 $this.addClass('active');
\r
10698 $this.attr({'aria-expanded': 'true', 'aria-describedby': angular.element(tooltipText).attr('id')});
\r
10699 angular.element(tooltipText).attr({'aria-live': 'polite'});
\r
10703 angular.element(btnIcon).on('blur', function (e) {
\r
10704 var $this = angular.element(this);
\r
10705 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
\r
10706 elem.removeClass('active');
\r
10707 $this.removeClass('active');
\r
10708 angular.element(tooltipText).removeAttr('aria-live');
\r
10709 $this.attr({'aria-expanded': 'false'});
\r
10710 $this.removeAttr('aria-describedby');
\r
10714 angular.element(btnIcon).on('keydown', function (e) {
\r
10715 var $this = angular.element(this);
\r
10716 if (e.keyCode == '27') {
\r
10717 var $this = angular.element(this);
\r
10718 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
\r
10719 elem.removeClass('active');
\r
10720 $this.removeClass('active');
\r
10721 angular.element(tooltipText).removeAttr('aria-live');
\r
10722 $this.attr({'aria-expanded': 'false'});
\r
10723 $this.removeAttr('aria-describedby');
\r
10728 // close all tooltips if clicking something else
\r
10729 $document.bind('click', function (e) {
\r
10730 var isElement = $isElement(angular.element(e.target), elem, $document);
\r
10731 if (!isElement) {
\r
10732 elem.removeClass('active');
\r
10733 angular.element(elem[0].querySelector('.tooltip-element')).removeClass('active');
\r
10734 angular.element(tooltipText).removeAttr('aria-live');
\r
10735 angular.element(elem[0].querySelector('.tooltip-element')).attr({'aria-expanded': 'false'});
\r
10736 angular.element(elem[0].querySelector('.tooltip-element')).removeAttr('aria-describedby');
\r
10740 angular.element(inputElm).on('keydown', function (e) {
\r
10741 if (e.keyCode == '27'){
\r
10742 elem.removeClass('active');
\r
10743 angular.element(tooltipText).css('display', 'none');
\r
10744 angular.element(tooltipText).removeAttr('aria-live');
\r
10746 if (angular.element(this).attr('aria-describedby') === undefined){
\r
10750 else if ((spaceIndex = angular.element(this).attr('aria-describedby').lastIndexOf(' ')) >= 0){
\r
10752 var describedByValue = angular.element(this).attr('aria-describedby').slice(0, spaceIndex);
\r
10754 angular.element(this).attr('aria-describedby', describedByValue);
\r
10758 angular.element(this).removeAttr('aria-describedby');
\r
10763 angular.element(textAreaElm).on('keydown', function (e) {
\r
10764 if (e.keyCode == '27'){
\r
10765 elem.removeClass('active');
\r
10766 angular.element(tooltipText).css('display', 'none');
\r
10767 angular.element(tooltipText).removeAttr('aria-live');
\r
10768 if (angular.element(this).attr('aria-describedby') === undefined){
\r
10772 else if ((spaceIndex = angular.element(this).attr('aria-describedby').lastIndexOf(' ')) >= 0){
\r
10774 var describedByValue = angular.element(this).attr('aria-describedby').slice(0, spaceIndex);
\r
10776 angular.element(this).attr('aria-describedby', describedByValue);
\r
10780 angular.element(this).removeAttr('aria-describedby');
\r
10785 // TOOLTIP TRIGGERED AUTOMATICALLY INSIDE A TEXT FIELD
\r
10786 angular.element(inputElm).on('focus', function (e) {
\r
10787 var allTooltip = $document[0].querySelectorAll('[class*="tooltip"]');
\r
10788 for (var i = 0; i < allTooltip.length; i++) {
\r
10789 if (angular.element(allTooltip[i]).hasClass('active')) {
\r
10790 angular.element(allTooltip[i]).triggerHandler('click');
\r
10793 angular.element(this).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
\r
10794 angular.element(tooltipText).css('display', 'block');
\r
10795 angular.element(tooltipText).attr({'aria-live': 'polite'});
\r
10796 elem.addClass('active');
\r
10798 angular.element(inputElm).on('blur', function (e) {
\r
10799 elem.removeClass('active');
\r
10800 angular.element(tooltipText).css('display', 'none');
\r
10801 angular.element(tooltipText).removeAttr('aria-live');
\r
10802 angular.element(this).removeAttr('aria-describedby');
\r
10805 // TOOLTIP TRIGGERED AUTOMATICALLY INSIDE A TEXTAREA
\r
10806 angular.element(textAreaElm).on('focus', function (e) {
\r
10807 var allTooltip = $document[0].querySelectorAll('[class*="tooltip"]');
\r
10808 for (var i = 0; i < allTooltip.length; i++) {
\r
10809 if (angular.element(allTooltip[i]).hasClass('active')) {
\r
10810 angular.element(allTooltip[i]).triggerHandler('click');
\r
10813 elem.addClass('active');
\r
10814 angular.element(tooltipText).css('display', 'block');
\r
10815 angular.element(tooltipText).attr({'aria-live': 'polite'});
\r
10816 angular.element(this).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
\r
10818 angular.element(textAreaElm).on('blur', function (e) {
\r
10819 elem.removeClass('active');
\r
10820 angular.element(tooltipText).css('display', 'none');
\r
10821 angular.element(tooltipText).removeAttr('aria-live');
\r
10822 angular.element(this).removeAttr('aria-describedby');
\r
10828 * @ngdoc directive
\r
10829 * @name Navigation.att:TreeNavigation
\r
10833 * @param {String} setRole - This value needs to be "tree". This is required to incorporate CATO requirements.
\r
10834 * @param {Boolean} groupIt - This value needs to be "false" for top-level tree rendered.
\r
10837 * <file src="src/treeNav/docs/readme.md" />
\r
10840 * <div class="b2b-tree">
\r
10841 * <b2b-tree-nav collection="treeStructure" set-role="tree" group-it="false"></b2b-tree-nav>
\r
10844 * <section id="code">
\r
10845 <example module="b2b.att">
\r
10846 <file src="src/treeNav/docs/demo.html" />
\r
10847 <file src="src/treeNav/docs/demo.js" />
\r
10852 angular.module('b2b.att.treeNav', ['b2b.att.utilities'])
\r
10853 .directive('b2bTreeNav', function () {
\r
10862 templateUrl: function (element, attrs) {
\r
10863 if (attrs.groupIt === 'true') {
\r
10864 return "b2bTemplate/treeNav/groupedTree.html";
\r
10866 return "b2bTemplate/treeNav/ungroupedTree.html";
\r
10869 link: function (scope) {
\r
10870 if (!(scope.setRole === 'tree')) {
\r
10871 scope.setRole = 'group';
\r
10876 .directive('b2bMember', ['$compile', '$timeout', 'keymap', function ($compile, $timeout, keymap) {
\r
10884 templateUrl: 'b2bTemplate/treeNav/treeMember.html',
\r
10885 link: function (scope, element, attrs) {
\r
10886 scope.elemArr = [];
\r
10887 var removeRootTabIndex = function (elem) {
\r
10888 if (elem.parent().parent().eq(0).hasClass('b2b-tree')) {
\r
10889 elem.attr('tabindex', -1);
\r
10892 removeRootTabIndex(elem.parent());
\r
10894 scope.$watch('member.child', function(newVal, oldVal){
\r
10895 if(newVal !== oldVal){
\r
10896 scope.showChild();
\r
10899 scope.showChild = function () {
\r
10900 if (!element.hasClass('grouped')) {
\r
10901 if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
\r
10902 scope.groupIt = false;
\r
10903 element.addClass('grouped');
\r
10904 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
\r
10905 $compile(element.contents())(scope);
\r
10906 if(scope.member.active && scope.member.active === true){
\r
10907 element.find('i').eq(0).removeClass('icon-primary-collapsed');
\r
10909 if(scope.member.selected && scope.member.selected === true){
\r
10910 element.attr('aria-selected', true);
\r
10911 element.attr('tabindex', 0);
\r
10912 removeRootTabIndex(element);
\r
10914 if(scope.member.active && scope.member.active == undefined){
\r
10915 element.find('i').eq(0).addClass('icon-primary-collapsed');
\r
10917 } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
\r
10918 element.addClass('grouped');
\r
10919 scope.groupIt = true;
\r
10920 // FILTER - GROUPBY - APPROACH
\r
10922 var grpName = '';
\r
10923 if(scope.member.child[0].groupName !== undefined){
\r
10924 grpName = scope.member.child[0].groupName;
\r
10927 var toSlice = scope.member.child[0].name.search(' ');
\r
10928 grpName = scope.member.child[0].name.slice(0, toSlice);
\r
10931 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
\r
10933 for (j = j + i; j < (i + scope.member.divide); j++) {
\r
10934 if (j === scope.member.child.length) {
\r
10935 scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
\r
10938 if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
\r
10939 scope.member.child[j-1].activeGrp = true;
\r
10943 if (i + scope.member.divide > scope.member.child.length) {
\r
10944 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
\r
10945 if(scope.member.child[j].active && scope.member.child[j].active===true){
\r
10946 scope.member.child[j].activeGrp = true;
\r
10950 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
\r
10951 if(scope.member.child[j].active && scope.member.child[j].active===true){
\r
10952 scope.member.child[j].activeGrp = true;
\r
10957 if(scope.member.divide){
\r
10958 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
\r
10960 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
\r
10962 $compile(element.contents())(scope);
\r
10963 if(scope.member.active && scope.member.active === true){
\r
10964 element.find('i').eq(0).removeClass('icon-primary-collapsed');
\r
10966 if(scope.member.selected && scope.member.selected === true){
\r
10967 element.attr('aria-selected', true);
\r
10969 if( scope.member.active && scope.member.active == undefined){
\r
10970 element.find('i').eq(0).addClass('icon-primary-collapsed');
\r
10975 //Below condition opens node for opening on json load.
\r
10976 if(scope.member.active && scope.member.active == true){
\r
10977 scope.showChild();
\r
10979 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
\r
10980 element.find('i').eq(0).addClass('icon-primary-collapsed');
\r
10982 else if(scope.member.child == undefined){
\r
10983 element.find('i').eq(0).addClass('icon-primary-circle');
\r
10985 element.bind('keydown', function (evt) {
\r
10986 switch (evt.keyCode) {
\r
10987 case keymap.KEY.ENTER:
\r
10988 if (element.hasClass('bg') && scope.member.onSelect !== undefined) {
\r
10989 scope.member.onSelect(scope.member);
\r
10991 evt.stopPropagation();
\r
10998 //else getting true in every case .. so better use switch case .. that makes more sense you dumb.
\r
10999 element.bind('click', function (evt) {
\r
11000 scope.showChild();
\r
11001 var expandFunc = scope.member.onExpand;
\r
11004 if (element.hasClass('bg') && scope.member.onSelect !== undefined) {
\r
11005 scope.member.onSelect(scope.member);
\r
11007 if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
\r
11008 var eValue = scope.member.onExpand(scope.member);
\r
11010 if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
\r
11011 scope.member.onCollapse(scope.member);
\r
11017 .directive('b2bTreeLink', ['keymap', '$timeout', function (keymap, $timeout) {
\r
11020 link: function (scope, element, attr, ctrl) {
\r
11021 var rootE, parentE, upE, downE;
\r
11022 var closeOthersUp = function (elem,isKeyPress,passiveClose) {
\r
11023 //For accordion functionality on sibling nodes
\r
11024 if (elem.find('a').eq(0).hasClass('active')) {
\r
11025 activeToggle(elem,isKeyPress,passiveClose);
\r
11028 if (elem.hasClass('bg') && !isKeyPress) {
\r
11029 elem.removeClass('bg');
\r
11030 if (elem.attr('aria-selected')) {
\r
11031 elem.attr('aria-selected', 'false');
\r
11034 if (elem[0].previousElementSibling !== null) {
\r
11035 closeOthersUp(angular.element(elem[0].previousElementSibling),isKeyPress);
\r
11038 var closeOthersDown = function (elem,isKeyPress,passiveClose) {
\r
11039 //For accordion functionality on sibling nodes
\r
11040 if (elem.find('a').eq(0).hasClass('active')) {
\r
11041 activeToggle(elem,isKeyPress,passiveClose);
\r
11044 if (elem.hasClass('bg') && !isKeyPress) {
\r
11045 elem.removeClass('bg');
\r
11046 if (elem.attr('aria-selected')) {
\r
11047 elem.attr('aria-selected', 'false');
\r
11050 if (elem[0].nextElementSibling !== null) {
\r
11051 closeOthersDown(angular.element(elem[0].nextElementSibling),isKeyPress);
\r
11056 var removeBackground = function(elem){
\r
11058 if(elem.hasClass('b2b-tree')){
\r
11059 angular.element(elem[0].getElementsByClassName('bg')).removeClass('bg');
\r
11062 removeBackground(elem.parent().parent());
\r
11068 * These two functions used for setting heights on parent nodes as the child node closes
\r
11069 * Retaining this code for future reference
\r
11071 var addParentHeight = function(e, h) {
\r
11072 var parentLi = e.parent().parent();
\r
11073 var parentUl = e.parent();
\r
11074 if(!parentLi.hasClass('b2b-tree')) {
\r
11075 var addHeight = parentUl[0].offsetHeight + h;
\r
11076 parentLi.find('ul').eq(0).css({
\r
11077 height: addHeight+'px'
\r
11079 addParentHeight(parentLi, h);
\r
11083 var removeParentHeight = function(e, h) {
\r
11084 var parentLi = e.parent().parent();
\r
11085 var parentUl = e.parent();
\r
11086 if(!parentLi.hasClass('b2b-tree')) {
\r
11087 var addHeight = parentUl[0].offsetHeight - h;
\r
11088 parentLi.find('ul').eq(0).css({
\r
11089 height: addHeight+'px'
\r
11091 removeParentHeight(parentLi, h);
\r
11096 // isKeyPress - to notify that the function is called by Right Key press
\r
11097 // passiveClose - prevents firing of oncollapse events during the action
\r
11098 // of expand function(check the function definition)
\r
11100 var activeToggle = function (elem,isKeyPress,passiveClose) {
\r
11101 var element = elem.find('a').eq(0);
\r
11102 if (element.hasClass('active')) {
\r
11104 elem.removeClass('bg');
\r
11107 if (elem.attr('aria-selected') && !isKeyPress) {
\r
11108 elem.attr('aria-selected', 'false');
\r
11110 if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
\r
11111 if(isKeyPress && scope.member){
\r
11112 if (scope.member.onCollapse !== undefined && !passiveClose) {
\r
11113 scope.member.onCollapse(scope.member);
\r
11116 element.removeClass('active');
\r
11117 elem.attr('aria-expanded', 'false');
\r
11118 element.find('i').eq(0).removeClass('icon-primary-expanded');
\r
11119 element.find('i').eq(0).addClass('icon-primary-collapsed');
\r
11120 //For Animation: below commented code is used to manually set height of UL to zero
\r
11121 //retaining code for future reference
\r
11123 var totalHeight = elem.find('ul')[0].scrollHeight;
\r
11124 removeParentHeight(elem, totalHeight);
\r
11125 elem.find('ul').eq(0).css({
\r
11131 elem.addClass('bg');
\r
11132 elem.attr('aria-selected', 'true');
\r
11135 if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
\r
11137 if(typeof scope.showChild === 'function' ){
\r
11138 scope.showChild();
\r
11140 if(scope.member){
\r
11141 if (scope.member.onExpand !== undefined) {
\r
11142 scope.member.onExpand(scope.member);
\r
11146 element.addClass('active');
\r
11147 elem.attr('aria-expanded', 'true');
\r
11148 element.find('i').eq(0).removeClass('icon-primary-collapsed');
\r
11149 element.find('i').eq(0).addClass('icon-primary-expanded');
\r
11150 //For Animation: below commented code is used to manually set height of the ul generatedon the click of parent LI.
\r
11151 //retaining code for future reference
\r
11153 var totalHeight = elem.find('ul')[0].scrollHeight;
\r
11154 addParentHeight(elem, totalHeight);
\r
11155 elem.find('ul').eq(0).css({
\r
11156 height: totalHeight+'px'
\r
11162 element.bind('click', function (evt) {
\r
11163 //first we close others and then we open the clicked element
\r
11164 if (element[0].previousElementSibling) {
\r
11165 closeOthersUp(angular.element(element[0].previousElementSibling));
\r
11167 if (element[0].nextElementSibling) {
\r
11168 closeOthersDown(angular.element(element[0].nextElementSibling));
\r
11170 removeBackground(element);
\r
11171 activeToggle(element);
\r
11173 evt.stopPropagation();
\r
11175 //default root tree element tabindex set zero
\r
11176 if (element.parent().parent().hasClass('b2b-tree') && (element.parent()[0].previousElementSibling === null)) {
\r
11177 element.attr('tabindex', 0);
\r
11179 //check root via class
\r
11180 var isRoot = function (elem) {
\r
11181 if (elem.parent().parent().eq(0).hasClass('b2b-tree')) {
\r
11187 var findRoot = function (elem) {
\r
11188 if (isRoot(elem)) {
\r
11192 findRoot(elem.parent());
\r
11195 var findPreActive = function (elem) {
\r
11197 if (!(elem.hasClass("active"))) {
\r
11200 var childElems = angular.element(elem[0].nextElementSibling.children);
\r
11201 lastE = angular.element(childElems[childElems.length - 1]);
\r
11202 if (lastE.find('a').eq(0).hasClass('active')) {
\r
11203 findPreActive(lastE.find('a').eq(0));
\r
11209 var findUp = function (elem) {
\r
11210 if (isRoot(elem)) {
\r
11214 if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
\r
11215 upE = angular.element(elem[0].previousElementSibling);
\r
11216 if (upE.find('a').eq(0).hasClass('active')) {
\r
11217 findPreActive(upE.find('a').eq(0));
\r
11220 upE = elem.parent().parent();
\r
11224 var downElement = function (elem) {
\r
11225 if (elem.next().hasClass('tree-hide')) {
\r
11226 downElement(elem.next());
\r
11228 downE = elem.next();
\r
11231 var isBottomElem = false;
\r
11232 var downParent = function(liElem){
\r
11233 if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree')){
\r
11234 isBottomElem = true;
\r
11237 if(liElem.next().length !== 0){
\r
11238 downE = liElem.next().eq(0);
\r
11242 downParent(liElem.parent().parent());
\r
11246 var findDown = function (elem) {
\r
11247 if (isRoot(elem.parent()) && !elem.hasClass('active')) {
\r
11248 downE = elem.parent();
\r
11251 if (elem.hasClass('active')) {
\r
11252 downE = elem.next().find('li').eq(0);
\r
11253 if (downE.hasClass('tree-hide')) {
\r
11254 downElement(downE);
\r
11258 downParent(elem.parent());
\r
11259 if(isBottomElem === true){
\r
11260 downE = elem.parent();
\r
11261 isBottomElem = false;
\r
11267 var resetTabPosition = function(element){
\r
11268 findRoot(element);
\r
11269 angular.element(rootE.parent().parent()[0].querySelector("li[tabindex='0']")).attr('tabindex','-1');
\r
11270 var elemToFocus = rootE.parent().parent()[0].querySelector(".bg")|| rootE;
\r
11272 angular.element(elemToFocus).attr('tabindex','0');
\r
11274 // Function to control the expansion of nodes when the user tabs into the tree and
\r
11275 // the slected node is not visible
\r
11276 var expand = function(elemArr){
\r
11277 var elem= elemArr.pop();
\r
11278 var element = elem.find('a').eq(0);
\r
11279 var selectedNode = elem.parent().parent()[0].querySelector(".bg");
\r
11280 if(selectedNode != null){
\r
11282 element = elem.find('a').eq(0);
\r
11283 if(!element.hasClass('active') ){
\r
11286 if (elem[0].previousElementSibling) {
\r
11287 closeOthersUp(angular.element(elem[0].previousElementSibling),true,true);
\r
11289 if (elem[0].nextElementSibling) {
\r
11290 closeOthersDown(angular.element(elem[0].nextElementSibling),true,true);
\r
11293 if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
\r
11294 if(typeof scope.showChild === 'function' ){
\r
11295 scope.showChild();
\r
11297 element.addClass('active');
\r
11298 elem.attr('aria-expanded', 'true');
\r
11299 element.find('i').eq(0).removeClass('icon-primary-collapsed');
\r
11300 element.find('i').eq(0).addClass('icon-primary-expanded');
\r
11304 elem = elemArr.pop();
\r
11312 element.find('a').eq(0).bind('mouseenter', function (evt) {
\r
11313 angular.forEach(document.querySelectorAll('.activeTooltip'), function(value, key) {
\r
11314 angular.element(value).removeClass('activeTooltip')
\r
11316 element.addClass('activeTooltip');
\r
11318 element.find('a').eq(0).bind('mouseleave', function (evt) {
\r
11319 element.removeClass('activeTooltip');
\r
11321 element.bind('focus', function (evt) {
\r
11322 angular.forEach(document.querySelectorAll('.activeTooltip'), function(value, key) {
\r
11323 angular.element(value).removeClass('activeTooltip')
\r
11325 element.addClass('activeTooltip');
\r
11327 element.bind('blur', function (evt) {
\r
11328 element.removeClass('activeTooltip');
\r
11330 element.bind('keydown', function (evt) {
\r
11331 switch (evt.keyCode) {
\r
11332 case keymap.KEY.HOME:
\r
11333 evt.preventDefault();
\r
11334 evt.stopPropagation();
\r
11335 element.attr('tabindex', -1);
\r
11336 findRoot(element);
\r
11337 rootE.eq(0).attr('tabindex', 0);
\r
11338 rootE[0].focus();
\r
11340 case keymap.KEY.LEFT:
\r
11341 evt.preventDefault();
\r
11342 evt.stopPropagation();
\r
11344 if(element.find('a').eq(0).hasClass('active')){
\r
11345 if (element[0].previousElementSibling) {
\r
11346 closeOthersUp(angular.element(element[0].previousElementSibling),true);
\r
11348 if (element[0].nextElementSibling) {
\r
11349 closeOthersDown(angular.element(element[0].nextElementSibling),true);
\r
11351 activeToggle(element,true);
\r
11354 element.attr('tabindex', -1);
\r
11355 parentE = element.parent().parent();
\r
11356 parentE.attr('tabindex', 0);
\r
11357 parentE[0].focus();
\r
11359 case keymap.KEY.UP:
\r
11360 evt.preventDefault();
\r
11361 evt.stopPropagation();
\r
11362 element.attr('tabindex', -1);
\r
11364 upE.eq(0).attr('tabindex', 0);
\r
11367 case keymap.KEY.RIGHT:
\r
11368 evt.preventDefault();
\r
11369 evt.stopPropagation();
\r
11370 if(element.find('i').eq(0).hasClass('icon-primary-circle')){
\r
11373 if (!element.find('a').eq(0).hasClass('active')) {
\r
11374 if (element[0].previousElementSibling) {
\r
11375 closeOthersUp(angular.element(element[0].previousElementSibling),true);
\r
11377 if (element[0].nextElementSibling) {
\r
11378 closeOthersDown(angular.element(element[0].nextElementSibling),true);
\r
11380 activeToggle(element,true);
\r
11384 element.attr('tabindex', -1);
\r
11385 findDown(element.find('a').eq(0));
\r
11386 downE.eq(0).attr('tabindex', 0);
\r
11387 downE[0].focus();
\r
11390 case keymap.KEY.DOWN:
\r
11391 evt.preventDefault();
\r
11392 element.attr('tabindex', -1);
\r
11393 findDown(element.find('a').eq(0));
\r
11394 downE.eq(0).attr('tabindex', 0);
\r
11395 downE[0].focus();
\r
11396 evt.stopPropagation();
\r
11398 case keymap.KEY.ENTER:
\r
11399 var isSelectedElem = element.hasClass('bg');
\r
11400 var enterFunc = function(element){
\r
11401 if (isSelectedElem) {
\r
11402 element.removeClass('bg');
\r
11403 if (element.attr('aria-selected')) {
\r
11404 element.attr('aria-selected', 'false');
\r
11408 element.addClass('bg');
\r
11409 element.attr('aria-selected', 'true');
\r
11412 if (element[0].previousElementSibling) {
\r
11413 closeOthersUp(angular.element(element[0].previousElementSibling));
\r
11415 if (element[0].nextElementSibling) {
\r
11416 closeOthersDown(angular.element(element[0].nextElementSibling));
\r
11419 removeBackground(element);
\r
11420 enterFunc(element);
\r
11421 evt.stopPropagation();
\r
11423 case keymap.KEY.TAB:
\r
11424 $timeout(function(){
\r
11425 resetTabPosition(element);
\r
11427 evt.stopPropagation();
\r
11434 element.bind('keyup',function(evt){
\r
11435 if(evt.keyCode === keymap.KEY.TAB){
\r
11437 var tempElem = element;
\r
11438 var elemArr = [];
\r
11439 while(!tempElem.hasClass('b2b-tree')){
\r
11440 elemArr.push(tempElem);
\r
11441 tempElem = tempElem.parent().parent();
\r
11443 elemArr.push(tempElem);
\r
11445 expand(elemArr);
\r
11447 evt.stopPropagation();
\r
11453 * @ngdoc directive
\r
11454 * @name Navigation.att:Tree nodes with checkboxes
\r
11456 * @param {String} setRole - The value needs to be "tree". This is required to incorporate CATO requirements.
\r
11457 * @param {boolean} groupIt - The value needs to be "false" for top-level tree rendered.
\r
11458 * @param {Object} collection - The JSON object of tree to be rendered.
\r
11460 * <file src="src/treeNodeCheckbox/docs/readme.md" />
\r
11463 * <div class="b2b-tree-checkbox">
\r
11464 * <b2b-tree-node-checkbox collection="treeStructure" set-role="tree" group-it="false"></b2b-tree-node-checkbox>
\r
11467 * <section id="code">
\r
11468 <example module="b2b.att">
\r
11469 <file src="src/treeNodeCheckbox/docs/demo.html" />
\r
11470 <file src="src/treeNodeCheckbox/docs/demo.js" />
\r
11475 angular.module('b2b.att.treeNodeCheckbox', ['b2b.att.utilities'])
\r
11476 .directive('b2bTreeNodeCheckbox', function () {
\r
11485 templateUrl: function (element, attrs) {
\r
11486 if (attrs.groupIt === 'true') {
\r
11487 return "b2bTemplate/treeNodeCheckbox/groupedTree.html";
\r
11489 return "b2bTemplate/treeNodeCheckbox/ungroupedTree.html";
\r
11492 link: function (scope) {
\r
11493 if (!(scope.setRole === 'tree')) {
\r
11494 scope.setRole = 'group';
\r
11499 .directive('b2bTreeMember', ['$compile', '$timeout', 'keymap', function ($compile, $timeout, keymap) {
\r
11507 templateUrl: 'b2bTemplate/treeNodeCheckbox/treeMember.html',
\r
11508 link: function (scope, element, attrs) {
\r
11509 scope.elemArr = [];
\r
11510 var removeRootTabIndex = function (elem) {
\r
11511 if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
\r
11512 elem.attr('tabindex', -1);
\r
11515 removeRootTabIndex(elem.parent());
\r
11517 scope.$watch('member.child', function(newVal, oldVal){
\r
11518 if(newVal !== oldVal){
\r
11519 scope.showChild();
\r
11523 var checkedCount = 0;
\r
11524 var nonCheckedCount = 0;
\r
11525 var checkBoxesCount = 0;
\r
11527 if(element.find('a').eq(0).find('input')){
\r
11528 if(scope.member.indeterminate){
\r
11529 element.find('a').eq(0).find('input').prop('indeterminate', true);
\r
11530 element.attr('aria-checked',"mixed");
\r
11532 element.attr('aria-checked',scope.member.isSelected);
\r
11535 element.find('a').eq(0).find('input').bind('change',function(){
\r
11536 scope.member.indeterminate = false;
\r
11537 downwardModalUpdate(scope.member);
\r
11538 downwardSelection(element);
\r
11539 upwardSelection(element);
\r
11540 element.attr('aria-checked',scope.member.isSelected);
\r
11541 if (scope.member.onSelect !== undefined) {
\r
11542 scope.member.onSelect(scope.member);
\r
11546 element.find('a').eq(0).find('input').bind('click',function(){
\r
11547 var elem = angular.element(this);
\r
11548 if(scope.member.indeterminate){
\r
11549 scope.member.indeterminate = false;
\r
11550 scope.member.isSelected = true;
\r
11551 elem.prop('indeterminate', false);
\r
11552 elem.prop('checked', true);
\r
11553 elem.triggerHandler('change');
\r
11557 var groupNode = false;
\r
11558 var checkedTreeNode = false;
\r
11560 var isCheckboxSelected = function(elem){
\r
11561 checkedTreeNode = false;
\r
11562 checkedTreeNode = angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox').checked;
\r
11565 var findCheckbox = function(elem){
\r
11566 return angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox');
\r
11569 var updateGrpNodeCheckboxes = function(elem, checked){
\r
11570 angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox').checked = checked;
\r
11574 var isGroupNode = function(elem){
\r
11575 groupNode = false;
\r
11576 if(angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.grpTreeCheckbox')){
\r
11577 groupNode = true;
\r
11581 var downwardModalUpdate = function(curMember){
\r
11582 angular.forEach(curMember.child, function(childMember, key) {
\r
11583 childMember.isSelected = curMember.isSelected;
\r
11584 childMember.indeterminate = false;
\r
11585 if(angular.isArray(childMember.child) && scope.member.child.length > 0){
\r
11586 downwardModalUpdate(childMember);
\r
11591 var downwardSelection = function(elem){
\r
11592 if(findCheckbox(elem)){
\r
11593 isCheckboxSelected(elem)
\r
11595 if(angular.element(elem).find('ul').length > 0){
\r
11596 var childNodes = angular.element(elem).find('ul').eq(0).children('li');
\r
11597 for(var i=0; i<childNodes.length; i++){
\r
11598 if(findCheckbox(childNodes[i])){
\r
11599 isGroupNode(childNodes[i]);
\r
11600 angular.element(findCheckbox(childNodes[i])).prop('indeterminate', false);
\r
11601 angular.element(childNodes[i]).attr('aria-checked',checkedTreeNode);
\r
11603 updateGrpNodeCheckboxes(childNodes[i],checkedTreeNode);
\r
11605 angular.element(childNodes[i]).scope().member.isSelected = checkedTreeNode;
\r
11606 angular.element(childNodes[i]).scope().member.indeterminate = false
\r
11607 angular.element(childNodes[i]).scope().$apply();
\r
11609 downwardSelection(childNodes[i]);
\r
11615 var upwardSelection = function(elem){
\r
11616 var childNodes = elem.parent().parent().find('ul').eq(0).children('li');
\r
11617 checkedCount = 0;
\r
11618 nonCheckedCount = 0;
\r
11619 checkBoxesCount = 0;
\r
11620 for(i=0; i<childNodes.length; i++){
\r
11621 if(findCheckbox(childNodes[i])){
\r
11622 isGroupNode(childNodes[i]);
\r
11623 isCheckboxSelected(childNodes[i]);
\r
11624 checkBoxesCount++;
\r
11625 if(checkedTreeNode){
\r
11627 }else if(!angular.element(angular.element(angular.element(childNodes[i]).find('a').eq(0))[0].querySelector('input.treeCheckBox')).prop('indeterminate')){
\r
11628 nonCheckedCount++;
\r
11632 var parentNodeScope;
\r
11633 parentNodeScope = angular.element(elem.parent().parent()).scope();
\r
11634 if(findCheckbox(elem.parent().parent())){
\r
11635 if(nonCheckedCount == checkBoxesCount){
\r
11636 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
\r
11637 if(parentNodeScope && parentNodeScope.member){
\r
11638 parentNodeScope.member.isSelected = false;
\r
11639 parentNodeScope.member.indeterminate = false;
\r
11641 updateGrpNodeCheckboxes(elem.parent().parent(),false);
\r
11643 angular.element(elem.parent().parent()).attr('aria-checked',false);
\r
11644 }else if(checkedCount == checkBoxesCount){
\r
11645 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
\r
11646 if(parentNodeScope && parentNodeScope.member){
\r
11647 parentNodeScope.member.isSelected = true;
\r
11648 parentNodeScope.member.indeterminate = false;
\r
11650 updateGrpNodeCheckboxes(elem.parent().parent(),true);
\r
11652 angular.element(elem.parent().parent()).attr('aria-checked',true);
\r
11654 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', true);
\r
11655 if(parentNodeScope && parentNodeScope.member){
\r
11656 parentNodeScope.member.isSelected = false;
\r
11657 parentNodeScope.member.indeterminate = true;
\r
11659 updateGrpNodeCheckboxes(elem.parent().parent(),false);
\r
11661 angular.element(elem.parent().parent()).attr('aria-checked',"mixed");
\r
11663 if(parentNodeScope && parentNodeScope.member){
\r
11664 parentNodeScope.$apply();
\r
11670 if(elem.parent().parent().attr('role') == "treeitem"){
\r
11671 upwardSelection(elem.parent().parent());
\r
11675 scope.showChild = function () {
\r
11676 if (!element.hasClass('grouped')) {
\r
11677 if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
\r
11678 scope.groupIt = false;
\r
11679 element.addClass('grouped');
\r
11680 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
\r
11681 $compile(element.contents())(scope);
\r
11682 if(scope.member.active && scope.member.active === true){
\r
11683 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
\r
11685 if(scope.member.selected && scope.member.selected === true){
\r
11686 element.attr('tabindex', 0);
\r
11687 removeRootTabIndex(element);
\r
11689 if(scope.member.active && scope.member.active == undefined){
\r
11690 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
\r
11692 } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
\r
11693 element.addClass('grouped');
\r
11694 scope.groupIt = true;
\r
11696 var grpName = '';
\r
11697 if(scope.member.child[0].groupName !== undefined){
\r
11698 grpName = scope.member.child[0].groupName;
\r
11701 var toSlice = scope.member.child[0].name.search(' ');
\r
11702 grpName = scope.member.child[0].name.slice(0, toSlice);
\r
11705 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
\r
11707 for (j = j + i; j < (i + scope.member.divide); j++) {
\r
11708 if (j === scope.member.child.length) {
\r
11709 scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
\r
11712 if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
\r
11713 scope.member.child[j-1].activeGrp = true;
\r
11717 if (i + scope.member.divide > scope.member.child.length) {
\r
11718 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
\r
11719 if(scope.member.child[j].active && scope.member.child[j].active===true){
\r
11720 scope.member.child[j].activeGrp = true;
\r
11724 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
\r
11725 if(scope.member.child[j].active && scope.member.child[j].active===true){
\r
11726 scope.member.child[j].activeGrp = true;
\r
11731 if(scope.member.divide){
\r
11732 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
\r
11734 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
\r
11736 $compile(element.contents())(scope);
\r
11737 if(scope.member.active && scope.member.active === true){
\r
11738 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
\r
11741 if( scope.member.active && scope.member.active == undefined){
\r
11742 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
\r
11746 $timeout(function () {
\r
11747 if(!scope.member.indeterminate){
\r
11748 downwardSelection(element);
\r
11754 if(scope.member.active && scope.member.active == true){
\r
11755 scope.showChild();
\r
11757 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
\r
11758 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
\r
11760 else if(scope.member.child == undefined){
\r
11761 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-circle');
\r
11762 if(scope.$parent.$index === 0) {
\r
11763 element.find('a').eq(0).append('<span class="first-link"></span>');
\r
11767 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
\r
11768 scope.showChild();
\r
11769 var expandFunc = scope.member.onExpand;
\r
11770 if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
\r
11771 var eValue = scope.member.onExpand(scope.member);
\r
11773 if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
\r
11774 scope.member.onCollapse(scope.member);
\r
11778 angular.element(element[0].querySelectorAll('.treeNodeName')).eq(0).bind('click', function (evt) {
\r
11785 .directive('b2bTreeNodeLink', ['keymap', '$timeout', function (keymap, $timeout) {
\r
11788 link: function (scope, element, attr, ctrl) {
\r
11789 var rootE, parentE, upE, downE;
\r
11790 var closeOthersUp = function (elem) {
\r
11792 if (elem.find('a').eq(0).hasClass('active')) {
\r
11793 activeToggle(elem);
\r
11796 if (elem.hasClass('bg')) {
\r
11797 elem.removeClass('bg');
\r
11799 if (elem[0].previousElementSibling !== null) {
\r
11800 closeOthersUp(angular.element(elem[0].previousElementSibling));
\r
11803 var closeOthersDown = function (elem) {
\r
11805 if (elem.find('a').eq(0).hasClass('active')) {
\r
11806 activeToggle(elem);
\r
11809 if (elem.hasClass('bg')) {
\r
11810 elem.removeClass('bg');
\r
11812 if (elem[0].nextElementSibling !== null) {
\r
11813 closeOthersDown(angular.element(elem[0].nextElementSibling));
\r
11817 var removeBackgroundUp = function (elem) {
\r
11819 if (elem.hasClass('b2b-tree-checkbox')) {
\r
11822 elem.parent().parent().removeClass('bg');
\r
11823 removeBackgroundUp(elem.parent().parent());
\r
11827 var removeBackgroundDown = function (elem) {
\r
11829 angular.element(elem[0].querySelector('.bg')).removeClass('bg');
\r
11834 var activeToggle = function (elem) {
\r
11835 var element = elem.find('a').eq(0);
\r
11836 if (element.hasClass('active')) {
\r
11837 elem.removeClass('bg');
\r
11838 if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
\r
11839 element.removeClass('active');
\r
11840 elem.attr('aria-expanded', 'false');
\r
11841 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-expanded');
\r
11842 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
\r
11845 elem.addClass('bg');
\r
11846 if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
\r
11847 element.addClass('active');
\r
11848 elem.attr('aria-expanded', 'true');
\r
11849 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
\r
11850 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-expanded');
\r
11854 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
\r
11856 if (element[0].previousElementSibling) {
\r
11857 closeOthersUp(angular.element(element[0].previousElementSibling));
\r
11859 if (element[0].nextElementSibling) {
\r
11860 closeOthersDown(angular.element(element[0].nextElementSibling));
\r
11863 activeToggle(element);
\r
11865 removeBackgroundDown(element);
\r
11866 removeBackgroundUp(element);
\r
11867 evt.stopPropagation();
\r
11870 if (element.parent().parent().hasClass('b2b-tree-checkbox') && (element.parent()[0].previousElementSibling === null)) {
\r
11871 element.attr('tabindex', 0);
\r
11874 var isRoot = function (elem) {
\r
11875 if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
\r
11881 var findRoot = function (elem) {
\r
11882 if (isRoot(elem)) {
\r
11886 findRoot(elem.parent());
\r
11889 var findPreActive = function (elem) {
\r
11891 if (!(elem.hasClass("active"))) {
\r
11894 var childElems = angular.element(elem[0].nextElementSibling.children);
\r
11895 lastE = angular.element(childElems[childElems.length - 1]);
\r
11896 if (lastE.find('a').eq(0).hasClass('active')) {
\r
11897 findPreActive(lastE.find('a').eq(0));
\r
11903 var findUp = function (elem) {
\r
11904 if (isRoot(elem)) {
\r
11908 if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
\r
11909 upE = angular.element(elem[0].previousElementSibling);
\r
11910 if (upE.find('a').eq(0).hasClass('active')) {
\r
11911 findPreActive(upE.find('a').eq(0));
\r
11914 upE = elem.parent().parent();
\r
11918 var downElement = function (elem) {
\r
11919 if (elem.next().hasClass('tree-hide')) {
\r
11920 downElement(elem.next());
\r
11922 downE = elem.next();
\r
11925 var isBottomElem = false;
\r
11926 var downParent = function(liElem){
\r
11927 if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree-checkbox')){
\r
11928 isBottomElem = true;
\r
11931 if(liElem.next().length !== 0){
\r
11932 downE = liElem.next().eq(0);
\r
11936 downParent(liElem.parent().parent());
\r
11940 var findDown = function (elem) {
\r
11941 if (isRoot(elem.parent()) && !elem.hasClass('active')) {
\r
11942 downE = elem.parent();
\r
11945 if (elem.hasClass('active')) {
\r
11946 downE = elem.next().find('li').eq(0);
\r
11947 if (downE.hasClass('tree-hide')) {
\r
11948 downElement(downE);
\r
11952 downParent(elem.parent());
\r
11953 if(isBottomElem === true){
\r
11954 downE = elem.parent();
\r
11955 isBottomElem = false;
\r
11959 element.bind('keydown', function (evt) {
\r
11960 switch (evt.keyCode) {
\r
11961 case keymap.KEY.HOME:
\r
11962 evt.preventDefault();
\r
11963 evt.stopPropagation();
\r
11964 element.attr('tabindex', -1);
\r
11965 findRoot(element);
\r
11966 rootE.eq(0).attr('tabindex', 0);
\r
11967 rootE[0].focus();
\r
11969 case keymap.KEY.LEFT:
\r
11970 evt.preventDefault();
\r
11971 evt.stopPropagation();
\r
11972 if (!isRoot(element)) {
\r
11973 if(element.find('a').eq(0).hasClass('active')){
\r
11974 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
\r
11977 element.attr('tabindex', -1);
\r
11978 parentE = element.parent().parent();
\r
11979 parentE.attr('tabindex', 0);
\r
11980 parentE[0].focus();
\r
11982 if (element.find('a').eq(0).hasClass('active')) {
\r
11983 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
\r
11987 case keymap.KEY.UP:
\r
11988 evt.preventDefault();
\r
11989 evt.stopPropagation();
\r
11990 element.attr('tabindex', -1);
\r
11992 upE.eq(0).attr('tabindex', 0);
\r
11995 case keymap.KEY.RIGHT:
\r
11996 evt.preventDefault();
\r
11997 evt.stopPropagation();
\r
11998 if(angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')){
\r
12001 if (!element.find('a').eq(0).hasClass('active')) {
\r
12002 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
\r
12005 element.attr('tabindex', -1);
\r
12006 findDown(element.find('a').eq(0));
\r
12007 downE.eq(0).attr('tabindex', 0);
\r
12008 downE[0].focus();
\r
12011 case keymap.KEY.DOWN:
\r
12012 evt.preventDefault();
\r
12013 element.attr('tabindex', -1);
\r
12014 findDown(element.find('a').eq(0));
\r
12015 downE.eq(0).attr('tabindex', 0);
\r
12016 downE[0].focus();
\r
12017 evt.stopPropagation();
\r
12019 case keymap.KEY.SPACE:
\r
12020 case keymap.KEY.ENTER:
\r
12021 evt.preventDefault();
\r
12022 evt.stopPropagation();
\r
12023 if(angular.isDefined(element.scope().member.isSelected)){
\r
12024 element.scope().member.isSelected = !element.scope().member.isSelected;
\r
12025 element.scope().member.indeterminate = false;
\r
12026 element.scope().$apply();
\r
12027 element.find('a').eq(0).find('input').prop('indeterminate', false);
\r
12028 element.find('a').eq(0).find('input').triggerHandler('change');
\r
12040 * DATE: 2014-01-14
\r
12041 * UPDATES AND DOCS AT: http://www.greensock.com
\r
12043 * @license Copyright (c) 2008-2014, GreenSock. All rights reserved.
\r
12044 * This work is subject to the terms at http://www.greensock.com/terms_of_use.html or for
\r
12045 * Club GreenSock members, the software agreement that was issued with your membership.
\r
12047 * @author: Jack Doyle, jack@greensock.com
\r
12049 (window._gsQueue || (window._gsQueue = [])).push( function() {
\r
12053 var _doc = document.documentElement,
\r
12054 _window = window,
\r
12055 _max = function(element, axis) {
\r
12056 var dim = (axis === "x") ? "Width" : "Height",
\r
12057 scroll = "scroll" + dim,
\r
12058 client = "client" + dim,
\r
12059 body = document.body;
\r
12060 return (element === _window || element === _doc || element === body) ? Math.max(_doc[scroll], body[scroll]) - (_window["inner" + dim] || Math.max(_doc[client], body[client])) : element[scroll] - element["offset" + dim];
\r
12063 ScrollToPlugin = window._gsDefine.plugin({
\r
12064 propName: "scrollTo",
\r
12068 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
\r
12069 init: function(target, value, tween) {
\r
12070 this._wdw = (target === _window);
\r
12071 this._target = target;
\r
12072 this._tween = tween;
\r
12073 if (typeof(value) !== "object") {
\r
12074 value = {y:value}; //if we don't receive an object as the parameter, assume the user intends "y".
\r
12076 this._autoKill = (value.autoKill !== false);
\r
12077 this.x = this.xPrev = this.getX();
\r
12078 this.y = this.yPrev = this.getY();
\r
12079 if (value.x != null) {
\r
12080 this._addTween(this, "x", this.x, (value.x === "max") ? _max(target, "x") : value.x, "scrollTo_x", true);
\r
12081 this._overwriteProps.push("scrollTo_x");
\r
12083 this.skipX = true;
\r
12085 if (value.y != null) {
\r
12086 this._addTween(this, "y", this.y, (value.y === "max") ? _max(target, "y") : value.y, "scrollTo_y", true);
\r
12087 this._overwriteProps.push("scrollTo_y");
\r
12089 this.skipY = true;
\r
12094 //called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
\r
12095 set: function(v) {
\r
12096 this._super.setRatio.call(this, v);
\r
12098 var x = (this._wdw || !this.skipX) ? this.getX() : this.xPrev,
\r
12099 y = (this._wdw || !this.skipY) ? this.getY() : this.yPrev,
\r
12100 yDif = y - this.yPrev,
\r
12101 xDif = x - this.xPrev;
\r
12103 if (this._autoKill) {
\r
12104 //note: iOS has a bug that throws off the scroll by several pixels, so we need to check if it's within 7 pixels of the previous one that we set instead of just looking for an exact match.
\r
12105 if (!this.skipX && (xDif > 7 || xDif < -7) && x < _max(this._target, "x")) {
\r
12106 this.skipX = true; //if the user scrolls separately, we should stop tweening!
\r
12108 if (!this.skipY && (yDif > 7 || yDif < -7) && y < _max(this._target, "y")) {
\r
12109 this.skipY = true; //if the user scrolls separately, we should stop tweening!
\r
12111 if (this.skipX && this.skipY) {
\r
12112 this._tween.kill();
\r
12116 _window.scrollTo((!this.skipX) ? this.x : x, (!this.skipY) ? this.y : y);
\r
12118 if (!this.skipY) {
\r
12119 this._target.scrollTop = this.y;
\r
12121 if (!this.skipX) {
\r
12122 this._target.scrollLeft = this.x;
\r
12125 this.xPrev = this.x;
\r
12126 this.yPrev = this.y;
\r
12130 p = ScrollToPlugin.prototype;
\r
12132 ScrollToPlugin.max = _max;
\r
12134 p.getX = function() {
\r
12135 return (!this._wdw) ? this._target.scrollLeft : (_window.pageXOffset != null) ? _window.pageXOffset : (_doc.scrollLeft != null) ? _doc.scrollLeft : document.body.scrollLeft;
\r
12138 p.getY = function() {
\r
12139 return (!this._wdw) ? this._target.scrollTop : (_window.pageYOffset != null) ? _window.pageYOffset : (_doc.scrollTop != null) ? _doc.scrollTop : document.body.scrollTop;
\r
12142 p._kill = function(lookup) {
\r
12143 if (lookup.scrollTo_x) {
\r
12144 this.skipX = true;
\r
12146 if (lookup.scrollTo_y) {
\r
12147 this.skipY = true;
\r
12149 return this._super._kill.call(this, lookup);
\r
12152 }); if (window._gsDefine) { window._gsQueue.pop()(); }
\r
12154 * VERSION: 1.12.1
\r
12155 * DATE: 2014-06-26
\r
12156 * UPDATES AND DOCS AT: http://www.greensock.com
\r
12158 * Includes all of the following: TweenLite, TweenMax, TimelineLite, TimelineMax, EasePack, CSSPlugin, RoundPropsPlugin, BezierPlugin, AttrPlugin, DirectionalRotationPlugin
\r
12160 * @license Copyright (c) 2008-2014, GreenSock. All rights reserved.
\r
12161 * This work is subject to the terms at http://www.greensock.com/terms_of_use.html or for
\r
12162 * Club GreenSock members, the software agreement that was issued with your membership.
\r
12164 * @author: Jack Doyle, jack@greensock.com
\r
12167 (window._gsQueue || (window._gsQueue = [])).push( function() {
\r
12171 window._gsDefine("TweenMax", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) {
\r
12173 var _slice = [].slice,
\r
12174 TweenMax = function(target, duration, vars) {
\r
12175 TweenLite.call(this, target, duration, vars);
\r
12177 this._yoyo = (this.vars.yoyo === true);
\r
12178 this._repeat = this.vars.repeat || 0;
\r
12179 this._repeatDelay = this.vars.repeatDelay || 0;
\r
12180 this._dirty = true; //ensures that if there is any repeat, the totalDuration will get recalculated to accurately report it.
\r
12181 this.render = TweenMax.prototype.render; //speed optimization (avoid prototype lookup on this "hot" method)
\r
12183 _tinyNum = 0.0000000001,
\r
12184 TweenLiteInternals = TweenLite._internals,
\r
12185 _isSelector = TweenLiteInternals.isSelector,
\r
12186 _isArray = TweenLiteInternals.isArray,
\r
12187 p = TweenMax.prototype = TweenLite.to({}, 0.1, {}),
\r
12188 _blankArray = [];
\r
12190 TweenMax.version = "1.12.1";
\r
12191 p.constructor = TweenMax;
\r
12192 p.kill()._gc = false;
\r
12193 TweenMax.killTweensOf = TweenMax.killDelayedCallsTo = TweenLite.killTweensOf;
\r
12194 TweenMax.getTweensOf = TweenLite.getTweensOf;
\r
12195 TweenMax.lagSmoothing = TweenLite.lagSmoothing;
\r
12196 TweenMax.ticker = TweenLite.ticker;
\r
12197 TweenMax.render = TweenLite.render;
\r
12199 p.invalidate = function() {
\r
12200 this._yoyo = (this.vars.yoyo === true);
\r
12201 this._repeat = this.vars.repeat || 0;
\r
12202 this._repeatDelay = this.vars.repeatDelay || 0;
\r
12203 this._uncache(true);
\r
12204 return TweenLite.prototype.invalidate.call(this);
\r
12207 p.updateTo = function(vars, resetDuration) {
\r
12208 var curRatio = this.ratio, p;
\r
12209 if (resetDuration && this._startTime < this._timeline._time) {
\r
12210 this._startTime = this._timeline._time;
\r
12211 this._uncache(false);
\r
12213 this._enabled(true, false);
\r
12215 this._timeline.insert(this, this._startTime - this._delay); //ensures that any necessary re-sequencing of Animations in the timeline occurs to make sure the rendering order is correct.
\r
12218 for (p in vars) {
\r
12219 this.vars[p] = vars[p];
\r
12221 if (this._initted) {
\r
12222 if (resetDuration) {
\r
12223 this._initted = false;
\r
12226 this._enabled(true, false);
\r
12228 if (this._notifyPluginsOfEnabled && this._firstPT) {
\r
12229 TweenLite._onPluginEvent("_onDisable", this); //in case a plugin like MotionBlur must perform some cleanup tasks
\r
12231 if (this._time / this._duration > 0.998) { //if the tween has finished (or come extremely close to finishing), we just need to rewind it to 0 and then render it again at the end which forces it to re-initialize (parsing the new vars). We allow tweens that are close to finishing (but haven't quite finished) to work this way too because otherwise, the values are so small when determining where to project the starting values that binary math issues creep in and can make the tween appear to render incorrectly when run backwards.
\r
12232 var prevTime = this._time;
\r
12233 this.render(0, true, false);
\r
12234 this._initted = false;
\r
12235 this.render(prevTime, true, false);
\r
12236 } else if (this._time > 0) {
\r
12237 this._initted = false;
\r
12239 var inv = 1 / (1 - curRatio),
\r
12240 pt = this._firstPT, endValue;
\r
12242 endValue = pt.s + pt.c;
\r
12244 pt.s = endValue - pt.c;
\r
12253 p.render = function(time, suppressEvents, force) {
\r
12254 if (!this._initted) if (this._duration === 0 && this.vars.repeat) { //zero duration tweens that render immediately have render() called from TweenLite's constructor, before TweenMax's constructor has finished setting _repeat, _repeatDelay, and _yoyo which are critical in determining totalDuration() so we need to call invalidate() which is a low-kb way to get those set properly.
\r
12255 this.invalidate();
\r
12257 var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
\r
12258 prevTime = this._time,
\r
12259 prevTotalTime = this._totalTime,
\r
12260 prevCycle = this._cycle,
\r
12261 duration = this._duration,
\r
12262 prevRawPrevTime = this._rawPrevTime,
\r
12263 isComplete, callback, pt, cycleDuration, r, type, pow, rawPrevTime, i;
\r
12264 if (time >= totalDur) {
\r
12265 this._totalTime = totalDur;
\r
12266 this._cycle = this._repeat;
\r
12267 if (this._yoyo && (this._cycle & 1) !== 0) {
\r
12269 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
\r
12271 this._time = duration;
\r
12272 this.ratio = this._ease._calcEnd ? this._ease.getRatio(1) : 1;
\r
12274 if (!this._reversed) {
\r
12275 isComplete = true;
\r
12276 callback = "onComplete";
\r
12278 if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
\r
12279 if (this._startTime === this._timeline._duration) { //if a zero-duration tween is at the VERY end of a timeline and that timeline renders at its end, it will typically add a tiny bit of cushion to the render time to prevent rounding errors from getting in the way of tweens rendering their VERY end. If we then reverse() that timeline, the zero-duration tween will trigger its onReverseComplete even though technically the playhead didn't pass over it again. It's a very specific edge case we must accommodate.
\r
12282 if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time) {
\r
12284 if (prevRawPrevTime > _tinyNum) {
\r
12285 callback = "onReverseComplete";
\r
12288 this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
\r
12291 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
\r
12292 this._totalTime = this._time = this._cycle = 0;
\r
12293 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
\r
12294 if (prevTotalTime !== 0 || (duration === 0 && prevRawPrevTime > 0 && prevRawPrevTime !== _tinyNum)) {
\r
12295 callback = "onReverseComplete";
\r
12296 isComplete = this._reversed;
\r
12299 this._active = false;
\r
12300 if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
\r
12301 if (prevRawPrevTime >= 0) {
\r
12304 this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
\r
12306 } else if (!this._initted) { //if we render the very beginning (time == 0) of a fromTo(), we must force the render (normal tweens wouldn't need to render at a time of 0 when the prevTime was also 0). This is also mandatory to make sure overwriting kicks in immediately.
\r
12310 this._totalTime = this._time = time;
\r
12312 if (this._repeat !== 0) {
\r
12313 cycleDuration = duration + this._repeatDelay;
\r
12314 this._cycle = (this._totalTime / cycleDuration) >> 0; //originally _totalTime % cycleDuration but floating point errors caused problems, so I normalized it. (4 % 0.8 should be 0 but Flash reports it as 0.79999999!)
\r
12315 if (this._cycle !== 0) if (this._cycle === this._totalTime / cycleDuration) {
\r
12316 this._cycle--; //otherwise when rendered exactly at the end time, it will act as though it is repeating (at the beginning)
\r
12318 this._time = this._totalTime - (this._cycle * cycleDuration);
\r
12319 if (this._yoyo) if ((this._cycle & 1) !== 0) {
\r
12320 this._time = duration - this._time;
\r
12322 if (this._time > duration) {
\r
12323 this._time = duration;
\r
12324 } else if (this._time < 0) {
\r
12329 if (this._easeType) {
\r
12330 r = this._time / duration;
\r
12331 type = this._easeType;
\r
12332 pow = this._easePower;
\r
12333 if (type === 1 || (type === 3 && r >= 0.5)) {
\r
12336 if (type === 3) {
\r
12341 } else if (pow === 2) {
\r
12343 } else if (pow === 3) {
\r
12345 } else if (pow === 4) {
\r
12346 r *= r * r * r * r;
\r
12349 if (type === 1) {
\r
12350 this.ratio = 1 - r;
\r
12351 } else if (type === 2) {
\r
12353 } else if (this._time / duration < 0.5) {
\r
12354 this.ratio = r / 2;
\r
12356 this.ratio = 1 - (r / 2);
\r
12360 this.ratio = this._ease.getRatio(this._time / duration);
\r
12365 if (prevTime === this._time && !force && prevCycle === this._cycle) {
\r
12366 if (prevTotalTime !== this._totalTime) if (this._onUpdate) if (!suppressEvents) { //so that onUpdate fires even during the repeatDelay - as long as the totalTime changed, we should trigger onUpdate.
\r
12367 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
\r
12370 } else if (!this._initted) {
\r
12372 if (!this._initted || this._gc) { //immediateRender tweens typically won't initialize until the playhead advances (_time is greater than 0) in order to ensure that overwriting occurs properly. Also, if all of the tweening properties have been overwritten (which would cause _gc to be true, as set in _init()), we shouldn't continue otherwise an onStart callback could be called for example.
\r
12374 } else if (!force && this._firstPT && ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration))) { //we stick it in the queue for rendering at the very end of the tick - this is a performance optimization because browsers invalidate styles and force a recalculation if you read, write, and then read style data (so it's better to read/read/read/write/write/write than read/write/read/write/read/write). The down side, of course, is that usually you WANT things to render immediately because you may have code running right after that which depends on the change. Like imagine running TweenLite.set(...) and then immediately after that, creating a nother tween that animates the same property to another value; the starting values of that 2nd tween wouldn't be accurate if lazy is true.
\r
12375 this._time = prevTime;
\r
12376 this._totalTime = prevTotalTime;
\r
12377 this._rawPrevTime = prevRawPrevTime;
\r
12378 this._cycle = prevCycle;
\r
12379 TweenLiteInternals.lazyTweens.push(this);
\r
12380 this._lazy = time;
\r
12383 //_ease is initially set to defaultEase, so now that init() has run, _ease is set properly and we need to recalculate the ratio. Overall this is faster than using conditional logic earlier in the method to avoid having to set ratio twice because we only init() once but renderTime() gets called VERY frequently.
\r
12384 if (this._time && !isComplete) {
\r
12385 this.ratio = this._ease.getRatio(this._time / duration);
\r
12386 } else if (isComplete && this._ease._calcEnd) {
\r
12387 this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1);
\r
12390 if (this._lazy !== false) {
\r
12391 this._lazy = false;
\r
12394 if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) {
\r
12395 this._active = true; //so that if the user renders a tween (as opposed to the timeline rendering it), the timeline is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the tween already finished but the user manually re-renders it as halfway done.
\r
12397 if (prevTotalTime === 0) {
\r
12398 if (this._initted === 2 && time > 0) {
\r
12399 //this.invalidate();
\r
12400 this._init(); //will just apply overwriting since _initted of (2) means it was a from() tween that had immediateRender:true
\r
12402 if (this._startAt) {
\r
12404 this._startAt.render(time, suppressEvents, force);
\r
12405 } else if (!callback) {
\r
12406 callback = "_dummyGS"; //if no callback is defined, use a dummy value just so that the condition at the end evaluates as true because _startAt should render AFTER the normal render loop when the time is negative. We could handle this in a more intuitive way, of course, but the render loop is the MOST important thing to optimize, so this technique allows us to avoid adding extra conditional logic in a high-frequency area.
\r
12409 if (this.vars.onStart) if (this._totalTime !== 0 || duration === 0) if (!suppressEvents) {
\r
12410 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
\r
12414 pt = this._firstPT;
\r
12417 pt.t[pt.p](pt.c * this.ratio + pt.s);
\r
12419 pt.t[pt.p] = pt.c * this.ratio + pt.s;
\r
12424 if (this._onUpdate) {
\r
12425 if (time < 0) if (this._startAt && this._startTime) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values.
\r
12426 this._startAt.render(time, suppressEvents, force); //note: for performance reasons, we tuck this conditional logic inside less traveled areas (most tweens don't have an onUpdate). We'd just have it at the end before the onComplete, but the values should be updated before any onUpdate is called, so we ALSO put it here and then if it's not called, we do so later near the onComplete.
\r
12428 if (!suppressEvents) if (this._totalTime !== prevTotalTime || isComplete) {
\r
12429 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
\r
12432 if (this._cycle !== prevCycle) if (!suppressEvents) if (!this._gc) if (this.vars.onRepeat) {
\r
12433 this.vars.onRepeat.apply(this.vars.onRepeatScope || this, this.vars.onRepeatParams || _blankArray);
\r
12435 if (callback) if (!this._gc) { //check gc because there's a chance that kill() could be called in an onUpdate
\r
12436 if (time < 0 && this._startAt && !this._onUpdate && this._startTime) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values.
\r
12437 this._startAt.render(time, suppressEvents, force);
\r
12439 if (isComplete) {
\r
12440 if (this._timeline.autoRemoveChildren) {
\r
12441 this._enabled(false, false);
\r
12443 this._active = false;
\r
12445 if (!suppressEvents && this.vars[callback]) {
\r
12446 this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
\r
12448 if (duration === 0 && this._rawPrevTime === _tinyNum && rawPrevTime !== _tinyNum) { //the onComplete or onReverseComplete could trigger movement of the playhead and for zero-duration tweens (which must discern direction) that land directly back on their start time, we don't want to fire again on the next render. Think of several addPause()'s in a timeline that forces the playhead to a certain spot, but what if it's already paused and another tween is tweening the "time" of the timeline? Each time it moves [forward] past that spot, it would move back, and since suppressEvents is true, it'd reset _rawPrevTime to _tinyNum so that when it begins again, the callback would fire (so ultimately it could bounce back and forth during that tween). Again, this is a very uncommon scenario, but possible nonetheless.
\r
12449 this._rawPrevTime = 0;
\r
12454 //---- STATIC FUNCTIONS -----------------------------------------------------------------------------------------------------------
\r
12456 TweenMax.to = function(target, duration, vars) {
\r
12457 return new TweenMax(target, duration, vars);
\r
12460 TweenMax.from = function(target, duration, vars) {
\r
12461 vars.runBackwards = true;
\r
12462 vars.immediateRender = (vars.immediateRender != false);
\r
12463 return new TweenMax(target, duration, vars);
\r
12466 TweenMax.fromTo = function(target, duration, fromVars, toVars) {
\r
12467 toVars.startAt = fromVars;
\r
12468 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
\r
12469 return new TweenMax(target, duration, toVars);
\r
12472 TweenMax.staggerTo = TweenMax.allTo = function(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
\r
12473 stagger = stagger || 0;
\r
12474 var delay = vars.delay || 0,
\r
12476 finalComplete = function() {
\r
12477 if (vars.onComplete) {
\r
12478 vars.onComplete.apply(vars.onCompleteScope || this, arguments);
\r
12480 onCompleteAll.apply(onCompleteAllScope || this, onCompleteAllParams || _blankArray);
\r
12483 if (!_isArray(targets)) {
\r
12484 if (typeof(targets) === "string") {
\r
12485 targets = TweenLite.selector(targets) || targets;
\r
12487 if (_isSelector(targets)) {
\r
12488 targets = _slice.call(targets, 0);
\r
12491 l = targets.length;
\r
12492 for (i = 0; i < l; i++) {
\r
12494 for (p in vars) {
\r
12495 copy[p] = vars[p];
\r
12497 copy.delay = delay;
\r
12498 if (i === l - 1 && onCompleteAll) {
\r
12499 copy.onComplete = finalComplete;
\r
12501 a[i] = new TweenMax(targets[i], duration, copy);
\r
12502 delay += stagger;
\r
12507 TweenMax.staggerFrom = TweenMax.allFrom = function(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
\r
12508 vars.runBackwards = true;
\r
12509 vars.immediateRender = (vars.immediateRender != false);
\r
12510 return TweenMax.staggerTo(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
\r
12513 TweenMax.staggerFromTo = TweenMax.allFromTo = function(targets, duration, fromVars, toVars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
\r
12514 toVars.startAt = fromVars;
\r
12515 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
\r
12516 return TweenMax.staggerTo(targets, duration, toVars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
\r
12519 TweenMax.delayedCall = function(delay, callback, params, scope, useFrames) {
\r
12520 return new TweenMax(callback, 0, {delay:delay, onComplete:callback, onCompleteParams:params, onCompleteScope:scope, onReverseComplete:callback, onReverseCompleteParams:params, onReverseCompleteScope:scope, immediateRender:false, useFrames:useFrames, overwrite:0});
\r
12523 TweenMax.set = function(target, vars) {
\r
12524 return new TweenMax(target, 0, vars);
\r
12527 TweenMax.isTweening = function(target) {
\r
12528 return (TweenLite.getTweensOf(target, true).length > 0);
\r
12531 var _getChildrenOf = function(timeline, includeTimelines) {
\r
12534 tween = timeline._first;
\r
12536 if (tween instanceof TweenLite) {
\r
12537 a[cnt++] = tween;
\r
12539 if (includeTimelines) {
\r
12540 a[cnt++] = tween;
\r
12542 a = a.concat(_getChildrenOf(tween, includeTimelines));
\r
12545 tween = tween._next;
\r
12549 getAllTweens = TweenMax.getAllTweens = function(includeTimelines) {
\r
12550 return _getChildrenOf(Animation._rootTimeline, includeTimelines).concat( _getChildrenOf(Animation._rootFramesTimeline, includeTimelines) );
\r
12553 TweenMax.killAll = function(complete, tweens, delayedCalls, timelines) {
\r
12554 if (tweens == null) {
\r
12557 if (delayedCalls == null) {
\r
12558 delayedCalls = true;
\r
12560 var a = getAllTweens((timelines != false)),
\r
12562 allTrue = (tweens && delayedCalls && timelines),
\r
12564 for (i = 0; i < l; i++) {
\r
12566 if (allTrue || (tween instanceof SimpleTimeline) || ((isDC = (tween.target === tween.vars.onComplete)) && delayedCalls) || (tweens && !isDC)) {
\r
12568 tween.totalTime(tween._reversed ? 0 : tween.totalDuration());
\r
12570 tween._enabled(false, false);
\r
12576 TweenMax.killChildTweensOf = function(parent, complete) {
\r
12577 if (parent == null) {
\r
12580 var tl = TweenLiteInternals.tweenLookup,
\r
12581 a, curParent, p, i, l;
\r
12582 if (typeof(parent) === "string") {
\r
12583 parent = TweenLite.selector(parent) || parent;
\r
12585 if (_isSelector(parent)) {
\r
12586 parent = _slice.call(parent, 0);
\r
12588 if (_isArray(parent)) {
\r
12589 i = parent.length;
\r
12590 while (--i > -1) {
\r
12591 TweenMax.killChildTweensOf(parent[i], complete);
\r
12597 curParent = tl[p].target.parentNode;
\r
12598 while (curParent) {
\r
12599 if (curParent === parent) {
\r
12600 a = a.concat(tl[p].tweens);
\r
12602 curParent = curParent.parentNode;
\r
12606 for (i = 0; i < l; i++) {
\r
12608 a[i].totalTime(a[i].totalDuration());
\r
12610 a[i]._enabled(false, false);
\r
12614 var _changePause = function(pause, tweens, delayedCalls, timelines) {
\r
12615 tweens = (tweens !== false);
\r
12616 delayedCalls = (delayedCalls !== false);
\r
12617 timelines = (timelines !== false);
\r
12618 var a = getAllTweens(timelines),
\r
12619 allTrue = (tweens && delayedCalls && timelines),
\r
12622 while (--i > -1) {
\r
12624 if (allTrue || (tween instanceof SimpleTimeline) || ((isDC = (tween.target === tween.vars.onComplete)) && delayedCalls) || (tweens && !isDC)) {
\r
12625 tween.paused(pause);
\r
12630 TweenMax.pauseAll = function(tweens, delayedCalls, timelines) {
\r
12631 _changePause(true, tweens, delayedCalls, timelines);
\r
12634 TweenMax.resumeAll = function(tweens, delayedCalls, timelines) {
\r
12635 _changePause(false, tweens, delayedCalls, timelines);
\r
12638 TweenMax.globalTimeScale = function(value) {
\r
12639 var tl = Animation._rootTimeline,
\r
12640 t = TweenLite.ticker.time;
\r
12641 if (!arguments.length) {
\r
12642 return tl._timeScale;
\r
12644 value = value || _tinyNum; //can't allow zero because it'll throw the math off
\r
12645 tl._startTime = t - ((t - tl._startTime) * tl._timeScale / value);
\r
12646 tl = Animation._rootFramesTimeline;
\r
12647 t = TweenLite.ticker.frame;
\r
12648 tl._startTime = t - ((t - tl._startTime) * tl._timeScale / value);
\r
12649 tl._timeScale = Animation._rootTimeline._timeScale = value;
\r
12654 //---- GETTERS / SETTERS ----------------------------------------------------------------------------------------------------------
\r
12656 p.progress = function(value) {
\r
12657 return (!arguments.length) ? this._time / this.duration() : this.totalTime( this.duration() * ((this._yoyo && (this._cycle & 1) !== 0) ? 1 - value : value) + (this._cycle * (this._duration + this._repeatDelay)), false);
\r
12660 p.totalProgress = function(value) {
\r
12661 return (!arguments.length) ? this._totalTime / this.totalDuration() : this.totalTime( this.totalDuration() * value, false);
\r
12664 p.time = function(value, suppressEvents) {
\r
12665 if (!arguments.length) {
\r
12666 return this._time;
\r
12668 if (this._dirty) {
\r
12669 this.totalDuration();
\r
12671 if (value > this._duration) {
\r
12672 value = this._duration;
\r
12674 if (this._yoyo && (this._cycle & 1) !== 0) {
\r
12675 value = (this._duration - value) + (this._cycle * (this._duration + this._repeatDelay));
\r
12676 } else if (this._repeat !== 0) {
\r
12677 value += this._cycle * (this._duration + this._repeatDelay);
\r
12679 return this.totalTime(value, suppressEvents);
\r
12682 p.duration = function(value) {
\r
12683 if (!arguments.length) {
\r
12684 return this._duration; //don't set _dirty = false because there could be repeats that haven't been factored into the _totalDuration yet. Otherwise, if you create a repeated TweenMax and then immediately check its duration(), it would cache the value and the totalDuration would not be correct, thus repeats wouldn't take effect.
\r
12686 return Animation.prototype.duration.call(this, value);
\r
12689 p.totalDuration = function(value) {
\r
12690 if (!arguments.length) {
\r
12691 if (this._dirty) {
\r
12692 //instead of Infinity, we use 999999999999 so that we can accommodate reverses
\r
12693 this._totalDuration = (this._repeat === -1) ? 999999999999 : this._duration * (this._repeat + 1) + (this._repeatDelay * this._repeat);
\r
12694 this._dirty = false;
\r
12696 return this._totalDuration;
\r
12698 return (this._repeat === -1) ? this : this.duration( (value - (this._repeat * this._repeatDelay)) / (this._repeat + 1) );
\r
12701 p.repeat = function(value) {
\r
12702 if (!arguments.length) {
\r
12703 return this._repeat;
\r
12705 this._repeat = value;
\r
12706 return this._uncache(true);
\r
12709 p.repeatDelay = function(value) {
\r
12710 if (!arguments.length) {
\r
12711 return this._repeatDelay;
\r
12713 this._repeatDelay = value;
\r
12714 return this._uncache(true);
\r
12717 p.yoyo = function(value) {
\r
12718 if (!arguments.length) {
\r
12719 return this._yoyo;
\r
12721 this._yoyo = value;
\r
12738 * ----------------------------------------------------------------
\r
12740 * ----------------------------------------------------------------
\r
12742 window._gsDefine("TimelineLite", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) {
\r
12744 var TimelineLite = function(vars) {
\r
12745 SimpleTimeline.call(this, vars);
\r
12746 this._labels = {};
\r
12747 this.autoRemoveChildren = (this.vars.autoRemoveChildren === true);
\r
12748 this.smoothChildTiming = (this.vars.smoothChildTiming === true);
\r
12749 this._sortChildren = true;
\r
12750 this._onUpdate = this.vars.onUpdate;
\r
12751 var v = this.vars,
\r
12755 if (_isArray(val)) if (val.join("").indexOf("{self}") !== -1) {
\r
12756 v[p] = this._swapSelfInParams(val);
\r
12759 if (_isArray(v.tweens)) {
\r
12760 this.add(v.tweens, 0, v.align, v.stagger);
\r
12763 _tinyNum = 0.0000000001,
\r
12764 _isSelector = TweenLite._internals.isSelector,
\r
12765 _isArray = TweenLite._internals.isArray,
\r
12766 _blankArray = [],
\r
12767 _globals = window._gsDefine.globals,
\r
12768 _copy = function(vars) {
\r
12769 var copy = {}, p;
\r
12770 for (p in vars) {
\r
12771 copy[p] = vars[p];
\r
12775 _pauseCallback = function(tween, callback, params, scope) {
\r
12776 tween._timeline.pause(tween._startTime);
\r
12778 callback.apply(scope || tween._timeline, params || _blankArray);
\r
12781 _slice = _blankArray.slice,
\r
12782 p = TimelineLite.prototype = new SimpleTimeline();
\r
12784 TimelineLite.version = "1.12.1";
\r
12785 p.constructor = TimelineLite;
\r
12786 p.kill()._gc = false;
\r
12788 p.to = function(target, duration, vars, position) {
\r
12789 var Engine = (vars.repeat && _globals.TweenMax) || TweenLite;
\r
12790 return duration ? this.add( new Engine(target, duration, vars), position) : this.set(target, vars, position);
\r
12793 p.from = function(target, duration, vars, position) {
\r
12794 return this.add( ((vars.repeat && _globals.TweenMax) || TweenLite).from(target, duration, vars), position);
\r
12797 p.fromTo = function(target, duration, fromVars, toVars, position) {
\r
12798 var Engine = (toVars.repeat && _globals.TweenMax) || TweenLite;
\r
12799 return duration ? this.add( Engine.fromTo(target, duration, fromVars, toVars), position) : this.set(target, toVars, position);
\r
12802 p.staggerTo = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
\r
12803 var tl = new TimelineLite({onComplete:onCompleteAll, onCompleteParams:onCompleteAllParams, onCompleteScope:onCompleteAllScope, smoothChildTiming:this.smoothChildTiming}),
\r
12805 if (typeof(targets) === "string") {
\r
12806 targets = TweenLite.selector(targets) || targets;
\r
12808 if (_isSelector(targets)) { //senses if the targets object is a selector. If it is, we should translate it into an array.
\r
12809 targets = _slice.call(targets, 0);
\r
12811 stagger = stagger || 0;
\r
12812 for (i = 0; i < targets.length; i++) {
\r
12813 if (vars.startAt) {
\r
12814 vars.startAt = _copy(vars.startAt);
\r
12816 tl.to(targets[i], duration, _copy(vars), i * stagger);
\r
12818 return this.add(tl, position);
\r
12821 p.staggerFrom = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
\r
12822 vars.immediateRender = (vars.immediateRender != false);
\r
12823 vars.runBackwards = true;
\r
12824 return this.staggerTo(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
\r
12827 p.staggerFromTo = function(targets, duration, fromVars, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
\r
12828 toVars.startAt = fromVars;
\r
12829 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
\r
12830 return this.staggerTo(targets, duration, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
\r
12833 p.call = function(callback, params, scope, position) {
\r
12834 return this.add( TweenLite.delayedCall(0, callback, params, scope), position);
\r
12837 p.set = function(target, vars, position) {
\r
12838 position = this._parseTimeOrLabel(position, 0, true);
\r
12839 if (vars.immediateRender == null) {
\r
12840 vars.immediateRender = (position === this._time && !this._paused);
\r
12842 return this.add( new TweenLite(target, 0, vars), position);
\r
12845 TimelineLite.exportRoot = function(vars, ignoreDelayedCalls) {
\r
12846 vars = vars || {};
\r
12847 if (vars.smoothChildTiming == null) {
\r
12848 vars.smoothChildTiming = true;
\r
12850 var tl = new TimelineLite(vars),
\r
12851 root = tl._timeline,
\r
12853 if (ignoreDelayedCalls == null) {
\r
12854 ignoreDelayedCalls = true;
\r
12856 root._remove(tl, true);
\r
12857 tl._startTime = 0;
\r
12858 tl._rawPrevTime = tl._time = tl._totalTime = root._time;
\r
12859 tween = root._first;
\r
12861 next = tween._next;
\r
12862 if (!ignoreDelayedCalls || !(tween instanceof TweenLite && tween.target === tween.vars.onComplete)) {
\r
12863 tl.add(tween, tween._startTime - tween._delay);
\r
12871 p.add = function(value, position, align, stagger) {
\r
12872 var curTime, l, i, child, tl, beforeRawTime;
\r
12873 if (typeof(position) !== "number") {
\r
12874 position = this._parseTimeOrLabel(position, 0, true, value);
\r
12876 if (!(value instanceof Animation)) {
\r
12877 if ((value instanceof Array) || (value && value.push && _isArray(value))) {
\r
12878 align = align || "normal";
\r
12879 stagger = stagger || 0;
\r
12880 curTime = position;
\r
12881 l = value.length;
\r
12882 for (i = 0; i < l; i++) {
\r
12883 if (_isArray(child = value[i])) {
\r
12884 child = new TimelineLite({tweens:child});
\r
12886 this.add(child, curTime);
\r
12887 if (typeof(child) !== "string" && typeof(child) !== "function") {
\r
12888 if (align === "sequence") {
\r
12889 curTime = child._startTime + (child.totalDuration() / child._timeScale);
\r
12890 } else if (align === "start") {
\r
12891 child._startTime -= child.delay();
\r
12894 curTime += stagger;
\r
12896 return this._uncache(true);
\r
12897 } else if (typeof(value) === "string") {
\r
12898 return this.addLabel(value, position);
\r
12899 } else if (typeof(value) === "function") {
\r
12900 value = TweenLite.delayedCall(0, value);
\r
12902 throw("Cannot add " + value + " into the timeline; it is not a tween, timeline, function, or string.");
\r
12906 SimpleTimeline.prototype.add.call(this, value, position);
\r
12908 //if the timeline has already ended but the inserted tween/timeline extends the duration, we should enable this timeline again so that it renders properly. We should also align the playhead with the parent timeline's when appropriate.
\r
12909 if (this._gc || this._time === this._duration) if (!this._paused) if (this._duration < this.duration()) {
\r
12910 //in case any of the ancestors had completed but should now be enabled...
\r
12912 beforeRawTime = (tl.rawTime() > value._startTime); //if the tween is placed on the timeline so that it starts BEFORE the current rawTime, we should align the playhead (move the timeline). This is because sometimes users will create a timeline, let it finish, and much later append a tween and expect it to run instead of jumping to its end state. While technically one could argue that it should jump to its end state, that's not what users intuitively expect.
\r
12913 while (tl._timeline) {
\r
12914 if (beforeRawTime && tl._timeline.smoothChildTiming) {
\r
12915 tl.totalTime(tl._totalTime, true); //moves the timeline (shifts its startTime) if necessary, and also enables it.
\r
12916 } else if (tl._gc) {
\r
12917 tl._enabled(true, false);
\r
12919 tl = tl._timeline;
\r
12926 p.remove = function(value) {
\r
12927 if (value instanceof Animation) {
\r
12928 return this._remove(value, false);
\r
12929 } else if (value instanceof Array || (value && value.push && _isArray(value))) {
\r
12930 var i = value.length;
\r
12931 while (--i > -1) {
\r
12932 this.remove(value[i]);
\r
12935 } else if (typeof(value) === "string") {
\r
12936 return this.removeLabel(value);
\r
12938 return this.kill(null, value);
\r
12941 p._remove = function(tween, skipDisable) {
\r
12942 SimpleTimeline.prototype._remove.call(this, tween, skipDisable);
\r
12943 var last = this._last;
\r
12945 this._time = this._totalTime = this._duration = this._totalDuration = 0;
\r
12946 } else if (this._time > last._startTime + last._totalDuration / last._timeScale) {
\r
12947 this._time = this.duration();
\r
12948 this._totalTime = this._totalDuration;
\r
12953 p.append = function(value, offsetOrLabel) {
\r
12954 return this.add(value, this._parseTimeOrLabel(null, offsetOrLabel, true, value));
\r
12957 p.insert = p.insertMultiple = function(value, position, align, stagger) {
\r
12958 return this.add(value, position || 0, align, stagger);
\r
12961 p.appendMultiple = function(tweens, offsetOrLabel, align, stagger) {
\r
12962 return this.add(tweens, this._parseTimeOrLabel(null, offsetOrLabel, true, tweens), align, stagger);
\r
12965 p.addLabel = function(label, position) {
\r
12966 this._labels[label] = this._parseTimeOrLabel(position);
\r
12970 p.addPause = function(position, callback, params, scope) {
\r
12971 return this.call(_pauseCallback, ["{self}", callback, params, scope], this, position);
\r
12974 p.removeLabel = function(label) {
\r
12975 delete this._labels[label];
\r
12979 p.getLabelTime = function(label) {
\r
12980 return (this._labels[label] != null) ? this._labels[label] : -1;
\r
12983 p._parseTimeOrLabel = function(timeOrLabel, offsetOrLabel, appendIfAbsent, ignore) {
\r
12985 //if we're about to add a tween/timeline (or an array of them) that's already a child of this timeline, we should remove it first so that it doesn't contaminate the duration().
\r
12986 if (ignore instanceof Animation && ignore.timeline === this) {
\r
12987 this.remove(ignore);
\r
12988 } else if (ignore && ((ignore instanceof Array) || (ignore.push && _isArray(ignore)))) {
\r
12989 i = ignore.length;
\r
12990 while (--i > -1) {
\r
12991 if (ignore[i] instanceof Animation && ignore[i].timeline === this) {
\r
12992 this.remove(ignore[i]);
\r
12996 if (typeof(offsetOrLabel) === "string") {
\r
12997 return this._parseTimeOrLabel(offsetOrLabel, (appendIfAbsent && typeof(timeOrLabel) === "number" && this._labels[offsetOrLabel] == null) ? timeOrLabel - this.duration() : 0, appendIfAbsent);
\r
12999 offsetOrLabel = offsetOrLabel || 0;
\r
13000 if (typeof(timeOrLabel) === "string" && (isNaN(timeOrLabel) || this._labels[timeOrLabel] != null)) { //if the string is a number like "1", check to see if there's a label with that name, otherwise interpret it as a number (absolute value).
\r
13001 i = timeOrLabel.indexOf("=");
\r
13003 if (this._labels[timeOrLabel] == null) {
\r
13004 return appendIfAbsent ? (this._labels[timeOrLabel] = this.duration() + offsetOrLabel) : offsetOrLabel;
\r
13006 return this._labels[timeOrLabel] + offsetOrLabel;
\r
13008 offsetOrLabel = parseInt(timeOrLabel.charAt(i-1) + "1", 10) * Number(timeOrLabel.substr(i+1));
\r
13009 timeOrLabel = (i > 1) ? this._parseTimeOrLabel(timeOrLabel.substr(0, i-1), 0, appendIfAbsent) : this.duration();
\r
13010 } else if (timeOrLabel == null) {
\r
13011 timeOrLabel = this.duration();
\r
13013 return Number(timeOrLabel) + offsetOrLabel;
\r
13016 p.seek = function(position, suppressEvents) {
\r
13017 return this.totalTime((typeof(position) === "number") ? position : this._parseTimeOrLabel(position), (suppressEvents !== false));
\r
13020 p.stop = function() {
\r
13021 return this.paused(true);
\r
13024 p.gotoAndPlay = function(position, suppressEvents) {
\r
13025 return this.play(position, suppressEvents);
\r
13028 p.gotoAndStop = function(position, suppressEvents) {
\r
13029 return this.pause(position, suppressEvents);
\r
13032 p.render = function(time, suppressEvents, force) {
\r
13034 this._enabled(true, false);
\r
13036 var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
\r
13037 prevTime = this._time,
\r
13038 prevStart = this._startTime,
\r
13039 prevTimeScale = this._timeScale,
\r
13040 prevPaused = this._paused,
\r
13041 tween, isComplete, next, callback, internalForce;
\r
13042 if (time >= totalDur) {
\r
13043 this._totalTime = this._time = totalDur;
\r
13044 if (!this._reversed) if (!this._hasPausedChild()) {
\r
13045 isComplete = true;
\r
13046 callback = "onComplete";
\r
13047 if (this._duration === 0) if (time === 0 || this._rawPrevTime < 0 || this._rawPrevTime === _tinyNum) if (this._rawPrevTime !== time && this._first) {
\r
13048 internalForce = true;
\r
13049 if (this._rawPrevTime > _tinyNum) {
\r
13050 callback = "onReverseComplete";
\r
13054 this._rawPrevTime = (this._duration || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
\r
13055 time = totalDur + 0.0001; //to avoid occasional floating point rounding errors - sometimes child tweens/timelines were not being fully completed (their progress might be 0.999999999999998 instead of 1 because when _time - tween._startTime is performed, floating point errors would return a value that was SLIGHTLY off). Try (999999999999.7 - 999999999999) * 1 = 0.699951171875 instead of 0.7.
\r
13057 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
\r
13058 this._totalTime = this._time = 0;
\r
13059 if (prevTime !== 0 || (this._duration === 0 && this._rawPrevTime !== _tinyNum && (this._rawPrevTime > 0 || (time < 0 && this._rawPrevTime >= 0)))) {
\r
13060 callback = "onReverseComplete";
\r
13061 isComplete = this._reversed;
\r
13064 this._active = false;
\r
13065 if (this._duration === 0) if (this._rawPrevTime >= 0 && this._first) { //zero-duration timelines are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
\r
13066 internalForce = true;
\r
13068 this._rawPrevTime = time;
\r
13070 this._rawPrevTime = (this._duration || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
\r
13072 time = 0; //to avoid occasional floating point rounding errors (could cause problems especially with zero-duration tweens at the very beginning of the timeline)
\r
13073 if (!this._initted) {
\r
13074 internalForce = true;
\r
13079 this._totalTime = this._time = this._rawPrevTime = time;
\r
13081 if ((this._time === prevTime || !this._first) && !force && !internalForce) {
\r
13083 } else if (!this._initted) {
\r
13084 this._initted = true;
\r
13087 if (!this._active) if (!this._paused && this._time !== prevTime && time > 0) {
\r
13088 this._active = true; //so that if the user renders the timeline (as opposed to the parent timeline rendering it), it is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the timeline already finished but the user manually re-renders it as halfway done, for example.
\r
13091 if (prevTime === 0) if (this.vars.onStart) if (this._time !== 0) if (!suppressEvents) {
\r
13092 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
\r
13095 if (this._time >= prevTime) {
\r
13096 tween = this._first;
\r
13098 next = tween._next; //record it here because the value could change after rendering...
\r
13099 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
\r
13101 } else if (tween._active || (tween._startTime <= this._time && !tween._paused && !tween._gc)) {
\r
13102 if (!tween._reversed) {
\r
13103 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
\r
13105 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
\r
13111 tween = this._last;
\r
13113 next = tween._prev; //record it here because the value could change after rendering...
\r
13114 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
\r
13116 } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) {
\r
13117 if (!tween._reversed) {
\r
13118 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
\r
13120 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
\r
13127 if (this._onUpdate) if (!suppressEvents) {
\r
13128 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
\r
13131 if (callback) if (!this._gc) if (prevStart === this._startTime || prevTimeScale !== this._timeScale) if (this._time === 0 || totalDur >= this.totalDuration()) { //if one of the tweens that was rendered altered this timeline's startTime (like if an onComplete reversed the timeline), it probably isn't complete. If it is, don't worry, because whatever call altered the startTime would complete if it was necessary at the new time. The only exception is the timeScale property. Also check _gc because there's a chance that kill() could be called in an onUpdate
\r
13132 if (isComplete) {
\r
13133 if (this._timeline.autoRemoveChildren) {
\r
13134 this._enabled(false, false);
\r
13136 this._active = false;
\r
13138 if (!suppressEvents && this.vars[callback]) {
\r
13139 this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
\r
13144 p._hasPausedChild = function() {
\r
13145 var tween = this._first;
\r
13147 if (tween._paused || ((tween instanceof TimelineLite) && tween._hasPausedChild())) {
\r
13150 tween = tween._next;
\r
13155 p.getChildren = function(nested, tweens, timelines, ignoreBeforeTime) {
\r
13156 ignoreBeforeTime = ignoreBeforeTime || -9999999999;
\r
13158 tween = this._first,
\r
13161 if (tween._startTime < ignoreBeforeTime) {
\r
13163 } else if (tween instanceof TweenLite) {
\r
13164 if (tweens !== false) {
\r
13165 a[cnt++] = tween;
\r
13168 if (timelines !== false) {
\r
13169 a[cnt++] = tween;
\r
13171 if (nested !== false) {
\r
13172 a = a.concat(tween.getChildren(true, tweens, timelines));
\r
13176 tween = tween._next;
\r
13181 p.getTweensOf = function(target, nested) {
\r
13182 var disabled = this._gc,
\r
13187 this._enabled(true, true); //getTweensOf() filters out disabled tweens, and we have to mark them as _gc = true when the timeline completes in order to allow clean garbage collection, so temporarily re-enable the timeline here.
\r
13189 tweens = TweenLite.getTweensOf(target);
\r
13190 i = tweens.length;
\r
13191 while (--i > -1) {
\r
13192 if (tweens[i].timeline === this || (nested && this._contains(tweens[i]))) {
\r
13193 a[cnt++] = tweens[i];
\r
13197 this._enabled(false, true);
\r
13202 p._contains = function(tween) {
\r
13203 var tl = tween.timeline;
\r
13205 if (tl === this) {
\r
13208 tl = tl.timeline;
\r
13213 p.shiftChildren = function(amount, adjustLabels, ignoreBeforeTime) {
\r
13214 ignoreBeforeTime = ignoreBeforeTime || 0;
\r
13215 var tween = this._first,
\r
13216 labels = this._labels,
\r
13219 if (tween._startTime >= ignoreBeforeTime) {
\r
13220 tween._startTime += amount;
\r
13222 tween = tween._next;
\r
13224 if (adjustLabels) {
\r
13225 for (p in labels) {
\r
13226 if (labels[p] >= ignoreBeforeTime) {
\r
13227 labels[p] += amount;
\r
13231 return this._uncache(true);
\r
13234 p._kill = function(vars, target) {
\r
13235 if (!vars && !target) {
\r
13236 return this._enabled(false, false);
\r
13238 var tweens = (!target) ? this.getChildren(true, true, false) : this.getTweensOf(target),
\r
13239 i = tweens.length,
\r
13241 while (--i > -1) {
\r
13242 if (tweens[i]._kill(vars, target)) {
\r
13249 p.clear = function(labels) {
\r
13250 var tweens = this.getChildren(false, true, true),
\r
13251 i = tweens.length;
\r
13252 this._time = this._totalTime = 0;
\r
13253 while (--i > -1) {
\r
13254 tweens[i]._enabled(false, false);
\r
13256 if (labels !== false) {
\r
13257 this._labels = {};
\r
13259 return this._uncache(true);
\r
13262 p.invalidate = function() {
\r
13263 var tween = this._first;
\r
13265 tween.invalidate();
\r
13266 tween = tween._next;
\r
13271 p._enabled = function(enabled, ignoreTimeline) {
\r
13272 if (enabled === this._gc) {
\r
13273 var tween = this._first;
\r
13275 tween._enabled(enabled, true);
\r
13276 tween = tween._next;
\r
13279 return SimpleTimeline.prototype._enabled.call(this, enabled, ignoreTimeline);
\r
13282 p.duration = function(value) {
\r
13283 if (!arguments.length) {
\r
13284 if (this._dirty) {
\r
13285 this.totalDuration(); //just triggers recalculation
\r
13287 return this._duration;
\r
13289 if (this.duration() !== 0 && value !== 0) {
\r
13290 this.timeScale(this._duration / value);
\r
13295 p.totalDuration = function(value) {
\r
13296 if (!arguments.length) {
\r
13297 if (this._dirty) {
\r
13299 tween = this._last,
\r
13300 prevStart = 999999999999,
\r
13303 prev = tween._prev; //record it here in case the tween changes position in the sequence...
\r
13304 if (tween._dirty) {
\r
13305 tween.totalDuration(); //could change the tween._startTime, so make sure the tween's cache is clean before analyzing it.
\r
13307 if (tween._startTime > prevStart && this._sortChildren && !tween._paused) { //in case one of the tweens shifted out of order, it needs to be re-inserted into the correct position in the sequence
\r
13308 this.add(tween, tween._startTime - tween._delay);
\r
13310 prevStart = tween._startTime;
\r
13312 if (tween._startTime < 0 && !tween._paused) { //children aren't allowed to have negative startTimes unless smoothChildTiming is true, so adjust here if one is found.
\r
13313 max -= tween._startTime;
\r
13314 if (this._timeline.smoothChildTiming) {
\r
13315 this._startTime += tween._startTime / this._timeScale;
\r
13317 this.shiftChildren(-tween._startTime, false, -9999999999);
\r
13320 end = tween._startTime + (tween._totalDuration / tween._timeScale);
\r
13326 this._duration = this._totalDuration = max;
\r
13327 this._dirty = false;
\r
13329 return this._totalDuration;
\r
13331 if (this.totalDuration() !== 0) if (value !== 0) {
\r
13332 this.timeScale(this._totalDuration / value);
\r
13337 p.usesFrames = function() {
\r
13338 var tl = this._timeline;
\r
13339 while (tl._timeline) {
\r
13340 tl = tl._timeline;
\r
13342 return (tl === Animation._rootFramesTimeline);
\r
13345 p.rawTime = function() {
\r
13346 return this._paused ? this._totalTime : (this._timeline.rawTime() - this._startTime) * this._timeScale;
\r
13349 return TimelineLite;
\r
13366 * ----------------------------------------------------------------
\r
13368 * ----------------------------------------------------------------
\r
13370 window._gsDefine("TimelineMax", ["TimelineLite","TweenLite","easing.Ease"], function(TimelineLite, TweenLite, Ease) {
\r
13372 var TimelineMax = function(vars) {
\r
13373 TimelineLite.call(this, vars);
\r
13374 this._repeat = this.vars.repeat || 0;
\r
13375 this._repeatDelay = this.vars.repeatDelay || 0;
\r
13377 this._yoyo = (this.vars.yoyo === true);
\r
13378 this._dirty = true;
\r
13380 _tinyNum = 0.0000000001,
\r
13381 _blankArray = [],
\r
13382 _easeNone = new Ease(null, null, 1, 0),
\r
13383 p = TimelineMax.prototype = new TimelineLite();
\r
13385 p.constructor = TimelineMax;
\r
13386 p.kill()._gc = false;
\r
13387 TimelineMax.version = "1.12.1";
\r
13389 p.invalidate = function() {
\r
13390 this._yoyo = (this.vars.yoyo === true);
\r
13391 this._repeat = this.vars.repeat || 0;
\r
13392 this._repeatDelay = this.vars.repeatDelay || 0;
\r
13393 this._uncache(true);
\r
13394 return TimelineLite.prototype.invalidate.call(this);
\r
13397 p.addCallback = function(callback, position, params, scope) {
\r
13398 return this.add( TweenLite.delayedCall(0, callback, params, scope), position);
\r
13401 p.removeCallback = function(callback, position) {
\r
13403 if (position == null) {
\r
13404 this._kill(null, callback);
\r
13406 var a = this.getTweensOf(callback, false),
\r
13408 time = this._parseTimeOrLabel(position);
\r
13409 while (--i > -1) {
\r
13410 if (a[i]._startTime === time) {
\r
13411 a[i]._enabled(false, false);
\r
13419 p.tweenTo = function(position, vars) {
\r
13420 vars = vars || {};
\r
13421 var copy = {ease:_easeNone, overwrite:(vars.delay ? 2 : 1), useFrames:this.usesFrames(), immediateRender:false},//note: set overwrite to 1 (true/all) by default unless there's a delay so that we avoid a racing situation that could happen if, for example, an onmousemove creates the same tweenTo() over and over again.
\r
13423 for (p in vars) {
\r
13424 copy[p] = vars[p];
\r
13426 copy.time = this._parseTimeOrLabel(position);
\r
13427 duration = (Math.abs(Number(copy.time) - this._time) / this._timeScale) || 0.001;
\r
13428 t = new TweenLite(this, duration, copy);
\r
13429 copy.onStart = function() {
\r
13430 t.target.paused(true);
\r
13431 if (t.vars.time !== t.target.time() && duration === t.duration()) { //don't make the duration zero - if it's supposed to be zero, don't worry because it's already initting the tween and will complete immediately, effectively making the duration zero anyway. If we make duration zero, the tween won't run at all.
\r
13432 t.duration( Math.abs( t.vars.time - t.target.time()) / t.target._timeScale );
\r
13434 if (vars.onStart) { //in case the user had an onStart in the vars - we don't want to overwrite it.
\r
13435 vars.onStart.apply(vars.onStartScope || t, vars.onStartParams || _blankArray);
\r
13441 p.tweenFromTo = function(fromPosition, toPosition, vars) {
\r
13442 vars = vars || {};
\r
13443 fromPosition = this._parseTimeOrLabel(fromPosition);
\r
13444 vars.startAt = {onComplete:this.seek, onCompleteParams:[fromPosition], onCompleteScope:this};
\r
13445 vars.immediateRender = (vars.immediateRender !== false);
\r
13446 var t = this.tweenTo(toPosition, vars);
\r
13447 return t.duration((Math.abs( t.vars.time - fromPosition) / this._timeScale) || 0.001);
\r
13450 p.render = function(time, suppressEvents, force) {
\r
13452 this._enabled(true, false);
\r
13454 var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
\r
13455 dur = this._duration,
\r
13456 prevTime = this._time,
\r
13457 prevTotalTime = this._totalTime,
\r
13458 prevStart = this._startTime,
\r
13459 prevTimeScale = this._timeScale,
\r
13460 prevRawPrevTime = this._rawPrevTime,
\r
13461 prevPaused = this._paused,
\r
13462 prevCycle = this._cycle,
\r
13463 tween, isComplete, next, callback, internalForce, cycleDuration;
\r
13464 if (time >= totalDur) {
\r
13465 if (!this._locked) {
\r
13466 this._totalTime = totalDur;
\r
13467 this._cycle = this._repeat;
\r
13469 if (!this._reversed) if (!this._hasPausedChild()) {
\r
13470 isComplete = true;
\r
13471 callback = "onComplete";
\r
13472 if (this._duration === 0) if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time && this._first) {
\r
13473 internalForce = true;
\r
13474 if (prevRawPrevTime > _tinyNum) {
\r
13475 callback = "onReverseComplete";
\r
13479 this._rawPrevTime = (this._duration || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
\r
13480 if (this._yoyo && (this._cycle & 1) !== 0) {
\r
13481 this._time = time = 0;
\r
13483 this._time = dur;
\r
13484 time = dur + 0.0001; //to avoid occasional floating point rounding errors - sometimes child tweens/timelines were not being fully completed (their progress might be 0.999999999999998 instead of 1 because when _time - tween._startTime is performed, floating point errors would return a value that was SLIGHTLY off). Try (999999999999.7 - 999999999999) * 1 = 0.699951171875 instead of 0.7. We cannot do less then 0.0001 because the same issue can occur when the duration is extremely large like 999999999999 in which case adding 0.00000001, for example, causes it to act like nothing was added.
\r
13487 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
\r
13488 if (!this._locked) {
\r
13489 this._totalTime = this._cycle = 0;
\r
13492 if (prevTime !== 0 || (dur === 0 && prevRawPrevTime !== _tinyNum && (prevRawPrevTime > 0 || (time < 0 && prevRawPrevTime >= 0)) && !this._locked)) { //edge case for checking time < 0 && prevRawPrevTime >= 0: a zero-duration fromTo() tween inside a zero-duration timeline (yeah, very rare)
\r
13493 callback = "onReverseComplete";
\r
13494 isComplete = this._reversed;
\r
13497 this._active = false;
\r
13498 if (dur === 0) if (prevRawPrevTime >= 0 && this._first) { //zero-duration timelines are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
\r
13499 internalForce = true;
\r
13501 this._rawPrevTime = time;
\r
13503 this._rawPrevTime = (dur || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
\r
13504 time = 0; //to avoid occasional floating point rounding errors (could cause problems especially with zero-duration tweens at the very beginning of the timeline)
\r
13505 if (!this._initted) {
\r
13506 internalForce = true;
\r
13511 if (dur === 0 && prevRawPrevTime < 0) { //without this, zero-duration repeating timelines (like with a simple callback nested at the very beginning and a repeatDelay) wouldn't render the first time through.
\r
13512 internalForce = true;
\r
13514 this._time = this._rawPrevTime = time;
\r
13515 if (!this._locked) {
\r
13516 this._totalTime = time;
\r
13517 if (this._repeat !== 0) {
\r
13518 cycleDuration = dur + this._repeatDelay;
\r
13519 this._cycle = (this._totalTime / cycleDuration) >> 0; //originally _totalTime % cycleDuration but floating point errors caused problems, so I normalized it. (4 % 0.8 should be 0 but it gets reported as 0.79999999!)
\r
13520 if (this._cycle !== 0) if (this._cycle === this._totalTime / cycleDuration) {
\r
13521 this._cycle--; //otherwise when rendered exactly at the end time, it will act as though it is repeating (at the beginning)
\r
13523 this._time = this._totalTime - (this._cycle * cycleDuration);
\r
13524 if (this._yoyo) if ((this._cycle & 1) !== 0) {
\r
13525 this._time = dur - this._time;
\r
13527 if (this._time > dur) {
\r
13528 this._time = dur;
\r
13529 time = dur + 0.0001; //to avoid occasional floating point rounding error
\r
13530 } else if (this._time < 0) {
\r
13531 this._time = time = 0;
\r
13533 time = this._time;
\r
13539 if (this._cycle !== prevCycle) if (!this._locked) {
\r
13541 make sure children at the end/beginning of the timeline are rendered properly. If, for example,
\r
13542 a 3-second long timeline rendered at 2.9 seconds previously, and now renders at 3.2 seconds (which
\r
13543 would get transated to 2.8 seconds if the timeline yoyos or 0.2 seconds if it just repeats), there
\r
13544 could be a callback or a short tween that's at 2.95 or 3 seconds in which wouldn't render. So
\r
13545 we need to push the timeline to the end (and/or beginning depending on its yoyo value). Also we must
\r
13546 ensure that zero-duration tweens at the very beginning or end of the TimelineMax work.
\r
13548 var backwards = (this._yoyo && (prevCycle & 1) !== 0),
\r
13549 wrap = (backwards === (this._yoyo && (this._cycle & 1) !== 0)),
\r
13550 recTotalTime = this._totalTime,
\r
13551 recCycle = this._cycle,
\r
13552 recRawPrevTime = this._rawPrevTime,
\r
13553 recTime = this._time;
\r
13555 this._totalTime = prevCycle * dur;
\r
13556 if (this._cycle < prevCycle) {
\r
13557 backwards = !backwards;
\r
13559 this._totalTime += dur;
\r
13561 this._time = prevTime; //temporarily revert _time so that render() renders the children in the correct order. Without this, tweens won't rewind correctly. We could arhictect things in a "cleaner" way by splitting out the rendering queue into a separate method but for performance reasons, we kept it all inside this method.
\r
13563 this._rawPrevTime = (dur === 0) ? prevRawPrevTime - 0.0001 : prevRawPrevTime;
\r
13564 this._cycle = prevCycle;
\r
13565 this._locked = true; //prevents changes to totalTime and skips repeat/yoyo behavior when we recursively call render()
\r
13566 prevTime = (backwards) ? 0 : dur;
\r
13567 this.render(prevTime, suppressEvents, (dur === 0));
\r
13568 if (!suppressEvents) if (!this._gc) {
\r
13569 if (this.vars.onRepeat) {
\r
13570 this.vars.onRepeat.apply(this.vars.onRepeatScope || this, this.vars.onRepeatParams || _blankArray);
\r
13574 prevTime = (backwards) ? dur + 0.0001 : -0.0001;
\r
13575 this.render(prevTime, true, false);
\r
13577 this._locked = false;
\r
13578 if (this._paused && !prevPaused) { //if the render() triggered callback that paused this timeline, we should abort (very rare, but possible)
\r
13581 this._time = recTime;
\r
13582 this._totalTime = recTotalTime;
\r
13583 this._cycle = recCycle;
\r
13584 this._rawPrevTime = recRawPrevTime;
\r
13587 if ((this._time === prevTime || !this._first) && !force && !internalForce) {
\r
13588 if (prevTotalTime !== this._totalTime) if (this._onUpdate) if (!suppressEvents) { //so that onUpdate fires even during the repeatDelay - as long as the totalTime changed, we should trigger onUpdate.
\r
13589 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
\r
13592 } else if (!this._initted) {
\r
13593 this._initted = true;
\r
13596 if (!this._active) if (!this._paused && this._totalTime !== prevTotalTime && time > 0) {
\r
13597 this._active = true; //so that if the user renders the timeline (as opposed to the parent timeline rendering it), it is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the timeline already finished but the user manually re-renders it as halfway done, for example.
\r
13600 if (prevTotalTime === 0) if (this.vars.onStart) if (this._totalTime !== 0) if (!suppressEvents) {
\r
13601 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
\r
13604 if (this._time >= prevTime) {
\r
13605 tween = this._first;
\r
13607 next = tween._next; //record it here because the value could change after rendering...
\r
13608 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
\r
13610 } else if (tween._active || (tween._startTime <= this._time && !tween._paused && !tween._gc)) {
\r
13611 if (!tween._reversed) {
\r
13612 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
\r
13614 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
\r
13621 tween = this._last;
\r
13623 next = tween._prev; //record it here because the value could change after rendering...
\r
13624 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
\r
13626 } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) {
\r
13627 if (!tween._reversed) {
\r
13628 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
\r
13630 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
\r
13637 if (this._onUpdate) if (!suppressEvents) {
\r
13638 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
\r
13640 if (callback) if (!this._locked) if (!this._gc) if (prevStart === this._startTime || prevTimeScale !== this._timeScale) if (this._time === 0 || totalDur >= this.totalDuration()) { //if one of the tweens that was rendered altered this timeline's startTime (like if an onComplete reversed the timeline), it probably isn't complete. If it is, don't worry, because whatever call altered the startTime would complete if it was necessary at the new time. The only exception is the timeScale property. Also check _gc because there's a chance that kill() could be called in an onUpdate
\r
13641 if (isComplete) {
\r
13642 if (this._timeline.autoRemoveChildren) {
\r
13643 this._enabled(false, false);
\r
13645 this._active = false;
\r
13647 if (!suppressEvents && this.vars[callback]) {
\r
13648 this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
\r
13653 p.getActive = function(nested, tweens, timelines) {
\r
13654 if (nested == null) {
\r
13657 if (tweens == null) {
\r
13660 if (timelines == null) {
\r
13661 timelines = false;
\r
13664 all = this.getChildren(nested, tweens, timelines),
\r
13668 for (i = 0; i < l; i++) {
\r
13670 if (tween.isActive()) {
\r
13671 a[cnt++] = tween;
\r
13678 p.getLabelAfter = function(time) {
\r
13679 if (!time) if (time !== 0) { //faster than isNan()
\r
13680 time = this._time;
\r
13682 var labels = this.getLabelsArray(),
\r
13683 l = labels.length,
\r
13685 for (i = 0; i < l; i++) {
\r
13686 if (labels[i].time > time) {
\r
13687 return labels[i].name;
\r
13693 p.getLabelBefore = function(time) {
\r
13694 if (time == null) {
\r
13695 time = this._time;
\r
13697 var labels = this.getLabelsArray(),
\r
13698 i = labels.length;
\r
13699 while (--i > -1) {
\r
13700 if (labels[i].time < time) {
\r
13701 return labels[i].name;
\r
13707 p.getLabelsArray = function() {
\r
13711 for (p in this._labels) {
\r
13712 a[cnt++] = {time:this._labels[p], name:p};
\r
13714 a.sort(function(a,b) {
\r
13715 return a.time - b.time;
\r
13721 //---- GETTERS / SETTERS -------------------------------------------------------------------------------------------------------
\r
13723 p.progress = function(value) {
\r
13724 return (!arguments.length) ? this._time / this.duration() : this.totalTime( this.duration() * ((this._yoyo && (this._cycle & 1) !== 0) ? 1 - value : value) + (this._cycle * (this._duration + this._repeatDelay)), false);
\r
13727 p.totalProgress = function(value) {
\r
13728 return (!arguments.length) ? this._totalTime / this.totalDuration() : this.totalTime( this.totalDuration() * value, false);
\r
13731 p.totalDuration = function(value) {
\r
13732 if (!arguments.length) {
\r
13733 if (this._dirty) {
\r
13734 TimelineLite.prototype.totalDuration.call(this); //just forces refresh
\r
13735 //Instead of Infinity, we use 999999999999 so that we can accommodate reverses.
\r
13736 this._totalDuration = (this._repeat === -1) ? 999999999999 : this._duration * (this._repeat + 1) + (this._repeatDelay * this._repeat);
\r
13738 return this._totalDuration;
\r
13740 return (this._repeat === -1) ? this : this.duration( (value - (this._repeat * this._repeatDelay)) / (this._repeat + 1) );
\r
13743 p.time = function(value, suppressEvents) {
\r
13744 if (!arguments.length) {
\r
13745 return this._time;
\r
13747 if (this._dirty) {
\r
13748 this.totalDuration();
\r
13750 if (value > this._duration) {
\r
13751 value = this._duration;
\r
13753 if (this._yoyo && (this._cycle & 1) !== 0) {
\r
13754 value = (this._duration - value) + (this._cycle * (this._duration + this._repeatDelay));
\r
13755 } else if (this._repeat !== 0) {
\r
13756 value += this._cycle * (this._duration + this._repeatDelay);
\r
13758 return this.totalTime(value, suppressEvents);
\r
13761 p.repeat = function(value) {
\r
13762 if (!arguments.length) {
\r
13763 return this._repeat;
\r
13765 this._repeat = value;
\r
13766 return this._uncache(true);
\r
13769 p.repeatDelay = function(value) {
\r
13770 if (!arguments.length) {
\r
13771 return this._repeatDelay;
\r
13773 this._repeatDelay = value;
\r
13774 return this._uncache(true);
\r
13777 p.yoyo = function(value) {
\r
13778 if (!arguments.length) {
\r
13779 return this._yoyo;
\r
13781 this._yoyo = value;
\r
13785 p.currentLabel = function(value) {
\r
13786 if (!arguments.length) {
\r
13787 return this.getLabelBefore(this._time + 0.00000001);
\r
13789 return this.seek(value, true);
\r
13792 return TimelineMax;
\r
13808 * ----------------------------------------------------------------
\r
13810 * ----------------------------------------------------------------
\r
13814 var _RAD2DEG = 180 / Math.PI,
\r
13819 Segment = function(a, b, c, d) {
\r
13828 _correlate = ",x,y,z,left,top,right,bottom,marginTop,marginLeft,marginRight,marginBottom,paddingLeft,paddingTop,paddingRight,paddingBottom,backgroundPosition,backgroundPosition_y,",
\r
13829 cubicToQuadratic = function(a, b, c, d) {
\r
13834 mab = (a + b) / 2,
\r
13835 mbc = (b + c) / 2,
\r
13836 mcd = (c + d) / 2,
\r
13837 mabc = (mab + mbc) / 2,
\r
13838 mbcd = (mbc + mcd) / 2,
\r
13839 m8 = (mbcd - mabc) / 8;
\r
13840 q1.b = mab + (a - mab) / 4;
\r
13841 q2.b = mabc + m8;
\r
13842 q1.c = q2.a = (q1.b + q2.b) / 2;
\r
13843 q2.c = q3.a = (mabc + mbcd) / 2;
\r
13844 q3.b = mbcd - m8;
\r
13845 q4.b = mcd + (d - mcd) / 4;
\r
13846 q3.c = q4.a = (q3.b + q4.b) / 2;
\r
13847 return [q1, q2, q3, q4];
\r
13849 _calculateControlPoints = function(a, curviness, quad, basic, correlate) {
\r
13850 var l = a.length - 1,
\r
13853 i, p1, p2, p3, seg, m1, m2, mm, cp2, qb, r1, r2, tl;
\r
13854 for (i = 0; i < l; i++) {
\r
13863 tl = ((r2 + r1) * curviness * 0.25) / (basic ? 0.5 : _r3[i] || 0.5);
\r
13864 m1 = p2 - (p2 - p1) * (basic ? curviness * 0.5 : (r1 !== 0 ? tl / r1 : 0));
\r
13865 m2 = p2 + (p3 - p2) * (basic ? curviness * 0.5 : (r2 !== 0 ? tl / r2 : 0));
\r
13866 mm = p2 - (m1 + (((m2 - m1) * ((r1 * 3 / (r1 + r2)) + 0.5) / 4) || 0));
\r
13868 m1 = p2 - (p2 - p1) * curviness * 0.5;
\r
13869 m2 = p2 + (p3 - p2) * curviness * 0.5;
\r
13870 mm = p2 - (m1 + m2) / 2;
\r
13875 seg.c = cp2 = m1;
\r
13879 seg.b = cp1 = seg.a + (seg.c - seg.a) * 0.6; //instead of placing b on a exactly, we move it inline with c so that if the user specifies an ease like Back.easeIn or Elastic.easeIn which goes BEYOND the beginning, it will do so smoothly.
\r
13882 seg.da = p2 - p1;
\r
13883 seg.ca = cp2 - p1;
\r
13884 seg.ba = cp1 - p1;
\r
13887 qb = cubicToQuadratic(p1, cp1, cp2, p2);
\r
13888 a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]);
\r
13898 seg.c = cp1 + (seg.d - cp1) * 0.4; //instead of placing c on d exactly, we move it inline with b so that if the user specifies an ease like Back.easeOut or Elastic.easeOut which goes BEYOND the end, it will do so smoothly.
\r
13899 seg.da = seg.d - seg.a;
\r
13900 seg.ca = seg.c - seg.a;
\r
13901 seg.ba = cp1 - seg.a;
\r
13903 qb = cubicToQuadratic(seg.a, cp1, seg.c, seg.d);
\r
13904 a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]);
\r
13907 _parseAnchors = function(values, p, correlate, prepend) {
\r
13909 l, i, p1, p2, p3, tmp;
\r
13911 values = [prepend].concat(values);
\r
13912 i = values.length;
\r
13913 while (--i > -1) {
\r
13914 if (typeof( (tmp = values[i][p]) ) === "string") if (tmp.charAt(1) === "=") {
\r
13915 values[i][p] = prepend[p] + Number(tmp.charAt(0) + tmp.substr(2)); //accommodate relative values. Do it inline instead of breaking it out into a function for speed reasons
\r
13919 l = values.length - 2;
\r
13921 a[0] = new Segment(values[0][p], 0, 0, values[(l < -1) ? 0 : 1][p]);
\r
13924 for (i = 0; i < l; i++) {
\r
13925 p1 = values[i][p];
\r
13926 p2 = values[i+1][p];
\r
13927 a[i] = new Segment(p1, 0, 0, p2);
\r
13929 p3 = values[i+2][p];
\r
13930 _r1[i] = (_r1[i] || 0) + (p2 - p1) * (p2 - p1);
\r
13931 _r2[i] = (_r2[i] || 0) + (p3 - p2) * (p3 - p2);
\r
13934 a[i] = new Segment(values[i][p], 0, 0, values[i+1][p]);
\r
13937 bezierThrough = function(values, curviness, quadratic, basic, correlate, prepend) {
\r
13940 first = prepend || values[0],
\r
13941 i, p, a, j, r, l, seamless, last;
\r
13942 correlate = (typeof(correlate) === "string") ? ","+correlate+"," : _correlate;
\r
13943 if (curviness == null) {
\r
13946 for (p in values[0]) {
\r
13949 //check to see if the last and first values are identical (well, within 0.05). If so, make seamless by appending the second element to the very end of the values array and the 2nd-to-last element to the very beginning (we'll remove those segments later)
\r
13950 if (values.length > 1) {
\r
13951 last = values[values.length - 1];
\r
13953 i = props.length;
\r
13954 while (--i > -1) {
\r
13956 if (Math.abs(first[p] - last[p]) > 0.05) { //build in a tolerance of +/-0.05 to accommodate rounding errors. For example, if you set an object's position to 4.945, Flash will make it 4.9
\r
13957 seamless = false;
\r
13962 values = values.concat(); //duplicate the array to avoid contaminating the original which the user may be reusing for other tweens
\r
13964 values.unshift(prepend);
\r
13966 values.push(values[1]);
\r
13967 prepend = values[values.length - 3];
\r
13970 _r1.length = _r2.length = _r3.length = 0;
\r
13971 i = props.length;
\r
13972 while (--i > -1) {
\r
13974 _corProps[p] = (correlate.indexOf(","+p+",") !== -1);
\r
13975 obj[p] = _parseAnchors(values, p, _corProps[p], prepend);
\r
13978 while (--i > -1) {
\r
13979 _r1[i] = Math.sqrt(_r1[i]);
\r
13980 _r2[i] = Math.sqrt(_r2[i]);
\r
13983 i = props.length;
\r
13984 while (--i > -1) {
\r
13985 if (_corProps[p]) {
\r
13986 a = obj[props[i]];
\r
13987 l = a.length - 1;
\r
13988 for (j = 0; j < l; j++) {
\r
13989 r = a[j+1].da / _r2[j] + a[j].da / _r1[j];
\r
13990 _r3[j] = (_r3[j] || 0) + r * r;
\r
13995 while (--i > -1) {
\r
13996 _r3[i] = Math.sqrt(_r3[i]);
\r
13999 i = props.length;
\r
14000 j = quadratic ? 4 : 1;
\r
14001 while (--i > -1) {
\r
14004 _calculateControlPoints(a, curviness, quadratic, basic, _corProps[p]); //this method requires that _parseAnchors() and _setSegmentRatios() ran first so that _r1, _r2, and _r3 values are populated for all properties
\r
14007 a.splice(a.length - j, j);
\r
14012 _parseBezierData = function(values, type, prepend) {
\r
14013 type = type || "soft";
\r
14015 inc = (type === "cubic") ? 3 : 2,
\r
14016 soft = (type === "soft"),
\r
14018 a, b, c, d, cur, i, j, l, p, cnt, tmp;
\r
14019 if (soft && prepend) {
\r
14020 values = [prepend].concat(values);
\r
14022 if (values == null || values.length < inc + 1) { throw "invalid Bezier data"; }
\r
14023 for (p in values[0]) {
\r
14026 i = props.length;
\r
14027 while (--i > -1) {
\r
14029 obj[p] = cur = [];
\r
14031 l = values.length;
\r
14032 for (j = 0; j < l; j++) {
\r
14033 a = (prepend == null) ? values[j][p] : (typeof( (tmp = values[j][p]) ) === "string" && tmp.charAt(1) === "=") ? prepend[p] + Number(tmp.charAt(0) + tmp.substr(2)) : Number(tmp);
\r
14034 if (soft) if (j > 1) if (j < l - 1) {
\r
14035 cur[cnt++] = (a + cur[cnt-2]) / 2;
\r
14039 l = cnt - inc + 1;
\r
14041 for (j = 0; j < l; j += inc) {
\r
14045 d = (inc === 2) ? 0 : cur[j+3];
\r
14046 cur[cnt++] = tmp = (inc === 3) ? new Segment(a, b, c, d) : new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c);
\r
14048 cur.length = cnt;
\r
14052 _addCubicLengths = function(a, steps, resolution) {
\r
14053 var inc = 1 / resolution,
\r
14055 d, d1, s, da, ca, ba, p, i, inv, bez, index;
\r
14056 while (--j > -1) {
\r
14063 for (i = 1; i <= resolution; i++) {
\r
14066 d = d1 - (d1 = (p * p * da + 3 * inv * (p * ca + inv * ba)) * p);
\r
14067 index = j * resolution + i - 1;
\r
14068 steps[index] = (steps[index] || 0) + d * d;
\r
14072 _parseLengthData = function(obj, resolution) {
\r
14073 resolution = resolution >> 0 || 6;
\r
14078 threshold = resolution - 1,
\r
14080 curLS = [], //current length segments array
\r
14083 _addCubicLengths(obj[p], a, resolution);
\r
14086 for (i = 0; i < l; i++) {
\r
14087 d += Math.sqrt(a[i]);
\r
14088 index = i % resolution;
\r
14089 curLS[index] = d;
\r
14090 if (index === threshold) {
\r
14092 index = (i / resolution) >> 0;
\r
14093 segments[index] = curLS;
\r
14094 lengths[index] = total;
\r
14099 return {length:total, lengths:lengths, segments:segments};
\r
14104 BezierPlugin = window._gsDefine.plugin({
\r
14105 propName: "bezier",
\r
14107 version: "1.3.2",
\r
14111 //gets called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
\r
14112 init: function(target, vars, tween) {
\r
14113 this._target = target;
\r
14114 if (vars instanceof Array) {
\r
14115 vars = {values:vars};
\r
14118 this._round = {};
\r
14119 this._props = [];
\r
14120 this._timeRes = (vars.timeResolution == null) ? 6 : parseInt(vars.timeResolution, 10);
\r
14121 var values = vars.values || [],
\r
14123 second = values[0],
\r
14124 autoRotate = vars.autoRotate || tween.vars.orientToBezier,
\r
14125 p, isFunc, i, j, prepend;
\r
14127 this._autoRotate = autoRotate ? (autoRotate instanceof Array) ? autoRotate : [["x","y","rotation",((autoRotate === true) ? 0 : Number(autoRotate) || 0)]] : null;
\r
14128 for (p in second) {
\r
14129 this._props.push(p);
\r
14132 i = this._props.length;
\r
14133 while (--i > -1) {
\r
14134 p = this._props[i];
\r
14136 this._overwriteProps.push(p);
\r
14137 isFunc = this._func[p] = (typeof(target[p]) === "function");
\r
14138 first[p] = (!isFunc) ? parseFloat(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]();
\r
14139 if (!prepend) if (first[p] !== values[0][p]) {
\r
14143 this._beziers = (vars.type !== "cubic" && vars.type !== "quadratic" && vars.type !== "soft") ? bezierThrough(values, isNaN(vars.curviness) ? 1 : vars.curviness, false, (vars.type === "thruBasic"), vars.correlate, prepend) : _parseBezierData(values, vars.type, first);
\r
14144 this._segCount = this._beziers[p].length;
\r
14146 if (this._timeRes) {
\r
14147 var ld = _parseLengthData(this._beziers, this._timeRes);
\r
14148 this._length = ld.length;
\r
14149 this._lengths = ld.lengths;
\r
14150 this._segments = ld.segments;
\r
14151 this._l1 = this._li = this._s1 = this._si = 0;
\r
14152 this._l2 = this._lengths[0];
\r
14153 this._curSeg = this._segments[0];
\r
14154 this._s2 = this._curSeg[0];
\r
14155 this._prec = 1 / this._curSeg.length;
\r
14158 if ((autoRotate = this._autoRotate)) {
\r
14159 this._initialRotations = [];
\r
14160 if (!(autoRotate[0] instanceof Array)) {
\r
14161 this._autoRotate = autoRotate = [autoRotate];
\r
14163 i = autoRotate.length;
\r
14164 while (--i > -1) {
\r
14165 for (j = 0; j < 3; j++) {
\r
14166 p = autoRotate[i][j];
\r
14167 this._func[p] = (typeof(target[p]) === "function") ? target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ] : false;
\r
14169 p = autoRotate[i][2];
\r
14170 this._initialRotations[i] = this._func[p] ? this._func[p].call(this._target) : this._target[p];
\r
14173 this._startRatio = tween.vars.runBackwards ? 1 : 0; //we determine the starting ratio when the tween inits which is always 0 unless the tween has runBackwards:true (indicating it's a from() tween) in which case it's 1.
\r
14177 //called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
\r
14178 set: function(v) {
\r
14179 var segments = this._segCount,
\r
14180 func = this._func,
\r
14181 target = this._target,
\r
14182 notStart = (v !== this._startRatio),
\r
14183 curIndex, inv, i, p, b, t, val, l, lengths, curSeg;
\r
14184 if (!this._timeRes) {
\r
14185 curIndex = (v < 0) ? 0 : (v >= 1) ? segments - 1 : (segments * v) >> 0;
\r
14186 t = (v - (curIndex * (1 / segments))) * segments;
\r
14188 lengths = this._lengths;
\r
14189 curSeg = this._curSeg;
\r
14190 v *= this._length;
\r
14192 //find the appropriate segment (if the currently cached one isn't correct)
\r
14193 if (v > this._l2 && i < segments - 1) {
\r
14194 l = segments - 1;
\r
14195 while (i < l && (this._l2 = lengths[++i]) <= v) { }
\r
14196 this._l1 = lengths[i-1];
\r
14198 this._curSeg = curSeg = this._segments[i];
\r
14199 this._s2 = curSeg[(this._s1 = this._si = 0)];
\r
14200 } else if (v < this._l1 && i > 0) {
\r
14201 while (i > 0 && (this._l1 = lengths[--i]) >= v) { }
\r
14202 if (i === 0 && v < this._l1) {
\r
14207 this._l2 = lengths[i];
\r
14209 this._curSeg = curSeg = this._segments[i];
\r
14210 this._s1 = curSeg[(this._si = curSeg.length - 1) - 1] || 0;
\r
14211 this._s2 = curSeg[this._si];
\r
14214 //now find the appropriate sub-segment (we split it into the number of pieces that was defined by "precision" and measured each one)
\r
14217 if (v > this._s2 && i < curSeg.length - 1) {
\r
14218 l = curSeg.length - 1;
\r
14219 while (i < l && (this._s2 = curSeg[++i]) <= v) { }
\r
14220 this._s1 = curSeg[i-1];
\r
14222 } else if (v < this._s1 && i > 0) {
\r
14223 while (i > 0 && (this._s1 = curSeg[--i]) >= v) { }
\r
14224 if (i === 0 && v < this._s1) {
\r
14229 this._s2 = curSeg[i];
\r
14232 t = (i + (v - this._s1) / (this._s2 - this._s1)) * this._prec;
\r
14236 i = this._props.length;
\r
14237 while (--i > -1) {
\r
14238 p = this._props[i];
\r
14239 b = this._beziers[p][curIndex];
\r
14240 val = (t * t * b.da + 3 * inv * (t * b.ca + inv * b.ba)) * t + b.a;
\r
14241 if (this._round[p]) {
\r
14242 val = Math.round(val);
\r
14251 if (this._autoRotate) {
\r
14252 var ar = this._autoRotate,
\r
14253 b2, x1, y1, x2, y2, add, conv;
\r
14255 while (--i > -1) {
\r
14257 add = ar[i][3] || 0;
\r
14258 conv = (ar[i][4] === true) ? 1 : _RAD2DEG;
\r
14259 b = this._beziers[ar[i][0]];
\r
14260 b2 = this._beziers[ar[i][1]];
\r
14262 if (b && b2) { //in case one of the properties got overwritten.
\r
14264 b2 = b2[curIndex];
\r
14266 x1 = b.a + (b.b - b.a) * t;
\r
14267 x2 = b.b + (b.c - b.b) * t;
\r
14268 x1 += (x2 - x1) * t;
\r
14269 x2 += ((b.c + (b.d - b.c) * t) - x2) * t;
\r
14271 y1 = b2.a + (b2.b - b2.a) * t;
\r
14272 y2 = b2.b + (b2.c - b2.b) * t;
\r
14273 y1 += (y2 - y1) * t;
\r
14274 y2 += ((b2.c + (b2.d - b2.c) * t) - y2) * t;
\r
14276 val = notStart ? Math.atan2(y2 - y1, x2 - x1) * conv + add : this._initialRotations[i];
\r
14288 p = BezierPlugin.prototype;
\r
14291 BezierPlugin.bezierThrough = bezierThrough;
\r
14292 BezierPlugin.cubicToQuadratic = cubicToQuadratic;
\r
14293 BezierPlugin._autoCSS = true; //indicates that this plugin can be inserted into the "css" object using the autoCSS feature of TweenLite
\r
14294 BezierPlugin.quadraticToCubic = function(a, b, c) {
\r
14295 return new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c);
\r
14298 BezierPlugin._cssRegister = function() {
\r
14299 var CSSPlugin = window._gsDefine.globals.CSSPlugin;
\r
14300 if (!CSSPlugin) {
\r
14303 var _internals = CSSPlugin._internals,
\r
14304 _parseToProxy = _internals._parseToProxy,
\r
14305 _setPluginRatio = _internals._setPluginRatio,
\r
14306 CSSPropTween = _internals.CSSPropTween;
\r
14307 _internals._registerComplexSpecialProp("bezier", {parser:function(t, e, prop, cssp, pt, plugin) {
\r
14308 if (e instanceof Array) {
\r
14311 plugin = new BezierPlugin();
\r
14312 var values = e.values,
\r
14313 l = values.length - 1,
\r
14314 pluginValues = [],
\r
14320 for (i = 0; i <= l; i++) {
\r
14321 data = _parseToProxy(t, values[i], cssp, pt, plugin, (l !== i));
\r
14322 pluginValues[i] = data.end;
\r
14325 v[p] = e[p]; //duplicate the vars object because we need to alter some things which would cause problems if the user plans to reuse the same vars object for another tween.
\r
14327 v.values = pluginValues;
\r
14328 pt = new CSSPropTween(t, "bezier", 0, 0, data.pt, 2);
\r
14330 pt.plugin = plugin;
\r
14331 pt.setRatio = _setPluginRatio;
\r
14332 if (v.autoRotate === 0) {
\r
14333 v.autoRotate = true;
\r
14335 if (v.autoRotate && !(v.autoRotate instanceof Array)) {
\r
14336 i = (v.autoRotate === true) ? 0 : Number(v.autoRotate);
\r
14337 v.autoRotate = (data.end.left != null) ? [["left","top","rotation",i,false]] : (data.end.x != null) ? [["x","y","rotation",i,false]] : false;
\r
14339 if (v.autoRotate) {
\r
14340 if (!cssp._transform) {
\r
14341 cssp._enableTransforms(false);
\r
14343 data.autoRotate = cssp._target._gsTransform;
\r
14345 plugin._onInitTween(data.proxy, v, cssp._tween);
\r
14350 p._roundProps = function(lookup, value) {
\r
14351 var op = this._overwriteProps,
\r
14353 while (--i > -1) {
\r
14354 if (lookup[op[i]] || lookup.bezier || lookup.bezierThrough) {
\r
14355 this._round[op[i]] = value;
\r
14360 p._kill = function(lookup) {
\r
14361 var a = this._props,
\r
14363 for (p in this._beziers) {
\r
14364 if (p in lookup) {
\r
14365 delete this._beziers[p];
\r
14366 delete this._func[p];
\r
14368 while (--i > -1) {
\r
14369 if (a[i] === p) {
\r
14375 return this._super._kill.call(this, lookup);
\r
14394 * ----------------------------------------------------------------
\r
14396 * ----------------------------------------------------------------
\r
14398 window._gsDefine("plugins.CSSPlugin", ["plugins.TweenPlugin","TweenLite"], function(TweenPlugin, TweenLite) {
\r
14400 /** @constructor **/
\r
14401 var CSSPlugin = function() {
\r
14402 TweenPlugin.call(this, "css");
\r
14403 this._overwriteProps.length = 0;
\r
14404 this.setRatio = CSSPlugin.prototype.setRatio; //speed optimization (avoid prototype lookup on this "hot" method)
\r
14406 _hasPriority, //turns true whenever a CSSPropTween instance is created that has a priority other than 0. This helps us discern whether or not we should spend the time organizing the linked list or not after a CSSPlugin's _onInitTween() method is called.
\r
14407 _suffixMap, //we set this in _onInitTween() each time as a way to have a persistent variable we can use in other methods like _parse() without having to pass it around as a parameter and we keep _parse() decoupled from a particular CSSPlugin instance
\r
14408 _cs, //computed style (we store this in a shared variable to conserve memory and make minification tighter
\r
14409 _overwriteProps, //alias to the currently instantiating CSSPlugin's _overwriteProps array. We use this closure in order to avoid having to pass a reference around from method to method and aid in minification.
\r
14410 _specialProps = {},
\r
14411 p = CSSPlugin.prototype = new TweenPlugin("css");
\r
14413 p.constructor = CSSPlugin;
\r
14414 CSSPlugin.version = "1.12.1";
\r
14415 CSSPlugin.API = 2;
\r
14416 CSSPlugin.defaultTransformPerspective = 0;
\r
14417 CSSPlugin.defaultSkewType = "compensated";
\r
14418 p = "px"; //we'll reuse the "p" variable to keep file size down
\r
14419 CSSPlugin.suffixMap = {top:p, right:p, bottom:p, left:p, width:p, height:p, fontSize:p, padding:p, margin:p, perspective:p, lineHeight:""};
\r
14422 var _numExp = /(?:\d|\-\d|\.\d|\-\.\d)+/g,
\r
14423 _relNumExp = /(?:\d|\-\d|\.\d|\-\.\d|\+=\d|\-=\d|\+=.\d|\-=\.\d)+/g,
\r
14424 _valuesExp = /(?:\+=|\-=|\-|\b)[\d\-\.]+[a-zA-Z0-9]*(?:%|\b)/gi, //finds all the values that begin with numbers or += or -= and then a number. Includes suffixes. We use this to split complex values apart like "1px 5px 20px rgb(255,102,51)"
\r
14425 _NaNExp = /[^\d\-\.]/g,
\r
14426 _suffixExp = /(?:\d|\-|\+|=|#|\.)*/g,
\r
14427 _opacityExp = /opacity *= *([^)]*)/i,
\r
14428 _opacityValExp = /opacity:([^;]*)/i,
\r
14429 _alphaFilterExp = /alpha\(opacity *=.+?\)/i,
\r
14430 _rgbhslExp = /^(rgb|hsl)/,
\r
14431 _capsExp = /([A-Z])/g,
\r
14432 _camelExp = /-([a-z])/gi,
\r
14433 _urlExp = /(^(?:url\(\"|url\())|(?:(\"\))$|\)$)/gi, //for pulling out urls from url(...) or url("...") strings (some browsers wrap urls in quotes, some don't when reporting things like backgroundImage)
\r
14434 _camelFunc = function(s, g) { return g.toUpperCase(); },
\r
14435 _horizExp = /(?:Left|Right|Width)/i,
\r
14436 _ieGetMatrixExp = /(M11|M12|M21|M22)=[\d\-\.e]+/gi,
\r
14437 _ieSetMatrixExp = /progid\:DXImageTransform\.Microsoft\.Matrix\(.+?\)/i,
\r
14438 _commasOutsideParenExp = /,(?=[^\)]*(?:\(|$))/gi, //finds any commas that are not within parenthesis
\r
14439 _DEG2RAD = Math.PI / 180,
\r
14440 _RAD2DEG = 180 / Math.PI,
\r
14443 _tempDiv = _doc.createElement("div"),
\r
14444 _tempImg = _doc.createElement("img"),
\r
14445 _internals = CSSPlugin._internals = {_specialProps:_specialProps}, //provides a hook to a few internal methods that we need to access from inside other plugins
\r
14446 _agent = navigator.userAgent,
\r
14448 _reqSafariFix, //we won't apply the Safari transform fix until we actually come across a tween that affects a transform property (to maintain best performance).
\r
14451 _isFirefox, //Firefox has a bug that causes 3D transformed elements to randomly disappear unless a repaint is forced after each update on each element.
\r
14452 _isSafariLT6, //Safari (and Android 4 which uses a flavor of Safari) has a bug that prevents changes to "top" and "left" properties from rendering properly if changed on the same frame as a transform UNLESS we set the element's WebkitBackfaceVisibility to hidden (weird, I know). Doing this for Android 3 and earlier seems to actually cause other problems, though (fun!)
\r
14454 _supportsOpacity = (function() { //we set _isSafari, _ieVers, _isFirefox, and _supportsOpacity all in one function here to reduce file size slightly, especially in the minified version.
\r
14455 var i = _agent.indexOf("Android"),
\r
14456 d = _doc.createElement("div"), a;
\r
14458 _isSafari = (_agent.indexOf("Safari") !== -1 && _agent.indexOf("Chrome") === -1 && (i === -1 || Number(_agent.substr(i+8, 1)) > 3));
\r
14459 _isSafariLT6 = (_isSafari && (Number(_agent.substr(_agent.indexOf("Version/")+8, 1)) < 6));
\r
14460 _isFirefox = (_agent.indexOf("Firefox") !== -1);
\r
14462 if ((/MSIE ([0-9]{1,}[\.0-9]{0,})/).exec(_agent)) {
\r
14463 _ieVers = parseFloat( RegExp.$1 );
\r
14466 d.innerHTML = "<a title='' style='top:1px;opacity:.55;'>a</a>";
\r
14467 a = d.getElementsByTagName("a")[0];
\r
14468 return a ? /^0.55/.test(a.style.opacity) : false;
\r
14470 _getIEOpacity = function(v) {
\r
14471 return (_opacityExp.test( ((typeof(v) === "string") ? v : (v.currentStyle ? v.currentStyle.filter : v.style.filter) || "") ) ? ( parseFloat( RegExp.$1 ) / 100 ) : 1);
\r
14473 _log = function(s) {//for logging messages, but in a way that won't throw errors in old versions of IE.
\r
14474 if (window.console) {
\r
14475 //console.log(s);
\r
14478 _prefixCSS = "", //the non-camelCase vendor prefix like "-o-", "-moz-", "-ms-", or "-webkit-"
\r
14479 _prefix = "", //camelCase vendor prefix like "O", "ms", "Webkit", or "Moz".
\r
14481 // @private feed in a camelCase property name like "transform" and it will check to see if it is valid as-is or if it needs a vendor prefix. It returns the corrected camelCase property name (i.e. "WebkitTransform" or "MozTransform" or "transform" or null if no such property is found, like if the browser is IE8 or before, "transform" won't be found at all)
\r
14482 _checkPropPrefix = function(p, e) {
\r
14483 e = e || _tempDiv;
\r
14486 if (s[p] !== undefined) {
\r
14489 p = p.charAt(0).toUpperCase() + p.substr(1);
\r
14490 a = ["O","Moz","ms","Ms","Webkit"];
\r
14492 while (--i > -1 && s[a[i]+p] === undefined) { }
\r
14494 _prefix = (i === 3) ? "ms" : a[i];
\r
14495 _prefixCSS = "-" + _prefix.toLowerCase() + "-";
\r
14496 return _prefix + p;
\r
14501 _getComputedStyle = _doc.defaultView ? _doc.defaultView.getComputedStyle : function() {},
\r
14504 * @private Returns the css style for a particular property of an element. For example, to get whatever the current "left" css value for an element with an ID of "myElement", you could do:
\r
14505 * var currentLeft = CSSPlugin.getStyle( document.getElementById("myElement"), "left");
\r
14507 * @param {!Object} t Target element whose style property you want to query
\r
14508 * @param {!string} p Property name (like "left" or "top" or "marginTop", etc.)
\r
14509 * @param {Object=} cs Computed style object. This just provides a way to speed processing if you're going to get several properties on the same element in quick succession - you can reuse the result of the getComputedStyle() call.
\r
14510 * @param {boolean=} calc If true, the value will not be read directly from the element's "style" property (if it exists there), but instead the getComputedStyle() result will be used. This can be useful when you want to ensure that the browser itself is interpreting the value.
\r
14511 * @param {string=} dflt Default value that should be returned in the place of null, "none", "auto" or "auto auto".
\r
14512 * @return {?string} The current property value
\r
14514 _getStyle = CSSPlugin.getStyle = function(t, p, cs, calc, dflt) {
\r
14516 if (!_supportsOpacity) if (p === "opacity") { //several versions of IE don't use the standard "opacity" property - they use things like filter:alpha(opacity=50), so we parse that here.
\r
14517 return _getIEOpacity(t);
\r
14519 if (!calc && t.style[p]) {
\r
14521 } else if ((cs = cs || _getComputedStyle(t))) {
\r
14522 rv = cs[p] || cs.getPropertyValue(p) || cs.getPropertyValue(p.replace(_capsExp, "-$1").toLowerCase());
\r
14523 } else if (t.currentStyle) {
\r
14524 rv = t.currentStyle[p];
\r
14526 return (dflt != null && (!rv || rv === "none" || rv === "auto" || rv === "auto auto")) ? dflt : rv;
\r
14530 * @private Pass the target element, the property name, the numeric value, and the suffix (like "%", "em", "px", etc.) and it will spit back the equivalent pixel number.
\r
14531 * @param {!Object} t Target element
\r
14532 * @param {!string} p Property name (like "left", "top", "marginLeft", etc.)
\r
14533 * @param {!number} v Value
\r
14534 * @param {string=} sfx Suffix (like "px" or "%" or "em")
\r
14535 * @param {boolean=} recurse If true, the call is a recursive one. In some browsers (like IE7/8), occasionally the value isn't accurately reported initially, but if we run the function again it will take effect.
\r
14536 * @return {number} value in pixels
\r
14538 _convertToPixels = _internals.convertToPixels = function(t, p, v, sfx, recurse) {
\r
14539 if (sfx === "px" || !sfx) { return v; }
\r
14540 if (sfx === "auto" || !v) { return 0; }
\r
14541 var horiz = _horizExp.test(p),
\r
14543 style = _tempDiv.style,
\r
14545 pix, cache, time;
\r
14549 if (sfx === "%" && p.indexOf("border") !== -1) {
\r
14550 pix = (v / 100) * (horiz ? t.clientWidth : t.clientHeight);
\r
14552 style.cssText = "border:0 solid red;position:" + _getStyle(t, "position") + ";line-height:0;";
\r
14553 if (sfx === "%" || !node.appendChild) {
\r
14554 node = t.parentNode || _doc.body;
\r
14555 cache = node._gsCache;
\r
14556 time = TweenLite.ticker.frame;
\r
14557 if (cache && horiz && cache.time === time) { //performance optimization: we record the width of elements along with the ticker frame so that we can quickly get it again on the same tick (seems relatively safe to assume it wouldn't change on the same tick)
\r
14558 return cache.width * v / 100;
\r
14560 style[(horiz ? "width" : "height")] = v + sfx;
\r
14562 style[(horiz ? "borderLeftWidth" : "borderTopWidth")] = v + sfx;
\r
14564 node.appendChild(_tempDiv);
\r
14565 pix = parseFloat(_tempDiv[(horiz ? "offsetWidth" : "offsetHeight")]);
\r
14566 node.removeChild(_tempDiv);
\r
14567 if (horiz && sfx === "%" && CSSPlugin.cacheWidths !== false) {
\r
14568 cache = node._gsCache = node._gsCache || {};
\r
14569 cache.time = time;
\r
14570 cache.width = pix / v * 100;
\r
14572 if (pix === 0 && !recurse) {
\r
14573 pix = _convertToPixels(t, p, v, sfx, true);
\r
14576 return neg ? -pix : pix;
\r
14578 _calculateOffset = _internals.calculateOffset = function(t, p, cs) { //for figuring out "top" or "left" in px when it's "auto". We need to factor in margin with the offsetLeft/offsetTop
\r
14579 if (_getStyle(t, "position", cs) !== "absolute") { return 0; }
\r
14580 var dim = ((p === "left") ? "Left" : "Top"),
\r
14581 v = _getStyle(t, "margin" + dim, cs);
\r
14582 return t["offset" + dim] - (_convertToPixels(t, p, parseFloat(v), v.replace(_suffixExp, "")) || 0);
\r
14585 // @private returns at object containing ALL of the style properties in camelCase and their associated values.
\r
14586 _getAllStyles = function(t, cs) {
\r
14589 if ((cs = cs || _getComputedStyle(t, null))) {
\r
14590 if ((i = cs.length)) {
\r
14591 while (--i > -1) {
\r
14592 s[cs[i].replace(_camelExp, _camelFunc)] = cs.getPropertyValue(cs[i]);
\r
14594 } else { //Opera behaves differently - cs.length is always 0, so we must do a for...in loop.
\r
14599 } else if ((cs = t.currentStyle || t.style)) {
\r
14601 if (typeof(i) === "string" && s[i] === undefined) {
\r
14602 s[i.replace(_camelExp, _camelFunc)] = cs[i];
\r
14606 if (!_supportsOpacity) {
\r
14607 s.opacity = _getIEOpacity(t);
\r
14609 tr = _getTransform(t, cs, false);
\r
14610 s.rotation = tr.rotation;
\r
14611 s.skewX = tr.skewX;
\r
14612 s.scaleX = tr.scaleX;
\r
14613 s.scaleY = tr.scaleY;
\r
14616 if (_supports3D) {
\r
14618 s.rotationX = tr.rotationX;
\r
14619 s.rotationY = tr.rotationY;
\r
14620 s.scaleZ = tr.scaleZ;
\r
14623 delete s.filters;
\r
14628 // @private analyzes two style objects (as returned by _getAllStyles()) and only looks for differences between them that contain tweenable values (like a number or color). It returns an object with a "difs" property which refers to an object containing only those isolated properties and values for tweening, and a "firstMPT" property which refers to the first MiniPropTween instance in a linked list that recorded all the starting values of the different properties so that we can revert to them at the end or beginning of the tween - we don't want the cascading to get messed up. The forceLookup parameter is an optional generic object with properties that should be forced into the results - this is necessary for className tweens that are overwriting others because imagine a scenario where a rollover/rollout adds/removes a class and the user swipes the mouse over the target SUPER fast, thus nothing actually changed yet and the subsequent comparison of the properties would indicate they match (especially when px rounding is taken into consideration), thus no tweening is necessary even though it SHOULD tween and remove those properties after the tween (otherwise the inline styles will contaminate things). See the className SpecialProp code for details.
\r
14629 _cssDif = function(t, s1, s2, vars, forceLookup) {
\r
14634 if (p !== "cssText") if (p !== "length") if (isNaN(p)) if (s1[p] !== (val = s2[p]) || (forceLookup && forceLookup[p])) if (p.indexOf("Origin") === -1) if (typeof(val) === "number" || typeof(val) === "string") {
\r
14635 difs[p] = (val === "auto" && (p === "left" || p === "top")) ? _calculateOffset(t, p) : ((val === "" || val === "auto" || val === "none") && typeof(s1[p]) === "string" && s1[p].replace(_NaNExp, "") !== "") ? 0 : val; //if the ending value is defaulting ("" or "auto"), we check the starting value and if it can be parsed into a number (a string which could have a suffix too, like 700px), then we swap in 0 for "" or "auto" so that things actually tween.
\r
14636 if (style[p] !== undefined) { //for className tweens, we must remember which properties already existed inline - the ones that didn't should be removed when the tween isn't in progress because they were only introduced to facilitate the transition between classes.
\r
14637 mpt = new MiniPropTween(style, p, style[p], mpt);
\r
14642 for (p in vars) { //copy properties (except className)
\r
14643 if (p !== "className") {
\r
14644 difs[p] = vars[p];
\r
14648 return {difs:difs, firstMPT:mpt};
\r
14650 _dimensions = {width:["Left","Right"], height:["Top","Bottom"]},
\r
14651 _margins = ["marginLeft","marginRight","marginTop","marginBottom"],
\r
14654 * @private Gets the width or height of an element
\r
14655 * @param {!Object} t Target element
\r
14656 * @param {!string} p Property name ("width" or "height")
\r
14657 * @param {Object=} cs Computed style object (if one exists). Just a speed optimization.
\r
14658 * @return {number} Dimension (in pixels)
\r
14660 _getDimension = function(t, p, cs) {
\r
14661 var v = parseFloat((p === "width") ? t.offsetWidth : t.offsetHeight),
\r
14662 a = _dimensions[p],
\r
14664 cs = cs || _getComputedStyle(t, null);
\r
14665 while (--i > -1) {
\r
14666 v -= parseFloat( _getStyle(t, "padding" + a[i], cs, true) ) || 0;
\r
14667 v -= parseFloat( _getStyle(t, "border" + a[i] + "Width", cs, true) ) || 0;
\r
14672 // @private Parses position-related complex strings like "top left" or "50px 10px" or "70% 20%", etc. which are used for things like transformOrigin or backgroundPosition. Optionally decorates a supplied object (recObj) with the following properties: "ox" (offsetX), "oy" (offsetY), "oxp" (if true, "ox" is a percentage not a pixel value), and "oxy" (if true, "oy" is a percentage not a pixel value)
\r
14673 _parsePosition = function(v, recObj) {
\r
14674 if (v == null || v === "" || v === "auto" || v === "auto auto") { //note: Firefox uses "auto auto" as default whereas Chrome uses "auto".
\r
14677 var a = v.split(" "),
\r
14678 x = (v.indexOf("left") !== -1) ? "0%" : (v.indexOf("right") !== -1) ? "100%" : a[0],
\r
14679 y = (v.indexOf("top") !== -1) ? "0%" : (v.indexOf("bottom") !== -1) ? "100%" : a[1];
\r
14682 } else if (y === "center") {
\r
14685 if (x === "center" || (isNaN(parseFloat(x)) && (x + "").indexOf("=") === -1)) { //remember, the user could flip-flop the values and say "bottom center" or "center bottom", etc. "center" is ambiguous because it could be used to describe horizontal or vertical, hence the isNaN(). If there's an "=" sign in the value, it's relative.
\r
14689 recObj.oxp = (x.indexOf("%") !== -1);
\r
14690 recObj.oyp = (y.indexOf("%") !== -1);
\r
14691 recObj.oxr = (x.charAt(1) === "=");
\r
14692 recObj.oyr = (y.charAt(1) === "=");
\r
14693 recObj.ox = parseFloat(x.replace(_NaNExp, ""));
\r
14694 recObj.oy = parseFloat(y.replace(_NaNExp, ""));
\r
14696 return x + " " + y + ((a.length > 2) ? " " + a[2] : "");
\r
14700 * @private Takes an ending value (typically a string, but can be a number) and a starting value and returns the change between the two, looking for relative value indicators like += and -= and it also ignores suffixes (but make sure the ending value starts with a number or +=/-= and that the starting value is a NUMBER!)
\r
14701 * @param {(number|string)} e End value which is typically a string, but could be a number
\r
14702 * @param {(number|string)} b Beginning value which is typically a string but could be a number
\r
14703 * @return {number} Amount of change between the beginning and ending values (relative values that have a "+=" or "-=" are recognized)
\r
14705 _parseChange = function(e, b) {
\r
14706 return (typeof(e) === "string" && e.charAt(1) === "=") ? parseInt(e.charAt(0) + "1", 10) * parseFloat(e.substr(2)) : parseFloat(e) - parseFloat(b);
\r
14710 * @private Takes a value and a default number, checks if the value is relative, null, or numeric and spits back a normalized number accordingly. Primarily used in the _parseTransform() function.
\r
14711 * @param {Object} v Value to be parsed
\r
14712 * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter)
\r
14713 * @return {number} Parsed value
\r
14715 _parseVal = function(v, d) {
\r
14716 return (v == null) ? d : (typeof(v) === "string" && v.charAt(1) === "=") ? parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) + d : parseFloat(v);
\r
14720 * @private Translates strings like "40deg" or "40" or 40rad" or "+=40deg" or "270_short" or "-90_cw" or "+=45_ccw" to a numeric radian angle. Of course a starting/default value must be fed in too so that relative values can be calculated properly.
\r
14721 * @param {Object} v Value to be parsed
\r
14722 * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter)
\r
14723 * @param {string=} p property name for directionalEnd (optional - only used when the parsed value is directional ("_short", "_cw", or "_ccw" suffix). We need a way to store the uncompensated value so that at the end of the tween, we set it to exactly what was requested with no directional compensation). Property name would be "rotation", "rotationX", or "rotationY"
\r
14724 * @param {Object=} directionalEnd An object that will store the raw end values for directional angles ("_short", "_cw", or "_ccw" suffix). We need a way to store the uncompensated value so that at the end of the tween, we set it to exactly what was requested with no directional compensation.
\r
14725 * @return {number} parsed angle in radians
\r
14727 _parseAngle = function(v, d, p, directionalEnd) {
\r
14728 var min = 0.000001,
\r
14729 cap, split, dif, result;
\r
14732 } else if (typeof(v) === "number") {
\r
14736 split = v.split("_");
\r
14737 dif = Number(split[0].replace(_NaNExp, "")) * ((v.indexOf("rad") === -1) ? 1 : _RAD2DEG) - ((v.charAt(1) === "=") ? 0 : d);
\r
14738 if (split.length) {
\r
14739 if (directionalEnd) {
\r
14740 directionalEnd[p] = d + dif;
\r
14742 if (v.indexOf("short") !== -1) {
\r
14744 if (dif !== dif % (cap / 2)) {
\r
14745 dif = (dif < 0) ? dif + cap : dif - cap;
\r
14748 if (v.indexOf("_cw") !== -1 && dif < 0) {
\r
14749 dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
\r
14750 } else if (v.indexOf("ccw") !== -1 && dif > 0) {
\r
14751 dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
\r
14754 result = d + dif;
\r
14756 if (result < min && result > -min) {
\r
14762 _colorLookup = {aqua:[0,255,255],
\r
14764 silver:[192,192,192],
\r
14766 maroon:[128,0,0],
\r
14767 teal:[0,128,128],
\r
14770 white:[255,255,255],
\r
14771 fuchsia:[255,0,255],
\r
14772 olive:[128,128,0],
\r
14773 yellow:[255,255,0],
\r
14774 orange:[255,165,0],
\r
14775 gray:[128,128,128],
\r
14776 purple:[128,0,128],
\r
14779 pink:[255,192,203],
\r
14780 cyan:[0,255,255],
\r
14781 transparent:[255,255,255,0]},
\r
14783 _hue = function(h, m1, m2) {
\r
14784 h = (h < 0) ? h + 1 : (h > 1) ? h - 1 : h;
\r
14785 return ((((h * 6 < 1) ? m1 + (m2 - m1) * h * 6 : (h < 0.5) ? m2 : (h * 3 < 2) ? m1 + (m2 - m1) * (2 / 3 - h) * 6 : m1) * 255) + 0.5) | 0;
\r
14789 * @private Parses a color (like #9F0, #FF9900, or rgb(255,51,153)) into an array with 3 elements for red, green, and blue. Also handles rgba() values (splits into array of 4 elements of course)
\r
14790 * @param {(string|number)} v The value the should be parsed which could be a string like #9F0 or rgb(255,102,51) or rgba(255,0,0,0.5) or it could be a number like 0xFF00CC or even a named color like red, blue, purple, etc.
\r
14791 * @return {Array.<number>} An array containing red, green, and blue (and optionally alpha) in that order.
\r
14793 _parseColor = function(v) {
\r
14794 var c1, c2, c3, h, s, l;
\r
14795 if (!v || v === "") {
\r
14796 return _colorLookup.black;
\r
14798 if (typeof(v) === "number") {
\r
14799 return [v >> 16, (v >> 8) & 255, v & 255];
\r
14801 if (v.charAt(v.length - 1) === ",") { //sometimes a trailing commma is included and we should chop it off (typically from a comma-delimited list of values like a textShadow:"2px 2px 2px blue, 5px 5px 5px rgb(255,0,0)" - in this example "blue," has a trailing comma. We could strip it out inside parseComplex() but we'd need to do it to the beginning and ending values plus it wouldn't provide protection from other potential scenarios like if the user passes in a similar value.
\r
14802 v = v.substr(0, v.length - 1);
\r
14804 if (_colorLookup[v]) {
\r
14805 return _colorLookup[v];
\r
14807 if (v.charAt(0) === "#") {
\r
14808 if (v.length === 4) { //for shorthand like #9F0
\r
14809 c1 = v.charAt(1),
\r
14810 c2 = v.charAt(2),
\r
14811 c3 = v.charAt(3);
\r
14812 v = "#" + c1 + c1 + c2 + c2 + c3 + c3;
\r
14814 v = parseInt(v.substr(1), 16);
\r
14815 return [v >> 16, (v >> 8) & 255, v & 255];
\r
14817 if (v.substr(0, 3) === "hsl") {
\r
14818 v = v.match(_numExp);
\r
14819 h = (Number(v[0]) % 360) / 360;
\r
14820 s = Number(v[1]) / 100;
\r
14821 l = Number(v[2]) / 100;
\r
14822 c2 = (l <= 0.5) ? l * (s + 1) : l + s - l * s;
\r
14824 if (v.length > 3) {
\r
14825 v[3] = Number(v[3]);
\r
14827 v[0] = _hue(h + 1 / 3, c1, c2);
\r
14828 v[1] = _hue(h, c1, c2);
\r
14829 v[2] = _hue(h - 1 / 3, c1, c2);
\r
14832 v = v.match(_numExp) || _colorLookup.transparent;
\r
14833 v[0] = Number(v[0]);
\r
14834 v[1] = Number(v[1]);
\r
14835 v[2] = Number(v[2]);
\r
14836 if (v.length > 3) {
\r
14837 v[3] = Number(v[3]);
\r
14841 _colorExp = "(?:\\b(?:(?:rgb|rgba|hsl|hsla)\\(.+?\\))|\\B#.+?\\b"; //we'll dynamically build this Regular Expression to conserve file size. After building it, it will be able to find rgb(), rgba(), # (hexadecimal), and named color values like red, blue, purple, etc.
\r
14843 for (p in _colorLookup) {
\r
14844 _colorExp += "|" + p + "\\b";
\r
14846 _colorExp = new RegExp(_colorExp+")", "gi");
\r
14849 * @private Returns a formatter function that handles taking a string (or number in some cases) and returning a consistently formatted one in terms of delimiters, quantity of values, etc. For example, we may get boxShadow values defined as "0px red" or "0px 0px 10px rgb(255,0,0)" or "0px 0px 20px 20px #F00" and we need to ensure that what we get back is described with 4 numbers and a color. This allows us to feed it into the _parseComplex() method and split the values up appropriately. The neat thing about this _getFormatter() function is that the dflt defines a pattern as well as a default, so for example, _getFormatter("0px 0px 0px 0px #777", true) not only sets the default as 0px for all distances and #777 for the color, but also sets the pattern such that 4 numbers and a color will always get returned.
\r
14850 * @param {!string} dflt The default value and pattern to follow. So "0px 0px 0px 0px #777" will ensure that 4 numbers and a color will always get returned.
\r
14851 * @param {boolean=} clr If true, the values should be searched for color-related data. For example, boxShadow values typically contain a color whereas borderRadius don't.
\r
14852 * @param {boolean=} collapsible If true, the value is a top/left/right/bottom style one that acts like margin or padding, where if only one value is received, it's used for all 4; if 2 are received, the first is duplicated for 3rd (bottom) and the 2nd is duplicated for the 4th spot (left), etc.
\r
14853 * @return {Function} formatter function
\r
14855 var _getFormatter = function(dflt, clr, collapsible, multi) {
\r
14856 if (dflt == null) {
\r
14857 return function(v) {return v;};
\r
14859 var dColor = clr ? (dflt.match(_colorExp) || [""])[0] : "",
\r
14860 dVals = dflt.split(dColor).join("").match(_valuesExp) || [],
\r
14861 pfx = dflt.substr(0, dflt.indexOf(dVals[0])),
\r
14862 sfx = (dflt.charAt(dflt.length - 1) === ")") ? ")" : "",
\r
14863 delim = (dflt.indexOf(" ") !== -1) ? " " : ",",
\r
14864 numVals = dVals.length,
\r
14865 dSfx = (numVals > 0) ? dVals[0].replace(_numExp, "") : "",
\r
14868 return function(v) {return v;};
\r
14871 formatter = function(v) {
\r
14872 var color, vals, i, a;
\r
14873 if (typeof(v) === "number") {
\r
14875 } else if (multi && _commasOutsideParenExp.test(v)) {
\r
14876 a = v.replace(_commasOutsideParenExp, "|").split("|");
\r
14877 for (i = 0; i < a.length; i++) {
\r
14878 a[i] = formatter(a[i]);
\r
14880 return a.join(",");
\r
14882 color = (v.match(_colorExp) || [dColor])[0];
\r
14883 vals = v.split(color).join("").match(_valuesExp) || [];
\r
14885 if (numVals > i--) {
\r
14886 while (++i < numVals) {
\r
14887 vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i];
\r
14890 return pfx + vals.join(delim) + delim + color + sfx + (v.indexOf("inset") !== -1 ? " inset" : "");
\r
14892 return formatter;
\r
14895 formatter = function(v) {
\r
14897 if (typeof(v) === "number") {
\r
14899 } else if (multi && _commasOutsideParenExp.test(v)) {
\r
14900 a = v.replace(_commasOutsideParenExp, "|").split("|");
\r
14901 for (i = 0; i < a.length; i++) {
\r
14902 a[i] = formatter(a[i]);
\r
14904 return a.join(",");
\r
14906 vals = v.match(_valuesExp) || [];
\r
14908 if (numVals > i--) {
\r
14909 while (++i < numVals) {
\r
14910 vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i];
\r
14913 return pfx + vals.join(delim) + sfx;
\r
14915 return formatter;
\r
14919 * @private returns a formatter function that's used for edge-related values like marginTop, marginLeft, paddingBottom, paddingRight, etc. Just pass a comma-delimited list of property names related to the edges.
\r
14920 * @param {!string} props a comma-delimited list of property names in order from top to left, like "marginTop,marginRight,marginBottom,marginLeft"
\r
14921 * @return {Function} a formatter function
\r
14923 _getEdgeParser = function(props) {
\r
14924 props = props.split(",");
\r
14925 return function(t, e, p, cssp, pt, plugin, vars) {
\r
14926 var a = (e + "").split(" "),
\r
14929 for (i = 0; i < 4; i++) {
\r
14930 vars[props[i]] = a[i] = a[i] || a[(((i - 1) / 2) >> 0)];
\r
14932 return cssp.parse(t, vars, pt, plugin);
\r
14936 // @private used when other plugins must tween values first, like BezierPlugin or ThrowPropsPlugin, etc. That plugin's setRatio() gets called first so that the values are updated, and then we loop through the MiniPropTweens which handle copying the values into their appropriate slots so that they can then be applied correctly in the main CSSPlugin setRatio() method. Remember, we typically create a proxy object that has a bunch of uniquely-named properties that we feed to the sub-plugin and it does its magic normally, and then we must interpret those values and apply them to the css because often numbers must get combined/concatenated, suffixes added, etc. to work with css, like boxShadow could have 4 values plus a color.
\r
14937 _setPluginRatio = _internals._setPluginRatio = function(v) {
\r
14938 this.plugin.setRatio(v);
\r
14939 var d = this.data,
\r
14941 mpt = d.firstMPT,
\r
14945 val = proxy[mpt.v];
\r
14947 val = Math.round(val);
\r
14948 } else if (val < min && val > -min) {
\r
14951 mpt.t[mpt.p] = val;
\r
14954 if (d.autoRotate) {
\r
14955 d.autoRotate.rotation = proxy.rotation;
\r
14957 //at the end, we must set the CSSPropTween's "e" (end) value dynamically here because that's what is used in the final setRatio() method.
\r
14959 mpt = d.firstMPT;
\r
14963 pt.e = pt.s + pt.xs0;
\r
14964 } else if (pt.type === 1) {
\r
14965 str = pt.xs0 + pt.s + pt.xs1;
\r
14966 for (i = 1; i < pt.l; i++) {
\r
14967 str += pt["xn"+i] + pt["xs"+(i+1)];
\r
14977 * @private @constructor Used by a few SpecialProps to hold important values for proxies. For example, _parseToProxy() creates a MiniPropTween instance for each property that must get tweened on the proxy, and we record the original property name as well as the unique one we create for the proxy, plus whether or not the value needs to be rounded plus the original value.
\r
14978 * @param {!Object} t target object whose property we're tweening (often a CSSPropTween)
\r
14979 * @param {!string} p property name
\r
14980 * @param {(number|string|object)} v value
\r
14981 * @param {MiniPropTween=} next next MiniPropTween in the linked list
\r
14982 * @param {boolean=} r if true, the tweened value should be rounded to the nearest integer
\r
14984 MiniPropTween = function(t, p, v, next, r) {
\r
14990 next._prev = this;
\r
14991 this._next = next;
\r
14996 * @private Most other plugins (like BezierPlugin and ThrowPropsPlugin and others) can only tween numeric values, but CSSPlugin must accommodate special values that have a bunch of extra data (like a suffix or strings between numeric values, etc.). For example, boxShadow has values like "10px 10px 20px 30px rgb(255,0,0)" which would utterly confuse other plugins. This method allows us to split that data apart and grab only the numeric data and attach it to uniquely-named properties of a generic proxy object ({}) so that we can feed that to virtually any plugin to have the numbers tweened. However, we must also keep track of which properties from the proxy go with which CSSPropTween values and instances. So we create a linked list of MiniPropTweens. Each one records a target (the original CSSPropTween), property (like "s" or "xn1" or "xn2") that we're tweening and the unique property name that was used for the proxy (like "boxShadow_xn1" and "boxShadow_xn2") and whether or not they need to be rounded. That way, in the _setPluginRatio() method we can simply copy the values over from the proxy to the CSSPropTween instance(s). Then, when the main CSSPlugin setRatio() method runs and applies the CSSPropTween values accordingly, they're updated nicely. So the external plugin tweens the numbers, _setPluginRatio() copies them over, and setRatio() acts normally, applying css-specific values to the element.
\r
14997 * This method returns an object that has the following properties:
\r
14998 * - proxy: a generic object containing the starting values for all the properties that will be tweened by the external plugin. This is what we feed to the external _onInitTween() as the target
\r
14999 * - end: a generic object containing the ending values for all the properties that will be tweened by the external plugin. This is what we feed to the external plugin's _onInitTween() as the destination values
\r
15000 * - firstMPT: the first MiniPropTween in the linked list
\r
15001 * - pt: the first CSSPropTween in the linked list that was created when parsing. If shallow is true, this linked list will NOT attach to the one passed into the _parseToProxy() as the "pt" (4th) parameter.
\r
15002 * @param {!Object} t target object to be tweened
\r
15003 * @param {!(Object|string)} vars the object containing the information about the tweening values (typically the end/destination values) that should be parsed
\r
15004 * @param {!CSSPlugin} cssp The CSSPlugin instance
\r
15005 * @param {CSSPropTween=} pt the next CSSPropTween in the linked list
\r
15006 * @param {TweenPlugin=} plugin the external TweenPlugin instance that will be handling tweening the numeric values
\r
15007 * @param {boolean=} shallow if true, the resulting linked list from the parse will NOT be attached to the CSSPropTween that was passed in as the "pt" (4th) parameter.
\r
15008 * @return An object containing the following properties: proxy, end, firstMPT, and pt (see above for descriptions)
\r
15010 _parseToProxy = _internals._parseToProxy = function(t, vars, cssp, pt, plugin, shallow) {
\r
15014 transform = cssp._transform,
\r
15015 oldForce = _forcePT,
\r
15016 i, p, xp, mpt, firstPT;
\r
15017 cssp._transform = null;
\r
15019 pt = firstPT = cssp.parse(t, vars, pt, plugin);
\r
15020 _forcePT = oldForce;
\r
15021 //break off from the linked list so the new ones are isolated.
\r
15023 cssp._transform = transform;
\r
15025 bpt._prev = null;
\r
15027 bpt._prev._next = null;
\r
15031 while (pt && pt !== bpt) {
\r
15032 if (pt.type <= 1) {
\r
15034 end[p] = pt.s + pt.c;
\r
15037 mpt = new MiniPropTween(pt, "s", p, mpt, pt.r);
\r
15040 if (pt.type === 1) {
\r
15042 while (--i > 0) {
\r
15044 p = pt.p + "_" + xp;
\r
15045 end[p] = pt.data[xp];
\r
15046 start[p] = pt[xp];
\r
15048 mpt = new MiniPropTween(pt, xp, p, mpt, pt.rxp[xp]);
\r
15055 return {proxy:start, end:end, firstMPT:mpt, pt:firstPT};
\r
15061 * @constructor Each property that is tweened has at least one CSSPropTween associated with it. These instances store important information like the target, property, starting value, amount of change, etc. They can also optionally have a number of "extra" strings and numeric values named xs1, xn1, xs2, xn2, xs3, xn3, etc. where "s" indicates string and "n" indicates number. These can be pieced together in a complex-value tween (type:1) that has alternating types of data like a string, number, string, number, etc. For example, boxShadow could be "5px 5px 8px rgb(102, 102, 51)". In that value, there are 6 numbers that may need to tween and then pieced back together into a string again with spaces, suffixes, etc. xs0 is special in that it stores the suffix for standard (type:0) tweens, -OR- the first string (prefix) in a complex-value (type:1) CSSPropTween -OR- it can be the non-tweening value in a type:-1 CSSPropTween. We do this to conserve memory.
\r
15062 * CSSPropTweens have the following optional properties as well (not defined through the constructor):
\r
15063 * - l: Length in terms of the number of extra properties that the CSSPropTween has (default: 0). For example, for a boxShadow we may need to tween 5 numbers in which case l would be 5; Keep in mind that the start/end values for the first number that's tweened are always stored in the s and c properties to conserve memory. All additional values thereafter are stored in xn1, xn2, etc.
\r
15064 * - xfirst: The first instance of any sub-CSSPropTweens that are tweening properties of this instance. For example, we may split up a boxShadow tween so that there's a main CSSPropTween of type:1 that has various xs* and xn* values associated with the h-shadow, v-shadow, blur, color, etc. Then we spawn a CSSPropTween for each of those that has a higher priority and runs BEFORE the main CSSPropTween so that the values are all set by the time it needs to re-assemble them. The xfirst gives us an easy way to identify the first one in that chain which typically ends at the main one (because they're all prepende to the linked list)
\r
15065 * - plugin: The TweenPlugin instance that will handle the tweening of any complex values. For example, sometimes we don't want to use normal subtweens (like xfirst refers to) to tween the values - we might want ThrowPropsPlugin or BezierPlugin some other plugin to do the actual tweening, so we create a plugin instance and store a reference here. We need this reference so that if we get a request to round values or disable a tween, we can pass along that request.
\r
15066 * - data: Arbitrary data that needs to be stored with the CSSPropTween. Typically if we're going to have a plugin handle the tweening of a complex-value tween, we create a generic object that stores the END values that we're tweening to and the CSSPropTween's xs1, xs2, etc. have the starting values. We store that object as data. That way, we can simply pass that object to the plugin and use the CSSPropTween as the target.
\r
15067 * - setRatio: Only used for type:2 tweens that require custom functionality. In this case, we call the CSSPropTween's setRatio() method and pass the ratio each time the tween updates. This isn't quite as efficient as doing things directly in the CSSPlugin's setRatio() method, but it's very convenient and flexible.
\r
15068 * @param {!Object} t Target object whose property will be tweened. Often a DOM element, but not always. It could be anything.
\r
15069 * @param {string} p Property to tween (name). For example, to tween element.width, p would be "width".
\r
15070 * @param {number} s Starting numeric value
\r
15071 * @param {number} c Change in numeric value over the course of the entire tween. For example, if element.width starts at 5 and should end at 100, c would be 95.
\r
15072 * @param {CSSPropTween=} next The next CSSPropTween in the linked list. If one is defined, we will define its _prev as the new instance, and the new instance's _next will be pointed at it.
\r
15073 * @param {number=} type The type of CSSPropTween where -1 = a non-tweening value, 0 = a standard simple tween, 1 = a complex value (like one that has multiple numbers in a comma- or space-delimited string like border:"1px solid red"), and 2 = one that uses a custom setRatio function that does all of the work of applying the values on each update.
\r
15074 * @param {string=} n Name of the property that should be used for overwriting purposes which is typically the same as p but not always. For example, we may need to create a subtween for the 2nd part of a "clip:rect(...)" tween in which case "p" might be xs1 but "n" is still "clip"
\r
15075 * @param {boolean=} r If true, the value(s) should be rounded
\r
15076 * @param {number=} pr Priority in the linked list order. Higher priority CSSPropTweens will be updated before lower priority ones. The default priority is 0.
\r
15077 * @param {string=} b Beginning value. We store this to ensure that it is EXACTLY what it was when the tween began without any risk of interpretation issues.
\r
15078 * @param {string=} e Ending value. We store this to ensure that it is EXACTLY what the user defined at the end of the tween without any risk of interpretation issues.
\r
15080 CSSPropTween = _internals.CSSPropTween = function(t, p, s, c, next, type, n, r, pr, b, e) {
\r
15081 this.t = t; //target
\r
15082 this.p = p; //property
\r
15083 this.s = s; //starting value
\r
15084 this.c = c; //change value
\r
15085 this.n = n || p; //name that this CSSPropTween should be associated to (usually the same as p, but not always - n is what overwriting looks at)
\r
15086 if (!(t instanceof CSSPropTween)) {
\r
15087 _overwriteProps.push(this.n);
\r
15089 this.r = r; //round (boolean)
\r
15090 this.type = type || 0; //0 = normal tween, -1 = non-tweening (in which case xs0 will be applied to the target's property, like tp.t[tp.p] = tp.xs0), 1 = complex-value SpecialProp, 2 = custom setRatio() that does all the work
\r
15093 _hasPriority = true;
\r
15095 this.b = (b === undefined) ? s : b;
\r
15096 this.e = (e === undefined) ? s + c : e;
\r
15098 this._next = next;
\r
15099 next._prev = this;
\r
15104 * Takes a target, the beginning value and ending value (as strings) and parses them into a CSSPropTween (possibly with child CSSPropTweens) that accommodates multiple numbers, colors, comma-delimited values, etc. For example:
\r
15105 * sp.parseComplex(element, "boxShadow", "5px 10px 20px rgb(255,102,51)", "0px 0px 0px red", true, "0px 0px 0px rgb(0,0,0,0)", pt);
\r
15106 * It will walk through the beginning and ending values (which should be in the same format with the same number and type of values) and figure out which parts are numbers, what strings separate the numeric/tweenable values, and then create the CSSPropTweens accordingly. If a plugin is defined, no child CSSPropTweens will be created. Instead, the ending values will be stored in the "data" property of the returned CSSPropTween like: {s:-5, xn1:-10, xn2:-20, xn3:255, xn4:0, xn5:0} so that it can be fed to any other plugin and it'll be plain numeric tweens but the recomposition of the complex value will be handled inside CSSPlugin's setRatio().
\r
15107 * If a setRatio is defined, the type of the CSSPropTween will be set to 2 and recomposition of the values will be the responsibility of that method.
\r
15109 * @param {!Object} t Target whose property will be tweened
\r
15110 * @param {!string} p Property that will be tweened (its name, like "left" or "backgroundColor" or "boxShadow")
\r
15111 * @param {string} b Beginning value
\r
15112 * @param {string} e Ending value
\r
15113 * @param {boolean} clrs If true, the value could contain a color value like "rgb(255,0,0)" or "#F00" or "red". The default is false, so no colors will be recognized (a performance optimization)
\r
15114 * @param {(string|number|Object)} dflt The default beginning value that should be used if no valid beginning value is defined or if the number of values inside the complex beginning and ending values don't match
\r
15115 * @param {?CSSPropTween} pt CSSPropTween instance that is the current head of the linked list (we'll prepend to this).
\r
15116 * @param {number=} pr Priority in the linked list order. Higher priority properties will be updated before lower priority ones. The default priority is 0.
\r
15117 * @param {TweenPlugin=} plugin If a plugin should handle the tweening of extra properties, pass the plugin instance here. If one is defined, then NO subtweens will be created for any extra properties (the properties will be created - just not additional CSSPropTween instances to tween them) because the plugin is expected to do so. However, the end values WILL be populated in the "data" property, like {s:100, xn1:50, xn2:300}
\r
15118 * @param {function(number)=} setRatio If values should be set in a custom function instead of being pieced together in a type:1 (complex-value) CSSPropTween, define that custom function here.
\r
15119 * @return {CSSPropTween} The first CSSPropTween in the linked list which includes the new one(s) added by the parseComplex() call.
\r
15121 _parseComplex = CSSPlugin.parseComplex = function(t, p, b, e, clrs, dflt, pt, pr, plugin, setRatio) {
\r
15122 //DEBUG: _log("parseComplex: "+p+", b: "+b+", e: "+e);
\r
15123 b = b || dflt || "";
\r
15124 pt = new CSSPropTween(t, p, 0, 0, pt, (setRatio ? 2 : 1), null, false, pr, b, e);
\r
15125 e += ""; //ensures it's a string
\r
15126 var ba = b.split(", ").join(",").split(" "), //beginning array
\r
15127 ea = e.split(", ").join(",").split(" "), //ending array
\r
15129 autoRound = (_autoRound !== false),
\r
15130 i, xi, ni, bv, ev, bnums, enums, bn, rgba, temp, cv, str;
\r
15131 if (e.indexOf(",") !== -1 || b.indexOf(",") !== -1) {
\r
15132 ba = ba.join(" ").replace(_commasOutsideParenExp, ", ").split(" ");
\r
15133 ea = ea.join(" ").replace(_commasOutsideParenExp, ", ").split(" ");
\r
15136 if (l !== ea.length) {
\r
15137 //DEBUG: _log("mismatched formatting detected on " + p + " (" + b + " vs " + e + ")");
\r
15138 ba = (dflt || "").split(" ");
\r
15141 pt.plugin = plugin;
\r
15142 pt.setRatio = setRatio;
\r
15143 for (i = 0; i < l; i++) {
\r
15146 bn = parseFloat(bv);
\r
15148 //if the value begins with a number (most common). It's fine if it has a suffix like px
\r
15149 if (bn || bn === 0) {
\r
15150 pt.appendXtra("", bn, _parseChange(ev, bn), ev.replace(_relNumExp, ""), (autoRound && ev.indexOf("px") !== -1), true);
\r
15152 //if the value is a color
\r
15153 } else if (clrs && (bv.charAt(0) === "#" || _colorLookup[bv] || _rgbhslExp.test(bv))) {
\r
15154 str = ev.charAt(ev.length - 1) === "," ? ")," : ")"; //if there's a comma at the end, retain it.
\r
15155 bv = _parseColor(bv);
\r
15156 ev = _parseColor(ev);
\r
15157 rgba = (bv.length + ev.length > 6);
\r
15158 if (rgba && !_supportsOpacity && ev[3] === 0) { //older versions of IE don't support rgba(), so if the destination alpha is 0, just use "transparent" for the end color
\r
15159 pt["xs" + pt.l] += pt.l ? " transparent" : "transparent";
\r
15160 pt.e = pt.e.split(ea[i]).join("transparent");
\r
15162 if (!_supportsOpacity) { //old versions of IE don't support rgba().
\r
15165 pt.appendXtra((rgba ? "rgba(" : "rgb("), bv[0], ev[0] - bv[0], ",", true, true)
\r
15166 .appendXtra("", bv[1], ev[1] - bv[1], ",", true)
\r
15167 .appendXtra("", bv[2], ev[2] - bv[2], (rgba ? "," : str), true);
\r
15169 bv = (bv.length < 4) ? 1 : bv[3];
\r
15170 pt.appendXtra("", bv, ((ev.length < 4) ? 1 : ev[3]) - bv, str, false);
\r
15175 bnums = bv.match(_numExp); //gets each group of numbers in the beginning value string and drops them into an array
\r
15177 //if no number is found, treat it as a non-tweening value and just append the string to the current xs.
\r
15179 pt["xs" + pt.l] += pt.l ? " " + bv : bv;
\r
15181 //loop through all the numbers that are found and construct the extra values on the pt.
\r
15183 enums = ev.match(_relNumExp); //get each group of numbers in the end value string and drop them into an array. We allow relative values too, like +=50 or -=.5
\r
15184 if (!enums || enums.length !== bnums.length) {
\r
15185 //DEBUG: _log("mismatched formatting detected on " + p + " (" + b + " vs " + e + ")");
\r
15189 for (xi = 0; xi < bnums.length; xi++) {
\r
15191 temp = bv.indexOf(cv, ni);
\r
15192 pt.appendXtra(bv.substr(ni, temp - ni), Number(cv), _parseChange(enums[xi], cv), "", (autoRound && bv.substr(temp + cv.length, 2) === "px"), (xi === 0));
\r
15193 ni = temp + cv.length;
\r
15195 pt["xs" + pt.l] += bv.substr(ni);
\r
15199 //if there are relative values ("+=" or "-=" prefix), we need to adjust the ending value to eliminate the prefixes and combine the values properly.
\r
15200 if (e.indexOf("=") !== -1) if (pt.data) {
\r
15201 str = pt.xs0 + pt.data.s;
\r
15202 for (i = 1; i < pt.l; i++) {
\r
15203 str += pt["xs" + i] + pt.data["xn" + i];
\r
15205 pt.e = str + pt["xs" + i];
\r
15211 return pt.xfirst || pt;
\r
15216 p = CSSPropTween.prototype;
\r
15217 p.l = p.pr = 0; //length (number of extra properties like xn1, xn2, xn3, etc.
\r
15218 while (--i > 0) {
\r
15220 p["xs" + i] = "";
\r
15223 p._next = p._prev = p.xfirst = p.data = p.plugin = p.setRatio = p.rxp = null;
\r
15227 * Appends and extra tweening value to a CSSPropTween and automatically manages any prefix and suffix strings. The first extra value is stored in the s and c of the main CSSPropTween instance, but thereafter any extras are stored in the xn1, xn2, xn3, etc. The prefixes and suffixes are stored in the xs0, xs1, xs2, etc. properties. For example, if I walk through a clip value like "rect(10px, 5px, 0px, 20px)", the values would be stored like this:
\r
15228 * xs0:"rect(", s:10, xs1:"px, ", xn1:5, xs2:"px, ", xn2:0, xs3:"px, ", xn3:20, xn4:"px)"
\r
15229 * And they'd all get joined together when the CSSPlugin renders (in the setRatio() method).
\r
15230 * @param {string=} pfx Prefix (if any)
\r
15231 * @param {!number} s Starting value
\r
15232 * @param {!number} c Change in numeric value over the course of the entire tween. For example, if the start is 5 and the end is 100, the change would be 95.
\r
15233 * @param {string=} sfx Suffix (if any)
\r
15234 * @param {boolean=} r Round (if true).
\r
15235 * @param {boolean=} pad If true, this extra value should be separated by the previous one by a space. If there is no previous extra and pad is true, it will automatically drop the space.
\r
15236 * @return {CSSPropTween} returns itself so that multiple methods can be chained together.
\r
15238 p.appendXtra = function(pfx, s, c, sfx, r, pad) {
\r
15241 pt["xs" + l] += (pad && l) ? " " + pfx : pfx || "";
\r
15242 if (!c) if (l !== 0 && !pt.plugin) { //typically we'll combine non-changing values right into the xs to optimize performance, but we don't combine them when there's a plugin that will be tweening the values because it may depend on the values being split apart, like for a bezier, if a value doesn't change between the first and second iteration but then it does on the 3rd, we'll run into trouble because there's no xn slot for that value!
\r
15243 pt["xs" + l] += s + (sfx || "");
\r
15247 pt.type = pt.setRatio ? 2 : 1;
\r
15248 pt["xs" + pt.l] = sfx || "";
\r
15250 pt.data["xn" + l] = s + c;
\r
15251 pt.rxp["xn" + l] = r; //round extra property (we need to tap into this in the _parseToProxy() method)
\r
15252 pt["xn" + l] = s;
\r
15253 if (!pt.plugin) {
\r
15254 pt.xfirst = new CSSPropTween(pt, "xn" + l, s, c, pt.xfirst || pt, 0, pt.n, r, pt.pr);
\r
15255 pt.xfirst.xs0 = 0; //just to ensure that the property stays numeric which helps modern browsers speed up processing. Remember, in the setRatio() method, we do pt.t[pt.p] = val + pt.xs0 so if pt.xs0 is "" (the default), it'll cast the end value as a string. When a property is a number sometimes and a string sometimes, it prevents the compiler from locking in the data type, slowing things down slightly.
\r
15259 pt.data = {s:s + c};
\r
15268 * @constructor A SpecialProp is basically a css property that needs to be treated in a non-standard way, like if it may contain a complex value like boxShadow:"5px 10px 15px rgb(255, 102, 51)" or if it is associated with another plugin like ThrowPropsPlugin or BezierPlugin. Every SpecialProp is associated with a particular property name like "boxShadow" or "throwProps" or "bezier" and it will intercept those values in the vars object that's passed to the CSSPlugin and handle them accordingly.
\r
15269 * @param {!string} p Property name (like "boxShadow" or "throwProps")
\r
15270 * @param {Object=} options An object containing any of the following configuration options:
\r
15271 * - defaultValue: the default value
\r
15272 * - parser: A function that should be called when the associated property name is found in the vars. This function should return a CSSPropTween instance and it should ensure that it is properly inserted into the linked list. It will receive 4 paramters: 1) The target, 2) The value defined in the vars, 3) The CSSPlugin instance (whose _firstPT should be used for the linked list), and 4) A computed style object if one was calculated (this is a speed optimization that allows retrieval of starting values quicker)
\r
15273 * - formatter: a function that formats any value received for this special property (for example, boxShadow could take "5px 5px red" and format it to "5px 5px 0px 0px red" so that both the beginning and ending values have a common order and quantity of values.)
\r
15274 * - prefix: if true, we'll determine whether or not this property requires a vendor prefix (like Webkit or Moz or ms or O)
\r
15275 * - color: set this to true if the value for this SpecialProp may contain color-related values like rgb(), rgba(), etc.
\r
15276 * - priority: priority in the linked list order. Higher priority SpecialProps will be updated before lower priority ones. The default priority is 0.
\r
15277 * - multi: if true, the formatter should accommodate a comma-delimited list of values, like boxShadow could have multiple boxShadows listed out.
\r
15278 * - collapsible: if true, the formatter should treat the value like it's a top/right/bottom/left value that could be collapsed, like "5px" would apply to all, "5px, 10px" would use 5px for top/bottom and 10px for right/left, etc.
\r
15279 * - keyword: a special keyword that can [optionally] be found inside the value (like "inset" for boxShadow). This allows us to validate beginning/ending values to make sure they match (if the keyword is found in one, it'll be added to the other for consistency by default).
\r
15281 var SpecialProp = function(p, options) {
\r
15282 options = options || {};
\r
15283 this.p = options.prefix ? _checkPropPrefix(p) || p : p;
\r
15284 _specialProps[p] = _specialProps[this.p] = this;
\r
15285 this.format = options.formatter || _getFormatter(options.defaultValue, options.color, options.collapsible, options.multi);
\r
15286 if (options.parser) {
\r
15287 this.parse = options.parser;
\r
15289 this.clrs = options.color;
\r
15290 this.multi = options.multi;
\r
15291 this.keyword = options.keyword;
\r
15292 this.dflt = options.defaultValue;
\r
15293 this.pr = options.priority || 0;
\r
15296 //shortcut for creating a new SpecialProp that can accept multiple properties as a comma-delimited list (helps minification). dflt can be an array for multiple values (we don't do a comma-delimited list because the default value may contain commas, like rect(0px,0px,0px,0px)). We attach this method to the SpecialProp class/object instead of using a private _createSpecialProp() method so that we can tap into it externally if necessary, like from another plugin.
\r
15297 _registerComplexSpecialProp = _internals._registerComplexSpecialProp = function(p, options, defaults) {
\r
15298 if (typeof(options) !== "object") {
\r
15299 options = {parser:defaults}; //to make backwards compatible with older versions of BezierPlugin and ThrowPropsPlugin
\r
15301 var a = p.split(","),
\r
15302 d = options.defaultValue,
\r
15304 defaults = defaults || [d];
\r
15305 for (i = 0; i < a.length; i++) {
\r
15306 options.prefix = (i === 0 && options.prefix);
\r
15307 options.defaultValue = defaults[i] || d;
\r
15308 temp = new SpecialProp(a[i], options);
\r
15312 //creates a placeholder special prop for a plugin so that the property gets caught the first time a tween of it is attempted, and at that time it makes the plugin register itself, thus taking over for all future tweens of that property. This allows us to not mandate that things load in a particular order and it also allows us to log() an error that informs the user when they attempt to tween an external plugin-related property without loading its .js file.
\r
15313 _registerPluginProp = function(p) {
\r
15314 if (!_specialProps[p]) {
\r
15315 var pluginName = p.charAt(0).toUpperCase() + p.substr(1) + "Plugin";
\r
15316 _registerComplexSpecialProp(p, {parser:function(t, e, p, cssp, pt, plugin, vars) {
\r
15317 var pluginClass = (window.GreenSockGlobals || window).com.greensock.plugins[pluginName];
\r
15318 if (!pluginClass) {
\r
15319 _log("Error: " + pluginName + " js file not loaded.");
\r
15322 pluginClass._cssRegister();
\r
15323 return _specialProps[p].parse(t, e, p, cssp, pt, plugin, vars);
\r
15329 p = SpecialProp.prototype;
\r
15332 * Alias for _parseComplex() that automatically plugs in certain values for this SpecialProp, like its property name, whether or not colors should be sensed, the default value, and priority. It also looks for any keyword that the SpecialProp defines (like "inset" for boxShadow) and ensures that the beginning and ending values have the same number of values for SpecialProps where multi is true (like boxShadow and textShadow can have a comma-delimited list)
\r
15333 * @param {!Object} t target element
\r
15334 * @param {(string|number|object)} b beginning value
\r
15335 * @param {(string|number|object)} e ending (destination) value
\r
15336 * @param {CSSPropTween=} pt next CSSPropTween in the linked list
\r
15337 * @param {TweenPlugin=} plugin If another plugin will be tweening the complex value, that TweenPlugin instance goes here.
\r
15338 * @param {function=} setRatio If a custom setRatio() method should be used to handle this complex value, that goes here.
\r
15339 * @return {CSSPropTween=} First CSSPropTween in the linked list
\r
15341 p.parseComplex = function(t, b, e, pt, plugin, setRatio) {
\r
15342 var kwd = this.keyword,
\r
15343 i, ba, ea, l, bi, ei;
\r
15344 //if this SpecialProp's value can contain a comma-delimited list of values (like boxShadow or textShadow), we must parse them in a special way, and look for a keyword (like "inset" for boxShadow) and ensure that the beginning and ending BOTH have it if the end defines it as such. We also must ensure that there are an equal number of values specified (we can't tween 1 boxShadow to 3 for example)
\r
15345 if (this.multi) if (_commasOutsideParenExp.test(e) || _commasOutsideParenExp.test(b)) {
\r
15346 ba = b.replace(_commasOutsideParenExp, "|").split("|");
\r
15347 ea = e.replace(_commasOutsideParenExp, "|").split("|");
\r
15348 } else if (kwd) {
\r
15353 l = (ea.length > ba.length) ? ea.length : ba.length;
\r
15354 for (i = 0; i < l; i++) {
\r
15355 b = ba[i] = ba[i] || this.dflt;
\r
15356 e = ea[i] = ea[i] || this.dflt;
\r
15358 bi = b.indexOf(kwd);
\r
15359 ei = e.indexOf(kwd);
\r
15361 e = (ei === -1) ? ea : ba;
\r
15362 e[i] += " " + kwd;
\r
15366 b = ba.join(", ");
\r
15367 e = ea.join(", ");
\r
15369 return _parseComplex(t, this.p, b, e, this.clrs, this.dflt, pt, this.pr, plugin, setRatio);
\r
15373 * Accepts a target and end value and spits back a CSSPropTween that has been inserted into the CSSPlugin's linked list and conforms with all the conventions we use internally, like type:-1, 0, 1, or 2, setting up any extra property tweens, priority, etc. For example, if we have a boxShadow SpecialProp and call:
\r
15374 * this._firstPT = sp.parse(element, "5px 10px 20px rgb(2550,102,51)", "boxShadow", this);
\r
15375 * It should figure out the starting value of the element's boxShadow, compare it to the provided end value and create all the necessary CSSPropTweens of the appropriate types to tween the boxShadow. The CSSPropTween that gets spit back should already be inserted into the linked list (the 4th parameter is the current head, so prepend to that).
\r
15376 * @param {!Object} t Target object whose property is being tweened
\r
15377 * @param {Object} e End value as provided in the vars object (typically a string, but not always - like a throwProps would be an object).
\r
15378 * @param {!string} p Property name
\r
15379 * @param {!CSSPlugin} cssp The CSSPlugin instance that should be associated with this tween.
\r
15380 * @param {?CSSPropTween} pt The CSSPropTween that is the current head of the linked list (we'll prepend to it)
\r
15381 * @param {TweenPlugin=} plugin If a plugin will be used to tween the parsed value, this is the plugin instance.
\r
15382 * @param {Object=} vars Original vars object that contains the data for parsing.
\r
15383 * @return {CSSPropTween} The first CSSPropTween in the linked list which includes the new one(s) added by the parse() call.
\r
15385 p.parse = function(t, e, p, cssp, pt, plugin, vars) {
\r
15386 return this.parseComplex(t.style, this.format(_getStyle(t, this.p, _cs, false, this.dflt)), this.format(e), pt, plugin);
\r
15390 * Registers a special property that should be intercepted from any "css" objects defined in tweens. This allows you to handle them however you want without CSSPlugin doing it for you. The 2nd parameter should be a function that accepts 3 parameters:
\r
15391 * 1) Target object whose property should be tweened (typically a DOM element)
\r
15392 * 2) The end/destination value (could be a string, number, object, or whatever you want)
\r
15393 * 3) The tween instance (you probably don't need to worry about this, but it can be useful for looking up information like the duration)
\r
15395 * Then, your function should return a function which will be called each time the tween gets rendered, passing a numeric "ratio" parameter to your function that indicates the change factor (usually between 0 and 1). For example:
\r
15397 * CSSPlugin.registerSpecialProp("myCustomProp", function(target, value, tween) {
\r
15398 * var start = target.style.width;
\r
15399 * return function(ratio) {
\r
15400 * target.style.width = (start + value * ratio) + "px";
\r
15401 * console.log("set width to " + target.style.width);
\r
15405 * Then, when I do this tween, it will trigger my special property:
\r
15407 * TweenLite.to(element, 1, {css:{myCustomProp:100}});
\r
15409 * In the example, of course, we're just changing the width, but you can do anything you want.
\r
15411 * @param {!string} name Property name (or comma-delimited list of property names) that should be intercepted and handled by your function. For example, if I define "myCustomProp", then it would handle that portion of the following tween: TweenLite.to(element, 1, {css:{myCustomProp:100}})
\r
15412 * @param {!function(Object, Object, Object, string):function(number)} onInitTween The function that will be called when a tween of this special property is performed. The function will receive 4 parameters: 1) Target object that should be tweened, 2) Value that was passed to the tween, 3) The tween instance itself (rarely used), and 4) The property name that's being tweened. Your function should return a function that should be called on every update of the tween. That function will receive a single parameter that is a "change factor" value (typically between 0 and 1) indicating the amount of change as a ratio. You can use this to determine how to set the values appropriately in your function.
\r
15413 * @param {number=} priority Priority that helps the engine determine the order in which to set the properties (default: 0). Higher priority properties will be updated before lower priority ones.
\r
15415 CSSPlugin.registerSpecialProp = function(name, onInitTween, priority) {
\r
15416 _registerComplexSpecialProp(name, {parser:function(t, e, p, cssp, pt, plugin, vars) {
\r
15417 var rv = new CSSPropTween(t, p, 0, 0, pt, 2, p, false, priority);
\r
15418 rv.plugin = plugin;
\r
15419 rv.setRatio = onInitTween(t, e, cssp._tween, p);
\r
15421 }, priority:priority});
\r
15431 //transform-related methods and properties
\r
15432 var _transformProps = ("scaleX,scaleY,scaleZ,x,y,z,skewX,skewY,rotation,rotationX,rotationY,perspective").split(","),
\r
15433 _transformProp = _checkPropPrefix("transform"), //the Javascript (camelCase) transform property, like msTransform, WebkitTransform, MozTransform, or OTransform.
\r
15434 _transformPropCSS = _prefixCSS + "transform",
\r
15435 _transformOriginProp = _checkPropPrefix("transformOrigin"),
\r
15436 _supports3D = (_checkPropPrefix("perspective") !== null),
\r
15437 Transform = _internals.Transform = function() {
\r
15442 * Parses the transform values for an element, returning an object with x, y, z, scaleX, scaleY, scaleZ, rotation, rotationX, rotationY, skewX, and skewY properties. Note: by default (for performance reasons), all skewing is combined into skewX and rotation but skewY still has a place in the transform object so that we can record how much of the skew is attributed to skewX vs skewY. Remember, a skewY of 10 looks the same as a rotation of 10 and skewX of -10.
\r
15443 * @param {!Object} t target element
\r
15444 * @param {Object=} cs computed style object (optional)
\r
15445 * @param {boolean=} rec if true, the transform values will be recorded to the target element's _gsTransform object, like target._gsTransform = {x:0, y:0, z:0, scaleX:1...}
\r
15446 * @param {boolean=} parse if true, we'll ignore any _gsTransform values that already exist on the element, and force a reparsing of the css (calculated style)
\r
15447 * @return {object} object containing all of the transform properties/values like {x:0, y:0, z:0, scaleX:1...}
\r
15449 _getTransform = _internals.getTransform = function(t, cs, rec, parse) {
\r
15450 if (t._gsTransform && rec && !parse) {
\r
15451 return t._gsTransform; //if the element already has a _gsTransform, use that. Note: some browsers don't accurately return the calculated style for the transform (particularly for SVG), so it's almost always safest to just use the values we've already applied rather than re-parsing things.
\r
15453 var tm = rec ? t._gsTransform || new Transform() : new Transform(),
\r
15454 invX = (tm.scaleX < 0), //in order to interpret things properly, we need to know if the user applied a negative scaleX previously so that we can adjust the rotation and skewX accordingly. Otherwise, if we always interpret a flipped matrix as affecting scaleY and the user only wants to tween the scaleX on multiple sequential tweens, it would keep the negative scaleY without that being the user's intent.
\r
15457 minAngle = 179.99,
\r
15458 minPI = minAngle * _DEG2RAD,
\r
15459 zOrigin = _supports3D ? parseFloat(_getStyle(t, _transformOriginProp, cs, false, "0 0 0").split(" ")[2]) || tm.zOrigin || 0 : 0,
\r
15460 s, m, i, n, dec, scaleX, scaleY, rotation, skewX, difX, difY, difR, difS;
\r
15461 if (_transformProp) {
\r
15462 s = _getStyle(t, _transformPropCSS, cs, true);
\r
15463 } else if (t.currentStyle) {
\r
15464 //for older versions of IE, we need to interpret the filter portion that is in the format: progid:DXImageTransform.Microsoft.Matrix(M11=6.123233995736766e-17, M12=-1, M21=1, M22=6.123233995736766e-17, sizingMethod='auto expand') Notice that we need to swap b and c compared to a normal matrix.
\r
15465 s = t.currentStyle.filter.match(_ieGetMatrixExp);
\r
15466 s = (s && s.length === 4) ? [s[0].substr(4), Number(s[2].substr(4)), Number(s[1].substr(4)), s[3].substr(4), (tm.x || 0), (tm.y || 0)].join(",") : "";
\r
15468 //split the matrix values out into an array (m for matrix)
\r
15469 m = (s || "").match(/(?:\-|\b)[\d\-\.e]+\b/gi) || [];
\r
15471 while (--i > -1) {
\r
15472 n = Number(m[i]);
\r
15473 m[i] = (dec = n - (n |= 0)) ? ((dec * rnd + (dec < 0 ? -0.5 : 0.5)) | 0) / rnd + n : n; //convert strings to Numbers and round to 5 decimal places to avoid issues with tiny numbers. Roughly 20x faster than Number.toFixed(). We also must make sure to round before dividing so that values like 0.9999999999 become 1 to avoid glitches in browser rendering and interpretation of flipped/rotated 3D matrices. And don't just multiply the number by rnd, floor it, and then divide by rnd because the bitwise operations max out at a 32-bit signed integer, thus it could get clipped at a relatively low value (like 22,000.00000 for example).
\r
15475 if (m.length === 16) {
\r
15477 //we'll only look at these position-related 6 variables first because if x/y/z all match, it's relatively safe to assume we don't need to re-parse everything which risks losing important rotational information (like rotationX:180 plus rotationY:180 would look the same as rotation:180 - there's no way to know for sure which direction was taken based solely on the matrix3d() values)
\r
15478 var a13 = m[8], a23 = m[9], a33 = m[10],
\r
15479 a14 = m[12], a24 = m[13], a34 = m[14];
\r
15481 //we manually compensate for non-zero z component of transformOrigin to work around bugs in Safari
\r
15482 if (tm.zOrigin) {
\r
15483 a34 = -tm.zOrigin;
\r
15484 a14 = a13*a34-m[12];
\r
15485 a24 = a23*a34-m[13];
\r
15486 a34 = a33*a34+tm.zOrigin-m[14];
\r
15489 //only parse from the matrix if we MUST because not only is it usually unnecessary due to the fact that we store the values in the _gsTransform object, but also because it's impossible to accurately interpret rotationX, rotationY, rotationZ, scaleX, and scaleY if all are applied, so it's much better to rely on what we store. However, we must parse the first time that an object is tweened. We also assume that if the position has changed, the user must have done some styling changes outside of CSSPlugin, thus we force a parse in that scenario.
\r
15490 if (!rec || parse || tm.rotationX == null) {
\r
15491 var a11 = m[0], a21 = m[1], a31 = m[2], a41 = m[3],
\r
15492 a12 = m[4], a22 = m[5], a32 = m[6], a42 = m[7],
\r
15494 angle = Math.atan2(a32, a33),
\r
15495 xFlip = (angle < -minPI || angle > minPI),
\r
15496 t1, t2, t3, cos, sin, yFlip, zFlip;
\r
15497 tm.rotationX = angle * _RAD2DEG;
\r
15500 cos = Math.cos(-angle);
\r
15501 sin = Math.sin(-angle);
\r
15502 t1 = a12*cos+a13*sin;
\r
15503 t2 = a22*cos+a23*sin;
\r
15504 t3 = a32*cos+a33*sin;
\r
15505 a13 = a12*-sin+a13*cos;
\r
15506 a23 = a22*-sin+a23*cos;
\r
15507 a33 = a32*-sin+a33*cos;
\r
15508 a43 = a42*-sin+a43*cos;
\r
15514 angle = Math.atan2(a13, a11);
\r
15515 tm.rotationY = angle * _RAD2DEG;
\r
15517 yFlip = (angle < -minPI || angle > minPI);
\r
15518 cos = Math.cos(-angle);
\r
15519 sin = Math.sin(-angle);
\r
15520 t1 = a11*cos-a13*sin;
\r
15521 t2 = a21*cos-a23*sin;
\r
15522 t3 = a31*cos-a33*sin;
\r
15523 a23 = a21*sin+a23*cos;
\r
15524 a33 = a31*sin+a33*cos;
\r
15525 a43 = a41*sin+a43*cos;
\r
15531 angle = Math.atan2(a21, a22);
\r
15532 tm.rotation = angle * _RAD2DEG;
\r
15534 zFlip = (angle < -minPI || angle > minPI);
\r
15535 cos = Math.cos(-angle);
\r
15536 sin = Math.sin(-angle);
\r
15537 a11 = a11*cos+a12*sin;
\r
15538 t2 = a21*cos+a22*sin;
\r
15539 a22 = a21*-sin+a22*cos;
\r
15540 a32 = a31*-sin+a32*cos;
\r
15544 if (zFlip && xFlip) {
\r
15545 tm.rotation = tm.rotationX = 0;
\r
15546 } else if (zFlip && yFlip) {
\r
15547 tm.rotation = tm.rotationY = 0;
\r
15548 } else if (yFlip && xFlip) {
\r
15549 tm.rotationY = tm.rotationX = 0;
\r
15552 tm.scaleX = ((Math.sqrt(a11 * a11 + a21 * a21) * rnd + 0.5) | 0) / rnd;
\r
15553 tm.scaleY = ((Math.sqrt(a22 * a22 + a23 * a23) * rnd + 0.5) | 0) / rnd;
\r
15554 tm.scaleZ = ((Math.sqrt(a32 * a32 + a33 * a33) * rnd + 0.5) | 0) / rnd;
\r
15556 tm.perspective = a43 ? 1 / ((a43 < 0) ? -a43 : a43) : 0;
\r
15562 } else if ((!_supports3D || parse || !m.length || tm.x !== m[4] || tm.y !== m[5] || (!tm.rotationX && !tm.rotationY)) && !(tm.x !== undefined && _getStyle(t, "display", cs) === "none")) { //sometimes a 6-element matrix is returned even when we performed 3D transforms, like if rotationX and rotationY are 180. In cases like this, we still need to honor the 3D transforms. If we just rely on the 2D info, it could affect how the data is interpreted, like scaleY might get set to -1 or rotation could get offset by 180 degrees. For example, do a TweenLite.to(element, 1, {css:{rotationX:180, rotationY:180}}) and then later, TweenLite.to(element, 1, {css:{rotationX:0}}) and without this conditional logic in place, it'd jump to a state of being unrotated when the 2nd tween starts. Then again, we need to honor the fact that the user COULD alter the transforms outside of CSSPlugin, like by manually applying new css, so we try to sense that by looking at x and y because if those changed, we know the changes were made outside CSSPlugin and we force a reinterpretation of the matrix values. Also, in Webkit browsers, if the element's "display" is "none", its calculated style value will always return empty, so if we've already recorded the values in the _gsTransform object, we'll just rely on those.
\r
15563 var k = (m.length >= 6),
\r
15564 a = k ? m[0] : 1,
\r
15567 d = k ? m[3] : 1;
\r
15568 tm.x = m[4] || 0;
\r
15569 tm.y = m[5] || 0;
\r
15570 scaleX = Math.sqrt(a * a + b * b);
\r
15571 scaleY = Math.sqrt(d * d + c * c);
\r
15572 rotation = (a || b) ? Math.atan2(b, a) * _RAD2DEG : tm.rotation || 0; //note: if scaleX is 0, we cannot accurately measure rotation. Same for skewX with a scaleY of 0. Therefore, we default to the previously recorded value (or zero if that doesn't exist).
\r
15573 skewX = (c || d) ? Math.atan2(c, d) * _RAD2DEG + rotation : tm.skewX || 0;
\r
15574 difX = scaleX - Math.abs(tm.scaleX || 0);
\r
15575 difY = scaleY - Math.abs(tm.scaleY || 0);
\r
15576 if (Math.abs(skewX) > 90 && Math.abs(skewX) < 270) {
\r
15579 skewX += (rotation <= 0) ? 180 : -180;
\r
15580 rotation += (rotation <= 0) ? 180 : -180;
\r
15583 skewX += (skewX <= 0) ? 180 : -180;
\r
15586 difR = (rotation - tm.rotation) % 180; //note: matching ranges would be very small (+/-0.0001) or very close to 180.
\r
15587 difS = (skewX - tm.skewX) % 180;
\r
15588 //if there's already a recorded _gsTransform in place for the target, we should leave those values in place unless we know things changed for sure (beyond a super small amount). This gets around ambiguous interpretations, like if scaleX and scaleY are both -1, the matrix would be the same as if the rotation was 180 with normal scaleX/scaleY. If the user tweened to particular values, those must be prioritized to ensure animation is consistent.
\r
15589 if (tm.skewX === undefined || difX > min || difX < -min || difY > min || difY < -min || (difR > -minAngle && difR < minAngle && (difR * rnd) | 0 !== 0) || (difS > -minAngle && difS < minAngle && (difS * rnd) | 0 !== 0)) {
\r
15590 tm.scaleX = scaleX;
\r
15591 tm.scaleY = scaleY;
\r
15592 tm.rotation = rotation;
\r
15593 tm.skewX = skewX;
\r
15595 if (_supports3D) {
\r
15596 tm.rotationX = tm.rotationY = tm.z = 0;
\r
15597 tm.perspective = parseFloat(CSSPlugin.defaultTransformPerspective) || 0;
\r
15601 tm.zOrigin = zOrigin;
\r
15603 //some browsers have a hard time with very small values like 2.4492935982947064e-16 (notice the "e-" towards the end) and would render the object slightly off. So we round to 0 in these cases. The conditional logic here is faster than calling Math.abs(). Also, browsers tend to render a SLIGHTLY rotated object in a fuzzy way, so we need to snap to exactly 0 when appropriate.
\r
15605 if (tm[i] < min) if (tm[i] > -min) {
\r
15609 //DEBUG: _log("parsed rotation: "+(tm.rotationX)+", "+(tm.rotationY)+", "+(tm.rotation)+", scale: "+tm.scaleX+", "+tm.scaleY+", "+tm.scaleZ+", position: "+tm.x+", "+tm.y+", "+tm.z+", perspective: "+tm.perspective);
\r
15611 t._gsTransform = tm; //record to the object's _gsTransform which we use so that tweens can control individual properties independently (we need all the properties to accurately recompose the matrix in the setRatio() method)
\r
15616 //for setting 2D transforms in IE6, IE7, and IE8 (must use a "filter" to emulate the behavior of modern day browser transforms)
\r
15617 _setIETransformRatio = function(v) {
\r
15618 var t = this.data, //refers to the element's _gsTransform object
\r
15619 ang = -t.rotation * _DEG2RAD,
\r
15620 skew = ang + t.skewX * _DEG2RAD,
\r
15622 a = ((Math.cos(ang) * t.scaleX * rnd) | 0) / rnd,
\r
15623 b = ((Math.sin(ang) * t.scaleX * rnd) | 0) / rnd,
\r
15624 c = ((Math.sin(skew) * -t.scaleY * rnd) | 0) / rnd,
\r
15625 d = ((Math.cos(skew) * t.scaleY * rnd) | 0) / rnd,
\r
15626 style = this.t.style,
\r
15627 cs = this.t.currentStyle,
\r
15632 val = b; //just for swapping the variables an inverting them (reused "val" to avoid creating another variable in memory). IE's filter matrix uses a non-standard matrix configuration (angle goes the opposite way, and b and c are reversed and inverted)
\r
15635 filters = cs.filter;
\r
15636 style.filter = ""; //remove filters so that we can accurately measure offsetWidth/offsetHeight
\r
15637 var w = this.t.offsetWidth,
\r
15638 h = this.t.offsetHeight,
\r
15639 clip = (cs.position !== "absolute"),
\r
15640 m = "progid:DXImageTransform.Microsoft.Matrix(M11=" + a + ", M12=" + b + ", M21=" + c + ", M22=" + d,
\r
15645 //if transformOrigin is being used, adjust the offset x and y
\r
15646 if (t.ox != null) {
\r
15647 dx = ((t.oxp) ? w * t.ox * 0.01 : t.ox) - w / 2;
\r
15648 dy = ((t.oyp) ? h * t.oy * 0.01 : t.oy) - h / 2;
\r
15649 ox += dx - (dx * a + dy * b);
\r
15650 oy += dy - (dx * c + dy * d);
\r
15654 m += ", sizingMethod='auto expand')";
\r
15658 //translate to ensure that transformations occur around the correct origin (default is center).
\r
15659 m += ", Dx=" + (dx - (dx * a + dy * b) + ox) + ", Dy=" + (dy - (dx * c + dy * d) + oy) + ")";
\r
15661 if (filters.indexOf("DXImageTransform.Microsoft.Matrix(") !== -1) {
\r
15662 style.filter = filters.replace(_ieSetMatrixExp, m);
\r
15664 style.filter = m + " " + filters; //we must always put the transform/matrix FIRST (before alpha(opacity=xx)) to avoid an IE bug that slices part of the object when rotation is applied with alpha.
\r
15667 //at the end or beginning of the tween, if the matrix is normal (1, 0, 0, 1) and opacity is 100 (or doesn't exist), remove the filter to improve browser performance.
\r
15668 if (v === 0 || v === 1) if (a === 1) if (b === 0) if (c === 0) if (d === 1) if (!clip || m.indexOf("Dx=0, Dy=0") !== -1) if (!_opacityExp.test(filters) || parseFloat(RegExp.$1) === 100) if (filters.indexOf("gradient(" && filters.indexOf("Alpha")) === -1) {
\r
15669 style.removeAttribute("filter");
\r
15672 //we must set the margins AFTER applying the filter in order to avoid some bugs in IE8 that could (in rare scenarios) cause them to be ignored intermittently (vibration).
\r
15674 var mult = (_ieVers < 8) ? 1 : -1, //in Internet Explorer 7 and before, the box model is broken, causing the browser to treat the width/height of the actual rotated filtered image as the width/height of the box itself, but Microsoft corrected that in IE8. We must use a negative offset in IE8 on the right/bottom
\r
15676 dx = t.ieOffsetX || 0;
\r
15677 dy = t.ieOffsetY || 0;
\r
15678 t.ieOffsetX = Math.round((w - ((a < 0 ? -a : a) * w + (b < 0 ? -b : b) * h)) / 2 + ox);
\r
15679 t.ieOffsetY = Math.round((h - ((d < 0 ? -d : d) * h + (c < 0 ? -c : c) * w)) / 2 + oy);
\r
15680 for (i = 0; i < 4; i++) {
\r
15681 prop = _margins[i];
\r
15683 //we need to get the current margin in case it is being tweened separately (we want to respect that tween's changes)
\r
15684 val = (marg.indexOf("px") !== -1) ? parseFloat(marg) : _convertToPixels(this.t, prop, parseFloat(marg), marg.replace(_suffixExp, "")) || 0;
\r
15685 if (val !== t[prop]) {
\r
15686 dif = (i < 2) ? -t.ieOffsetX : -t.ieOffsetY; //if another tween is controlling a margin, we cannot only apply the difference in the ieOffsets, so we essentially zero-out the dx and dy here in that case. We record the margin(s) later so that we can keep comparing them, making this code very flexible.
\r
15688 dif = (i < 2) ? dx - t.ieOffsetX : dy - t.ieOffsetY;
\r
15690 style[prop] = (t[prop] = Math.round( val - dif * ((i === 0 || i === 2) ? 1 : mult) )) + "px";
\r
15695 _set3DTransformRatio = _internals.set3DTransformRatio = function(v) {
\r
15696 var t = this.data, //refers to the element's _gsTransform object
\r
15697 style = this.t.style,
\r
15698 angle = t.rotation * _DEG2RAD,
\r
15702 perspective = t.perspective,
\r
15703 a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, a42, a43,
\r
15704 zOrigin, rnd, cos, sin, t1, t2, t3, t4;
\r
15705 if (v === 1 || v === 0) if (t.force3D === "auto") if (!t.rotationY && !t.rotationX && sz === 1 && !perspective && !t.z) { //on the final render (which could be 0 for a from tween), if there are no 3D aspects, render in 2D to free up memory and improve performance especially on mobile devices
\r
15706 _set2DTransformRatio.call(this, v);
\r
15709 if (_isFirefox) {
\r
15711 if (sx < n && sx > -n) { //Firefox has a bug (at least in v25) that causes it to render the transparent part of 32-bit PNG images as black when displayed inside an iframe and the 3D scale is very small and doesn't change sufficiently enough between renders (like if you use a Power4.easeInOut to scale from 0 to 1 where the beginning values only change a tiny amount to begin the tween before accelerating). In this case, we force the scale to be 0.00002 instead which is visually the same but works around the Firefox issue.
\r
15712 sx = sz = 0.00002;
\r
15714 if (sy < n && sy > -n) {
\r
15715 sy = sz = 0.00002;
\r
15717 if (perspective && !t.z && !t.rotationX && !t.rotationY) { //Firefox has a bug that causes elements to have an odd super-thin, broken/dotted black border on elements that have a perspective set but aren't utilizing 3D space (no rotationX, rotationY, or z).
\r
15721 if (angle || t.skewX) {
\r
15722 cos = Math.cos(angle);
\r
15723 sin = Math.sin(angle);
\r
15727 angle -= t.skewX * _DEG2RAD;
\r
15728 cos = Math.cos(angle);
\r
15729 sin = Math.sin(angle);
\r
15730 if (t.skewType === "simple") { //by default, we compensate skewing on the other axis to make it look more natural, but you can set the skewType to "simple" to use the uncompensated skewing that CSS does
\r
15731 t1 = Math.tan(t.skewX * _DEG2RAD);
\r
15732 t1 = Math.sqrt(1 + t1 * t1);
\r
15740 } else if (!t.rotationY && !t.rotationX && sz === 1 && !perspective) { //if we're only translating and/or 2D scaling, this is faster...
\r
15741 style[_transformProp] = "translate3d(" + t.x + "px," + t.y + "px," + t.z +"px)" + ((sx !== 1 || sy !== 1) ? " scale(" + sx + "," + sy + ")" : "");
\r
15748 a13 = a14 = a23 = a24 = a31 = a32 = a34 = a41 = a42 = 0;
\r
15749 a43 = (perspective) ? -1 / perspective : 0;
\r
15750 zOrigin = t.zOrigin;
\r
15752 angle = t.rotationY * _DEG2RAD;
\r
15754 cos = Math.cos(angle);
\r
15755 sin = Math.sin(angle);
\r
15765 angle = t.rotationX * _DEG2RAD;
\r
15767 cos = Math.cos(angle);
\r
15768 sin = Math.sin(angle);
\r
15769 t1 = a12*cos+a13*sin;
\r
15770 t2 = a22*cos+a23*sin;
\r
15771 t3 = a32*cos+a33*sin;
\r
15772 t4 = a42*cos+a43*sin;
\r
15773 a13 = a12*-sin+a13*cos;
\r
15774 a23 = a22*-sin+a23*cos;
\r
15775 a33 = a32*-sin+a33*cos;
\r
15776 a43 = a42*-sin+a43*cos;
\r
15804 a34 = a33*a34+zOrigin;
\r
15806 //we round the x, y, and z slightly differently to allow even larger values.
\r
15807 a14 = (t1 = (a14 += t.x) - (a14 |= 0)) ? ((t1 * rnd + (t1 < 0 ? -0.5 : 0.5)) | 0) / rnd + a14 : a14;
\r
15808 a24 = (t1 = (a24 += t.y) - (a24 |= 0)) ? ((t1 * rnd + (t1 < 0 ? -0.5 : 0.5)) | 0) / rnd + a24 : a24;
\r
15809 a34 = (t1 = (a34 += t.z) - (a34 |= 0)) ? ((t1 * rnd + (t1 < 0 ? -0.5 : 0.5)) | 0) / rnd + a34 : a34;
\r
15810 style[_transformProp] = "matrix3d(" + [ (((a11 * rnd) | 0) / rnd), (((a21 * rnd) | 0) / rnd), (((a31 * rnd) | 0) / rnd), (((a41 * rnd) | 0) / rnd), (((a12 * rnd) | 0) / rnd), (((a22 * rnd) | 0) / rnd), (((a32 * rnd) | 0) / rnd), (((a42 * rnd) | 0) / rnd), (((a13 * rnd) | 0) / rnd), (((a23 * rnd) | 0) / rnd), (((a33 * rnd) | 0) / rnd), (((a43 * rnd) | 0) / rnd), a14, a24, a34, (perspective ? (1 + (-a34 / perspective)) : 1) ].join(",") + ")";
\r
15813 _set2DTransformRatio = _internals.set2DTransformRatio = function(v) {
\r
15814 var t = this.data, //refers to the element's _gsTransform object
\r
15816 style = targ.style,
\r
15817 ang, skew, rnd, sx, sy;
\r
15818 if (t.rotationX || t.rotationY || t.z || t.force3D === true || (t.force3D === "auto" && v !== 1 && v !== 0)) { //if a 3D tween begins while a 2D one is running, we need to kick the rendering over to the 3D method. For example, imagine a yoyo-ing, infinitely repeating scale tween running, and then the object gets rotated in 3D space with a different tween.
\r
15819 this.setRatio = _set3DTransformRatio;
\r
15820 _set3DTransformRatio.call(this, v);
\r
15823 if (!t.rotation && !t.skewX) {
\r
15824 style[_transformProp] = "matrix(" + t.scaleX + ",0,0," + t.scaleY + "," + t.x + "," + t.y + ")";
\r
15826 ang = t.rotation * _DEG2RAD;
\r
15827 skew = ang - t.skewX * _DEG2RAD;
\r
15829 sx = t.scaleX * rnd;
\r
15830 sy = t.scaleY * rnd;
\r
15831 //some browsers have a hard time with very small values like 2.4492935982947064e-16 (notice the "e-" towards the end) and would render the object slightly off. So we round to 5 decimal places.
\r
15832 style[_transformProp] = "matrix(" + (((Math.cos(ang) * sx) | 0) / rnd) + "," + (((Math.sin(ang) * sx) | 0) / rnd) + "," + (((Math.sin(skew) * -sy) | 0) / rnd) + "," + (((Math.cos(skew) * sy) | 0) / rnd) + "," + t.x + "," + t.y + ")";
\r
15836 _registerComplexSpecialProp("transform,scale,scaleX,scaleY,scaleZ,x,y,z,rotation,rotationX,rotationY,rotationZ,skewX,skewY,shortRotation,shortRotationX,shortRotationY,shortRotationZ,transformOrigin,transformPerspective,directionalRotation,parseTransform,force3D,skewType", {parser:function(t, e, p, cssp, pt, plugin, vars) {
\r
15837 if (cssp._transform) { return pt; } //only need to parse the transform once, and only if the browser supports it.
\r
15838 var m1 = cssp._transform = _getTransform(t, _cs, true, vars.parseTransform),
\r
15841 i = _transformProps.length,
\r
15843 endRotations = {},
\r
15844 m2, skewY, copy, orig, has3D, hasChange, dr;
\r
15845 if (typeof(v.transform) === "string" && _transformProp) { //for values like transform:"rotate(60deg) scale(0.5, 0.8)"
\r
15846 copy = _tempDiv.style; //don't use the original target because it might be SVG in which case some browsers don't report computed style correctly.
\r
15847 copy[_transformProp] = v.transform;
\r
15848 copy.display = "block"; //if display is "none", the browser often refuses to report the transform properties correctly.
\r
15849 copy.position = "absolute";
\r
15850 _doc.body.appendChild(_tempDiv);
\r
15851 m2 = _getTransform(_tempDiv, null, false);
\r
15852 _doc.body.removeChild(_tempDiv);
\r
15853 } else if (typeof(v) === "object") { //for values like scaleX, scaleY, rotation, x, y, skewX, and skewY or transform:{...} (object)
\r
15854 m2 = {scaleX:_parseVal((v.scaleX != null) ? v.scaleX : v.scale, m1.scaleX),
\r
15855 scaleY:_parseVal((v.scaleY != null) ? v.scaleY : v.scale, m1.scaleY),
\r
15856 scaleZ:_parseVal(v.scaleZ, m1.scaleZ),
\r
15857 x:_parseVal(v.x, m1.x),
\r
15858 y:_parseVal(v.y, m1.y),
\r
15859 z:_parseVal(v.z, m1.z),
\r
15860 perspective:_parseVal(v.transformPerspective, m1.perspective)};
\r
15861 dr = v.directionalRotation;
\r
15862 if (dr != null) {
\r
15863 if (typeof(dr) === "object") {
\r
15864 for (copy in dr) {
\r
15865 v[copy] = dr[copy];
\r
15871 m2.rotation = _parseAngle(("rotation" in v) ? v.rotation : ("shortRotation" in v) ? v.shortRotation + "_short" : ("rotationZ" in v) ? v.rotationZ : m1.rotation, m1.rotation, "rotation", endRotations);
\r
15872 if (_supports3D) {
\r
15873 m2.rotationX = _parseAngle(("rotationX" in v) ? v.rotationX : ("shortRotationX" in v) ? v.shortRotationX + "_short" : m1.rotationX || 0, m1.rotationX, "rotationX", endRotations);
\r
15874 m2.rotationY = _parseAngle(("rotationY" in v) ? v.rotationY : ("shortRotationY" in v) ? v.shortRotationY + "_short" : m1.rotationY || 0, m1.rotationY, "rotationY", endRotations);
\r
15876 m2.skewX = (v.skewX == null) ? m1.skewX : _parseAngle(v.skewX, m1.skewX);
\r
15878 //note: for performance reasons, we combine all skewing into the skewX and rotation values, ignoring skewY but we must still record it so that we can discern how much of the overall skew is attributed to skewX vs. skewY. Otherwise, if the skewY would always act relative (tween skewY to 10deg, for example, multiple times and if we always combine things into skewX, we can't remember that skewY was 10 from last time). Remember, a skewY of 10 degrees looks the same as a rotation of 10 degrees plus a skewX of -10 degrees.
\r
15879 m2.skewY = (v.skewY == null) ? m1.skewY : _parseAngle(v.skewY, m1.skewY);
\r
15880 if ((skewY = m2.skewY - m1.skewY)) {
\r
15881 m2.skewX += skewY;
\r
15882 m2.rotation += skewY;
\r
15886 if (_supports3D && v.force3D != null) {
\r
15887 m1.force3D = v.force3D;
\r
15888 hasChange = true;
\r
15891 m1.skewType = v.skewType || m1.skewType || CSSPlugin.defaultSkewType;
\r
15893 has3D = (m1.force3D || m1.z || m1.rotationX || m1.rotationY || m2.z || m2.rotationX || m2.rotationY || m2.perspective);
\r
15894 if (!has3D && v.scale != null) {
\r
15895 m2.scaleZ = 1; //no need to tween scaleZ.
\r
15898 while (--i > -1) {
\r
15899 p = _transformProps[i];
\r
15900 orig = m2[p] - m1[p];
\r
15901 if (orig > min || orig < -min || _forcePT[p] != null) {
\r
15902 hasChange = true;
\r
15903 pt = new CSSPropTween(m1, p, m1[p], orig, pt);
\r
15904 if (p in endRotations) {
\r
15905 pt.e = endRotations[p]; //directional rotations typically have compensated values during the tween, but we need to make sure they end at exactly what the user requested
\r
15907 pt.xs0 = 0; //ensures the value stays numeric in setRatio()
\r
15908 pt.plugin = plugin;
\r
15909 cssp._overwriteProps.push(pt.n);
\r
15913 orig = v.transformOrigin;
\r
15914 if (orig || (_supports3D && has3D && m1.zOrigin)) { //if anything 3D is happening and there's a transformOrigin with a z component that's non-zero, we must ensure that the transformOrigin's z-component is set to 0 so that we can manually do those calculations to get around Safari bugs. Even if the user didn't specifically define a "transformOrigin" in this particular tween (maybe they did it via css directly).
\r
15915 if (_transformProp) {
\r
15916 hasChange = true;
\r
15917 p = _transformOriginProp;
\r
15918 orig = (orig || _getStyle(t, p, _cs, false, "50% 50%")) + ""; //cast as string to avoid errors
\r
15919 pt = new CSSPropTween(style, p, 0, 0, pt, -1, "transformOrigin");
\r
15921 pt.plugin = plugin;
\r
15922 if (_supports3D) {
\r
15923 copy = m1.zOrigin;
\r
15924 orig = orig.split(" ");
\r
15925 m1.zOrigin = ((orig.length > 2 && !(copy !== 0 && orig[2] === "0px")) ? parseFloat(orig[2]) : copy) || 0; //Safari doesn't handle the z part of transformOrigin correctly, so we'll manually handle it in the _set3DTransformRatio() method.
\r
15926 pt.xs0 = pt.e = orig[0] + " " + (orig[1] || "50%") + " 0px"; //we must define a z value of 0px specifically otherwise iOS 5 Safari will stick with the old one (if one was defined)!
\r
15927 pt = new CSSPropTween(m1, "zOrigin", 0, 0, pt, -1, pt.n); //we must create a CSSPropTween for the _gsTransform.zOrigin so that it gets reset properly at the beginning if the tween runs backward (as opposed to just setting m1.zOrigin here)
\r
15929 pt.xs0 = pt.e = m1.zOrigin;
\r
15931 pt.xs0 = pt.e = orig;
\r
15934 //for older versions of IE (6-8), we need to manually calculate things inside the setRatio() function. We record origin x and y (ox and oy) and whether or not the values are percentages (oxp and oyp).
\r
15936 _parsePosition(orig + "", m1);
\r
15941 cssp._transformType = (has3D || this._transformType === 3) ? 3 : 2; //quicker than calling cssp._enableTransforms();
\r
15944 }, prefix:true});
\r
15946 _registerComplexSpecialProp("boxShadow", {defaultValue:"0px 0px 0px 0px #999", prefix:true, color:true, multi:true, keyword:"inset"});
\r
15948 _registerComplexSpecialProp("borderRadius", {defaultValue:"0px", parser:function(t, e, p, cssp, pt, plugin) {
\r
15949 e = this.format(e);
\r
15950 var props = ["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],
\r
15952 ea1, i, es2, bs2, bs, es, bn, en, w, h, esfx, bsfx, rel, hn, vn, em;
\r
15953 w = parseFloat(t.offsetWidth);
\r
15954 h = parseFloat(t.offsetHeight);
\r
15955 ea1 = e.split(" ");
\r
15956 for (i = 0; i < props.length; i++) { //if we're dealing with percentages, we must convert things separately for the horizontal and vertical axis!
\r
15957 if (this.p.indexOf("border")) { //older browsers used a prefix
\r
15958 props[i] = _checkPropPrefix(props[i]);
\r
15960 bs = bs2 = _getStyle(t, props[i], _cs, false, "0px");
\r
15961 if (bs.indexOf(" ") !== -1) {
\r
15962 bs2 = bs.split(" ");
\r
15966 es = es2 = ea1[i];
\r
15967 bn = parseFloat(bs);
\r
15968 bsfx = bs.substr((bn + "").length);
\r
15969 rel = (es.charAt(1) === "=");
\r
15971 en = parseInt(es.charAt(0)+"1", 10);
\r
15972 es = es.substr(2);
\r
15973 en *= parseFloat(es);
\r
15974 esfx = es.substr((en + "").length - (en < 0 ? 1 : 0)) || "";
\r
15976 en = parseFloat(es);
\r
15977 esfx = es.substr((en + "").length);
\r
15979 if (esfx === "") {
\r
15980 esfx = _suffixMap[p] || bsfx;
\r
15982 if (esfx !== bsfx) {
\r
15983 hn = _convertToPixels(t, "borderLeft", bn, bsfx); //horizontal number (we use a bogus "borderLeft" property just because the _convertToPixels() method searches for the keywords "Left", "Right", "Top", and "Bottom" to determine of it's a horizontal or vertical property, and we need "border" in the name so that it knows it should measure relative to the element itself, not its parent.
\r
15984 vn = _convertToPixels(t, "borderTop", bn, bsfx); //vertical number
\r
15985 if (esfx === "%") {
\r
15986 bs = (hn / w * 100) + "%";
\r
15987 bs2 = (vn / h * 100) + "%";
\r
15988 } else if (esfx === "em") {
\r
15989 em = _convertToPixels(t, "borderLeft", 1, "em");
\r
15990 bs = (hn / em) + "em";
\r
15991 bs2 = (vn / em) + "em";
\r
15997 es = (parseFloat(bs) + en) + esfx;
\r
15998 es2 = (parseFloat(bs2) + en) + esfx;
\r
16001 pt = _parseComplex(style, props[i], bs + " " + bs2, es + " " + es2, false, "0px", pt);
\r
16004 }, prefix:true, formatter:_getFormatter("0px 0px 0px 0px", false, true)});
\r
16005 _registerComplexSpecialProp("backgroundPosition", {defaultValue:"0 0", parser:function(t, e, p, cssp, pt, plugin) {
\r
16006 var bp = "background-position",
\r
16007 cs = (_cs || _getComputedStyle(t, null)),
\r
16008 bs = this.format( ((cs) ? _ieVers ? cs.getPropertyValue(bp + "-x") + " " + cs.getPropertyValue(bp + "-y") : cs.getPropertyValue(bp) : t.currentStyle.backgroundPositionX + " " + t.currentStyle.backgroundPositionY) || "0 0"), //Internet Explorer doesn't report background-position correctly - we must query background-position-x and background-position-y and combine them (even in IE10). Before IE9, we must do the same with the currentStyle object and use camelCase
\r
16009 es = this.format(e),
\r
16010 ba, ea, i, pct, overlap, src;
\r
16011 if ((bs.indexOf("%") !== -1) !== (es.indexOf("%") !== -1)) {
\r
16012 src = _getStyle(t, "backgroundImage").replace(_urlExp, "");
\r
16013 if (src && src !== "none") {
\r
16014 ba = bs.split(" ");
\r
16015 ea = es.split(" ");
\r
16016 _tempImg.setAttribute("src", src); //set the temp <img>'s src to the background-image so that we can measure its width/height
\r
16018 while (--i > -1) {
\r
16020 pct = (bs.indexOf("%") !== -1);
\r
16021 if (pct !== (ea[i].indexOf("%") !== -1)) {
\r
16022 overlap = (i === 0) ? t.offsetWidth - _tempImg.width : t.offsetHeight - _tempImg.height;
\r
16023 ba[i] = pct ? (parseFloat(bs) / 100 * overlap) + "px" : (parseFloat(bs) / overlap * 100) + "%";
\r
16026 bs = ba.join(" ");
\r
16029 return this.parseComplex(t.style, bs, es, pt, plugin);
\r
16030 }, formatter:_parsePosition});
\r
16031 _registerComplexSpecialProp("backgroundSize", {defaultValue:"0 0", formatter:_parsePosition});
\r
16032 _registerComplexSpecialProp("perspective", {defaultValue:"0px", prefix:true});
\r
16033 _registerComplexSpecialProp("perspectiveOrigin", {defaultValue:"50% 50%", prefix:true});
\r
16034 _registerComplexSpecialProp("transformStyle", {prefix:true});
\r
16035 _registerComplexSpecialProp("backfaceVisibility", {prefix:true});
\r
16036 _registerComplexSpecialProp("userSelect", {prefix:true});
\r
16037 _registerComplexSpecialProp("margin", {parser:_getEdgeParser("marginTop,marginRight,marginBottom,marginLeft")});
\r
16038 _registerComplexSpecialProp("padding", {parser:_getEdgeParser("paddingTop,paddingRight,paddingBottom,paddingLeft")});
\r
16039 _registerComplexSpecialProp("clip", {defaultValue:"rect(0px,0px,0px,0px)", parser:function(t, e, p, cssp, pt, plugin){
\r
16040 var b, cs, delim;
\r
16041 if (_ieVers < 9) { //IE8 and earlier don't report a "clip" value in the currentStyle - instead, the values are split apart into clipTop, clipRight, clipBottom, and clipLeft. Also, in IE7 and earlier, the values inside rect() are space-delimited, not comma-delimited.
\r
16042 cs = t.currentStyle;
\r
16043 delim = _ieVers < 8 ? " " : ",";
\r
16044 b = "rect(" + cs.clipTop + delim + cs.clipRight + delim + cs.clipBottom + delim + cs.clipLeft + ")";
\r
16045 e = this.format(e).split(",").join(delim);
\r
16047 b = this.format(_getStyle(t, this.p, _cs, false, this.dflt));
\r
16048 e = this.format(e);
\r
16050 return this.parseComplex(t.style, b, e, pt, plugin);
\r
16052 _registerComplexSpecialProp("textShadow", {defaultValue:"0px 0px 0px #999", color:true, multi:true});
\r
16053 _registerComplexSpecialProp("autoRound,strictUnits", {parser:function(t, e, p, cssp, pt) {return pt;}}); //just so that we can ignore these properties (not tween them)
\r
16054 _registerComplexSpecialProp("border", {defaultValue:"0px solid #000", parser:function(t, e, p, cssp, pt, plugin) {
\r
16055 return this.parseComplex(t.style, this.format(_getStyle(t, "borderTopWidth", _cs, false, "0px") + " " + _getStyle(t, "borderTopStyle", _cs, false, "solid") + " " + _getStyle(t, "borderTopColor", _cs, false, "#000")), this.format(e), pt, plugin);
\r
16056 }, color:true, formatter:function(v) {
\r
16057 var a = v.split(" ");
\r
16058 return a[0] + " " + (a[1] || "solid") + " " + (v.match(_colorExp) || ["#000"])[0];
\r
16060 _registerComplexSpecialProp("borderWidth", {parser:_getEdgeParser("borderTopWidth,borderRightWidth,borderBottomWidth,borderLeftWidth")}); //Firefox doesn't pick up on borderWidth set in style sheets (only inline).
\r
16061 _registerComplexSpecialProp("float,cssFloat,styleFloat", {parser:function(t, e, p, cssp, pt, plugin) {
\r
16063 prop = ("cssFloat" in s) ? "cssFloat" : "styleFloat";
\r
16064 return new CSSPropTween(s, prop, 0, 0, pt, -1, p, false, 0, s[prop], e);
\r
16067 //opacity-related
\r
16068 var _setIEOpacityRatio = function(v) {
\r
16069 var t = this.t, //refers to the element's style property
\r
16070 filters = t.filter || _getStyle(this.data, "filter"),
\r
16071 val = (this.s + this.c * v) | 0,
\r
16073 if (val === 100) { //for older versions of IE that need to use a filter to apply opacity, we should remove the filter if opacity hits 1 in order to improve performance, but make sure there isn't a transform (matrix) or gradient in the filters.
\r
16074 if (filters.indexOf("atrix(") === -1 && filters.indexOf("radient(") === -1 && filters.indexOf("oader(") === -1) {
\r
16075 t.removeAttribute("filter");
\r
16076 skip = (!_getStyle(this.data, "filter")); //if a class is applied that has an alpha filter, it will take effect (we don't want that), so re-apply our alpha filter in that case. We must first remove it and then check.
\r
16078 t.filter = filters.replace(_alphaFilterExp, "");
\r
16084 t.filter = filters = filters || ("alpha(opacity=" + val + ")"); //works around bug in IE7/8 that prevents changes to "visibility" from being applied properly if the filter is changed to a different alpha on the same frame.
\r
16086 if (filters.indexOf("pacity") === -1) { //only used if browser doesn't support the standard opacity style property (IE 7 and 8). We omit the "O" to avoid case-sensitivity issues
\r
16087 if (val !== 0 || !this.xn1) { //bugs in IE7/8 won't render the filter properly if opacity is ADDED on the same frame/render as "visibility" changes (this.xn1 is 1 if this tween is an "autoAlpha" tween)
\r
16088 t.filter = filters + " alpha(opacity=" + val + ")"; //we round the value because otherwise, bugs in IE7/8 can prevent "visibility" changes from being applied properly.
\r
16091 t.filter = filters.replace(_opacityExp, "opacity=" + val);
\r
16095 _registerComplexSpecialProp("opacity,alpha,autoAlpha", {defaultValue:"1", parser:function(t, e, p, cssp, pt, plugin) {
\r
16096 var b = parseFloat(_getStyle(t, "opacity", _cs, false, "1")),
\r
16098 isAutoAlpha = (p === "autoAlpha");
\r
16099 if (typeof(e) === "string" && e.charAt(1) === "=") {
\r
16100 e = ((e.charAt(0) === "-") ? -1 : 1) * parseFloat(e.substr(2)) + b;
\r
16102 if (isAutoAlpha && b === 1 && _getStyle(t, "visibility", _cs) === "hidden" && e !== 0) { //if visibility is initially set to "hidden", we should interpret that as intent to make opacity 0 (a convenience)
\r
16105 if (_supportsOpacity) {
\r
16106 pt = new CSSPropTween(style, "opacity", b, e - b, pt);
\r
16108 pt = new CSSPropTween(style, "opacity", b * 100, (e - b) * 100, pt);
\r
16109 pt.xn1 = isAutoAlpha ? 1 : 0; //we need to record whether or not this is an autoAlpha so that in the setRatio(), we know to duplicate the setting of the alpha in order to work around a bug in IE7 and IE8 that prevents changes to "visibility" from taking effect if the filter is changed to a different alpha(opacity) at the same time. Setting it to the SAME value first, then the new value works around the IE7/8 bug.
\r
16110 style.zoom = 1; //helps correct an IE issue.
\r
16112 pt.b = "alpha(opacity=" + pt.s + ")";
\r
16113 pt.e = "alpha(opacity=" + (pt.s + pt.c) + ")";
\r
16115 pt.plugin = plugin;
\r
16116 pt.setRatio = _setIEOpacityRatio;
\r
16118 if (isAutoAlpha) { //we have to create the "visibility" PropTween after the opacity one in the linked list so that they run in the order that works properly in IE8 and earlier
\r
16119 pt = new CSSPropTween(style, "visibility", 0, 0, pt, -1, null, false, 0, ((b !== 0) ? "inherit" : "hidden"), ((e === 0) ? "hidden" : "inherit"));
\r
16120 pt.xs0 = "inherit";
\r
16121 cssp._overwriteProps.push(pt.n);
\r
16122 cssp._overwriteProps.push(p);
\r
16128 var _removeProp = function(s, p) {
\r
16130 if (s.removeProperty) {
\r
16131 if (p.substr(0,2) === "ms") { //Microsoft browsers don't conform to the standard of capping the first prefix character, so we adjust so that when we prefix the caps with a dash, it's correct (otherwise it'd be "ms-transform" instead of "-ms-transform" for IE9, for example)
\r
16132 p = "M" + p.substr(1);
\r
16134 s.removeProperty(p.replace(_capsExp, "-$1").toLowerCase());
\r
16135 } else { //note: old versions of IE use "removeAttribute()" instead of "removeProperty()"
\r
16136 s.removeAttribute(p);
\r
16140 _setClassNameRatio = function(v) {
\r
16141 this.t._gsClassPT = this;
\r
16142 if (v === 1 || v === 0) {
\r
16143 this.t.setAttribute("class", (v === 0) ? this.b : this.e);
\r
16144 var mpt = this.data, //first MiniPropTween
\r
16145 s = this.t.style;
\r
16148 _removeProp(s, mpt.p);
\r
16150 s[mpt.p] = mpt.v;
\r
16154 if (v === 1 && this.t._gsClassPT === this) {
\r
16155 this.t._gsClassPT = null;
\r
16157 } else if (this.t.getAttribute("class") !== this.e) {
\r
16158 this.t.setAttribute("class", this.e);
\r
16161 _registerComplexSpecialProp("className", {parser:function(t, e, p, cssp, pt, plugin, vars) {
\r
16162 var b = t.getAttribute("class") || "", //don't use t.className because it doesn't work consistently on SVG elements; getAttribute("class") and setAttribute("class", value") is more reliable.
\r
16163 cssText = t.style.cssText,
\r
16164 difData, bs, cnpt, cnptLookup, mpt;
\r
16165 pt = cssp._classNamePT = new CSSPropTween(t, p, 0, 0, pt, 2);
\r
16166 pt.setRatio = _setClassNameRatio;
\r
16168 _hasPriority = true;
\r
16170 bs = _getAllStyles(t, _cs);
\r
16171 //if there's a className tween already operating on the target, force it to its end so that the necessary inline styles are removed and the class name is applied before we determine the end state (we don't want inline styles interfering that were there just for class-specific values)
\r
16172 cnpt = t._gsClassPT;
\r
16175 mpt = cnpt.data; //first MiniPropTween which stores the inline styles - we need to force these so that the inline styles don't contaminate things. Otherwise, there's a small chance that a tween could start and the inline values match the destination values and they never get cleaned.
\r
16177 cnptLookup[mpt.p] = 1;
\r
16180 cnpt.setRatio(1);
\r
16182 t._gsClassPT = pt;
\r
16183 pt.e = (e.charAt(1) !== "=") ? e : b.replace(new RegExp("\\s*\\b" + e.substr(2) + "\\b"), "") + ((e.charAt(0) === "+") ? " " + e.substr(2) : "");
\r
16184 if (cssp._tween._duration) { //if it's a zero-duration tween, there's no need to tween anything or parse the data. In fact, if we switch classes temporarily (which we must do for proper parsing) and the class has a transition applied, it could cause a quick flash to the end state and back again initially in some browsers.
\r
16185 t.setAttribute("class", pt.e);
\r
16186 difData = _cssDif(t, bs, _getAllStyles(t), vars, cnptLookup);
\r
16187 t.setAttribute("class", b);
\r
16188 pt.data = difData.firstMPT;
\r
16189 t.style.cssText = cssText; //we recorded cssText before we swapped classes and ran _getAllStyles() because in cases when a className tween is overwritten, we remove all the related tweening properties from that class change (otherwise class-specific stuff can't override properties we've directly set on the target's style object due to specificity).
\r
16190 pt = pt.xfirst = cssp.parse(t, difData.difs, pt, plugin); //we record the CSSPropTween as the xfirst so that we can handle overwriting propertly (if "className" gets overwritten, we must kill all the properties associated with the className part of the tween, so we can loop through from xfirst to the pt itself)
\r
16196 var _setClearPropsRatio = function(v) {
\r
16197 if (v === 1 || v === 0) if (this.data._totalTime === this.data._totalDuration && this.data.data !== "isFromStart") { //this.data refers to the tween. Only clear at the END of the tween (remember, from() tweens make the ratio go from 1 to 0, so we can't just check that and if the tween is the zero-duration one that's created internally to render the starting values in a from() tween, ignore that because otherwise, for example, from(...{height:100, clearProps:"height", delay:1}) would wipe the height at the beginning of the tween and after 1 second, it'd kick back in).
\r
16198 var s = this.t.style,
\r
16199 transformParse = _specialProps.transform.parse,
\r
16200 a, p, i, clearTransform;
\r
16201 if (this.e === "all") {
\r
16203 clearTransform = true;
\r
16205 a = this.e.split(",");
\r
16207 while (--i > -1) {
\r
16209 if (_specialProps[p]) {
\r
16210 if (_specialProps[p].parse === transformParse) {
\r
16211 clearTransform = true;
\r
16213 p = (p === "transformOrigin") ? _transformOriginProp : _specialProps[p].p; //ensures that special properties use the proper browser-specific property name, like "scaleX" might be "-webkit-transform" or "boxShadow" might be "-moz-box-shadow"
\r
16216 _removeProp(s, p);
\r
16219 if (clearTransform) {
\r
16220 _removeProp(s, _transformProp);
\r
16221 if (this.t._gsTransform) {
\r
16222 delete this.t._gsTransform;
\r
16228 _registerComplexSpecialProp("clearProps", {parser:function(t, e, p, cssp, pt) {
\r
16229 pt = new CSSPropTween(t, p, 0, 0, pt, 2);
\r
16230 pt.setRatio = _setClearPropsRatio;
\r
16233 pt.data = cssp._tween;
\r
16234 _hasPriority = true;
\r
16238 p = "bezier,throwProps,physicsProps,physics2D".split(",");
\r
16241 _registerPluginProp(p[i]);
\r
16251 p = CSSPlugin.prototype;
\r
16252 p._firstPT = null;
\r
16254 //gets called when the tween renders for the first time. This kicks everything off, recording start/end values, etc.
\r
16255 p._onInitTween = function(target, vars, tween) {
\r
16256 if (!target.nodeType) { //css is only for dom elements
\r
16259 this._target = target;
\r
16260 this._tween = tween;
\r
16261 this._vars = vars;
\r
16262 _autoRound = vars.autoRound;
\r
16263 _hasPriority = false;
\r
16264 _suffixMap = vars.suffixMap || CSSPlugin.suffixMap;
\r
16265 _cs = _getComputedStyle(target, "");
\r
16266 _overwriteProps = this._overwriteProps;
\r
16267 var style = target.style,
\r
16268 v, pt, pt2, first, last, next, zIndex, tpt, threeD;
\r
16269 if (_reqSafariFix) if (style.zIndex === "") {
\r
16270 v = _getStyle(target, "zIndex", _cs);
\r
16271 if (v === "auto" || v === "") {
\r
16272 //corrects a bug in [non-Android] Safari that prevents it from repainting elements in their new positions if they don't have a zIndex set. We also can't just apply this inside _parseTransform() because anything that's moved in any way (like using "left" or "top" instead of transforms like "x" and "y") can be affected, so it is best to ensure that anything that's tweening has a z-index. Setting "WebkitPerspective" to a non-zero value worked too except that on iOS Safari things would flicker randomly. Plus zIndex is less memory-intensive.
\r
16273 this._addLazySet(style, "zIndex", 0);
\r
16277 if (typeof(vars) === "string") {
\r
16278 first = style.cssText;
\r
16279 v = _getAllStyles(target, _cs);
\r
16280 style.cssText = first + ";" + vars;
\r
16281 v = _cssDif(target, v, _getAllStyles(target)).difs;
\r
16282 if (!_supportsOpacity && _opacityValExp.test(vars)) {
\r
16283 v.opacity = parseFloat( RegExp.$1 );
\r
16286 style.cssText = first;
\r
16288 this._firstPT = pt = this.parse(target, vars, null);
\r
16290 if (this._transformType) {
\r
16291 threeD = (this._transformType === 3);
\r
16292 if (!_transformProp) {
\r
16293 style.zoom = 1; //helps correct an IE issue.
\r
16294 } else if (_isSafari) {
\r
16295 _reqSafariFix = true;
\r
16296 //if zIndex isn't set, iOS Safari doesn't repaint things correctly sometimes (seemingly at random).
\r
16297 if (style.zIndex === "") {
\r
16298 zIndex = _getStyle(target, "zIndex", _cs);
\r
16299 if (zIndex === "auto" || zIndex === "") {
\r
16300 this._addLazySet(style, "zIndex", 0);
\r
16303 //Setting WebkitBackfaceVisibility corrects 3 bugs:
\r
16304 // 1) [non-Android] Safari skips rendering changes to "top" and "left" that are made on the same frame/render as a transform update.
\r
16305 // 2) iOS Safari sometimes neglects to repaint elements in their new positions. Setting "WebkitPerspective" to a non-zero value worked too except that on iOS Safari things would flicker randomly.
\r
16306 // 3) Safari sometimes displayed odd artifacts when tweening the transform (or WebkitTransform) property, like ghosts of the edges of the element remained. Definitely a browser bug.
\r
16307 //Note: we allow the user to override the auto-setting by defining WebkitBackfaceVisibility in the vars of the tween.
\r
16308 if (_isSafariLT6) {
\r
16309 this._addLazySet(style, "WebkitBackfaceVisibility", this._vars.WebkitBackfaceVisibility || (threeD ? "visible" : "hidden"));
\r
16313 while (pt2 && pt2._next) {
\r
16316 tpt = new CSSPropTween(target, "transform", 0, 0, null, 2);
\r
16317 this._linkCSSP(tpt, null, pt2);
\r
16318 tpt.setRatio = (threeD && _supports3D) ? _set3DTransformRatio : _transformProp ? _set2DTransformRatio : _setIETransformRatio;
\r
16319 tpt.data = this._transform || _getTransform(target, _cs, true);
\r
16320 _overwriteProps.pop(); //we don't want to force the overwrite of all "transform" tweens of the target - we only care about individual transform properties like scaleX, rotation, etc. The CSSPropTween constructor automatically adds the property to _overwriteProps which is why we need to pop() here.
\r
16323 if (_hasPriority) {
\r
16324 //reorders the linked list in order of pr (priority)
\r
16328 while (pt2 && pt2.pr > pt.pr) {
\r
16331 if ((pt._prev = pt2 ? pt2._prev : last)) {
\r
16332 pt._prev._next = pt;
\r
16336 if ((pt._next = pt2)) {
\r
16343 this._firstPT = first;
\r
16349 p.parse = function(target, vars, pt, plugin) {
\r
16350 var style = target.style,
\r
16351 p, sp, bn, en, bs, es, bsfx, esfx, isStr, rel;
\r
16352 for (p in vars) {
\r
16353 es = vars[p]; //ending value string
\r
16354 sp = _specialProps[p]; //SpecialProp lookup.
\r
16356 pt = sp.parse(target, es, p, this, pt, plugin, vars);
\r
16359 bs = _getStyle(target, p, _cs) + "";
\r
16360 isStr = (typeof(es) === "string");
\r
16361 if (p === "color" || p === "fill" || p === "stroke" || p.indexOf("Color") !== -1 || (isStr && _rgbhslExp.test(es))) { //Opera uses background: to define color sometimes in addition to backgroundColor:
\r
16363 es = _parseColor(es);
\r
16364 es = ((es.length > 3) ? "rgba(" : "rgb(") + es.join(",") + ")";
\r
16366 pt = _parseComplex(style, p, bs, es, true, "transparent", pt, 0, plugin);
\r
16368 } else if (isStr && (es.indexOf(" ") !== -1 || es.indexOf(",") !== -1)) {
\r
16369 pt = _parseComplex(style, p, bs, es, true, null, pt, 0, plugin);
\r
16372 bn = parseFloat(bs);
\r
16373 bsfx = (bn || bn === 0) ? bs.substr((bn + "").length) : ""; //remember, bs could be non-numeric like "normal" for fontWeight, so we should default to a blank suffix in that case.
\r
16375 if (bs === "" || bs === "auto") {
\r
16376 if (p === "width" || p === "height") {
\r
16377 bn = _getDimension(target, p, _cs);
\r
16379 } else if (p === "left" || p === "top") {
\r
16380 bn = _calculateOffset(target, p, _cs);
\r
16383 bn = (p !== "opacity") ? 0 : 1;
\r
16388 rel = (isStr && es.charAt(1) === "=");
\r
16390 en = parseInt(es.charAt(0) + "1", 10);
\r
16391 es = es.substr(2);
\r
16392 en *= parseFloat(es);
\r
16393 esfx = es.replace(_suffixExp, "");
\r
16395 en = parseFloat(es);
\r
16396 esfx = isStr ? es.substr((en + "").length) || "" : "";
\r
16399 if (esfx === "") {
\r
16400 esfx = (p in _suffixMap) ? _suffixMap[p] : bsfx; //populate the end suffix, prioritizing the map, then if none is found, use the beginning suffix.
\r
16403 es = (en || en === 0) ? (rel ? en + bn : en) + esfx : vars[p]; //ensures that any += or -= prefixes are taken care of. Record the end value before normalizing the suffix because we always want to end the tween on exactly what they intended even if it doesn't match the beginning value's suffix.
\r
16405 //if the beginning/ending suffixes don't match, normalize them...
\r
16406 if (bsfx !== esfx) if (esfx !== "") if (en || en === 0) if (bn) { //note: if the beginning value (bn) is 0, we don't need to convert units!
\r
16407 bn = _convertToPixels(target, p, bn, bsfx);
\r
16408 if (esfx === "%") {
\r
16409 bn /= _convertToPixels(target, p, 100, "%") / 100;
\r
16410 if (vars.strictUnits !== true) { //some browsers report only "px" values instead of allowing "%" with getComputedStyle(), so we assume that if we're tweening to a %, we should start there too unless strictUnits:true is defined. This approach is particularly useful for responsive designs that use from() tweens.
\r
16414 } else if (esfx === "em") {
\r
16415 bn /= _convertToPixels(target, p, 1, "em");
\r
16417 //otherwise convert to pixels.
\r
16418 } else if (esfx !== "px") {
\r
16419 en = _convertToPixels(target, p, en, esfx);
\r
16420 esfx = "px"; //we don't use bsfx after this, so we don't need to set it to px too.
\r
16422 if (rel) if (en || en === 0) {
\r
16423 es = (en + bn) + esfx; //the changes we made affect relative calculations, so adjust the end value here.
\r
16431 if ((bn || bn === 0) && (en || en === 0)) { //faster than isNaN(). Also, previously we required en !== bn but that doesn't really gain much performance and it prevents _parseToProxy() from working properly if beginning and ending values match but need to get tweened by an external plugin anyway. For example, a bezier tween where the target starts at left:0 and has these points: [{left:50},{left:0}] wouldn't work properly because when parsing the last point, it'd match the first (current) one and a non-tweening CSSPropTween would be recorded when we actually need a normal tween (type:0) so that things get updated during the tween properly.
\r
16432 pt = new CSSPropTween(style, p, bn, en - bn, pt, 0, p, (_autoRound !== false && (esfx === "px" || p === "zIndex")), 0, bs, es);
\r
16434 //DEBUG: _log("tween "+p+" from "+pt.b+" ("+bn+esfx+") to "+pt.e+" with suffix: "+pt.xs0);
\r
16435 } else if (style[p] === undefined || !es && (es + "" === "NaN" || es == null)) {
\r
16436 _log("invalid " + p + " tween value: " + vars[p]);
\r
16438 pt = new CSSPropTween(style, p, en || bn || 0, 0, pt, -1, p, false, 0, bs, es);
\r
16439 pt.xs0 = (es === "none" && (p === "display" || p.indexOf("Style") !== -1)) ? bs : es; //intermediate value should typically be set immediately (end value) except for "display" or things like borderTopStyle, borderBottomStyle, etc. which should use the beginning value during the tween.
\r
16440 //DEBUG: _log("non-tweening value "+p+": "+pt.xs0);
\r
16444 if (plugin) if (pt && !pt.plugin) {
\r
16445 pt.plugin = plugin;
\r
16452 //gets called every time the tween updates, passing the new ratio (typically a value between 0 and 1, but not always (for example, if an Elastic.easeOut is used, the value can jump above 1 mid-tween). It will always start and 0 and end at 1.
\r
16453 p.setRatio = function(v) {
\r
16454 var pt = this._firstPT,
\r
16458 //at the end of the tween, we set the values to exactly what we received in order to make sure non-tweening values (like "position" or "float" or whatever) are set and so that if the beginning/ending suffixes (units) didn't match and we normalized to px, the value that the user passed in is used here. We check to see if the tween is at its beginning in case it's a from() tween in which case the ratio will actually go from 1 to 0 over the course of the tween (backwards).
\r
16459 if (v === 1 && (this._tween._time === this._tween._duration || this._tween._time === 0)) {
\r
16461 if (pt.type !== 2) {
\r
16462 pt.t[pt.p] = pt.e;
\r
16469 } else if (v || !(this._tween._time === this._tween._duration || this._tween._time === 0) || this._tween._rawPrevTime === -0.000001) {
\r
16471 val = pt.c * v + pt.s;
\r
16473 val = Math.round(val);
\r
16474 } else if (val < min) if (val > -min) {
\r
16478 pt.t[pt.p] = val + pt.xs0;
\r
16479 } else if (pt.type === 1) { //complex value (one that typically has multiple numbers inside a string, like "rect(5px,10px,20px,25px)"
\r
16482 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2;
\r
16483 } else if (i === 3) {
\r
16484 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3;
\r
16485 } else if (i === 4) {
\r
16486 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3 + pt.xn3 + pt.xs4;
\r
16487 } else if (i === 5) {
\r
16488 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3 + pt.xn3 + pt.xs4 + pt.xn4 + pt.xs5;
\r
16490 str = pt.xs0 + val + pt.xs1;
\r
16491 for (i = 1; i < pt.l; i++) {
\r
16492 str += pt["xn"+i] + pt["xs"+(i+1)];
\r
16494 pt.t[pt.p] = str;
\r
16497 } else if (pt.type === -1) { //non-tweening value
\r
16498 pt.t[pt.p] = pt.xs0;
\r
16500 } else if (pt.setRatio) { //custom setRatio() for things like SpecialProps, external plugins, etc.
\r
16506 //if the tween is reversed all the way back to the beginning, we need to restore the original values which may have different units (like % instead of px or em or whatever).
\r
16509 if (pt.type !== 2) {
\r
16510 pt.t[pt.p] = pt.b;
\r
16521 * Forces rendering of the target's transforms (rotation, scale, etc.) whenever the CSSPlugin's setRatio() is called.
\r
16522 * Basically, this tells the CSSPlugin to create a CSSPropTween (type 2) after instantiation that runs last in the linked
\r
16523 * list and calls the appropriate (3D or 2D) rendering function. We separate this into its own method so that we can call
\r
16524 * it from other plugins like BezierPlugin if, for example, it needs to apply an autoRotation and this CSSPlugin
\r
16525 * doesn't have any transform-related properties of its own. You can call this method as many times as you
\r
16526 * want and it won't create duplicate CSSPropTweens.
\r
16528 * @param {boolean} threeD if true, it should apply 3D tweens (otherwise, just 2D ones are fine and typically faster)
\r
16530 p._enableTransforms = function(threeD) {
\r
16531 this._transformType = (threeD || this._transformType === 3) ? 3 : 2;
\r
16532 this._transform = this._transform || _getTransform(this._target, _cs, true); //ensures that the element has a _gsTransform property with the appropriate values.
\r
16535 var lazySet = function(v) {
\r
16536 this.t[this.p] = this.e;
\r
16537 this.data._linkCSSP(this, this._next, null, true); //we purposefully keep this._next even though it'd make sense to null it, but this is a performance optimization, as this happens during the while (pt) {} loop in setRatio() at the bottom of which it sets pt = pt._next, so if we null it, the linked list will be broken in that loop.
\r
16539 /** @private Gives us a way to set a value on the first render (and only the first render). **/
\r
16540 p._addLazySet = function(t, p, v) {
\r
16541 var pt = this._firstPT = new CSSPropTween(t, p, 0, 0, this._firstPT, 2);
\r
16543 pt.setRatio = lazySet;
\r
16548 p._linkCSSP = function(pt, next, prev, remove) {
\r
16554 pt._next._prev = pt._prev;
\r
16557 pt._prev._next = pt._next;
\r
16558 } else if (this._firstPT === pt) {
\r
16559 this._firstPT = pt._next;
\r
16560 remove = true; //just to prevent resetting this._firstPT 5 lines down in case pt._next is null. (optimized for speed)
\r
16564 } else if (!remove && this._firstPT === null) {
\r
16565 this._firstPT = pt;
\r
16573 //we need to make sure that if alpha or autoAlpha is killed, opacity is too. And autoAlpha affects the "visibility" property.
\r
16574 p._kill = function(lookup) {
\r
16575 var copy = lookup,
\r
16577 if (lookup.autoAlpha || lookup.alpha) {
\r
16579 for (p in lookup) { //copy the lookup so that we're not changing the original which may be passed elsewhere.
\r
16580 copy[p] = lookup[p];
\r
16582 copy.opacity = 1;
\r
16583 if (copy.autoAlpha) {
\r
16584 copy.visibility = 1;
\r
16587 if (lookup.className && (pt = this._classNamePT)) { //for className tweens, we need to kill any associated CSSPropTweens too; a linked list starts at the className's "xfirst".
\r
16588 xfirst = pt.xfirst;
\r
16589 if (xfirst && xfirst._prev) {
\r
16590 this._linkCSSP(xfirst._prev, pt._next, xfirst._prev._prev); //break off the prev
\r
16591 } else if (xfirst === this._firstPT) {
\r
16592 this._firstPT = pt._next;
\r
16595 this._linkCSSP(pt._next, pt._next._next, xfirst._prev);
\r
16597 this._classNamePT = null;
\r
16599 return TweenPlugin.prototype._kill.call(this, copy);
\r
16604 //used by cascadeTo() for gathering all the style properties of each child element into an array for comparison.
\r
16605 var _getChildStyles = function(e, props, targets) {
\r
16606 var children, i, child, type;
\r
16609 while (--i > -1) {
\r
16610 _getChildStyles(e[i], props, targets);
\r
16614 children = e.childNodes;
\r
16615 i = children.length;
\r
16616 while (--i > -1) {
\r
16617 child = children[i];
\r
16618 type = child.type;
\r
16619 if (child.style) {
\r
16620 props.push(_getAllStyles(child));
\r
16622 targets.push(child);
\r
16625 if ((type === 1 || type === 9 || type === 11) && child.childNodes.length) {
\r
16626 _getChildStyles(child, props, targets);
\r
16632 * Typically only useful for className tweens that may affect child elements, this method creates a TweenLite
\r
16633 * and then compares the style properties of all the target's child elements at the tween's start and end, and
\r
16634 * if any are different, it also creates tweens for those and returns an array containing ALL of the resulting
\r
16635 * tweens (so that you can easily add() them to a TimelineLite, for example). The reason this functionality is
\r
16636 * wrapped into a separate static method of CSSPlugin instead of being integrated into all regular className tweens
\r
16637 * is because it creates entirely new tweens that may have completely different targets than the original tween,
\r
16638 * so if they were all lumped into the original tween instance, it would be inconsistent with the rest of the API
\r
16639 * and it would create other problems. For example:
\r
16640 * - If I create a tween of elementA, that tween instance may suddenly change its target to include 50 other elements (unintuitive if I specifically defined the target I wanted)
\r
16641 * - We can't just create new independent tweens because otherwise, what happens if the original/parent tween is reversed or pause or dropped into a TimelineLite for tight control? You'd expect that tween's behavior to affect all the others.
\r
16642 * - Analyzing every style property of every child before and after the tween is an expensive operation when there are many children, so this behavior shouldn't be imposed on all className tweens by default, especially since it's probably rare that this extra functionality is needed.
\r
16644 * @param {Object} target object to be tweened
\r
16645 * @param {number} Duration in seconds (or frames for frames-based tweens)
\r
16646 * @param {Object} Object containing the end values, like {className:"newClass", ease:Linear.easeNone}
\r
16647 * @return {Array} An array of TweenLite instances
\r
16649 CSSPlugin.cascadeTo = function(target, duration, vars) {
\r
16650 var tween = TweenLite.to(target, duration, vars),
\r
16651 results = [tween],
\r
16655 _reservedProps = TweenLite._internals.reservedProps,
\r
16657 target = tween._targets || tween.target;
\r
16658 _getChildStyles(target, b, targets);
\r
16659 tween.render(duration, true);
\r
16660 _getChildStyles(target, e);
\r
16661 tween.render(0, true);
\r
16662 tween._enabled(true);
\r
16663 i = targets.length;
\r
16664 while (--i > -1) {
\r
16665 difs = _cssDif(targets[i], b[i], e[i]);
\r
16666 if (difs.firstMPT) {
\r
16667 difs = difs.difs;
\r
16668 for (p in vars) {
\r
16669 if (_reservedProps[p]) {
\r
16670 difs[p] = vars[p];
\r
16673 results.push( TweenLite.to(targets[i], duration, difs) );
\r
16679 TweenPlugin.activate([CSSPlugin]);
\r
16680 return CSSPlugin;
\r
16695 * ----------------------------------------------------------------
\r
16696 * RoundPropsPlugin
\r
16697 * ----------------------------------------------------------------
\r
16701 var RoundPropsPlugin = window._gsDefine.plugin({
\r
16702 propName: "roundProps",
\r
16706 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
\r
16707 init: function(target, value, tween) {
\r
16708 this._tween = tween;
\r
16713 p = RoundPropsPlugin.prototype;
\r
16715 p._onInitAllProps = function() {
\r
16716 var tween = this._tween,
\r
16717 rp = (tween.vars.roundProps instanceof Array) ? tween.vars.roundProps : tween.vars.roundProps.split(","),
\r
16720 rpt = tween._propLookup.roundProps,
\r
16722 while (--i > -1) {
\r
16723 lookup[rp[i]] = 1;
\r
16726 while (--i > -1) {
\r
16728 pt = tween._firstPT;
\r
16730 next = pt._next; //record here, because it may get removed
\r
16732 pt.t._roundProps(lookup, true);
\r
16733 } else if (pt.n === prop) {
\r
16734 this._add(pt.t, prop, pt.s, pt.c);
\r
16735 //remove from linked list
\r
16737 next._prev = pt._prev;
\r
16740 pt._prev._next = next;
\r
16741 } else if (tween._firstPT === pt) {
\r
16742 tween._firstPT = next;
\r
16744 pt._next = pt._prev = null;
\r
16745 tween._propLookup[prop] = rpt;
\r
16753 p._add = function(target, p, s, c) {
\r
16754 this._addTween(target, p, s, s + c, p, true);
\r
16755 this._overwriteProps.push(p);
\r
16770 * ----------------------------------------------------------------
\r
16772 * ----------------------------------------------------------------
\r
16774 window._gsDefine.plugin({
\r
16775 propName: "attr",
\r
16777 version: "0.3.2",
\r
16779 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
\r
16780 init: function(target, value, tween) {
\r
16781 var p, start, end;
\r
16782 if (typeof(target.setAttribute) !== "function") {
\r
16785 this._target = target;
\r
16786 this._proxy = {};
\r
16787 this._start = {}; // we record start and end values exactly as they are in case they're strings (not numbers) - we need to be able to revert to them cleanly.
\r
16789 for (p in value) {
\r
16790 this._start[p] = this._proxy[p] = start = target.getAttribute(p);
\r
16791 end = this._addTween(this._proxy, p, parseFloat(start), value[p], p);
\r
16792 this._end[p] = end ? end.s + end.c : value[p];
\r
16793 this._overwriteProps.push(p);
\r
16798 //called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
\r
16799 set: function(ratio) {
\r
16800 this._super.setRatio.call(this, ratio);
\r
16801 var props = this._overwriteProps,
\r
16802 i = props.length,
\r
16803 lookup = (ratio === 1) ? this._end : ratio ? this._proxy : this._start,
\r
16805 while (--i > -1) {
\r
16807 this._target.setAttribute(p, lookup[p] + "");
\r
16823 * ----------------------------------------------------------------
\r
16824 * DirectionalRotationPlugin
\r
16825 * ----------------------------------------------------------------
\r
16827 window._gsDefine.plugin({
\r
16828 propName: "directionalRotation",
\r
16830 version: "0.2.0",
\r
16832 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
\r
16833 init: function(target, value, tween) {
\r
16834 if (typeof(value) !== "object") {
\r
16835 value = {rotation:value};
\r
16837 this.finals = {};
\r
16838 var cap = (value.useRadians === true) ? Math.PI * 2 : 360,
\r
16840 p, v, start, end, dif, split;
\r
16841 for (p in value) {
\r
16842 if (p !== "useRadians") {
\r
16843 split = (value[p] + "").split("_");
\r
16845 start = parseFloat( (typeof(target[p]) !== "function") ? target[p] : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]() );
\r
16846 end = this.finals[p] = (typeof(v) === "string" && v.charAt(1) === "=") ? start + parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) : Number(v) || 0;
\r
16847 dif = end - start;
\r
16848 if (split.length) {
\r
16849 v = split.join("_");
\r
16850 if (v.indexOf("short") !== -1) {
\r
16852 if (dif !== dif % (cap / 2)) {
\r
16853 dif = (dif < 0) ? dif + cap : dif - cap;
\r
16856 if (v.indexOf("_cw") !== -1 && dif < 0) {
\r
16857 dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
\r
16858 } else if (v.indexOf("ccw") !== -1 && dif > 0) {
\r
16859 dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
\r
16862 if (dif > min || dif < -min) {
\r
16863 this._addTween(target, p, start, start + dif, p);
\r
16864 this._overwriteProps.push(p);
\r
16871 //called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
\r
16872 set: function(ratio) {
\r
16874 if (ratio !== 1) {
\r
16875 this._super.setRatio.call(this, ratio);
\r
16877 pt = this._firstPT;
\r
16880 pt.t[pt.p](this.finals[pt.p]);
\r
16882 pt.t[pt.p] = this.finals[pt.p];
\r
16889 })._autoCSS = true;
\r
16902 * ----------------------------------------------------------------
\r
16904 * ----------------------------------------------------------------
\r
16906 window._gsDefine("easing.Back", ["easing.Ease"], function(Ease) {
\r
16908 var w = (window.GreenSockGlobals || window),
\r
16909 gs = w.com.greensock,
\r
16910 _2PI = Math.PI * 2,
\r
16911 _HALF_PI = Math.PI / 2,
\r
16912 _class = gs._class,
\r
16913 _create = function(n, f) {
\r
16914 var C = _class("easing." + n, function(){}, true),
\r
16915 p = C.prototype = new Ease();
\r
16916 p.constructor = C;
\r
16920 _easeReg = Ease.register || function(){}, //put an empty function in place just as a safety measure in case someone loads an OLD version of TweenLite.js where Ease.register doesn't exist.
\r
16921 _wrap = function(name, EaseOut, EaseIn, EaseInOut, aliases) {
\r
16922 var C = _class("easing."+name, {
\r
16923 easeOut:new EaseOut(),
\r
16924 easeIn:new EaseIn(),
\r
16925 easeInOut:new EaseInOut()
\r
16927 _easeReg(C, name);
\r
16930 EasePoint = function(time, value, next) {
\r
16934 this.next = next;
\r
16935 next.prev = this;
\r
16936 this.c = next.v - value;
\r
16937 this.gap = next.t - time;
\r
16942 _createBack = function(n, f) {
\r
16943 var C = _class("easing." + n, function(overshoot) {
\r
16944 this._p1 = (overshoot || overshoot === 0) ? overshoot : 1.70158;
\r
16945 this._p2 = this._p1 * 1.525;
\r
16947 p = C.prototype = new Ease();
\r
16948 p.constructor = C;
\r
16950 p.config = function(overshoot) {
\r
16951 return new C(overshoot);
\r
16956 Back = _wrap("Back",
\r
16957 _createBack("BackOut", function(p) {
\r
16958 return ((p = p - 1) * p * ((this._p1 + 1) * p + this._p1) + 1);
\r
16960 _createBack("BackIn", function(p) {
\r
16961 return p * p * ((this._p1 + 1) * p - this._p1);
\r
16963 _createBack("BackInOut", function(p) {
\r
16964 return ((p *= 2) < 1) ? 0.5 * p * p * ((this._p2 + 1) * p - this._p2) : 0.5 * ((p -= 2) * p * ((this._p2 + 1) * p + this._p2) + 2);
\r
16970 SlowMo = _class("easing.SlowMo", function(linearRatio, power, yoyoMode) {
\r
16971 power = (power || power === 0) ? power : 0.7;
\r
16972 if (linearRatio == null) {
\r
16973 linearRatio = 0.7;
\r
16974 } else if (linearRatio > 1) {
\r
16977 this._p = (linearRatio !== 1) ? power : 0;
\r
16978 this._p1 = (1 - linearRatio) / 2;
\r
16979 this._p2 = linearRatio;
\r
16980 this._p3 = this._p1 + this._p2;
\r
16981 this._calcEnd = (yoyoMode === true);
\r
16983 p = SlowMo.prototype = new Ease(),
\r
16984 SteppedEase, RoughEase, _createElastic;
\r
16986 p.constructor = SlowMo;
\r
16987 p.getRatio = function(p) {
\r
16988 var r = p + (0.5 - p) * this._p;
\r
16989 if (p < this._p1) {
\r
16990 return this._calcEnd ? 1 - ((p = 1 - (p / this._p1)) * p) : r - ((p = 1 - (p / this._p1)) * p * p * p * r);
\r
16991 } else if (p > this._p3) {
\r
16992 return this._calcEnd ? 1 - (p = (p - this._p3) / this._p1) * p : r + ((p - r) * (p = (p - this._p3) / this._p1) * p * p * p);
\r
16994 return this._calcEnd ? 1 : r;
\r
16996 SlowMo.ease = new SlowMo(0.7, 0.7);
\r
16998 p.config = SlowMo.config = function(linearRatio, power, yoyoMode) {
\r
16999 return new SlowMo(linearRatio, power, yoyoMode);
\r
17004 SteppedEase = _class("easing.SteppedEase", function(steps) {
\r
17005 steps = steps || 1;
\r
17006 this._p1 = 1 / steps;
\r
17007 this._p2 = steps + 1;
\r
17009 p = SteppedEase.prototype = new Ease();
\r
17010 p.constructor = SteppedEase;
\r
17011 p.getRatio = function(p) {
\r
17014 } else if (p >= 1) {
\r
17017 return ((this._p2 * p) >> 0) * this._p1;
\r
17019 p.config = SteppedEase.config = function(steps) {
\r
17020 return new SteppedEase(steps);
\r
17025 RoughEase = _class("easing.RoughEase", function(vars) {
\r
17026 vars = vars || {};
\r
17027 var taper = vars.taper || "none",
\r
17030 points = (vars.points || 20) | 0,
\r
17032 randomize = (vars.randomize !== false),
\r
17033 clamp = (vars.clamp === true),
\r
17034 template = (vars.template instanceof Ease) ? vars.template : null,
\r
17035 strength = (typeof(vars.strength) === "number") ? vars.strength * 0.4 : 0.4,
\r
17036 x, y, bump, invX, obj, pnt;
\r
17037 while (--i > -1) {
\r
17038 x = randomize ? Math.random() : (1 / points) * i;
\r
17039 y = template ? template.getRatio(x) : x;
\r
17040 if (taper === "none") {
\r
17042 } else if (taper === "out") {
\r
17044 bump = invX * invX * strength;
\r
17045 } else if (taper === "in") {
\r
17046 bump = x * x * strength;
\r
17047 } else if (x < 0.5) { //"both" (start)
\r
17049 bump = invX * invX * 0.5 * strength;
\r
17050 } else { //"both" (end)
\r
17051 invX = (1 - x) * 2;
\r
17052 bump = invX * invX * 0.5 * strength;
\r
17055 y += (Math.random() * bump) - (bump * 0.5);
\r
17056 } else if (i % 2) {
\r
17064 } else if (y < 0) {
\r
17068 a[cnt++] = {x:x, y:y};
\r
17070 a.sort(function(a, b) {
\r
17071 return a.x - b.x;
\r
17074 pnt = new EasePoint(1, 1, null);
\r
17076 while (--i > -1) {
\r
17078 pnt = new EasePoint(obj.x, obj.y, pnt);
\r
17081 this._prev = new EasePoint(0, 0, (pnt.t !== 0) ? pnt : pnt.next);
\r
17083 p = RoughEase.prototype = new Ease();
\r
17084 p.constructor = RoughEase;
\r
17085 p.getRatio = function(p) {
\r
17086 var pnt = this._prev;
\r
17088 while (pnt.next && p >= pnt.t) {
\r
17093 while (pnt.prev && p <= pnt.t) {
\r
17097 this._prev = pnt;
\r
17098 return (pnt.v + ((p - pnt.t) / pnt.gap) * pnt.c);
\r
17100 p.config = function(vars) {
\r
17101 return new RoughEase(vars);
\r
17103 RoughEase.ease = new RoughEase();
\r
17108 _create("BounceOut", function(p) {
\r
17109 if (p < 1 / 2.75) {
\r
17110 return 7.5625 * p * p;
\r
17111 } else if (p < 2 / 2.75) {
\r
17112 return 7.5625 * (p -= 1.5 / 2.75) * p + 0.75;
\r
17113 } else if (p < 2.5 / 2.75) {
\r
17114 return 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375;
\r
17116 return 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375;
\r
17118 _create("BounceIn", function(p) {
\r
17119 if ((p = 1 - p) < 1 / 2.75) {
\r
17120 return 1 - (7.5625 * p * p);
\r
17121 } else if (p < 2 / 2.75) {
\r
17122 return 1 - (7.5625 * (p -= 1.5 / 2.75) * p + 0.75);
\r
17123 } else if (p < 2.5 / 2.75) {
\r
17124 return 1 - (7.5625 * (p -= 2.25 / 2.75) * p + 0.9375);
\r
17126 return 1 - (7.5625 * (p -= 2.625 / 2.75) * p + 0.984375);
\r
17128 _create("BounceInOut", function(p) {
\r
17129 var invert = (p < 0.5);
\r
17135 if (p < 1 / 2.75) {
\r
17136 p = 7.5625 * p * p;
\r
17137 } else if (p < 2 / 2.75) {
\r
17138 p = 7.5625 * (p -= 1.5 / 2.75) * p + 0.75;
\r
17139 } else if (p < 2.5 / 2.75) {
\r
17140 p = 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375;
\r
17142 p = 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375;
\r
17144 return invert ? (1 - p) * 0.5 : p * 0.5 + 0.5;
\r
17151 _create("CircOut", function(p) {
\r
17152 return Math.sqrt(1 - (p = p - 1) * p);
\r
17154 _create("CircIn", function(p) {
\r
17155 return -(Math.sqrt(1 - (p * p)) - 1);
\r
17157 _create("CircInOut", function(p) {
\r
17158 return ((p*=2) < 1) ? -0.5 * (Math.sqrt(1 - p * p) - 1) : 0.5 * (Math.sqrt(1 - (p -= 2) * p) + 1);
\r
17164 _createElastic = function(n, f, def) {
\r
17165 var C = _class("easing." + n, function(amplitude, period) {
\r
17166 this._p1 = amplitude || 1;
\r
17167 this._p2 = period || def;
\r
17168 this._p3 = this._p2 / _2PI * (Math.asin(1 / this._p1) || 0);
\r
17170 p = C.prototype = new Ease();
\r
17171 p.constructor = C;
\r
17173 p.config = function(amplitude, period) {
\r
17174 return new C(amplitude, period);
\r
17179 _createElastic("ElasticOut", function(p) {
\r
17180 return this._p1 * Math.pow(2, -10 * p) * Math.sin( (p - this._p3) * _2PI / this._p2 ) + 1;
\r
17182 _createElastic("ElasticIn", function(p) {
\r
17183 return -(this._p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - this._p3) * _2PI / this._p2 ));
\r
17185 _createElastic("ElasticInOut", function(p) {
\r
17186 return ((p *= 2) < 1) ? -0.5 * (this._p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - this._p3) * _2PI / this._p2)) : this._p1 * Math.pow(2, -10 *(p -= 1)) * Math.sin( (p - this._p3) * _2PI / this._p2 ) *0.5 + 1;
\r
17193 _create("ExpoOut", function(p) {
\r
17194 return 1 - Math.pow(2, -10 * p);
\r
17196 _create("ExpoIn", function(p) {
\r
17197 return Math.pow(2, 10 * (p - 1)) - 0.001;
\r
17199 _create("ExpoInOut", function(p) {
\r
17200 return ((p *= 2) < 1) ? 0.5 * Math.pow(2, 10 * (p - 1)) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
\r
17207 _create("SineOut", function(p) {
\r
17208 return Math.sin(p * _HALF_PI);
\r
17210 _create("SineIn", function(p) {
\r
17211 return -Math.cos(p * _HALF_PI) + 1;
\r
17213 _create("SineInOut", function(p) {
\r
17214 return -0.5 * (Math.cos(Math.PI * p) - 1);
\r
17218 _class("easing.EaseLookup", {
\r
17219 find:function(s) {
\r
17220 return Ease.map[s];
\r
17224 //register the non-standard eases
\r
17225 _easeReg(w.SlowMo, "SlowMo", "ease,");
\r
17226 _easeReg(RoughEase, "RoughEase", "ease,");
\r
17227 _easeReg(SteppedEase, "SteppedEase", "ease,");
\r
17247 * ----------------------------------------------------------------
\r
17248 * Base classes like TweenLite, SimpleTimeline, Ease, Ticker, etc.
\r
17249 * ----------------------------------------------------------------
\r
17251 (function(window) {
\r
17254 var _globals = window.GreenSockGlobals || window;
\r
17255 if (_globals.TweenLite) {
\r
17256 return; //in case the core set of classes is already loaded, don't instantiate twice.
\r
17258 var _namespace = function(ns) {
\r
17259 var a = ns.split("."),
\r
17261 for (i = 0; i < a.length; i++) {
\r
17262 p[a[i]] = p = p[a[i]] || {};
\r
17266 gs = _namespace("com.greensock"),
\r
17267 _tinyNum = 0.0000000001,
\r
17268 _slice = [].slice,
\r
17269 _emptyFunc = function() {},
\r
17270 _isArray = (function() { //works around issues in iframe environments where the Array global isn't shared, thus if the object originates in a different window/iframe, "(obj instanceof Array)" will evaluate false. We added some speed optimizations to avoid Object.prototype.toString.call() unless it's absolutely necessary because it's VERY slow (like 20x slower)
\r
17271 var toString = Object.prototype.toString,
\r
17272 array = toString.call([]);
\r
17273 return function(obj) {
\r
17274 return obj != null && (obj instanceof Array || (typeof(obj) === "object" && !!obj.push && toString.call(obj) === array));
\r
17277 a, i, p, _ticker, _tickerActive,
\r
17282 * Defines a GreenSock class, optionally with an array of dependencies that must be instantiated first and passed into the definition.
\r
17283 * This allows users to load GreenSock JS files in any order even if they have interdependencies (like CSSPlugin extends TweenPlugin which is
\r
17284 * inside TweenLite.js, but if CSSPlugin is loaded first, it should wait to run its code until TweenLite.js loads and instantiates TweenPlugin
\r
17285 * and then pass TweenPlugin to CSSPlugin's definition). This is all done automatically and internally.
\r
17287 * Every definition will be added to a "com.greensock" global object (typically window, but if a window.GreenSockGlobals object is found,
\r
17288 * it will go there as of v1.7). For example, TweenLite will be found at window.com.greensock.TweenLite and since it's a global class that should be available anywhere,
\r
17289 * it is ALSO referenced at window.TweenLite. However some classes aren't considered global, like the base com.greensock.core.Animation class, so
\r
17290 * those will only be at the package like window.com.greensock.core.Animation. Again, if you define a GreenSockGlobals object on the window, everything
\r
17291 * gets tucked neatly inside there instead of on the window directly. This allows you to do advanced things like load multiple versions of GreenSock
\r
17292 * files and put them into distinct objects (imagine a banner ad uses a newer version but the main site uses an older one). In that case, you could
\r
17293 * sandbox the banner one like:
\r
17296 * var gs = window.GreenSockGlobals = {}; //the newer version we're about to load could now be referenced in a "gs" object, like gs.TweenLite.to(...). Use whatever alias you want as long as it's unique, "gs" or "banner" or whatever.
\r
17298 * <script src="js/greensock/v1.7/TweenMax.js"></script>
\r
17300 * window.GreenSockGlobals = null; //reset it back to null so that the next load of TweenMax affects the window and we can reference things directly like TweenLite.to(...)
\r
17302 * <script src="js/greensock/v1.6/TweenMax.js"></script>
\r
17304 * gs.TweenLite.to(...); //would use v1.7
\r
17305 * TweenLite.to(...); //would use v1.6
\r
17308 * @param {!string} ns The namespace of the class definition, leaving off "com.greensock." as that's assumed. For example, "TweenLite" or "plugins.CSSPlugin" or "easing.Back".
\r
17309 * @param {!Array.<string>} dependencies An array of dependencies (described as their namespaces minus "com.greensock." prefix). For example ["TweenLite","plugins.TweenPlugin","core.Animation"]
\r
17310 * @param {!function():Object} func The function that should be called and passed the resolved dependencies which will return the actual class for this definition.
\r
17311 * @param {boolean=} global If true, the class will be added to the global scope (typically window unless you define a window.GreenSockGlobals object)
\r
17313 Definition = function(ns, dependencies, func, global) {
\r
17314 this.sc = (_defLookup[ns]) ? _defLookup[ns].sc : []; //subclasses
\r
17315 _defLookup[ns] = this;
\r
17316 this.gsClass = null;
\r
17317 this.func = func;
\r
17318 var _classes = [];
\r
17319 this.check = function(init) {
\r
17320 var i = dependencies.length,
\r
17323 while (--i > -1) {
\r
17324 if ((cur = _defLookup[dependencies[i]] || new Definition(dependencies[i], [])).gsClass) {
\r
17325 _classes[i] = cur.gsClass;
\r
17327 } else if (init) {
\r
17328 cur.sc.push(this);
\r
17331 if (missing === 0 && func) {
\r
17332 a = ("com.greensock." + ns).split(".");
\r
17334 cl = _namespace(a.join("."))[n] = this.gsClass = func.apply(func, _classes);
\r
17336 //exports to multiple environments
\r
17338 _globals[n] = cl; //provides a way to avoid global namespace pollution. By default, the main classes like TweenLite, Power1, Strong, etc. are added to window unless a GreenSockGlobals is defined. So if you want to have things added to a custom object instead, just do something like window.GreenSockGlobals = {} before loading any GreenSock files. You can even set up an alias like window.GreenSockGlobals = windows.gs = {} so that you can access everything like gs.TweenLite. Also remember that ALL classes are added to the window.com.greensock object (in their respective packages, like com.greensock.easing.Power1, com.greensock.TweenLite, etc.)
\r
17339 if (typeof(define) === "function" && define.amd){ //AMD
\r
17340 define((window.GreenSockAMDPath ? window.GreenSockAMDPath + "/" : "") + ns.split(".").join("/"), [], function() { return cl; });
\r
17341 } else if (typeof(module) !== "undefined" && module.exports){ //node
\r
17342 module.exports = cl;
\r
17345 for (i = 0; i < this.sc.length; i++) {
\r
17346 this.sc[i].check();
\r
17350 this.check(true);
\r
17353 //used to create Definition instances (which basically registers a class that has dependencies).
\r
17354 _gsDefine = window._gsDefine = function(ns, dependencies, func, global) {
\r
17355 return new Definition(ns, dependencies, func, global);
\r
17358 //a quick way to create a class that doesn't have any dependencies. Returns the class, but first registers it in the GreenSock namespace so that other classes can grab it (other classes might be dependent on the class).
\r
17359 _class = gs._class = function(ns, func, global) {
\r
17360 func = func || function() {};
\r
17361 _gsDefine(ns, [], function(){ return func; }, global);
\r
17365 _gsDefine.globals = _globals;
\r
17370 * ----------------------------------------------------------------
\r
17372 * ----------------------------------------------------------------
\r
17374 var _baseParams = [0, 0, 1, 1],
\r
17375 _blankArray = [],
\r
17376 Ease = _class("easing.Ease", function(func, extraParams, type, power) {
\r
17377 this._func = func;
\r
17378 this._type = type || 0;
\r
17379 this._power = power || 0;
\r
17380 this._params = extraParams ? _baseParams.concat(extraParams) : _baseParams;
\r
17382 _easeMap = Ease.map = {},
\r
17383 _easeReg = Ease.register = function(ease, names, types, create) {
\r
17384 var na = names.split(","),
\r
17386 ta = (types || "easeIn,easeOut,easeInOut").split(","),
\r
17387 e, name, j, type;
\r
17388 while (--i > -1) {
\r
17390 e = create ? _class("easing."+name, null, true) : gs.easing[name] || {};
\r
17392 while (--j > -1) {
\r
17394 _easeMap[name + "." + type] = _easeMap[type + name] = e[type] = ease.getRatio ? ease : ease[type] || new ease();
\r
17399 p = Ease.prototype;
\r
17400 p._calcEnd = false;
\r
17401 p.getRatio = function(p) {
\r
17402 if (this._func) {
\r
17403 this._params[0] = p;
\r
17404 return this._func.apply(null, this._params);
\r
17406 var t = this._type,
\r
17407 pw = this._power,
\r
17408 r = (t === 1) ? 1 - p : (t === 2) ? p : (p < 0.5) ? p * 2 : (1 - p) * 2;
\r
17411 } else if (pw === 2) {
\r
17413 } else if (pw === 3) {
\r
17415 } else if (pw === 4) {
\r
17416 r *= r * r * r * r;
\r
17418 return (t === 1) ? 1 - r : (t === 2) ? r : (p < 0.5) ? r / 2 : 1 - (r / 2);
\r
17421 //create all the standard eases like Linear, Quad, Cubic, Quart, Quint, Strong, Power0, Power1, Power2, Power3, and Power4 (each with easeIn, easeOut, and easeInOut)
\r
17422 a = ["Linear","Quad","Cubic","Quart","Quint,Strong"];
\r
17424 while (--i > -1) {
\r
17425 p = a[i]+",Power"+i;
\r
17426 _easeReg(new Ease(null,null,1,i), p, "easeOut", true);
\r
17427 _easeReg(new Ease(null,null,2,i), p, "easeIn" + ((i === 0) ? ",easeNone" : ""));
\r
17428 _easeReg(new Ease(null,null,3,i), p, "easeInOut");
\r
17430 _easeMap.linear = gs.easing.Linear.easeIn;
\r
17431 _easeMap.swing = gs.easing.Quad.easeInOut; //for jQuery folks
\r
17435 * ----------------------------------------------------------------
\r
17436 * EventDispatcher
\r
17437 * ----------------------------------------------------------------
\r
17439 var EventDispatcher = _class("events.EventDispatcher", function(target) {
\r
17440 this._listeners = {};
\r
17441 this._eventTarget = target || this;
\r
17443 p = EventDispatcher.prototype;
\r
17445 p.addEventListener = function(type, callback, scope, useParam, priority) {
\r
17446 priority = priority || 0;
\r
17447 var list = this._listeners[type],
\r
17450 if (list == null) {
\r
17451 this._listeners[type] = list = [];
\r
17454 while (--i > -1) {
\r
17455 listener = list[i];
\r
17456 if (listener.c === callback && listener.s === scope) {
\r
17457 list.splice(i, 1);
\r
17458 } else if (index === 0 && listener.pr < priority) {
\r
17462 list.splice(index, 0, {c:callback, s:scope, up:useParam, pr:priority});
\r
17463 if (this === _ticker && !_tickerActive) {
\r
17468 p.removeEventListener = function(type, callback) {
\r
17469 var list = this._listeners[type], i;
\r
17472 while (--i > -1) {
\r
17473 if (list[i].c === callback) {
\r
17474 list.splice(i, 1);
\r
17481 p.dispatchEvent = function(type) {
\r
17482 var list = this._listeners[type],
\r
17486 t = this._eventTarget;
\r
17487 while (--i > -1) {
\r
17488 listener = list[i];
\r
17489 if (listener.up) {
\r
17490 listener.c.call(listener.s || t, {type:type, target:t});
\r
17492 listener.c.call(listener.s || t);
\r
17500 * ----------------------------------------------------------------
\r
17502 * ----------------------------------------------------------------
\r
17504 var _reqAnimFrame = window.requestAnimationFrame,
\r
17505 _cancelAnimFrame = window.cancelAnimationFrame,
\r
17506 _getTime = Date.now || function() {return new Date().getTime();},
\r
17507 _lastUpdate = _getTime();
\r
17509 //now try to determine the requestAnimationFrame and cancelAnimationFrame functions and if none are found, we'll use a setTimeout()/clearTimeout() polyfill.
\r
17510 a = ["ms","moz","webkit","o"];
\r
17512 while (--i > -1 && !_reqAnimFrame) {
\r
17513 _reqAnimFrame = window[a[i] + "RequestAnimationFrame"];
\r
17514 _cancelAnimFrame = window[a[i] + "CancelAnimationFrame"] || window[a[i] + "CancelRequestAnimationFrame"];
\r
17517 _class("Ticker", function(fps, useRAF) {
\r
17518 var _self = this,
\r
17519 _startTime = _getTime(),
\r
17520 _useRAF = (useRAF !== false && _reqAnimFrame),
\r
17521 _lagThreshold = 500,
\r
17522 _adjustedLag = 33,
\r
17523 _fps, _req, _id, _gap, _nextTime,
\r
17524 _tick = function(manual) {
\r
17525 var elapsed = _getTime() - _lastUpdate,
\r
17526 overlap, dispatch;
\r
17527 if (elapsed > _lagThreshold) {
\r
17528 _startTime += elapsed - _adjustedLag;
\r
17530 _lastUpdate += elapsed;
\r
17531 _self.time = (_lastUpdate - _startTime) / 1000;
\r
17532 overlap = _self.time - _nextTime;
\r
17533 if (!_fps || overlap > 0 || manual === true) {
\r
17535 _nextTime += overlap + (overlap >= _gap ? 0.004 : _gap - overlap);
\r
17538 if (manual !== true) { //make sure the request is made before we dispatch the "tick" event so that timing is maintained. Otherwise, if processing the "tick" requires a bunch of time (like 15ms) and we're using a setTimeout() that's based on 16.7ms, it'd technically take 31.7ms between frames otherwise.
\r
17539 _id = _req(_tick);
\r
17542 _self.dispatchEvent("tick");
\r
17546 EventDispatcher.call(_self);
\r
17547 _self.time = _self.frame = 0;
\r
17548 _self.tick = function() {
\r
17552 _self.lagSmoothing = function(threshold, adjustedLag) {
\r
17553 _lagThreshold = threshold || (1 / _tinyNum); //zero should be interpreted as basically unlimited
\r
17554 _adjustedLag = Math.min(adjustedLag, _lagThreshold, 0);
\r
17557 _self.sleep = function() {
\r
17558 if (_id == null) {
\r
17561 if (!_useRAF || !_cancelAnimFrame) {
\r
17562 clearTimeout(_id);
\r
17564 _cancelAnimFrame(_id);
\r
17566 _req = _emptyFunc;
\r
17568 if (_self === _ticker) {
\r
17569 _tickerActive = false;
\r
17573 _self.wake = function() {
\r
17574 if (_id !== null) {
\r
17576 } else if (_self.frame > 10) { //don't trigger lagSmoothing if we're just waking up, and make sure that at least 10 frames have elapsed because of the iOS bug that we work around below with the 1.5-second setTimout().
\r
17577 _lastUpdate = _getTime() - _lagThreshold + 5;
\r
17579 _req = (_fps === 0) ? _emptyFunc : (!_useRAF || !_reqAnimFrame) ? function(f) { return setTimeout(f, ((_nextTime - _self.time) * 1000 + 1) | 0); } : _reqAnimFrame;
\r
17580 if (_self === _ticker) {
\r
17581 _tickerActive = true;
\r
17586 _self.fps = function(value) {
\r
17587 if (!arguments.length) {
\r
17591 _gap = 1 / (_fps || 60);
\r
17592 _nextTime = this.time + _gap;
\r
17596 _self.useRAF = function(value) {
\r
17597 if (!arguments.length) {
\r
17606 //a bug in iOS 6 Safari occasionally prevents the requestAnimationFrame from working initially, so we use a 1.5-second timeout that automatically falls back to setTimeout() if it senses this condition.
\r
17607 setTimeout(function() {
\r
17608 if (_useRAF && (!_id || _self.frame < 5)) {
\r
17609 _self.useRAF(false);
\r
17614 p = gs.Ticker.prototype = new gs.events.EventDispatcher();
\r
17615 p.constructor = gs.Ticker;
\r
17619 * ----------------------------------------------------------------
\r
17621 * ----------------------------------------------------------------
\r
17623 var Animation = _class("core.Animation", function(duration, vars) {
\r
17624 this.vars = vars = vars || {};
\r
17625 this._duration = this._totalDuration = duration || 0;
\r
17626 this._delay = Number(vars.delay) || 0;
\r
17627 this._timeScale = 1;
\r
17628 this._active = (vars.immediateRender === true);
\r
17629 this.data = vars.data;
\r
17630 this._reversed = (vars.reversed === true);
\r
17632 if (!_rootTimeline) {
\r
17635 if (!_tickerActive) { //some browsers (like iOS 6 Safari) shut down JavaScript execution when the tab is disabled and they [occasionally] neglect to start up requestAnimationFrame again when returning - this code ensures that the engine starts up again properly.
\r
17639 var tl = this.vars.useFrames ? _rootFramesTimeline : _rootTimeline;
\r
17640 tl.add(this, tl._time);
\r
17642 if (this.vars.paused) {
\r
17643 this.paused(true);
\r
17647 _ticker = Animation.ticker = new gs.Ticker();
\r
17648 p = Animation.prototype;
\r
17649 p._dirty = p._gc = p._initted = p._paused = false;
\r
17650 p._totalTime = p._time = 0;
\r
17651 p._rawPrevTime = -1;
\r
17652 p._next = p._last = p._onUpdate = p._timeline = p.timeline = null;
\r
17653 p._paused = false;
\r
17656 //some browsers (like iOS) occasionally drop the requestAnimationFrame event when the user switches to a different tab and then comes back again, so we use a 2-second setTimeout() to sense if/when that condition occurs and then wake() the ticker.
\r
17657 var _checkTimeout = function() {
\r
17658 if (_tickerActive && _getTime() - _lastUpdate > 2000) {
\r
17661 setTimeout(_checkTimeout, 2000);
\r
17666 p.play = function(from, suppressEvents) {
\r
17667 if (from != null) {
\r
17668 this.seek(from, suppressEvents);
\r
17670 return this.reversed(false).paused(false);
\r
17673 p.pause = function(atTime, suppressEvents) {
\r
17674 if (atTime != null) {
\r
17675 this.seek(atTime, suppressEvents);
\r
17677 return this.paused(true);
\r
17680 p.resume = function(from, suppressEvents) {
\r
17681 if (from != null) {
\r
17682 this.seek(from, suppressEvents);
\r
17684 return this.paused(false);
\r
17687 p.seek = function(time, suppressEvents) {
\r
17688 return this.totalTime(Number(time), suppressEvents !== false);
\r
17691 p.restart = function(includeDelay, suppressEvents) {
\r
17692 return this.reversed(false).paused(false).totalTime(includeDelay ? -this._delay : 0, (suppressEvents !== false), true);
\r
17695 p.reverse = function(from, suppressEvents) {
\r
17696 if (from != null) {
\r
17697 this.seek((from || this.totalDuration()), suppressEvents);
\r
17699 return this.reversed(true).paused(false);
\r
17702 p.render = function(time, suppressEvents, force) {
\r
17703 //stub - we override this method in subclasses.
\r
17706 p.invalidate = function() {
\r
17710 p.isActive = function() {
\r
17711 var tl = this._timeline, //the 2 root timelines won't have a _timeline; they're always active.
\r
17712 startTime = this._startTime,
\r
17714 return (!tl || (!this._gc && !this._paused && tl.isActive() && (rawTime = tl.rawTime()) >= startTime && rawTime < startTime + this.totalDuration() / this._timeScale));
\r
17717 p._enabled = function (enabled, ignoreTimeline) {
\r
17718 if (!_tickerActive) {
\r
17721 this._gc = !enabled;
\r
17722 this._active = this.isActive();
\r
17723 if (ignoreTimeline !== true) {
\r
17724 if (enabled && !this.timeline) {
\r
17725 this._timeline.add(this, this._startTime - this._delay);
\r
17726 } else if (!enabled && this.timeline) {
\r
17727 this._timeline._remove(this, true);
\r
17734 p._kill = function(vars, target) {
\r
17735 return this._enabled(false, false);
\r
17738 p.kill = function(vars, target) {
\r
17739 this._kill(vars, target);
\r
17743 p._uncache = function(includeSelf) {
\r
17744 var tween = includeSelf ? this : this.timeline;
\r
17746 tween._dirty = true;
\r
17747 tween = tween.timeline;
\r
17752 p._swapSelfInParams = function(params) {
\r
17753 var i = params.length,
\r
17754 copy = params.concat();
\r
17755 while (--i > -1) {
\r
17756 if (params[i] === "{self}") {
\r
17763 //----Animation getters/setters --------------------------------------------------------
\r
17765 p.eventCallback = function(type, callback, params, scope) {
\r
17766 if ((type || "").substr(0,2) === "on") {
\r
17767 var v = this.vars;
\r
17768 if (arguments.length === 1) {
\r
17771 if (callback == null) {
\r
17774 v[type] = callback;
\r
17775 v[type + "Params"] = (_isArray(params) && params.join("").indexOf("{self}") !== -1) ? this._swapSelfInParams(params) : params;
\r
17776 v[type + "Scope"] = scope;
\r
17778 if (type === "onUpdate") {
\r
17779 this._onUpdate = callback;
\r
17785 p.delay = function(value) {
\r
17786 if (!arguments.length) {
\r
17787 return this._delay;
\r
17789 if (this._timeline.smoothChildTiming) {
\r
17790 this.startTime( this._startTime + value - this._delay );
\r
17792 this._delay = value;
\r
17796 p.duration = function(value) {
\r
17797 if (!arguments.length) {
\r
17798 this._dirty = false;
\r
17799 return this._duration;
\r
17801 this._duration = this._totalDuration = value;
\r
17802 this._uncache(true); //true in case it's a TweenMax or TimelineMax that has a repeat - we'll need to refresh the totalDuration.
\r
17803 if (this._timeline.smoothChildTiming) if (this._time > 0) if (this._time < this._duration) if (value !== 0) {
\r
17804 this.totalTime(this._totalTime * (value / this._duration), true);
\r
17809 p.totalDuration = function(value) {
\r
17810 this._dirty = false;
\r
17811 return (!arguments.length) ? this._totalDuration : this.duration(value);
\r
17814 p.time = function(value, suppressEvents) {
\r
17815 if (!arguments.length) {
\r
17816 return this._time;
\r
17818 if (this._dirty) {
\r
17819 this.totalDuration();
\r
17821 return this.totalTime((value > this._duration) ? this._duration : value, suppressEvents);
\r
17824 p.totalTime = function(time, suppressEvents, uncapped) {
\r
17825 if (!_tickerActive) {
\r
17828 if (!arguments.length) {
\r
17829 return this._totalTime;
\r
17831 if (this._timeline) {
\r
17832 if (time < 0 && !uncapped) {
\r
17833 time += this.totalDuration();
\r
17835 if (this._timeline.smoothChildTiming) {
\r
17836 if (this._dirty) {
\r
17837 this.totalDuration();
\r
17839 var totalDuration = this._totalDuration,
\r
17840 tl = this._timeline;
\r
17841 if (time > totalDuration && !uncapped) {
\r
17842 time = totalDuration;
\r
17844 this._startTime = (this._paused ? this._pauseTime : tl._time) - ((!this._reversed ? time : totalDuration - time) / this._timeScale);
\r
17845 if (!tl._dirty) { //for performance improvement. If the parent's cache is already dirty, it already took care of marking the ancestors as dirty too, so skip the function call here.
\r
17846 this._uncache(false);
\r
17848 //in case any of the ancestor timelines had completed but should now be enabled, we should reset their totalTime() which will also ensure that they're lined up properly and enabled. Skip for animations that are on the root (wasteful). Example: a TimelineLite.exportRoot() is performed when there's a paused tween on the root, the export will not complete until that tween is unpaused, but imagine a child gets restarted later, after all [unpaused] tweens have completed. The startTime of that child would get pushed out, but one of the ancestors may have completed.
\r
17849 if (tl._timeline) {
\r
17850 while (tl._timeline) {
\r
17851 if (tl._timeline._time !== (tl._startTime + tl._totalTime) / tl._timeScale) {
\r
17852 tl.totalTime(tl._totalTime, true);
\r
17854 tl = tl._timeline;
\r
17859 this._enabled(true, false);
\r
17861 if (this._totalTime !== time || this._duration === 0) {
\r
17862 this.render(time, suppressEvents, false);
\r
17863 if (_lazyTweens.length) { //in case rendering caused any tweens to lazy-init, we should render them because typically when someone calls seek() or time() or progress(), they expect an immediate render.
\r
17871 p.progress = p.totalProgress = function(value, suppressEvents) {
\r
17872 return (!arguments.length) ? this._time / this.duration() : this.totalTime(this.duration() * value, suppressEvents);
\r
17875 p.startTime = function(value) {
\r
17876 if (!arguments.length) {
\r
17877 return this._startTime;
\r
17879 if (value !== this._startTime) {
\r
17880 this._startTime = value;
\r
17881 if (this.timeline) if (this.timeline._sortChildren) {
\r
17882 this.timeline.add(this, value - this._delay); //ensures that any necessary re-sequencing of Animations in the timeline occurs to make sure the rendering order is correct.
\r
17888 p.timeScale = function(value) {
\r
17889 if (!arguments.length) {
\r
17890 return this._timeScale;
\r
17892 value = value || _tinyNum; //can't allow zero because it'll throw the math off
\r
17893 if (this._timeline && this._timeline.smoothChildTiming) {
\r
17894 var pauseTime = this._pauseTime,
\r
17895 t = (pauseTime || pauseTime === 0) ? pauseTime : this._timeline.totalTime();
\r
17896 this._startTime = t - ((t - this._startTime) * this._timeScale / value);
\r
17898 this._timeScale = value;
\r
17899 return this._uncache(false);
\r
17902 p.reversed = function(value) {
\r
17903 if (!arguments.length) {
\r
17904 return this._reversed;
\r
17906 if (value != this._reversed) {
\r
17907 this._reversed = value;
\r
17908 this.totalTime(((this._timeline && !this._timeline.smoothChildTiming) ? this.totalDuration() - this._totalTime : this._totalTime), true);
\r
17913 p.paused = function(value) {
\r
17914 if (!arguments.length) {
\r
17915 return this._paused;
\r
17917 if (value != this._paused) if (this._timeline) {
\r
17918 if (!_tickerActive && !value) {
\r
17921 var tl = this._timeline,
\r
17922 raw = tl.rawTime(),
\r
17923 elapsed = raw - this._pauseTime;
\r
17924 if (!value && tl.smoothChildTiming) {
\r
17925 this._startTime += elapsed;
\r
17926 this._uncache(false);
\r
17928 this._pauseTime = value ? raw : null;
\r
17929 this._paused = value;
\r
17930 this._active = this.isActive();
\r
17931 if (!value && elapsed !== 0 && this._initted && this.duration()) {
\r
17932 this.render((tl.smoothChildTiming ? this._totalTime : (raw - this._startTime) / this._timeScale), true, true); //in case the target's properties changed via some other tween or manual update by the user, we should force a render.
\r
17935 if (this._gc && !value) {
\r
17936 this._enabled(true, false);
\r
17943 * ----------------------------------------------------------------
\r
17945 * ----------------------------------------------------------------
\r
17947 var SimpleTimeline = _class("core.SimpleTimeline", function(vars) {
\r
17948 Animation.call(this, 0, vars);
\r
17949 this.autoRemoveChildren = this.smoothChildTiming = true;
\r
17952 p = SimpleTimeline.prototype = new Animation();
\r
17953 p.constructor = SimpleTimeline;
\r
17954 p.kill()._gc = false;
\r
17955 p._first = p._last = null;
\r
17956 p._sortChildren = false;
\r
17958 p.add = p.insert = function(child, position, align, stagger) {
\r
17959 var prevTween, st;
\r
17960 child._startTime = Number(position || 0) + child._delay;
\r
17961 if (child._paused) if (this !== child._timeline) { //we only adjust the _pauseTime if it wasn't in this timeline already. Remember, sometimes a tween will be inserted again into the same timeline when its startTime is changed so that the tweens in the TimelineLite/Max are re-ordered properly in the linked list (so everything renders in the proper order).
\r
17962 child._pauseTime = child._startTime + ((this.rawTime() - child._startTime) / child._timeScale);
\r
17964 if (child.timeline) {
\r
17965 child.timeline._remove(child, true); //removes from existing timeline so that it can be properly added to this one.
\r
17967 child.timeline = child._timeline = this;
\r
17969 child._enabled(true, true);
\r
17971 prevTween = this._last;
\r
17972 if (this._sortChildren) {
\r
17973 st = child._startTime;
\r
17974 while (prevTween && prevTween._startTime > st) {
\r
17975 prevTween = prevTween._prev;
\r
17979 child._next = prevTween._next;
\r
17980 prevTween._next = child;
\r
17982 child._next = this._first;
\r
17983 this._first = child;
\r
17985 if (child._next) {
\r
17986 child._next._prev = child;
\r
17988 this._last = child;
\r
17990 child._prev = prevTween;
\r
17991 if (this._timeline) {
\r
17992 this._uncache(true);
\r
17997 p._remove = function(tween, skipDisable) {
\r
17998 if (tween.timeline === this) {
\r
17999 if (!skipDisable) {
\r
18000 tween._enabled(false, true);
\r
18002 tween.timeline = null;
\r
18004 if (tween._prev) {
\r
18005 tween._prev._next = tween._next;
\r
18006 } else if (this._first === tween) {
\r
18007 this._first = tween._next;
\r
18009 if (tween._next) {
\r
18010 tween._next._prev = tween._prev;
\r
18011 } else if (this._last === tween) {
\r
18012 this._last = tween._prev;
\r
18015 if (this._timeline) {
\r
18016 this._uncache(true);
\r
18022 p.render = function(time, suppressEvents, force) {
\r
18023 var tween = this._first,
\r
18025 this._totalTime = this._time = this._rawPrevTime = time;
\r
18027 next = tween._next; //record it here because the value could change after rendering...
\r
18028 if (tween._active || (time >= tween._startTime && !tween._paused)) {
\r
18029 if (!tween._reversed) {
\r
18030 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
\r
18032 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
\r
18039 p.rawTime = function() {
\r
18040 if (!_tickerActive) {
\r
18043 return this._totalTime;
\r
18047 * ----------------------------------------------------------------
\r
18049 * ----------------------------------------------------------------
\r
18051 var TweenLite = _class("TweenLite", function(target, duration, vars) {
\r
18052 Animation.call(this, duration, vars);
\r
18053 this.render = TweenLite.prototype.render; //speed optimization (avoid prototype lookup on this "hot" method)
\r
18055 if (target == null) {
\r
18056 throw "Cannot tween a null target.";
\r
18059 this.target = target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target;
\r
18061 var isSelector = (target.jquery || (target.length && target !== window && target[0] && (target[0] === window || (target[0].nodeType && target[0].style && !target.nodeType)))),
\r
18062 overwrite = this.vars.overwrite,
\r
18063 i, targ, targets;
\r
18065 this._overwrite = overwrite = (overwrite == null) ? _overwriteLookup[TweenLite.defaultOverwrite] : (typeof(overwrite) === "number") ? overwrite >> 0 : _overwriteLookup[overwrite];
\r
18067 if ((isSelector || target instanceof Array || (target.push && _isArray(target))) && typeof(target[0]) !== "number") {
\r
18068 this._targets = targets = _slice.call(target, 0);
\r
18069 this._propLookup = [];
\r
18070 this._siblings = [];
\r
18071 for (i = 0; i < targets.length; i++) {
\r
18072 targ = targets[i];
\r
18074 targets.splice(i--, 1);
\r
18076 } else if (typeof(targ) === "string") {
\r
18077 targ = targets[i--] = TweenLite.selector(targ); //in case it's an array of strings
\r
18078 if (typeof(targ) === "string") {
\r
18079 targets.splice(i+1, 1); //to avoid an endless loop (can't imagine why the selector would return a string, but just in case)
\r
18082 } else if (targ.length && targ !== window && targ[0] && (targ[0] === window || (targ[0].nodeType && targ[0].style && !targ.nodeType))) { //in case the user is passing in an array of selector objects (like jQuery objects), we need to check one more level and pull things out if necessary. Also note that <select> elements pass all the criteria regarding length and the first child having style, so we must also check to ensure the target isn't an HTML node itself.
\r
18083 targets.splice(i--, 1);
\r
18084 this._targets = targets = targets.concat(_slice.call(targ, 0));
\r
18087 this._siblings[i] = _register(targ, this, false);
\r
18088 if (overwrite === 1) if (this._siblings[i].length > 1) {
\r
18089 _applyOverwrite(targ, this, null, 1, this._siblings[i]);
\r
18094 this._propLookup = {};
\r
18095 this._siblings = _register(target, this, false);
\r
18096 if (overwrite === 1) if (this._siblings.length > 1) {
\r
18097 _applyOverwrite(target, this, null, 1, this._siblings);
\r
18100 if (this.vars.immediateRender || (duration === 0 && this._delay === 0 && this.vars.immediateRender !== false)) {
\r
18101 this._time = -_tinyNum; //forces a render without having to set the render() "force" parameter to true because we want to allow lazying by default (using the "force" parameter always forces an immediate full render)
\r
18102 this.render(-this._delay);
\r
18105 _isSelector = function(v) {
\r
18106 return (v.length && v !== window && v[0] && (v[0] === window || (v[0].nodeType && v[0].style && !v.nodeType))); //we cannot check "nodeType" if the target is window from within an iframe, otherwise it will trigger a security error in some browsers like Firefox.
\r
18108 _autoCSS = function(vars, target) {
\r
18111 for (p in vars) {
\r
18112 if (!_reservedProps[p] && (!(p in target) || p === "transform" || p === "x" || p === "y" || p === "width" || p === "height" || p === "className" || p === "border") && (!_plugins[p] || (_plugins[p] && _plugins[p]._autoCSS))) { //note: <img> elements contain read-only "x" and "y" properties. We should also prioritize editing css width/height rather than the element's properties.
\r
18113 css[p] = vars[p];
\r
18120 p = TweenLite.prototype = new Animation();
\r
18121 p.constructor = TweenLite;
\r
18122 p.kill()._gc = false;
\r
18124 //----TweenLite defaults, overwrite management, and root updates ----------------------------------------------------
\r
18127 p._firstPT = p._targets = p._overwrittenProps = p._startAt = null;
\r
18128 p._notifyPluginsOfEnabled = p._lazy = false;
\r
18130 TweenLite.version = "1.12.1";
\r
18131 TweenLite.defaultEase = p._ease = new Ease(null, null, 1, 1);
\r
18132 TweenLite.defaultOverwrite = "auto";
\r
18133 TweenLite.ticker = _ticker;
\r
18134 TweenLite.autoSleep = true;
\r
18135 TweenLite.lagSmoothing = function(threshold, adjustedLag) {
\r
18136 _ticker.lagSmoothing(threshold, adjustedLag);
\r
18138 TweenLite.selector = window.$ || window.jQuery || function(e) { if (window.$) { TweenLite.selector = window.$; return window.$(e); } return window.document ? window.document.getElementById((e.charAt(0) === "#") ? e.substr(1) : e) : e; };
\r
18140 var _lazyTweens = [],
\r
18141 _lazyLookup = {},
\r
18142 _internals = TweenLite._internals = {isArray:_isArray, isSelector:_isSelector, lazyTweens:_lazyTweens}, //gives us a way to expose certain private values to other GreenSock classes without contaminating tha main TweenLite object.
\r
18143 _plugins = TweenLite._plugins = {},
\r
18144 _tweenLookup = _internals.tweenLookup = {},
\r
18145 _tweenLookupNum = 0,
\r
18146 _reservedProps = _internals.reservedProps = {ease:1, delay:1, overwrite:1, onComplete:1, onCompleteParams:1, onCompleteScope:1, useFrames:1, runBackwards:1, startAt:1, onUpdate:1, onUpdateParams:1, onUpdateScope:1, onStart:1, onStartParams:1, onStartScope:1, onReverseComplete:1, onReverseCompleteParams:1, onReverseCompleteScope:1, onRepeat:1, onRepeatParams:1, onRepeatScope:1, easeParams:1, yoyo:1, immediateRender:1, repeat:1, repeatDelay:1, data:1, paused:1, reversed:1, autoCSS:1, lazy:1},
\r
18147 _overwriteLookup = {none:0, all:1, auto:2, concurrent:3, allOnStart:4, preexisting:5, "true":1, "false":0},
\r
18148 _rootFramesTimeline = Animation._rootFramesTimeline = new SimpleTimeline(),
\r
18149 _rootTimeline = Animation._rootTimeline = new SimpleTimeline(),
\r
18150 _lazyRender = function() {
\r
18151 var i = _lazyTweens.length;
\r
18152 _lazyLookup = {};
\r
18153 while (--i > -1) {
\r
18154 a = _lazyTweens[i];
\r
18155 if (a && a._lazy !== false) {
\r
18156 a.render(a._lazy, false, true);
\r
18160 _lazyTweens.length = 0;
\r
18163 _rootTimeline._startTime = _ticker.time;
\r
18164 _rootFramesTimeline._startTime = _ticker.frame;
\r
18165 _rootTimeline._active = _rootFramesTimeline._active = true;
\r
18166 setTimeout(_lazyRender, 1); //on some mobile devices, there isn't a "tick" before code runs which means any lazy renders wouldn't run before the next official "tick".
\r
18168 Animation._updateRoot = TweenLite.render = function() {
\r
18170 if (_lazyTweens.length) { //if code is run outside of the requestAnimationFrame loop, there may be tweens queued AFTER the engine refreshed, so we need to ensure any pending renders occur before we refresh again.
\r
18173 _rootTimeline.render((_ticker.time - _rootTimeline._startTime) * _rootTimeline._timeScale, false, false);
\r
18174 _rootFramesTimeline.render((_ticker.frame - _rootFramesTimeline._startTime) * _rootFramesTimeline._timeScale, false, false);
\r
18175 if (_lazyTweens.length) {
\r
18178 if (!(_ticker.frame % 120)) { //dump garbage every 120 frames...
\r
18179 for (p in _tweenLookup) {
\r
18180 a = _tweenLookup[p].tweens;
\r
18182 while (--i > -1) {
\r
18187 if (a.length === 0) {
\r
18188 delete _tweenLookup[p];
\r
18191 //if there are no more tweens in the root timelines, or if they're all paused, make the _timer sleep to reduce load on the CPU slightly
\r
18192 p = _rootTimeline._first;
\r
18193 if (!p || p._paused) if (TweenLite.autoSleep && !_rootFramesTimeline._first && _ticker._listeners.tick.length === 1) {
\r
18194 while (p && p._paused) {
\r
18204 _ticker.addEventListener("tick", Animation._updateRoot);
\r
18206 var _register = function(target, tween, scrub) {
\r
18207 var id = target._gsTweenID, a, i;
\r
18208 if (!_tweenLookup[id || (target._gsTweenID = id = "t" + (_tweenLookupNum++))]) {
\r
18209 _tweenLookup[id] = {target:target, tweens:[]};
\r
18212 a = _tweenLookup[id].tweens;
\r
18213 a[(i = a.length)] = tween;
\r
18215 while (--i > -1) {
\r
18216 if (a[i] === tween) {
\r
18222 return _tweenLookup[id].tweens;
\r
18225 _applyOverwrite = function(target, tween, props, mode, siblings) {
\r
18226 var i, changed, curTween, l;
\r
18227 if (mode === 1 || mode >= 4) {
\r
18228 l = siblings.length;
\r
18229 for (i = 0; i < l; i++) {
\r
18230 if ((curTween = siblings[i]) !== tween) {
\r
18231 if (!curTween._gc) if (curTween._enabled(false, false)) {
\r
18234 } else if (mode === 5) {
\r
18240 //NOTE: Add 0.0000000001 to overcome floating point errors that can cause the startTime to be VERY slightly off (when a tween's time() is set for example)
\r
18241 var startTime = tween._startTime + _tinyNum,
\r
18244 zeroDur = (tween._duration === 0),
\r
18246 i = siblings.length;
\r
18247 while (--i > -1) {
\r
18248 if ((curTween = siblings[i]) === tween || curTween._gc || curTween._paused) {
\r
18250 } else if (curTween._timeline !== tween._timeline) {
\r
18251 globalStart = globalStart || _checkOverlap(tween, 0, zeroDur);
\r
18252 if (_checkOverlap(curTween, globalStart, zeroDur) === 0) {
\r
18253 overlaps[oCount++] = curTween;
\r
18255 } else if (curTween._startTime <= startTime) if (curTween._startTime + curTween.totalDuration() / curTween._timeScale > startTime) if (!((zeroDur || !curTween._initted) && startTime - curTween._startTime <= 0.0000000002)) {
\r
18256 overlaps[oCount++] = curTween;
\r
18261 while (--i > -1) {
\r
18262 curTween = overlaps[i];
\r
18263 if (mode === 2) if (curTween._kill(props, target)) {
\r
18266 if (mode !== 2 || (!curTween._firstPT && curTween._initted)) {
\r
18267 if (curTween._enabled(false, false)) { //if all property tweens have been overwritten, kill the tween.
\r
18275 _checkOverlap = function(tween, reference, zeroDur) {
\r
18276 var tl = tween._timeline,
\r
18277 ts = tl._timeScale,
\r
18278 t = tween._startTime;
\r
18279 while (tl._timeline) {
\r
18280 t += tl._startTime;
\r
18281 ts *= tl._timeScale;
\r
18282 if (tl._paused) {
\r
18285 tl = tl._timeline;
\r
18288 return (t > reference) ? t - reference : ((zeroDur && t === reference) || (!tween._initted && t - reference < 2 * _tinyNum)) ? _tinyNum : ((t += tween.totalDuration() / tween._timeScale / ts) > reference + _tinyNum) ? 0 : t - reference - _tinyNum;
\r
18292 //---- TweenLite instance methods -----------------------------------------------------------------------------
\r
18294 p._init = function() {
\r
18295 var v = this.vars,
\r
18296 op = this._overwrittenProps,
\r
18297 dur = this._duration,
\r
18298 immediate = !!v.immediateRender,
\r
18300 i, initPlugins, pt, p, startVars;
\r
18302 if (this._startAt) {
\r
18303 this._startAt.render(-1, true); //if we've run a startAt previously (when the tween instantiated), we should revert it so that the values re-instantiate correctly particularly for relative tweens. Without this, a TweenLite.fromTo(obj, 1, {x:"+=100"}, {x:"-=100"}), for example, would actually jump to +=200 because the startAt would run twice, doubling the relative change.
\r
18304 this._startAt.kill();
\r
18307 for (p in v.startAt) { //copy the properties/values into a new object to avoid collisions, like var to = {x:0}, from = {x:500}; timeline.fromTo(e, 1, from, to).fromTo(e, 1, to, from);
\r
18308 startVars[p] = v.startAt[p];
\r
18310 startVars.overwrite = false;
\r
18311 startVars.immediateRender = true;
\r
18312 startVars.lazy = (immediate && v.lazy !== false);
\r
18313 startVars.startAt = startVars.delay = null; //no nesting of startAt objects allowed (otherwise it could cause an infinite loop).
\r
18314 this._startAt = TweenLite.to(this.target, 0, startVars);
\r
18316 if (this._time > 0) {
\r
18317 this._startAt = null; //tweens that render immediately (like most from() and fromTo() tweens) shouldn't revert when their parent timeline's playhead goes backward past the startTime because the initial render could have happened anytime and it shouldn't be directly correlated to this tween's startTime. Imagine setting up a complex animation where the beginning states of various objects are rendered immediately but the tween doesn't happen for quite some time - if we revert to the starting values as soon as the playhead goes backward past the tween's startTime, it will throw things off visually. Reversion should only happen in TimelineLite/Max instances where immediateRender was false (which is the default in the convenience methods like from()).
\r
18318 } else if (dur !== 0) {
\r
18319 return; //we skip initialization here so that overwriting doesn't occur until the tween actually begins. Otherwise, if you create several immediateRender:true tweens of the same target/properties to drop into a TimelineLite or TimelineMax, the last one created would overwrite the first ones because they didn't get placed into the timeline yet before the first render occurs and kicks in overwriting.
\r
18322 } else if (v.runBackwards && dur !== 0) {
\r
18323 //from() tweens must be handled uniquely: their beginning values must be rendered but we don't want overwriting to occur yet (when time is still 0). Wait until the tween actually begins before doing all the routines like overwriting. At that time, we should render at the END of the tween to ensure that things initialize correctly (remember, from() tweens go backwards)
\r
18324 if (this._startAt) {
\r
18325 this._startAt.render(-1, true);
\r
18326 this._startAt.kill();
\r
18327 this._startAt = null;
\r
18330 for (p in v) { //copy props into a new object and skip any reserved props, otherwise onComplete or onUpdate or onStart could fire. We should, however, permit autoCSS to go through.
\r
18331 if (!_reservedProps[p] || p === "autoCSS") {
\r
18335 pt.overwrite = 0;
\r
18336 pt.data = "isFromStart"; //we tag the tween with as "isFromStart" so that if [inside a plugin] we need to only do something at the very END of a tween, we have a way of identifying this tween as merely the one that's setting the beginning values for a "from()" tween. For example, clearProps in CSSPlugin should only get applied at the very END of a tween and without this tag, from(...{height:100, clearProps:"height", delay:1}) would wipe the height at the beginning of the tween and after 1 second, it'd kick back in.
\r
18337 pt.lazy = (immediate && v.lazy !== false);
\r
18338 pt.immediateRender = immediate; //zero-duration tweens render immediately by default, but if we're not specifically instructed to render this tween immediately, we should skip this and merely _init() to record the starting values (rendering them immediately would push them to completion which is wasteful in that case - we'd have to render(-1) immediately after)
\r
18339 this._startAt = TweenLite.to(this.target, 0, pt);
\r
18340 if (!immediate) {
\r
18341 this._startAt._init(); //ensures that the initial values are recorded
\r
18342 this._startAt._enabled(false); //no need to have the tween render on the next cycle. Disable it because we'll always manually control the renders of the _startAt tween.
\r
18343 } else if (this._time === 0) {
\r
18349 this._ease = TweenLite.defaultEase;
\r
18350 } else if (ease instanceof Ease) {
\r
18351 this._ease = (v.easeParams instanceof Array) ? ease.config.apply(ease, v.easeParams) : ease;
\r
18353 this._ease = (typeof(ease) === "function") ? new Ease(ease, v.easeParams) : _easeMap[ease] || TweenLite.defaultEase;
\r
18355 this._easeType = this._ease._type;
\r
18356 this._easePower = this._ease._power;
\r
18357 this._firstPT = null;
\r
18359 if (this._targets) {
\r
18360 i = this._targets.length;
\r
18361 while (--i > -1) {
\r
18362 if ( this._initProps( this._targets[i], (this._propLookup[i] = {}), this._siblings[i], (op ? op[i] : null)) ) {
\r
18363 initPlugins = true;
\r
18367 initPlugins = this._initProps(this.target, this._propLookup, this._siblings, op);
\r
18370 if (initPlugins) {
\r
18371 TweenLite._onPluginEvent("_onInitAllProps", this); //reorders the array in order of priority. Uses a static TweenPlugin method in order to minimize file size in TweenLite
\r
18373 if (op) if (!this._firstPT) if (typeof(this.target) !== "function") { //if all tweening properties have been overwritten, kill the tween. If the target is a function, it's probably a delayedCall so let it live.
\r
18374 this._enabled(false, false);
\r
18376 if (v.runBackwards) {
\r
18377 pt = this._firstPT;
\r
18384 this._onUpdate = v.onUpdate;
\r
18385 this._initted = true;
\r
18388 p._initProps = function(target, propLookup, siblings, overwrittenProps) {
\r
18389 var p, i, initPlugins, plugin, pt, v;
\r
18390 if (target == null) {
\r
18394 if (_lazyLookup[target._gsTweenID]) {
\r
18395 _lazyRender(); //if other tweens of the same target have recently initted but haven't rendered yet, we've got to force the render so that the starting values are correct (imagine populating a timeline with a bunch of sequential tweens and then jumping to the end)
\r
18398 if (!this.vars.css) if (target.style) if (target !== window && target.nodeType) if (_plugins.css) if (this.vars.autoCSS !== false) { //it's so common to use TweenLite/Max to animate the css of DOM elements, we assume that if the target is a DOM element, that's what is intended (a convenience so that users don't have to wrap things in css:{}, although we still recommend it for a slight performance boost and better specificity). Note: we cannot check "nodeType" on the window inside an iframe.
\r
18399 _autoCSS(this.vars, target);
\r
18401 for (p in this.vars) {
\r
18402 v = this.vars[p];
\r
18403 if (_reservedProps[p]) {
\r
18404 if (v) if ((v instanceof Array) || (v.push && _isArray(v))) if (v.join("").indexOf("{self}") !== -1) {
\r
18405 this.vars[p] = v = this._swapSelfInParams(v, this);
\r
18408 } else if (_plugins[p] && (plugin = new _plugins[p]())._onInitTween(target, this.vars[p], this)) {
\r
18410 //t - target [object]
\r
18411 //p - property [string]
\r
18412 //s - start [number]
\r
18413 //c - change [number]
\r
18414 //f - isFunction [boolean]
\r
18415 //n - name [string]
\r
18416 //pg - isPlugin [boolean]
\r
18417 //pr - priority [number]
\r
18418 this._firstPT = pt = {_next:this._firstPT, t:plugin, p:"setRatio", s:0, c:1, f:true, n:p, pg:true, pr:plugin._priority};
\r
18419 i = plugin._overwriteProps.length;
\r
18420 while (--i > -1) {
\r
18421 propLookup[plugin._overwriteProps[i]] = this._firstPT;
\r
18423 if (plugin._priority || plugin._onInitAllProps) {
\r
18424 initPlugins = true;
\r
18426 if (plugin._onDisable || plugin._onEnable) {
\r
18427 this._notifyPluginsOfEnabled = true;
\r
18431 this._firstPT = propLookup[p] = pt = {_next:this._firstPT, t:target, p:p, f:(typeof(target[p]) === "function"), n:p, pg:false, pr:0};
\r
18432 pt.s = (!pt.f) ? parseFloat(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]();
\r
18433 pt.c = (typeof(v) === "string" && v.charAt(1) === "=") ? parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) : (Number(v) - pt.s) || 0;
\r
18435 if (pt) if (pt._next) {
\r
18436 pt._next._prev = pt;
\r
18440 if (overwrittenProps) if (this._kill(overwrittenProps, target)) { //another tween may have tried to overwrite properties of this tween before init() was called (like if two tweens start at the same time, the one created second will run first)
\r
18441 return this._initProps(target, propLookup, siblings, overwrittenProps);
\r
18443 if (this._overwrite > 1) if (this._firstPT) if (siblings.length > 1) if (_applyOverwrite(target, this, propLookup, this._overwrite, siblings)) {
\r
18444 this._kill(propLookup, target);
\r
18445 return this._initProps(target, propLookup, siblings, overwrittenProps);
\r
18447 if (this._firstPT) if ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration)) { //zero duration tweens don't lazy render by default; everything else does.
\r
18448 _lazyLookup[target._gsTweenID] = true;
\r
18450 return initPlugins;
\r
18453 p.render = function(time, suppressEvents, force) {
\r
18454 var prevTime = this._time,
\r
18455 duration = this._duration,
\r
18456 prevRawPrevTime = this._rawPrevTime,
\r
18457 isComplete, callback, pt, rawPrevTime;
\r
18458 if (time >= duration) {
\r
18459 this._totalTime = this._time = duration;
\r
18460 this.ratio = this._ease._calcEnd ? this._ease.getRatio(1) : 1;
\r
18461 if (!this._reversed ) {
\r
18462 isComplete = true;
\r
18463 callback = "onComplete";
\r
18465 if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
\r
18466 if (this._startTime === this._timeline._duration) { //if a zero-duration tween is at the VERY end of a timeline and that timeline renders at its end, it will typically add a tiny bit of cushion to the render time to prevent rounding errors from getting in the way of tweens rendering their VERY end. If we then reverse() that timeline, the zero-duration tween will trigger its onReverseComplete even though technically the playhead didn't pass over it again. It's a very specific edge case we must accommodate.
\r
18469 if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time) {
\r
18471 if (prevRawPrevTime > _tinyNum) {
\r
18472 callback = "onReverseComplete";
\r
18475 this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
\r
18478 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
\r
18479 this._totalTime = this._time = 0;
\r
18480 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
\r
18481 if (prevTime !== 0 || (duration === 0 && prevRawPrevTime > 0 && prevRawPrevTime !== _tinyNum)) {
\r
18482 callback = "onReverseComplete";
\r
18483 isComplete = this._reversed;
\r
18486 this._active = false;
\r
18487 if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
\r
18488 if (prevRawPrevTime >= 0) {
\r
18491 this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
\r
18493 } else if (!this._initted) { //if we render the very beginning (time == 0) of a fromTo(), we must force the render (normal tweens wouldn't need to render at a time of 0 when the prevTime was also 0). This is also mandatory to make sure overwriting kicks in immediately.
\r
18497 this._totalTime = this._time = time;
\r
18499 if (this._easeType) {
\r
18500 var r = time / duration, type = this._easeType, pow = this._easePower;
\r
18501 if (type === 1 || (type === 3 && r >= 0.5)) {
\r
18504 if (type === 3) {
\r
18509 } else if (pow === 2) {
\r
18511 } else if (pow === 3) {
\r
18513 } else if (pow === 4) {
\r
18514 r *= r * r * r * r;
\r
18517 if (type === 1) {
\r
18518 this.ratio = 1 - r;
\r
18519 } else if (type === 2) {
\r
18521 } else if (time / duration < 0.5) {
\r
18522 this.ratio = r / 2;
\r
18524 this.ratio = 1 - (r / 2);
\r
18528 this.ratio = this._ease.getRatio(time / duration);
\r
18532 if (this._time === prevTime && !force) {
\r
18534 } else if (!this._initted) {
\r
18536 if (!this._initted || this._gc) { //immediateRender tweens typically won't initialize until the playhead advances (_time is greater than 0) in order to ensure that overwriting occurs properly. Also, if all of the tweening properties have been overwritten (which would cause _gc to be true, as set in _init()), we shouldn't continue otherwise an onStart callback could be called for example.
\r
18538 } else if (!force && this._firstPT && ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration))) {
\r
18539 this._time = this._totalTime = prevTime;
\r
18540 this._rawPrevTime = prevRawPrevTime;
\r
18541 _lazyTweens.push(this);
\r
18542 this._lazy = time;
\r
18545 //_ease is initially set to defaultEase, so now that init() has run, _ease is set properly and we need to recalculate the ratio. Overall this is faster than using conditional logic earlier in the method to avoid having to set ratio twice because we only init() once but renderTime() gets called VERY frequently.
\r
18546 if (this._time && !isComplete) {
\r
18547 this.ratio = this._ease.getRatio(this._time / duration);
\r
18548 } else if (isComplete && this._ease._calcEnd) {
\r
18549 this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1);
\r
18552 if (this._lazy !== false) { //in case a lazy render is pending, we should flush it because the new render is occuring now (imagine a lazy tween instantiating and then immediately the user calls tween.seek(tween.duration()), skipping to the end - the end render would be forced, and then if we didn't flush the lazy render, it'd fire AFTER the seek(), rendering it at the wrong time.
\r
18553 this._lazy = false;
\r
18555 if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) {
\r
18556 this._active = true; //so that if the user renders a tween (as opposed to the timeline rendering it), the timeline is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the tween already finished but the user manually re-renders it as halfway done.
\r
18558 if (prevTime === 0) {
\r
18559 if (this._startAt) {
\r
18561 this._startAt.render(time, suppressEvents, force);
\r
18562 } else if (!callback) {
\r
18563 callback = "_dummyGS"; //if no callback is defined, use a dummy value just so that the condition at the end evaluates as true because _startAt should render AFTER the normal render loop when the time is negative. We could handle this in a more intuitive way, of course, but the render loop is the MOST important thing to optimize, so this technique allows us to avoid adding extra conditional logic in a high-frequency area.
\r
18566 if (this.vars.onStart) if (this._time !== 0 || duration === 0) if (!suppressEvents) {
\r
18567 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
\r
18571 pt = this._firstPT;
\r
18574 pt.t[pt.p](pt.c * this.ratio + pt.s);
\r
18576 pt.t[pt.p] = pt.c * this.ratio + pt.s;
\r
18581 if (this._onUpdate) {
\r
18582 if (time < 0) if (this._startAt && this._startTime) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values.
\r
18583 this._startAt.render(time, suppressEvents, force); //note: for performance reasons, we tuck this conditional logic inside less traveled areas (most tweens don't have an onUpdate). We'd just have it at the end before the onComplete, but the values should be updated before any onUpdate is called, so we ALSO put it here and then if it's not called, we do so later near the onComplete.
\r
18585 if (!suppressEvents) if (this._time !== prevTime || isComplete) {
\r
18586 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
\r
18590 if (callback) if (!this._gc) { //check _gc because there's a chance that kill() could be called in an onUpdate
\r
18591 if (time < 0 && this._startAt && !this._onUpdate && this._startTime) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values.
\r
18592 this._startAt.render(time, suppressEvents, force);
\r
18594 if (isComplete) {
\r
18595 if (this._timeline.autoRemoveChildren) {
\r
18596 this._enabled(false, false);
\r
18598 this._active = false;
\r
18600 if (!suppressEvents && this.vars[callback]) {
\r
18601 this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
\r
18603 if (duration === 0 && this._rawPrevTime === _tinyNum && rawPrevTime !== _tinyNum) { //the onComplete or onReverseComplete could trigger movement of the playhead and for zero-duration tweens (which must discern direction) that land directly back on their start time, we don't want to fire again on the next render. Think of several addPause()'s in a timeline that forces the playhead to a certain spot, but what if it's already paused and another tween is tweening the "time" of the timeline? Each time it moves [forward] past that spot, it would move back, and since suppressEvents is true, it'd reset _rawPrevTime to _tinyNum so that when it begins again, the callback would fire (so ultimately it could bounce back and forth during that tween). Again, this is a very uncommon scenario, but possible nonetheless.
\r
18604 this._rawPrevTime = 0;
\r
18610 p._kill = function(vars, target) {
\r
18611 if (vars === "all") {
\r
18614 if (vars == null) if (target == null || target === this.target) {
\r
18615 this._lazy = false;
\r
18616 return this._enabled(false, false);
\r
18618 target = (typeof(target) !== "string") ? (target || this._targets || this.target) : TweenLite.selector(target) || target;
\r
18619 var i, overwrittenProps, p, pt, propLookup, changed, killProps, record;
\r
18620 if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") {
\r
18621 i = target.length;
\r
18622 while (--i > -1) {
\r
18623 if (this._kill(vars, target[i])) {
\r
18628 if (this._targets) {
\r
18629 i = this._targets.length;
\r
18630 while (--i > -1) {
\r
18631 if (target === this._targets[i]) {
\r
18632 propLookup = this._propLookup[i] || {};
\r
18633 this._overwrittenProps = this._overwrittenProps || [];
\r
18634 overwrittenProps = this._overwrittenProps[i] = vars ? this._overwrittenProps[i] || {} : "all";
\r
18638 } else if (target !== this.target) {
\r
18641 propLookup = this._propLookup;
\r
18642 overwrittenProps = this._overwrittenProps = vars ? this._overwrittenProps || {} : "all";
\r
18645 if (propLookup) {
\r
18646 killProps = vars || propLookup;
\r
18647 record = (vars !== overwrittenProps && overwrittenProps !== "all" && vars !== propLookup && (typeof(vars) !== "object" || !vars._tempKill)); //_tempKill is a super-secret way to delete a particular tweening property but NOT have it remembered as an official overwritten property (like in BezierPlugin)
\r
18648 for (p in killProps) {
\r
18649 if ((pt = propLookup[p])) {
\r
18650 if (pt.pg && pt.t._kill(killProps)) {
\r
18651 changed = true; //some plugins need to be notified so they can perform cleanup tasks first
\r
18653 if (!pt.pg || pt.t._overwriteProps.length === 0) {
\r
18655 pt._prev._next = pt._next;
\r
18656 } else if (pt === this._firstPT) {
\r
18657 this._firstPT = pt._next;
\r
18660 pt._next._prev = pt._prev;
\r
18662 pt._next = pt._prev = null;
\r
18664 delete propLookup[p];
\r
18667 overwrittenProps[p] = 1;
\r
18670 if (!this._firstPT && this._initted) { //if all tweening properties are killed, kill the tween. Without this line, if there's a tween with multiple targets and then you killTweensOf() each target individually, the tween would technically still remain active and fire its onComplete even though there aren't any more properties tweening.
\r
18671 this._enabled(false, false);
\r
18678 p.invalidate = function() {
\r
18679 if (this._notifyPluginsOfEnabled) {
\r
18680 TweenLite._onPluginEvent("_onDisable", this);
\r
18682 this._firstPT = null;
\r
18683 this._overwrittenProps = null;
\r
18684 this._onUpdate = null;
\r
18685 this._startAt = null;
\r
18686 this._initted = this._active = this._notifyPluginsOfEnabled = this._lazy = false;
\r
18687 this._propLookup = (this._targets) ? {} : [];
\r
18691 p._enabled = function(enabled, ignoreTimeline) {
\r
18692 if (!_tickerActive) {
\r
18695 if (enabled && this._gc) {
\r
18696 var targets = this._targets,
\r
18699 i = targets.length;
\r
18700 while (--i > -1) {
\r
18701 this._siblings[i] = _register(targets[i], this, true);
\r
18704 this._siblings = _register(this.target, this, true);
\r
18707 Animation.prototype._enabled.call(this, enabled, ignoreTimeline);
\r
18708 if (this._notifyPluginsOfEnabled) if (this._firstPT) {
\r
18709 return TweenLite._onPluginEvent((enabled ? "_onEnable" : "_onDisable"), this);
\r
18715 //----TweenLite static methods -----------------------------------------------------
\r
18717 TweenLite.to = function(target, duration, vars) {
\r
18718 return new TweenLite(target, duration, vars);
\r
18721 TweenLite.from = function(target, duration, vars) {
\r
18722 vars.runBackwards = true;
\r
18723 vars.immediateRender = (vars.immediateRender != false);
\r
18724 return new TweenLite(target, duration, vars);
\r
18727 TweenLite.fromTo = function(target, duration, fromVars, toVars) {
\r
18728 toVars.startAt = fromVars;
\r
18729 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
\r
18730 return new TweenLite(target, duration, toVars);
\r
18733 TweenLite.delayedCall = function(delay, callback, params, scope, useFrames) {
\r
18734 return new TweenLite(callback, 0, {delay:delay, onComplete:callback, onCompleteParams:params, onCompleteScope:scope, onReverseComplete:callback, onReverseCompleteParams:params, onReverseCompleteScope:scope, immediateRender:false, useFrames:useFrames, overwrite:0});
\r
18737 TweenLite.set = function(target, vars) {
\r
18738 return new TweenLite(target, 0, vars);
\r
18741 TweenLite.getTweensOf = function(target, onlyActive) {
\r
18742 if (target == null) { return []; }
\r
18743 target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target;
\r
18745 if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") {
\r
18746 i = target.length;
\r
18748 while (--i > -1) {
\r
18749 a = a.concat(TweenLite.getTweensOf(target[i], onlyActive));
\r
18752 //now get rid of any duplicates (tweens of arrays of objects could cause duplicates)
\r
18753 while (--i > -1) {
\r
18756 while (--j > -1) {
\r
18757 if (t === a[j]) {
\r
18763 a = _register(target).concat();
\r
18765 while (--i > -1) {
\r
18766 if (a[i]._gc || (onlyActive && !a[i].isActive())) {
\r
18774 TweenLite.killTweensOf = TweenLite.killDelayedCallsTo = function(target, onlyActive, vars) {
\r
18775 if (typeof(onlyActive) === "object") {
\r
18776 vars = onlyActive; //for backwards compatibility (before "onlyActive" parameter was inserted)
\r
18777 onlyActive = false;
\r
18779 var a = TweenLite.getTweensOf(target, onlyActive),
\r
18781 while (--i > -1) {
\r
18782 a[i]._kill(vars, target);
\r
18789 * ----------------------------------------------------------------
\r
18790 * TweenPlugin (could easily be split out as a separate file/class, but included for ease of use (so that people don't need to include another <script> call before loading plugins which is easy to forget)
\r
18791 * ----------------------------------------------------------------
\r
18793 var TweenPlugin = _class("plugins.TweenPlugin", function(props, priority) {
\r
18794 this._overwriteProps = (props || "").split(",");
\r
18795 this._propName = this._overwriteProps[0];
\r
18796 this._priority = priority || 0;
\r
18797 this._super = TweenPlugin.prototype;
\r
18800 p = TweenPlugin.prototype;
\r
18801 TweenPlugin.version = "1.10.1";
\r
18802 TweenPlugin.API = 2;
\r
18803 p._firstPT = null;
\r
18805 p._addTween = function(target, prop, start, end, overwriteProp, round) {
\r
18807 if (end != null && (c = (typeof(end) === "number" || end.charAt(1) !== "=") ? Number(end) - start : parseInt(end.charAt(0) + "1", 10) * Number(end.substr(2)))) {
\r
18808 this._firstPT = pt = {_next:this._firstPT, t:target, p:prop, s:start, c:c, f:(typeof(target[prop]) === "function"), n:overwriteProp || prop, r:round};
\r
18810 pt._next._prev = pt;
\r
18816 p.setRatio = function(v) {
\r
18817 var pt = this._firstPT,
\r
18821 val = pt.c * v + pt.s;
\r
18823 val = Math.round(val);
\r
18824 } else if (val < min) if (val > -min) { //prevents issues with converting very small numbers to strings in the browser
\r
18830 pt.t[pt.p] = val;
\r
18836 p._kill = function(lookup) {
\r
18837 var a = this._overwriteProps,
\r
18838 pt = this._firstPT,
\r
18840 if (lookup[this._propName] != null) {
\r
18841 this._overwriteProps = [];
\r
18844 while (--i > -1) {
\r
18845 if (lookup[a[i]] != null) {
\r
18851 if (lookup[pt.n] != null) {
\r
18853 pt._next._prev = pt._prev;
\r
18856 pt._prev._next = pt._next;
\r
18858 } else if (this._firstPT === pt) {
\r
18859 this._firstPT = pt._next;
\r
18867 p._roundProps = function(lookup, value) {
\r
18868 var pt = this._firstPT;
\r
18870 if (lookup[this._propName] || (pt.n != null && lookup[ pt.n.split(this._propName + "_").join("") ])) { //some properties that are very plugin-specific add a prefix named after the _propName plus an underscore, so we need to ignore that extra stuff here.
\r
18877 TweenLite._onPluginEvent = function(type, tween) {
\r
18878 var pt = tween._firstPT,
\r
18879 changed, pt2, first, last, next;
\r
18880 if (type === "_onInitAllProps") {
\r
18881 //sorts the PropTween linked list in order of priority because some plugins need to render earlier/later than others, like MotionBlurPlugin applies its effects after all x/y/alpha tweens have rendered on each frame.
\r
18885 while (pt2 && pt2.pr > pt.pr) {
\r
18888 if ((pt._prev = pt2 ? pt2._prev : last)) {
\r
18889 pt._prev._next = pt;
\r
18893 if ((pt._next = pt2)) {
\r
18900 pt = tween._firstPT = first;
\r
18903 if (pt.pg) if (typeof(pt.t[type]) === "function") if (pt.t[type]()) {
\r
18911 TweenPlugin.activate = function(plugins) {
\r
18912 var i = plugins.length;
\r
18913 while (--i > -1) {
\r
18914 if (plugins[i].API === TweenPlugin.API) {
\r
18915 _plugins[(new plugins[i]())._propName] = plugins[i];
\r
18921 //provides a more concise way to define plugins that have no dependencies besides TweenPlugin and TweenLite, wrapping common boilerplate stuff into one function (added in 1.9.0). You don't NEED to use this to define a plugin - the old way still works and can be useful in certain (rare) situations.
\r
18922 _gsDefine.plugin = function(config) {
\r
18923 if (!config || !config.propName || !config.init || !config.API) { throw "illegal plugin definition."; }
\r
18924 var propName = config.propName,
\r
18925 priority = config.priority || 0,
\r
18926 overwriteProps = config.overwriteProps,
\r
18927 map = {init:"_onInitTween", set:"setRatio", kill:"_kill", round:"_roundProps", initAll:"_onInitAllProps"},
\r
18928 Plugin = _class("plugins." + propName.charAt(0).toUpperCase() + propName.substr(1) + "Plugin",
\r
18930 TweenPlugin.call(this, propName, priority);
\r
18931 this._overwriteProps = overwriteProps || [];
\r
18932 }, (config.global === true)),
\r
18933 p = Plugin.prototype = new TweenPlugin(propName),
\r
18935 p.constructor = Plugin;
\r
18936 Plugin.API = config.API;
\r
18937 for (prop in map) {
\r
18938 if (typeof(config[prop]) === "function") {
\r
18939 p[map[prop]] = config[prop];
\r
18942 Plugin.version = config.version;
\r
18943 TweenPlugin.activate([Plugin]);
\r
18948 //now run through all the dependencies discovered and if any are missing, log that to the console as a warning. This is why it's best to have TweenLite load last - it can check all the dependencies for you.
\r
18949 a = window._gsQueue;
\r
18951 for (i = 0; i < a.length; i++) {
\r
18954 for (p in _defLookup) {
\r
18955 if (!_defLookup[p].func) {
\r
18956 //window.console.log("GSAP encountered missing dependency: com.greensock." + p);
\r
18961 _tickerActive = false; //ensures that the first official animation forces a ticker.tick() to update the time when it is instantiated
\r
18965 angular.module('b2b.att.collapse', ['b2b.att.transition'])
\r
18967 // The collapsible directive indicates a block of html that will expand and collapse
\r
18968 .directive('b2bCollapse', ['$transition', function($transition) {
\r
18969 // CSS transitions don't work with height: auto, so we have to manually change the height to a
\r
18970 // specific value and then once the animation completes, we can reset the height to auto.
\r
18971 // Unfortunately if you do this while the CSS transitions are specified (i.e. in the CSS class
\r
18972 // "collapse") then you trigger a change to height 0 in between.
\r
18973 // The fix is to remove the "collapse" CSS class while changing the height back to auto - phew!
\r
18978 marginBottom: null,
\r
18979 paddingTop: null,
\r
18980 paddingBottom: null,
\r
18987 paddingBottom: 0,
\r
18992 var fixUpHeight = function(scope, element, height) {
\r
18993 // We remove the collapse CSS class to prevent a transition when we change to height: auto
\r
18994 element.removeClass('b2bCollapse');
\r
18995 element.css({height: height});
\r
18996 //adjusting for any margin or padding
\r
18997 if (height === 0) {
\r
18998 element.css(props.closed);
\r
19000 element.css(props.open);
\r
19002 // It appears that reading offsetWidth makes the browser realise that we have changed the
\r
19003 // height already :-/
\r
19004 var x = element[0].offsetWidth;
\r
19005 element.addClass('b2bCollapse');
\r
19009 link: function(scope, element, attrs) {
\r
19011 var initialAnimSkip = true;
\r
19012 scope.$watch(function() {
\r
19013 return element[0].scrollHeight;
\r
19014 }, function(value) {
\r
19015 //The listener is called when scrollHeight changes
\r
19016 //It actually does on 2 scenarios:
\r
19017 // 1. Parent is set to display none
\r
19018 // 2. angular bindings inside are resolved
\r
19019 //When we have a change of scrollHeight we are setting again the correct height if the group is opened
\r
19020 if (element[0].scrollHeight !== 0) {
\r
19021 if (!isCollapsed) {
\r
19022 if (initialAnimSkip) {
\r
19023 fixUpHeight(scope, element, element[0].scrollHeight + 'px');
\r
19025 fixUpHeight(scope, element, 'auto');
\r
19026 element.css({overflow: 'visible'});
\r
19032 scope.$watch(attrs.b2bCollapse, function(value) {
\r
19041 var currentTransition;
\r
19042 var doTransition = function(change) {
\r
19043 if (currentTransition) {
\r
19044 currentTransition.cancel();
\r
19046 currentTransition = $transition(element, change);
\r
19047 currentTransition.then(
\r
19049 currentTransition = undefined;
\r
19052 currentTransition = undefined;
\r
19055 return currentTransition;
\r
19058 var expand = function() {
\r
19059 scope.postTransition = true;
\r
19060 if (initialAnimSkip) {
\r
19061 initialAnimSkip = false;
\r
19062 if (!isCollapsed) {
\r
19063 fixUpHeight(scope, element, 'auto');
\r
19066 //doTransition({ height : element[0].scrollHeight + 'px' })
\r
19067 doTransition(angular.extend({height: element[0].scrollHeight + 'px'}, props.open))
\r
19068 .then(function() {
\r
19069 // This check ensures that we don't accidentally update the height if the user has closed
\r
19070 // the group while the animation was still running
\r
19071 if (!isCollapsed) {
\r
19072 fixUpHeight(scope, element, 'auto');
\r
19076 isCollapsed = false;
\r
19079 var collapse = function() {
\r
19080 isCollapsed = true;
\r
19081 if (initialAnimSkip) {
\r
19082 initialAnimSkip = false;
\r
19083 fixUpHeight(scope, element, 0);
\r
19085 fixUpHeight(scope, element, element[0].scrollHeight + 'px');
\r
19086 doTransition(angular.extend({height: 0}, props.closed)).then(function() {
\r
19087 scope.postTransition = false;
\r
19089 element.css({overflow: 'hidden'});
\r
19095 angular.module('b2b.att.position', [])
\r
19097 .factory('$position', ['$document', '$window', function ($document, $window) {
\r
19098 function getStyle(el, cssprop) {
\r
19099 if (el.currentStyle) { //IE
\r
19100 return el.currentStyle[cssprop];
\r
19101 } else if ($window.getComputedStyle) {
\r
19102 return $window.getComputedStyle(el)[cssprop];
\r
19104 // finally try and get inline style
\r
19105 return el.style[cssprop];
\r
19109 * Checks if a given element is statically positioned
\r
19110 * @param element - raw DOM element
\r
19112 function isStaticPositioned(element) {
\r
19113 return (getStyle(element, "position") || 'static') === 'static';
\r
19117 * returns the closest, non-statically positioned parentOffset of a given element
\r
19120 var parentOffsetEl = function (element) {
\r
19121 var docDomEl = $document[0];
\r
19122 var offsetParent = element.offsetParent || docDomEl;
\r
19123 while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent)) {
\r
19124 offsetParent = offsetParent.offsetParent;
\r
19126 return offsetParent || docDomEl;
\r
19131 * Provides read-only equivalent of jQuery's position function:
\r
19132 * http://api.jquery.com/position/
\r
19134 position: function (element) {
\r
19135 var elBCR = this.offset(element);
\r
19136 var offsetParentBCR = {
\r
19140 var offsetParentEl = parentOffsetEl(element[0]);
\r
19141 if (offsetParentEl !== $document[0]) {
\r
19142 offsetParentBCR = this.offset(angular.element(offsetParentEl));
\r
19143 offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
\r
19144 offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
\r
19148 width: element.prop('offsetWidth'),
\r
19149 height: element.prop('offsetHeight'),
\r
19150 top: elBCR.top - offsetParentBCR.top,
\r
19151 left: elBCR.left - offsetParentBCR.left
\r
19156 * Provides read-only equivalent of jQuery's offset function:
\r
19157 * http://api.jquery.com/offset/
\r
19159 offset: function (element) {
\r
19160 var boundingClientRect = element[0].getBoundingClientRect();
\r
19162 width: element.prop('offsetWidth'),
\r
19163 height: element.prop('offsetHeight'),
\r
19164 top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
\r
19165 left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
\r
19170 * Provides functionality to check whether an element is in view port.
\r
19172 isElementInViewport: function (element) {
\r
19174 var rect = element[0].getBoundingClientRect();
\r
19177 rect.left >= 0 &&
\r
19178 rect.bottom <= ($window.innerHeight || $document[0].documentElement.clientHeight) &&
\r
19179 rect.right <= ($window.innerWidth || $document[0].documentElement.clientWidth)
\r
19188 .factory('$isElement', [function () {
\r
19189 var isElement = function (currentElem, targetElem, alternateElem) {
\r
19190 if (currentElem[0] === targetElem[0]) {
\r
19192 } else if (currentElem[0] === alternateElem[0]) {
\r
19195 return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
\r
19199 return isElement;
\r
19202 .directive('attPosition', ['$position', function ($position) {
\r
19205 link: function (scope, elem, attr) {
\r
19206 scope.$watchCollection(function () {
\r
19207 return $position.position(elem);
\r
19208 }, function (value) {
\r
19209 scope[attr.attPosition] = value;
\r
19215 angular.module('b2b.att.transition', [])
\r
19217 .factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
\r
19219 var $transition = function(element, trigger, options) {
\r
19220 options = options || {};
\r
19221 var deferred = $q.defer();
\r
19222 var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
\r
19224 var transitionEndHandler = function() {
\r
19225 $rootScope.$apply(function() {
\r
19226 element.unbind(endEventName, transitionEndHandler);
\r
19227 deferred.resolve(element);
\r
19231 if (endEventName) {
\r
19232 element.bind(endEventName, transitionEndHandler);
\r
19235 // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
\r
19236 $timeout(function() {
\r
19237 if ( angular.isString(trigger) ) {
\r
19238 element.addClass(trigger);
\r
19239 } else if ( angular.isFunction(trigger) ) {
\r
19240 trigger(element);
\r
19241 } else if ( angular.isObject(trigger) ) {
\r
19242 element.css(trigger);
\r
19244 //If browser does not support transitions, instantly resolve
\r
19245 if ( !endEventName ) {
\r
19246 deferred.resolve(element);
\r
19250 // Add our custom cancel function to the promise that is returned
\r
19251 // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
\r
19252 // i.e. it will therefore never raise a transitionEnd event for that transition
\r
19253 deferred.promise.cancel = function() {
\r
19254 if ( endEventName ) {
\r
19255 element.unbind(endEventName, transitionEndHandler);
\r
19257 deferred.reject('Transition cancelled');
\r
19260 return deferred.promise;
\r
19263 // Work out the name of the transitionEnd event
\r
19264 var transElement = document.createElement('trans');
\r
19265 var transitionEndEventNames = {
\r
19266 'WebkitTransition': 'webkitTransitionEnd',
\r
19267 'MozTransition': 'transitionend',
\r
19268 'OTransition': 'oTransitionEnd',
\r
19269 'transition': 'transitionend'
\r
19271 var animationEndEventNames = {
\r
19272 'WebkitTransition': 'webkitAnimationEnd',
\r
19273 'MozTransition': 'animationend',
\r
19274 'OTransition': 'oAnimationEnd',
\r
19275 'transition': 'animationend'
\r
19277 function findEndEventName(endEventNames) {
\r
19278 for (var name in endEventNames){
\r
19279 if (transElement.style[name] !== undefined) {
\r
19280 return endEventNames[name];
\r
19284 $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
\r
19285 $transition.animationEndEventName = findEndEventName(animationEndEventNames);
\r
19286 return $transition;
\r
19289 .factory('$scrollTo', ['$window', function($window) {
\r
19290 var $scrollTo = function(offsetLeft, offsetTop, duration) {
\r
19291 TweenMax.to($window, duration || 1, {scrollTo: {y: offsetTop, x: offsetLeft}, ease: Power4.easeOut});
\r
19293 return $scrollTo;
\r
19295 .factory('animation', function(){
\r
19298 .factory('$progressBar', function(){
\r
19300 //Provides a function to pass in code for closure purposes
\r
19301 var loadingAnimationCreator = function(onUpdateCallback){
\r
19303 //Use closure to setup some resuable code
\r
19304 var loadingAnimation = function(callback, duration){
\r
19305 TweenMax.to({}, duration, {
\r
19306 onUpdateParams: ["{self}"],
\r
19307 onUpdate: onUpdateCallback,
\r
19308 onComplete: callback
\r
19311 //Returns a function that takes a callback function and a duration for the animation
\r
19312 return (function(){
\r
19313 return loadingAnimation;
\r
19317 return loadingAnimationCreator;
\r
19319 .factory('$height', function(){
\r
19320 var heightAnimation = function(element,duration,height,alpha){
\r
19321 TweenMax.to(element,
\r
19323 {height:height, autoAlpha:alpha},
\r
19326 return heightAnimation;
\r
19328 angular.module('b2b.att.utilities', ['ngSanitize'])
\r
19329 .constant('b2bUtilitiesConfig', {
\r
19336 enableSearch: false,
\r
19337 searchTimer: 200,
\r
19338 circularTraversal: false
\r
19340 .constant('b2bWhenScrollEndsConstants', {
\r
19341 'threshold': 100,
\r
19345 // All breakpoints ranges from >= min and < max
\r
19346 .constant('b2bAwdBreakpoints', {
\r
19362 .filter('groupBy', function ($timeout) {
\r
19363 //Custom GroupBy Filter for treeNav, returns key string and value.childarray as set of grouped elements
\r
19364 return function (data, key) {
\r
19365 if (!key) return data;
\r
19366 var outputPropertyName = '__groupBy__' + key;
\r
19367 if (!data[outputPropertyName]) {
\r
19369 for (var i = 0; i < data.length; i++) {
\r
19370 if (!result[data[i][key]])
\r
19371 result[data[i][key]] = {};
\r
19372 if (!result[data[i][key]].childArray) {
\r
19373 result[data[i][key]].childArray = [];
\r
19375 result[data[i][key]].childArray.push(data[i]);
\r
19376 if (data[i].activeGrp && data[i].activeGrp == true) {
\r
19377 console.log('make ' + data[i].grpChild + ' active');
\r
19378 result[data[i][key]].showGroup = true;
\r
19381 Object.defineProperty(result, 'length', {enumerable: false,value: Object.keys(result).length});
\r
19382 Object.defineProperty(data, outputPropertyName, {enumerable: false,configurable: true,writable:false,value:result});
\r
19383 $timeout(function(){delete data[outputPropertyName];},0,false);
\r
19385 return data[outputPropertyName];
\r
19388 .filter('searchObjectPropertiesFilter', [function() {
\r
19389 return function(items, searchText, attrs) {
\r
19393 var filtered = [];
\r
19394 searchText = searchText.toLowerCase();
\r
19395 angular.forEach(items, function(item) {
\r
19396 angular.forEach(attrs, function(attr) {
\r
19397 if (item.hasOwnProperty(attr) && item[attr].toLowerCase().includes(searchText)) {
\r
19398 filtered.push(item);
\r
19406 .filter('unsafe',[ '$sce', function ($sce) {
\r
19407 return function(val){
\r
19408 return $sce.trustAsHtml(val);
\r
19411 .filter('b2bHighlight', function () {
\r
19412 function escapeRegexp(queryToEscape) {
\r
19413 return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
\r
19415 return function (matchItem, query, className) {
\r
19416 return query && matchItem ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<span class=\"' + className + '\">$&</span>') : matchItem;
\r
19420 Copyright © 2013 Matt Diamond
\r
19421 https://github.com/cwilso/AudioRecorder/blob/master/js/recorderjs/recorder.js
\r
19423 .factory('b2bRecorder', function() {
\r
19425 var Recorder = function(source, cfg) {
\r
19426 var WORKER_PATH = 'recorderWorker.js';
\r
19427 var config = cfg || {};
\r
19428 var bufferLen = config.bufferLen || 4096;
\r
19429 this.context = source.context;
\r
19430 if(!this.context.createScriptProcessor) {
\r
19431 this.node = this.context.createJavacriptProcessor(bufferLen, 2, 2);
\r
19433 this.node = this.context.createScriptProcessor(bufferLen, 2, 2);
\r
19435 var workerCode = 'function init(a){sampleRate=a.sampleRate}function record(a){recBuffersL.push(a[0]),recBuffersR.push(a[1]),recLength+=a[0].length}function exportWAV(a){var b=mergeBuffers(recBuffersL,recLength),c=mergeBuffers(recBuffersR,recLength),d=interleave(b,c),e=encodeWAV(d),f=new Blob([e],{type:a});this.postMessage(f)}function exportMonoWAV(a){var b=mergeBuffers(recBuffersL,recLength),c=encodeWAV(b,!0),d=new Blob([c],{type:a});this.postMessage(d)}function getBuffers(){var a=[];a.push(mergeBuffers(recBuffersL,recLength)),a.push(mergeBuffers(recBuffersR,recLength)),this.postMessage(a)}function clear(){recLength=0,recBuffersL=[],recBuffersR=[]}function mergeBuffers(a,b){for(var c=new Float32Array(b),d=0,e=0;e<a.length;e++)c.set(a[e],d),d+=a[e].length;return c}function interleave(a,b){for(var c=a.length+b.length,d=new Float32Array(c),e=0,f=0;e<c;)d[e++]=a[f],d[e++]=b[f],f++;return d}function floatTo16BitPCM(a,b,c){for(var d=0;d<c.length;d++,b+=2){var e=Math.max(-1,Math.min(1,c[d]));a.setInt16(b,e<0?32768*e:32767*e,!0)}}function writeString(a,b,c){for(var d=0;d<c.length;d++)a.setUint8(b+d,c.charCodeAt(d))}function encodeWAV(a,b){var c=new ArrayBuffer(44+2*a.length),d=new DataView(c);return writeString(d,0,"RIFF"),d.setUint32(4,32+2*a.length,!0),writeString(d,8,"WAVE"),writeString(d,12,"fmt "),d.setUint32(16,16,!0),d.setUint16(20,1,!0),d.setUint16(22,b?1:2,!0),d.setUint32(24,sampleRate,!0),d.setUint32(28,4*sampleRate,!0),d.setUint16(32,4,!0),d.setUint16(34,16,!0),writeString(d,36,"data"),d.setUint32(40,2*a.length,!0),floatTo16BitPCM(d,44,a),d}var recLength=0,recBuffersL=[],recBuffersR=[],sampleRate;this.onmessage=function(a){switch(a.data.command){case"init":init(a.data.config);break;case"record":record(a.data.buffer);break;case"exportWAV":exportWAV(a.data.type);break;case"exportMonoWAV":exportMonoWAV(a.data.type);break;case"getBuffers":getBuffers();break;case"clear":clear()}};';
\r
19436 var blob = new Blob([workerCode]);
\r
19438 var worker = new Worker(window.URL.createObjectURL(blob)); //TODO: Use a blob instead
\r
19439 worker.postMessage({
\r
19442 sampleRate: this.context.sampleRate
\r
19445 var recording = false,
\r
19448 this.node.onaudioprocess = function(e) {
\r
19449 if (!recording) return;
\r
19450 worker.postMessage({
\r
19451 command: 'record',
\r
19453 e.inputBuffer.getChannelData(0),
\r
19454 e.inputBuffer.getChannelData(1)
\r
19459 this.configure = function(cfg) {
\r
19460 for (var prop in cfg) {//TODO: look into using angular.extend() here
\r
19461 if (cfg.hasOwnProperty(prop)) {
\r
19462 config[prop] = cfg[prop];
\r
19467 this.record = function() {
\r
19468 recording = true;
\r
19471 this.stop = function() {
\r
19472 recording = false;
\r
19475 this.clear = function() {
\r
19476 worker.postMessage({ command: 'clear' });
\r
19477 window.URL.revokeObjectURL(blob);
\r
19480 this.getBuffers = function(cb) {
\r
19481 currCallback = cb || config.callback;
\r
19482 worker.postMessage({ command: 'getBuffers' });
\r
19485 this.exportWAV = function(cb, type) {
\r
19486 currCallback = cb || config.callback;
\r
19487 type = type || config.type || 'audio/wav';
\r
19488 if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
\r
19489 worker.postMessage({
\r
19490 command: 'exportWAV',
\r
19495 this.exportMonoWAV = function(cb, type) {
\r
19496 currCallback = cb || config.callback;
\r
19497 type = type || config.type || 'audio/wav';
\r
19498 if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
\r
19499 worker.postMessage({
\r
19500 command: 'exportMonoWAV',
\r
19505 worker.onmessage = function(e) {
\r
19506 var blob = e.data;
\r
19507 currCallback(blob);
\r
19510 source.connect(this.node);
\r
19511 this.node.connect(this.context.destination); // if the script node is not connected to an output the "onaudioprocess" event is not triggerd in Chrome
\r
19518 .factory('b2bViewport', function() {
\r
19519 /* Source: https://gist.github.com/bjankord/2399828 */
\r
19520 var _viewportWidth = function() {
\r
19522 var webkit = (!(window.webkitConvertPointFromNodeToPage == null));
\r
19526 var vpwtest = document.createElement( "div" );
\r
19527 // Sets test div to width 100%, !important overrides any other misc. box model styles that may be set in the CSS
\r
19528 vpwtest.style.cssText = "width:100% !important; margin:0 !important; padding:0 !important; border:none !important;";
\r
19529 document.documentElement.insertBefore( vpwtest, document.documentElement.firstChild );
\r
19530 vpw = vpwtest.offsetWidth;
\r
19531 document.documentElement.removeChild( vpwtest );
\r
19534 else if ( window.innerWidth === undefined ) {
\r
19535 vpw = document.documentElement.clientWidth;
\r
19539 vpw = window.innerWidth;
\r
19545 viewportWidth: _viewportWidth
\r
19548 .directive('b2bWhenScrollEnds', function(b2bWhenScrollEndsConstants, $log) {
\r
19551 link: function (scope, element, attrs) {
\r
19553 * Exposed Attributes:
\r
19554 * threshold - integer - number of pixels before scrollbar hits end that callback is called
\r
19555 * width - integer - override for element's width (px)
\r
19556 * height - integer - override for element's height (px)
\r
19557 * axis - string - x/y for scroll bar axis
\r
19559 var threshold = parseInt(attrs.threshold, 10) || b2bWhenScrollEndsConstants.threshold;
\r
19561 if (!attrs.axis || attrs.axis === '') {
\r
19562 $log.warn('axis attribute must be defined for b2bWhenScrollEnds.');
\r
19566 if (attrs.axis === 'x') {
\r
19567 visibleWidth = parseInt(attrs.width, 10) || b2bWhenScrollEndsConstants.width;
\r
19568 if (element.css('width')) {
\r
19569 visibleWidth = element.css('width').split('px')[0];
\r
19572 element[0].addEventListener('scroll', function() {
\r
19573 var scrollableWidth = element.prop('scrollWidth');
\r
19574 if (scrollableWidth === undefined) {
\r
19575 scrollableWidth = 1;
\r
19577 var hiddenContentWidth = scrollableWidth - visibleWidth;
\r
19579 if (hiddenContentWidth - element[0].scrollLeft <= threshold) {
\r
19580 /* Scroll almost at bottom, load more rows */
\r
19581 scope.$apply(attrs.b2bWhenScrollEnds);
\r
19584 } else if (attrs.axis === 'y') {
\r
19585 visibleHeight = parseInt(attrs.height, 10) || b2bWhenScrollEndsConstants.height;
\r
19586 if (element.css('width')) {
\r
19587 visibleHeight = element.css('height').split('px')[0];
\r
19590 element[0].addEventListener('scroll', function() {
\r
19591 var scrollableHeight = element.prop('scrollHeight');
\r
19592 if (scrollableHeight === undefined) {
\r
19593 scrollableHeight = 1;
\r
19595 var hiddenContentHeight = scrollableHeight - visibleHeight;
\r
19597 if (hiddenContentHeight - element[0].scrollTop <= threshold) {
\r
19598 /* Scroll almost at bottom, load more rows */
\r
19599 scope.$apply(attrs.b2bWhenScrollEnds);
\r
19607 .factory('$windowBind', ['$window', '$timeout', function($window, $timeout) {
\r
19608 var win = angular.element($window);
\r
19609 var _scroll = function (flag, callbackFunc, scope) {
\r
19610 scope.$watch(flag, function (val) {
\r
19611 $timeout(function () {
\r
19613 win.bind('scroll', callbackFunc);
\r
19615 win.unbind('scroll', callbackFunc);
\r
19621 var throttle = function(type, name, obj) {
\r
19622 obj = obj || window;
\r
19623 var running = false;
\r
19624 var func = function() {
\r
19625 if (running) { return; }
\r
19627 requestAnimationFrame(function() {
\r
19628 obj.dispatchEvent(new CustomEvent(name));
\r
19632 obj.addEventListener(type, func);
\r
19635 var _resize = function(callbackFunc, scope) {
\r
19636 throttle("resize", "optimizedResize");
\r
19637 window.addEventListener("optimizedResize", function(event) {
\r
19639 //win.bind(event, callbackFunc);
\r
19640 if (!scope.$$phase) {
\r
19646 var _click = function (flag, callbackFunc, scope) {
\r
19647 scope.$watch(flag, function (val) {
\r
19648 $timeout(function () {
\r
19650 win.bind('click', callbackFunc);
\r
19652 win.unbind('click', callbackFunc);
\r
19658 var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
\r
19659 if (timeoutFlag) {
\r
19660 if (!(timeoutValue)) {
\r
19661 timeoutValue = 0;
\r
19663 scope.$watch(flag, function (newVal, oldVal) {
\r
19664 if (newVal !== oldVal) {
\r
19665 $timeout(function () {
\r
19667 win.bind(event, callbackFunc);
\r
19669 win.unbind(event, callbackFunc);
\r
19671 }, timeoutValue);
\r
19675 scope.$watch(flag, function (newVal, oldVal) {
\r
19676 if (newVal !== oldVal) {
\r
19678 win.bind(event, callbackFunc);
\r
19680 win.unbind(event, callbackFunc);
\r
19695 .factory('keymap', function () {
\r
19717 MAP: { 91 : "COMMAND", 8 : "BACKSPACE" , 9 : "TAB" , 13 : "ENTER" , 16 : "SHIFT" , 17 : "CTRL" , 18 : "ALT" , 19 : "PAUSEBREAK" , 20 : "CAPSLOCK" , 27 : "ESC" , 32 : "SPACE" , 33 : "PAGE_UP", 34 : "PAGE_DOWN" , 35 : "END" , 36 : "HOME" , 37 : "LEFT" , 38 : "UP" , 39 : "RIGHT" , 40 : "DOWN" , 43 : "+" , 44 : "PRINTSCREEN" , 45 : "INSERT" , 46 : "DELETE", 48 : "0" , 49 : "1" , 50 : "2" , 51 : "3" , 52 : "4" , 53 : "5" , 54 : "6" , 55 : "7" , 56 : "8" , 57 : "9" , 59 : ";", 61 : "=" , 65 : "A" , 66 : "B" , 67 : "C" , 68 : "D" , 69 : "E" , 70 : "F" , 71 : "G" , 72 : "H" , 73 : "I" , 74 : "J" , 75 : "K" , 76 : "L", 77 : "M" , 78 : "N" , 79 : "O" , 80 : "P" , 81 : "Q" , 82 : "R" , 83 : "S" , 84 : "T" , 85 : "U" , 86 : "V" , 87 : "W" , 88 : "X" , 89 : "Y" , 90 : "Z", 96 : "0" , 97 : "1" , 98 : "2" , 99 : "3" , 100 : "4" , 101 : "5" , 102 : "6" , 103 : "7" , 104 : "8" , 105 : "9", 106 : "*" , 107 : "+" , 109 : "-" , 110 : "." , 111 : "/", 112 : "F1" , 113 : "F2" , 114 : "F3" , 115 : "F4" , 116 : "F5" , 117 : "F6" , 118 : "F7" , 119 : "F8" , 120 : "F9" , 121 : "F10" , 122 : "F11" , 123 : "F12", 144 : "NUMLOCK" , 145 : "SCROLLLOCK" , 186 : ";" , 187 : "=" , 188 : "," , 189 : "-" , 190 : "." , 191 : "/" , 192 : "`" , 219 : "[" , 220 : "\\" , 221 : "]" , 222 : "'"
\r
19719 isControl: function (e) {
\r
19720 var k = e.keyCode;
\r
19722 case this.KEY.COMMAND:
\r
19723 case this.KEY.SHIFT:
\r
19724 case this.KEY.CTRL:
\r
19725 case this.KEY.ALT:
\r
19736 isFunctionKey: function (k) {
\r
19737 k = k.keyCode ? k.keyCode : k;
\r
19738 return k >= 112 && k <= 123;
\r
19740 isVerticalMovement: function (k) {
\r
19741 return ~[this.KEY.UP, this.KEY.DOWN].indexOf(k);
\r
19743 isHorizontalMovement: function (k) {
\r
19744 return ~[this.KEY.LEFT, this.KEY.RIGHT, this.KEY.BACKSPACE, this.KEY.DELETE].indexOf(k);
\r
19746 isAllowedKey: function (k) {
\r
19747 return (~[this.KEY.SPACE, this.KEY.ESC, this.KEY.ENTER].indexOf(k)) || this.isHorizontalMovement(k) || this.isVerticalMovement(k);
\r
19749 isNumericKey: function (e) {
\r
19750 var k = e.keyCode;
\r
19751 if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105)) {
\r
19757 isAlphaNumericKey: function (e) {
\r
19758 var k = e.keyCode;
\r
19759 if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105) || (k >= 65 && k <= 90)) {
\r
19768 .factory('$isElement', [function () {
\r
19769 var isElement = function (currentElem, targetElem, alternateElem) {
\r
19770 if (currentElem[0] === targetElem[0]) {
\r
19772 } else if (currentElem[0] === alternateElem[0]) {
\r
19775 return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
\r
19779 return isElement;
\r
19782 .factory('events', function () {
\r
19783 var _stopPropagation = function (evt) {
\r
19784 if (evt.stopPropagation) {
\r
19785 evt.stopPropagation();
\r
19787 evt.returnValue = false;
\r
19790 var _preventDefault = function (evt) {
\r
19791 if (evt.preventDefault) {
\r
19792 evt.preventDefault();
\r
19794 evt.returnValue = false;
\r
19798 stopPropagation: _stopPropagation,
\r
19799 preventDefault: _preventDefault
\r
19804 .factory('$documentBind', ['$document', '$timeout', function ($document, $timeout) {
\r
19805 var _click = function (flag, callbackFunc, scope) {
\r
19806 scope.$watch(flag, function (val) {
\r
19807 $timeout(function () {
\r
19809 $document.bind('click', callbackFunc);
\r
19811 $document.unbind('click', callbackFunc);
\r
19817 var _scroll = function (flag, callbackFunc, scope) {
\r
19818 scope.$watch(flag, function (val) {
\r
19819 $timeout(function () {
\r
19821 $document.bind('scroll', callbackFunc);
\r
19823 $document.unbind('scroll', callbackFunc);
\r
19829 var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
\r
19830 if (timeoutFlag) {
\r
19831 if (!(timeoutValue)) {
\r
19832 timeoutValue = 0;
\r
19834 scope.$watch(flag, function (newVal, oldVal) {
\r
19835 if (newVal !== oldVal) {
\r
19836 $timeout(function () {
\r
19838 $document.bind(event, callbackFunc);
\r
19840 $document.unbind(event, callbackFunc);
\r
19842 }, timeoutValue);
\r
19846 scope.$watch(flag, function (newVal, oldVal) {
\r
19847 if (newVal !== oldVal) {
\r
19849 $document.bind(event, callbackFunc);
\r
19851 $document.unbind(event, callbackFunc);
\r
19865 .directive('b2bOnlyNums', function (keymap) {
\r
19868 require: 'ngModel',
\r
19869 link: function (scope, elm, attrs, ctrl) {
\r
19870 var maxChars = attrs.b2bOnlyNums ? attrs.b2bOnlyNums : 4;
\r
19871 elm.on('keydown', function (event) {
\r
19872 if ((event.which >= 48 && event.which <= 57) || (event.which >= 96 && event.which <= 105)) {
\r
19873 // check for maximum characters allowed
\r
19874 if (elm.val().length < maxChars){
\r
19877 event.preventDefault();
\r
19880 } else if ([8, 9, 13, 27, 37, 38, 39, 40].indexOf(event.which) > -1) {
\r
19881 // to allow backspace, tab, enter, escape, arrows
\r
19883 } else if (event.altKey || event.ctrlKey) {
\r
19884 // to allow alter, control, and shift keys
\r
19887 // to stop others
\r
19888 event.preventDefault();
\r
19893 var validateString = function (value) {
\r
19894 if (angular.isUndefined(value) || value === null || value === '') {
\r
19895 return ctrl.$modelValue;
\r
19899 ctrl.$parsers.unshift(validateString);
\r
19904 .directive('b2bKeyupClick', [ function () {
\r
19907 link: function (scope, elem, attr) {
\r
19908 var keyCode = [];
\r
19909 attr.$observe('b2bKeyupClick', function (value) {
\r
19911 keyCode = value.split(',');
\r
19914 elem.bind('keydown keyup', function (ev) {
\r
19915 var keyCodeCondition = function () {
\r
19916 var flag = false;
\r
19917 if (!(ev.keyCode)) {
\r
19919 ev.keyCode = ev.which;
\r
19920 } else if (ev.charCode) {
\r
19921 ev.keyCode = ev.charCode;
\r
19924 if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
\r
19929 if (ev.type === 'keydown' && keyCodeCondition()) {
\r
19930 ev.preventDefault();
\r
19932 else if (ev.type === 'keyup' && keyCodeCondition()) {
\r
19940 .factory('b2bDOMHelper', function() {
\r
19942 var _isTabable = function(node) {
\r
19943 var element = angular.element(node);
\r
19944 var tagName = element[0].tagName.toUpperCase();
\r
19946 if (isHidden(element)) {
\r
19949 if (element.attr('tabindex') !== undefined) {
\r
19950 return (parseInt(element.attr('tabindex'), 10) >= 0);
\r
19952 if (tagName === 'A' || tagName === 'AREA' || tagName === 'BUTTON' || tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
\r
19953 if (tagName === 'A' || tagName === 'AREA') {
\r
19954 // anchors/areas without href are not focusable
\r
19955 return (element[0].href !== '');
\r
19957 return !(element[0].disabled || element[0].readOnly);
\r
19962 function isValidChild(child) {
\r
19963 return child.nodeType == 1 && child.nodeName != 'SCRIPT' && child.nodeName != 'STYLE';
\r
19966 function isHidden(obj) {
\r
19967 var elem = angular.element(obj);
\r
19968 var elemStyle = undefined;
\r
19969 if(obj instanceof HTMLElement){
\r
19970 elemStyle = window.getComputedStyle(obj);
\r
19973 elemStyle = window.getComputedStyle(obj[0]);
\r
19975 return elem.hasClass('ng-hide') || elem.css('display') === 'none' || elemStyle.display === 'none' || elemStyle.visibility === 'hidden' || elem.css('visibility') === 'hidden';
\r
19978 function hasValidParent(obj) {
\r
19979 return (isValidChild(obj) && obj.parentElement.nodeName !== 'BODY');
\r
19982 function traverse(obj, fromTop) {
\r
19983 var obj = obj || document.getElementsByTagName('body')[0];
\r
19984 if (isValidChild(obj) && _isTabable(obj)) {
\r
19987 // If object is hidden, skip it's children
\r
19988 if (isValidChild(obj) && isHidden(obj)) {
\r
19989 return undefined;
\r
19991 // If object is hidden, skip it's children
\r
19992 if (angular.element(obj).hasClass('ng-hide')) {
\r
19993 return undefined;
\r
19995 if (obj.hasChildNodes()) {
\r
19998 child = obj.firstChild;
\r
20000 child = obj.lastChild;
\r
20003 var res = traverse(child, fromTop);
\r
20009 child = child.nextSibling;
\r
20011 child = child.previousSibling;
\r
20017 return undefined;
\r
20021 var _previousElement = function(el, isFocusable){
\r
20024 if (el.hasOwnProperty('length')) {
\r
20028 var parent = elem.parentElement;
\r
20029 var previousElem = undefined;
\r
20031 if(isFocusable) {
\r
20032 if (hasValidParent(elem)) {
\r
20033 var siblings = angular.element(parent).children();
\r
20034 if (siblings.length > 0) {
\r
20035 // Good practice to splice out the elem from siblings if there, saving some time.
\r
20036 // We allow for a quick check for jumping to parent first before removing.
\r
20037 if (siblings[0] === elem) {
\r
20038 // If we are looking at immidiate parent and elem is first child, we need to go higher
\r
20039 var e = _previousElement(angular.element(elem).parent(), isFocusable);
\r
20040 if (_isTabable(e)) {
\r
20044 // I need to filter myself and any nodes next to me from the siblings
\r
20045 var indexOfElem = Array.prototype.indexOf.call(siblings, elem);
\r
20046 siblings = Array.prototype.filter.call(siblings, function(item, itemIndex) {
\r
20047 if (!angular.equals(elem, item) && itemIndex < indexOfElem) {
\r
20052 // We need to search backwards
\r
20053 for (var i = 0; i <= siblings.length-1; i++) {//for (var i = siblings.length-1; i >= 0; i--) {
\r
20054 var ret = traverse(siblings[i], false);
\r
20055 if (ret !== undefined) {
\r
20060 var e = _previousElement(angular.element(elem).parent(), isFocusable);
\r
20061 if (_isTabable(e)) {
\r
20067 var siblings = angular.element(parent).children();
\r
20068 if (siblings.length > 1) {
\r
20069 // Since indexOf is on Array.prototype and parent.children is a NodeList, we have to use call()
\r
20070 var index = Array.prototype.indexOf.call(siblings, elem);
\r
20071 previousElem = siblings[index-1];
\r
20074 return previousElem;
\r
20077 var _lastTabableElement = function(el) {
\r
20078 /* This will return the first tabable element from the parent el */
\r
20080 if (el.hasOwnProperty('length')) {
\r
20084 return traverse(elem, false);
\r
20087 var _firstTabableElement = function(el) {
\r
20088 /* This will return the first tabable element from the parent el */
\r
20090 if (el.hasOwnProperty('length')) {
\r
20094 return traverse(elem, true);
\r
20097 var _isInDOM = function(obj) {
\r
20098 return document.documentElement.contains(obj);
\r
20102 firstTabableElement: _firstTabableElement,
\r
20103 lastTabableElement: _lastTabableElement,
\r
20104 previousElement: _previousElement,
\r
20105 isInDOM: _isInDOM,
\r
20106 isTabable: _isTabable,
\r
20107 isHidden: isHidden
\r
20111 .factory('trapFocusInElement', ['$document', '$isElement', 'DOMHelper', 'keymap', function ($document, $isElement, DOMHelper, keymap) {
\r
20112 var elementStack = [];
\r
20113 var stackHead = undefined;
\r
20114 var trapFocusInElement = function (flag) {
\r
20115 var bodyElements = $document.find('body').children();
\r
20117 var firstTabableElement = angular.element(DOMHelper.firstTabableElement(stackHead));
\r
20118 var lastTabableElement = angular.element(DOMHelper.lastTabableElement(stackHead));
\r
20120 var trapKeyboardFocusInFirstElement = function (e) {
\r
20121 if (!e.keyCode) {
\r
20122 e.keyCode = e.which;
\r
20125 if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
\r
20126 lastTabableElement[0].focus();
\r
20127 e.preventDefault(e);
\r
20128 e.stopPropagation(e);
\r
20133 var trapKeyboardFocusInLastElement = function (e) {
\r
20134 if (!e.keyCode) {
\r
20135 e.keyCode = e.which;
\r
20138 if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
\r
20139 firstTabableElement[0].focus();
\r
20140 e.preventDefault(e);
\r
20141 e.stopPropagation(e);
\r
20146 for (var i = 0; i < bodyElements.length; i++) {
\r
20147 if (bodyElements[i] !== stackHead[0]) {
\r
20148 bodyElements.eq(i).attr('aria-hidden', true);
\r
20151 firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
\r
20152 lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
\r
20154 for (var j = 0; j < bodyElements.length; j++) {
\r
20155 if (bodyElements[j] !== stackHead[0]) {
\r
20156 bodyElements.eq(j).removeAttr('aria-hidden');
\r
20159 firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
\r
20160 lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
\r
20163 var toggleTrapFocusInElement = function (flag, element) {
\r
20164 if (angular.isDefined(flag) && angular.isDefined(element)) {
\r
20165 if (angular.isUndefined(stackHead)) {
\r
20166 stackHead = element;
\r
20167 trapFocusInElement(flag);
\r
20170 trapFocusInElement(false);
\r
20171 elementStack.push(stackHead);
\r
20172 stackHead = element;
\r
20173 trapFocusInElement(true);
\r
20175 if (stackHead.prop('$$hashKey') === element.prop('$$hashKey')) {
\r
20176 trapFocusInElement(false);
\r
20177 stackHead = elementStack.pop();
\r
20178 if (angular.isDefined(stackHead)) {
\r
20179 trapFocusInElement(true);
\r
20187 return toggleTrapFocusInElement;
\r
20190 .factory('DOMHelper', function () {
\r
20192 var _isTabable = function (node) {
\r
20193 var element = angular.element(node);
\r
20194 var tagName = element[0].tagName.toUpperCase();
\r
20196 if (isHidden(element)) {
\r
20199 if (element.attr('tabindex') !== undefined) {
\r
20200 return (parseInt(element.attr('tabindex'), 10) >= 0);
\r
20202 if (tagName === 'A' || tagName === 'AREA' || tagName === 'BUTTON' || tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
\r
20203 if (tagName === 'A' || tagName === 'AREA') {
\r
20204 // anchors/areas without href are not focusable
\r
20205 return (element[0].href !== '');
\r
20207 return !(element[0].disabled || element[0].readOnly);
\r
20212 function isValidChild(child) {
\r
20213 return child.nodeType == 1 && child.nodeName != 'SCRIPT' && child.nodeName != 'STYLE';
\r
20216 function isHidden(obj) {
\r
20217 var elem = angular.element(obj);
\r
20220 style = window.getComputedStyle(obj);
\r
20223 style = window.getComputedStyle(obj[0]);
\r
20226 // getComputedStyle() for Zepto object returns null
\r
20227 if (style === null){
\r
20228 return elem.hasClass('ng-hide') || elem.css('display') === 'none';
\r
20231 return elem.hasClass('ng-hide') || elem.css('display') === 'none' || style.display === 'none' || style.display === 'hidden';
\r
20234 function traverse(obj, fromTop) {
\r
20235 var obj = obj || document.getElementsByTagName('body')[0];
\r
20237 if (isValidChild(obj) && _isTabable(obj)) {
\r
20241 // If object is hidden, skip it's children
\r
20242 if (isValidChild(obj) && isHidden(obj)) {
\r
20243 return undefined;
\r
20245 // If object is hidden, skip it's children
\r
20246 if (angular.element(obj).hasClass('ng-hide')) {
\r
20247 return undefined;
\r
20250 if (obj.hasChildNodes()) {
\r
20253 child = obj.firstChild;
\r
20255 child = obj.lastChild;
\r
20258 var res = traverse(child, fromTop);
\r
20263 child = child.nextSibling;
\r
20265 child = child.previousSibling;
\r
20270 return undefined;
\r
20274 var _lastTabableElement = function (el) {
\r
20275 /* This will return the last tabable element from the parent el */
\r
20277 if (el.hasOwnProperty('length')) {
\r
20281 return traverse(elem, false);
\r
20284 var _firstTabableElement = function (el) {
\r
20285 /* This will return the first tabable element from the parent el */
\r
20287 if (el.hasOwnProperty('length')) {
\r
20291 return traverse(elem, true);
\r
20295 firstTabableElement: _firstTabableElement,
\r
20296 lastTabableElement: _lastTabableElement,
\r
20297 isTabable: _isTabable
\r
20301 .factory('windowOrientation', ['$window', function ($window) {
\r
20302 var _isPotrait = function () {
\r
20303 if ($window.innerHeight > $window.innerWidth) {
\r
20309 var _isLandscape = function () {
\r
20310 if ($window.innerHeight < $window.innerWidth) {
\r
20318 isPotrait: _isPotrait,
\r
20319 isLandscape: _isLandscape
\r
20323 .directive('b2bNextElement', function() {
\r
20326 transclude: false,
\r
20327 link: function (scope, elem, attr, ctrls) {
\r
20329 var keys = attr.b2bNextElement.split(',');
\r
20331 elem.bind('keydown', function (e) {
\r
20332 var nextElement = elem.next();
\r
20333 if(e.keyCode == 39 || e.keyCode == 40){ // if e.keyCode in keys
\r
20334 if(nextElement.length) {
\r
20335 e.preventDefault();
\r
20336 nextElement[0].focus();
\r
20344 .directive('b2bAccessibilityClick', [function () {
\r
20347 link: function (scope, elem, attr, ctrl) {
\r
20348 var keyCode = [];
\r
20349 attr.$observe('b2bAccessibilityClick', function (value) {
\r
20351 keyCode = value.split(',');
\r
20354 elem.bind('keydown keypress', function (ev) {
\r
20355 var keyCodeCondition = function () {
\r
20356 var flag = false;
\r
20357 if (!(ev.keyCode)) {
\r
20359 ev.keyCode = ev.which;
\r
20360 } else if (ev.charCode) {
\r
20361 ev.keyCode = ev.charCode;
\r
20364 if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
\r
20369 if (keyCode.length > 0 && keyCodeCondition()) {
\r
20371 ev.preventDefault();
\r
20378 .directive('b2bReset', ['$compile', function ($compile) {
\r
20381 require: ['?ngModel', 'b2bReset'],
\r
20382 controller: ['$scope', function ($scope) {
\r
20383 var resetButton = angular.element('<button type="button" class="reset-field" tabindex="-1" aria-label="Click to reset" aria-hidden="true" role="button"></button>');
\r
20385 this.getResetButton = function () {
\r
20386 return resetButton;
\r
20389 link: function (scope, element, attrs, ctrls) {
\r
20391 var ngModelCtrl = ctrls[0];
\r
20392 var ctrl = ctrls[1];
\r
20394 var resetButton = ctrl.getResetButton();
\r
20397 resetButton.on('click', function () {
\r
20398 element[0].value = '';
\r
20400 if (ngModelCtrl) {
\r
20401 if (attrs.b2bReset) {
\r
20402 ngModelCtrl.$setViewValue(attrs.b2bReset);
\r
20404 ngModelCtrl.$setViewValue('');
\r
20406 element[0].value = ngModelCtrl.$viewValue;
\r
20407 ngModelCtrl.$render();
\r
20410 element[0].focus();
\r
20411 element[0].select();
\r
20414 var addResetButton = function () {
\r
20415 element.after(resetButton);
\r
20416 element.unbind('focus input', addResetButton);
\r
20419 element.bind('focus input', addResetButton);
\r
20424 .directive('b2bPrevElement', ['b2bDOMHelper', 'keymap', function (b2bDOMHelper, keymap) {
\r
20427 transclude: false,
\r
20428 link: function (scope, elem, attr) {
\r
20430 elem.bind('keydown', function (e) {
\r
20431 if(e.keyCode == 37 || e.keyCode == 38){
\r
20432 var prev = b2bDOMHelper.previousElement(elem, false);
\r
20433 if(prev !== undefined) {
\r
20434 e.preventDefault();
\r
20443 * @param {integer} delay - Timeout before first and last focusable elements are found
\r
20444 * @param {boolean} trigger - A variable on scope that will trigger refinding first/last focusable elements
\r
20446 .directive('b2bTrapFocusInsideElement', ['$timeout', 'b2bDOMHelper', 'keymap', 'events', function ($timeout, b2bDOMHelper, keymap, events) {
\r
20449 transclude: false,
\r
20450 link: function (scope, elem, attr) {
\r
20452 var delay = parseInt(attr.delay, 10) || 10;
\r
20454 /* Before opening modal, find the focused element */
\r
20455 var firstTabableElement = undefined,
\r
20456 lastTabableElement = undefined;
\r
20458 function init() {
\r
20459 $timeout(function () {
\r
20460 firstTabableElement = b2bDOMHelper.firstTabableElement(elem);
\r
20461 lastTabableElement = b2bDOMHelper.lastTabableElement(elem);
\r
20462 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
\r
20463 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
\r
20464 }, delay, false);
\r
20467 if (attr.trigger !== undefined) {
\r
20468 scope.$watch('trigger', function() {
\r
20469 if (scope.trigger) {
\r
20475 var firstTabableElementKeyhandler = function(e) {
\r
20476 if (!e.keyCode) {
\r
20477 e.keyCode = e.which;
\r
20479 if (e.keyCode === keymap.KEY.TAB && e.shiftKey) {
\r
20480 if (attr.trapFocusInsideElement === 'true') {
\r
20481 var temp = b2bDOMHelper.lastTabableElement(elem);
\r
20482 if (lastTabableElement !== temp) {
\r
20483 // Unbind keydown handler on lastTabableElement
\r
20484 angular.element(lastTabableElement).unbind('keydown', lastTabableElementKeyhandler);
\r
20485 lastTabableElement = temp;
\r
20486 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
\r
20489 lastTabableElement.focus();
\r
20490 events.preventDefault(e);
\r
20491 events.stopPropagation(e);
\r
20495 var lastTabableElementKeyhandler = function(e) {
\r
20496 if (!e.keyCode) {
\r
20497 e.keyCode = e.which;
\r
20499 if (e.keyCode === keymap.KEY.TAB && !e.shiftKey) {
\r
20500 if (attr.trapFocusInsideElement === 'true') {
\r
20501 var temp = b2bDOMHelper.firstTabableElement(elem);
\r
20502 if (firstTabableElement !== temp) {
\r
20503 // Unbind keydown handler on firstTabableElement
\r
20504 angular.element(firstTabableElement).unbind('keydown', firstTabableElementKeyhandler);
\r
20505 firstTabableElement = temp;
\r
20506 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
\r
20509 firstTabableElement.focus();
\r
20510 events.preventDefault(e);
\r
20511 events.stopPropagation(e);
\r
20520 .factory('trapFocusInElement', ['$document', '$isElement', 'DOMHelper', 'keymap', '$interval', function ($document, $isElement, DOMHelper, keymap, $interval) {
\r
20521 var elementStack = [];
\r
20522 var stackHead = undefined;
\r
20523 var stopInterval = undefined;
\r
20524 var intervalRequired = false;
\r
20525 var interval = 1000;
\r
20526 var firstTabableElement, lastTabableElement;
\r
20528 var trapKeyboardFocusInFirstElement = function (e) {
\r
20529 if (!e.keyCode) {
\r
20530 e.keyCode = e.which;
\r
20533 if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
\r
20534 lastTabableElement[0].focus();
\r
20535 e.preventDefault(e);
\r
20536 e.stopPropagation(e);
\r
20541 var trapKeyboardFocusInLastElement = function (e) {
\r
20542 if (!e.keyCode) {
\r
20543 e.keyCode = e.which;
\r
20546 if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
\r
20547 firstTabableElement[0].focus();
\r
20548 e.preventDefault(e);
\r
20549 e.stopPropagation(e);
\r
20553 var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
\r
20554 var bodyElements = $document.find('body').children();
\r
20556 firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(DOMHelper.firstTabableElement(stackHead));
\r
20557 lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(DOMHelper.lastTabableElement(stackHead));
\r
20560 for (var i = 0; i < bodyElements.length; i++) {
\r
20561 if (bodyElements[i] !== stackHead[0]) {
\r
20562 bodyElements.eq(i).attr('aria-hidden', true);
\r
20565 firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
\r
20566 lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
\r
20568 for (var j = 0; j < bodyElements.length; j++) {
\r
20569 if (bodyElements[j] !== stackHead[0]) {
\r
20570 bodyElements.eq(j).removeAttr('aria-hidden');
\r
20573 firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
\r
20574 lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
\r
20577 if (intervalRequired && flag) {
\r
20578 stopInterval = $interval(function () {
\r
20579 var firstTabableElementTemp = angular.element(DOMHelper.firstTabableElement(stackHead));
\r
20580 var lastTabableElementTemp = angular.element(DOMHelper.lastTabableElement(stackHead));
\r
20581 if (firstTabableElementTemp[0] !== firstTabableElement[0] || lastTabableElementTemp[0] !== lastTabableElement[0]) {
\r
20582 $interval.cancel(stopInterval);
\r
20583 stopInterval = undefined;
\r
20584 trapFocusInElement(false, firstTabableElement, lastTabableElement);
\r
20585 trapFocusInElement(true, firstTabableElementTemp, lastTabableElementTemp);
\r
20589 if (stopInterval) {
\r
20590 $interval.cancel(stopInterval);
\r
20591 stopInterval = undefined;
\r
20595 var toggleTrapFocusInElement = function (flag, element, intervalRequiredParam, intervalParam) {
\r
20596 intervalRequired = intervalRequiredParam ? intervalRequiredParam : intervalRequired;
\r
20597 interval = intervalParam ? intervalParam : interval;
\r
20598 if (angular.isDefined(flag) && angular.isDefined(element)) {
\r
20599 if (flag && angular.isUndefined(stackHead)) {
\r
20600 stackHead = element;
\r
20601 trapFocusInElement(flag);
\r
20604 trapFocusInElement(false);
\r
20605 elementStack.push(stackHead);
\r
20606 stackHead = element;
\r
20607 trapFocusInElement(true);
\r
20609 if (angular.isDefined(stackHead) && (stackHead[0] === element[0])) {
\r
20610 trapFocusInElement(false);
\r
20611 stackHead = elementStack.pop();
\r
20612 if (angular.isDefined(stackHead)) {
\r
20613 trapFocusInElement(true);
\r
20619 if (angular.isDefined(stackHead)) {
\r
20620 trapFocusInElement(false, firstTabableElement, lastTabableElement);
\r
20621 trapFocusInElement(true);
\r
20626 return toggleTrapFocusInElement;
\r
20629 .directive('b2bSetNextFocusOn', ['b2bDOMHelper', '$timeout', function(b2bDOMHelper, $timeout) {
\r
20633 link: function (scope, elem, attr) {
\r
20634 elem.bind('click', function(){
\r
20635 var firstFocusableElement = undefined;
\r
20636 var containerElem = undefined;
\r
20637 var containerArray = [];
\r
20638 var timeout = parseInt(attr.setNextFocusTimeout, 0) | 100;
\r
20639 var index = parseInt(attr.b2bSetNextFocusIndex, 0) | 0;
\r
20642 *Fix for IE7 and lower
\r
20643 *polyfill src: https://github.com/HubSpot/pace/issues/102
\r
20645 if (!document.querySelectorAll) {
\r
20646 document.querySelectorAll = function (selectors) {
\r
20647 var style = document.createElement('style'), elements = [], element;
\r
20648 document.documentElement.firstChild.appendChild(style);
\r
20649 document._qsa = [];
\r
20651 style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
\r
20652 window.scrollBy(0, 0);
\r
20653 style.parentNode.removeChild(style);
\r
20655 while (document._qsa.length) {
\r
20656 element = document._qsa.shift();
\r
20657 element.style.removeAttribute('x-qsa');
\r
20658 elements.push(element);
\r
20660 document._qsa = null;
\r
20665 if (attr.b2bSetNextFocusOn === '') {
\r
20668 containerArray = attr.b2bSetNextFocusOn.split(' ');
\r
20670 $timeout(function(){
\r
20672 do { // cycles thru containerArray until finds a match in DOM to set focus to
\r
20673 containerElem = document.querySelectorAll(containerArray[i])[index];
\r
20675 } while ( (!containerElem) && (i < containerArray.length) );
\r
20676 if(containerElem){
\r
20677 if (!angular.isDefined(firstFocusableElement)) {
\r
20678 firstFocusableElement = b2bDOMHelper.firstTabableElement(containerElem);
\r
20680 firstFocusableElement.focus();
\r
20682 }, timeout, false)
\r
20689 .directive('b2bInputAllow', [function() {
\r
20692 require: 'ngModel',
\r
20693 link: function (scope, elem, attr, ctrl) {
\r
20694 var regexExpression = null;
\r
20695 attr.$observe('b2bInputAllow', function (value) {
\r
20697 regexExpression = new RegExp(value);
\r
20700 var isValid = function(str) {
\r
20701 if (regexExpression !== null) {
\r
20702 return regexExpression.test(str);
\r
20706 elem.bind('keypress', function($event) {
\r
20707 var charcode = String.fromCharCode($event.which || $event.keyCode);
\r
20708 if (!isValid(charcode)) {
\r
20709 $event.preventDefault();
\r
20710 $event.stopPropagation();
\r
20713 elem.bind('input', function (evt) {
\r
20714 var inputString = ctrl.$viewValue;
\r
20715 if (isValid(inputString)) {
\r
20716 ctrl.$setViewValue(inputString);
\r
20725 .directive('b2bInputDeny', [function() {
\r
20728 require: 'ngModel',
\r
20729 link: function (scope, elem, attr, ctrl) {
\r
20730 var regexExpression = null;
\r
20731 attr.$observe('b2bInputDeny', function (value) {
\r
20733 regexExpression = new RegExp(value, 'g');
\r
20736 elem.bind('input', function () {
\r
20737 var inputString = ctrl.$viewValue && ctrl.$viewValue.replace(regexExpression, '');
\r
20738 if (inputString !== ctrl.$viewValue) {
\r
20739 ctrl.$setViewValue(inputString);
\r
20748 .directive('b2bDragonInput', [function() {
\r
20751 require: 'ngModel',
\r
20752 link: function (scope, elem, attr, ctrl) {
\r
20753 elem.on('focus keyup', function(){
\r
20754 elem.triggerHandler('change');
\r
20760 .directive('b2bKey', ['b2bUtilitiesConfig', '$timeout', 'keymap', function (b2bUtilitiesConfig, $timeout, keymap) {
\r
20763 controller: ['$scope', '$element', '$attrs', function ($scope, $element,attr) {
\r
20764 this.childElements = [];
\r
20765 this.disableNodes = {};
\r
20766 this.enableSearch = attr.enableSearch !== undefined ? true : b2bUtilitiesConfig.enableSearch;
\r
20767 this.circularTraversal = attr.circularTraversal !== undefined ? true : b2bUtilitiesConfig.circularTraversal;
\r
20768 this.counter = -1;
\r
20769 if (this.enableSearch) {
\r
20770 this.searchKeys = [];
\r
20772 var searchString = '';
\r
20774 var selfCtrl = this;
\r
20776 this.childElementsList = [];
\r
20778 this.b2bKeyID = "";
\r
20780 if (angular.isDefined(attr.b2bKey)) {
\r
20781 this.b2bKeyID = attr.b2bKey;
\r
20784 this.calculateChildElementsList = function () {
\r
20785 return $element[0].querySelectorAll("[b2b-key-item='" + this.b2bKeyID + "']:not([disabled])");
\r
20788 this.resetChildElementsList = function () {
\r
20789 return $timeout(function () {
\r
20790 selfCtrl.childElementsList = selfCtrl.calculateChildElementsList();
\r
20794 this.resetChildElementsList();
\r
20796 $scope.$on('b2b-key-reset-child-elements-list', function () {
\r
20797 selfCtrl.resetChildElementsList();
\r
20801 this.registerElement = function (childElement, searchKey) {
\r
20802 this.childElements.push(childElement);
\r
20803 if (this.enableSearch) {
\r
20804 this.searchKeys.push(searchKey);
\r
20806 var count = this.childElements.length - 1;
\r
20807 this.maxLength = count + 1;
\r
20810 this.toggleDisable = function (count, state) {
\r
20811 this.disableNodes[count] = state;
\r
20813 this.searchElement = function (searchExp) {
\r
20814 var regex = new RegExp("\\b" + searchExp, "gi");
\r
20815 var position = this.searchKeys.regexIndexOf(regex, this.counter + 1, true);
\r
20816 if (position > -1) {
\r
20817 this.counter = position;
\r
20818 this.moveFocus(this.counter);
\r
20821 this.startTimer = function (time) {
\r
20822 if (searchString === '') {
\r
20823 $timeout(function () {
\r
20824 searchString = '';
\r
20828 this.resetCounter = function (count) {
\r
20829 this.counter = count;
\r
20831 this.moveNext = function (count) {
\r
20832 this.counter = (this.counter + count) < this.maxLength ? this.counter + count : (this.circularTraversal ? 0 : this.counter);
\r
20833 if (this.disableNodes[this.counter]) {
\r
20834 if ((this.counter + count) < this.maxLength) {
\r
20835 this.moveNext(count);
\r
20838 this.moveFocus(this.counter);
\r
20841 this.movePrev = function (count) {
\r
20842 this.counter = (this.counter - count) > -1 ? this.counter - count : (this.circularTraversal ? this.maxLength-1 : this.counter);
\r
20843 if (this.disableNodes[this.counter]) {
\r
20844 if ((this.counter - count) > -1) {
\r
20845 this.movePrev(count);
\r
20848 this.moveFocus(this.counter);
\r
20851 this.moveFocus = function (index) {
\r
20852 this.childElements[index][0].focus();
\r
20855 this.keyDownHandler = function (ev, count) {
\r
20856 if (angular.isDefined(count) && !isNaN(count) && count !== this.counter) {
\r
20857 this.resetCounter(count);
\r
20859 if (!ev.keyCode) {
\r
20861 ev.keyCode = ev.which;
\r
20862 } else if (ev.charCode) {
\r
20863 ev.keyCode = ev.charCode;
\r
20866 if (ev.keyCode) {
\r
20867 if (this.prev && this.prev.indexOf(ev.keyCode.toString()) > -1) {
\r
20868 this.movePrev(1);
\r
20869 ev.preventDefault();
\r
20870 ev.stopPropagation();
\r
20871 } else if (this.next && this.next.indexOf(ev.keyCode.toString()) > -1) {
\r
20872 this.moveNext(1);
\r
20873 ev.preventDefault();
\r
20874 ev.stopPropagation();
\r
20875 } else if (this.up && this.up.indexOf(ev.keyCode.toString()) > -1) {
\r
20876 if (this.type === 'table') {
\r
20877 this.movePrev(this.columns);
\r
20878 ev.preventDefault();
\r
20879 ev.stopPropagation();
\r
20881 } else if (this.down && this.down.indexOf(ev.keyCode.toString()) > -1) {
\r
20882 if (this.type === 'table') {
\r
20883 this.moveNext(this.columns);
\r
20884 ev.preventDefault();
\r
20885 ev.stopPropagation();
\r
20887 } else if (ev.keyCode === keymap.KEY.HOME) {
\r
20888 var firstIndex = 0;
\r
20889 while (this.disableNodes[firstIndex] !== false) {
\r
20892 var count = this.counter - firstIndex;
\r
20893 this.movePrev(count);
\r
20894 ev.preventDefault();
\r
20895 ev.stopPropagation();
\r
20896 } else if (ev.keyCode === keymap.KEY.END) {
\r
20897 var lastIndex = this.childElements.length - 1;
\r
20898 while (this.disableNodes[lastIndex] !== false) {
\r
20901 var count = lastIndex - this.counter;
\r
20902 this.moveNext(count);
\r
20903 ev.preventDefault();
\r
20904 ev.stopPropagation();
\r
20905 } else if (ev.keyCode >= 48 && ev.keyCode <= 105) {
\r
20906 if (this.enableSearch) {
\r
20907 this.startTimer(b2bUtilitiesConfig.searchTimer);
\r
20908 searchString = searchString + (keymap.MAP[ev.keyCode] || '');
\r
20909 this.searchElement(searchString);
\r
20910 ev.preventDefault();
\r
20911 ev.stopPropagation();
\r
20917 link: function (scope, elem, attr, ctrl) {
\r
20918 ctrl.prev = attr.prev ? attr.prev.split(',') : b2bUtilitiesConfig.prev.split(',');
\r
20919 ctrl.next = attr.next ? attr.next.split(',') : b2bUtilitiesConfig.next.split(',');
\r
20920 ctrl.type = attr.type ? attr.type : b2bUtilitiesConfig.type;
\r
20921 if (ctrl.type === 'table') {
\r
20922 ctrl.up = attr.up ? attr.up.split(',') : b2bUtilitiesConfig.up.split(',');
\r
20923 ctrl.down = attr.down ? attr.down.split(',') : b2bUtilitiesConfig.down.split(',');
\r
20924 ctrl.columns = attr.columns ? parseInt(attr.columns, 10) : b2bUtilitiesConfig.columns;
\r
20927 elem.bind('keydown', function (ev) {
\r
20928 ctrl.keyDownHandler(ev);
\r
20934 .directive('b2bKeyItem', [function () {
\r
20937 link: function (scope, elem, attr, ctrl) {
\r
20938 var parentCtrl = (elem.parent() && elem.parent().controller('b2bKey')) || undefined;
\r
20939 if (angular.isDefined(parentCtrl)) {
\r
20940 var count = parentCtrl.registerElement(elem, attr.searchKey);
\r
20941 elem.bind('keydown', function (ev) {
\r
20942 parentCtrl.keyDownHandler(ev, count);
\r
20944 scope.$watch(attr.b2bKeyItem, function (value) {
\r
20945 value = value === undefined ? true : value;
\r
20946 parentCtrl.toggleDisable(count, !value);
\r
20948 scope.$on('$destroy', function () {
\r
20949 parentCtrl.toggleDisable(count, true);
\r
20956 .directive('b2bElementFocus', [function () {
\r
20959 link: function (scope, elem, attr, ctrl) {
\r
20960 scope.$watch(attr.b2bElementFocus, function (value) {
\r
20961 if (value === true) {
\r
20970 .directive('b2bAppendElement', ['$compile', function ($compile) {
\r
20973 link: function (scope, elem, attr, ctrl) {
\r
20974 var parameters = attr.b2bAppendElement.split(':');
\r
20975 if (parameters.length === 1) {
\r
20976 elem.append(scope.$eval(parameters[0]));
\r
20977 } else if (parameters.length === 2) {
\r
20978 if (parameters[1] === 'compile') {
\r
20979 var element = angular.element('<span>' + scope.$eval(parameters[0]) + '</span>');
\r
20980 elem.append($compile(element)(scope));
\r
20988 .directive('b2bKeyItemRefreshInNgRepeat', [function () {
\r
20991 require: '^^b2bKey',
\r
20992 link: function (scope, elem, attr, parentCtrl) {
\r
20993 if (angular.isDefined(parentCtrl)) {
\r
20995 var attrToObserve = 'attrToObserve';
\r
20997 if (attr.b2bKeyItemRefreshInNgRepeat) {
\r
20998 attrToObserve = 'b2bKeyItemRefreshInNgRepeat';
\r
21001 attr.$observe(attrToObserve, function (newVal, oldVal) {
\r
21002 if (newVal && newVal !== oldVal) {
\r
21003 parentCtrl.resetChildElementsList();
\r
21011 .filter('b2bMultiSepartorHighlight', function($sce) {
\r
21012 return function(text, searchText, searchSeperator) {
\r
21013 var splitText = function(string) {
\r
21014 if(angular.isDefined(searchSeperator)){
\r
21015 if (string.indexOf(searchSeperator) > -1) {
\r
21016 return string.split(searchSeperator);
\r
21025 var newText = splitText(text);
\r
21026 var newPhrase = splitText(searchText);
\r
21027 if (angular.isArray(newPhrase)) {
\r
21028 for (var i = 0; i < newText.length; i++) {
\r
21030 text = newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
\r
21031 '<span class="b2b-search-hightlight">$1</span>');
\r
21033 text = text + searchSeperator + ' ' + (newPhrase[i] ? newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
\r
21034 '<span class="b2b-search-hightlight">$1</span>') : newText[i]);
\r
21038 text = text.replace(new RegExp('(' + searchText + ')', 'gi'),
\r
21039 '<span class="b2b-search-hightlight">$1</span>');
\r
21042 return $sce.trustAsHtml(text)
\r
21046 .factory('b2bUserAgent', [function() {
\r
21047 var _isMobile = function() {
\r
21048 return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
\r
21050 var _notMobile = function() {
\r
21051 return !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
\r
21053 var _isIE = function() {
\r
21054 return /msie|trident/i.test(navigator.userAgent);
\r
21056 var _isFF = function() {
\r
21057 return /Firefox/.test(navigator.userAgent);
\r
21059 var _isChrome = function() {
\r
21060 return /Google Inc/.test(navigator.vendor);
\r
21062 var _isSafari = function() {
\r
21063 return /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
\r
21067 isMobile: _isMobile,
\r
21068 notMobile: _notMobile,
\r
21071 isChrome: _isChrome,
\r
21072 isSafari: _isSafari
\r
21075 .run(['$document', 'b2bUserAgent', function($document, b2bUserAgent) {
\r
21076 var html = $document.find('html').eq(0);
\r
21077 if (b2bUserAgent.isIE()) {
\r
21078 html.addClass('isIE');
\r
21080 html.removeClass('isIE');
\r
21086 String.prototype.toSnakeCase = function () {
\r
21087 return this.replace(/([A-Z])/g, function ($1) {
\r
21088 return "-" + $1.toLowerCase();
\r
21091 var concat = function (character, times) {
\r
21092 character = character || '';
\r
21093 times = (!isNaN(times) && times) || 0;
\r
21094 var finalChar = '';
\r
21095 for (var i = 0; i < times; i++) {
\r
21096 finalChar += character;
\r
21098 return finalChar;
\r
21101 // direction: true for left and false for right
\r
21102 var pad = function (actualString, width, character, direction) {
\r
21103 actualString = actualString || '';
\r
21104 width = (!isNaN(width) && width) || 0;
\r
21105 character = character || '';
\r
21106 if (width > actualString.length) {
\r
21108 return concat(character, (width - actualString.length)) + actualString;
\r
21110 return actualString + concat(character, (width - actualString.length));
\r
21113 return actualString;
\r
21116 String.prototype.lPad = function (width, character) {
\r
21117 return pad(this, width, character, true);
\r
21120 String.prototype.rPad = function (width, character) {
\r
21121 return pad(this, width, character, false);
\r
21124 if (!Array.prototype.indexOf) {
\r
21125 Array.prototype.indexOf = function (val) {
\r
21126 for (var index = 0; index < this.length; index++) {
\r
21127 if (this[index] === val) {
\r
21135 if (!Array.prototype.regexIndexOf) {
\r
21136 Object.defineProperty(Array.prototype, 'regexIndexOf', {
\r
21137 enumerable: false,
\r
21138 value: function (regex, startIndex, loop) {
\r
21139 startIndex = startIndex && startIndex > -1 ? startIndex : 0;
\r
21140 for (var index = startIndex; index < this.length; index++) {
\r
21141 if (this[index].toString().match(regex)) {
\r
21146 for (var index = 0; index < startIndex; index++) {
\r
21147 if (this[index].toString().match(regex)) {
\r
21157 angular.module("b2bTemplate/audioPlayer/audioPlayer.html", []).run(["$templateCache", function($templateCache) {
\r
21158 $templateCache.put("b2bTemplate/audioPlayer/audioPlayer.html",
\r
21159 "<div class=\"b2b-audio\">\n" +
\r
21160 " <audio preload=\"auto\">\n" +
\r
21161 " <source ng-src=\"{{audio.mp3 | trustedAudioUrl}}\" type=\"audio/mp3\"></source>\n" +
\r
21162 " <i>Your browser does not support the audio element.</i>\n" +
\r
21165 " <div audio-play-pause-icon class=\"controls-wrapper\" ng-click=\"toggleAudio()\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" role=\"button\" aria-label=\"{{isPlayInProgress?'pause':'play'}}\">\n" +
\r
21166 " <i class=\"icoControls-pointer\" ng-show='!isPlayInProgress'></i>\n" +
\r
21167 " <i class=\"icoControls-pause\" ng-show='isPlayInProgress'></i>\n" +
\r
21170 " <div class=\"seek-bar-container-wrapper\">\n" +
\r
21171 " <b2b-seek-bar min=\"0\" max=\"{{audio.duration}}\" step=\"1\" skip-interval=\"{{audio.timeShiftInSeconds}}\" no-aria-label ng-model=\"audio.currentTime\" on-drag-init=\"isAudioDragging=true\" on-drag-end=\"setAudioPosition(audio.currentTime)\"></b2b-seek-bar>\n" +
\r
21172 " <div class=\"timing-container\">\n" +
\r
21173 " <span class=\"timing-container-left\">{{timeFormatter(audio.currentTime)}}</span>\n" +
\r
21174 " <span class=\"timing-container-right\">{{timeFormatter(audio.duration)}}</span>\n" +
\r
21175 " <div class=\"timing-container-spacer\"></div>\n" +
\r
21179 " <b2b-flyout>\n" +
\r
21180 " <div tabindex=\"0\" b2b-accessibility-click=\"13,32\" role=\"button\" aria-label=\"Volume pop-over. Volume set at {{audio.currentVolume}}%\" class=\"controls-wrapper audio-volume-control\" b2b-flyout-toggler>\n" +
\r
21181 " <i class=\"icoControls-mutespeakers\" ng-show=\"audio.currentVolume === 0\"></i>\n" +
\r
21182 " <i class=\"icoControls-volumedown\" ng-show=\"audio.currentVolume > 0 && audio.currentVolume <= 50\"></i>\n" +
\r
21183 " <i class=\"icoControls-volumeup\" ng-show=\"audio.currentVolume > 50\"></i>\n" +
\r
21186 " <b2b-flyout-content horizontal-placement=\"center\" flyout-style=\"width:70px; height:190px;\" vertical-placement=\"above\">\n" +
\r
21187 " <div class=\"b2b-audio-popover text-center\">\n" +
\r
21188 " <span>Max</span>\n" +
\r
21189 " <b2b-seek-bar min=\"0\" max=\"100\" step=\"1\" skip-interval=\"10\" vertical data-ng-model=\"audio.currentVolume\" class='volume-popover'></b2b-seek-bar>\n" +
\r
21190 " <div class=\"min-label\">Min</div>\n" +
\r
21192 " </b2b-flyout-content>\n" +
\r
21193 " </b2b-flyout>\n" +
\r
21197 angular.module("b2bTemplate/audioRecorder/audioRecorder.html", []).run(["$templateCache", function($templateCache) {
\r
21198 $templateCache.put("b2bTemplate/audioRecorder/audioRecorder.html",
\r
21199 "<div class=\"b2b-audio-recorder row\">\n" +
\r
21200 " <div class=\"b2b-elapsed-time span11\">\n" +
\r
21201 " <div ng-if=\"isRecording\">\n" +
\r
21202 " <span style=\"padding-right: 25px;\">{{config.whileRecordingMessage}}</span>\n" +
\r
21203 " <span>{{timeFormatter(elapsedTime)}}</span>\n" +
\r
21205 " <span ng-if=\"!isRecording\">{{config.startRecordingMessage}}</span>\n" +
\r
21207 " <div class=\"b2b-controls\" title=\"{{isRecording ? 'Stop' : 'REC'}}\" b2b-accessibility-click=\"13,32\" ng-click=\"toggleRecording()\" role=\"button\">\n" +
\r
21208 " <i ng-if=\"isRecording\" class=\"icoControls-stop\" ></i>\n" +
\r
21209 " <i ng-if=\"!isRecording\" class=\"icoControls-record\"></i>\n" +
\r
21214 angular.module("b2bTemplate/backToTop/backToTop.html", []).run(["$templateCache", function($templateCache) {
\r
21215 $templateCache.put("b2bTemplate/backToTop/backToTop.html",
\r
21216 "<button class=\"btn-arrow b2b-backtotop-button\" type=\"button\" aria-label=\"Back to top\">\n" +
\r
21217 " <div class=\"btn-secondary b2b-top-btn\">\n" +
\r
21218 " <i class=\"icoControls-upPRIMARY\" role=\"img\"></i>\n" +
\r
21224 angular.module("b2bTemplate/boardstrip/b2bAddBoard.html", []).run(["$templateCache", function($templateCache) {
\r
21225 $templateCache.put("b2bTemplate/boardstrip/b2bAddBoard.html",
\r
21226 "<div tabindex=\"0\" role=\"menuitem\" b2b-accessibility-click=\"13,32\" ng-click=\"addBoard()\" aria-label=\"Add Board\" class=\"boardstrip-item--add\">\n" +
\r
21227 " <div class=\"centered\"><i aria-hidden=\"true\" class=\"icoControls-add-maximize\"></i> Add board</div>\n" +
\r
21231 angular.module("b2bTemplate/boardstrip/b2bBoard.html", []).run(["$templateCache", function($templateCache) {
\r
21232 $templateCache.put("b2bTemplate/boardstrip/b2bBoard.html",
\r
21233 "<li b2b-board-navigation tabindex=\"-1\" role=\"menuitem\" aria-label=\"{{boardLabel}} {{getCurrentIndex()===boardIndex?'selected':''}}\" b2b-accessibility-click=\"13,32\" ng-click=\"selectBoard(boardIndex)\" class=\"board-item\" ng-class=\"{'selected': getCurrentIndex()===boardIndex}\">\n" +
\r
21234 " <div ng-transclude></div>\n" +
\r
21235 " <div class=\"board-caret\" ng-if=\"getCurrentIndex()===boardIndex\">\n" +
\r
21236 " <div class=\"board-caret-indicator\"></div>\n" +
\r
21237 " <div class=\"board-caret-arrow-up\"></div>\n" +
\r
21242 angular.module("b2bTemplate/boardstrip/b2bBoardstrip.html", []).run(["$templateCache", function($templateCache) {
\r
21243 $templateCache.put("b2bTemplate/boardstrip/b2bBoardstrip.html",
\r
21244 "<div class=\"b2b-boardstrip\">\n" +
\r
21245 " <div class=\"boardstrip-reel\" role=\"menu\">\n" +
\r
21246 " <div class=\"prev-items\">\n" +
\r
21247 " <!-- <i tabindex=\"{{isPrevBoard() ? 0 : -1}}\" ng-class=\"{'disabled': !isPrevBoard()}\" role=\"menuitem\" aria-label=\"See previous boards\" b2b-accessibility-click=\"13,32\" ng-click=\"prevBoard()\" class=\"arrow icoControls-left\"></i> -->\n" +
\r
21248 " <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"prevBoard()\" ng-disabled=\"!isPrevBoard()\">\n" +
\r
21249 " <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-left\"></i>\n" +
\r
21251 " <span class=\"offscreen-text\">Previous boards</span>\n" +
\r
21254 " <div b2b-add-board on-add-board=\"onAddBoard()\"></div>\n" +
\r
21255 " <div class=\"board-viewport\"><ul role=\"menu\" class=\"boardstrip-container\" ng-transclude></ul></div>\n" +
\r
21256 " <div class=\"next-items\">\n" +
\r
21257 " <!-- <i tabindex=\"{{isNextBoard() ? 0 : -1}}\" ng-class=\"{'disabled': !isNextBoard()}\" role=\"menuitem\" aria-label=\"See next boards\" b2b-accessibility-click=\"13,32\" ng-click=\"nextBoard()\" class=\"arrow icoControls-right\"></i> -->\n" +
\r
21258 " <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"nextBoard()\" ng-disabled=\"!isNextBoard()\">\n" +
\r
21259 " <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-right\"></i>\n" +
\r
21261 " <span class=\"offscreen-text\">Next boards</span>\n" +
\r
21269 angular.module("b2bTemplate/calendar/datepicker-popup.html", []).run(["$templateCache", function($templateCache) {
\r
21270 $templateCache.put("b2bTemplate/calendar/datepicker-popup.html",
\r
21271 "<div id=\"datepicker\" class=\"datepicker dropdown-menu\" ng-class=\"{'datepicker-dropdown datepicker-orient-top': !inline, 'datepicker-orient-left': !inline && orientation === 'left', 'datepicker-orient-right': !inline && orientation === 'right'}\" ng-style=\"{position: inline && 'relative', 'z-index': inline && '0', top: !inline && position.top + 'px' || 0, left: !inline && position.left + 'px'}\" style=\"display: block;\" aria-hidden=\"false\" role=\"dialog\" tabindex=\"-1\" b2b-key type=\"table\" columns=\"7\">\n" +
\r
21272 " <div class=\"datepicker-days\" style=\"display: block;\">\n" +
\r
21273 " <div ng-repeat=\"header in headers\" class=\"text-left\" style=\"width: 100%;\" b2b-append-element=\"header\"></div>\n" +
\r
21274 " <table class=\"table-condensed\">\n" +
\r
21277 " <th id=\"prev\" class=\"prev\" tabindex=\"0\" b2b-accessibility-click=\"13\" aria-label=\"Previous Month\" role=\"button\" b2b-element-focus=\"!disablePrev && getFocus\" ng-style=\"{visibility: visibilityPrev}\" ng-click=\"!disablePrev && move(-1,$event)\"><i class=\"icon-primary-left\" aria-hidden=\"true\"></i></th>\n" +
\r
21278 " <th id=\"month\" tabindex=\"-1\" aria-label=\"{{title}}\" class=\"datepicker-switch\" colspan=\"{{rows[0].length - 2}}\">{{title}}</th>\n" +
\r
21279 " <th id=\"next\" class=\"next\" tabindex=\"0\" b2b-accessibility-click=\"13\" b2b-element-focus=\"disablePrev && getFocus\" aria-label=\"Next Month\" role=\"button\" ng-style=\"{visibility: visibilityNext}\" ng-click=\"!disableNext && move(1,$event)\"><i class=\"icon-primary-right\" aria-hidden=\"true\"></i></th>\n" +
\r
21281 " <tr ng-show=\"labels.length > 0\">\n" +
\r
21282 " <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
\r
21286 " <tr ng-repeat=\"row in rows\">\n" +
\r
21287 " <td headers=\"{{dt.header}}\" b2b-key-item=\"dt.focusable\" b2b-accessibility-click=\"13\" b2b-element-focus=\"disablePrev && disableNext && getFocus && (dt.selected || dt.firstFocus || dt.fromDate || dt.dateRange)\" tabindex=\"{{(!(dt.focusable && (dt.selected || dt.firstFocus || dt.fromDate || currFocus)) && -1) || 0}}\" aria-hidden=\"{{(!dt.focusable && true) || false}}\" role=\"{{(dt.focusable && 'gridcell') || ''}}\" aria-label=\"{{(dt.focusable && dt.date | date : 'EEEE, MMMM d') || ''}}\" aria-selected=\"{{(dt.focusable && (dt.selected || dt.dateRange) && true) || false}}\" ng-repeat=\"dt in row\" class=\"day magic\" ng-class=\"{'active': dt.focusable && dt.selected && !dt.dateRange, 'start-date': dt.focusable && dt.fromDate && dt.dateRange, 'between-date': dt.focusable && !dt.fromDate && !dt.selected && dt.dateRange, 'end-date': dt.focusable && dt.selected && dt.dateRange, 'old': dt.oldMonth, 'new': dt.nextMonth, 'disabled': dt.disabled, 'due-date': dt.dueDate, 'late-fee': dt.pastDue}\" ng-focus=\"currFocus=true; trapFocus();\" ng-blur=\"currFocus=false; trapFocus();\" title=\"{{(dt.focusable && dt.pastDue && legendMessage) || ''}}\" ng-click=\"!ngDisabled && !dt.disabled && select(dt.date)\">\n" +
\r
21288 " <div aria-hidden=\"true\" tabindex=\"-1\" class=\"show-date\" ng-if=\"!(dt.oldMonth || dt.nextMonth)\">{{dt.label}}</div>{{(dt.focusable && dt.date | date : 'EEEE, MMMM d yyyy') || ''}}{{(dt.focusable && dt.dueDate && '. Bill-due-date.. ') || ''}}{{(dt.focusable && dt.pastDue && legendMessage) || ''}}</td>\n" +
\r
21292 " <tr ng-repeat=\"footer in footers\">\n" +
\r
21293 " <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"footer\"></th>\n" +
\r
21301 angular.module("b2bTemplate/calendar/datepicker.html", []).run(["$templateCache", function($templateCache) {
\r
21302 $templateCache.put("b2bTemplate/calendar/datepicker.html",
\r
21304 " <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
\r
21308 angular.module("b2bTemplate/coachmark/coachmark.html", []).run(["$templateCache", function($templateCache) {
\r
21309 $templateCache.put("b2bTemplate/coachmark/coachmark.html",
\r
21310 "<div class=\"b2b-coachmark-container\" tabindex=\"-1\" role=\"dialog\" aria-label=\"{{currentCoachmark.contentHeader}} {{currentCoachmark.content}} {{currentCoachmark.offscreenText}}\" ng-style=\"{'top':coackmarkElPos.top,'left':coackmarkElPos.left}\" aria-describeby=\"{{currentCoachmark}}\">\n" +
\r
21311 " <i class=\"b2b-coachmark-caret\"></i>\n" +
\r
21312 " <div class=\"b2b-coachmark-header\">\n" +
\r
21313 " <div class=\"b2b-coachmark-countlabel\"><span ng-if=\"coachmarkIndex !== 0\">{{coachmarkIndex}} of {{(coachmarks.length-1)}}<span></div>\n" +
\r
21314 " <div class=\"corner-button\">\n" +
\r
21315 " <button type=\"button\" ng-focus=\"closeButtonFocus()\" class=\"close\" title=\"close\" aria-label=\"Close\" ng-click=\"closeCoachmark()\"></button>\n" +
\r
21318 " <div class=\"b2b-coachmark-content\"> \n" +
\r
21319 " <i class=\"icon-misc-dimmer\"></i>\n" +
\r
21320 " <div class=\"b2b-coachmark-content-header\"><span class=\"offscreen-text\">{{currentCoachmark.offscreenText}}</span>{{currentCoachmark.contentHeader}}</div>\n" +
\r
21321 " <div class=\"b2b-coachmark-description\">{{currentCoachmark.content}}</div>\n" +
\r
21322 " <div class=\"b2b-coachmark-btn-group\">\n" +
\r
21323 " <a class=\"b2b-coachmark-link\" href=\"javascript:void(0)\" ng-if=\"currentCoachmark.linkLabel !== '' && currentCoachmark.linkLabel !== undefined\" ng-click=\"actionCoachmark(currentCoachmark.linkLabel)\">{{currentCoachmark.linkLabel}}</a>\n" +
\r
21324 " <button class=\"btn btn-alt\" ng-if=\"currentCoachmark.buttonLabel !== '' && currentCoachmark.buttonLabel !== undefined\" ng-click=\"actionCoachmark(currentCoachmark.buttonLabel)\">{{currentCoachmark.buttonLabel}}</button>\n" +
\r
21330 angular.module("b2bTemplate/dropdowns/b2bDropdownDesktop.html", []).run(["$templateCache", function($templateCache) {
\r
21331 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownDesktop.html",
\r
21332 "<span b2b-key prev=\"38\" next=\"40\" enable-search ng-class=\"{'large': (dropdownSize === 'large'), 'disabled': (disabled), 'selectWrap': (isInputDropdown), 'selectorModule': (dropdownType === 'menu'), 'linkSelectorModule': (dropdownType === 'link-menu')}\">\n" +
\r
21333 " <input b2b-dropdown-toggle b2b-dropdown-validation ng-disabled=\"disabled\" type=\"text\" id=\"{{dropdownId}}\" name=\"{{dropdownName}}\" class=\"awd-select isWrapped\" ng-required=\"dropdownRequired\" ng-model=\"currentSelected.text\" role=\"combobox\" aria-owns=\"listbox{{$id}}\" aria-expanded=\"{{toggleFlag}}\" ng-click=\"toggleDropdown()\" ng-focus=\"focused=true\" ng-blur=\"setBlur(); focused=false\" ng-class=\"{'active': toggleFlag, 'closed': !toggleFlag, 'large': (dropdownSize === 'large'), 'focused':focused}\" style=\"width:100%;\" value=\"{{currentSelected.text}}\" ng-show=\"isInputDropdown\" aria-describedby=\"{{dropdownDescribedBy}}\" readonly=\"readonly\">\n" +
\r
21334 " <button type=\"button\" b2b-dropdown-toggle ng-disabled=\"disabled\" class=\"selectModule\" aria-label=\"{{labelText}} {{currentSelected.label}}\" aria-expanded=\"{{toggleFlag}}\" aria-haspopup=\"true\" ng-click=\"toggleDropdown()\" ng-blur=\"setBlur()\" ng-class=\"{'active opened': toggleFlag, 'closed': !toggleFlag, 'large': (dropdownSize === 'large')}\" ng-bind-html=\"currentSelected.text\" ng-show=\"!isInputDropdown\"></button>\n" +
\r
21335 " <div ng-class=\"{'selectWrapper': (isInputDropdown), 'moduleWrapper': (!isInputDropdown)}\">\n" +
\r
21336 " <ul id=\"listbox{{$id}}\" role=\"{{isInputDropdown?'listbox':'menu'}}\" ng-class=\"{'awd-select-list': (isInputDropdown), 'awd-module-list': (!isInputDropdown)}\" tabindex=\"-1\" ng-show=\"toggleFlag\" aria-label=\"Choose options\"></ul>\n" +
\r
21337 " <ul class=\"module-optinalcta\" ng-if=\"toggleFlag && optionalCta\" tabindex=\"-1\" aria-hidden=\"false\">\n" +
\r
21338 " <li class=\"module-list-item\" tabindex=\"-1\" role=\"menuitem\" value=\"\" aria-label=\"Optinal CTA\" aria-selected=\"true\">\n" +
\r
21339 " <span class=\"module-data\" b2b-append-element=\"optionalCta\"></span>\n" +
\r
21343 "<i class=\"icon-primary-down\" aria-hidden=\"true\"></i>\n" +
\r
21347 angular.module("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html", []).run(["$templateCache", function($templateCache) {
\r
21348 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html",
\r
21349 "<li b2b-dropdown-group-desktop class=\"optgroup-wrapper\">{{groupHeader}}\n" +
\r
21350 " <ul class=\"optgroup\" role=\"group\" aria-label=\"{{groupHeader}}\"></ul>\n" +
\r
21354 angular.module("b2bTemplate/dropdowns/b2bDropdownListDesktop.html", []).run(["$templateCache", function($templateCache) {
\r
21355 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownListDesktop.html",
\r
21356 "<li b2b-dropdown-list-desktop b2b-key-item b2b-accessibility-click=\"13\" aria-selected=\"{{currentSelected.value === dropdownListValue}}\" ng-class=\"{'awd-select-list-item': (isInputDropdown), 'module-list-item': (!isInputDropdown)}\" tabindex=\"0\" role=\"{{isInputDropdown?'option':'menuitem'}}\" ng-click=\"selectDropdownItem()\"></li>");
\r
21359 angular.module("b2bTemplate/fileUpload/fileUpload.html", []).run(["$templateCache", function($templateCache) {
\r
21360 $templateCache.put("b2bTemplate/fileUpload/fileUpload.html",
\r
21361 "<label class=\"b2b-file-container\">\n" +
\r
21362 " <span class=\"b2b-upload-link\" ng-transclude></span>\n" +
\r
21363 " <input type=\"file\" b2b-file-change>\n" +
\r
21367 angular.module("b2bTemplate/flyout/flyout.html", []).run(["$templateCache", function($templateCache) {
\r
21368 $templateCache.put("b2bTemplate/flyout/flyout.html",
\r
21369 "<span class=\"b2b-flyout\" b2b-flyout-trap-focus-inside>\n" +
\r
21370 " <span ng-transclude></span>\n" +
\r
21374 angular.module("b2bTemplate/flyout/flyoutContent.html", []).run(["$templateCache", function($templateCache) {
\r
21375 $templateCache.put("b2bTemplate/flyout/flyoutContent.html",
\r
21376 "<div class=\"b2b-flyout-container\" aria-live=\"polite\" aria-atomic=\"false\" ng-class=\"{'b2b-flyout-left':horizontalPlacement==='left',\n" +
\r
21377 " 'b2b-flyout-center':horizontalPlacement==='center', \n" +
\r
21378 " 'b2b-flyout-right':horizontalPlacement==='right',\n" +
\r
21379 " 'b2b-flyout-centerLeft':horizontalPlacement==='centerLeft',\n" +
\r
21380 " 'b2b-flyout-centerRight':horizontalPlacement==='centerRight', \n" +
\r
21381 " 'b2b-flyout-above':verticalPlacement==='above', \n" +
\r
21382 " 'b2b-flyout-below':verticalPlacement==='below',\n" +
\r
21383 " 'open-flyout': openFlyout,\n" +
\r
21384 " 'b2b-close-flyout': !openFlyout}\">\n" +
\r
21385 " <i class=\"b2b-flyout-caret\" ng-class=\"{'open-flyout': openFlyout, \n" +
\r
21386 " 'b2b-flyout-caret-above':verticalPlacement==='above',\n" +
\r
21387 " 'b2b-flyout-caret-below':verticalPlacement==='below'}\"></i>\n" +
\r
21388 " <span ng-transclude></span>\n" +
\r
21392 angular.module("b2bTemplate/footer/footer_column_switch_tpl.html", []).run(["$templateCache", function($templateCache) {
\r
21393 $templateCache.put("b2bTemplate/footer/footer_column_switch_tpl.html",
\r
21394 "<div class=\"footer-columns \" ng-class=\"{'five-column':footerColumns===5, 'four-column':footerColumns===4, 'three-column':footerColumns===3}\" ng-repeat=\"item in footerLinkItems\">\n" +
\r
21395 " <h3 class=\"b2b-footer-header\">{{item.categoryName}}</h3>\n" +
\r
21397 " <li ng-repeat=\"i in item.values\">\n" +
\r
21398 " <a href=\"{{i.href}}\" title=\"{{item.categoryName}} - {{i.displayLink}}\">{{i.displayLink}}</a> \n" +
\r
21404 "<div ng-transclude></div>\n" +
\r
21408 angular.module("b2bTemplate/horizontalTable/horizontalTable.html", []).run(["$templateCache", function($templateCache) {
\r
21409 $templateCache.put("b2bTemplate/horizontalTable/horizontalTable.html",
\r
21410 "<div class=\"b2b-horizontal-table\">\n" +
\r
21411 " <div class=\"b2b-horizontal-table-arrows row span12\">\n" +
\r
21412 " <div class=\"span4\">\n" +
\r
21413 " <button class=\"btn-arrow left\" type=\"button\" ng-click=\"moveViewportLeft()\" ddh-accessibility-click=\"13,32\" ng-disabled=\"disableLeft\"><div class=\"btn btn-alt\"><i class=\"icon-primary-left\"></i></div>Previous Set</button>\n" +
\r
21416 " <span class=\"span5 b2b-horizontal-table-column-info\" aria-live=\"polite\" tabindex=\"-1\">\n" +
\r
21417 " Displaying {{getColumnSet()[0]}} - {{getColumnSet()[1]}} of {{numOfCols}} (total) columns\n" +
\r
21420 " <div class=\"span3\">\n" +
\r
21421 " <button class=\"btn-arrow right\" ng-click=\"moveViewportRight()\" ddh-accessibility-click=\"13,32\" ng-disabled=\"disableRight\" type=\"button\">Next Set<div class=\"btn btn-alt\"><i class=\"icon-primary-right\"></i></div></button>\n" +
\r
21424 " <div class=\"b2b-horizontal-table-inner-container\">\n" +
\r
21425 " <span ng-transclude></span>\n" +
\r
21432 angular.module("b2bTemplate/hourPicker/b2bHourpicker.html", []).run(["$templateCache", function($templateCache) {
\r
21433 $templateCache.put("b2bTemplate/hourPicker/b2bHourpicker.html",
\r
21434 "<div class=\"hp-container\">\n" +
\r
21435 " <div class=\"hp-selected\">\n" +
\r
21436 " <div b2b-hourpicker-value=\"$index\" days=\"value.days\" start-time=\"value.startTime\" start-meridiem=\"value.startMeridiem\" end-time=\"value.endTime\" end-meridiem=\"value.endMeridiem\" ng-repeat=\"value in finalHourpickerValues\"></div>\n" +
\r
21438 " <div b2b-hourpicker-panel></div>\n" +
\r
21442 angular.module("b2bTemplate/hourPicker/b2bHourpickerPanel.html", []).run(["$templateCache", function($templateCache) {
\r
21443 $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerPanel.html",
\r
21444 "<form name=\"{{'hourpickerForm' + $id}}\">\n" +
\r
21445 " <div class=\"hp-checkbox\" role=\"group\">\n" +
\r
21446 " <label class=\"checkbox\" for=\"checkbox_{{dayOption.title}}_{{$id}}\" aria-label=\"{{dayOption.title}}\" ng-repeat=\"dayOption in hourpicker.dayOptions\">\n" +
\r
21447 " <input type=\"checkbox\" id=\"checkbox_{{dayOption.title}}_{{$id}}\" name=\"{{'hourpickerDays' + $id}}\" ng-model=\"hourpickerPanelValue.days[$index].value\" ng-disabled=\"dayOption.disabled\" /><i class=\"skin\"></i><span>{{dayOption.label}}</span>\n" +
\r
21450 " <div class=\"row hp-dropdowns\">\n" +
\r
21451 " <div class=\"span4\">\n" +
\r
21452 " <label for=\"{{'hourpickerStartTime' + $id}}\">From</label>\n" +
\r
21453 " <select id=\"{{'hourpickerStartTime' + $parent.$id}}\" b2b-dropdown type=\"\" ng-model=\"hourpickerPanelValue.startTime\">\n" +
\r
21454 " <option b2b-dropdown-list value=\"\">From</option>\n" +
\r
21455 " <option b2b-dropdown-list option-repeat=\"startTimeOption in hourpicker.startTimeOptions\" value=\"{{startTimeOption}}\">{{startTimeOption}}</option>\n" +
\r
21458 " <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
\r
21459 " <label class=\"radio\" for=\"hourpickerStartMeridiem_AM_{{$id}}\">\n" +
\r
21460 " <input type=\"radio\" id=\"hourpickerStartMeridiem_AM_{{$id}}\" name=\"{{'hourpickerStartMeridiem' + $id}}\" value=\"am\" ng-model=\"hourpickerPanelValue.startMeridiem\" /><i class=\"skin\"></i><span>AM</span>\n" +
\r
21462 " <label class=\"radio\" for=\"hourpickerStartMeridiem_PM_{{$id}}\">\n" +
\r
21463 " <input type=\"radio\" id=\"hourpickerStartMeridiem_PM_{{$id}}\" name=\"{{'hourpickerStartMeridiem' + $id}}\" value=\"pm\" ng-model=\"hourpickerPanelValue.startMeridiem\" /><i class=\"skin\"></i><span>PM</span>\n" +
\r
21467 " <div class=\"row hp-dropdowns\">\n" +
\r
21468 " <div class=\"span4\">\n" +
\r
21469 " <label for=\"{{'hourpickerEndTime' + $id}}\">To</label>\n" +
\r
21470 " <select id=\"{{'hourpickerEndTime' + $parent.$id}}\" b2b-dropdown ng-model=\"hourpickerPanelValue.endTime\">\n" +
\r
21471 " <option b2b-dropdown-list value=\"\">To</option>\n" +
\r
21472 " <option b2b-dropdown-list option-repeat=\"endTimeOption in hourpicker.endTimeOptions\" value=\"{{endTimeOption}}\">{{endTimeOption}}</option>\n" +
\r
21475 " <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
\r
21476 " <label class=\"radio\" for=\"hourpickerEndMeridiem_AM_{{$id}}\">\n" +
\r
21477 " <input type=\"radio\" id=\"hourpickerEndMeridiem_AM_{{$id}}\" name=\"{{'hourpickerEndMeridiem' + $id}}\" value=\"am\" ng-model=\"hourpickerPanelValue.endMeridiem\" /><i class=\"skin\"></i><span>AM</span>\n" +
\r
21479 " <label class=\"radio\" for=\"hourpickerEndMeridiem_PM_{{$id}}\">\n" +
\r
21480 " <input type=\"radio\" id=\"hourpickerEndMeridiem_PM_{{$id}}\" name=\"{{'hourpickerEndMeridiem' + $id}}\" value=\"pm\" ng-model=\"hourpickerPanelValue.endMeridiem\" /><i class=\"skin\"></i><span>PM</span>\n" +
\r
21484 " <div class=\"row hp-buttons\">\n" +
\r
21485 " <div class=\"span12\">\n" +
\r
21486 " <div style=\"float:right\">\n" +
\r
21487 " <button class=\"btn btn-secondary btn-small\" ng-click=\"resetHourpickerPanelValue()\">Clear</button>\n" +
\r
21488 " <button class=\"btn btn-alt btn-small\" ng-disabled = \"disableAddBtn\" ng-click=\"updateHourpickerValue()\">{{hourpicker.editMode > -1 ? 'Update' : 'Add'}}</button>\n" +
\r
21495 angular.module("b2bTemplate/hourPicker/b2bHourpickerValue.html", []).run(["$templateCache", function($templateCache) {
\r
21496 $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerValue.html",
\r
21497 "<div class=\"selected-days\">\n" +
\r
21498 " <span class=\"day\">{{hourpickerValue.days}} {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}</span>\n" +
\r
21499 " <span style=\"float:right\">\n" +
\r
21500 " <i class=\"icon-misc-pen\" role=\"button\" title=\"Edit {{hourpickerValue.days}} {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" ng-click=\"editHourpickerValue(hourpickerValue.index)\"></i>\n" +
\r
21501 " <i class=\"icon-misc-trash\" role=\"button\" title=\"Delete {{hourpickerValue.days}} {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" ng-click=\"deleteHourpickerValue(hourpickerValue.index)\"></i>\n" +
\r
21503 " <div style=\"clear:both\"></div>\n" +
\r
21507 angular.module("b2bTemplate/leftNavigation/leftNavigation.html", []).run(["$templateCache", function($templateCache) {
\r
21508 $templateCache.put("b2bTemplate/leftNavigation/leftNavigation.html",
\r
21509 "<div class=\"b2b-nav-menu\" ng-init=\"(showmenu = true)\" ng-class=\"{false: 'left-menu-collapsed'}[showmenu]\">\n" +
\r
21510 " <div class=\"b2b-subnav-container\">\n" +
\r
21511 " <ul class=\"b2b-subnav-content\">\n" +
\r
21513 " <div ng-class=\"{true: 'leftmenu-arrow-expand', false: 'leftmenu-arrow-collapse'}[showmenu]\">"+
\r
21514 " <a ng-click=\"toggleDrawer(showmenu);(showmenu = !showmenu) \" class=\"text-right\">" +
\r
21515 " <i ng-class=\"{true: 'icon-controls-left', false: 'icon-controls-right'}[showmenu]\"> </i></a>" +
\r
21518 " <li ng-repeat=\"menu in menuData\" ui-sref=\"{{menu.state}}\">" +
\r
21519 " <span ng-class=\"{true: 'menu-icon', false: 'menu-icon-collapse'}[showmenu]\"><span class=\"{{menu.imageSrc}}\"></span> </span>" +
\r
21520 " <a ng-class=\"{expand: isOpen($index)}\" ng-if=\"showmenu\" aria-label=\"{{menu.name}}\" title=\" \" aria-expanded=\"{{(idx==$index)?true:false;}}\" href=\"javascript:void(0);\">" +
\r
21521 " {{menu.name}}" +
\r
21522 " <i aria-hidden=\"true\" ng-if=\"(menu.menuItems.length > 0)\" class=\"b2b-icon-primary-plus-minus\"ng-class=\"idx==$index ? 'icon-primary-expanded' : 'icon-primary-collapsed'\"></i>" +
\r
21524 " <div role=\"region\" aria-hidden=\"{{(isOpen($index))?false:true;}}\">\n" +
\r
21525 " <ul ng-class=\"{expand: idx==$index}\">\n" +
\r
21526 " <li ng-repeat=\"menuItem in menu.menuItems\" ng-click=\"liveLink($event, $index, $parent.$index)\"><a aria-hidden=\"{{!(idx==$parent.$index)}}\" aria-label=\"{{menuItem.name}}\" title=\" \" href=\"{{menuItem.href}}\" tabindex=\"{{(idx==$parent.$index)?0:-1;}}\" ng-class=\"{active: itemIdx==$index && navIdx==$parent.$index}\">{{menuItem.name}}</a></li>\n" +
\r
21535 angular.module("b2bTemplate/listbox/listbox.html", []).run(["$templateCache", function($templateCache) {
\r
21536 $templateCache.put("b2bTemplate/listbox/listbox.html",
\r
21537 "<div class=\"b2b-list-box\" tabindex=\"0\" role=\"listbox\" ng-transclude>\n" +
\r
21541 angular.module("b2bTemplate/modalsAndAlerts/b2b-backdrop.html", []).run(["$templateCache", function($templateCache) {
\r
21542 $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-backdrop.html",
\r
21543 "<div class=\"b2b-modal-backdrop fade in hidden-by-modal\" aria-hidden=\"true\" tabindex=\"-1\"></div>");
\r
21546 angular.module("b2bTemplate/modalsAndAlerts/b2b-window.html", []).run(["$templateCache", function($templateCache) {
\r
21547 $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-window.html",
\r
21548 "<div class=\"modalwrapper active {{windowClass}}\" ng-class=\"{'modal-landscape': isModalLandscape}\" role=\"{{isNotifDialog?'alertdialog':'dialog'}}\" tabindex=\"-1\" aria-labelledby=\"{{title}}\" aria-describedby=\"{{content}}\">\n" +
\r
21549 " <div class=\"modal fade in {{sizeClass}}\" ng-transclude></div>\n" +
\r
21553 angular.module("b2bTemplate/monthSelector/monthSelector-popup.html", []).run(["$templateCache", function($templateCache) {
\r
21554 $templateCache.put("b2bTemplate/monthSelector/monthSelector-popup.html",
\r
21555 "<div id=\"monthselector{{$id}}\" class=\"datepicker dropdown-menu monthselector\" \n" +
\r
21556 " ng-class=\"{'datepicker-dropdown datepicker-orient-top': !inline, 'datepicker-orient-left': !inline && orientation === 'left', 'datepicker-orient-right': !inline && orientation === 'right'}\" \n" +
\r
21557 " ng-style=\"{position: inline && 'relative', 'z-index': inline && '0', top: !inline && position.top + 'px' || 0, left: !inline && position.left + 'px'}\" \n" +
\r
21558 " style=\"display: block;\" aria-hidden=\"false\" role=\"dialog presentation\" tabindex=\"-1\">\n" +
\r
21559 " <div class=\"datepicker-days\" style=\"display: block;\">\n" +
\r
21560 " <span class=\"offscreen-text\" aria-live=\"polite\" aria-atomic=\"true\">{{title}} displaying</span>\n" +
\r
21561 " <table class=\"table-condensed\" role=\"grid\">\n" +
\r
21563 " <tr ng-repeat=\"header in headers\">\n" +
\r
21564 " <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"header\"></th>\n" +
\r
21567 " <th id=\"prev{{$id}}\" role=\"button\" tabindex=\"0\" b2b-accessibility-click=\"13\" aria-label=\"Previous Year\" ng-class=\"{'prev': !disablePrev}\" ng-style=\"{visibility: visibilityPrev}\" ng-click=\"!disablePrev && move(-1,$event)\"><i class=\"icon-primary-left\"></i></th>\n" +
\r
21568 " <th id=\"year{{$id}}\" role=\"heading\" tabindex=\"-1\" aria-label=\"{{title}}\" class=\"datepicker-switch b2b-monthSelector-label\" colspan=\"{{rows[0].length - 2}}\">{{title}}</th>\n" +
\r
21569 " <th id=\"next{{$id}}\" role=\"button\" tabindex=\"0\" b2b-accessibility-click=\"13\" aria-label=\"Next Year\" ng-class=\"{'next': !disableNext}\" ng-style=\"{visibility: visibilityNext}\" ng-click=\"!disableNext && move(1,$event)\"><i class=\"icon-primary-right\"></i></th>\n" +
\r
21571 " <tr ng-show=\"labels.length > 0\">\n" +
\r
21572 " <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
\r
21575 " <tbody b2b-key type=\"table\" columns=\"4\" >\n" +
\r
21576 " <tr ng-repeat=\"row in rows\">\n" +
\r
21577 " <td headers=\"{{dt.header}}\" b2b-key-item=\"dt.focusable\" b2b-accessibility-click=\"13,32\" tabindex=\"{{(!(dt.focusable && (dt.selected || dt.firstFocus || currFocus)) && -1) || 0}}\" aria-hidden=\"{{(!dt.focusable && true) || false}}\" role=\"{{(dt.focusable && 'gridcell') || ''}}\" aria-label=\"{{(dt.focusable && dt.date | date : 'MMMM, y') || ''}}\" aria-selected=\"{{(dt.focusable && (dt.selected || dt.dateRange) && true) || false}}\" ng-repeat=\"dt in row\" class=\"day magic\" ng-class=\"{'active': dt.focusable && dt.selected && !dt.dateRange, 'start-date': dt.focusable && dt.fromDate && dt.dateRange, 'between-date': dt.focusable && !dt.fromDate && !dt.selected && dt.dateRange, 'end-date': dt.focusable && dt.selected && dt.dateRange, 'old': dt.oldMonth, 'new': dt.nextMonth, 'disabled': dt.disabled, 'due-date': dt.dueDate, 'late-fee': dt.pastDue}\" ng-focus=\"currFocus=true\" ng-blur=\"currFocus=false\" title=\"{{(dt.focusable && dt.pastDue && legendMessage) || ''}}\" b2b-element-focus=\"inline && dt.focusable && dt.selected && !dt.dateRange\" ng-click=\"!ngDisabled && !dt.disabled && select(dt.date)\">\n" +
\r
21578 " <div aria-hidden=\"true\" tabindex=\"-1\" class=\"show-date\" ng-if=\"!(dt.oldMonth || dt.nextMonth)\">{{dt.label | limitTo: 3}}</div>\n" +
\r
21583 " <tr ng-repeat=\"footer in footers\">\n" +
\r
21584 " <th colspan=\"7\" class=\"text-left\" b2b-append-element=\"footer\"></th>\n" +
\r
21592 angular.module("b2bTemplate/monthSelector/monthSelector.html", []).run(["$templateCache", function($templateCache) {
\r
21593 $templateCache.put("b2bTemplate/monthSelector/monthSelector.html",
\r
21595 " <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
\r
21599 angular.module("b2bTemplate/monthSelector/monthSelectorLink.html", []).run(["$templateCache", function($templateCache) {
\r
21600 $templateCache.put("b2bTemplate/monthSelector/monthSelectorLink.html",
\r
21602 " <span class=\"span12\" ng-transclude></span>\n" +
\r
21606 angular.module("b2bTemplate/pagination/b2b-pagination.html", []).run(["$templateCache", function($templateCache) {
\r
21607 $templateCache.put("b2bTemplate/pagination/b2b-pagination.html",
\r
21608 "<div class=\"b2b-pager\">\n" +
\r
21609 " <div ng-if=\"notMobile && totalPages > 1\">\n" +
\r
21610 " <a tabindex=\"{{currentPage <= 1 ? -1 : 0 }}\" class=\"b2b-pager__item--prev\" b2b-accessibility-click=\"13,32\" title=\"Previous Page\" ng-click=\"prev($event)\" ng-if=\"totalPages > 10\" ng-class=\"currentPage <= 1 ? 'b2b-pager__item--prev-disabled': '' \">\n" +
\r
21611 " <i class=\"icon-primary-left\"></i>\n" +
\r
21613 " <a tabindex=\"{{currentPage === 1 ? -1 : 0}}\" ng-class=\"{'b2b-pager__item--noclick': currentPage === 1}\" aria-selected=\"{{checkSelectedPage(page)}}\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page 1{{checkSelectedPage(page) ? ' selected' : '' }}\" ng-if=\"totalPages > 10 && currentPage > 6\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(1, $event)\">\n" +
\r
21614 " 1<span class=\"offscreen-text\" ng-if=\"currentPage === 1\"> is selected</span>\n" +
\r
21616 " <a tabindex=\"{{currentPage === 2 ? -1 : 0}}\" aria-selected=\"{{checkSelectedPage(page)}}\" ng-class=\"{'b2b-pager__item--noclick': currentPage === 2}\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page 2{{checkSelectedPage(page) ? ' selected' : '' }}\" ng-if=\"totalPages > 10 && currentPage > 6\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(2, $event)\">2<span class=\"offscreen-text\" ng-if=\"currentPage === 2\"> is selected</span></a>\n" +
\r
21618 " <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage > 6\">...</span>\n" +
\r
21620 " <a tabindex=\"{{checkSelectedPage(page) ? -1 : 0}}\" href=\"javascript:void(0)\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{page}}\" b2b-element-focus=\"isFocused(page)\" ng-repeat=\"page in pages\" ng-class=\"{'b2b-pager__item--active': checkSelectedPage(page), 'b2b-pager__item--noclick': checkSelectedPage(page)}\" b2b-accessibility-click=\"13,32\" ng-click=\"!checkSelectedPage(page) && selectPage(page, $event)\">{{page}}<span class=\"offscreen-text\" ng-if=\"currentPage === page\"> is selected</span></a>\n" +
\r
21622 " <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage <= totalPages - 5\">...</span>\n" +
\r
21624 " <a tabindex=\"{{checkSelectedPage(page) ? -1 : 0}}\" href=\"javascript:void(0)\" ng-class=\"{'b2b-pager__item--noclick': checkSelectedPage(page)}\" aria-selected=\"{{checkSelectedPage(page)}}\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{totalPages-1}}\" ng-if=\"totalPages > 10 && currentPage <= totalPages - 5\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(totalPages-1, $event)\">{{totalPages-1}}<span class=\"offscreen-text\" ng-if=\"currentPage === totalPages-1\"> is selected</span></a>\n" +
\r
21626 " <a tabindex=\"{{currentPage === totalPages ? -1 : 0}}\" href=\"javascript:void(0)\" ng-class=\"{'b2b-pager__item--noclick': currentPage === totalPages}\" aria-selected=\"{{checkSelectedPage(page)}}\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{totalPages}}\" ng-if=\"totalPages > 10 && currentPage <= totalPages - 5\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(totalPages, $event)\">{{totalPages}}<span class=\"offscreen-text\" ng-if=\"currentPage === totalPages\"> is selected</span></a>\n" +
\r
21628 " <a tabindex=\"{{currentPage >= totalPages ? -1 : 0 }}\" href=\"javascript:void(0)\" class=\"b2b-pager__item--next\" b2b-accessibility-click=\"13,32\" title=\"Next Page\" ng-click=\"next($event)\" ng-if=\"totalPages > 10\" ng-class=\"currentPage >= totalPages ? 'b2b-pager__item--next-disabled' :'' \">\n" +
\r
21629 " <i class=\"icon-primary-right\"></i>\n" +
\r
21632 " <span class=\"fieldLabel\" ng-show=\"totalPages > 10 && showInput === true\"> \n" +
\r
21633 " <label for=\"{{inputId}}\">Go to Page:</label>\n" +
\r
21634 " <input id=\"{{inputId}}\" class=\"b2b-pager__item--input\" type=\"number\" ng-blur=\"onfocusOut($event)\" ng-focus=\"onfocusIn($event)\" ng-keypress=\"gotoKeyClick($event)\" b2b-only-nums=\"4\" ng-model=\"$parent.gotoPage\" />\n" +
\r
21635 " <button class=\"btn-arrow\" ng-click=\"gotoBtnClick()\" ng-disabled=\"$parent.gotoPage !== 0 || $parent.gotoPage !== undefined\" aria-label=\"Go to\">\n" +
\r
21636 " <div class=\"btn btn-small btn-secondary\">\n" +
\r
21637 " <i class=\"icon-primary-right\"></i>\n" +
\r
21642 " <div ng-if=\"isMobile && totalPages > 1\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\" ng-class=\"isMobile ? 'b2b-mobile-view': '' \">\n" +
\r
21643 " <a tabindex=\"{{checkSelectedPage(page) ? -1 : 0}}\" href=\"javascript:void(0)\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{page}}\" b2b-element-focus=\"isFocused(page)\" ng-repeat=\"page in pages\" ng-class=\"{'b2b-pager__item--active': checkSelectedPage(page), fade1: ($index == 0 && currentPage > meanVal+1), fade2: ($index == 1 && currentPage > meanVal+1), fadesl: ($index == pages.length-2 && currentPage < totalPages - meanVal), fadel: ($last && currentPage < totalPages - meanVal), 'b2b-pager__item--noclick': checkSelectedPage(page)}\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(page, $event)\">{{page}}</a>\n" +
\r
21649 angular.module("b2bTemplate/paneSelector/paneSelector.html", []).run(["$templateCache", function($templateCache) {
\r
21650 $templateCache.put("b2bTemplate/paneSelector/paneSelector.html",
\r
21651 "<div class=\"panes\" ng-transclude></div>");
\r
21654 angular.module("b2bTemplate/paneSelector/paneSelectorPane.html", []).run(["$templateCache", function($templateCache) {
\r
21655 $templateCache.put("b2bTemplate/paneSelector/paneSelectorPane.html",
\r
21656 "<div class=\"pane-block\" ng-transclude></div>");
\r
21659 angular.module("b2bTemplate/profileCard/profileCard-addUser.html", []).run(["$templateCache", function($templateCache) {
\r
21660 $templateCache.put("b2bTemplate/profileCard/profileCard-addUser.html",
\r
21661 "<div class=\"span3 b2b-profile-card b2b-add-user\">\n" +
\r
21662 " <div class=\"atcenter\">\n" +
\r
21663 " <div class=\"circle\"><i class=\"icon-primary-add-maximize\"></i></div>\n" +
\r
21664 " <div>Create new user</div>\n" +
\r
21669 angular.module("b2bTemplate/profileCard/profileCard.html", []).run(["$templateCache", function($templateCache) {
\r
21670 $templateCache.put("b2bTemplate/profileCard/profileCard.html",
\r
21671 "<div class=\"span3 b2b-profile-card\">\n" +
\r
21672 " <div class=\"top-block\">\n" +
\r
21673 " <div class=\"profile-image\">\n" +
\r
21674 " <img ng-if=\"image\" profile-name=\"{{profile.name}}\" ng-src=\"{{profile.img}}\" alt=\"{{profile.name}}\">\n" +
\r
21675 " <span ng-if=\"!image\" class=\"default-img\">{{initials}}</span>\n" +
\r
21677 " <h4 class=\"name\">{{profile.name}}</h4>\n" +
\r
21679 " <p class=\"status\">\n" +
\r
21680 " <span class=\"status-icon\" ng-class=\"{'status-green':colorIcon==='green','status-red':colorIcon==='red','status-blue':colorIcon==='blue','status-yellow':colorIcon==='yellow'}\"> \n" +
\r
21682 " <span>{{profile.state}}<span ng-if=\"badge\" class=\"status-badge\">Admin</span></span>\n" +
\r
21686 " <div class=\"bottom-block\">\n" +
\r
21687 " <div class=\"profile-details\">\n" +
\r
21688 " <label>Username</label>\n" +
\r
21689 " <div ng-if=\"shouldClip(profile.userName)\" ng-mouseover=\"showUserNameTooltip = true;\" ng-mouseleave=\"showUserNameTooltip = false;\">\n" +
\r
21690 " <div ng-if=\"shouldClip(profile.userName)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showUserNameTooltip\">\n" +
\r
21691 " {{profile.userName.slice(0, 25)+'...'}}\n" +
\r
21692 " <div class=\"arrow\"></div>\n" +
\r
21693 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
\r
21694 " <div class=\"tooltip-size-control\">\n" +
\r
21695 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
\r
21696 " {{profile.userName}}\n" +
\r
21702 " <div ng-if=\"!shouldClip(profile.userName)\">\n" +
\r
21703 " {{profile.userName}}\n" +
\r
21705 " <label>Email</label>\n" +
\r
21706 " <div ng-if=\"shouldClip(profile.email)\" ng-mouseover=\"showEmailTooltip = true;\" ng-mouseleave=\"showEmailTooltip = false;\">\n" +
\r
21707 " <div ng-if=\"shouldClip(profile.email)\" class=\"tooltip\" data-placement=\"bottom\" b2b-tooltip show-tooltip=\"showEmailTooltip\">\n" +
\r
21708 " {{profile.email.slice(0, 25)+'...'}}\n" +
\r
21709 " <div class=\"arrow\"></div>\n" +
\r
21710 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
\r
21711 " <div class=\"tooltip-size-control\">\n" +
\r
21712 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
\r
21713 " {{profile.email}}\n" +
\r
21719 " <div ng-if=\"!shouldClip(profile.email)\">\n" +
\r
21720 " {{profile.email}}\n" +
\r
21722 " <label>Role</label>\n" +
\r
21723 " <div ng-if=\"shouldClip(profile.role)\" ng-mouseover=\"showRoleTooltip = true;\" ng-mouseleave=\"showRoleTooltip = false;\">\n" +
\r
21724 " <div ng-if=\"shouldClip(profile.role)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showRoleTooltip\">\n" +
\r
21725 " {{profile.role.slice(0, 25)+'...'}}\n" +
\r
21726 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
\r
21727 " <div class=\"tooltip-size-control\">\n" +
\r
21728 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
\r
21729 " {{profile.role}}\n" +
\r
21735 " <div ng-if=\"!shouldClip(profile.role)\">\n" +
\r
21736 " {{profile.role}}\n" +
\r
21738 " <label>Last login</label>\n" +
\r
21739 " <div ng-if=\"shouldClip(profile.lastLogin)\" ng-mouseover=\"showLastLoginTooltip = true;\" ng-mouseleave=\"showLastLoginTooltip = false;\">\n" +
\r
21740 " <div ng-if=\"shouldClip(profile.lastLogin)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showLastLoginTooltip\">\n" +
\r
21741 " {{profile.lastLogin.slice(0, 25)+'...'}}\n" +
\r
21742 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
\r
21743 " <div class=\"tooltip-size-control\">\n" +
\r
21744 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
\r
21745 " {{profile.lastLogin}}\n" +
\r
21751 " <div ng-if=\"!shouldClip(profile.lastLogin)\">\n" +
\r
21752 " {{profile.lastLogin}}\n" +
\r
21759 angular.module("b2bTemplate/searchField/searchField.html", []).run(["$templateCache", function($templateCache) {
\r
21760 $templateCache.put("b2bTemplate/searchField/searchField.html",
\r
21761 "<div class=\"search-bar\">\n" +
\r
21762 " <div class='input-container' ng-blur=\"blurInput()\">\n" +
\r
21763 " <input type=\"text\" class=\"innershadow b2b-search-input-field\" id=\"{{configObj.labelId}}\" b2b-search-input ng-model=\"inputModel\" ng-disabled=\"disabled\" b2b-reset ng-keydown=\"selectionIndex($event)\" placeholder=\"{{configObj.ghostText}}\" style=\"width:100%\" maxlength=\"{{configObj.maxLength}}\" title=\"{{inputModel}}\" aria-label=\"{{inputModel.length>0?inputModel:configObj.ghostText}}\" aria-live=\"assertive\"/>\n" +
\r
21764 " <button class=\"btn-search\" ng-disabled=\"disabled\" ng-click=\"startSearch()\" b2b-accessibility-click=\"13,32\" aria-label=\"Search\" ng-focus=\"showListFlag = false\" type=\"button\"><i class=\"icoControls-magnifyingglass\"></i></button>\n" +
\r
21766 " <div class=\"search-suggestion-wrapper\" ng-if=\"filterList.length >=0\" ng-show=\"showListFlag\">\n" +
\r
21767 " <ul class=\"search-suggestion-list\" role=\"listbox\"> \n" +
\r
21768 " <li class=\"no-result\" ng-if=\"filterList.length == 0 && configObj.display\">{{configObj.resultText}}</li>\n" +
\r
21769 " <li class=\"search-suggestion-item\" ng-if=\"filterList.length\" ng-repeat=\"item in filterList | limitTo:configObj.noOfItemsDisplay track by $index\" ng-bind-html=\"item.title | b2bMultiSepartorHighlight:inputModel:configObj.searchSeperator\" ng-class=\"{active:isActive($index,filterList.length)}\" ng-click=\"selectItem($index,item.title)\">\n" +
\r
21770 " {{item.title}} \n" +
\r
21777 angular.module("b2bTemplate/seekBar/seekBar.html", []).run(["$templateCache", function($templateCache) {
\r
21778 $templateCache.put("b2bTemplate/seekBar/seekBar.html",
\r
21779 "<div class=\"b2b-seek-bar-container\" ng-class=\"{vertical:verticalSeekBar}\">\n" +
\r
21780 " <div class=\"b2b-seek-bar-track-container\">\n" +
\r
21781 " <div class=\"b2b-seek-bar-track\"></div>\n" +
\r
21782 " <div class=\"b2b-seek-bar-track-fill\"></div>\n" +
\r
21784 " <div class=\"b2b-seek-bar-knob-container\" role=\"menu\" >\n" +
\r
21785 " <div class=\"b2b-seek-bar-knob\" tabindex=\"0\" role=\"menuitem\"></div>\n" +
\r
21790 angular.module("b2bTemplate/slider/slider.html", []).run(["$templateCache", function($templateCache) {
\r
21791 $templateCache.put("b2bTemplate/slider/slider.html",
\r
21792 "<div class=\"b2b-slider-container\" ng-class=\"{'vertical':verticalSlider}\">\n" +
\r
21793 " <div class=\"slider-track-container\">\n" +
\r
21794 " <div class=\"slider-track\"></div>\n" +
\r
21795 " <div class=\"slider-track-fill\" ng-style=\"{backgroundColor:trackFillColor}\"></div>\n" +
\r
21797 " <div class=\"slider-knob-container\" ng-class=\"{'slider-knob-hidden':hideKnob}\">\n" +
\r
21798 " <div class=\"slider-knob\" role=\"slider\" aria-valuemin=\"{{min}}\" aria-valuemax=\"{{max}}\" aria-labelledby=\"{{labelId}}\" aria-valuenow=\"{{currentModelValue}}\" aria-valuetext=\"{{currentModelValue}}{{postAriaLabel}}\" aria-orientation=\"{{verticalSlider ? 'vertical' : 'horizontal'}}\"></div>\n" +
\r
21803 angular.module("b2bTemplate/spinButton/spinButton.html", []).run(["$templateCache", function($templateCache) {
\r
21804 $templateCache.put("b2bTemplate/spinButton/spinButton.html",
\r
21805 "<div class=\"btn-group btn-spinbutton-toggle\" ng-class=\"{'disabled': disabledFlag}\">\n" +
\r
21806 " <button type=\"button\" tabindex=\"{{isMobile?'0':'-1'}}\" aria-hidden=\"{{notMobile}}\" class=\"btn btn-secondary btn-prev icon-primary-subtractminimize\" ng-click=\"minus();focusInputSpinButton($event)\" aria-label=\"Remove {{step}}\" aria-controls=\"{{spinButtonId}}\" ng-class=\"{'disabled': isMinusDisabled()}\" ng-disabled=\"isMinusDisabled()\" aria-disabled=\"{{isMinusDisabled()}}\" role=\"spinbutton\"></button>\n" +
\r
21807 " <input class=\"btn pull-left\" id=\"{{spinButtonId}}\" type=\"tel\" b2b-only-nums=\"3\" maxlength=\"3\" min={{min}} max={{max}} data-max-value=\"{{max}}\" ng-model=\"inputValue[inputModelKey]\" value=\"{{inputValue[inputModelKey]}}\" aria-live=\"polite\" aria-valuenow=\"{{inputValue[inputModelKey]}}\" aria-valuemin=\"{{min}}\" aria-valuemax=\"{{max}}\" ng-disabled=\"disabledFlag\">\n" +
\r
21808 " <button type=\"button\" tabindex=\"{{isMobile?'0':'-1'}}\" aria-hidden=\"{{notMobile}}\" class=\"btn btn-secondary btn-next icon-primary-add-maximize\" ng-click=\"plus();focusInputSpinButton($event)\" aria-label=\"Add {{step}}\" aria-controls=\"{{spinButtonId}}\" ng-class=\"{'disabled': isPlusDisabled()}\" ng-disabled=\"isPlusDisabled()\" aria-disabled=\"{{isPlusDisabled()}}\" role=\"spinbutton\"></button>\n" +
\r
21812 angular.module("b2bTemplate/statusTracker/statusTracker.html", []).run(["$templateCache", function($templateCache) {
\r
21813 $templateCache.put("b2bTemplate/statusTracker/statusTracker.html",
\r
21814 "<div class=\"b2b-status-tracker row\">\n" +
\r
21815 " <button tabindex=\"0\" ng-disabled=\"currentViewIndex === 0\" ng-if=\"statuses.length > b2bStatusTrackerConfig.maxViewItems\" class=\"btn-arrow\" type=\"button\" aria-label=\"Previous status\" ng-click=\"previousStatus()\">\n" +
\r
21816 " <div class=\"btn btn-small btn-secondary\">\n" +
\r
21817 " <i class=\"icon-primary-left\"></i>\n" +
\r
21820 " <div ng-repeat=\"status in statuses\" class=\"b2b-status-tracker-step\" ng-class=\"{ 'complete' : status.complete, 'current' : currentStatus($index)}\" ng-show=\"isInViewport($index)\">\n" +
\r
21821 " <p class=\"b2b-status-tracker-heading\">{{status.heading}}</p>\n" +
\r
21822 " <div class=\"progress\">\n" +
\r
21823 " <div class=\"progress-bar\">\n" +
\r
21824 " <span class=\"hidden-spoken\">\n" +
\r
21825 " {{status.complete ? 'Complete' : 'Incomplete'}}\n" +
\r
21829 " <div class=\"b2b-status-tracker-estimate\">\n" +
\r
21830 " <i ng-show=\"status.estimate !== '' && status.estimate !== undefined\" ng-class=\"status.complete ? 'icoControls-approval' : 'icon-misc-time'\"></i> {{status.complete ? 'Complete' : status.estimate}}\n" +
\r
21834 " <div class=\"b2b-status-tracker-description\">\n" +
\r
21835 " {{status.description}}\n" +
\r
21838 " <button tabindex=\"0\" ng-disabled=\"currentViewIndex + b2bStatusTrackerConfig.maxViewItems === statuses.length\" ng-if=\"statuses.length > b2bStatusTrackerConfig.maxViewItems\" class=\"btn-arrow\" type=\"button\" aria-label=\"Next status\" ng-click=\"nextStatus()\">\n" +
\r
21839 " <div class=\"btn btn-small btn-secondary\">\n" +
\r
21840 " <i class=\"icon-primary-right\"></i>\n" +
\r
21846 angular.module("b2bTemplate/stepTracker/stepTracker.html", []).run(["$templateCache", function($templateCache) {
\r
21847 $templateCache.put("b2bTemplate/stepTracker/stepTracker.html",
\r
21848 "<div class=\"b2b-step-tracker\">\n" +
\r
21849 " <button class=\"btn-arrow b2b-left-arrow\" ng-click=\"previousStatus()\" ng-disabled=\"currentViewIndex === 0\" ng-if=\"stepsItemsObject.length > b2bStepTrackerConfig.maxViewItems\" aria-label=\"Previous step\">\n" +
\r
21850 " <div class=\"btn btn-left btn-small btn-secondary\"><i class=\"icon-primary-left\"></i></div>\n" +
\r
21852 " <button class=\"btn-arrow b2b-right-arrow\" ng-click=\"nextStatus()\" ng-disabled=\"currentViewIndex + b2bStepTrackerConfig.maxViewItems === stepsItemsObject.length\" ng-if=\"stepsItemsObject.length > b2bStepTrackerConfig.maxViewItems\" aria-label=\"Next step\">\n" +
\r
21853 " <div class=\"btn btn-small btn-right btn-secondary\"><i class=\"icon-primary-right\"></i></div>\n" +
\r
21855 " <ul class=\"b2b-steps\">\n" +
\r
21856 " <li ng-class=\"{'b2b-step-done':$index < currentIndex - 1 ,'b2b-step-on':$index == currentIndex - 1}\" \n" +
\r
21857 " ng-repeat=\"stepsItem in stepsItemsObject\" ng-show=\"isInViewport($index)\">\n" +
\r
21858 " <p class=\"b2b-step-text\" data-sm-text=\"{{stepsItem.dataMobile}}\" data-large-text=\"{{stepsItem.dataDesktop}}\">{{stepsItem.text}}</p>\n" +
\r
21859 " <span class=\"hidden-spoken\">\n" +
\r
21860 " {{($index < currentIndex - 1)? 'Complete. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
\r
21861 " {{($index == currentIndex - 1)? 'In Progress. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
\r
21862 " {{($index > currentIndex - 1)? 'Incomplete. '+stepsItem.text+' '+stepsItem.dataMobile:''}}\n" +
\r
21869 angular.module("b2bTemplate/switches/switches-spanish.html", []).run(["$templateCache", function($templateCache) {
\r
21870 $templateCache.put("b2bTemplate/switches/switches-spanish.html",
\r
21871 "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
\r
21872 " <span class=\"btn-slider-on\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"activo\">activo</i></span>\n" +
\r
21873 " <span class=\"switch-handle\"></span>\n" +
\r
21874 " <span class=\"btn-slider-off\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"inactivo\">inactivo</i></span>\n" +
\r
21878 angular.module("b2bTemplate/switches/switches.html", []).run(["$templateCache", function($templateCache) {
\r
21879 $templateCache.put("b2bTemplate/switches/switches.html",
\r
21880 "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
\r
21881 " <span class=\"btn-slider-on\">On</span>\n" +
\r
21882 " <span class=\"switch-handle\"></span>\n" +
\r
21883 " <span class=\"btn-slider-off\">Off</span>\n" +
\r
21887 angular.module("b2bTemplate/tableMessages/tableMessage.html", []).run(["$templateCache", function($templateCache) {
\r
21888 $templateCache.put("b2bTemplate/tableMessages/tableMessage.html",
\r
21889 "<div class=\"b2b-table-message\">\n" +
\r
21890 " <div class=\"b2b-message\" ng-if=\"msgType === 'noMatchingResults'\">\n" +
\r
21891 " <div class=\"b2b-magnify-glass\"></div>\n" +
\r
21893 " <div ng-transclude></div>\n" +
\r
21896 " <div class=\"b2b-message\" ng-if=\"msgType == 'infoCouldNotLoad'\">\n" +
\r
21897 " <div class=\"icon-primary-alert b2b-alert\" aria-label=\"Oops! The information could not load at this time. Please click link to refresh the page.\"></div>\n" +
\r
21898 " <div>Oops!</div>\n" +
\r
21899 " <div>The information could not load at this time.</div>\n" +
\r
21900 " <div>Please <a href=\"javascript:void(0)\" title=\"Refresh the page\" ng-click=\"refreshAction($event)\">refresh the page</a>\n" +
\r
21903 " <div class=\"b2b-message\" ng-if=\"msgType == 'magnifySearch'\">\n" +
\r
21904 " <div class=\"b2b-magnify-glass\"></div>\n" +
\r
21906 " <p class=\"b2b-message-title\">Please input values to\n" +
\r
21907 " <br/> begin your search.</p>\n" +
\r
21910 " <div class=\"b2b-message\" ng-if=\"msgType === 'loadingTable'\">\n" +
\r
21911 " <div class=\"icon-primary-spinner-ddh b2b-loading-dots\"></div>\n" +
\r
21912 " <div ng-transclude></div>\n" +
\r
21918 angular.module("b2bTemplate/tableScrollbar/tableScrollbar.html", []).run(["$templateCache", function($templateCache) {
\r
21919 $templateCache.put("b2bTemplate/tableScrollbar/tableScrollbar.html",
\r
21920 "<div class=\"b2b-table-scrollbar\">\n" +
\r
21921 " <div class=\"b2b-scrollbar-arrows\">\n" +
\r
21922 " <button class=\"btn-arrow b2b-scrollbar-arrow-left\" type=\"button\" ng-attr-aria-label=\"{{disableLeft ? 'Scroll Left Disabled' : 'Scroll Left'}}\" ng-click=\"scrollLeft()\" ng-disabled=\"disableLeft\"><div class=\"btn btn-alt\"><i class=\"icon-primary-left\"></i></div></button>\n" +
\r
21923 " <button class=\"btn-arrow b2b-scrollbar-arrow-right\" ng-attr-aria-label=\"{{disableRight ? 'Scroll Right Disabled' : 'Scroll Right'}}\" ng-click=\"scrollRight()\" ng-disabled=\"disableRight\" type=\"button\"><div class=\"btn btn-alt\"><i class=\"icon-primary-right\"></i></div></button>\n" +
\r
21925 " <div class=\"b2b-table-inner-container\">\n" +
\r
21926 " <span ng-transclude></span>\n" +
\r
21931 angular.module("b2bTemplate/tables/b2bTable.html", []).run(["$templateCache", function($templateCache) {
\r
21932 $templateCache.put("b2bTemplate/tables/b2bTable.html",
\r
21933 "<table ng-class=\"{'striped': responsive, 'complex-table': responsive && active}\" ng-transclude></table>");
\r
21936 angular.module("b2bTemplate/tables/b2bTableBody.html", []).run(["$templateCache", function($templateCache) {
\r
21937 $templateCache.put("b2bTemplate/tables/b2bTableBody.html",
\r
21938 "<td ng-hide=\"isHidden()\" ng-transclude></td>");
\r
21941 angular.module("b2bTemplate/tables/b2bTableHeaderSortable.html", []).run(["$templateCache", function($templateCache) {
\r
21942 $templateCache.put("b2bTemplate/tables/b2bTableHeaderSortable.html",
\r
21943 "<th scope=\"col\" role=\"columnheader\" aria-sort=\"{{sortPattern !== 'null' && 'none' || sortPattern}}\" aria-label=\"{{headerName}} {{sortable !== 'false' && ': activate to sort' || ' '}} {{sortPattern !== 'null' && '' || sortPattern}}\" tabindex=\"{{sortable !== 'false' && '0' || '-1'}}\" b2b-accessibility-click=\"13,32\" ng-click=\"(sortable !== 'false') && sort();\" ng-hide=\"isHidden()\">\n" +
\r
21944 " <span ng-transclude></span>\n" +
\r
21945 " <i ng-class=\"{'icon-primary-arrows-sort-arrow active': sortPattern === 'ascending', 'icon-primary-arrows-sort-arrow active down': sortPattern === 'descending'}\"></i>\n" +
\r
21949 angular.module("b2bTemplate/tables/b2bTableHeaderUnsortable.html", []).run(["$templateCache", function($templateCache) {
\r
21950 $templateCache.put("b2bTemplate/tables/b2bTableHeaderUnsortable.html",
\r
21951 "<th scope=\"col\" ng-hide=\"isHidden()\" ng-transclude></th>");
\r
21954 angular.module("b2bTemplate/tabs/b2bTab.html", []).run(["$templateCache", function($templateCache) {
\r
21955 $templateCache.put("b2bTemplate/tabs/b2bTab.html",
\r
21956 "<li role=\"tab\" aria-selected=\"{{isTabActive()}}\" aria-controls=\"{{tabPanelId}}\" class=\"tab\" \n" +
\r
21957 " ng-class=\"{'active': isTabActive()}\" ng-click=\"clickTab()\" ng-hide=\"tabItem.disabled\">\n" +
\r
21958 " <a href=\"javascript:void(0)\" tabindex=\"{{(isTabActive() && tabItem.disabled)?-1:0}}\"\n" +
\r
21959 " ng-disabled=\"tabItem.disabled\" aria-expanded=\"{{(isTabActive() && !tabItem.disabled)}}\" \n" +
\r
21960 " b2b-accessibility-click=\"13,32\" ng-transclude></a>\n" +
\r
21961 " <span class=\"hidden-spoken\" ng-if=\"isTabActive()\">Active tab</span>\n" +
\r
21965 angular.module("b2bTemplate/tabs/b2bTabset.html", []).run(["$templateCache", function($templateCache) {
\r
21966 $templateCache.put("b2bTemplate/tabs/b2bTabset.html",
\r
21967 "<ul class=\"tabs promo-tabs\" role=\"tablist\" ng-transclude></ul>");
\r
21970 angular.module("b2bTemplate/treeNav/groupedTree.html", []).run(["$templateCache", function($templateCache) {
\r
21971 $templateCache.put("b2bTemplate/treeNav/groupedTree.html",
\r
21972 "<ul role=\"group\">\n" +
\r
21973 " <li aria-expanded=\"{{(member.active?true:false)}}\" role=\"treeitem\" aria-label=\"{{key}}\" ng-repeat='(key,value) in collection | groupBy : \"grpChild\"' b2b-tree-link><a class=\"grp\" ng-class=\"{'active': value.showGroup == true}\" tabindex=\"-1\" href='javascript:void(0);'>{{(key)?key:''}}<span class=\"b2b-tree-node-icon\"><i ng-class=\"{'icon-primary-expanded': value.showGroup == true, 'icon-primary-collapsed': value.showGroup == undefined || value.showGroup == false }\"></i></span></a>\n" +
\r
21974 " <ul role=\"group\">\n" +
\r
21975 " <b2b-member ng-repeat='member in value.childArray' member='member' time-delay='{{timeDelay}}' group-it='groupIt'></b2b-member>\n" +
\r
21981 angular.module("b2bTemplate/treeNav/treeMember.html", []).run(["$templateCache", function($templateCache) {
\r
21982 $templateCache.put("b2bTemplate/treeNav/treeMember.html",
\r
21983 "<li role=\"treeitem\" aria-expanded=\"{{(member.active?true:false)}}\" aria-label=\"{{member.tooltipContent ? member.tooltipContent : member.name}}\" aria-describedby=\"description_{{$id}}\" ng-class=\"{'bg':member.selected}\" b2b-tree-link>\n" +
\r
21984 " <a tabindex=\"-1\" title=\"{{member.tooltipContent ? member.tooltipContent : member.name}}\" href=\"javascript:void(0)\" ng-class=\"{'active':member.active,'b2b-locked-node':member.locked}\"> <span class=\"b2b-tree-node-name\">{{member.name}}</span>\n" +
\r
21985 " <span class=\"{{!member.child?'end':''}} b2b-tree-node-icon\">\n" +
\r
21986 " <i class=\"b2b-tree-expandCollapse-icon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
\r
21988 " <div id=\"description_{{$id}}\" class=\"offscreen-text\">\n" +
\r
21989 " {{member.descriptionText}}\n" +
\r
21991 " <div class=\"b2b-tree-tooltip\" ng-if=\"member.showTooltip\">\n" +
\r
21992 " <span class=\"b2b-tree-arrow-left\"></span>\n" +
\r
21993 " <div class=\"b2b-tree-tooltip-content\">\n" +
\r
21994 " {{member.tooltipContent}}\n" +
\r
22001 angular.module("b2bTemplate/treeNav/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
\r
22002 $templateCache.put("b2bTemplate/treeNav/ungroupedTree.html",
\r
22003 "<ul role=\"{{setRole}}\"><b2b-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-member></ul>");
\r
22006 angular.module("b2bTemplate/treeNodeCheckbox/groupedTree.html", []).run(["$templateCache", function($templateCache) {
\r
22007 $templateCache.put("b2bTemplate/treeNodeCheckbox/groupedTree.html",
\r
22008 "<ul role=\"group\">\n" +
\r
22009 " <li aria-expanded=\"{{(member.active?true:false)}}\" role=\"treeitem\" aria-label=\"{{key}}\" ng-repeat='(key,value) in collection | groupBy : \"grpChild\"' b2b-tree-node-link><a class=\"grp\" ng-class=\"{'active': value.showGroup == true}\" tabindex=\"-1\" href='javascript:void(0);'>\n" +
\r
22010 " <span class=\"ng-hide\">\n" +
\r
22011 " <label class=\"checkbox\">\n" +
\r
22012 " <input type=\"checkbox\" tabindex=\"-1\" class=\"treeCheckBox grpTreeCheckbox\" style=\"margin-top:2px;\"/><i class=\"skin\"></i><span> {{(key)?key:''}}</span>\n" +
\r
22016 " {{(key)?key:''}} \n" +
\r
22018 " <span class=\"nodeIcon\"><i class=\"expandCollapseIcon\" ng-class=\"{'icon-primary-expanded': value.showGroup == true, 'icon-primary-collapsed': value.showGroup == undefined || value.showGroup == false }\"></i></span></a>\n" +
\r
22019 " <ul role=\"group\">\n" +
\r
22020 " <b2b-tree-member ng-repeat='member in value.childArray' member='member' group-it='groupIt'></b2b-tree-member>\n" +
\r
22026 angular.module("b2bTemplate/treeNodeCheckbox/treeMember.html", []).run(["$templateCache", function($templateCache) {
\r
22027 $templateCache.put("b2bTemplate/treeNodeCheckbox/treeMember.html",
\r
22028 "<li role=\"treeitem\" aria-expanded=\"{{(member.active?true:false)}}\" aria-label=\"{{member.name}}\" ng-class=\"{'bg':member.selected}\" b2b-tree-node-link>\n" +
\r
22029 " <a tabindex=\"-1\" title=\"{{member.name}}\" href=\"javascript:void(0)\" ng-class=\"{'active':member.active}\">\n" +
\r
22030 " <span ng-show=\"member.displayCheckbox\">\n" +
\r
22031 " <label class=\"checkbox\">\n" +
\r
22032 " <input type=\"checkbox\" tabindex=\"-1\" ng-model=\"member.isSelected\" ng-class=\"{'treeCheckBox': (member.displayCheckbox !== undefined)}\" style=\"margin-top:2px;\"/><i class=\"skin\"></i><span> {{member.name}}</span>\n" +
\r
22035 " <span ng-show=\"!member.displayCheckbox\">\n" +
\r
22036 " {{member.name}} \n" +
\r
22038 " <span class=\"nodeIcon {{!member.child?'end':''}}\">\n" +
\r
22039 " <i class=\"expandCollapseIcon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
\r
22045 angular.module("b2bTemplate/treeNodeCheckbox/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
\r
22046 $templateCache.put("b2bTemplate/treeNodeCheckbox/ungroupedTree.html",
\r
22047 "<ul role=\"{{setRole}}\"><b2b-tree-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-tree-member></ul>");
\r