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
3623 /*Change for ECOMP - hide header menu when click on iframe in a new tab*/
\r
3624 $(window).blur(function() {
\r
3625 if(scope.showMenu){
\r
3626 scope.showMenu = false;
\r
3627 elem.removeClass('active');
\r
3633 }]).directive('b2bHeaderSubmenu', ['$timeout',function ($timeout) {
\r
3636 link: function (scope, elem) {
\r
3637 var caretSign = angular.element("<i class='menuCaret'></i>");
\r
3638 $timeout(function(){
\r
3639 var menuItem = angular.element(elem[0].children[0]);
\r
3640 menuItem.bind('focus mouseenter', function () {
\r
3641 elem.parent().children().removeClass('active');
\r
3642 elem.addClass('active');
\r
3643 if(elem[0].childElementCount > 1){ // > 1 has third level menu
\r
3644 menuItem.attr('aria-expanded',true);
\r
3645 menuItem.attr('aria-haspopup',true);
\r
3647 var caretLeft = (elem[0].offsetLeft + elem[0].offsetWidth/2) - 10;
\r
3648 caretSign.css({left: caretLeft + 'px'});
\r
3649 angular.element(caretSign);
\r
3650 var tertiaryItems = elem[0].querySelectorAll('[b2b-header-tertiarymenu]');
\r
3651 if(tertiaryItems.length >=1){
\r
3652 elem.append(caretSign);
\r
3655 menuItem.bind('blur', function () {
\r
3656 $timeout(function () {
\r
3657 var parentElm = document.activeElement.parentElement.parentElement;
\r
3659 if (!(parentElm.hasAttribute('b2b-header-tertiarymenu'))) {
\r
3660 elem.removeClass('active');
\r
3661 if(elem[0].childElementCount > 1){ // > 1 has third level menu
\r
3662 menuItem.attr('aria-expanded',false);
\r
3664 var caret = elem[0].querySelector('.menuCaret');
\r
3675 }]).directive('b2bHeaderTertiarymenu', ['$timeout','keymap', function ($timeout,keymap){
\r
3678 require:'^b2bHeaderMenu',
\r
3679 link: function (scope, elem,attr,ctrl) {
\r
3681 elem.bind('keydown', function (evt) {
\r
3682 var activeElm = document.activeElement;
\r
3683 var activeParentElm = activeElm.parentElement;
\r
3684 var activeParentObj = angular.element(activeParentElm)[0];
\r
3686 if(activeParentElm.hasAttribute('b2b-tertiary-link')){
\r
3687 var quarterNav = angular.element(activeParentElm)[0].querySelector('li[b2b-header-quarternarymenu]');
\r
3689 var links = ctrl.getFirstElement(angular.element(quarterNav)[0],'a');
\r
3692 var tertiaryMenu = activeElm.parentElement.parentElement.parentElement;
\r
3693 var tertiaryMenuFlag = tertiaryMenu.hasAttribute('b2b-tertiary-link');
\r
3695 switch (evt.keyCode) {
\r
3696 case keymap.KEY.DOWN:
\r
3697 evt.stopPropagation();
\r
3698 evt.preventDefault();
\r
3699 if (activeParentElm.hasAttribute('b2b-tertiary-link')) {
\r
3700 if(angular.element(quarterNav).hasClass('active')){
\r
3702 }else if(activeParentObj.nextElementSibling){
\r
3703 ctrl.nextSiblingFocus(activeParentObj,true);
\r
3706 else if(angular.element(activeParentElm).hasClass('active')){
\r
3707 ctrl.nextSiblingFocus(activeElm);
\r
3710 case keymap.KEY.UP:
\r
3711 evt.stopPropagation();
\r
3712 evt.preventDefault();
\r
3713 if(activeParentElm.hasAttribute('b2b-tertiary-link')){
\r
3714 if(activeParentObj.previousElementSibling.hasAttribute('b2b-tertiary-link')){
\r
3715 ctrl.previousSiblingFocus(activeParentObj,true);
\r
3717 var elm = angular.element(activeElm.parentElement.parentElement.parentElement.parentElement.parentElement)[0];
\r
3718 ctrl.getFirstElement(elm,"a").focus();
\r
3720 }else if(angular.element(activeParentElm).hasClass('active')){
\r
3721 if (activeElm.previousElementSibling) {
\r
3722 ctrl.previousSiblingFocus(activeElm);
\r
3723 }else if (tertiaryMenuFlag) {
\r
3724 var elm = angular.element(tertiaryMenu)[0];
\r
3725 ctrl.getFirstElement(elm,"a.header-tertiaryitem").focus();
\r
3735 }]).directive('b2bHeaderTogglemenu', ['$timeout', 'keymap', function ($timeout, keymap) {
\r
3738 require: '^b2bHeaderMenu',
\r
3739 link: function (scope, elem, attrs, ctrl) {
\r
3741 $timeout(function () {
\r
3742 quarterNav = angular.element(elem.parent())[0].querySelector('li[b2b-header-quarternarymenu]');
\r
3743 elem.bind('click', function () {
\r
3744 angular.element(quarterNav).toggleClass('active');
\r
3749 }]).directive('b2bHeaderResponsive', ['$timeout',function ($timeout) {
\r
3752 controller: function($scope){
\r
3753 this.applyMediaQueries = function(value){
\r
3754 document.querySelector('style').textContent +=
\r
3755 "@media screen and (max-width:950px) { \
\r
3756 .header__item.profile { right: " + value + "px; } \
\r
3759 this.arrangeResponsiveHeader = function(children){
\r
3761 * clientWidth of 1090 === max-width of 1100px
\r
3762 * clientWidth of 920 === max-width of 950px
\r
3763 * see b2b-angular.css for rest of responsive header CSS
\r
3765 if (document.documentElement.clientWidth <= 920) {
\r
3768 this.applyMediaQueries(200);
\r
3771 this.applyMediaQueries(200);
\r
3773 default: // anthing above 3, however, should not have more than 3 to date
\r
3774 this.applyMediaQueries(200);
\r
3779 link: function (scope, elem, attrs, ctrl) {
\r
3784 $timeout(function(){
\r
3785 profile = document.querySelector('li.header__item.profile');
\r
3786 children = angular.element(profile).children().length;
\r
3788 ctrl.arrangeResponsiveHeader(children); // shift right-side icon flyovers
\r
3791 // on screen resize
\r
3792 window.addEventListener('resize', function(event){ // caret adjustmet
\r
3793 var activeSubmenu = elem[0].querySelector('[b2b-header-menu] [b2b-header-submenu].active');
\r
3794 var activeSubmenuEl = angular.element(activeSubmenu);
\r
3795 if(activeSubmenu){
\r
3796 var caretSign = activeSubmenu.querySelector('i.menuCaret');
\r
3798 var caretSignEl = angular.element(caretSign);
\r
3799 var caretLeft = (activeSubmenu.offsetLeft + activeSubmenu.offsetWidth/2) - 10;
\r
3800 caretSignEl.css({left: caretLeft + 'px'});
\r
3804 ctrl.arrangeResponsiveHeader(children); // shift right-side icon flyovers
\r
3811 * @ngdoc directive
\r
3812 * @name Layouts.att:headings
\r
3815 * <file src="src/headings/docs/readme.md" />
\r
3818 <h1 class="heading-page">38px page heading</h1>
\r
3819 <h2 class="heading-major-section">30px major section heading</h2>
\r
3820 <h3 class="heading-sub-section">24px sub-section heading</h3>
\r
3821 <h2 class="heading-medium">20px medium heading</h2>
\r
3822 <h2 class="heading-medium-emphasis">20px medium emphasis heading</h2>
\r
3823 <h3 class="heading-small">18px small heading</h3>
\r
3824 <h3 class="heading-small-emphasis">18px small emphasis heading</h3>
\r
3825 <h3 class="heading-micro">13px micro heading</h3>
\r
3827 <h2 class="heading-group">Lead</h2>
\r
3828 <h1 class="heading-page">This is 38px heading</h1>
\r
3829 <h2 class="lead">This is lead text...The next big thing since the last big thing we announced.</h2>
\r
3830 <h2 class="heading-group">Eyebrow</h2>
\r
3831 <h3 class="eyebrow">EYEBROW TEXT</h3>
\r
3832 <h2 class="heading-major-section">This is a 30px heading</h2>
\r
3833 <h3 class="eyebrow">EYEBROW TEXT</h3>
\r
3834 <h3 class="heading-sub-section">24px sub-section heading</h3>
\r
3835 <h2 class="heading-group">Subheading</h2>
\r
3836 <h2 class="heading-major-section">This is a 30px heading</h2>
\r
3837 <h3 class="subheading">A subheading here to support what was said above</h3>
\r
3839 <section id="code">
\r
3840 <b>HTML + AngularJS</b>
\r
3841 <example module="b2b.att">
\r
3842 <file src="src/headings/docs/demo.html" />
\r
3847 var b2bLegalCopy = angular.module('b2b.att.headings', []);
\r
3849 * @ngdoc directive
\r
3850 * @name Tabs, tables & accordions.att:horizontalTable
\r
3853 * <file src="src/horizontalTable/docs/readme.md" />
\r
3856 * @param {int} sticky - Number of sticky columns to have. Maximum of 3.
\r
3857 * @param {boolean} refresh - A boolean that when set to true will force a re-render of table. Only use when using 'bulk mode'
\r
3859 * <section id="code">
\r
3860 <example module="b2b.att">
\r
3861 <file src="src/horizontalTable/docs/demo.html" />
\r
3862 <file src="src/horizontalTable/docs/demo.js" />
\r
3867 angular.module('b2b.att.horizontalTable', [])
\r
3868 .constant('b2bHorizontalTableConfig', {
\r
3869 'maxStickyColumns': 3
\r
3871 .directive('b2bHorizontalTable', ['$timeout', 'b2bHorizontalTableConfig', 'b2bDOMHelper', function ($timeout, b2bHorizontalTableConfig, b2bDOMHelper) {
\r
3877 numOfStickyCols: '=?sticky',
\r
3881 templateUrl: 'b2bTemplate/horizontalTable/horizontalTable.html',
\r
3882 link: function (scope, element, attrs, ctrl) {
\r
3883 scope.numOfStickyCols = scope.numOfStickyCols || 1;
\r
3884 scope.viewportIndex = scope.numOfStickyCols;
\r
3886 // JM520E: This is a temporary hack until I solve the ngRepeat issue
\r
3888 if (element.find('th').length < scope.numOfStickyCols) {
\r
3889 // DOM ngRepeat is not ready, let's check back in 10 ms
\r
3890 console.info('THs are not ready, trying again in 10ms');
\r
3891 $timeout(hack, 10, false);
\r
3898 if (attrs.refresh !== undefined && attrs.refresh !== '') {
\r
3899 scope.$watch('refresh', function(oldVal, newVal) {
\r
3900 if (scope.refresh) {
\r
3901 // From testing it takes about 30 ms before ngRepeat executes, so let's set initial timeout
\r
3902 // NOTE: May need to expose timeout to developers. Application is known to have digest cycle of 3-5k watches.
\r
3903 $timeout(init, 100, false);
\r
3904 scope.refresh = false;
\r
3909 var tableElement = element.find('table');
\r
3910 var thElements = element.find('th');
\r
3911 var innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container'));
\r
3912 var outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table'));
\r
3914 var tableColumns = [];
\r
3915 var tableRows = element.find('tr');
\r
3919 var totalWidth = element.children()[0].offsetWidth;
\r
3920 var lastVisibleColumn = 0;
\r
3921 var collectiveColumnWidth = [];
\r
3922 var collectiveRowHeight = [];
\r
3923 var columnSets = [];
\r
3924 var setIndex = 0;
\r
3925 var stickyPixels = 0;
\r
3927 var displayNoneCSS = {'display': 'none'};
\r
3928 var displayBlockCSS = {'display': 'table-cell'};
\r
3930 function calculateVisibleColumns(startingPoint) {
\r
3931 var usedWidth = 0,
\r
3932 visibleColumns = startingPoint || scope.numOfStickyCols;
\r
3934 while(usedWidth < stickyPixels && visibleColumns < collectiveColumnWidth.length) {
\r
3935 if (usedWidth+collectiveColumnWidth[visibleColumns] > stickyPixels) {
\r
3936 if (startingPoint === visibleColumns) {
\r
3937 return visibleColumns; // The next cell is too large to fit, it should be only thing to fit
\r
3940 return visibleColumns;
\r
3942 usedWidth += collectiveColumnWidth[visibleColumns];
\r
3946 if (usedWidth > stickyPixels) {
\r
3947 return --visibleColumns;
\r
3949 return visibleColumns;
\r
3952 function updateCellDisplay(set) {
\r
3953 for (var i = scope.numOfStickyCols; i < tableColumns.length; i++) {
\r
3954 angular.element(tableColumns[i]).css(displayNoneCSS);
\r
3957 for (var i = set[0]; i <= set[1]; i++) {
\r
3958 angular.element(tableColumns[i]).css(displayBlockCSS);
\r
3962 function forceDigest() {
\r
3963 if (!scope.$$phase) {
\r
3968 function findMax(arr, prop) {
\r
3973 for (var i = 0; i < arr.length; i++) {
\r
3975 prevDisplay = angular.element(item).css('display');
\r
3976 if (scope.$$phase) {
\r
3979 if (prop === 'width') {
\r
3980 localVal = Math.ceil(parseInt(window.getComputedStyle(item).width.split('px')[0], 10)) + 30; // 30 px is padding
\r
3981 } else if (prop === 'offsetWidth') {
\r
3982 localVal = item.offsetWidth;
\r
3983 } else if (prop === 'height') {
\r
3984 localVal = item.offsetHeight;
\r
3987 if (localVal >= max) {
\r
3996 // Reset this from a previous execution
\r
3997 tableColumns = [];
\r
3998 collectiveColumnWidth = [];
\r
3999 collectiveRowHeight = [];
\r
4002 lastVisibleColumn = 0;
\r
4005 visibleColumns = [];
\r
4008 tableElement = element.find('table');
\r
4009 thElements = element.find('th');
\r
4010 innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container'));
\r
4011 outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table'));
\r
4012 totalWidth = element.children()[0].offsetWidth;
\r
4015 tableRows = element.find('tr');
\r
4016 totalWidth = element.children()[0].offsetWidth;
\r
4018 scope.disableLeft = true;
\r
4019 scope.disableRight = false;
\r
4021 if (scope.numOfStickyCols > b2bHorizontalTableConfig.maxStickyColumns) {
\r
4022 throw new Error("Table can only support 3 sticky columns.");
\r
4025 angular.forEach(tableRows, function(row, rowIndex) {
\r
4026 collectiveRowHeight.push(findMax(row.children, 'height'));
\r
4027 for(var j = 0; j < row.children.length; j++) {
\r
4028 if (tableColumns[j] === undefined) {
\r
4029 tableColumns[j] = [];
\r
4031 tableColumns[j].push(row.children[j]);
\r
4035 // We need to reset all the displayNones from previous runs, if applicable
\r
4036 if (attrs.refresh !== undefined && attrs.refresh !== '') {
\r
4037 for (var i = scope.numOfStickyCols+1; i < tableColumns.length; i++) {
\r
4038 angular.element(tableColumns[i]).css(displayBlockCSS);
\r
4042 for (var i = 0; i < tableColumns.length; i++) {
\r
4043 collectiveColumnWidth.push(findMax(tableColumns[i], 'width')); //offsetWidth doesn't take into account custom css inside
\r
4045 for(var i = 0; i < scope.numOfStickyCols; i++) {
\r
4046 maxWidth += collectiveColumnWidth[i];
\r
4049 stickyPixels = totalWidth-maxWidth;
\r
4052 // At this point, for each tr, I need to set the properties (height) and each numOfStickyCols children
\r
4053 // should be set with sticky properties (margin-left and width)
\r
4054 var width = maxWidth;
\r
4055 for(var i = 0; i < scope.numOfStickyCols; i++) {
\r
4056 for (var j = 0; j < tableRows.length; j++) {
\r
4057 trObject = angular.element(tableRows[j].children[i]);
\r
4059 angular.element(trObject).css({
\r
4060 'margin-left': -(width + 2) + 'px',
\r
4061 'width': (collectiveColumnWidth[i] + 3) + 'px', // instead of taking the max width, grab max width for that column
\r
4062 'height': collectiveRowHeight[j] + 'px',
\r
4063 'position': 'absolute',
\r
4064 'background-color': 'lightgrey'
\r
4069 width -= collectiveColumnWidth[i];
\r
4072 innerContainer.css({
\r
4073 'padding-left': (maxWidth + 2) + 'px'
\r
4077 // Let's precompute all the (set) combinations beforehand
\r
4079 for (var i = scope.numOfStickyCols; i < tableColumns.length;) {
\r
4080 visibleColumns = calculateVisibleColumns(i);
\r
4081 columnSets.push([i, visibleColumns]);
\r
4082 i = visibleColumns + 1;
\r
4085 updateCellDisplay(columnSets[setIndex]);
\r
4086 checkScrollArrows();
\r
4088 scope.numOfCols = tableColumns.length;
\r
4090 console.log('Bulk Mode is ' + (attrs.bulkMode ? 'enabled': 'disabled'));
\r
4091 console.log('tableColumns', tableColumns);
\r
4092 console.log('collectiveColumnWidth: ', collectiveColumnWidth);
\r
4093 console.log('maxWidth: ', maxWidth);
\r
4096 function checkScrollArrows() {
\r
4097 scope.disableLeft = (setIndex === 0);
\r
4098 scope.disableRight = !(setIndex < columnSets.length-1);
\r
4102 scope.moveViewportLeft = function () {
\r
4104 updateCellDisplay(columnSets[setIndex]);
\r
4105 checkScrollArrows();
\r
4107 if (scope.disableLeft) {
\r
4108 element.find('span')[0].focus();
\r
4112 scope.moveViewportRight = function () {
\r
4114 updateCellDisplay(columnSets[setIndex]);
\r
4115 checkScrollArrows();
\r
4117 if (scope.disableRight) {
\r
4118 element.find('span')[0].focus();
\r
4122 scope.getColumnSet = function () {
\r
4123 return columnSets[setIndex];
\r
4126 innerContainer.bind('scroll', function () {
\r
4127 $timeout(function () {
\r
4128 checkScrollArrows();
\r
4136 * @ngdoc directive
\r
4137 * @name Forms.att:hourPicker
\r
4140 * <file src="src/hourPicker/docs/readme.md" />
\r
4143 * <div b2b-hourpicker ng-model="hourpickerValue.value"></div>
\r
4146 * <section id="code">
\r
4147 <example module="b2b.att">
\r
4148 <file src="src/hourPicker/docs/demo.html" />
\r
4149 <file src="src/hourPicker/docs/demo.js" />
\r
4154 angular.module('b2b.att.hourPicker', ['b2b.att.utilities'])
\r
4156 .constant('b2bHourpickerConfig', {
\r
4173 title: 'wednesday',
\r
4178 title: 'thursday',
\r
4188 title: 'saturday',
\r
4193 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
4194 startTimeDefaultOptionIndex: -1,
\r
4195 startTimeDefaultMeridiem: "am",
\r
4196 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
4197 endTimeDefaultOptionIndex: -1,
\r
4198 endTimeDefaultMeridiem: "pm",
\r
4199 sameDayOption: true
\r
4202 .factory('b2bNormalizeHourpickerValues', [function () {
\r
4203 var _normalize = function (hourpickerValues) {
\r
4204 if (angular.isDefined(hourpickerValues) && hourpickerValues != null) {
\r
4205 var finalHourpickerValues = [];
\r
4206 var hourpickerValue = {};
\r
4208 for (var i = 0; i < hourpickerValues.length; i++) {
\r
4209 days = hourpickerValues[i].days ? hourpickerValues[i].days : {};
\r
4210 hourpickerValue.startTime = hourpickerValues[i].startTime ? hourpickerValues[i].startTime : '';
\r
4211 hourpickerValue.startMeridiem = hourpickerValues[i].startMeridiem ? hourpickerValues[i].startMeridiem : '';
\r
4212 hourpickerValue.endTime = hourpickerValues[i].endTime ? hourpickerValues[i].endTime : '';
\r
4213 hourpickerValue.endMeridiem = hourpickerValues[i].endMeridiem ? hourpickerValues[i].endMeridiem : '';
\r
4214 hourpickerValue.days = [];
\r
4216 var retrieveDaysText = function (daysDetails) {
\r
4217 var daysTexts = [];
\r
4221 for (var i in days) {
\r
4222 if (days[i].value) {
\r
4223 daysTexts.push(i);
\r
4227 first = daysTexts[0];
\r
4228 last = daysTexts[0];
\r
4230 hourpickerValue.days[index] = days[first].caption;
\r
4231 if (daysTexts.length > 1) {
\r
4232 for (var i = 1; i < daysTexts.length; i++) {
\r
4233 if (daysTexts[i] - last === 1) {
\r
4234 last = daysTexts[i];
\r
4235 hourpickerValue.days[index] = days[first].caption + ' - ' + days[last].caption;
\r
4238 first = last = daysTexts[i];
\r
4239 hourpickerValue.days[index] = days[first].caption;
\r
4244 retrieveDaysText();
\r
4246 finalHourpickerValues.push(angular.copy(hourpickerValue));
\r
4249 return angular.copy(finalHourpickerValues);
\r
4254 normalize: _normalize
\r
4258 .directive('b2bHourpicker', ['b2bHourpickerConfig', 'b2bNormalizeHourpickerValues', function (b2bHourpickerConfig, b2bNormalizeHourpickerValues) {
\r
4263 require: 'ngModel',
\r
4264 templateUrl: 'b2bTemplate/hourPicker/b2bHourpicker.html',
\r
4265 controller: ['$scope', function (scope) {
\r
4268 link: function (scope, elem, attr, ctrl) {
\r
4269 scope.hourpicker = {};
\r
4270 scope.hourpicker.dayOptions = attr.dayOptions ? scope.$parent.$eval(attr.dayOptions) : b2bHourpickerConfig.dayOptions;
\r
4271 scope.hourpicker.startTimeOptions = attr.startTimeOptions ? scope.$parent.$eval(attr.startTimeOptions) : b2bHourpickerConfig.startTimeOptions;
\r
4272 scope.hourpicker.endTimeOptions = attr.endTimeOptions ? scope.$parent.$eval(attr.endTimeOptions) : b2bHourpickerConfig.endTimeOptions;
\r
4273 scope.hourpicker.startTimeDefaultOptionIndex = attr.startTimeDefaultOptionIndex ? scope.$parent.$eval(attr.startTimeDefaultOptionIndex) : b2bHourpickerConfig.startTimeDefaultOptionIndex;
\r
4274 scope.hourpicker.endTimeDefaultOptionIndex = attr.endTimeDefaultOptionIndex ? scope.$parent.$eval(attr.endTimeDefaultOptionIndex) : b2bHourpickerConfig.endTimeDefaultOptionIndex;
\r
4275 scope.hourpicker.startTimeDefaultMeridiem = attr.startTimeDefaultMeridiem ? scope.$parent.$eval(attr.startTimeDefaultMeridiem) : b2bHourpickerConfig.startTimeDefaultMeridiem;
\r
4276 scope.hourpicker.endTimeDefaultMeridiem = attr.endTimeDefaultMeridiem ? scope.$parent.$eval(attr.endTimeDefaultMeridiem) : b2bHourpickerConfig.endTimeDefaultMeridiem;
\r
4277 scope.hourpicker.sameDayOption = attr.sameDayOption ? scope.$parent.$eval(attr.sameDayOption) : b2bHourpickerConfig.sameDayOption;
\r
4278 scope.hourpicker.editMode = -1;
\r
4280 scope.hourpickerValues = [];
\r
4281 scope.finalHourpickerValues = [];
\r
4282 scope.addHourpickerValue = function (hourpickerPanelValue) {
\r
4283 if (hourpickerPanelValue) {
\r
4284 if (scope.hourpicker.editMode > -1) {
\r
4285 scope.hourpickerValues[scope.hourpicker.editMode] = hourpickerPanelValue;
\r
4286 scope.hourpicker.editMode = -1;
\r
4288 scope.hourpickerValues.push(hourpickerPanelValue);
\r
4291 scope.finalHourpickerValues = b2bNormalizeHourpickerValues.normalize(angular.copy(scope.hourpickerValues));
\r
4292 ctrl.$setViewValue(angular.copy(scope.hourpickerValues));
\r
4294 ctrl.$render = function () {
\r
4295 if (angular.isDefined(ctrl.$modelValue)) {
\r
4296 scope.hourpickerValues = angular.copy(ctrl.$modelValue);
\r
4297 scope.finalHourpickerValues = b2bNormalizeHourpickerValues.normalize(angular.copy(scope.hourpickerValues));
\r
4300 scope.editHourpickerValue = function (index) {
\r
4301 scope.hourpickerPanelValue = angular.copy(scope.hourpickerValues[index]);
\r
4302 scope.hourpicker.editMode = index;
\r
4304 scope.deleteHourpickerValue = function (index) {
\r
4305 scope.hourpickerValues.splice(index, 1);
\r
4306 scope.resetHourpickerPanelValue();
\r
4307 scope.addHourpickerValue();
\r
4310 scope.setValidity = function (errorType, errorValue) {
\r
4311 ctrl.$setValidity(errorType, errorValue);
\r
4317 .directive('b2bHourpickerPanel', [function () {
\r
4321 templateUrl: 'b2bTemplate/hourPicker/b2bHourpickerPanel.html',
\r
4322 controller: ['$scope', function (scope) {
\r
4325 link: function (scope, elem, attr, ctrl) {
\r
4326 var hourpickerPanelValueTemplate = {
\r
4329 startMeridiem: 'am',
\r
4333 for (var i = 0; i < scope.hourpicker.dayOptions.length; i++) {
\r
4334 hourpickerPanelValueTemplate.days[i] = {
\r
4336 title: scope.hourpicker.dayOptions[i].title,
\r
4337 caption: scope.hourpicker.dayOptions[i].caption
\r
4340 scope.hourpickerPanelValue = {};
\r
4341 scope.disableAddBtn = true;
\r
4343 scope.$watch('hourpickerPanelValue.days', function(){
\r
4344 for(var i in scope.hourpickerPanelValue.days)
\r
4346 if(scope.hourpickerPanelValue.days[i].value)
\r
4348 scope.disableAddBtn = false;
\r
4351 scope.disableAddBtn = true;
\r
4355 scope.resetHourpickerPanelValue = function () {
\r
4356 scope.hourpickerPanelValue = angular.copy(hourpickerPanelValueTemplate);
\r
4357 if (scope.hourpicker.startTimeDefaultOptionIndex > -1) {
\r
4358 scope.hourpickerPanelValue.startTime = scope.hourpicker.startTimeOptions[scope.hourpicker.startTimeDefaultOptionIndex];
\r
4360 if (scope.hourpicker.endTimeDefaultOptionIndex > -1) {
\r
4361 scope.hourpickerPanelValue.endTime = scope.hourpicker.endTimeOptions[scope.hourpicker.endTimeDefaultOptionIndex];
\r
4363 scope.hourpickerPanelValue.startMeridiem = scope.hourpicker.startTimeDefaultMeridiem;
\r
4364 scope.hourpickerPanelValue.endMeridiem = scope.hourpicker.endTimeDefaultMeridiem;
\r
4365 scope.hourpicker.editMode = -1;
\r
4366 scope.setValidity('invalidHourpickerData', true);
\r
4367 scope.setValidity('invalidHourpickerTimeRange', true);
\r
4369 scope.resetHourpickerPanelValue();
\r
4370 scope.updateHourpickerValue = function () {
\r
4371 if (scope.isFormValid() && !scope.isTimeOverlap()) {
\r
4372 scope.addHourpickerValue(angular.copy(scope.hourpickerPanelValue));
\r
4373 scope.resetHourpickerPanelValue();
\r
4377 scope.isFormValid = function () {
\r
4378 var isStartTimeAvailable = scope.hourpickerPanelValue.startTime ? true : false;
\r
4379 var isStartMeridiemAvailable = scope.hourpickerPanelValue.startMeridiem ? true : false;
\r
4380 var isEndTimeAvailable = scope.hourpickerPanelValue.endTime ? true : false;
\r
4381 var isEndMeridiemAvailable = scope.hourpickerPanelValue.endMeridiem ? true : false;
\r
4382 var currentStartTime = getTime(scope.hourpickerPanelValue.startTime, scope.hourpickerPanelValue.startMeridiem);
\r
4383 var currentEndTime = getTime(scope.hourpickerPanelValue.endTime, scope.hourpickerPanelValue.endMeridiem);
\r
4384 var isTimeInProperSequence = currentEndTime > currentStartTime;
\r
4385 var isDayChecked = false;
\r
4386 for (var i in scope.hourpickerPanelValue.days) {
\r
4387 if (scope.hourpickerPanelValue.days[i].value) {
\r
4388 isDayChecked = true;
\r
4393 if (isStartTimeAvailable && isStartMeridiemAvailable && isEndTimeAvailable && isEndMeridiemAvailable && isTimeInProperSequence && isDayChecked) {
\r
4394 scope.setValidity('invalidHourpickerData', true);
\r
4397 scope.setValidity('invalidHourpickerData', false);
\r
4401 scope.isTimeOverlap = function () {
\r
4402 var selectedDays = [];
\r
4403 for (var i in scope.hourpickerPanelValue.days) {
\r
4404 if (scope.hourpickerPanelValue.days[i].value) {
\r
4405 selectedDays.push(i);
\r
4409 var currentStartTime, currentEndTime, existingStartTime, existingEndTime;
\r
4410 currentStartTime = getTime(scope.hourpickerPanelValue.startTime, scope.hourpickerPanelValue.startMeridiem);
\r
4411 currentEndTime = getTime(scope.hourpickerPanelValue.endTime, scope.hourpickerPanelValue.endMeridiem);
\r
4412 for (var i = 0; i < scope.hourpickerValues.length; i++) {
\r
4414 if (i === scope.hourpicker.editMode) {
\r
4418 for (var j = 0; j < selectedDays.length; j++) {
\r
4419 existingStartTime = getTime(scope.hourpickerValues[i].startTime, scope.hourpickerValues[i].startMeridiem);
\r
4420 existingEndTime = getTime(scope.hourpickerValues[i].endTime, scope.hourpickerValues[i].endMeridiem);
\r
4421 if (scope.hourpickerValues[i].days[selectedDays[j]].value) {
\r
4422 if(!scope.hourpicker.sameDayOption){
\r
4423 scope.setValidity('dayAlreadySelected', false);
\r
4425 } else if ((currentStartTime > existingStartTime && currentStartTime < existingEndTime) || (currentEndTime > existingStartTime && currentEndTime < existingEndTime)) {
\r
4426 scope.setValidity('invalidHourpickerTimeRange', false);
\r
4428 } else if ((existingStartTime > currentStartTime && existingStartTime < currentEndTime) || (existingEndTime > currentStartTime && existingEndTime < currentEndTime)) {
\r
4429 scope.setValidity('invalidHourpickerTimeRange', false);
\r
4431 } else if ((currentStartTime === existingStartTime) && (currentEndTime === existingEndTime)) {
\r
4432 scope.setValidity('invalidHourpickerTimeRange', false);
\r
4439 scope.setValidity('dayAlreadySelected', true);
\r
4440 scope.setValidity('invalidHourpickerTimeRange', true);
\r
4443 var getTime = function (timeString, meridiem) {
\r
4444 var tempDate = new Date();
\r
4445 if (timeString && meridiem) {
\r
4446 var timeSplit = timeString.split(':');
\r
4447 var hour = ((meridiem === 'PM' || meridiem === 'pm') && timeSplit[0] !== '12') ? parseInt(timeSplit[0], 10) + 12 : parseInt(timeSplit[0], 10);
\r
4448 tempDate.setHours(hour, parseInt(timeSplit[1], 10), 0, 0);
\r
4451 return tempDate.getTime();
\r
4457 .directive('b2bHourpickerValue', [function () {
\r
4461 templateUrl: 'b2bTemplate/hourPicker/b2bHourpickerValue.html',
\r
4462 controller: ['$scope', function (scope) {
\r
4465 link: function (scope, elem, attr, ctrl) {
\r
4466 scope.hourpickerValue = {};
\r
4467 scope.hourpickerValue.startTime = attr.startTime ? scope.$eval(attr.startTime) : '';
\r
4468 scope.hourpickerValue.startMeridiem = attr.startMeridiem ? scope.$eval(attr.startMeridiem) : '';
\r
4469 scope.hourpickerValue.endTime = attr.endTime ? scope.$eval(attr.endTime) : '';
\r
4470 scope.hourpickerValue.endMeridiem = attr.endMeridiem ? scope.$eval(attr.endMeridiem) : '';
\r
4471 scope.hourpickerValue.days = attr.days ? scope.$eval(attr.days).join(', ') : '';
\r
4472 scope.hourpickerValue.index = attr.b2bHourpickerValue ? scope.$eval(attr.b2bHourpickerValue) : -1;
\r
4477 * @ngdoc directive
\r
4478 * @name Template.att:inputTemplate
\r
4481 * <file src="src/inputTemplate/docs/readme.md" />
\r
4484 * <input type="text" id="fieldId" placeholder="placholder text here" class="span12 input-enhanced" name="fieldName">
\r
4487 <section id="code">
\r
4488 <b>HTML + AngularJS</b>
\r
4489 <example module="b2b.att">
\r
4490 <file src="src/inputTemplate/docs/demo.html" />
\r
4491 <file src="src/inputTemplate/docs/demo.js" />
\r
4495 angular.module('b2b.att.inputTemplate', []);
\r
4498 * @ngdoc directive
\r
4499 * @name Navigation.att:leftNavigation
\r
4502 * <file src="src/leftNavigation/docs/readme.md" />
\r
4505 * <b2b-left-navigation data-menu="menuData"></b2b-left-navigation>
\r
4508 * <section id="code">
\r
4509 <example module="b2b.att">
\r
4510 <file src="src/leftNavigation/docs/demo.html" />
\r
4511 <file src="src/leftNavigation/docs/demo.js" />
\r
4516 angular.module('b2b.att.leftNavigation', [])
\r
4517 .directive('b2bLeftNavigation', [function () {
\r
4520 templateUrl: 'b2bTemplate/leftNavigation/leftNavigation.html',
\r
4524 link: function (scope, element, attrs, ctrl) {
\r
4526 scope.itemIdx = -1;
\r
4527 scope.navIdx = -1;
\r
4528 scope.toggleNav = function (val,link) {
\r
4529 if (val === scope.idx) {
\r
4535 /*New function for ECOMP sdk*/
\r
4536 scope.toggleDrawer = function(showmenu){
\r
4537 scope.idx=-1; /*hide the sunmenus*/
\r
4539 document.getElementById('page-content').style.paddingLeft = "50px";
\r
4542 document.getElementById('page-content').style.paddingLeft = "230px";
\r
4544 scope.liveLink = function (evt, val1, val2) {
\r
4545 scope.itemIdx = val1;
\r
4546 scope.navIdx = val2;
\r
4547 evt.stopPropagation();
\r
4553 * @ngdoc directive
\r
4554 * @name Buttons, links & UI controls.att:links
\r
4557 * <file src="src/links/docs/readme.md" />
\r
4559 * <!-- See below examples for link implementation -->
\r
4562 <section id="code">
\r
4563 <b>HTML + AngularJS</b>
\r
4564 <example module="b2b.att">
\r
4565 <file src="src/links/docs/demo.html" />
\r
4566 <file src="src/links/docs/demo.js" />
\r
4570 angular.module('b2b.att.links', []);
\r
4572 * @ngdoc directive
\r
4573 * @name Misc.att:listbox
\r
4576 * <file src="src/listbox/docs/readme.md" />
\r
4578 * @param {int} currentIndex - Current index of selected listbox item. Is not supported on multiselect listbox
\r
4579 * @param {Array} listboxData - Data of listbox items. Should include full data regardless if HTML will be filtered.
\r
4582 * <section id="code">
\r
4583 <example module="b2b.att">
\r
4584 <file src="src/listbox/docs/demo.html" />
\r
4585 <file src="src/listbox/docs/demo.js" />
\r
4590 angular.module('b2b.att.listbox', ['b2b.att.utilities'])
\r
4591 .directive('b2bListBox', ['keymap', 'b2bDOMHelper', '$rootScope', function(keymap, b2bDOMHelper, $rootScope) {
\r
4597 currentIndex: '=',
\r
4600 templateUrl: 'b2bTemplate/listbox/listbox.html',
\r
4601 link: function(scope, elem, attr) {
\r
4603 if (attr.ariaMultiselectable !== undefined || attr.ariaMultiselectable === 'true') {
\r
4604 scope.multiselectable = true;
\r
4606 scope.multiselectable = false;
\r
4609 var shiftKey = false;
\r
4610 var elements = [];
\r
4611 var prevDirection = undefined; // previous direction is used for an edge case when shifting
\r
4612 var shiftKeyPressed = false; // Used to handle shift clicking
\r
4613 var ctrlKeyPressed = false;
\r
4615 var currentIndexSet = {
\r
4616 'elementIndex': 0,
\r
4617 'listboxDataIndex': 0
\r
4620 /*scope.$watch('currentIndex', function(oldVal, newVal) {
\r
4621 if (angular.equals(oldVal, newVal)) return;
\r
4622 if (!scope.multiselectable) {
\r
4623 // This doesn't garuntee anything. index will update on focus based on rules
\r
4624 currentIndexSet.listboxDataIndex = scope.currentIndex;
\r
4625 // Should this occur?
\r
4626 //scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
\r
4628 // Update elementIndex
\r
4629 elements = elem.children();
\r
4630 var indecies = Array.prototype.map.call(elements, function(item) {
\r
4631 return parseInt(angular.element(item).attr('data-index'), 10);
\r
4632 }).filter(function(item) {
\r
4633 return item === scope.currentIndex;
\r
4635 currentIndex.elementIndex = indecies[0];
\r
4636 //focusOnElement(currentIndexSet.elementIndex); // This isn't shifting focus
\r
4637 if (!scope.$$phase) {
\r
4643 function isTrue(item) {
\r
4644 if (item.selected === true) {
\r
4649 function incrementIndex(elem) {
\r
4650 $rootScope.$apply();
\r
4652 var nextElem = elem.next();
\r
4653 if (!angular.isDefined(nextElem) || nextElem.length === 0) {
\r
4657 currentIndexSet.elementIndex += 1;
\r
4658 currentIndexSet.listboxDataIndex = parseInt(nextElem.attr('data-index'), 10);
\r
4659 scope.currentIndex = currentIndexSet.listboxDataIndex;
\r
4661 if (currentIndexSet.elementIndex >= elements.length - 1) {
\r
4662 currentIndexSet.elementIndex = elements.length-1;
\r
4666 function decrementIndex(elem) {
\r
4667 $rootScope.$apply();
\r
4668 var prevElem = angular.element(b2bDOMHelper.previousElement(elem));
\r
4669 if (!angular.isDefined(prevElem) || prevElem.length === 0) {
\r
4673 currentIndexSet.elementIndex -= 1;
\r
4674 currentIndexSet.listboxDataIndex = parseInt(prevElem.attr('data-index'), 10);
\r
4675 scope.currentIndex = currentIndexSet.listboxDataIndex;
\r
4677 if (currentIndexSet.elementIndex <= 0) {
\r
4678 currentIndexSet.elementIndex = 0;
\r
4682 var focusOnElement = function(index) {
\r
4684 elements[index].focus();
\r
4688 function selectItems(startIndex, endIndex, forceValue) {
\r
4689 for (var i = startIndex; i < endIndex; i++) {
\r
4690 if (forceValue === undefined) {
\r
4691 // We will flip the value
\r
4692 scope.listboxData[i].selected = !scope.listboxData[i].selected;
\r
4694 scope.listboxData[i].selected = forceValue;
\r
4698 if (!scope.$$phase) {
\r
4703 elem.bind('focus', function(evt) {
\r
4704 // If multiselectable or not and nothing is selected, put focus on first element
\r
4705 // If multiselectable and a range is set, put focus on first element of range
\r
4706 // If not multiselectable and something selected, put focus on element
\r
4707 elements = elem.children();
\r
4708 var selectedItems = scope.listboxData.filter(isTrue);
\r
4709 var elementsIndies = Array.prototype.map.call(elements, function(item) {
\r
4710 return parseInt(angular.element(item).attr('data-index'), 10);
\r
4713 if (selectedItems.length == 0) {
\r
4714 focusOnElement(0);
\r
4715 currentIndexSet.listboxDataIndex = 0;
\r
4716 } else if (attr.ariaMultiselectable) {
\r
4717 var index = scope.listboxData.indexOf(selectedItems[0]);
\r
4718 var indies = elementsIndies.filter(function(item) {
\r
4719 return (item === index);
\r
4722 if (indies.length === 0 || indies[0] != index) {
\r
4723 // Set focused on 0
\r
4724 currentIndexSet.elementIndex = elementsIndies[0];
\r
4725 currentIndexSet.listboxDataIndex = 0;
\r
4726 focusOnElement(currentIndexSet.elementIndex);
\r
4728 focusOnElement(indies[0]);
\r
4729 currentIndexSet.elementIndex = indies[0];
\r
4730 currentIndexSet.listboxDataIndex = index;
\r
4733 focusOnElement(currentIndexSet.elementIndex);
\r
4735 scope.currentIndex = currentIndexSet.listboxDataIndex;
\r
4737 if (!scope.$$phase) {
\r
4741 elem.bind('keyup', function(evt) {
\r
4742 if (evt.keyCode === keymap.KEY.SHIFT) {
\r
4743 shiftKeyPressed = false;
\r
4744 } else if (evt.keyCode === keymap.KEY.CTRL) {
\r
4745 ctrlKeyPressed = false;
\r
4749 elem.bind('keydown', function(evt) {
\r
4750 var keyCode = evt.keyCode;
\r
4751 elements = elem.children();
\r
4752 if (keyCode === keymap.KEY.SHIFT) {
\r
4753 shiftKeyPressed = true;
\r
4754 } else if (evt.keyCode === keymap.KEY.CTRL) {
\r
4755 ctrlKeyPressed = true;
\r
4761 if (scope.multiselectable && evt.ctrlKey) {
\r
4762 var arr = scope.listboxData.filter(isTrue);
\r
4763 var elementsIndies = Array.prototype.map.call(elements, function(item) {
\r
4764 return parseInt(angular.element(item).attr('data-index'), 10);
\r
4766 var val = !(arr.length === scope.listboxData.length);
\r
4767 for (var i = 0; i < elementsIndies.length; i++) {
\r
4768 scope.listboxData[elementsIndies[i]].selected = val;
\r
4771 if (!scope.$$phase) {
\r
4775 evt.preventDefault();
\r
4776 evt.stopPropagation();
\r
4780 case keymap.KEY.END:
\r
4782 if (scope.multiselectable && evt.ctrlKey && evt.shiftKey) {
\r
4783 var elementsIndies = Array.prototype.map.call(elements, function(item) {
\r
4784 return parseInt(angular.element(item).attr('data-index'), 10);
\r
4785 }).filter(function(item) {
\r
4786 return (item >= currentIndexSet.listboxDataIndex);
\r
4788 for (var i = 0; i < elementsIndies.length; i++) {
\r
4789 scope.listboxData[elementsIndies[i]].selected = true;
\r
4791 evt.preventDefault();
\r
4792 evt.stopPropagation();
\r
4794 if (!scope.$$phase) {
\r
4800 case keymap.KEY.HOME:
\r
4802 if (scope.multiselectable && evt.ctrlKey && evt.shiftKey) {
\r
4803 selectItems(0, currentIndexSet.listboxDataIndex+1, true); // currentIndex+1 is what is being focused on
\r
4804 evt.preventDefault();
\r
4805 evt.stopPropagation();
\r
4809 case keymap.KEY.LEFT:
\r
4810 case keymap.KEY.UP:
\r
4812 if (currentIndexSet.listboxDataIndex === 0) {
\r
4813 evt.preventDefault();
\r
4814 evt.stopPropagation();
\r
4818 decrementIndex(elements.eq(currentIndexSet.elementIndex));
\r
4819 if (scope.multiselectable && (evt.shiftKey || evt.ctrlKey)) {
\r
4820 if (evt.shiftKey) {
\r
4821 if (prevDirection === 'DOWN') {
\r
4822 scope.listboxData[currentIndexSet.listboxDataIndex+1].selected = !scope.listboxData[currentIndexSet.listboxDataIndex+1].selected;
\r
4824 scope.listboxData[currentIndexSet.listboxDataIndex].selected = !scope.listboxData[currentIndexSet.listboxDataIndex].selected;
\r
4826 prevDirection = 'UP';
\r
4828 // If no modifier keys are selected, all other items need to be unselected.
\r
4829 prevDirection = undefined;
\r
4830 selectItems(0, scope.listboxData.length, false);
\r
4831 scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
\r
4833 focusOnElement(currentIndexSet.elementIndex);
\r
4834 if(!scope.$$phase) {
\r
4837 evt.preventDefault();
\r
4838 evt.stopPropagation();
\r
4841 case keymap.KEY.RIGHT:
\r
4842 case keymap.KEY.DOWN:
\r
4844 if (currentIndexSet.listboxDataIndex === scope.listboxData.length-1) {
\r
4845 evt.preventDefault();
\r
4846 evt.stopPropagation();
\r
4850 incrementIndex(elements.eq(currentIndexSet.elementIndex));
\r
4852 if (scope.multiselectable && (evt.shiftKey || evt.ctrlKey)) {
\r
4853 if (evt.shiftKey) {
\r
4854 if (prevDirection === 'UP') {
\r
4855 scope.listboxData[currentIndexSet.listboxDataIndex-1].selected = !scope.listboxData[currentIndexSet.listboxDataIndex-1].selected;
\r
4858 scope.listboxData[currentIndexSet.listboxDataIndex].selected = !scope.listboxData[currentIndexSet.listboxDataIndex].selected;
\r
4860 prevDirection = 'DOWN';
\r
4862 // If no modifier keys are selected, all other items need to be unselected.
\r
4863 prevDirection = undefined;
\r
4864 selectItems(0, scope.listboxData.length, false);
\r
4865 scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
\r
4868 focusOnElement(currentIndexSet.elementIndex);
\r
4869 if(!scope.$$phase) {
\r
4872 evt.preventDefault();
\r
4873 evt.stopPropagation();
\r
4876 case keymap.KEY.TAB:
\r
4877 if(evt.shiftKey) {
\r
4878 var previousElement = b2bDOMHelper.previousElement(elem.parent().parent(), true);
\r
4879 evt.preventDefault();
\r
4880 previousElement.focus();
\r
4888 elem.bind('click', function(evt) {
\r
4889 var index = parseInt(evt.target.dataset.index, 10);
\r
4890 if (index === undefined || isNaN(index)) {
\r
4893 if (scope.multiselectable && currentIndexSet.listboxDataIndex !== undefined) {
\r
4894 if (shiftKeyPressed) {
\r
4895 var min = Math.min(index, currentIndexSet.listboxDataIndex);
\r
4896 var max = Math.max(index, currentIndexSet.listboxDataIndex);
\r
4898 if (index === min) { // clicking up
\r
4899 var firstIndex = scope.listboxData.findIndex(function(item) { return item.selected === true;});
\r
4900 // Given the firstIndex, let's find the matching element to get proper element match
\r
4901 elements = elem.children();
\r
4902 elements.eq(firstIndex)
\r
4903 var elementsThatMatch = Array.prototype.filter.call(elements, function(item) {
\r
4904 if (parseInt(angular.element(item).attr('data-index'), 10) === firstIndex) {
\r
4908 firstIndex = parseInt(angular.element(elementsThatMatch).attr('data-index'), 10);
\r
4910 if (index <= firstIndex && scope.listboxData.filter(isTrue).length > 1) {
\r
4911 // Break the selection into 2
\r
4912 selectItems(firstIndex + 1, max + 1, undefined); // + 1 needed because selectItems only selects up to MAX
\r
4913 selectItems(min, firstIndex, undefined);
\r
4914 } else if (scope.listboxData.filter(isTrue).length == 1){
\r
4915 selectItems(min, max, undefined);
\r
4917 selectItems(min + 1, max + 1, undefined);
\r
4919 } else { // clicking down
\r
4920 selectItems(min + 1, max + 1, scope.listboxData[min].selected);
\r
4922 } else if (ctrlKeyPressed) {
\r
4923 scope.listboxData[index].selected = !scope.listboxData[index].selected;
\r
4925 selectItems(0, scope.listboxData.length, false);
\r
4926 scope.listboxData[index].selected = !scope.listboxData[index].selected;
\r
4929 selectItems(0, scope.listboxData.length, false);
\r
4930 scope.listboxData[index].selected = !scope.listboxData[index].selected;
\r
4932 currentIndexSet.elementIndex = index;
\r
4933 currentIndexSet.listboxDataIndex = index;
\r
4934 scope.currentIndex = currentIndexSet.listboxDataIndex;
\r
4935 if (!scope.$$phase) {
\r
4938 focusOnElement(index);
\r
4944 * @ngdoc directive
\r
4945 * @name Videos, audio & animation.att:loaderAnimation
\r
4948 * <file src="src/loaderAnimation/docs/readme.md" />
\r
4951 * <!-- Below demo js shows-->
\r
4952 * Angular library uses Global.css's icon-primary-spinner.
\r
4955 * <section id="code">
\r
4956 <example module="b2b.att">
\r
4957 <file src="src/loaderAnimation/docs/demo.html" />
\r
4958 <file src="src/loaderAnimation/docs/demo.js" />
\r
4963 angular.module('b2b.att.loaderAnimation', [])
\r
4964 .constant('b2bSpinnerConfig', {
\r
4965 loadingText: 'Loading...',
\r
4966 startEvent: 'startButtonSpinner',
\r
4967 stopEvent: 'stopButtonSpinner'
\r
4969 .constant("progressTrackerConfig", {
\r
4970 loadingText: 'Loading...',
\r
4972 activationDelay: "",
\r
4973 minDurationPromise: "",
\r
4974 activationDelayPromise: ""
\r
4977 .provider('progressTracker', function () {
\r
4978 this.$get = ['$q', '$timeout', function ($q, $timeout) {
\r
4979 function cancelTimeout(promise) {
\r
4981 $timeout.cancel(promise);
\r
4984 return function ProgressTracker(options) {
\r
4985 //do new if user doesn't
\r
4986 if (!(this instanceof ProgressTracker)) {
\r
4987 return new ProgressTracker(options);
\r
4990 options = options || {};
\r
4991 //Array of promises being tracked
\r
4994 //Allow an optional "minimum duration" that the tracker has to stay active for.
\r
4995 var minDuration = options.minDuration;
\r
4996 //Allow a delay that will stop the tracker from activating until that time is reached
\r
4997 var activationDelay = options.activationDelay;
\r
4998 var minDurationPromise;
\r
4999 var activationDelayPromise;
\r
5000 self.active = function () {
\r
5001 //Even if we have a promise in our tracker, we aren't active until delay is elapsed
\r
5002 if (activationDelayPromise) {
\r
5005 return tracked.length > 0;
\r
5007 self.tracking = function () {
\r
5008 //Even if we aren't active, we could still have a promise in our tracker
\r
5009 return tracked.length > 0;
\r
5011 self.destroy = self.cancel = function () {
\r
5012 minDurationPromise = cancelTimeout(minDurationPromise);
\r
5013 activationDelayPromise = cancelTimeout(activationDelayPromise);
\r
5014 for (var i = tracked.length - 1; i >= 0; i--) {
\r
5015 tracked[i].resolve();
\r
5017 tracked.length = 0;
\r
5019 //Create a promise that will make our tracker active until it is resolved.
\r
5020 // @return deferred - our deferred object that is being tracked
\r
5021 self.createPromise = function () {
\r
5022 var deferred = $q.defer();
\r
5023 tracked.push(deferred);
\r
5024 //If the tracker was just inactive and this the first in the list of promises, we reset our delay and minDuration again.
\r
5025 if (tracked.length === 1) {
\r
5026 if (activationDelay) {
\r
5027 activationDelayPromise = $timeout(function () {
\r
5028 activationDelayPromise = cancelTimeout(activationDelayPromise);
\r
5029 startMinDuration();
\r
5030 }, activationDelay);
\r
5032 startMinDuration();
\r
5035 deferred.promise.then(onDone(false), onDone(true));
\r
5038 function startMinDuration() {
\r
5039 if (minDuration) {
\r
5040 minDurationPromise = $timeout(angular.noop, minDuration);
\r
5043 //Create a callback for when this promise is done. It will remove our tracked promise from the array if once minDuration is complete
\r
5044 function onDone() {
\r
5045 return function () {
\r
5046 (minDurationPromise || $q.when()).then(function () {
\r
5047 var index = tracked.indexOf(deferred);
\r
5048 tracked.splice(index, 1);
\r
5049 //If this is the last promise, cleanup the timeouts for activationDelay
\r
5050 if (tracked.length === 0) {
\r
5051 activationDelayPromise = cancelTimeout(activationDelayPromise);
\r
5057 self.addPromise = function (promise) {
\r
5059 // we cannot assign then function in other var and then add the resolve and reject
\r
5060 var thenFxn = promise && (promise.then || promise.$then || (promise.$promise && promise.$promise.then));
\r
5062 throw new Error("progressTracker expects a promise object :: Not found");
\r
5064 var deferred = self.createPromise();
\r
5065 //When given promise is done, resolve our created promise
\r
5066 //Allow $then for angular-resource objects
\r
5068 promise.then(function (value) {
\r
5069 deferred.resolve(value);
\r
5071 }, function (value) {
\r
5072 deferred.reject(value);
\r
5073 return $q.reject(value);
\r
5082 .config(['$httpProvider', function ($httpProvider) {
\r
5083 $httpProvider.interceptors.push(['$q', 'progressTracker', function ($q) {
\r
5085 request: function (config) {
\r
5086 if (config.tracker) {
\r
5087 if (!angular.isArray(config.tracker)) {
\r
5088 config.tracker = [config.tracker];
\r
5090 config.$promiseTrackerDeferred = config.$promiseTrackerDeferred || [];
\r
5092 angular.forEach(config.tracker, function (tracker) {
\r
5093 var deferred = tracker.createPromise();
\r
5094 config.$promiseTrackerDeferred.push(deferred);
\r
5097 return $q.when(config);
\r
5099 response: function (response) {
\r
5100 if (response.config && response.config.$promiseTrackerDeferred) {
\r
5101 angular.forEach(response.config.$promiseTrackerDeferred, function (deferred) {
\r
5102 deferred.resolve(response);
\r
5105 return $q.when(response);
\r
5107 responseError: function (response) {
\r
5108 if (response.config && response.config.$promiseTrackerDeferred) {
\r
5109 angular.forEach(response.config.$promiseTrackerDeferred, function (deferred) {
\r
5110 deferred.reject(response);
\r
5113 return $q.reject(response);
\r
5119 .directive('b2bClickSpin', ['$timeout', '$parse', '$rootScope', 'progressTracker', function ($timeout, $parse, $rootScope, progressTracker) {
\r
5122 link: function (scope, elm, attrs) {
\r
5123 var fn = $parse(attrs.b2bClickSpin);
\r
5124 elm.on('click', function (event) {
\r
5125 var promise = $timeout(function () {console.log("inside Promise")}, 5000);
\r
5126 scope.$apply(function () {
\r
5131 //comment this line if not running unit test
\r
5132 $rootScope.loadingTracker = progressTracker({
\r
5135 $rootScope.loadingTracker.addPromise(promise);
\r
5136 angular.forEach("$routeChangeSuccess $viewContentLoaded $locationChangeSuccess".split(" "), function (event) {
\r
5137 $rootScope.$on(event, function () {
\r
5139 $timeout.cancel(promise);
\r
5147 .directive('b2bProgressTracker', ['progressTrackerConfig', function (ptc) {
\r
5151 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
5155 .directive('b2bLoadButton', ['b2bSpinnerConfig', '$timeout', function (spinnerConfig, $timeout) {
\r
5156 var spinButton = function (state, element, data) {
\r
5158 var attr = element.html() ? 'html' : 'val';
\r
5159 state = state + 'Text';
\r
5160 if (state === 'loadingText') {
\r
5161 element[attr](data[state]);
\r
5162 element.attr("disabled",'disabled');
\r
5163 element.addClass('disabled');
\r
5164 } else if (state === 'resetText') {
\r
5165 element[attr](data[state]);
\r
5166 element.removeAttr("disabled");
\r
5167 element.removeClass('disabled');
\r
5175 promise: '=promise',
\r
5176 startEvent: '@startEvent',
\r
5177 stopEvent: '@stopEvent'
\r
5179 link: function (scope, element, attr) {
\r
5180 var validAttr = element.html() ? 'html' : 'val';
\r
5186 var updateLoadingText = function (val) {
\r
5187 var loadingText = val;
\r
5188 if (!angular.isDefined(loadingText) || loadingText === "") {
\r
5189 loadingText = spinnerConfig.loadingText;
\r
5191 data.loadingText = validAttr === 'html' ? "<i class=\"icon-primary-spinner small\"></i>" + loadingText : loadingText;
\r
5193 var updateResetText = function (val) {
\r
5194 data.resetText = val;
\r
5197 attr.$observe('b2bLoadButton', function (val) {
\r
5198 updateLoadingText(val);
\r
5200 $timeout(function () {
\r
5201 updateResetText(element[validAttr]());
\r
5204 if (!angular.isDefined(scope.startEvent) || scope.startEvent === "") {
\r
5205 scope.startEvent = spinnerConfig.startEvent;
\r
5208 if (!angular.isDefined(scope.stopEvent) || scope.stopEvent === "") {
\r
5209 scope.stopEvent = spinnerConfig.stopEvent;
\r
5212 scope.$watch('promise', function () {
\r
5213 if (angular.isDefined(scope.promise) && angular.isFunction(scope.promise.then)) {
\r
5214 spinButton('loading', element, data);
\r
5215 scope.promise.then(function () {
\r
5216 spinButton('reset', element, data);
\r
5218 spinButton('reset', element, data);
\r
5223 scope.$on(scope.startEvent, function () {
\r
5224 spinButton('loading', element, data);
\r
5225 scope.$on(scope.stopEvent, function () {
\r
5226 spinButton('reset', element, data);
\r
5236 * @ngdoc directive
\r
5237 * @name Misc.att:messageWrapper
\r
5239 * @param {boolean} trigger - A boolean that triggers directive to switch focus
\r
5240 * @param {integer} delay - Extra delay added to trigger code to allow for DOM to be ready. Default is 10ms.
\r
5241 * @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
5242 * @param {string} trapFocus - Attribute-based API to trap focus within the message. This should be enabled by default on all toast messages.
\r
5244 * <file src="src/messageWrapper/docs/readme.md" />
\r
5246 * <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
5249 * <section id="code">
\r
5250 <b>HTML + AngularJS</b>
\r
5251 <example module="b2b.att">
\r
5252 <file src="src/messageWrapper/docs/demo.html" />
\r
5253 <file src="src/messageWrapper/docs/demo.js" />
\r
5258 angular.module('b2b.att.messageWrapper', ['b2b.att.utilities'])
\r
5259 .directive('b2bMessageWrapper', ['b2bDOMHelper', '$compile', '$timeout', '$log', function(b2bDOMHelper, $compile, $timeout, $log) {
\r
5268 template: '<div ng-transclude></div>',
\r
5269 link: function(scope, elem, attrs) {
\r
5270 scope.delay = scope.delay || 10;
\r
5272 if (attrs.trapFocus != undefined && !elem.children().eq(0).attr('b2b-trap-focus-inside-element')) {
\r
5273 // Append b2bTrapFocusInsideElement onto first child and recompile
\r
5274 elem.children().eq(0).attr('b2b-trap-focus-inside-element', 'false');
\r
5275 elem.children().eq(0).attr('trigger', scope.trigger);
\r
5276 $compile(elem.contents())(scope);
\r
5279 var firstElement = undefined,
\r
5280 launchingElement = undefined;
\r
5282 scope.$watch('trigger', function(oldVal, newVal) {
\r
5283 if (oldVal === newVal) return;
\r
5284 if (!angular.isDefined(launchingElement)) {
\r
5285 launchingElement = document.activeElement;
\r
5287 $timeout(function() {
\r
5288 if (scope.trigger) {
\r
5290 if (attrs.noFocus === true || attrs.noFocus === "") {
\r
5291 elem.children()[0].focus();
\r
5293 firstElement = b2bDOMHelper.firstTabableElement(elem);
\r
5295 if (angular.isDefined(firstElement)) {
\r
5296 firstElement.focus();
\r
5301 if (angular.isDefined(launchingElement) && launchingElement.nodeName !== 'BODY') {
\r
5302 if (launchingElement === document.activeElement) {
\r
5306 if (b2bDOMHelper.isInDOM(launchingElement) && b2bDOMHelper.isTabable(launchingElement)) {
\r
5307 // At this point, launchingElement is still a valid element, but focus will fail and
\r
5308 // activeElement will become body, hence we want to apply custom logic and find previousElement
\r
5309 var prevLaunchingElement = launchingElement;
\r
5310 launchingElement.focus();
\r
5312 if (document.activeElement !== launchingElement || document.activeElement.nodeName === 'BODY') {
\r
5313 launchingElement = b2bDOMHelper.previousElement(angular.element(prevLaunchingElement), true);
\r
5314 launchingElement.focus();
\r
5317 launchingElement = b2bDOMHelper.previousElement(launchingElement, true);
\r
5318 launchingElement.focus();
\r
5328 * @ngdoc directive
\r
5329 * @name Messages, modals & alerts.att:modalsAndAlerts
\r
5332 * <file src="src/modalsAndAlerts/docs/readme.md" />
\r
5335 * <button class="btn" b2b-modal="b2bTemplate/modalsAndAlerts/demo_modal.html" modal-ok="ok()" modal-cancel="cancel()">Launch demo modal</button>
\r
5338 * <section id="code">
\r
5339 <example module="b2b.att">
\r
5340 <file src="src/modalsAndAlerts/docs/demo.html" />
\r
5341 <file src="src/modalsAndAlerts/docs/demo.js" />
\r
5346 angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transition', 'b2b.att.utilities'])
\r
5349 * A helper, internal data structure that acts as a map but also allows getting / removing
\r
5350 * elements in the LIFO order
\r
5352 .factory('$$stackedMap', function () {
\r
5354 createNew: function () {
\r
5358 add: function (key, value) {
\r
5364 get: function (key) {
\r
5365 for (var i = 0; i < stack.length; i++) {
\r
5366 if (key === stack[i].key) {
\r
5371 keys: function () {
\r
5373 for (var i = 0; i < stack.length; i++) {
\r
5374 keys.push(stack[i].key);
\r
5378 top: function () {
\r
5379 return stack[stack.length - 1];
\r
5381 remove: function (key) {
\r
5383 for (var i = 0; i < stack.length; i++) {
\r
5384 if (key === stack[i].key) {
\r
5389 return stack.splice(idx, 1)[0];
\r
5391 removeTop: function () {
\r
5392 return stack.splice(stack.length - 1, 1)[0];
\r
5394 length: function () {
\r
5395 return stack.length;
\r
5400 }).factory('trapFocusInElement', ['$document', '$isElement', 'DOMHelper', 'keymap', function ($document, $isElement, DOMHelper, keymap) {
\r
5401 var elementStack = [];
\r
5402 var stackHead = undefined;
\r
5403 var firstTabableElement, lastTabableElement;
\r
5405 var trapKeyboardFocusInFirstElement = function (e) {
\r
5407 e.keyCode = e.which;
\r
5410 if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
\r
5411 lastTabableElement[0].focus();
\r
5412 e.preventDefault(e);
\r
5413 e.stopPropagation(e);
\r
5418 var trapKeyboardFocusInLastElement = function (e) {
\r
5420 e.keyCode = e.which;
\r
5423 if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
\r
5424 firstTabableElement[0].focus();
\r
5425 e.preventDefault(e);
\r
5426 e.stopPropagation(e);
\r
5430 var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
\r
5431 var bodyElements = $document.find('body').children();
\r
5433 firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(DOMHelper.firstTabableElement(stackHead));
\r
5434 lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(DOMHelper.lastTabableElement(stackHead));
\r
5437 for (var i = 0; i < bodyElements.length; i++) {
\r
5438 if (bodyElements[i] !== stackHead[0]) {
\r
5439 bodyElements.eq(i).attr('aria-hidden', true);
\r
5442 firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
\r
5443 lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
\r
5445 for (var j = 0; j < bodyElements.length; j++) {
\r
5446 if (bodyElements[j] !== stackHead[0]) {
\r
5447 bodyElements.eq(j).removeAttr('aria-hidden');
\r
5450 firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
\r
5451 lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
\r
5454 var toggleTrapFocusInElement = function (flag, element) {
\r
5455 if (angular.isDefined(flag) && angular.isDefined(element)) {
\r
5456 if (angular.isUndefined(stackHead)) {
\r
5457 stackHead = element;
\r
5458 trapFocusInElement(flag);
\r
5461 trapFocusInElement(false);
\r
5462 elementStack.push(stackHead);
\r
5463 stackHead = element;
\r
5464 trapFocusInElement(true);
\r
5466 if (stackHead.prop('$$hashKey') === element.prop('$$hashKey')) {
\r
5467 trapFocusInElement(false);
\r
5468 stackHead = elementStack.pop();
\r
5469 if (angular.isDefined(stackHead)) {
\r
5470 trapFocusInElement(true);
\r
5476 if (angular.isDefined(stackHead)) {
\r
5477 trapFocusInElement(false, firstTabableElement, lastTabableElement);
\r
5478 trapFocusInElement(true);
\r
5483 return toggleTrapFocusInElement;
\r
5487 * A helper directive for the $modal service. It creates a backdrop element.
\r
5489 .directive('b2bModalBackdrop', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
\r
5493 templateUrl: 'b2bTemplate/modalsAndAlerts/b2b-backdrop.html',
\r
5494 link: function (scope, element, attrs) {
\r
5495 scope.close = function (evt) {
\r
5496 var modal = $modalStack.getTop();
\r
5497 if (modal && modal.value.backdrop && modal.value.backdrop !== 'static') {
\r
5498 evt.preventDefault();
\r
5499 evt.stopPropagation();
\r
5500 $modalStack.dismiss(modal.key, 'backdrop click');
\r
5507 .directive('b2bModalWindow', ['$timeout', 'windowOrientation', '$window', function ($timeout, windowOrientation, $window) {
\r
5515 templateUrl: 'b2bTemplate/modalsAndAlerts/b2b-window.html',
\r
5516 controller: ['$scope', '$element', '$attrs', function (scope, element, attrs) {
\r
5517 scope.windowClass = attrs.windowClass || '';
\r
5518 scope.sizeClass = attrs.sizeClass || '';
\r
5519 scope.isNotifDialog = false;
\r
5521 this.setTitle = function (title) {
\r
5522 scope.title = title;
\r
5524 this.setContent = function (content) {
\r
5525 scope.content = content;
\r
5526 scope.isNotifDialog = true;
\r
5528 this.isDockedModal = scope.windowClass.indexOf('modal-docked') > -1;
\r
5530 link: function (scope, element, attrs, ctrl) {
\r
5531 if (ctrl.isDockedModal) {
\r
5532 scope.isModalLandscape = false;
\r
5534 var window = angular.element($window);
\r
5535 scope.updateCss = function () {
\r
5536 if (windowOrientation.isPotrait()) { // Potrait Mode
\r
5537 scope.isModalLandscape = false;
\r
5538 } else if (windowOrientation.isLandscape()) { // Landscape Mode
\r
5539 scope.isModalLandscape = true;
\r
5543 $timeout(function () {
\r
5544 scope.updateCss();
\r
5547 window.bind('orientationchange', function () {
\r
5548 scope.updateCss();
\r
5551 window.bind('resize', function () {
\r
5552 scope.updateCss();
\r
5556 angular.element(element[0].querySelectorAll(".awd-select-list")).css({
\r
5557 "max-height": "200px"
\r
5561 var isIE = /msie|trident/i.test(navigator.userAgent);
\r
5563 if(angular.element(element[0].querySelector('.corner-button button.close')).length > 0){
\r
5564 angular.element(element[0].querySelector('.corner-button button.close')).bind('focus', function () {
\r
5565 angular.element(element[0].querySelector('.b2b-modal-header'))[0].scrollLeft = 0;
\r
5566 angular.element(element[0].querySelector('.b2b-modal-header'))[0].scrollTop = 0;
\r
5575 .directive('b2bModalTitle', [function () {
\r
5578 require: '^b2bModalWindow',
\r
5579 link: function (scope, elem, attr, ctrl) {
\r
5580 ctrl.setTitle(attr.id);
\r
5585 .directive('b2bModalContent', [function () {
\r
5588 require: '^b2bModalWindow',
\r
5589 link: function (scope, elem, attr, ctrl) {
\r
5590 ctrl.setContent(attr.id);
\r
5596 .directive('b2bModalBody', ['$timeout', '$position', '$document', '$window', 'windowOrientation', 'b2bAwdBreakpoints', function ($timeout, $position, $document, $window, windowOrientation, b2bAwdBreakpoints) {
\r
5602 require: '^b2bModalWindow',
\r
5603 link: function (scope, element, attrs, ctrl) {
\r
5604 var window = angular.element($window);
\r
5605 var body = $document.find('body').eq(0);
\r
5606 scope.setModalHeight = function () {
\r
5607 var modalHeaderHeight, modalFooterHeight, modalBodyHeight, windowHeight, windowWidth, modalHeight;
\r
5608 modalHeaderHeight = 0;
\r
5609 modalFooterHeight = 0;
\r
5610 windowHeight = $window.innerHeight;
\r
5611 windowWidth = $window.innerWidth;
\r
5613 'height': windowHeight + 'px'
\r
5616 if (ctrl.isDockedModal) {
\r
5617 var modalElements = element.parent().children();
\r
5618 for (var i = 0; i < modalElements.length; i++) {
\r
5619 if (modalElements.eq(i).hasClass('b2b-modal-header')) {
\r
5620 modalHeaderHeight = $position.position(modalElements.eq(i)).height;
\r
5621 } else if (modalElements.eq(i).hasClass('b2b-modal-footer')) {
\r
5622 modalFooterHeight = $position.position(modalElements.eq(i)).height;
\r
5626 modalHeight = $position.position(element.parent()).height;
\r
5628 modalBodyHeight = modalHeight - (modalHeaderHeight + modalFooterHeight) + 'px';
\r
5630 if (windowOrientation.isPotrait()) { // Potrait Mode
\r
5631 element.removeAttr('style').css({
\r
5632 height: modalBodyHeight
\r
5634 } else if (windowOrientation.isLandscape() && windowWidth < b2bAwdBreakpoints.breakpoints.mobile.max) { // Landscape Mode Mobile
\r
5635 element.removeAttr('style');
\r
5636 } else if (windowOrientation.isLandscape() && windowWidth >= b2bAwdBreakpoints.breakpoints.mobile.max) { // Landscape Mode Non-Mobile
\r
5637 element.removeAttr('style').css({
\r
5638 height: modalBodyHeight
\r
5644 $timeout(function () {
\r
5645 scope.setModalHeight();
\r
5648 window.bind('orientationchange', function () {
\r
5649 scope.setModalHeight();
\r
5652 window.bind('resize', function () {
\r
5653 scope.setModalHeight();
\r
5660 .directive('b2bModalFooter', ['windowOrientation', '$window', function (windowOrientation, $window) {
\r
5666 link: function (scope, element, attrs) {
\r
5672 .factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap', '$log', '$timeout', 'trapFocusInElement', function ($document, $compile, $rootScope, $$stackedMap, $log, $timeout, trapFocusInElement) {
\r
5673 var backdropjqLiteEl, backdropDomEl;
\r
5674 var backdropScope = $rootScope.$new(true);
\r
5675 var body = $document.find('body').eq(0);
\r
5676 var html = $document.find('html').eq(0);
\r
5677 var openedWindows = $$stackedMap.createNew();
\r
5678 var $modalStack = {};
\r
5680 function backdropIndex() {
\r
5681 var topBackdropIndex = -1;
\r
5682 var opened = openedWindows.keys();
\r
5683 for (var i = 0; i < opened.length; i++) {
\r
5684 if (openedWindows.get(opened[i]).value.backdrop) {
\r
5685 topBackdropIndex = i;
\r
5688 return topBackdropIndex;
\r
5691 $rootScope.$watch(backdropIndex, function (newBackdropIndex) {
\r
5692 backdropScope.index = newBackdropIndex;
\r
5695 function removeModalWindow(modalInstance) {
\r
5696 //background scroll fix
\r
5697 html.removeAttr('style');
\r
5698 body.removeAttr('style');
\r
5699 body.removeClass('styled-by-modal');
\r
5701 var modalWindow = openedWindows.get(modalInstance).value;
\r
5702 trapFocusInElement(false, modalWindow.modalDomEl);
\r
5704 //clean up the stack
\r
5705 openedWindows.remove(modalInstance);
\r
5707 //remove window DOM element
\r
5708 modalWindow.modalDomEl.remove();
\r
5710 //remove backdrop if no longer needed
\r
5711 if (backdropDomEl && backdropIndex() === -1) {
\r
5712 backdropDomEl.remove();
\r
5713 backdropDomEl = undefined;
\r
5717 modalWindow.modalScope.$destroy();
\r
5720 $document.bind('keydown', function (evt) {
\r
5723 if (evt.which === 27) {
\r
5724 modal = openedWindows.top();
\r
5725 if (modal && modal.value.keyboard) {
\r
5726 $rootScope.$apply(function () {
\r
5727 $modalStack.dismiss(modal.key);
\r
5733 $modalStack.open = function (modalInstance, modal) {
\r
5735 openedWindows.add(modalInstance, {
\r
5736 deferred: modal.deferred,
\r
5737 modalScope: modal.scope,
\r
5738 backdrop: modal.backdrop,
\r
5739 keyboard: modal.keyboard
\r
5742 var angularDomEl = angular.element('<div b2b-modal-window></div>');
\r
5743 angularDomEl.attr('window-class', modal.windowClass);
\r
5744 angularDomEl.attr('size-class', modal.sizeClass);
\r
5745 angularDomEl.attr('index', openedWindows.length() - 1);
\r
5746 angularDomEl.html(modal.content);
\r
5748 var modalDomEl = $compile(angularDomEl)(modal.scope);
\r
5749 openedWindows.top().value.modalDomEl = modalDomEl;
\r
5750 //background page scroll fix
\r
5752 'overflow-y': 'hidden'
\r
5755 'overflow-y': 'hidden',
\r
5757 'height': window.innerHeight + 'px'
\r
5759 body.addClass('styled-by-modal');
\r
5760 body.append(modalDomEl);
\r
5762 if (backdropIndex() >= 0 && !backdropDomEl) {
\r
5763 backdropjqLiteEl = angular.element('<div b2b-modal-backdrop></div>');
\r
5764 backdropDomEl = $compile(backdropjqLiteEl)(backdropScope);
\r
5765 body.append(backdropDomEl);
\r
5768 $timeout(function () {
\r
5770 if (modal.scope.$$childHead.isNotifDialog) {
\r
5771 angular.element(modalDomEl).find('button')[0].focus();
\r
5773 angular.element(modalDomEl)[0].focus();
\r
5775 trapFocusInElement(true, angular.element(modalDomEl).eq(0));
\r
5779 $modalStack.close = function (modalInstance, result) {
\r
5780 var modal = openedWindows.get(modalInstance);
\r
5782 modal.value.deferred.resolve(result);
\r
5783 removeModalWindow(modalInstance);
\r
5787 $modalStack.dismiss = function (modalInstance, reason) {
\r
5788 var modalWindow = openedWindows.get(modalInstance).value;
\r
5789 if (modalWindow) {
\r
5790 modalWindow.deferred.reject(reason);
\r
5791 removeModalWindow(modalInstance);
\r
5795 $modalStack.getTop = function () {
\r
5796 return openedWindows.top();
\r
5799 return $modalStack;
\r
5802 .provider('$modal', function () {
\r
5803 var $modalProvider = {
\r
5805 backdrop: true, //can be also false or 'static'
\r
5808 $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack', function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
\r
5811 function getTemplatePromise(options) {
\r
5812 return options.template ? $q.when(options.template) :
\r
5813 $http.get(options.templateUrl, {
\r
5814 cache: $templateCache
\r
5815 }).then(function (result) {
\r
5816 return result.data;
\r
5820 function getResolvePromises(resolves) {
\r
5821 var promisesArr = [];
\r
5822 angular.forEach(resolves, function (value, key) {
\r
5823 if (angular.isFunction(value) || angular.isArray(value)) {
\r
5824 promisesArr.push($q.when($injector.invoke(value)));
\r
5827 return promisesArr;
\r
5830 $modal.open = function (modalOptions) {
\r
5832 var modalResultDeferred = $q.defer();
\r
5833 var modalOpenedDeferred = $q.defer();
\r
5834 //prepare an instance of a modal to be injected into controllers and returned to a caller
\r
5835 var modalInstance = {
\r
5836 result: modalResultDeferred.promise,
\r
5837 opened: modalOpenedDeferred.promise,
\r
5838 close: function (result) {
\r
5839 $modalStack.close(modalInstance, result);
\r
5841 dismiss: function (reason) {
\r
5842 $modalStack.dismiss(modalInstance, reason);
\r
5846 //merge and clean up options
\r
5847 modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
\r
5848 modalOptions.resolve = modalOptions.resolve || {};
\r
5851 if (!modalOptions.template && !modalOptions.templateUrl) {
\r
5852 throw new Error('One of template or templateUrl options is required.');
\r
5855 var templateAndResolvePromise =
\r
5856 $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
\r
5859 templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
\r
5861 var modalScope = (modalOptions.scope || $rootScope).$new();
\r
5862 modalScope.$close = modalInstance.close;
\r
5863 modalScope.$dismiss = modalInstance.dismiss;
\r
5865 var ctrlInstance, ctrlLocals = {};
\r
5866 var resolveIter = 1;
\r
5869 if (modalOptions.controller) {
\r
5870 ctrlLocals.$scope = modalScope;
\r
5871 ctrlLocals.$modalInstance = modalInstance;
\r
5872 angular.forEach(modalOptions.resolve, function (value, key) {
\r
5873 ctrlLocals[key] = tplAndVars[resolveIter++];
\r
5876 ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
\r
5879 $modalStack.open(modalInstance, {
\r
5880 scope: modalScope,
\r
5881 deferred: modalResultDeferred,
\r
5882 content: tplAndVars[0],
\r
5883 backdrop: modalOptions.backdrop,
\r
5884 keyboard: modalOptions.keyboard,
\r
5885 windowClass: modalOptions.windowClass,
\r
5886 sizeClass: modalOptions.sizeClass
\r
5889 }, function resolveError(reason) {
\r
5890 modalResultDeferred.reject(reason);
\r
5893 templateAndResolvePromise.then(function () {
\r
5894 modalOpenedDeferred.resolve(true);
\r
5896 modalOpenedDeferred.reject(false);
\r
5899 return modalInstance;
\r
5906 return $modalProvider;
\r
5909 .directive("b2bModal", ["$modal", "$log", '$scrollTo', function ($modal, $log, $scrollTo) {
\r
5914 modalController: '@',
\r
5920 link: function (scope, elm, attr) {
\r
5921 elm.bind('click', function (ev) {
\r
5922 var currentPosition = ev.pageY - ev.clientY;
\r
5923 ev.preventDefault();
\r
5924 if (angular.isDefined(elm.attr("href")) && elm.attr("href") !== "") {
\r
5925 scope.b2bModal = elm.attr("href");
\r
5928 templateUrl: scope.b2bModal,
\r
5929 controller: scope.modalController,
\r
5930 windowClass: scope.windowClass,
\r
5931 sizeClass: scope.sizeClass
\r
5932 }).result.then(function (value) {
\r
5937 }, function (value) {
\r
5938 scope.modalCancel({
\r
5948 .directive("utilityFilter", ["$modal", "$log", '$scrollTo', function ($modal, $log, $scrollTo) {
\r
5952 utilityFilter: '@'
\r
5954 require: 'ngModel',
\r
5955 templateUrl: 'b2bTemplate/modal/u-filter.html',
\r
5956 link: function (scope, element, attribute, ctrl) {
\r
5957 //controller to be passed to $modal service
\r
5958 scope.options = angular.copy(scope.$parent.$eval(attribute.ngModel));
\r
5959 scope.$parent.$watch(attribute.ngModel, function (newVal, oldVal) {
\r
5960 if (newVal !== oldVal) {
\r
5961 scope.options = newVal;
\r
5964 var modalCtrl = function ($scope, options) {
\r
5965 $scope.options = angular.copy(options);
\r
5968 if (angular.isDefined(scope.utilityFilter)) {
\r
5969 scope.templateUrl = scope.utilityFilter;
\r
5971 scope.templateUrl = 'b2bTemplate/modal/u-filter-window.html';
\r
5973 element.bind('click', function (ev) {
\r
5974 var currentPosition = ev.pageY - ev.clientY;
\r
5976 templateUrl: scope.templateUrl,
\r
5977 controller: modalCtrl,
\r
5979 options: function () {
\r
5980 return scope.options;
\r
5983 }).result.then(function (value) {
\r
5984 ctrl.$setViewValue(value);
\r
5985 element[0].focus();
\r
5986 $scrollTo(0, currentPosition, 0);
\r
5988 element[0].focus();
\r
5989 $scrollTo(0, currentPosition, 0);
\r
5996 * @ngdoc directive
\r
5997 * @name Forms.att:monthSelector
\r
6000 * <file src="src/monthSelector/docs/readme.md" />
\r
6003 * <div b2b-monthpicker ng-model="dt" min="minDate" max="maxDate" mode="monthpicker"></div>
\r
6006 * <section id="code">
\r
6007 <example module="b2b.att">
\r
6008 <file src="src/monthSelector/docs/demo.html" />
\r
6009 <file src="src/monthSelector/docs/demo.js" />
\r
6014 angular.module('b2b.att.monthSelector', ['b2b.att.position', 'b2b.att.utilities'])
\r
6016 .constant('b2bMonthpickerConfig', {
\r
6017 dateFormat: 'MM/dd/yyyy',
\r
6019 monthFormat: 'MMMM',
\r
6020 yearFormat: 'yyyy',
\r
6021 dayHeaderFormat: 'EEEE',
\r
6022 dayTitleFormat: 'MMMM yyyy',
\r
6023 disableWeekend: false,
\r
6024 disableSunday: false,
\r
6025 disableDates: null,
\r
6026 onSelectClose: null,
\r
6033 legendMessage: null,
\r
6034 calendarDisabled: false,
\r
6036 orientation: 'left',
\r
6039 helperText: 'The date you selected is $date. Double tap to open calendar. Select a date to close the calendar.',
\r
6040 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
6041 MonthpickerEvalAttributes: ['dateFormat', 'dayFormat', 'monthFormat', 'yearFormat', 'dayHeaderFormat', 'dayTitleFormat', 'disableWeekend', 'disableSunday', 'startingDay', 'collapseWait', 'orientation','mode','id'],
\r
6042 MonthpickerWatchAttributes: ['min', 'max', 'due', 'from', 'legendIcon', 'legendMessage', 'ngDisabled'],
\r
6043 MonthpickerFunctionAttributes: ['disableDates', 'onSelectClose']
\r
6046 .factory('b2bMonthpickerService', ['b2bMonthpickerConfig', 'dateFilter', function (b2bMonthpickerConfig, dateFilter) {
\r
6047 var setAttributes = function (attr, elem) {
\r
6048 if (angular.isDefined(attr) && attr !== null && angular.isDefined(elem) && elem !== null) {
\r
6049 var attributes = b2bMonthpickerConfig.MonthpickerEvalAttributes.concat(b2bMonthpickerConfig.MonthpickerWatchAttributes, b2bMonthpickerConfig.MonthpickerFunctionAttributes);
\r
6050 for (var key in attr) {
\r
6051 var val = attr[key];
\r
6052 if (attributes.indexOf(key) !== -1 && angular.isDefined(val)) {
\r
6053 elem.attr(key.toSnakeCase(), key);
\r
6059 var bindScope = function (attr, scope) {
\r
6060 if (angular.isDefined(attr) && attr !== null && angular.isDefined(scope) && scope !== null) {
\r
6061 var evalFunction = function (key, val) {
\r
6062 scope[key] = scope.$parent.$eval(val);
\r
6065 var watchFunction = function (key, val) {
\r
6066 scope.$parent.$watch(val, function (value) {
\r
6067 scope[key] = value;
\r
6069 scope.$watch(key, function (value) {
\r
6070 scope.$parent[val] = value;
\r
6074 var evalAttributes = b2bMonthpickerConfig.MonthpickerEvalAttributes;
\r
6075 var watchAttributes = b2bMonthpickerConfig.MonthpickerWatchAttributes;
\r
6076 for (var key in attr) {
\r
6077 var val = attr[key];
\r
6078 if (evalAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
\r
6079 evalFunction(key, val);
\r
6080 } else if (watchAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
\r
6081 watchFunction(key, val);
\r
6088 setAttributes: setAttributes,
\r
6089 bindScope: bindScope
\r
6093 .controller('b2bMonthpickerController', ['$scope', '$attrs', 'dateFilter', '$element', '$position', 'b2bMonthpickerConfig', function ($scope, $attrs, dateFilter, $element, $position, dtConfig) {
\r
6095 date: getValue($attrs.dateFormat, dtConfig.dateFormat),
\r
6096 day: getValue($attrs.dayFormat, dtConfig.dayFormat),
\r
6097 month: getValue($attrs.monthFormat, dtConfig.monthFormat),
\r
6098 year: getValue($attrs.yearFormat, dtConfig.yearFormat),
\r
6099 dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat),
\r
6100 dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat),
\r
6101 disableWeekend: getValue($attrs.disableWeekend, dtConfig.disableWeekend),
\r
6102 disableSunday: getValue($attrs.disableSunday, dtConfig.disableSunday),
\r
6103 disableDates: getValue($attrs.disableDates, dtConfig.disableDates)
\r
6105 startingDay = getValue($attrs.startingDay, dtConfig.startingDay);
\r
6107 $scope.minDate = dtConfig.minDate ? $scope.resetTime(dtConfig.minDate) : null;
\r
6108 $scope.maxDate = dtConfig.maxDate ? $scope.resetTime(dtConfig.maxDate) : null;
\r
6109 $scope.dueDate = dtConfig.dueDate ? $scope.resetTime(dtConfig.dueDate) : null;
\r
6110 $scope.fromDate = dtConfig.fromDate ? $scope.resetTime(dtConfig.fromDate) : null;
\r
6111 $scope.legendIcon = dtConfig.legendIcon ? dtConfig.legendIcon : null;
\r
6112 $scope.legendMessage = dtConfig.legendMessage ? dtConfig.legendMessage : null;
\r
6113 $scope.ngDisabled = dtConfig.calendarDisabled ? dtConfig.calendarDisabled : null;
\r
6114 $scope.collapseWait = getValue($attrs.collapseWait, dtConfig.collapseWait);
\r
6115 $scope.orientation = getValue($attrs.orientation, dtConfig.orientation);
\r
6116 $scope.onSelectClose = getValue($attrs.onSelectClose, dtConfig.onSelectClose);
\r
6117 $scope.mode = getValue($attrs.mode, dtConfig.mode);
\r
6119 $scope.inline = $attrs.inline === 'true' ? true : dtConfig.inline;
\r
6121 function getValue(value, defaultValue) {
\r
6122 return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
\r
6125 function getDaysInMonth(year, month) {
\r
6126 return new Date(year, month, 0).getDate();
\r
6129 function getDates(startDate, n) {
\r
6130 var dates = new Array(n);
\r
6131 var current = startDate,
\r
6134 dates[i++] = new Date(current);
\r
6135 current.setDate(current.getDate() + 1);
\r
6140 this.updatePosition = function (b2bMonthpickerPopupTemplate) {
\r
6141 $scope.position = $position.offset($element);
\r
6142 if($element.find('input').length > 0 ){
\r
6143 $scope.position.top += $element.find('input').prop('offsetHeight');
\r
6145 $scope.position.top += $element.find('a').prop('offsetHeight');
\r
6148 if ($scope.orientation === 'right') {
\r
6149 $scope.position.left -= (((b2bMonthpickerPopupTemplate && b2bMonthpickerPopupTemplate.prop('offsetWidth')) || 290) - $element.find('input').prop('offsetWidth'));
\r
6153 function isSelected(dt) {
\r
6154 if (dt && angular.isDate($scope.currentDate) && compare(dt, $scope.currentDate) === 0) {
\r
6160 function isFromDate(dt) {
\r
6161 if (dt && angular.isDate($scope.fromDate) && compare(dt, $scope.fromDate) === 0) {
\r
6167 function isDateRange(dt) {
\r
6168 if (dt && $scope.fromDate && angular.isDate($scope.currentDate) && (compare(dt, $scope.fromDate) >= 0) && (compare(dt, $scope.currentDate) <= 0)) {
\r
6170 } else if (dt && $scope.fromDate && compare(dt, $scope.fromDate) === 0) {
\r
6176 function isOld(date, currentMonthDate) {
\r
6177 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
6184 function isNew(date, currentMonthDate) {
\r
6185 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
6192 function isPastDue(dt) {
\r
6193 if ($scope.dueDate) {
\r
6194 return (dt > $scope.dueDate);
\r
6199 function isDueDate(dt) {
\r
6200 if ($scope.dueDate) {
\r
6201 return (dt.getTime() === $scope.dueDate.getTime());
\r
6206 var isDisabled = function (date, currentMonthDate) {
\r
6207 if ($attrs.from && !angular.isDate($scope.fromDate)) {
\r
6210 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
\r
6213 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
\r
6216 if (isOld(date, currentMonthDate) || isNew(date, currentMonthDate)) {
\r
6219 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
\r
6224 var isDisabledMonth = function (date, currentMonthDate) {
\r
6225 if ($attrs.from && !angular.isDate($scope.fromDate)) {
\r
6228 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
\r
6231 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
\r
6234 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
\r
6239 var compare = function (date1, date2) {
\r
6240 return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
\r
6243 function isMinDateAvailable(startDate, endDate) {
\r
6244 if (($scope.minDate && $scope.minDate.getTime() >= startDate.getTime()) && ($scope.minDate.getTime() <= endDate.getTime())) {
\r
6245 $scope.disablePrev = true;
\r
6246 $scope.visibilityPrev = "hidden";
\r
6248 $scope.disablePrev = false;
\r
6249 $scope.visibilityPrev = "visible";
\r
6253 function isMaxDateAvailable(startDate, endDate) {
\r
6254 if (($scope.maxDate && $scope.maxDate.getTime() >= startDate.getTime()) && ($scope.maxDate.getTime() <= endDate.getTime())) {
\r
6255 $scope.disableNext = true;
\r
6256 $scope.visibilityNext = "hidden";
\r
6258 $scope.disableNext = false;
\r
6259 $scope.visibilityNext = "visible";
\r
6263 function isYearInRange(currentYear) {
\r
6265 if ($scope.minDate && currentYear === $scope.minDate.getFullYear()) {
\r
6266 $scope.disablePrev = true;
\r
6267 $scope.visibilityPrev = "hidden";
\r
6269 $scope.disablePrev = false;
\r
6270 $scope.visibilityPrev = "visible";
\r
6273 if ($scope.maxDate && currentYear === $scope.maxDate.getFullYear()) {
\r
6274 $scope.disableNext = true;
\r
6275 $scope.visibilityNext = "hidden";
\r
6277 $scope.disableNext = false;
\r
6278 $scope.visibilityNext = "visible";
\r
6283 this.focusNextPrev = function(b2bMonthpickerPopupTemplate,init){
\r
6285 if (!$scope.disablePrev){
\r
6286 b2bMonthpickerPopupTemplate[0].querySelector('th.prev').focus();
\r
6287 }else if (!$scope.disableNext){
\r
6288 b2bMonthpickerPopupTemplate[0].querySelector('th.next').focus();
\r
6290 b2bMonthpickerPopupTemplate[0].querySelector('th.b2b-monthSelector-label').focus();
\r
6293 if ($scope.disableNext || $scope.disablePrev){
\r
6294 b2bMonthpickerPopupTemplate[0].querySelector('th.b2b-monthSelector-label').focus();
\r
6299 function getLabel(label) {
\r
6302 pre: label.substr(0, 1).toUpperCase(),
\r
6310 function makeDate(date, dayFormat, dayHeaderFormat, isSelected, isFromDate, isDateRange, isOld, isNew, isDisabled, dueDate, pastDue) {
\r
6313 label: dateFilter(date, dayFormat),
\r
6314 header: dateFilter(date, dayHeaderFormat),
\r
6315 selected: !!isSelected,
\r
6316 fromDate: !!isFromDate,
\r
6317 dateRange: !!isDateRange,
\r
6318 oldMonth: !!isOld,
\r
6319 nextMonth: !!isNew,
\r
6320 disabled: !!isDisabled,
\r
6321 dueDate: !!dueDate,
\r
6322 pastDue: !!pastDue,
\r
6323 focusable: !((isDisabled && !(isSelected || isDateRange)) || (isOld || isNew))
\r
6330 getVisibleDates: function (date) {
\r
6331 var year = date.getFullYear(),
\r
6332 month = date.getMonth(),
\r
6333 firstDayOfMonth = new Date(year, month, 1),
\r
6334 lastDayOfMonth = new Date(year, month + 1, 0);
\r
6335 var difference = startingDay - firstDayOfMonth.getDay(),
\r
6336 numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : -difference,
\r
6337 firstDate = new Date(firstDayOfMonth),
\r
6340 if (numDisplayedFromPreviousMonth > 0) {
\r
6341 firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
\r
6342 numDates += numDisplayedFromPreviousMonth; // Previous
\r
6344 numDates += getDaysInMonth(year, month + 1); // Current
\r
6345 numDates += (7 - numDates % 7) % 7; // Next
\r
6347 var days = getDates(firstDate, numDates),
\r
6348 labels = new Array(7);
\r
6349 for (var i = 0; i < numDates; i++) {
\r
6350 var dt = new Date(days[i]);
\r
6351 days[i] = makeDate(dt,
\r
6359 isDisabled(dt, date),
\r
6363 for (var j = 0; j < 7; j++) {
\r
6364 labels[j] = getLabel(dateFilter(days[j].date, format.dayHeader));
\r
6366 isMinDateAvailable(firstDayOfMonth, lastDayOfMonth);
\r
6367 isMaxDateAvailable(firstDayOfMonth, lastDayOfMonth);
\r
6370 title: dateFilter(date, format.dayTitle),
\r
6381 getVisibleDates: function(date) {
\r
6384 year = date.getFullYear();
\r
6385 for (var i = 0; i < 12; i++) {
\r
6386 var dt = new Date(year,i,1);
\r
6387 months[i] = makeDate(dt,
\r
6395 isDisabledMonth(dt, date),
\r
6399 isYearInRange(year);
\r
6400 return {objects: months, title: dateFilter(date, format.year), labels: labels};
\r
6408 .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
6416 templateUrl: function (elem, attr) {
\r
6417 if (attr.inline === 'true') {
\r
6418 return 'b2bTemplate/monthSelector/monthSelector-popup.html';
\r
6419 }else if (attr.link === 'true') {
\r
6420 return 'b2bTemplate/monthSelector/monthSelectorLink.html';
\r
6422 return 'b2bTemplate/monthSelector/monthSelector.html';
\r
6426 require: ['b2bMonthpickerPopup', 'ngModel', '?^b2bMonthpickerGroup'],
\r
6427 controller: 'b2bMonthpickerController',
\r
6428 link: function (scope, element, attrs, ctrls) {
\r
6429 var MonthpickerCtrl = ctrls[0],
\r
6430 ngModel = ctrls[1],
\r
6431 b2bMonthpickerGroupCtrl = ctrls[2];
\r
6432 var b2bMonthpickerPopupTemplate;
\r
6435 $log.error("ng-model is required.");
\r
6436 return; // do nothing if no ng-model
\r
6439 // Configuration parameters
\r
6440 var mode = scope.mode,
\r
6442 scope.isOpen = false;
\r
6444 scope.headers = [];
\r
6445 scope.footers = [];
\r
6446 scope.triggerInterval=undefined;
\r
6449 if (b2bMonthpickerGroupCtrl) {
\r
6450 b2bMonthpickerGroupCtrl.registerMonthpickerScope(scope);
\r
6453 element.bind('keydown', function (ev) {
\r
6454 if (!ev.keyCode) {
\r
6456 ev.keyCode = ev.which;
\r
6457 } else if (ev.charCode) {
\r
6458 ev.keyCode = ev.charCode;
\r
6461 if(ev.keyCode === keymap.KEY.ESC)
\r
6463 scope.isOpen = false;
\r
6464 toggleCalendar(scope.isOpen);
\r
6469 element.find('button').bind('click', function () {
\r
6473 element.find('a').bind('click', function () {
\r
6478 element.find('input').bind('click', function () {
\r
6482 var onClicked = function() {
\r
6483 if (!scope.ngDisabled) {
\r
6484 scope.isOpen = !scope.isOpen;
\r
6485 toggleCalendar(scope.isOpen);
\r
6486 MonthpickerCtrl.updatePosition(b2bMonthpickerPopupTemplate);
\r
6491 var toggleCalendar = function (flag) {
\r
6492 if (!scope.inline) {
\r
6494 b2bMonthpickerPopupTemplate = angular.element($templateCache.get('b2bTemplate/monthSelector/monthSelector-popup.html'));
\r
6495 b2bMonthpickerPopupTemplate.attr('b2b-trap-focus-inside-element', 'false');
\r
6496 b2bMonthpickerPopupTemplate.attr('trigger', 'true');
\r
6497 b2bMonthpickerPopupTemplate = $compile(b2bMonthpickerPopupTemplate)(scope);
\r
6498 $document.find('body').append(b2bMonthpickerPopupTemplate);
\r
6499 b2bMonthpickerPopupTemplate.bind('keydown', escPress);
\r
6500 $timeout(function () {
\r
6501 scope.getFocus = true;
\r
6504 $timeout(function () {
\r
6505 scope.getFocus = false;
\r
6507 MonthpickerCtrl.focusNextPrev(b2bMonthpickerPopupTemplate,true);
\r
6510 scope.triggerInterval = $interval(function () {
\r
6511 //This value is updated to trigger init() function of directive on year change.
\r
6512 scope.trigger=(scope.trigger === 0 ? 1 : 0);
\r
6516 b2bMonthpickerPopupTemplate.unbind('keydown', escPress);
\r
6517 if(scope.triggerInterval)
\r
6519 $interval.cancel(scope.triggerInterval);
\r
6520 scope.triggerInterval=undefined;
\r
6522 b2bMonthpickerPopupTemplate.remove();
\r
6523 if(element.find('button').length > 0){
\r
6524 element.find('button')[0].focus();
\r
6526 element.find('a')[0].focus();
\r
6529 scope.getFocus = false;
\r
6534 var outsideClick = function (e) {
\r
6535 var isElement = $isElement(angular.element(e.target), element, $document);
\r
6536 var isb2bMonthpickerPopupTemplate = $isElement(angular.element(e.target), b2bMonthpickerPopupTemplate, $document);
\r
6537 if (!(isElement || isb2bMonthpickerPopupTemplate)) {
\r
6538 scope.isOpen = false;
\r
6539 toggleCalendar(scope.isOpen);
\r
6544 var escPress = function (ev) {
\r
6545 if (!ev.keyCode) {
\r
6547 ev.keyCode = ev.which;
\r
6548 } else if (ev.charCode) {
\r
6549 ev.keyCode = ev.charCode;
\r
6553 if (ev.keyCode === keymap.KEY.ESC) {
\r
6554 scope.isOpen = false;
\r
6555 toggleCalendar(scope.isOpen);
\r
6556 ev.preventDefault();
\r
6557 ev.stopPropagation();
\r
6558 } else if (ev.keyCode === 33) {
\r
6559 !scope.disablePrev && scope.move(-1);
\r
6560 $timeout(function () {
\r
6561 scope.getFocus = true;
\r
6563 $timeout(function () {
\r
6564 scope.getFocus = false;
\r
6568 ev.preventDefault();
\r
6569 ev.stopPropagation();
\r
6570 } else if (ev.keyCode === 34) {
\r
6571 !scope.disableNext && scope.move(1);
\r
6572 $timeout(function () {
\r
6573 scope.getFocus = true;
\r
6575 $timeout(function () {
\r
6576 scope.getFocus = false;
\r
6580 ev.preventDefault();
\r
6581 ev.stopPropagation();
\r
6587 $documentBind.click('isOpen', outsideClick, scope);
\r
6589 scope.$on('$destroy', function () {
\r
6590 if (scope.isOpen) {
\r
6591 scope.isOpen = false;
\r
6592 toggleCalendar(scope.isOpen);
\r
6596 scope.resetTime = function (date) {
\r
6597 if (typeof date === 'string') {
\r
6598 date = date + 'T12:00:00';
\r
6601 if (!isNaN(new Date(date))) {
\r
6602 dt = new Date(date);
\r
6603 if(scope.mode === 1){
\r
6604 dt = new Date(dt.getFullYear(), dt.getMonth());
\r
6606 dt = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
\r
6611 return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
\r
6615 scope.$parent.$watch($parse(attrs.min), function (value) {
\r
6616 scope.minDate = value ? scope.resetTime(value) : null;
\r
6621 scope.$parent.$watch($parse(attrs.max), function (value) {
\r
6622 scope.maxDate = value ? scope.resetTime(value) : null;
\r
6627 scope.$parent.$watch($parse(attrs.due), function (value) {
\r
6628 scope.dueDate = value ? scope.resetTime(value) : null;
\r
6633 scope.$parent.$watch($parse(attrs.from), function (value) {
\r
6634 scope.fromDate = value ? scope.resetTime(value) : null;
\r
6639 if (attrs.legendIcon) {
\r
6640 scope.$parent.$watch(attrs.legendIcon, function (value) {
\r
6641 scope.legendIcon = value ? value : null;
\r
6645 if (attrs.legendMessage) {
\r
6646 scope.$parent.$watch(attrs.legendMessage, function (value) {
\r
6647 scope.legendMessage = value ? value : null;
\r
6651 if (attrs.ngDisabled) {
\r
6652 scope.$parent.$watch(attrs.ngDisabled, function (value) {
\r
6653 scope.ngDisabled = value ? value : null;
\r
6658 // Split array into smaller arrays
\r
6659 function split(arr, size) {
\r
6661 while (arr.length > 0) {
\r
6662 arrays.push(arr.splice(0, size));
\r
6667 var moveMonth = function(selectedDate, direction) {
\r
6668 var step = MonthpickerCtrl.modes[scope.mode].step;
\r
6669 selectedDate.setDate(1);
\r
6670 selectedDate.setMonth(selectedDate.getMonth() + direction * (step.months || 0));
\r
6671 selectedDate.setFullYear(selectedDate.getFullYear() + direction * (step.years || 0));
\r
6673 return selectedDate;
\r
6676 function refill(date) {
\r
6677 if (angular.isDate(date) && !isNaN(date)) {
\r
6678 selected = new Date(date);
\r
6681 selected = new Date();
\r
6686 var selectedCalendar;
\r
6687 if(scope.mode === 1){
\r
6688 if(!angular.isDate(selected))
\r
6690 selected = new Date();
\r
6692 selectedCalendar = moveMonth(angular.copy(selected), -1);
\r
6694 selectedCalendar = angular.copy(selected);
\r
6697 var currentMode = MonthpickerCtrl.modes[mode],
\r
6698 data = currentMode.getVisibleDates(selected);
\r
6700 scope.rows = split(data.objects, currentMode.split);
\r
6703 var startFlag=false;
\r
6704 var firstSelected = false;
\r
6705 for(var i=0; i<scope.rows.length; i++)
\r
6707 for(var j=0; j<scope.rows[i].length; j++)
\r
6709 if(!scope.rows[i][j].disabled && !firstSelected)
\r
6711 firstSelected=true;
\r
6712 var firstDay = scope.rows[i][j];
\r
6715 if(scope.rows[i][j].selected)
\r
6726 if(!flag && firstSelected)
\r
6728 firstDay.firstFocus=true;
\r
6731 scope.labels = data.labels || [];
\r
6732 scope.title = data.title;
\r
6736 scope.select = function (date,$event) {
\r
6737 var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate());
\r
6738 scope.currentDate = dt;
\r
6739 if (!scope.onSelectClose || (scope.onSelectClose && scope.onSelectClose({
\r
6742 if (angular.isNumber(scope.collapseWait)) {
\r
6743 $timeout(function () {
\r
6744 scope.isOpen = false;
\r
6745 toggleCalendar(scope.isOpen);
\r
6746 }, scope.collapseWait);
\r
6748 scope.isOpen = false;
\r
6749 toggleCalendar(scope.isOpen);
\r
6754 scope.move = function (direction,$event) {
\r
6755 var step = MonthpickerCtrl.modes[mode].step;
\r
6756 selected.setDate(1);
\r
6757 selected.setMonth(selected.getMonth() + direction * (step.months || 0));
\r
6758 selected.setFullYear(selected.getFullYear() + direction * (step.years || 0));
\r
6760 scope.getFocus = true;
\r
6761 $timeout(function () {
\r
6762 if (attrs.inline === 'true') {
\r
6763 MonthpickerCtrl.focusNextPrev(element,false);
\r
6765 MonthpickerCtrl.focusNextPrev(b2bMonthpickerPopupTemplate,false);
\r
6768 $event.preventDefault();
\r
6769 $event.stopPropagation();
\r
6772 scope.$watch('currentDate', function (value) {
\r
6773 if (angular.isDefined(value) && value !== null) {
\r
6778 ngModel.$setViewValue(value);
\r
6781 ngModel.$render = function () {
\r
6782 scope.currentDate = ngModel.$viewValue;
\r
6785 var stringToDate = function (value) {
\r
6786 if (!isNaN(new Date(value))) {
\r
6787 value = new Date(value);
\r
6791 ngModel.$formatters.unshift(stringToDate);
\r
6796 .directive('b2bMonthpicker', ['$compile', '$log', 'b2bMonthpickerConfig', 'b2bMonthpickerService', function ($compile, $log, b2bMonthpickerConfig, b2bMonthpickerService) {
\r
6800 disableDates: '&',
\r
6801 onSelectClose: '&'
\r
6803 require: 'ngModel',
\r
6804 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
\r
6805 var dateFormatString = angular.isDefined(attr.dateFormat) ? scope.$parent.$eval(attr.dateFormat) : b2bMonthpickerConfig.dateFormat;
\r
6806 var helperText = angular.isDefined(attr.helperText) ? scope.$parent.$eval(attr.helperText) : b2bMonthpickerConfig.helperText;
\r
6807 helperText = helperText.replace('$date', '{{dt | date : \'' + dateFormatString + '\'}}');
\r
6809 var descriptionText = angular.isDefined(attr.descriptionText) ? scope.$parent.$eval(attr.descriptionText) : b2bMonthpickerConfig.descriptionText;
\r
6812 var inline = false;
\r
6813 if (elem.prop('nodeName') !== 'INPUT' && elem.prop('nodeName') !== 'A') {
\r
6817 var selectedDateMessage = "";
\r
6819 if (elem.prop('nodeName') !== 'A'){
\r
6820 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
6821 elem.attr('tabindex', '-1');
\r
6822 elem.attr('aria-hidden', 'true');
\r
6823 elem.attr('readonly', 'true');
\r
6825 selectedDateMessage = ''
\r
6826 elem.attr('aria-label', helperText);
\r
6829 var descriptionTextSpan = '<span class="offscreen-text" id="monthpicker-description'+scope.$id+'">'+descriptionText+'</span>';
\r
6830 elem.removeAttr('b2b-Monthpicker');
\r
6831 elem.removeAttr('ng-model');
\r
6832 elem.removeAttr('ng-disabled');
\r
6833 elem.addClass('Monthpicker-input');
\r
6834 elem.attr('ng-model', 'dt');
\r
6835 elem.attr('aria-describedby', 'monthpicker-description'+scope.$id);
\r
6839 elem.attr('ng-disabled', 'ngDisabled');
\r
6840 elem.attr('b2b-format-date', dateFormatString);
\r
6842 var wrapperElement = angular.element('<div></div>');
\r
6843 wrapperElement.attr('b2b-Monthpicker-popup', '');
\r
6844 wrapperElement.attr('ng-model', 'dt');
\r
6846 wrapperElement.attr('inline', inline);
\r
6848 if (elem.prop('nodeName') === 'A'){
\r
6849 wrapperElement.attr('link', true);
\r
6851 b2bMonthpickerService.setAttributes(attr, wrapperElement);
\r
6852 b2bMonthpickerService.bindScope(attr, scope);
\r
6854 wrapperElement.html('');
\r
6855 wrapperElement.append(selectedDateMessage);
\r
6856 wrapperElement.append('');
\r
6857 wrapperElement.append(descriptionTextSpan);
\r
6858 wrapperElement.append('');
\r
6859 wrapperElement.append(elem.prop('outerHTML'));
\r
6861 var elm = wrapperElement.prop('outerHTML');
\r
6862 elm = $compile(elm)(scope);
\r
6863 elem.replaceWith(elm);
\r
6865 link: function (scope, elem, attr, ctrl) {
\r
6867 $log.error("ng-model is required.");
\r
6868 return; // do nothing if no ng-model
\r
6871 scope.$watch('dt', function (value) {
\r
6872 ctrl.$setViewValue(value);
\r
6874 ctrl.$render = function () {
\r
6875 scope.dt = ctrl.$viewValue;
\r
6881 .directive('b2bMonthpickerGroup', [function () {
\r
6884 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
\r
6885 this.$$headers = [];
\r
6886 this.$$footers = [];
\r
6887 this.registerMonthpickerScope = function (MonthpickerScope) {
\r
6888 MonthpickerScope.headers = this.$$headers;
\r
6889 MonthpickerScope.footers = this.$$footers;
\r
6892 link: function (scope, elem, attr, ctrl) {}
\r
6896 .directive('b2bFormatDate', ['dateFilter', function (dateFilter) {
\r
6899 require: 'ngModel',
\r
6900 link: function (scope, elem, attr, ctrl) {
\r
6901 var b2bFormatDate = "";
\r
6902 attr.$observe('b2bFormatDate', function (value) {
\r
6903 b2bFormatDate = value;
\r
6905 var dateToString = function (value) {
\r
6906 if (!isNaN(new Date(value))) {
\r
6907 return dateFilter(new Date(value), b2bFormatDate);
\r
6911 ctrl.$formatters.unshift(dateToString);
\r
6916 .directive('b2bMonthpickerHeader', [function () {
\r
6919 require: '^b2bMonthpickerGroup',
\r
6923 compile: function (elem, attr, transclude) {
\r
6924 return function link(scope, elem, attr, ctrl) {
\r
6926 ctrl.$$headers.push(transclude(scope, function () {}));
\r
6934 .directive('b2bMonthpickerFooter', [function () {
\r
6937 require: '^b2bMonthpickerGroup',
\r
6941 compile: function (elem, attr, transclude) {
\r
6942 return function link(scope, elem, attr, ctrl) {
\r
6944 ctrl.$$footers.push(transclude(scope, function () {}));
\r
6952 * @ngdoc directive
\r
6953 * @name Navigation.att:multiLevelNavigation
\r
6956 * <file src="src/multiLevelNavigation/docs/readme.md" />
\r
6959 * <div class="b2b-ml-nav">
\r
6960 * <ul role="tree">
\r
6961 * <li aria-label="{{child.name}}" tabindex="-1" b2b-ml-nav="{{child.type}}" role="treeitem" ng-repeat="child in treeStructure">
\r
6962 * <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
6963 * <!-- Below UL tag is RECURSIVE to generate n-childs -->
\r
6964 * <ul role="group" ng-if="child.child">
\r
6965 * <li aria-label="{{child.name}}" b2b-ml-nav="{{child.type}}" tabindex="-1" role="treeitem" ng-repeat="child in child.child">
\r
6966 * <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
6967 * <!-- RECURSIVE UL tag goes here -->
\r
6975 * <section id="code">
\r
6976 <example module="b2b.att">
\r
6977 <file src="src/multiLevelNavigation/docs/demo.html" />
\r
6978 <file src="src/multiLevelNavigation/docs/demo.js" />
\r
6983 angular.module('b2b.att.multiLevelNavigation', ['b2b.att.utilities'])
\r
6984 //directive b2bMlNav Test coverage 100% on 5/13
\r
6985 .directive('b2bMlNav', ['keymap', function (keymap) {
\r
6988 link: function (scope, element) {
\r
6989 var rootE, parentE, upE, downE, lastE, homeE, endE;
\r
6990 //default root tree element tabindex set zero
\r
6991 if (element.parent().parent().hasClass('b2b-ml-nav') && (element[0].previousElementSibling === null)) {
\r
6992 element.attr('tabindex', 0);
\r
6994 //check root via class
\r
6995 var isRoot = function (elem) {
\r
6996 if (elem.parent().parent().eq(0).hasClass('b2b-ml-nav')) {
\r
7003 //for any expandable tree item on click
\r
7004 var toggleState = function (e) {
\r
7005 if (angular.element(e.target).attr("b2b-ml-nav") !== "endNode") {
\r
7006 var eLink = element.find('a').eq(0);
\r
7007 if (eLink.hasClass('active')) {
\r
7008 eLink.removeClass('active');
\r
7009 eLink.parent().attr("aria-expanded", "false");
\r
7010 eLink.find('i').eq(0).removeClass('icon-primary-expanded');
\r
7011 eLink.find('i').eq(0).addClass('icon-primary-collapsed');
\r
7013 eLink.addClass('active');
\r
7014 eLink.parent().attr("aria-expanded", "true");
\r
7015 eLink.find('i').eq(0).removeClass('icon-primary-collapsed');
\r
7016 eLink.find('i').eq(0).addClass('icon-primary-expanded');
\r
7020 //function finds the main root-item from particular tree-group
\r
7021 var findRoot = function (elem) {
\r
7022 if (isRoot(elem)) {
\r
7026 if (elem.attr("b2b-ml-nav") === "middleNode" || elem.attr("b2b-ml-nav") === "endNode") {
\r
7027 parentE = elem.parent().parent();
\r
7031 if (parentE.attr("b2b-ml-nav") === "rootNode") {
\r
7034 findRoot(parentE);
\r
7037 //finds the last visible node of the previous tree-group
\r
7038 var findPreActive = function (elem) {
\r
7039 if (!(elem.hasClass("active"))) {
\r
7042 var childElems = angular.element(elem[0].nextElementSibling.children);
\r
7043 lastE = angular.element(childElems[childElems.length - 1]);
\r
7044 if (lastE.attr("b2b-ml-nav") === "middleNode" && lastE.find('a').eq(0).hasClass('active')) {
\r
7045 findPreActive(lastE.find('a').eq(0));
\r
7050 //find above visible link
\r
7051 var findUp = function (elem) {
\r
7052 if (elem[0].previousElementSibling !== null) {
\r
7053 upE = angular.element(elem[0].previousElementSibling);
\r
7055 upE = elem.parent().parent();
\r
7057 if (isRoot(elem) || (upE.attr('b2b-ml-nav') === "middleNode" && upE[0] !== elem.parent().parent()[0])) {
\r
7058 findPreActive(upE.find('a').eq(0));
\r
7061 //find below visible link
\r
7062 var findDown = function (elem) {
\r
7063 if (elem.hasClass('active')) {
\r
7064 downE = elem.next().find('li').eq(0);
\r
7066 if (elem.parent().next().length !== 0) {
\r
7067 downE = elem.parent().next().eq(0);
\r
7069 if (elem.parent().parent().parent().next().length !== 0) {
\r
7070 downE = elem.parent().parent().parent().next().eq(0);
\r
7073 downE = elem.parent().eq(0);
\r
7077 //finds last root-group element of the tree
\r
7078 var findEnd = function (elem) {
\r
7080 endE = angular.element(rootE.parent()[0].children[rootE.parent()[0].children.length - 1]);
\r
7082 //finds first root element of tree
\r
7083 var findHome = function (elem) {
\r
7085 homeE = angular.element(rootE.parent()[0].children[0]);
\r
7087 element.bind('click', function (e) {
\r
7088 if(element.attr("b2b-ml-nav") !== "endNode") {
\r
7091 e.stopPropagation();
\r
7093 element.bind('focus', function (e) {
\r
7094 if(element.attr("b2b-ml-nav") !== "endNode") {
\r
7095 if(element.find('a').eq(0).hasClass('active')) {
\r
7096 element.attr("aria-expanded", true);
\r
7099 element.attr("aria-expanded", false);
\r
7104 //Keyboard functionality approach:
\r
7106 //set set tabindex -1 on the current focus element
\r
7107 //find the next element to be focussed, set tabindex 0 and throw focus
\r
7108 element.bind('keydown', function (evt) {
\r
7109 switch (evt.keyCode) {
\r
7110 case keymap.KEY.ENTER:
\r
7111 case keymap.KEY.SPACE:
\r
7112 element.triggerHandler('click');
\r
7113 evt.stopPropagation();
\r
7114 evt.preventDefault();
\r
7116 case keymap.KEY.END:
\r
7117 evt.preventDefault();
\r
7118 element.attr('tabindex', -1);
\r
7120 endE.eq(0).attr('tabindex', 0);
\r
7122 evt.stopPropagation();
\r
7124 case keymap.KEY.HOME:
\r
7125 evt.preventDefault();
\r
7126 element.attr('tabindex', -1);
\r
7127 findHome(element);
\r
7128 homeE.eq(0).attr('tabindex', 0);
\r
7130 evt.stopPropagation();
\r
7132 case keymap.KEY.LEFT:
\r
7133 evt.preventDefault();
\r
7134 if (!isRoot(element)) {
\r
7135 element.attr('tabindex', -1);
\r
7136 parentE = element.parent().parent();
\r
7137 parentE.eq(0).attr('tabindex', 0);
\r
7138 parentE[0].focus();
\r
7139 parentE.eq(0).triggerHandler('click');
\r
7141 if (element.find('a').eq(0).hasClass('active')) {
\r
7142 element.triggerHandler('click');
\r
7145 evt.stopPropagation();
\r
7147 case keymap.KEY.UP:
\r
7148 evt.preventDefault();
\r
7149 if (!(isRoot(element) && element[0].previousElementSibling === null)) {
\r
7150 element.attr('tabindex', -1);
\r
7152 upE.eq(0).attr('tabindex', 0);
\r
7155 evt.stopPropagation();
\r
7157 case keymap.KEY.RIGHT:
\r
7158 evt.preventDefault();
\r
7159 if (element.attr("b2b-ml-nav") !== "endNode") {
\r
7160 if (!element.find('a').eq(0).hasClass('active')) {
\r
7161 element.triggerHandler('click');
\r
7163 element.attr('tabindex', -1);
\r
7164 findDown(element.find('a').eq(0));
\r
7165 downE.eq(0).attr('tabindex', 0);
\r
7168 evt.stopPropagation();
\r
7170 case keymap.KEY.DOWN:
\r
7171 evt.preventDefault();
\r
7172 element.attr('tabindex', -1);
\r
7173 if (!(element.attr("b2b-ml-nav") === "middleNode" && element.find('a').eq(0).hasClass('active')) && (element.next().length === 0)) {
\r
7174 if(element.parent().parent().attr("b2b-ml-nav") !== "rootNode" && element.parent().parent()[0].nextElementSibling !== null)
\r
7176 findDown(element.find('a').eq(0));
\r
7177 downE.eq(0).attr('tabindex', 0);
\r
7179 evt.stopPropagation();
\r
7182 findRoot(element);
\r
7183 if (!(rootE.next().length === 0)) {
\r
7184 rootE.next().eq(0).attr('tabindex', 0);
\r
7185 rootE.next()[0].focus();
\r
7187 rootE.eq(0).attr('tabindex', 0);
\r
7190 evt.stopPropagation();
\r
7193 findDown(element.find('a').eq(0));
\r
7194 downE.eq(0).attr('tabindex', 0);
\r
7196 evt.stopPropagation();
\r
7206 * @ngdoc directive
\r
7207 * @name Tabs, tables & accordions.att:multipurposeExpander
\r
7210 * <file src="src/multipurposeExpander/docs/readme.md" />
\r
7213 * <!--With Close Other -->
\r
7214 * <b2b-expander-group close-others="true">
\r
7215 * <b2b-expanders class="mpc-expanders" is-open="testmpc">
\r
7216 * <b2b-expander-heading ng-class=" { 'b2b-toggle-header-active': !testmpc, 'b2b-toggle-header-inactive': testmpc } ">Heading content goes here</b2b-expander-heading>
\r
7217 * <b2b-expander-body>
\r
7218 <p>body content goes here</p>
\r
7219 </b2b-expander-body>
\r
7220 * </b2b-expanders>
\r
7221 * </b2b-expander-group>
\r
7223 * <!-- Without Close Other -->
\r
7224 * <b2b-expanders class="mpc-expanders" is-open="testmpc2">
\r
7225 * <b2b-expander-heading ng-class=" { 'b2b-toggle-header-active': !testmpc2, 'b2b-toggle-header-inactive': testmpc2 } ">Heading content goes here</b2b-expander-heading>
\r
7226 * <b2b-expander-body>
\r
7227 <p>body content goes here</p>
\r
7228 </b2b-expander-body>
\r
7229 * </b2b-expanders>
\r
7232 * <section id="code">
\r
7233 <example module="b2b.att.multipurposeExpander">
\r
7234 <file src="src/multipurposeExpander/docs/demo.html" />
\r
7235 <file src="src/multipurposeExpander/docs/demo.js" />
\r
7241 angular.module('b2b.att.multipurposeExpander', ['b2b.att', 'b2b.att.collapse'])
\r
7242 .directive('b2bExpanderGroup', function () {
\r
7246 template: "<ng-transclude></ng-transclude>",
\r
7247 controller:['$scope','$attrs', function($scope,$attrs){
\r
7250 this.scope = $scope;
\r
7252 this.addGroup = function (groupScope) {
\r
7254 groupScope.index = this.groups.length;
\r
7255 this.groups.push(groupScope);
\r
7256 if(this.groups.length > 0){
\r
7259 groupScope.$on('$destroy', function () {
\r
7260 that.removeGroup(groupScope);
\r
7264 this.closeOthers = function (openGroup) {
\r
7265 var closeOthers = angular.isDefined($attrs.closeOthers);
\r
7266 if (closeOthers && !$scope.forceExpand) {
\r
7267 angular.forEach(this.groups, function (group) {
\r
7268 if (group !== openGroup) {
\r
7269 group.isOpen = false;
\r
7273 if (this.groups.indexOf(openGroup) === (this.groups.length - 1) && $scope.forceExpand) {
\r
7274 $scope.forceExpand = false;
\r
7277 this.removeGroup = function (group) {
\r
7278 var index = this.groups.indexOf(group);
\r
7279 if (index !== -1) {
\r
7280 this.groups.splice(this.groups.indexOf(group), 1);
\r
7288 .directive('b2bExpanders', function () {
\r
7292 require:['b2bExpanders','?^b2bExpanderGroup'],
\r
7294 scope:{isOpen:'=?'},
\r
7295 template: "<div ng-transclude></div>",
\r
7296 controller: ['$scope', function ($scope){
\r
7297 var bodyScope = null;
\r
7298 var expanderScope = null;
\r
7299 this.isOpened = function(){
\r
7308 this.setScope = function (scope) {
\r
7309 bodyScope = scope;
\r
7310 bodyScope.isOpen = $scope.isOpen;
\r
7312 this.setExpanderScope = function (scope) {
\r
7313 expanderScope = scope;
\r
7315 this.toggle = function () {
\r
7316 $scope.isOpen = bodyScope.isOpen = !bodyScope.isOpen;
\r
7317 return bodyScope.isOpen;
\r
7320 this.watchToggle = function(io){
\r
7321 bodyScope.isOpen = io;
\r
7322 expanderScope.updateIcons(io);
\r
7325 link: function (scope, elem, attr, myCtrl)
\r
7327 //scope.isOpen = false;
\r
7329 myCtrl[1].addGroup(scope);
\r
7331 scope.$watch('isOpen', function(val){
\r
7332 myCtrl[0].watchToggle(scope.isOpen);
\r
7333 if(val && myCtrl[1]){
\r
7334 myCtrl[1].closeOthers(scope);
\r
7341 .directive('b2bExpanderHeading', function () {
\r
7343 require: "^b2bExpanders",
\r
7348 template: "<div style='padding:10px 10px 10px 0 !important' ng-transclude></div>"
\r
7352 .directive('b2bExpanderBody', function () {
\r
7355 require: "^b2bExpanders",
\r
7359 template: "<div b2b-collapse='!isOpen' ><div ng-transclude></div></div>",
\r
7360 link: function (scope, elem, attr, myCtrl) {
\r
7361 scope.isOpen = false;
\r
7362 myCtrl.setScope(scope);
\r
7367 .directive('b2bExpanderToggle', function () {
\r
7370 require: "^b2bExpanders",
\r
7376 link: function (scope, element, attr, myCtrl)
\r
7378 myCtrl.setExpanderScope(scope);
\r
7379 var isOpen = myCtrl.isOpened();
\r
7381 scope.setIcon = function () {
\r
7382 element.attr("role", "tab");
\r
7384 if (scope.expandIcon && scope.collapseIcon)
\r
7387 element.removeClass(scope.expandIcon);
\r
7388 element.addClass(scope.collapseIcon);
\r
7390 element.attr("aria-expanded", "true");
\r
7393 element.removeClass(scope.collapseIcon);
\r
7394 element.addClass(scope.expandIcon);
\r
7396 element.attr("aria-expanded", "false");
\r
7401 element.bind('click', function (){
\r
7404 scope.updateIcons = function(nStat){
\r
7408 scope.toggleit = function (){
\r
7409 isOpen = myCtrl.toggle();
\r
7418 * @ngdoc directive
\r
7419 * @name Messages, modals & alerts.att:notesMessagesAndErrors
\r
7422 * <file src="src/notesMessagesAndErrors/docs/readme.md" />
\r
7428 * <section id="code">
\r
7429 <example module="b2b.att">
\r
7430 <file src="src/notesMessagesAndErrors/docs/demo.html" />
\r
7431 <file src="src/notesMessagesAndErrors/docs/demo.js" />
\r
7436 angular.module('b2b.att.notesMessagesAndErrors', []);
\r
7438 * @ngdoc directive
\r
7439 * @name Template.att:Notification Card
\r
7442 * <file src="src/notificationCardTemplate/docs/readme.md" />
\r
7445 * <section id="code">
\r
7446 <b>HTML + AngularJS</b>
\r
7447 <example module="b2b.att">
\r
7448 <file src="src/notificationCardTemplate/docs/demo.html" />
\r
7449 <file src="src/notificationCardTemplate/docs/demo.js" />
\r
7454 angular.module('b2b.att.notificationCardTemplate', [])
\r
7457 * @ngdoc directive
\r
7458 * @name Template.att:Order Confirmation Template
\r
7461 * <file src="src/orderConfirmationTemplate/docs/readme.md" />
\r
7464 * <section id="code">
\r
7465 <b>HTML + AngularJS</b>
\r
7466 <example module="b2b.att">
\r
7467 <file src="src/orderConfirmationTemplate/docs/demo.html" />
\r
7468 <file src="src/orderConfirmationTemplate/docs/demo.js" />
\r
7473 angular.module('b2b.att.orderConfirmationTemplate', []);
\r
7476 * @ngdoc directive
\r
7477 * @name Navigation.att:pagination
\r
7480 * <file src="src/pagination/docs/readme.md" />
\r
7483 * <div b2b-pagination="" input-id="goto-page-2" total-pages="totalPages1" current-page="currentPage1" click-handler="customHandler" show-input="showInput"></div>
\r
7486 * <section id="code">
\r
7487 <example module="b2b.att">
\r
7488 <file src="src/pagination/docs/demo.html" />
\r
7489 <file src="src/pagination/docs/demo.js" />
\r
7494 angular.module('b2b.att.pagination', ['b2b.att.utilities', 'ngTouch'])
\r
7495 .directive('b2bPagination', ['b2bUserAgent', 'keymap', '$window', '$timeout', function (b2bUserAgent, keymap, $window, $timeout) {
\r
7502 clickHandler: '=?',
\r
7506 templateUrl: 'b2bTemplate/pagination/b2b-pagination.html',
\r
7507 link: function (scope, elem) {
\r
7508 scope.isMobile = b2bUserAgent.isMobile();
\r
7509 scope.notMobile = b2bUserAgent.notMobile();
\r
7510 scope.focusedPage;
\r
7511 scope.meanVal = 3;
\r
7512 scope.$watch('totalPages', function (value) {
\r
7513 if (angular.isDefined(value) && value !== null) {
\r
7516 scope.totalPages = 1;
\r
7519 if (value <= 10) {
\r
7520 for (var i = 1; i <= value; i++) {
\r
7521 scope.pages.push(i);
\r
7523 } else if (value > 10) {
\r
7524 var midVal = Math.ceil(value / 2);
\r
7525 scope.pages = [midVal - 2, midVal - 1, midVal, midVal + 1, midVal + 2];
\r
7527 if(scope.currentPage === undefined || scope.currentPage === 1)
\r
7529 currentPageChanged(1);
\r
7533 scope.$watch('currentPage', function (value) {
\r
7534 currentPageChanged(value);
\r
7535 callbackHandler(value);
\r
7537 var callbackHandler = function (num) {
\r
7538 if (angular.isFunction(scope.clickHandler)) {
\r
7539 scope.clickHandler(num);
\r
7543 function currentPageChanged(value) {
\r
7544 if (angular.isDefined(value) && value !== null) {
\r
7545 if (!value || value < 1) {
\r
7548 if (value > scope.totalPages) {
\r
7549 value = scope.totalPages;
\r
7551 if (scope.currentPage !== value) {
\r
7552 scope.currentPage = value;
\r
7553 callbackHandler(scope.currentPage);
\r
7555 if (scope.totalPages > 10) {
\r
7556 var val = parseInt(value);
\r
7558 scope.pages = [1, 2, 3, 4, 5, 6, 7, 8];
\r
7559 } else if (val > 6 && val <= scope.totalPages - 5) {
\r
7560 scope.pages = [val - 1, val, val + 1];
\r
7561 } else if (val >= scope.totalPages - 5) {
\r
7562 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
7565 if (scope.isMobile) {
\r
7566 var inWidth = $window.innerWidth;
\r
7567 var viewLimit = 7;
\r
7568 if (inWidth <= 400) {
\r
7570 } else if (inWidth > 400 && inWidth < 500) {
\r
7572 } else if (inWidth >= 500 && inWidth < 600) {
\r
7574 } else if (inWidth >= 600 && inWidth < 700) {
\r
7576 } else if (inWidth >= 700 && inWidth < 800) {
\r
7580 var val = parseInt(value);
\r
7582 scope.meanVal = Math.floor(viewLimit / 2);
\r
7583 var lowerLimit = (val - scope.meanVal) < 1 ? 1 : val - scope.meanVal;
\r
7584 var upperLimit = (lowerLimit + viewLimit - 1) > scope.totalPages ? scope.totalPages : lowerLimit + viewLimit - 1;
\r
7586 for (var i = lowerLimit; i <= upperLimit; i++) {
\r
7587 scope.pages.push(i);
\r
7592 scope.gotoKeyClick = function (keyEvent) {
\r
7593 if (keyEvent.which === keymap.KEY.ENTER) {
\r
7594 scope.gotoBtnClick()
\r
7597 scope.gotoBtnClick = function () {
\r
7598 currentPageChanged(parseInt(scope.gotoPage));
\r
7599 callbackHandler(scope.currentPage);
\r
7600 var qResult = elem[0].querySelector('button');
\r
7601 angular.element(qResult).attr('disabled','true');
\r
7602 $timeout(function(){
\r
7603 elem[0].querySelector('.b2b-pager__item--active').focus();
\r
7605 scope.gotoPage = null;
\r
7607 scope.onfocusIn = function(evt)
\r
7609 var qResult = elem[0].querySelector('button');
\r
7610 angular.element(qResult).removeAttr('disabled');
\r
7612 scope.onfocusOut = function(evt)
\r
7614 if(evt.target.value === "")
\r
7616 var qResult = elem[0].querySelector('button');
\r
7617 angular.element(qResult).attr('disabled','true');
\r
7620 scope.next = function (event) {
\r
7621 if (event != undefined) {
\r
7622 event.preventDefault();
\r
7624 if (scope.currentPage < scope.totalPages) {
\r
7625 scope.currentPage += 1;
\r
7626 callbackHandler(scope.currentPage);
\r
7629 scope.prev = function (event) {
\r
7630 if (event != undefined) {
\r
7631 event.preventDefault();
\r
7633 if (scope.currentPage > 1) {
\r
7634 scope.currentPage -= 1;
\r
7635 callbackHandler(scope.currentPage);
\r
7638 scope.selectPage = function (value, event) {
\r
7639 event.preventDefault();
\r
7640 scope.currentPage = value;
\r
7641 scope.focusedPage = value;
\r
7642 callbackHandler(scope.currentPage);
\r
7644 scope.checkSelectedPage = function (value) {
\r
7645 if (scope.currentPage === value) {
\r
7650 scope.isFocused = function (page) {
\r
7651 return scope.focusedPage === page;
\r
7657 * @ngdoc directive
\r
7658 * @name Navigation.att:paneSelector
\r
7661 * <file src="src/paneSelector/docs/readme.md" />
\r
7664 * Please refer demo.html tab in Example section below.
\r
7667 <section id="code">
\r
7668 <b>HTML + AngularJS</b>
\r
7669 <example module="b2b.att">
\r
7670 <file src="src/paneSelector/docs/demo.html" />
\r
7671 <file src="src/paneSelector/docs/demo.js" />
\r
7676 angular.module('b2b.att.paneSelector', ['b2b.att.tabs', 'b2b.att.utilities'])
\r
7678 .filter('paneSelectorSelectedItemsFilter', [function () {
\r
7679 return function (listOfItemsArray) {
\r
7681 if (!listOfItemsArray) {
\r
7682 listOfItemsArray = [];
\r
7685 var returnArray = [];
\r
7687 for (var i = 0; i < listOfItemsArray.length; i++) {
\r
7688 if (listOfItemsArray[i].isSelected) {
\r
7689 returnArray.push(listOfItemsArray[i]);
\r
7693 return returnArray;
\r
7697 .filter('paneSelectorFetchChildItemsFilter', [function () {
\r
7698 return function (listOfItemsArray) {
\r
7700 if (!listOfItemsArray) {
\r
7701 listOfItemsArray = [];
\r
7704 var returnArray = [];
\r
7706 for (var i = 0; i < listOfItemsArray.length; i++) {
\r
7707 for (var j = 0; j < listOfItemsArray[i].childItems.length; j++) {
\r
7708 returnArray.push(listOfItemsArray[i].childItems[j]);
\r
7712 return returnArray;
\r
7716 .directive('b2bPaneSelector', [function () {
\r
7720 templateUrl: 'b2bTemplate/paneSelector/paneSelector.html',
\r
7726 .directive('b2bPaneSelectorPane', [ function () {
\r
7730 templateUrl: 'b2bTemplate/paneSelector/paneSelectorPane.html',
\r
7736 .directive('b2bTabVertical', ['$timeout', 'keymap', function ($timeout, keymap) {
\r
7739 require: '^b2bTab',
\r
7740 link: function (scope, element, attr, b2bTabCtrl) {
\r
7742 if (!b2bTabCtrl) {
\r
7746 // retreive the isolateScope
\r
7747 var iScope = angular.element(element).isolateScope();
\r
7749 $timeout(function () {
\r
7750 angular.element(element[0].querySelector('a')).unbind('keydown');
\r
7751 angular.element(element[0].querySelector('a')).bind('keydown', function (evt) {
\r
7753 if (!(evt.keyCode)) {
\r
7754 evt.keyCode = evt.which;
\r
7757 switch (evt.keyCode) {
\r
7758 case keymap.KEY.DOWN:
\r
7759 evt.preventDefault();
\r
7763 case keymap.KEY.UP:
\r
7764 evt.preventDefault();
\r
7765 iScope.previousKey();
\r
7776 * @ngdoc directive
\r
7777 * @name Forms.att:phoneNumberInput
\r
7780 * <file src="src/phoneNumberInput/docs/readme.md" />
\r
7783 <form name="userForm1">
\r
7784 <div class="form-row" ng-class="{'error':!userForm1.text.$valid && userForm1.text.$dirty}">
\r
7785 <label>Phone Mask<span style="color:red">*</span>: (npa) nxx-line Model Value: {{mask.text}}</label>
\r
7787 <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
7788 <div ng-messages="userForm1.text.$error" class="error-msg" aria-live="polite" aria-atomic="true">
\r
7789 <span ng-message="required" role="alert">This field is mandatory!</span>
\r
7790 <span ng-message="invalidPhoneNumber" role="alert">Please enter valid phone number!</span>
\r
7791 <span ng-message="mask" role="alert">Please enter valid phone number!</span>
\r
7798 * <section id="code">
\r
7799 <example module="b2b.att">
\r
7800 <file src="src/phoneNumberInput/docs/demo.html" />
\r
7801 <file src="src/phoneNumberInput/docs/demo.js" />
\r
7806 angular.module('b2b.att.phoneNumberInput', ['ngMessages', 'b2b.att.utilities'])
\r
7807 .constant("CoreFormsUiConfig", {
\r
7808 phoneMask: '(___) ___-____',
\r
7809 phoneMaskDot: '___.___.____',
\r
7810 phoneMaskHyphen: '___-___-____'
\r
7812 .directive('b2bPhoneMask', ['$parse', 'CoreFormsUiConfig', 'keymap', function ($parse, CoreFormsUiConfig, keymap) {
\r
7814 require: 'ngModel',
\r
7818 link: function (scope, iElement, iAttrs, ctrl) {
\r
7819 var navigatorAgent = navigator.userAgent.toLowerCase(),
\r
7820 isAndroid = navigatorAgent.indexOf("android") > -1,
\r
7821 oldIE = navigatorAgent.indexOf('msie 8.0') !== -1;
\r
7823 var validPhoneNumber = false;
\r
7824 var currentKey = '';
\r
7826 mask = "__________";
\r
7828 switch (iAttrs.b2bPhoneMask) {
\r
7830 mask = CoreFormsUiConfig.phoneMask;
\r
7832 case "phoneMaskDot":
\r
7833 mask = CoreFormsUiConfig.phoneMaskDot;
\r
7835 case "phoneMaskHyphen":
\r
7836 mask = CoreFormsUiConfig.phoneMaskHyphen;
\r
7839 mask = CoreFormsUiConfig.phoneMask;
\r
7842 iElement.attr("maxlength", mask.length);
\r
7843 var checkValidity = function (unmaskedValue, rawValue) {
\r
7844 var valid = false;
\r
7845 if (angular.isUndefined(rawValue) || rawValue === '') {
\r
7847 } else if (unmaskedValue) {
\r
7848 valid = (unmaskedValue.length === 10);
\r
7850 ctrl.$setValidity('invalidPhoneNumber', validPhoneNumber);
\r
7851 ctrl.$setValidity('mask', valid);
\r
7854 var handleKeyup = function (evt) {
\r
7856 if (evt && evt.keyCode === keymap.KEY.SHIFT) {
\r
7860 var index, formattedNumber;
\r
7861 if (ctrl.$modelValue) {
\r
7862 formattedNumber = ctrl.$modelValue;
\r
7864 formattedNumber = iElement.val();
\r
7866 if (!formattedNumber.length && currentKey === '') {
\r
7869 var maskLength, inputNumbers, maskArray, tempArray, maskArrayLength;
\r
7871 maskArray = mask.split("");
\r
7872 maskArrayLength = maskArray.length;
\r
7873 maskLength = formattedNumber.substring(0, mask.length);
\r
7874 inputNumbers = formattedNumber.replace(/[^0-9]/g, "").split("");
\r
7875 for (index = 0; index < maskArrayLength; index++) {
\r
7876 tempArray.push(maskArray[index] === "_" ? inputNumbers.shift() : maskArray[index]);
\r
7877 if (inputNumbers.length === 0) {
\r
7881 formattedNumber = tempArray.join("");
\r
7882 if (formattedNumber === '(') {
\r
7883 formattedNumber = '';
\r
7886 if ( (angular.isDefined(evt) && evt.which) && currentKey !== '') {
\r
7887 if (maskArray[0] === currentKey && formattedNumber === '') {
\r
7888 formattedNumber = '(';
\r
7889 } else if (evt.which === keymap.KEY.SPACE && currentKey === ' ') {
\r
7890 formattedNumber = formattedNumber + ') ';
\r
7891 } else if (maskArray[0] === currentKey && formattedNumber === '') {
\r
7892 formattedNumber = formattedNumber + currentKey;
\r
7893 } else if (maskArray[formattedNumber.length] === currentKey) {
\r
7894 formattedNumber = formattedNumber + currentKey;
\r
7899 ctrl.$setViewValue(formattedNumber);
\r
7901 return formattedNumber;
\r
7905 // since we are only allowing 0-9, why even let the keypress go forward?
\r
7906 // also added in delete... in case they want to delete :)
\r
7907 var handlePress = function (e) {
\r
7909 if ((e.which < 48 || e.which > 57) && (e.which < 96 || e.which > 105)) {
\r
7910 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
7911 // Allow: Ctrl+V/v
\r
7912 (!(e.ctrlKey) && (e.which !== '118' || e.which !== '86')) &&
\r
7913 // Allow: Ctrl+C/c
\r
7914 (!(e.ctrlKey) && (e.which !== '99' || e.which !== '67')) &&
\r
7915 // Allow: Ctrl+X/x
\r
7916 (!(e.ctrlKey) && (e.which !== '120' || e.which !== '88'))) {
\r
7917 e.preventDefault ? e.preventDefault() : e.returnValue = false;
\r
7918 iElement.attr("aria-label", "Only numbers are allowed");
\r
7919 validPhoneNumber = false;
\r
7922 iElement.removeAttr("aria-label");
\r
7923 validPhoneNumber = true;
\r
7930 // i moved this out because i thought i might need focus as well..
\r
7931 // to handle setting the model as the view changes
\r
7932 var parser = function (fromViewValue) {
\r
7933 var letters = /^[A-Za-z]+$/;
\r
7934 var numbers = /^[0-9]+$/;
\r
7935 if (angular.isUndefined(fromViewValue) || fromViewValue === '') {
\r
7936 validPhoneNumber = true;
\r
7938 if (fromViewValue.match(letters)) {
\r
7939 validPhoneNumber = false;
\r
7941 if (fromViewValue.match(numbers)) {
\r
7942 validPhoneNumber = true;
\r
7946 if (fromViewValue && fromViewValue.length > 0) {
\r
7947 clean = fromViewValue.replace(/[^0-9]/g, '');
\r
7949 checkValidity(clean, fromViewValue);
\r
7953 //to handle reading the model and formatting it
\r
7954 var formatter = function (fromModelView) {
\r
7956 checkValidity(fromModelView);
\r
7957 if (fromModelView) {
\r
7958 input = handleKeyup();
\r
7963 var setCurrentKey = function (e) {
\r
7964 switch (e.which) {
\r
7974 if (e.shiftKey === true) {
\r
7979 if (e.shiftKey === true) {
\r
7989 if (angular.isDefined(scope.ngModel)) {
\r
7990 parser(scope.ngModel);
\r
7993 ctrl.$parsers.push(parser);
\r
7994 ctrl.$formatters.push(formatter);
\r
7995 iElement.bind('keyup', handleKeyup);
\r
7996 iElement.bind('keydown', handlePress);
\r
8001 * @ngdoc directive
\r
8002 * @name Template.att:Profile Blocks
\r
8005 * <file src="src/profileBlockTemplate/docs/readme.md" />
\r
8007 * <section id="code">
\r
8008 <example module="b2b.att">
\r
8009 <file src="src/profileBlockTemplate/docs/demo.html" />
\r
8010 <file src="src/profileBlockTemplate/docs/demo.js" />
\r
8016 angular.module('b2b.att.profileBlockTemplate', [])
\r
8021 * @ngdoc directive
\r
8022 * @name Layouts.att:profileCard
\r
8025 * <file src="src/profileCard/docs/readme.md" />
\r
8028 * <b2b-profile-card></b2b-profile-card>
\r
8031 <section id="code">
\r
8032 <example module="b2b.att">
\r
8033 <file src="src/profileCard/docs/demo.html" />
\r
8034 <file src="src/profileCard/docs/demo.js" />
\r
8039 angular.module('b2b.att.profileCard', ['b2b.att'])
\r
8040 .constant('profileStatus',{
\r
8047 status: "Deactivated",
\r
8059 status: "Pending",
\r
8063 role: "COMPANY ADMINISTRATOR"
\r
8066 .directive('b2bProfileCard',['$http','$q','profileStatus', function($http,$q,profileStatus) {
\r
8070 templateUrl: function(element, attrs){
\r
8071 if(!attrs.addUser){
\r
8072 return 'b2bTemplate/profileCard/profileCard.html';
\r
8075 return 'b2bTemplate/profileCard/profileCard-addUser.html';
\r
8080 characterLimit: '@'
\r
8082 link: function(scope, elem, attr){
\r
8083 scope.characterLimit = parseInt(attr.characterLimit, 10) || 25;
\r
8084 scope.shouldClip = function(str) {
\r
8085 return str.length > scope.characterLimit;
\r
8088 scope.showEmailTooltip = false;
\r
8091 function isImage(src) {
\r
8092 var deferred = $q.defer();
\r
8093 var image = new Image();
\r
8094 image.onerror = function() {
\r
8095 deferred.reject(false);
\r
8097 image.onload = function() {
\r
8098 deferred.resolve(true);
\r
8100 if(src !== undefined && src.length>0 ){
\r
8103 deferred.reject(false);
\r
8105 return deferred.promise;
\r
8107 if(!attr.addUser){
\r
8108 scope.image=false;
\r
8109 isImage(scope.profile.img).then(function(img) {
\r
8112 var splitName=(scope.profile.name).split(' ');
\r
8113 scope.initials='';
\r
8114 for(var i=0;i<splitName.length;i++){
\r
8115 scope.initials += splitName[i][0];
\r
8117 if(scope.profile.role.toUpperCase() === profileStatus.role){
\r
8120 var profileState=profileStatus.status[scope.profile.state.toUpperCase()];
\r
8121 if(profileState) {
\r
8122 scope.profile.state=profileStatus.status[scope.profile.state.toUpperCase()].status;
\r
8123 scope.colorIcon=profileStatus.status[scope.profile.state.toUpperCase()].color;
\r
8124 if(scope.profile.state.toUpperCase()===profileStatus.status.PENDING.status.toUpperCase()||scope.profile.state.toUpperCase()===profileStatus.status.LOCKED.status.toUpperCase()){
\r
8125 scope.profile.lastLogin=scope.profile.state;
\r
8128 var today=new Date().getTime();
\r
8129 var lastlogin=new Date(scope.profile.lastLogin).getTime();
\r
8130 var diff=(today-lastlogin)/(1000*60*60*24);
\r
8132 scope.profile.lastLogin="Today";
\r
8135 scope.profile.lastLogin="Yesterday";
\r
8142 * @ngdoc directive
\r
8143 * @name Forms.att:radios
\r
8146 * <file src="src/radios/docs/readme.md" />
\r
8149 * See demo section
\r
8151 * @param {boolean} refreshRadioGroup - A trigger that recalculates and updates the accessibility roles on radios in a group.
\r
8154 <section id="code">
\r
8155 <b>HTML + AngularJS</b>
\r
8156 <example module="b2b.att">
\r
8157 <file src="src/radios/docs/demo.html" />
\r
8158 <file src="src/radios/docs/demo.js" />
\r
8162 angular.module('b2b.att.radios', ['b2b.att.utilities'])
\r
8163 .directive('b2bRadioGroupAccessibility', ['$timeout', 'b2bUserAgent', function($timeout, b2bUserAgent) {
\r
8167 refreshRadioGroup: "=",
\r
8169 link: function(scope, ele, attr) {
\r
8171 var roleRadioElement, radioProductSelectElement, radioInputTypeElement;
\r
8173 $timeout(calculateNumberOfRadio);
\r
8175 scope.$watch('refreshRadioGroup', function(value) {
\r
8176 if (value === true) {
\r
8177 addingRoleAttribute();
\r
8178 $timeout(calculateNumberOfRadio);
\r
8179 scope.refreshRadioGroup = false;
\r
8186 function calculateNumberOfRadio() {
\r
8187 roleRadioElement = ele[0].querySelectorAll('[role="radio"]');
\r
8189 radioProductSelectElement = ele[0].querySelectorAll('[role="radiogroup"] li.radio-box');
\r
8191 radioInputTypeElement = ele[0].querySelectorAll('input[type="radio"]');
\r
8193 for (var i = 0; i < radioInputTypeElement.length; i++) {
\r
8194 var isChecked = radioInputTypeElement[i].checked ? 'true' : 'false';
\r
8195 var isDisabled = radioInputTypeElement[i].disabled ? 'true' : 'false';
\r
8196 var numOfx = i + 1 + ' of ' + radioInputTypeElement.length;
\r
8197 angular.element(roleRadioElement[i]).attr({
\r
8198 'aria-checked': isChecked,
\r
8199 'aria-disabled': isDisabled,
\r
8200 'data-opNum': numOfx
\r
8202 if (b2bUserAgent.notMobile()) {
\r
8203 angular.element(roleRadioElement[i]).removeAttr("role");
\r
8206 if (radioProductSelectElement.length) {
\r
8207 isChecked === 'true' ? angular.element(radioProductSelectElement[i]).addClass('active') : angular.element(radioProductSelectElement[i]).removeClass('active');
\r
8210 if (/Android/i.test(navigator.userAgent)) {
\r
8211 angular.element(roleRadioElement[i]).append('<span class="hidden-spoken">. ' + numOfx + '.</span>');
\r
8215 angular.element(radioInputTypeElement[i]).bind('click', radioStateChangeonClick);
\r
8220 function addingRoleAttribute() {
\r
8221 for (var i = 0; i < radioInputTypeElement.length; i++) {
\r
8222 if (b2bUserAgent.notMobile()) {
\r
8223 angular.element(roleRadioElement[i]).attr("role", "radio");
\r
8228 function radioStateChangeonClick() {
\r
8229 for (var i = 0; i < radioInputTypeElement.length; i++) {
\r
8230 var isChecked = radioInputTypeElement[i].checked ? 'true' : 'false';
\r
8231 var isDisabled = radioInputTypeElement[i].disabled ? 'true' : 'false';
\r
8232 if (radioProductSelectElement.length) {
\r
8233 isChecked === 'true' ? angular.element(radioProductSelectElement[i]).addClass('active') : angular.element(radioProductSelectElement[i]).removeClass('active');
\r
8235 angular.element(roleRadioElement[i]).attr({
\r
8236 'aria-checked': isChecked,
\r
8237 'aria-disabled': isDisabled
\r
8248 * @ngdoc directive
\r
8249 * @name Forms.att:searchField
\r
8252 * <file src="src/searchField/docs/readme.md" />
\r
8255 * <div b2b-search-field dropdown-list="listdata" on-click-callback="clickFn(value)" class="span12" input-model='typedString' config-obj='keyConfigObj'></div>
\r
8258 <section id="code">
\r
8259 <example module="b2b.att">
\r
8260 <file src="src/searchField/docs/demo.html" />
\r
8261 <file src="src/searchField/docs/demo.js" />
\r
8266 angular.module('b2b.att.searchField', ['b2b.att.utilities', 'b2b.att.position'])
\r
8267 .filter('b2bFilterInput', [function() {
\r
8268 return function(list, str, keyArray, displayListKey, isContainsSearch, searchSeperator) {
\r
8271 var searchCondition;
\r
8272 var conditionCheck = function(searchSeperator, listItem, displayListKey, splitString) {
\r
8273 var displayTitle = null;
\r
8274 if (splitString) {
\r
8275 for (var i = 0; i < displayListKey.length; i++) {
\r
8277 displayTitle = listItem[displayListKey[i]].toLowerCase().indexOf(splitString[i].toLowerCase()) > -1;
\r
8279 displayTitle = (splitString[i]) ? displayTitle && listItem[displayListKey[i]].toLowerCase().indexOf(splitString[i].toLowerCase().trim()) > -1 : displayTitle;
\r
8283 angular.forEach(displayListKey, function(value) {
\r
8284 if (!displayTitle) {
\r
8285 displayTitle = listItem[value];
\r
8287 displayTitle = displayTitle + (listItem[value] ? searchSeperator + ' ' + listItem[value] : '');
\r
8291 return displayTitle;
\r
8293 angular.forEach(list, function(listItem) {
\r
8294 var splitString = str.indexOf(searchSeperator) > -1 ? str.split(searchSeperator) : false;
\r
8295 var displayList = conditionCheck(searchSeperator, listItem, displayListKey, splitString)
\r
8296 for (var i = 0; i < keyArray.length; i++) {
\r
8297 searchLabel = keyArray[i];
\r
8298 if (listItem[searchLabel]) {
\r
8299 if (isContainsSearch) {
\r
8300 var displaySearchList = listItem[searchLabel].toLowerCase().indexOf(str.toLowerCase()) > -1;
\r
8301 if (splitString.length > 1) {
\r
8302 displaySearchList = (splitString.length <= keyArray.length) ? displayList : false;
\r
8304 searchCondition = displaySearchList;
\r
8306 searchCondition = listItem[searchLabel].match(new RegExp('^' + str, 'gi'));
\r
8308 if (searchCondition) {
\r
8310 'title': conditionCheck(searchSeperator, listItem, displayListKey),
\r
8311 'valueObj': listItem
\r
8320 }]).directive('b2bSearchField', ['$filter', 'b2bFilterInputFilter', 'keymap', '$documentBind', '$isElement', '$document', 'events', '$timeout', function($filter, b2bFilterInput, keymap, $documentBind, $isElement, $document, events, $timeout) {
\r
8324 dataList: '=dropdownList',
\r
8325 onClickCallback: '&',
\r
8333 templateUrl: 'b2bTemplate/searchField/searchField.html',
\r
8334 controller: ['$scope', function($scope) {
\r
8335 this.searchKeyArray = [];
\r
8336 if ($scope.configObj.searchKeys) {
\r
8337 this.searchKeyArray = $scope.configObj.searchKeys;
\r
8339 if (angular.isUndefined($scope.disabled)) {
\r
8340 $scope.disabled = false;
\r
8342 this.triggerInput = function(searchString) {
\r
8343 $scope.originalInputModel = searchString;
\r
8344 if (searchString === '') {
\r
8345 $scope.currentIndex = -1;
\r
8346 $scope.filterList = [];
\r
8347 $scope.showListFlag = false;
\r
8348 } else if (searchString !== '' && !$scope.isFilterEnabled) {
\r
8349 $scope.filterList = $filter('b2bFilterInput')($scope.dataList, searchString, this.searchKeyArray, $scope.configObj.displayListDataKey, $scope.configObj.isContainsSearch, $scope.configObj.searchSeperator);
\r
8350 $scope.showListFlag = true;
\r
8353 this.denyRegex = function() {
\r
8354 return $scope.inputDeny;
\r
8357 link: function(scope, elem) {
\r
8358 scope.isFilterEnabled = false;
\r
8359 scope.showListFlag = false;
\r
8360 scope.currentIndex = -1;
\r
8361 scope.setCurrentIdx = function(idx) {
\r
8362 scope.currentIndex = idx;
\r
8364 scope.inputModel = scope.filterList[idx].title;
\r
8365 scope.objModel = scope.filterList[idx];
\r
8368 scope.isActive = function(index, dropdownLength) {
\r
8369 scope.dropdownLength = dropdownLength;
\r
8370 return scope.currentIndex === index;
\r
8372 scope.selectItem = function(idx) {
\r
8373 scope.setCurrentIdx(idx);
\r
8374 scope.onClickCallback({
\r
8375 value: scope.inputModel,
\r
8376 objValue: scope.objModel
\r
8378 scope.showListFlag = false;
\r
8379 $timeout(function() {
\r
8380 elem.find('div').find('input')[0].focus();
\r
8383 scope.startSearch = function() {
\r
8384 scope.onClickCallback({
\r
8385 value: scope.inputModel,
\r
8386 objValue: scope.objModel
\r
8389 scope.selectPrev = function() {
\r
8390 if (scope.currentIndex > 0 && scope.filterList.length > 0) {
\r
8391 scope.currentIndex = scope.currentIndex - 1;
\r
8392 scope.setCurrentIdx(scope.currentIndex);
\r
8393 } else if (scope.currentIndex === 0) {
\r
8394 scope.currentIndex = scope.currentIndex - 1;
\r
8395 scope.inputModel = scope.originalInputModel;
\r
8396 scope.isFilterEnabled = true;
\r
8399 scope.selectNext = function() {
\r
8400 if (scope.currentIndex < scope.configObj.noOfItemsDisplay - 1) {
\r
8401 if (scope.currentIndex < scope.filterList.length - 1) {
\r
8402 scope.currentIndex = scope.currentIndex + 1;
\r
8403 scope.setCurrentIdx(scope.currentIndex);
\r
8407 scope.selectCurrent = function() {
\r
8408 scope.selectItem(scope.currentIndex);
\r
8410 scope.selectionIndex = function(e) {
\r
8411 switch (e.keyCode) {
\r
8412 case keymap.KEY.DOWN:
\r
8413 events.preventDefault(e);
\r
8414 scope.isFilterEnabled = true;
\r
8415 scope.selectNext();
\r
8417 case keymap.KEY.UP:
\r
8418 events.preventDefault(e);
\r
8419 scope.isFilterEnabled = true;
\r
8420 scope.selectPrev();
\r
8422 case keymap.KEY.ENTER:
\r
8423 events.preventDefault(e);
\r
8424 scope.isFilterEnabled = true;
\r
8425 scope.selectCurrent();
\r
8427 case keymap.KEY.ESC:
\r
8428 events.preventDefault(e);
\r
8429 scope.isFilterEnabled = false;
\r
8430 scope.showListFlag = false;
\r
8431 scope.inputModel = '';
\r
8434 scope.isFilterEnabled = false;
\r
8437 if (elem[0].querySelector('.filtercontainer')) {
\r
8438 elem[0].querySelector('.filtercontainer').scrollTop = (scope.currentIndex - 1) * 35;
\r
8441 scope.$watch('filterList', function(newVal, oldVal) {
\r
8442 if (newVal !== oldVal) {
\r
8443 scope.currentIndex = -1;
\r
8446 scope.blurInput = function() {
\r
8447 $timeout(function() {
\r
8448 scope.showListFlag = false;
\r
8451 var outsideClick = function(e) {
\r
8452 var isElement = $isElement(angular.element(e.target), elem.find('ul').eq(0), $document);
\r
8453 if (!isElement && document.activeElement.tagName.toLowerCase() !== 'input') {
\r
8454 scope.showListFlag = false;
\r
8458 $documentBind.click('showListFlag', outsideClick, scope);
\r
8462 .directive('b2bSearchInput', [function() {
\r
8465 require: ['ngModel', '^b2bSearchField'],
\r
8466 link: function(scope, elem, attr, ctrl) {
\r
8467 var ngModelCtrl = ctrl[0];
\r
8468 var attSearchBarCtrl = ctrl[1];
\r
8469 var REGEX = ctrl[1].denyRegex();
\r
8470 var parser = function(viewValue) {
\r
8471 attSearchBarCtrl.triggerInput(viewValue);
\r
8474 ngModelCtrl.$parsers.push(parser);
\r
8476 if (REGEX !== undefined || REGEX !== '') {
\r
8477 elem.bind('input', function() {
\r
8478 var inputString = ngModelCtrl.$viewValue && ngModelCtrl.$viewValue.replace(REGEX, '');
\r
8479 if (inputString !== ngModelCtrl.$viewValue) {
\r
8480 ngModelCtrl.$setViewValue(inputString);
\r
8481 ngModelCtrl.$render();
\r
8491 * @ngdoc directive
\r
8492 * @name Buttons, links & UI controls.att:Seek bar
\r
8495 * <file src="src/seekBar/docs/readme.md" />
\r
8498 * Horizontal Seek Bar
\r
8499 * <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
8501 * Vertical Seek Bar
\r
8502 * <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
8505 <section id="code">
\r
8506 <b>HTML + AngularJS</b>
\r
8507 <example module="b2b.att">
\r
8508 <file src="src/seekBar/docs/demo.html" />
\r
8509 <file src="src/seekBar/docs/demo.js" />
\r
8514 angular.module('b2b.att.seekBar', ['b2b.att.utilities','b2b.att.position'])
\r
8515 .constant('b2bSeekBarConfig', {
\r
8521 .directive('b2bSeekBar', ['$documentBind', 'events', 'b2bSeekBarConfig', 'keymap', '$compile', function($documentBind, events, b2bSeekBarConfig, keymap, $compile) {
\r
8525 require: 'ngModel',
\r
8526 templateUrl: 'b2bTemplate/seekBar/seekBar.html',
\r
8531 link: function(scope, elm, attr, ngModelCtrl) {
\r
8532 scope.isDragging = false;
\r
8533 scope.verticalSeekBar = false;
\r
8536 var step = b2bSeekBarConfig.step;
\r
8537 var skipInterval = b2bSeekBarConfig.skipInterval;
\r
8538 var knob = angular.element(elm[0].querySelector('.b2b-seek-bar-knob-container'));
\r
8539 var seekBarKnob = angular.element(knob[0].querySelector('.b2b-seek-bar-knob'));
\r
8540 var trackContainer = angular.element(elm[0].querySelector('.b2b-seek-bar-track-container'));
\r
8541 var trackFill = angular.element(elm[0].querySelector('.b2b-seek-bar-track-fill'));
\r
8542 var trackContainerRect = {};
\r
8544 var trackFillOrderPositioning;
\r
8546 if (angular.isDefined(attr.vertical)) {
\r
8547 scope.verticalSeekBar = true;
\r
8548 axisPosition = "clientY";
\r
8551 scope.verticalSeekBar = false;
\r
8552 axisPosition = "clientX";
\r
8554 var getValidStep = function(val) {
\r
8555 val = parseFloat(val);
\r
8556 // in case $modelValue came in string number
\r
8557 if (angular.isNumber(val)) {
\r
8558 val = Math.round((val - min) / step) * step + min;
\r
8559 return Math.round(val * 1000) / 1000;
\r
8563 var getPositionToPercent = function(x) {
\r
8564 if (scope.verticalSeekBar) {
\r
8565 return Math.max(0, Math.min(1, (trackContainerRect.bottom - x) / (trackFillOrderPositioning)));
\r
8568 return Math.max(0, Math.min(1, (x - trackContainerRect.left) / (trackFillOrderPositioning)));
\r
8572 var getPercentToValue = function(percent) {
\r
8573 return (min + percent * (max - min));
\r
8576 var getValueToPercent = function(val) {
\r
8577 return (val - min) / (max - min);
\r
8580 var getValidMinMax = function(val) {
\r
8581 return Math.max(min, Math.min(max, val));
\r
8584 var updateTrackContainerRect = function() {
\r
8585 trackContainerRect = trackContainer[0].getBoundingClientRect();
\r
8586 if (scope.verticalSeekBar) {
\r
8587 if (!trackContainerRect.height) {
\r
8588 trackFillOrderPositioning = trackContainer[0].scrollHeight;
\r
8590 trackFillOrderPositioning = trackContainerRect.height;
\r
8594 if (!trackContainerRect.width) {
\r
8595 trackFillOrderPositioning = trackContainer[0].scrollWidth;
\r
8597 trackFillOrderPositioning = trackContainerRect.width;
\r
8604 var updateKnobPosition = function(percent) {
\r
8605 var percentStr = (percent * 100) + '%';
\r
8606 if (scope.verticalSeekBar) {
\r
8607 knob.css('bottom', percentStr);
\r
8608 trackFill.css('height', percentStr);
\r
8611 knob.css('left', percentStr);
\r
8612 trackFill.css('width', percentStr);
\r
8616 var modelRenderer = function() {
\r
8617 if (isNaN(ngModelCtrl.$viewValue)) {
\r
8618 ngModelCtrl.$viewValue = ngModelCtrl.$modelValue || min;
\r
8621 var viewVal = ngModelCtrl.$viewValue;
\r
8622 scope.currentModelValue = viewVal;
\r
8624 //wait for min, max and step to be set then only update UI to avoid NaN on percent calculation
\r
8625 if ((min || min === 0) && max && step) {
\r
8626 updateKnobPosition(getValueToPercent(viewVal));
\r
8630 var setModelValue = function(val) {
\r
8631 scope.currentModelValue = getValidMinMax(getValidStep(val));
\r
8632 ngModelCtrl.$setViewValue(scope.currentModelValue);
\r
8635 var updateMin = function(val) {
\r
8636 min = parseFloat(val);
\r
8638 min = b2bSeekBarConfig.min;
\r
8643 var updateMax = function(val) {
\r
8644 max = parseFloat(val);
\r
8646 max = b2bSeekBarConfig.max;
\r
8651 var updateStep = function(val) {
\r
8652 step = parseFloat(val);
\r
8653 if (!attr['skipInterval']) {
\r
8654 skipInterval = step;
\r
8658 var updateSkipInterval = function(val) {
\r
8659 skipInterval = step * Math.ceil(val / (step!==0?step:1));
\r
8662 angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(b2bSeekBarConfig.min);
\r
8663 angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(b2bSeekBarConfig.max);
\r
8664 if (angular.isDefined(attr.step)) {
\r
8665 attr.$observe('step', updateStep);
\r
8667 if (angular.isDefined(attr.skipInterval)) {
\r
8668 attr.$observe('skipInterval', updateSkipInterval);
\r
8670 scope.currentModelValue = getValidMinMax(getValidStep(min));
\r
8671 var onMouseDown = function(e) {
\r
8672 switch (e.which) {
\r
8674 // left mouse button
\r
8678 // right or middle mouse button
\r
8683 scope.isDragging = true;
\r
8684 seekBarKnob[0].focus();
\r
8685 updateTrackContainerRect();
\r
8686 if (attr['onDragInit']) {
\r
8687 scope.onDragInit();
\r
8689 events.stopPropagation(e);
\r
8690 events.preventDefault(e);
\r
8691 scope.$apply(function() {
\r
8692 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
\r
8696 var onMouseUp = function() {
\r
8698 if (attr['onDragEnd']) {
\r
8699 scope.onDragEnd();
\r
8701 scope.isDragging = false;
\r
8705 var onMouseMove = function(e) {
\r
8706 if (scope.isDragging) {
\r
8707 events.stopPropagation(e);
\r
8708 events.preventDefault(e);
\r
8710 scope.$apply(function() {
\r
8711 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
\r
8716 function onKeyDown(e) {
\r
8717 if (!(e.keyCode)) {
\r
8718 e.keyCode = e.which;
\r
8721 switch (e.keyCode) {
\r
8722 case keymap.KEY.LEFT:
\r
8723 if (!scope.verticalSeekBar) {
\r
8724 updateStep = -skipInterval;
\r
8727 case keymap.KEY.RIGHT:
\r
8728 if (!scope.verticalSeekBar) {
\r
8729 updateStep = skipInterval;
\r
8732 case keymap.KEY.UP:
\r
8733 if (scope.verticalSeekBar) {
\r
8734 updateStep = skipInterval;
\r
8737 case keymap.KEY.DOWN:
\r
8738 if (scope.verticalSeekBar) {
\r
8739 updateStep = -skipInterval;
\r
8747 events.stopPropagation(e);
\r
8748 events.preventDefault(e);
\r
8749 scope.$apply(function() {
\r
8750 setModelValue(ngModelCtrl.$viewValue + updateStep);
\r
8752 if (attr['onDragEnd']) {
\r
8753 scope.onDragEnd();
\r
8758 elm.on('keydown', onKeyDown);
\r
8759 elm.on('mousedown', onMouseDown);
\r
8761 $documentBind.event('mousemove', 'isDragging', onMouseMove, scope, true, 0);
\r
8762 $documentBind.event('mouseup', 'isDragging', onMouseUp, scope, true, 0);
\r
8764 ngModelCtrl.$render = function() {
\r
8765 if (!scope.isDragging) {
\r
8769 ngModelCtrl.$viewChangeListeners.push(modelRenderer);
\r
8770 ngModelCtrl.$formatters.push(getValidMinMax);
\r
8771 ngModelCtrl.$formatters.push(getValidStep);
\r
8776 * @ngdoc directive
\r
8777 * @name Forms.att:selectorModule
\r
8780 * <file src="src/selectorModule/docs/readme.md" />
\r
8783 * <select name="myNameBig" type="large" b2b-dropdown ng-model="Controller Variable here">
\r
8784 * <option b2b-dropdown-list option-repeat="option data here" imgsrc="image path" value="value">List Text</option>
\r
8787 * <select name="myNameBig" type="large" b2b-dropdown ng-model="Controller Variable here">
\r
8788 * <option b2b-dropdown-list option-repeat="option data here" imgsrc="image path" value="value">List Text</option>
\r
8791 * <select name="myNameBig" b2b-dropdown ng-model="Controller Variable here">
\r
8792 * <optgroup b2b-dropdown-group label="Group Label here">
\r
8793 * <option b2b-dropdown-list option-repeat="option data here" imgsrc="image path" value="value">List Text</option>
\r
8798 * <section id="code">
\r
8799 <example module="b2b.att">
\r
8800 <file src="src/selectorModule/docs/demo.html" />
\r
8801 <file src="src/selectorModule/docs/demo.js" />
\r
8806 angular.module('b2b.att.selectorModule', ['b2b.att.dropdowns']);
\r
8808 * @ngdoc directive
\r
8809 * @name Layouts.att:separators
\r
8812 * <file src="src/separators/docs/readme.md" />
\r
8818 * <section id="code">
\r
8819 <b>HTML + AngularJS</b>
\r
8820 <example module="b2b.att">
\r
8821 <file src="src/separators/docs/demo.html" />
\r
8822 <file src="src/separators/docs/demo.js" />
\r
8828 angular.module('b2b.att.separators', []);
\r
8830 * @ngdoc directive
\r
8831 * @name Buttons, links & UI controls.att:slider
\r
8834 * <file src="src/slider/docs/readme.md" />
\r
8837 * <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
8840 <section id="code">
\r
8841 <b>HTML + AngularJS</b>
\r
8842 <example module="b2b.att">
\r
8843 <file src="src/slider/docs/demo.html" />
\r
8844 <file src="src/slider/docs/demo.js" />
\r
8849 angular.module('b2b.att.slider', ['b2b.att.utilities'])
\r
8850 .constant('SliderConfig', {
\r
8856 .directive('b2bSlider', ['$documentBind', 'SliderConfig', 'keymap', '$compile', '$log', function($documentBind, SliderConfig, keymap, $compile, $log) {
\r
8860 require: 'ngModel',
\r
8861 templateUrl: 'b2bTemplate/slider/slider.html',
\r
8865 trackFillColor: '=?',
\r
8866 preAriaLabel: '=?',
\r
8867 postAriaLabel: '=?',
\r
8868 onRenderEnd: '&?',
\r
8869 sliderSnapPoints: '=?',
\r
8870 customAriaLabel: '=?',
\r
8873 link: function(scope, elm, attr, ngModelCtrl) {
\r
8874 scope.isDragging = false;
\r
8875 scope.verticalSlider = false;
\r
8878 var step = SliderConfig.step;
\r
8879 var skipInterval = SliderConfig.skipInterval;
\r
8880 var knob = angular.element(elm[0].querySelector('.slider-knob-container'));
\r
8881 var sliderKnob = angular.element(knob[0].querySelector('.slider-knob'));
\r
8882 var trackContainer = angular.element(elm[0].querySelector('.slider-track-container'));
\r
8883 var trackFill = angular.element(elm[0].querySelector('.slider-track-fill'));
\r
8884 var trackContainerRect = {};
\r
8885 var axisPosition = "clientX";
\r
8886 var trackFillOrderPositioning;
\r
8888 //Forcefully disabling the vertical Slider code.
\r
8889 if (angular.isDefined(attr.vertical)) {
\r
8890 scope.verticalSlider = true;
\r
8891 axisPosition = "clientY";
\r
8894 if (angular.isDefined(scope.noAriaLabel) && scope.noAriaLabel !== '') {
\r
8895 $log.warn('no-aria-label has been deprecated. This will be removed in v0.6.0.');
\r
8897 if (angular.isDefined(scope.preAriaLabel) && scope.preAriaLabel !== '') {
\r
8898 $log.warn('pre-aria-label has been deprecated. Please use label-id instead. This will be removed in v0.6.0.');
\r
8900 if (angular.isDefined(scope.customAriaLabel) && scope.customAriaLabel !== '') {
\r
8901 $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
8904 var binarySearchNearest = function (num, arr) {
\r
8907 var hi = arr.length - 1;
\r
8909 while (hi - lo > 1) {
\r
8910 mid = Math.floor((lo + hi) / 2);
\r
8911 if (arr[mid] < num) {
\r
8917 if (num - arr[lo] < arr[hi] - num) {
\r
8923 var getValidStep = function(val) {
\r
8924 val = parseFloat(val);
\r
8925 // in case $modelValue came in string number
\r
8926 if (!isNaN(val)) {
\r
8928 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
\r
8929 val = binarySearchNearest(val, scope.sliderSnapPoints);
\r
8932 val = Math.round((val - min) / step) * step + min;
\r
8935 return Math.round(val * 1000) / 1000;
\r
8939 var getPositionToPercent = function(x) {
\r
8940 if (scope.verticalSlider) {
\r
8941 return Math.max(0, Math.min(1, (trackContainerRect.bottom - x) / (trackFillOrderPositioning)));
\r
8944 return Math.max(0, Math.min(1, (x - trackContainerRect.left) / (trackFillOrderPositioning)));
\r
8948 var getPercentToValue = function(percent) {
\r
8949 return (min + percent * (max - min));
\r
8952 var getValueToPercent = function(val) {
\r
8953 return (val - min) / (max - min);
\r
8956 var getValidMinMax = function(val) {
\r
8957 return Math.max(min, Math.min(max, val));
\r
8960 var updateTrackContainerRect = function() {
\r
8961 trackContainerRect = trackContainer[0].getBoundingClientRect();
\r
8962 if (scope.verticalSlider) {
\r
8963 if (!trackContainerRect.height) {
\r
8964 trackFillOrderPositioning = trackContainer[0].scrollHeight;
\r
8966 trackFillOrderPositioning = trackContainerRect.height;
\r
8970 if (!trackContainerRect.width) {
\r
8971 trackFillOrderPositioning = trackContainer[0].scrollWidth;
\r
8973 trackFillOrderPositioning = trackContainerRect.width;
\r
8980 var updateKnobPosition = function(percent) {
\r
8981 var percentStr = (percent * 100) + '%';
\r
8982 if (scope.verticalSlider) {
\r
8983 knob.css('bottom', percentStr);
\r
8984 trackFill.css('height', percentStr);
\r
8987 knob.css('left', percentStr);
\r
8988 trackFill.css('width', percentStr);
\r
8992 var modelRenderer = function() {
\r
8994 if(attr['disabled']){
\r
8998 if (isNaN(ngModelCtrl.$viewValue)) {
\r
8999 ngModelCtrl.$viewValue = ngModelCtrl.$modelValue || min;
\r
9002 var viewVal = ngModelCtrl.$viewValue;
\r
9003 scope.currentModelValue = viewVal;
\r
9005 //wait for min, max and step to be set then only update UI to avoid NaN on percent calculation
\r
9006 if ((min || min === 0) && max && step) {
\r
9007 updateKnobPosition(getValueToPercent(viewVal));
\r
9011 var setModelValue = function(val) {
\r
9012 scope.currentModelValue = getValidMinMax(getValidStep(val));
\r
9013 ngModelCtrl.$setViewValue(scope.currentModelValue);
\r
9016 var updateMin = function(val) {
\r
9017 min = parseFloat(val);
\r
9019 min = SliderConfig.min;
\r
9025 var updateMax = function(val) {
\r
9026 max = parseFloat(val);
\r
9028 max = SliderConfig.max;
\r
9034 var updateStep = function(val) {
\r
9035 step = parseFloat(val);
\r
9036 if (!attr['skipInterval']) {
\r
9037 skipInterval = step;
\r
9041 var updateSkipInterval = function(val) {
\r
9042 skipInterval = step * Math.ceil(val / (step!==0?step:1));
\r
9045 angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(SliderConfig.min);
\r
9046 angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(SliderConfig.max);
\r
9047 if (angular.isDefined(attr.step)) {
\r
9048 attr.$observe('step', updateStep);
\r
9050 if (angular.isDefined(attr.skipInterval)) {
\r
9051 attr.$observe('skipInterval', updateSkipInterval);
\r
9053 scope.currentModelValue = getValidMinMax(getValidStep(min));
\r
9054 var onMouseDown = function(e) {
\r
9056 if(attr['disabled']){
\r
9060 switch (e.which) {
\r
9062 // left mouse button
\r
9066 // right or middle mouse button
\r
9070 scope.isDragging = true;
\r
9071 sliderKnob[0].focus();
\r
9072 updateTrackContainerRect();
\r
9073 if (attr['onDragInit']) {
\r
9074 scope.onDragInit();
\r
9076 e.stopPropagation();
\r
9077 e.preventDefault();
\r
9078 scope.$apply(function() {
\r
9079 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
\r
9083 var onMouseUp = function() {
\r
9085 if (attr['onDragEnd']) {
\r
9086 scope.onDragEnd();
\r
9088 scope.isDragging = false;
\r
9092 var onMouseMove = function(e) {
\r
9093 if (scope.isDragging) {
\r
9094 e.stopPropagation();
\r
9095 e.preventDefault();
\r
9097 scope.$apply(function() {
\r
9098 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
\r
9103 function onKeyDown(e) {
\r
9104 if (!(e.keyCode)) {
\r
9105 e.keyCode = e.which;
\r
9108 switch (e.keyCode) {
\r
9109 case keymap.KEY.DOWN:
\r
9110 case keymap.KEY.LEFT:
\r
9111 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
\r
9112 var currentIndex = scope.sliderSnapPoints.indexOf(ngModelCtrl.$viewValue);
\r
9113 if (currentIndex > 0) {
\r
9116 updateStep = scope.sliderSnapPoints[currentIndex];
\r
9119 updateStep = ngModelCtrl.$viewValue - skipInterval;
\r
9122 case keymap.KEY.UP:
\r
9123 case keymap.KEY.RIGHT:
\r
9124 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
\r
9125 var currentIndex = scope.sliderSnapPoints.indexOf(ngModelCtrl.$viewValue);
\r
9126 if (currentIndex < scope.sliderSnapPoints.length-1) {
\r
9129 updateStep = scope.sliderSnapPoints[currentIndex];
\r
9132 updateStep = ngModelCtrl.$viewValue + skipInterval;
\r
9135 case keymap.KEY.END:
\r
9136 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
\r
9137 currentIndex = scope.sliderSnapPoints.length-1;
\r
9138 updateStep = scope.sliderSnapPoints[currentIndex];
\r
9140 setModelValue(scope.max);
\r
9142 e.preventDefault();
\r
9143 e.stopPropagation();
\r
9145 case keymap.KEY.HOME:
\r
9146 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
\r
9148 updateStep = scope.sliderSnapPoints[currentIndex];
\r
9150 setModelValue(scope.min);
\r
9152 e.preventDefault();
\r
9153 e.stopPropagation();
\r
9159 if (angular.isNumber(updateStep) && !attr['disabled']) {
\r
9160 e.stopPropagation();
\r
9161 e.preventDefault();
\r
9162 scope.$apply(function() {
\r
9163 setModelValue(updateStep);
\r
9165 if (attr['onDragEnd']) {
\r
9166 scope.onDragEnd();
\r
9171 elm.on('keydown', onKeyDown);
\r
9172 elm.on('mousedown', onMouseDown);
\r
9174 $documentBind.event('mousemove', 'isDragging', onMouseMove, scope, true, 0);
\r
9175 $documentBind.event('mouseup', 'isDragging', onMouseUp, scope, true, 0);
\r
9177 attr.$observe('disabled', function (disabled) {
\r
9179 sliderKnob.removeAttr('tabindex');
\r
9181 sliderKnob.attr('tabindex', '0');
\r
9185 elm.toggleClass("slider-disabled", disabled);
\r
9187 if (angular.isDefined(attr.hideDisabledKnob)) {
\r
9188 scope.hideKnob = disabled;
\r
9192 ngModelCtrl.$render = function() {
\r
9193 if (!scope.isDragging) {
\r
9195 if (attr['onRenderEnd'] && !attr['disabled']) {
\r
9196 scope.onRenderEnd({currentModelValue: scope.currentModelValue});
\r
9200 ngModelCtrl.$viewChangeListeners.push(modelRenderer);
\r
9201 ngModelCtrl.$formatters.push(getValidMinMax);
\r
9202 ngModelCtrl.$formatters.push(getValidStep);
\r
9207 * @ngdoc directive
\r
9208 * @name Forms.att:spinButton
\r
9210 * @param {String} spin-button-id - An ID for the input field
\r
9211 * @param {Integer} min - Minimum value for the input
\r
9212 * @param {Integer} max - Maximum value for the input
\r
9213 * @param {Integer} step - Value by which input field increments or decrements on up/down arrow keys
\r
9214 * @param {Integer} page-step - Value by which input field increments or decrements on page up/down keys
\r
9215 * @param {boolean} input-model-key - Default value for input field
\r
9216 * @param {boolean} disabled-flag - A boolean that triggers directive once the min or max value has reached
\r
9219 * <file src="src/spinButton/docs/readme.md" />
\r
9222 * <section id="code">
\r
9223 <example module="b2b.att">
\r
9224 <file src="src/spinButton/docs/demo.html" />
\r
9225 <file src="src/spinButton/docs/demo.js" />
\r
9230 angular.module('b2b.att.spinButton', ['b2b.att.utilities'])
\r
9231 .constant('b2bSpinButtonConfig', {
\r
9236 inputModelKey: 'value',
\r
9237 disabledFlag: false
\r
9239 .directive('b2bSpinButton', ['keymap', 'b2bSpinButtonConfig', 'b2bUserAgent', function (keymap, b2bSpinButtonConfig, userAgent) {
\r
9242 require: '?ngModel',
\r
9243 transclude: false,
\r
9249 pageStep: '=pageStep',
\r
9250 spinButtonId: '@',
\r
9251 inputValue: '=ngModel',
\r
9252 inputModelKey: '@',
\r
9253 disabledFlag: "=?"
\r
9255 templateUrl: 'b2bTemplate/spinButton/spinButton.html',
\r
9256 controller: ['$scope', '$element', '$attrs', function (scope, element, attrs) {
\r
9258 scope.isMobile = userAgent.isMobile();
\r
9259 scope.notMobile = userAgent.notMobile();
\r
9261 scope.min = attrs.min ? scope.min : b2bSpinButtonConfig.min;
\r
9262 scope.max = attrs.max ? scope.max : b2bSpinButtonConfig.max;
\r
9263 scope.step = attrs.step ? scope.step : b2bSpinButtonConfig.step;
\r
9264 scope.pageStep = attrs.pageStep ? scope.pageStep : b2bSpinButtonConfig.pageStep;
\r
9265 scope.inputModelKey = attrs.inputModelKey ? scope.inputModelKey : b2bSpinButtonConfig.inputModelKey;
\r
9266 scope.disabledFlag = attrs.disabledFlag ? scope.disabledFlag : b2bSpinButtonConfig.disabledFlag;
\r
9268 if (scope.min < 0) {
\r
9271 if (scope.max > 999) {
\r
9275 scope.isPlusDisabled = function () {
\r
9276 return (scope.disabledFlag || scope.inputValue[scope.inputModelKey] >= scope.max);
\r
9278 scope.isMinusDisabled = function () {
\r
9279 return (scope.disabledFlag || scope.inputValue[scope.inputModelKey] <= scope.min);
\r
9282 scope.getValidateInputValue = function (value) {
\r
9283 if (value <= scope.min) {
\r
9285 } else if (value >= scope.max) {
\r
9292 scope.plus = function () {
\r
9293 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) + scope.step);
\r
9295 scope.minus = function () {
\r
9296 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) - scope.step);
\r
9298 scope.pagePlus = function () {
\r
9299 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) + scope.pageStep);
\r
9301 scope.pageMinus = function () {
\r
9302 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) - scope.pageStep);
\r
9306 link: function (scope, elem) {
\r
9308 if (scope.notMobile) {
\r
9309 angular.element(elem).find('input').attr('aria-live', 'off');
\r
9310 angular.element(elem).find('input').attr('role', 'spinbutton');
\r
9313 elem.find('input').bind('keydown', function (e) {
\r
9314 if (e.keyCode === keymap.KEY.UP) {
\r
9316 } else if (e.keyCode === keymap.KEY.DOWN){
\r
9318 } else if (e.keyCode === keymap.KEY.HOME) {
\r
9319 scope.inputValue[scope.inputModelKey] = parseInt(scope.min);
\r
9320 } else if (e.keyCode === keymap.KEY.END) {
\r
9321 scope.inputValue[scope.inputModelKey] = parseInt(scope.max);
\r
9322 } else if (e.keyCode === keymap.KEY.PAGE_UP) {
\r
9324 } else if (e.keyCode === keymap.KEY.PAGE_DOWN) {
\r
9325 scope.pageMinus();
\r
9330 elem.find('input').bind('keyup', function () {
\r
9331 if (scope.inputValue[scope.inputModelKey] === null ||
\r
9332 scope.inputValue[scope.inputModelKey] === '' ||
\r
9333 scope.inputValue[scope.inputModelKey] < scope.min) {
\r
9334 scope.inputValue[scope.inputModelKey] = scope.min;
\r
9336 } else if (angular.isUndefined(scope.inputValue[scope.inputModelKey]) ||
\r
9337 scope.inputValue[scope.inputModelKey] > scope.max) {
\r
9338 scope.inputValue[scope.inputModelKey] = scope.max;
\r
9343 scope.focusInputSpinButton = function (evt) {
\r
9344 evt.preventDefault();
\r
9345 if (scope.notMobile) {
\r
9346 elem[0].querySelector('input').focus();
\r
9354 * @ngdoc directive
\r
9355 * @name Template.att:Static Route
\r
9358 * <file src="src/staticRouteTemplate/docs/readme.md" />
\r
9361 * <section id="code">
\r
9362 <example module="b2b.att">
\r
9363 <file src="src/staticRouteTemplate/docs/demo.html" />
\r
9364 <file src="src/staticRouteTemplate/docs/demo.js" />
\r
9369 angular.module('b2b.att.staticRouteTemplate', ['b2b.att.utilities'])
\r
9372 * @ngdoc directive
\r
9373 * @name Progress & usage indicators.att:statusTracker
\r
9376 * @param {array} statuses - An array of status objects
\r
9378 * <file src="src/statusTracker/docs/readme.md" />
\r
9382 <div ng-controller="statusTrackerController">
\r
9383 <b2b-status-tracker statuses="statusObject"></b2b-status-tracker>
\r
9387 <section id="code">
\r
9388 <b>HTML + AngularJS</b>
\r
9389 <example module="b2b.att">
\r
9390 <file src="src/statusTracker/docs/demo.html" />
\r
9391 <file src="src/statusTracker/docs/demo.js" />
\r
9396 angular.module('b2b.att.statusTracker', ['b2b.att.utilities'])
\r
9397 .constant('b2bStatusTrackerConfig', {
\r
9400 .directive('b2bStatusTracker', ['b2bStatusTrackerConfig', function(b2bStatusTrackerConfig) {
\r
9403 transclude: false,
\r
9408 templateUrl: function(scope) {
\r
9409 return 'b2bTemplate/statusTracker/statusTracker.html';
\r
9411 link: function(scope, element, attr) {
\r
9412 scope.currentViewIndex = 0;
\r
9413 scope.b2bStatusTrackerConfig = b2bStatusTrackerConfig;
\r
9415 scope.nextStatus = function() {
\r
9416 if (scope.currentViewIndex+1 <= scope.statuses.length) {
\r
9417 scope.currentViewIndex++;
\r
9420 scope.previousStatus = function() {
\r
9421 if (scope.currentViewIndex-1 >= 0) {
\r
9422 scope.currentViewIndex--;
\r
9425 scope.isInViewport = function(index) {
\r
9426 return (index < scope.currentViewIndex+3 && index >= scope.currentViewIndex); // && index > scope.currentViewIndex-2
\r
9428 scope.currentStatus = function(index) {
\r
9429 if(index != undefined){
\r
9430 if(!scope.statuses[index].complete) {
\r
9431 if(index > 0 && scope.statuses[index-1].complete) {
\r
9433 } else if(index == 0 && !scope.statuses[index].complete){
\r
9445 * @ngdoc directive
\r
9446 * @name Progress & usage indicators.att:stepTracker
\r
9449 * @param {array} stepsItemsObject - An array of step objects
\r
9450 * @param {Integer} currenIindex - This indicates the current running step
\r
9451 * @param {Integer} viewportIndex - This is optional. This can used to start the view port rather than 1 item.
\r
9453 * <file src="src/stepTracker/docs/readme.md" />
\r
9457 * <b2b-step-tracker steps-items-object="stepsObject" current-index="currentStepIndex" step-indicator-heading="stepHeading"></b2b-step-tracker>
\r
9461 <section id="code">
\r
9462 <b>HTML + AngularJS</b>
\r
9463 <example module="b2b.att">
\r
9464 <file src="src/stepTracker/docs/demo.html" />
\r
9465 <file src="src/stepTracker/docs/demo.js" />
\r
9469 angular.module('b2b.att.stepTracker', ['b2b.att.utilities'])
\r
9470 .constant('b2bStepTrackerConfig', {
\r
9473 .directive('b2bStepTracker', ['b2bStepTrackerConfig', function(b2bStepTrackerConfig) {
\r
9478 stepsItemsObject:"=",
\r
9480 viewportIndex:"=?"
\r
9482 templateUrl: 'b2bTemplate/stepTracker/stepTracker.html',
\r
9483 link: function(scope, ele, attr) {
\r
9484 if (angular.isDefined(scope.viewportIndex)) {
\r
9485 scope.currentViewIndex = scope.viewportIndex - 1;
\r
9487 scope.currentViewIndex = 0;
\r
9490 scope.b2bStepTrackerConfig = b2bStepTrackerConfig;
\r
9491 scope.nextStatus = function() {
\r
9492 if (scope.currentViewIndex+1 <= scope.stepsItemsObject.length) {
\r
9493 scope.currentViewIndex++;
\r
9496 scope.previousStatus = function() {
\r
9497 if (scope.currentViewIndex-1 >= 0) {
\r
9498 scope.currentViewIndex--;
\r
9501 scope.isInViewport = function(index) {
\r
9502 return (index < scope.currentViewIndex+b2bStepTrackerConfig.maxViewItems && index >= scope.currentViewIndex);
\r
9509 * @ngdoc directive
\r
9510 * @name Buttons, links & UI controls.att:switches
\r
9513 * <file src="src/switches/docs/readme.md" />
\r
9517 * <!-- On / Off Toggle switch -->
\r
9518 * <label for="switch1" class="controlled-text-wrap"> This is ON
\r
9519 * <input type="checkbox" role="checkbox" b2b-switches id="switch1" ng-model="switch1.value">
\r
9522 * <!-- On / Off Toggle switch and DISABLED -->
\r
9523 * <label for="switch2" class="controlled-text-wrap"> This is ON (disabled)
\r
9524 * <input type="checkbox" role="checkbox" b2b-switches id="switch2" ng-model="switch1.value" ng-disabled="true" >
\r
9529 * <section id="code">
\r
9530 <b>HTML + AngularJS</b>
\r
9531 <example module="b2b.att">
\r
9532 <file src="src/switches/docs/demo.js" />
\r
9533 <file src="src/switches/docs/demo.html" />
\r
9537 angular.module('b2b.att.switches', ['b2b.att.utilities'])
\r
9538 .directive('b2bSwitches', ['$compile', '$templateCache', 'keymap', 'events', function ($compile, $templateCache, keymap, events) {
\r
9541 require: ['ngModel'],
\r
9542 link: function (scope, element, attrs, ctrl) {
\r
9543 var ngModelController = ctrl[0];
\r
9545 element.parent().bind("keydown", function (e) {
\r
9546 if (!attrs.disabled && (e.keyCode === keymap.KEY.ENTER || e.keyCode === keymap.KEY.SPACE)) {
\r
9547 events.preventDefault(e);
\r
9548 ngModelController.$setViewValue(!ngModelController.$viewValue);
\r
9549 element.prop("checked", ngModelController.$viewValue);
\r
9553 element.wrap('<div class="btn-switch">');
\r
9554 //element.attr("tabindex", -1);
\r
9555 if (navigator.userAgent.match(/iphone/i)){
\r
9556 element.attr("aria-live", "polite");
\r
9559 element.removeAttr('aria-live');
\r
9562 var templateSwitch = angular.element($templateCache.get("b2bTemplate/switches/switches.html"));
\r
9563 if (angular.isDefined(attrs.typeSpanish)) {
\r
9564 templateSwitch = angular.element($templateCache.get("b2bTemplate/switches/switches-spanish.html"));
\r
9567 templateSwitch = $compile(templateSwitch)(scope);
\r
9568 element.parent().append(templateSwitch);
\r
9570 element.bind("focus", function (e) {
\r
9571 element.parent().addClass('focused');
\r
9574 element.bind("blur", function (e) {
\r
9575 element.parent().removeClass('focused');
\r
9581 * @ngdoc directive
\r
9582 * @name Messages, modals & alerts.att:tableMessages
\r
9585 * <file src="src/tableMessages/docs/readme.md" />
\r
9588 <!-- no matching results -->
\r
9589 <b2b-table-message msg-type="'noMatchingResults'">
\r
9590 <p>No Matching Results</p>
\r
9591 </b2b-table-message>
\r
9593 <!-- info could not load -->
\r
9594 <b2b-table-message msg-type="'infoCouldNotLoad'" on-refresh-click="refreshClicked()">
\r
9595 </b2b-table-message>
\r
9597 <!-- magnify search -->
\r
9598 <b2b-table-message msg-type="'magnifySearch'">
\r
9599 </b2b-table-message>
\r
9601 <!-- loading data -->
\r
9602 <b2b-table-message msg-type="'loadingTable'">
\r
9603 <!-- custom html -->
\r
9604 <p>The data is currently loading...</p>
\r
9605 </b2b-table-message>
\r
9608 <section id="code">
\r
9609 <b>HTML + AngularJS</b>
\r
9610 <example module="b2b.att">
\r
9611 <file src="src/tableMessages/docs/demo.html" />
\r
9612 <file src="src/tableMessages/docs/demo.js" />
\r
9616 angular.module('b2b.att.tableMessages', [])
\r
9617 .directive('b2bTableMessage', [function() {
\r
9624 onRefreshClick: '&'
\r
9626 templateUrl: 'b2bTemplate/tableMessages/tableMessage.html',
\r
9627 link: function(scope) {
\r
9628 scope.refreshAction = function(evt) {
\r
9629 scope.onRefreshClick(evt);
\r
9636 * @ngdoc directive
\r
9637 * @name Tabs, tables & accordions.att:tableScrollbar
\r
9640 * <file src="src/tableScrollbar/docs/readme.md" />
\r
9644 <b2b-table-scrollbar>
\r
9646 <thead type="header">
\r
9648 <th role="columnheader" scope="col" key="Id" id="col1">Id</th>
\r
9652 <tbody type="body">
\r
9654 <td id="rowheader0" headers="col1">1002</td>
\r
9659 </b2b-table-scrollbar>
\r
9662 * <section id="code">
\r
9663 <example module="b2b.att">
\r
9664 <file src="src/tableScrollbar/docs/demo.html" />
\r
9665 <file src="src/tableScrollbar/docs/demo.js" />
\r
9670 angular.module('b2b.att.tableScrollbar', [])
\r
9671 .directive('b2bTableScrollbar', ['$timeout', function ($timeout) {
\r
9676 templateUrl: 'b2bTemplate/tableScrollbar/tableScrollbar.html',
\r
9677 link: function (scope, element, attrs, ctrl) {
\r
9678 var firstThWidth, firstTdWidth, firstColumnWidth, firstColumnHeight, trHeight = 0;
\r
9679 var pxToScroll = '';
\r
9680 var tableElement = element.find('table');
\r
9681 var thElements = element.find('th');
\r
9682 var tdElements = element.find('td');
\r
9683 var innerContainer = angular.element(element[0].querySelector('.b2b-table-inner-container'));
\r
9684 var outerContainer = angular.element(element[0].querySelector('.b2b-table-scrollbar'));
\r
9686 scope.disableLeft = true;
\r
9687 scope.disableRight = false;
\r
9689 if (angular.isDefined(thElements[0])) {
\r
9690 firstThWidth = thElements[0].offsetWidth;
\r
9692 if (angular.isDefined(tdElements[0])) {
\r
9693 firstTdWidth = tdElements[0].offsetWidth;
\r
9695 firstColumnWidth = (firstThWidth > firstTdWidth) ? firstThWidth : firstTdWidth;
\r
9697 innerContainer.css({
\r
9698 'padding-left': (firstColumnWidth + 2) + 'px'
\r
9701 angular.forEach(element.find('tr'), function (eachTr, index) {
\r
9702 trObject = angular.element(eachTr);
\r
9703 firstColumn = angular.element(trObject.children()[0]);
\r
9705 angular.element(firstColumn).css({
\r
9706 'margin-left': -(firstColumnWidth + 2) + 'px',
\r
9707 'width': (firstColumnWidth + 2) + 'px',
\r
9708 'position': 'absolute'
\r
9711 trHeight = trObject[0].offsetHeight;
\r
9712 firstColumnHeight = firstColumn[0].offsetHeight;
\r
9713 if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
\r
9714 firstColumnHeight += 1;
\r
9717 if (trHeight !== firstColumnHeight - 1) {
\r
9718 if (trHeight > firstColumnHeight) {
\r
9719 if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
\r
9722 angular.element(firstColumn).css({
\r
9723 'height': (trHeight + 1) + 'px'
\r
9726 angular.element(trObject).css({
\r
9727 'height': (firstColumnHeight - 1) + 'px'
\r
9734 pxToScroll = outerContainer[0].offsetWidth - firstColumnWidth;
\r
9736 scope.scrollLeft = function () {
\r
9737 innerContainer[0].scrollLeft = innerContainer[0].scrollLeft + 20 - pxToScroll;
\r
9740 scope.scrollRight = function () {
\r
9741 innerContainer[0].scrollLeft = innerContainer[0].scrollLeft + pxToScroll - 20;
\r
9744 scope.checkScrollArrows = function () {
\r
9745 if (innerContainer[0].scrollLeft == 0) {
\r
9746 scope.disableLeft = true;
\r
9748 scope.disableLeft = false;
\r
9751 if (((innerContainer[0].offsetWidth - firstColumnWidth) + innerContainer[0].scrollLeft) >= tableElement[0].offsetWidth) {
\r
9752 scope.disableRight = true;
\r
9754 scope.disableRight = false;
\r
9758 innerContainer.bind('scroll', function () {
\r
9759 $timeout(function () {
\r
9760 scope.checkScrollArrows();
\r
9768 * @ngdoc directive
\r
9769 * @name Tabs, tables & accordions.att:tables
\r
9772 * <file src="src/tables/docs/readme.md" />
\r
9777 <table b2b-table table-data="tableData" search-string="searchString">
\r
9778 <thead b2b-table-row type="header">
\r
9780 <th b2b-table-header key="requestId" default-sort="a" id="col1">Header 1</th>
\r
9781 <th b2b-table-header key="requestType" sortable="false" id="col2">Header 2</th>
\r
9784 <tbody b2b-table-row type="body" row-repeat="rowData in tableData">
\r
9786 <td b2b-table-body id="rowheader{{$index}}" headers="col1" ng-bind="rowData['requestId']"> </td>
\r
9787 <td b2b-table-body ng-bind="rowData['requestType']"></td>
\r
9793 * <section id="code">
\r
9794 <example module="b2b.att">
\r
9795 <file src="src/tables/docs/demo.html" />
\r
9796 <file src="src/tables/docs/demo.js" />
\r
9801 angular.module('b2b.att.tables', ['b2b.att.utilities'])
\r
9802 .constant('b2bTableConfig', {
\r
9803 defaultSortPattern: false, // true for descending & false for ascending
\r
9804 highlightSearchStringClass: 'tablesorter-search-highlight',
\r
9805 zebraStripCutOff: 6, // > zebraStripCutOff
\r
9806 tableBreakpoints: [ // breakpoints are >= min and < max
\r
9829 .directive('b2bTable', ['$filter', '$window', 'b2bTableConfig', '$timeout', function ($filter, $window, b2bTableConfig, $timeout) {
\r
9839 searchCategory: "=",
\r
9840 searchString: "=",
\r
9843 require: 'b2bTable',
\r
9844 templateUrl: 'b2bTemplate/tables/b2bTable.html',
\r
9845 controller: ['$scope', '$attrs', function ($scope, $attrs) {
\r
9846 this.headers = [];
\r
9847 this.currentSortIndex = null;
\r
9848 this.responsive = $scope.responsive = $attrs.responsive;
\r
9849 this.maxTableColumns = -1;
\r
9850 this.totalTableColums = 0;
\r
9851 this.active = $scope.active = false;
\r
9852 this.responsiveRowScopes = [];
\r
9853 this.hideColumnPriority = [];
\r
9854 this.hiddenColumn = [];
\r
9855 this.setIndex = function (headerScope, priority) {
\r
9856 this.headers.push(headerScope);
\r
9857 if (this.responsive) {
\r
9858 this.totalTableColums++;
\r
9859 if (!isNaN(priority)) {
\r
9860 this.hideColumnPriority[priority] = this.totalTableColums - 1;
\r
9862 this.hideColumnPriority[this.totalTableColums - 1] = this.totalTableColums - 1;
\r
9865 return this.totalTableColums - 1;
\r
9867 this.getIndex = function (headerName) {
\r
9868 for (var i = 0; i < this.headers.length; i++) {
\r
9869 if (this.headers[i].headerName === headerName) {
\r
9870 return this.headers[i].index;
\r
9875 this.setResponsiveRow = function (responsiveRowScope) {
\r
9876 this.responsiveRowScopes.push(responsiveRowScope);
\r
9878 $scope.nextSort = '';
\r
9879 this.sortData = function (columnIndex, reverse, externalSort) {
\r
9880 if ($scope.$parent && $scope.$parent !== undefined) {
\r
9881 $scope.$parent.columnIndex = columnIndex;
\r
9882 $scope.$parent.reverse = reverse;
\r
9884 this.currentSortIndex = columnIndex;
\r
9885 if (externalSort === true) {
\r
9887 $scope.nextSort = 'd'
\r
9889 $scope.nextSort = 'a'
\r
9892 $scope.currentPage = 1;
\r
9893 this.resetSortPattern();
\r
9895 this.getSearchString = function () {
\r
9896 return $scope.searchString;
\r
9898 this.resetSortPattern = function () {
\r
9899 for (var i = 0; i < this.headers.length; i++) {
\r
9900 var currentScope = this.headers[i];
\r
9901 if (currentScope.index !== this.currentSortIndex) {
\r
9902 currentScope.resetSortPattern();
\r
9907 $scope.$watch('nextSort', function (val) {
\r
9908 if ($scope.$parent && $scope.$parent !== undefined) {
\r
9909 $scope.$parent.nextSort = val;
\r
9914 link: function (scope, elem, attr, ctrl) {
\r
9915 scope.searchCriteria = {};
\r
9916 scope.tableBreakpoints = attr.tableConfig ? scope.$parent.$eval(attr.tableConfig) : angular.copy(b2bTableConfig.tableBreakpoints);
\r
9917 scope.$watchCollection('tableData', function (value) {
\r
9918 if (value && !isNaN(value.length)) {
\r
9919 scope.totalRows = value.length;
\r
9922 scope.$watch('currentPage', function (val) {
\r
9923 if (scope.$parent && scope.$parent !== undefined) {
\r
9924 scope.$parent.currentPage = val;
\r
9928 scope.$watch('viewPerPage', function (val) {
\r
9929 if (scope.$parent && scope.$parent !== undefined) {
\r
9930 scope.$parent.viewPerPage = val;
\r
9933 scope.$watch('totalRows', function (val) {
\r
9934 if (scope.$parent && scope.$parent !== undefined) {
\r
9935 if (val > b2bTableConfig.zebraStripCutOff) {
\r
9936 scope.$parent.zebraStripFlag = true;
\r
9938 scope.$parent.zebraStripFlag = false;
\r
9942 scope.$watch(function () {
\r
9943 return scope.totalRows / scope.viewPerPage;
\r
9944 }, function (value) {
\r
9945 if (!isNaN(value)) {
\r
9946 scope.totalPage = Math.ceil(value);
\r
9947 scope.currentPage = 1;
\r
9950 var searchValCheck = function (val) {
\r
9951 if (angular.isDefined(val) && val !== null && val !== "") {
\r
9955 var setSearchCriteria = function (v1, v2) {
\r
9956 if (searchValCheck(v1) && searchValCheck(v2)) {
\r
9957 var index = ctrl.getIndex(v2);
\r
9958 scope.searchCriteria = {};
\r
9959 if (index !== null) {
\r
9960 scope.searchCriteria[index] = v1;
\r
9962 } else if (searchValCheck(v1) && (!angular.isDefined(v2) || v2 === null || v2 === "")) {
\r
9963 scope.searchCriteria = {
\r
9967 scope.searchCriteria = {};
\r
9970 scope.$watch('searchCategory', function (newVal, oldVal) {
\r
9971 if (newVal !== oldVal) {
\r
9972 setSearchCriteria(scope.searchString, newVal);
\r
9975 scope.$watch('searchString', function (newVal, oldVal) {
\r
9976 if (newVal !== oldVal) {
\r
9977 setSearchCriteria(newVal, scope.searchCategory);
\r
9980 scope.$watchCollection('searchCriteria', function (val) {
\r
9981 if (scope.$parent && scope.$parent !== undefined) {
\r
9982 scope.$parent.searchCriteria = val;
\r
9984 scope.totalRows = scope.tableData && ($filter('filter')(scope.tableData, val, false)).length || 0;
\r
9985 scope.currentPage = 1;
\r
9987 var window = angular.element($window);
\r
9988 var findMaxTableColumns = function () {
\r
9990 windowWidth = $window.innerWidth;
\r
9991 ctrl.maxTableColumns = -1;
\r
9992 for (var i in scope.tableBreakpoints) {
\r
9993 if (windowWidth >= scope.tableBreakpoints[i].min && windowWidth < scope.tableBreakpoints[i].max) {
\r
9994 ctrl.maxTableColumns = scope.tableBreakpoints[i].columns;
\r
9998 if (ctrl.maxTableColumns > -1 && ctrl.totalTableColums > ctrl.maxTableColumns) {
\r
9999 ctrl.active = true;
\r
10001 ctrl.active = false;
\r
10003 for (var i in ctrl.responsiveRowScopes) {
\r
10004 ctrl.responsiveRowScopes[i].setActive(ctrl.active);
\r
10007 var findHiddenColumn = function () {
\r
10008 var columnDiffenence = ctrl.maxTableColumns > -1 ? ctrl.totalTableColums - ctrl.maxTableColumns : 0;
\r
10009 ctrl.hiddenColumn = [];
\r
10010 if (columnDiffenence > 0) {
\r
10011 var tempHideColumnPriority = angular.copy(ctrl.hideColumnPriority);
\r
10012 for (var i = 0; i < columnDiffenence; i++) {
\r
10013 ctrl.hiddenColumn.push(tempHideColumnPriority.pop());
\r
10017 var resizeListener = function () {
\r
10018 findMaxTableColumns();
\r
10019 findHiddenColumn();
\r
10021 if (ctrl.responsive) {
\r
10022 window.bind('resize', function () {
\r
10023 resizeListener();
\r
10026 $timeout(function () {
\r
10027 resizeListener();
\r
10033 .directive('b2bTableRow', [function () {
\r
10036 compile: function (elem, attr) {
\r
10037 if (attr.type === 'header') {
\r
10039 } else if (attr.type === 'body') {
\r
10040 var html = elem.children();
\r
10041 if (attr.rowRepeat) {
\r
10042 html.attr('ng-repeat', attr.rowRepeat.concat(" | orderBy : columnIndex : reverse | filter : searchCriteria : false "));
\r
10044 html.attr('ng-class', "{'odd': $odd && zebraStripFlag}");
\r
10045 html.attr('class', 'data-row');
\r
10046 html.attr('b2b-responsive-row', '{{$index}}');
\r
10047 elem.append(html);
\r
10052 .directive('b2bTableHeader', ['b2bTableConfig', function (b2bTableConfig) {
\r
10056 transclude: true,
\r
10059 defaultSort: '@',
\r
10062 require: '^b2bTable',
\r
10063 templateUrl: function (elem, attr) {
\r
10064 if (attr.sortable === 'false') {
\r
10065 return 'b2bTemplate/tables/b2bTableHeaderUnsortable.html';
\r
10067 return 'b2bTemplate/tables/b2bTableHeaderSortable.html';
\r
10070 link: function (scope, elem, attr, ctrl) {
\r
10071 var reverse = b2bTableConfig.defaultSortPattern;
\r
10072 scope.headerName = elem.text();
\r
10073 scope.headerId = elem.attr('id');
\r
10074 scope.sortPattern = null;
\r
10075 var priority = parseInt(attr.priority, 10);
\r
10076 scope.columnIndex = ctrl.setIndex(scope, priority);
\r
10078 scope.isHidden = function () {
\r
10079 return (ctrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
\r
10082 scope.$watch(function () {
\r
10083 return elem.text();
\r
10084 }, function (value) {
\r
10085 scope.headerName = value;
\r
10087 scope.sort = function (sortType) {
\r
10088 if (typeof sortType === 'boolean') {
\r
10089 reverse = sortType;
\r
10091 ctrl.sortData(scope.index, reverse, false);
\r
10092 scope.sortPattern = reverse ? 'descending' : 'ascending';
\r
10093 reverse = !reverse;
\r
10095 scope.$watch(function () {
\r
10096 return ctrl.currentSortIndex;
\r
10097 }, function (value) {
\r
10098 if (value !== scope.index) {
\r
10099 scope.sortPattern = null;
\r
10103 if (scope.sortable === undefined || scope.sortable === 'true' || scope.sortable === true) {
\r
10104 scope.sortable = 'true';
\r
10105 } else if (scope.sortable === false || scope.sortable === 'false') {
\r
10106 scope.sortable = 'false';
\r
10109 if (scope.sortable !== 'false') {
\r
10110 if (scope.defaultSort === 'A' || scope.defaultSort === 'a') {
\r
10111 scope.sort(false);
\r
10112 } else if (scope.defaultSort === 'D' || scope.defaultSort === 'd') {
\r
10113 scope.sort(true);
\r
10116 scope.resetSortPattern = function () {
\r
10117 reverse = b2bTableConfig.defaultSortPattern;
\r
10122 .directive('b2bResponsiveRow', ['$templateCache', '$timeout', '$compile', function ($templateCache, $timeout, $compile) {
\r
10125 require: '^b2bTable',
\r
10126 controller: ['$scope', function ($scope) {
\r
10127 this.rowValues = $scope.rowValues = [];
\r
10128 this.setRowValues = function (rowValue) {
\r
10129 this.rowValues.push(rowValue);
\r
10131 var columnIndexCounter = -1;
\r
10132 this.getIndex = function () {
\r
10133 columnIndexCounter++;
\r
10134 return columnIndexCounter;
\r
10137 link: function (scope, elem, attr, ctrl) {
\r
10138 if (ctrl.responsive) {
\r
10139 scope.rowIndex = attr.b2bResponsiveRow;
\r
10140 scope.active = false;
\r
10141 scope.expandFlag = false;
\r
10142 scope.headerValues = ctrl.headers;
\r
10143 ctrl.setResponsiveRow(scope);
\r
10144 var firstTd = elem.find('td').eq(0);
\r
10145 scope.setActive = function (activeFlag) {
\r
10146 scope.active = activeFlag;
\r
10147 if (scope.active) {
\r
10148 elem.addClass('has-button');
\r
10149 firstTd.attr('role', 'rowheader');
\r
10150 firstTd.parent().attr('role', 'row');
\r
10152 elem.removeClass('has-button');
\r
10153 firstTd.removeAttr('role');
\r
10154 firstTd.parent().removeAttr('role');
\r
10157 scope.toggleExpandFlag = function (expandFlag) {
\r
10158 if (angular.isDefined(expandFlag)) {
\r
10159 scope.expandFlag = expandFlag;
\r
10161 scope.expandFlag = !scope.expandFlag;
\r
10163 if (scope.expandFlag) {
\r
10164 elem.addClass('opened');
\r
10166 elem.removeClass('opened');
\r
10170 firstTd.attr('scope', 'row');
\r
10171 firstTd.addClass('col-1');
\r
10172 scope.$on('$destroy', function () {
\r
10173 elem.next().remove();
\r
10175 $timeout(function () {
\r
10176 scope.firstTdId = firstTd.attr('id');
\r
10177 var firstTdContent = firstTd.html();
\r
10178 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
10179 toggleButtonTemplate = $compile(toggleButtonTemplate)(scope);
\r
10180 firstTd.html('');
\r
10181 firstTd.prepend(toggleButtonTemplate);
\r
10183 var template = $templateCache.get('b2bTemplate/tables/b2bResponsiveRow.html');
\r
10184 template = $compile(template)(scope);
\r
10185 elem.after(template);
\r
10191 .directive('b2bResponsiveList', ['$templateCache', '$timeout', '$compile', function ($templateCache, $timeout, $compile) {
\r
10194 require: '^b2bTable',
\r
10195 link: function (scope, elem, attr, ctrl) {
\r
10196 scope.columnIndex = parseInt(attr.b2bResponsiveList, 10);
\r
10197 scope.isVisible = function () {
\r
10198 return (ctrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
\r
10203 .directive('b2bTableBody', ['$filter', '$timeout', 'b2bTableConfig', function ($filter, $timeout, b2bTableConfig) {
\r
10206 require: ['^b2bTable', '?^b2bResponsiveRow'],
\r
10209 transclude: true,
\r
10210 templateUrl: 'b2bTemplate/tables/b2bTableBody.html',
\r
10211 link: function (scope, elem, attr, ctrl) {
\r
10212 var b2bTableCtrl = ctrl[0];
\r
10213 var b2bResponsiveRowCtrl = ctrl[1];
\r
10214 var highlightSearchStringClass = b2bTableConfig.highlightSearchStringClass;
\r
10215 var searchString = "";
\r
10216 var wrapElement = function (elem) {
\r
10217 var text = elem.text();
\r
10218 elem.html($filter('b2bHighlight')(text, searchString, highlightSearchStringClass));
\r
10220 var traverse = function (elem) {
\r
10221 var innerHtml = elem.children();
\r
10222 if (innerHtml.length > 0) {
\r
10223 for (var i = 0; i < innerHtml.length; i++) {
\r
10224 traverse(innerHtml.eq(i));
\r
10227 wrapElement(elem);
\r
10231 var clearWrap = function (elem) {
\r
10232 var elems = elem.find('*');
\r
10233 for (var i = 0; i < elems.length; i++) {
\r
10234 if (elems.eq(i).attr('class') && elems.eq(i).attr('class').indexOf(highlightSearchStringClass) !== -1) {
\r
10235 var text = elems.eq(i).text();
\r
10236 elems.eq(i).replaceWith(text);
\r
10240 if (b2bResponsiveRowCtrl) {
\r
10241 scope.columnIndex = b2bResponsiveRowCtrl.getIndex();
\r
10242 scope.isHidden = function () {
\r
10243 return (b2bTableCtrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
\r
10246 $timeout(function () {
\r
10247 var actualHtml = elem.children();
\r
10248 scope.$watch(function () {
\r
10249 return b2bTableCtrl.getSearchString();
\r
10250 }, function (val) {
\r
10251 searchString = val;
\r
10253 if (actualHtml.length > 0) {
\r
10256 wrapElement(elem);
\r
10259 if (b2bResponsiveRowCtrl) {
\r
10260 b2bResponsiveRowCtrl.setRowValues(elem.html());
\r
10266 .directive('b2bTableSort', ['b2bTableConfig','$timeout', function (b2bTableConfig,$timeout) {
\r
10270 require: '^b2bTable',
\r
10271 link: function (scope, elem, attr, ctrl) {
\r
10272 var initialSort = '',
\r
10275 initialSort = attr.initialSort;
\r
10277 scope.sortTable = function (msg) {
\r
10278 $timeout(function(){
\r
10279 if (nextSort.length > 0) {
\r
10281 if (nextSort === 'd' || nextSort === 'D') {
\r
10282 tempsort = nextSort
\r
10283 ctrl.sortData(msg, true, true);
\r
10285 $timeout(function(){
\r
10286 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
\r
10287 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
\r
10292 tempsort = nextSort
\r
10293 ctrl.sortData(msg, false, true);
\r
10295 $timeout(function(){
\r
10296 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
\r
10297 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
\r
10301 } else if (initialSort.length > 0) {
\r
10303 if (initialSort === 'd' || initialSort === 'D') {
\r
10304 tempsort = nextSort
\r
10305 ctrl.sortData(msg, true, true);
\r
10307 $timeout(function(){
\r
10308 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
\r
10309 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
\r
10314 tempsort = nextSort
\r
10315 ctrl.sortData(msg, false, true);
\r
10317 $timeout(function(){
\r
10318 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
\r
10319 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
\r
10330 scope.sortDropdown = function(msg) {
\r
10332 if(tempsort==='') {
\r
10336 if(tempsort === 'd' || tempsort === 'D' ) {
\r
10337 ctrl.sortData(msg, true, false);
\r
10339 ctrl.sortData(msg, false, false);
\r
10347 * @ngdoc directive
\r
10348 * @name Tabs, tables & accordions.att:tabs
\r
10351 * <file src="src/tabs/docs/readme.md" />
\r
10354 * <b2b-tabset tab-id-selected="activeTabsId">
\r
10355 <b2b-tab ng-repeat="tab in gTabs" tab-item="tab"
\r
10356 id="{{tab.uniqueId}}" aria-controls="{{tab.tabPanelId}}"
\r
10357 ng-disabled="tab.disabled">
\r
10363 * <section id="code">
\r
10364 <example module="b2b.att">
\r
10365 <file src="src/tabs/docs/demo.html" />
\r
10366 <file src="src/tabs/docs/demo.js" />
\r
10372 angular.module('b2b.att.tabs', ['b2b.att.utilities'])
\r
10373 .directive('b2bTabset', function () {
\r
10376 transclude: true,
\r
10379 tabIdSelected: '='
\r
10381 templateUrl: 'b2bTemplate/tabs/b2bTabset.html',
\r
10382 controller: ['$scope', function ($scope) {
\r
10384 this.setTabIdSelected = function (tab) {
\r
10385 $scope.tabIdSelected = tab.id;
\r
10388 this.getTabIdSelected = function () {
\r
10389 return $scope.tabIdSelected;
\r
10394 .directive('b2bTab', ['keymap', function (keymap) {
\r
10397 transclude: true,
\r
10399 require: '^b2bTabset',
\r
10403 templateUrl: 'b2bTemplate/tabs/b2bTab.html',
\r
10404 controller: [function(){}],
\r
10405 link: function (scope, element, attr, b2bTabsetCtrl) {
\r
10407 if (scope.tabItem && !scope.tabItem.disabled) {
\r
10408 scope.tabItem.disabled = false;
\r
10411 scope.isTabActive = function () {
\r
10412 return (scope.tabItem.id === b2bTabsetCtrl.getTabIdSelected());
\r
10415 scope.clickTab = function () {
\r
10416 if (attr.disabled) {
\r
10419 b2bTabsetCtrl.setTabIdSelected(scope.tabItem);
\r
10422 scope.nextKey = function () {
\r
10423 var el = angular.element(element[0])[0];
\r
10424 var elementToFocus = null;
\r
10425 while (el && el.nextElementSibling) {
\r
10426 el = el.nextElementSibling;
\r
10427 if (!el.querySelector('a').disabled) {
\r
10428 elementToFocus = el.querySelector('a');
\r
10433 if (!elementToFocus) {
\r
10434 var childTabs = element.parent().children();
\r
10435 for (var i = 0; i < childTabs.length; i++) {
\r
10436 if (!childTabs[i].querySelector('a').disabled) {
\r
10437 elementToFocus = childTabs[i].querySelector('a');
\r
10443 if (elementToFocus) {
\r
10444 elementToFocus.focus();
\r
10448 scope.previousKey = function () {
\r
10449 var el = angular.element(element[0])[0];
\r
10450 var elementToFocus = null;
\r
10452 while (el && el.previousElementSibling) {
\r
10453 el = el.previousElementSibling;
\r
10454 if (!el.querySelector('a').disabled) {
\r
10455 elementToFocus = el.querySelector('a');
\r
10460 if (!elementToFocus) {
\r
10461 var childTabs = element.parent().children();
\r
10462 for (var i = childTabs.length - 1; i > 0; i--) {
\r
10463 if (!childTabs[i].querySelector('a').disabled) {
\r
10464 elementToFocus = childTabs[i].querySelector('a');
\r
10470 if (elementToFocus) {
\r
10471 elementToFocus.focus();
\r
10475 angular.element(element[0].querySelector('a')).bind('keydown', function (evt) {
\r
10477 if (!(evt.keyCode)) {
\r
10478 evt.keyCode = evt.which;
\r
10481 switch (evt.keyCode) {
\r
10482 case keymap.KEY.RIGHT:
\r
10483 evt.preventDefault();
\r
10487 case keymap.KEY.LEFT:
\r
10488 evt.preventDefault();
\r
10489 scope.previousKey();
\r
10499 * @ngdoc directive
\r
10500 * @name Messages, modals & alerts.att:tagBadges
\r
10503 * <file src="src/tagBadges/docs/readme.md" />
\r
10506 * <section id="code">
\r
10507 <example module="b2b.att">
\r
10508 <file src="src/tagBadges/docs/demo.html" />
\r
10509 <file src="src/tagBadges/docs/demo.js" />
\r
10514 angular.module('b2b.att.tagBadges', ['b2b.att.utilities'])
\r
10515 .directive('b2bTagBadge',['$timeout',function($timeout){
\r
10518 link: function(scope,elem,attr,ctrl){
\r
10519 elem.addClass('b2b-tags');
\r
10520 if(angular.element(elem[0].querySelector('.icon-primary-close')).length>0) {
\r
10521 var item = angular.element(elem[0].querySelector('.icon-primary-close'));
\r
10522 item.bind('click',function(){
\r
10523 elem.css({'height':'0','width':'0','padding':'0','border':'0'});
\r
10524 elem.attr('tabindex','0');
\r
10526 item.parent().remove();
\r
10527 elem[0].bind('blur',function(){
\r
10528 elem[0].remove();
\r
10540 * @ngdoc directive
\r
10541 * @name Forms.att:textArea
\r
10544 * <file src="src/textArea/docs/readme.md" />
\r
10547 * <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
10550 <section id="code">
\r
10551 <b>HTML + AngularJS</b>
\r
10552 <example module="b2b.att">
\r
10553 <file src="src/textArea/docs/demo.html" />
\r
10554 <file src="src/textArea/docs/demo.js" />
\r
10558 angular.module('b2b.att.textArea', ['b2b.att.utilities'])
\r
10560 .directive('b2bResetTextarea', [ function () {
\r
10563 require: 'b2bReset',
\r
10564 link: function (scope, element, attrs, ctrl) {
\r
10566 var resetButton = ctrl.getResetButton();
\r
10568 var computeScrollbarAndAddClass = function () {
\r
10569 if (element.prop('scrollHeight') > element[0].clientHeight) {
\r
10570 element.addClass('hasScrollbar');
\r
10572 element.removeClass('hasScrollbar');
\r
10576 computeScrollbarAndAddClass();
\r
10578 element.on('focus keyup', function(){
\r
10579 computeScrollbarAndAddClass();
\r
10586 * @ngdoc directive
\r
10587 * @name Forms.att:tooltipsForForms
\r
10590 * <file src="src/tooltipsForForms/docs/readme.md" />
\r
10593 <example module="b2b.att">
\r
10594 <file src="src/tooltipsForForms/docs/demo.html" />
\r
10595 <file src="src/tooltipsForForms/docs/demo.js" />
\r
10598 angular.module('b2b.att.tooltipsForForms', ['b2b.att.utilities'])
\r
10599 .directive('b2bTooltip', ['$document', '$window', '$isElement', function ($document, $window, $isElement) {
\r
10602 link: function (scope, elem, attr, ctrl) {
\r
10603 var icon = elem[0].querySelector('a.tooltip-element');
\r
10604 var btnIcon = elem[0].querySelector('.btn.tooltip-element');
\r
10605 var tooltipText = elem[0].querySelector('.helpertext');
\r
10606 var tooltipWrapper = elem[0].querySelector('.tooltip-size-control');
\r
10607 if (elem.hasClass('tooltip-onfocus')) {
\r
10608 var inputElm = angular.element(elem[0].querySelector("input"));
\r
10609 var textAreaElm = angular.element(elem[0].querySelector("textarea"));
\r
10611 angular.element(icon).attr({'aria-expanded': false});
\r
10612 angular.element(btnIcon).attr({'aria-expanded': false});
\r
10613 var calcTooltip = function () {
\r
10614 if (!elem.hasClass('tooltip active')) {
\r
10615 if (elem.hasClass('tooltip-onfocus')) {
\r
10616 angular.element(elem[0].querySelector("input")).triggerHandler('focusout');
\r
10618 if (elem.hasClass('tooltip-onclick')) {
\r
10621 angular.element(icon).removeClass('active');
\r
10622 angular.element(icon).attr({'aria-expanded': true});
\r
10623 angular.element(icon).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
\r
10624 angular.element(tooltipText).attr({'aria-hidden': false});
\r
10625 elem.addClass('active');
\r
10627 var tooltipIconPos = angular.element(icon).prop('offsetLeft'),
\r
10628 tooltipPosition = angular.element(tooltipText).prop('offsetWidth') / 2,
\r
10629 tipOffset = (tooltipIconPos - 30) - tooltipPosition,
\r
10630 maxRightPos = (($window.innerWidth - 72) - (tooltipPosition * 2)) - 14.5;
\r
10632 if ($window.innerWidth >= '768') {
\r
10633 if (tipOffset < 0) {// if icon on far left side of page
\r
10636 else if (tooltipIconPos > maxRightPos) {// if icon is far right side of page
\r
10637 tipOffset = maxRightPos;
\r
10639 else {// if tooltip in the middle somewhere
\r
10640 tipOffset = tipOffset;
\r
10642 angular.element(tooltipWrapper).css({left: tipOffset + 'px'});
\r
10647 // TOOLTIP LINK ONCLICK AND FOCUS
\r
10648 angular.element(icon).on('click mouseover mouseout focus blur', function (e) {
\r
10649 if (e.type == 'mouseover') {
\r
10652 else if (e.type == 'mouseout' && elem.hasClass('active')) {
\r
10653 if (!elem.hasClass('activeClick')) {
\r
10654 angular.element(tooltipText).attr({
\r
10655 'aria-hidden': true,
\r
10658 elem.removeClass('active');
\r
10659 } else if (elem.hasClass('activeClick') && navigator.userAgent.match(/iphone/i)) {
\r
10660 elem.removeClass('active activeClick');
\r
10665 if (elem.hasClass('activeClick')) {
\r
10666 angular.element(icon).attr({'aria-expanded': false});
\r
10667 angular.element(tooltipText).attr({'aria-hidden': true});
\r
10668 angular.element(icon).removeAttr('aria-describedby');
\r
10669 elem.removeClass('activeClick active');
\r
10670 e.preventDefault();
\r
10672 else if (e.type == 'click') {
\r
10673 elem.addClass('activeClick');
\r
10675 e.preventDefault();
\r
10678 angular.element(icon).on('keydown', function (e) {
\r
10679 if (e.keyCode == '32') {
\r
10680 (elem.hasClass('active')) ? elem.removeClass('active') : elem.addClass('value');
\r
10681 angular.element(icon).triggerHandler('click');
\r
10682 e.preventDefault();
\r
10683 } else if (e.keyCode == '27') {
\r
10684 (elem.hasClass('active')) ? elem.removeClass('active activeClick') : elem.addClass('value');
\r
10687 e.preventDefault();
\r
10690 e.preventDefault();
\r
10693 // TOOLTIP BUTTON INSIDE A TEXT FIELD
\r
10694 angular.element(btnIcon).on('click', function (e) {
\r
10695 var $this = angular.element(this);
\r
10696 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
\r
10697 elem.removeClass('active');
\r
10698 $this.removeClass('active');
\r
10699 angular.element(tooltipText).removeAttr('aria-live');
\r
10700 $this.attr({'aria-expanded': 'false'});
\r
10701 $this.removeAttr('aria-describedby');
\r
10703 elem.addClass('active');
\r
10704 $this.addClass('active');
\r
10705 $this.attr({'aria-expanded': 'true', 'aria-describedby': angular.element(tooltipText).attr('id')});
\r
10706 angular.element(tooltipText).attr({'aria-live': 'polite'});
\r
10710 angular.element(btnIcon).on('blur', function (e) {
\r
10711 var $this = angular.element(this);
\r
10712 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
\r
10713 elem.removeClass('active');
\r
10714 $this.removeClass('active');
\r
10715 angular.element(tooltipText).removeAttr('aria-live');
\r
10716 $this.attr({'aria-expanded': 'false'});
\r
10717 $this.removeAttr('aria-describedby');
\r
10721 angular.element(btnIcon).on('keydown', function (e) {
\r
10722 var $this = angular.element(this);
\r
10723 if (e.keyCode == '27') {
\r
10724 var $this = angular.element(this);
\r
10725 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
\r
10726 elem.removeClass('active');
\r
10727 $this.removeClass('active');
\r
10728 angular.element(tooltipText).removeAttr('aria-live');
\r
10729 $this.attr({'aria-expanded': 'false'});
\r
10730 $this.removeAttr('aria-describedby');
\r
10735 // close all tooltips if clicking something else
\r
10736 $document.bind('click', function (e) {
\r
10737 var isElement = $isElement(angular.element(e.target), elem, $document);
\r
10738 if (!isElement) {
\r
10739 elem.removeClass('active');
\r
10740 angular.element(elem[0].querySelector('.tooltip-element')).removeClass('active');
\r
10741 angular.element(tooltipText).removeAttr('aria-live');
\r
10742 angular.element(elem[0].querySelector('.tooltip-element')).attr({'aria-expanded': 'false'});
\r
10743 angular.element(elem[0].querySelector('.tooltip-element')).removeAttr('aria-describedby');
\r
10747 angular.element(inputElm).on('keydown', function (e) {
\r
10748 if (e.keyCode == '27'){
\r
10749 elem.removeClass('active');
\r
10750 angular.element(tooltipText).css('display', 'none');
\r
10751 angular.element(tooltipText).removeAttr('aria-live');
\r
10753 if (angular.element(this).attr('aria-describedby') === undefined){
\r
10757 else if ((spaceIndex = angular.element(this).attr('aria-describedby').lastIndexOf(' ')) >= 0){
\r
10759 var describedByValue = angular.element(this).attr('aria-describedby').slice(0, spaceIndex);
\r
10761 angular.element(this).attr('aria-describedby', describedByValue);
\r
10765 angular.element(this).removeAttr('aria-describedby');
\r
10770 angular.element(textAreaElm).on('keydown', function (e) {
\r
10771 if (e.keyCode == '27'){
\r
10772 elem.removeClass('active');
\r
10773 angular.element(tooltipText).css('display', 'none');
\r
10774 angular.element(tooltipText).removeAttr('aria-live');
\r
10775 if (angular.element(this).attr('aria-describedby') === undefined){
\r
10779 else if ((spaceIndex = angular.element(this).attr('aria-describedby').lastIndexOf(' ')) >= 0){
\r
10781 var describedByValue = angular.element(this).attr('aria-describedby').slice(0, spaceIndex);
\r
10783 angular.element(this).attr('aria-describedby', describedByValue);
\r
10787 angular.element(this).removeAttr('aria-describedby');
\r
10792 // TOOLTIP TRIGGERED AUTOMATICALLY INSIDE A TEXT FIELD
\r
10793 angular.element(inputElm).on('focus', function (e) {
\r
10794 var allTooltip = $document[0].querySelectorAll('[class*="tooltip"]');
\r
10795 for (var i = 0; i < allTooltip.length; i++) {
\r
10796 if (angular.element(allTooltip[i]).hasClass('active')) {
\r
10797 angular.element(allTooltip[i]).triggerHandler('click');
\r
10800 angular.element(this).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
\r
10801 angular.element(tooltipText).css('display', 'block');
\r
10802 angular.element(tooltipText).attr({'aria-live': 'polite'});
\r
10803 elem.addClass('active');
\r
10805 angular.element(inputElm).on('blur', function (e) {
\r
10806 elem.removeClass('active');
\r
10807 angular.element(tooltipText).css('display', 'none');
\r
10808 angular.element(tooltipText).removeAttr('aria-live');
\r
10809 angular.element(this).removeAttr('aria-describedby');
\r
10812 // TOOLTIP TRIGGERED AUTOMATICALLY INSIDE A TEXTAREA
\r
10813 angular.element(textAreaElm).on('focus', function (e) {
\r
10814 var allTooltip = $document[0].querySelectorAll('[class*="tooltip"]');
\r
10815 for (var i = 0; i < allTooltip.length; i++) {
\r
10816 if (angular.element(allTooltip[i]).hasClass('active')) {
\r
10817 angular.element(allTooltip[i]).triggerHandler('click');
\r
10820 elem.addClass('active');
\r
10821 angular.element(tooltipText).css('display', 'block');
\r
10822 angular.element(tooltipText).attr({'aria-live': 'polite'});
\r
10823 angular.element(this).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
\r
10825 angular.element(textAreaElm).on('blur', function (e) {
\r
10826 elem.removeClass('active');
\r
10827 angular.element(tooltipText).css('display', 'none');
\r
10828 angular.element(tooltipText).removeAttr('aria-live');
\r
10829 angular.element(this).removeAttr('aria-describedby');
\r
10835 * @ngdoc directive
\r
10836 * @name Navigation.att:TreeNavigation
\r
10840 * @param {String} setRole - This value needs to be "tree". This is required to incorporate CATO requirements.
\r
10841 * @param {Boolean} groupIt - This value needs to be "false" for top-level tree rendered.
\r
10844 * <file src="src/treeNav/docs/readme.md" />
\r
10847 * <div class="b2b-tree">
\r
10848 * <b2b-tree-nav collection="treeStructure" set-role="tree" group-it="false"></b2b-tree-nav>
\r
10851 * <section id="code">
\r
10852 <example module="b2b.att">
\r
10853 <file src="src/treeNav/docs/demo.html" />
\r
10854 <file src="src/treeNav/docs/demo.js" />
\r
10859 angular.module('b2b.att.treeNav', ['b2b.att.utilities'])
\r
10860 .directive('b2bTreeNav', function () {
\r
10869 templateUrl: function (element, attrs) {
\r
10870 if (attrs.groupIt === 'true') {
\r
10871 return "b2bTemplate/treeNav/groupedTree.html";
\r
10873 return "b2bTemplate/treeNav/ungroupedTree.html";
\r
10876 link: function (scope) {
\r
10877 if (!(scope.setRole === 'tree')) {
\r
10878 scope.setRole = 'group';
\r
10883 .directive('b2bMember', ['$compile', '$timeout', 'keymap', function ($compile, $timeout, keymap) {
\r
10891 templateUrl: 'b2bTemplate/treeNav/treeMember.html',
\r
10892 link: function (scope, element, attrs) {
\r
10893 scope.elemArr = [];
\r
10894 var removeRootTabIndex = function (elem) {
\r
10895 if (elem.parent().parent().eq(0).hasClass('b2b-tree')) {
\r
10896 elem.attr('tabindex', -1);
\r
10899 removeRootTabIndex(elem.parent());
\r
10901 scope.$watch('member.child', function(newVal, oldVal){
\r
10902 if(newVal !== oldVal){
\r
10903 scope.showChild();
\r
10906 scope.showChild = function () {
\r
10907 if (!element.hasClass('grouped')) {
\r
10908 if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
\r
10909 scope.groupIt = false;
\r
10910 element.addClass('grouped');
\r
10911 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
\r
10912 $compile(element.contents())(scope);
\r
10913 if(scope.member.active && scope.member.active === true){
\r
10914 element.find('i').eq(0).removeClass('icon-primary-collapsed');
\r
10916 if(scope.member.selected && scope.member.selected === true){
\r
10917 element.attr('aria-selected', true);
\r
10918 element.attr('tabindex', 0);
\r
10919 removeRootTabIndex(element);
\r
10921 if(scope.member.active && scope.member.active == undefined){
\r
10922 element.find('i').eq(0).addClass('icon-primary-collapsed');
\r
10924 } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
\r
10925 element.addClass('grouped');
\r
10926 scope.groupIt = true;
\r
10927 // FILTER - GROUPBY - APPROACH
\r
10929 var grpName = '';
\r
10930 if(scope.member.child[0].groupName !== undefined){
\r
10931 grpName = scope.member.child[0].groupName;
\r
10934 var toSlice = scope.member.child[0].name.search(' ');
\r
10935 grpName = scope.member.child[0].name.slice(0, toSlice);
\r
10938 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
\r
10940 for (j = j + i; j < (i + scope.member.divide); j++) {
\r
10941 if (j === scope.member.child.length) {
\r
10942 scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
\r
10945 if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
\r
10946 scope.member.child[j-1].activeGrp = true;
\r
10950 if (i + scope.member.divide > scope.member.child.length) {
\r
10951 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
\r
10952 if(scope.member.child[j].active && scope.member.child[j].active===true){
\r
10953 scope.member.child[j].activeGrp = true;
\r
10957 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
\r
10958 if(scope.member.child[j].active && scope.member.child[j].active===true){
\r
10959 scope.member.child[j].activeGrp = true;
\r
10964 if(scope.member.divide){
\r
10965 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
\r
10967 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
\r
10969 $compile(element.contents())(scope);
\r
10970 if(scope.member.active && scope.member.active === true){
\r
10971 element.find('i').eq(0).removeClass('icon-primary-collapsed');
\r
10973 if(scope.member.selected && scope.member.selected === true){
\r
10974 element.attr('aria-selected', true);
\r
10976 if( scope.member.active && scope.member.active == undefined){
\r
10977 element.find('i').eq(0).addClass('icon-primary-collapsed');
\r
10982 //Below condition opens node for opening on json load.
\r
10983 if(scope.member.active && scope.member.active == true){
\r
10984 scope.showChild();
\r
10986 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
\r
10987 element.find('i').eq(0).addClass('icon-primary-collapsed');
\r
10989 else if(scope.member.child == undefined){
\r
10990 element.find('i').eq(0).addClass('icon-primary-circle');
\r
10992 element.bind('keydown', function (evt) {
\r
10993 switch (evt.keyCode) {
\r
10994 case keymap.KEY.ENTER:
\r
10995 if (element.hasClass('bg') && scope.member.onSelect !== undefined) {
\r
10996 scope.member.onSelect(scope.member);
\r
10998 evt.stopPropagation();
\r
11005 //else getting true in every case .. so better use switch case .. that makes more sense you dumb.
\r
11006 element.bind('click', function (evt) {
\r
11007 scope.showChild();
\r
11008 var expandFunc = scope.member.onExpand;
\r
11011 if (element.hasClass('bg') && scope.member.onSelect !== undefined) {
\r
11012 scope.member.onSelect(scope.member);
\r
11014 if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
\r
11015 var eValue = scope.member.onExpand(scope.member);
\r
11017 if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
\r
11018 scope.member.onCollapse(scope.member);
\r
11024 .directive('b2bTreeLink', ['keymap', '$timeout', function (keymap, $timeout) {
\r
11027 link: function (scope, element, attr, ctrl) {
\r
11028 var rootE, parentE, upE, downE;
\r
11029 var closeOthersUp = function (elem,isKeyPress,passiveClose) {
\r
11030 //For accordion functionality on sibling nodes
\r
11031 if (elem.find('a').eq(0).hasClass('active')) {
\r
11032 activeToggle(elem,isKeyPress,passiveClose);
\r
11035 if (elem.hasClass('bg') && !isKeyPress) {
\r
11036 elem.removeClass('bg');
\r
11037 if (elem.attr('aria-selected')) {
\r
11038 elem.attr('aria-selected', 'false');
\r
11041 if (elem[0].previousElementSibling !== null) {
\r
11042 closeOthersUp(angular.element(elem[0].previousElementSibling),isKeyPress);
\r
11045 var closeOthersDown = function (elem,isKeyPress,passiveClose) {
\r
11046 //For accordion functionality on sibling nodes
\r
11047 if (elem.find('a').eq(0).hasClass('active')) {
\r
11048 activeToggle(elem,isKeyPress,passiveClose);
\r
11051 if (elem.hasClass('bg') && !isKeyPress) {
\r
11052 elem.removeClass('bg');
\r
11053 if (elem.attr('aria-selected')) {
\r
11054 elem.attr('aria-selected', 'false');
\r
11057 if (elem[0].nextElementSibling !== null) {
\r
11058 closeOthersDown(angular.element(elem[0].nextElementSibling),isKeyPress);
\r
11063 var removeBackground = function(elem){
\r
11065 if(elem.hasClass('b2b-tree')){
\r
11066 angular.element(elem[0].getElementsByClassName('bg')).removeClass('bg');
\r
11069 removeBackground(elem.parent().parent());
\r
11075 * These two functions used for setting heights on parent nodes as the child node closes
\r
11076 * Retaining this code for future reference
\r
11078 var addParentHeight = function(e, h) {
\r
11079 var parentLi = e.parent().parent();
\r
11080 var parentUl = e.parent();
\r
11081 if(!parentLi.hasClass('b2b-tree')) {
\r
11082 var addHeight = parentUl[0].offsetHeight + h;
\r
11083 parentLi.find('ul').eq(0).css({
\r
11084 height: addHeight+'px'
\r
11086 addParentHeight(parentLi, h);
\r
11090 var removeParentHeight = function(e, h) {
\r
11091 var parentLi = e.parent().parent();
\r
11092 var parentUl = e.parent();
\r
11093 if(!parentLi.hasClass('b2b-tree')) {
\r
11094 var addHeight = parentUl[0].offsetHeight - h;
\r
11095 parentLi.find('ul').eq(0).css({
\r
11096 height: addHeight+'px'
\r
11098 removeParentHeight(parentLi, h);
\r
11103 // isKeyPress - to notify that the function is called by Right Key press
\r
11104 // passiveClose - prevents firing of oncollapse events during the action
\r
11105 // of expand function(check the function definition)
\r
11107 var activeToggle = function (elem,isKeyPress,passiveClose) {
\r
11108 var element = elem.find('a').eq(0);
\r
11109 if (element.hasClass('active')) {
\r
11111 elem.removeClass('bg');
\r
11114 if (elem.attr('aria-selected') && !isKeyPress) {
\r
11115 elem.attr('aria-selected', 'false');
\r
11117 if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
\r
11118 if(isKeyPress && scope.member){
\r
11119 if (scope.member.onCollapse !== undefined && !passiveClose) {
\r
11120 scope.member.onCollapse(scope.member);
\r
11123 element.removeClass('active');
\r
11124 elem.attr('aria-expanded', 'false');
\r
11125 element.find('i').eq(0).removeClass('icon-primary-expanded');
\r
11126 element.find('i').eq(0).addClass('icon-primary-collapsed');
\r
11127 //For Animation: below commented code is used to manually set height of UL to zero
\r
11128 //retaining code for future reference
\r
11130 var totalHeight = elem.find('ul')[0].scrollHeight;
\r
11131 removeParentHeight(elem, totalHeight);
\r
11132 elem.find('ul').eq(0).css({
\r
11138 elem.addClass('bg');
\r
11139 elem.attr('aria-selected', 'true');
\r
11142 if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
\r
11144 if(typeof scope.showChild === 'function' ){
\r
11145 scope.showChild();
\r
11147 if(scope.member){
\r
11148 if (scope.member.onExpand !== undefined) {
\r
11149 scope.member.onExpand(scope.member);
\r
11153 element.addClass('active');
\r
11154 elem.attr('aria-expanded', 'true');
\r
11155 element.find('i').eq(0).removeClass('icon-primary-collapsed');
\r
11156 element.find('i').eq(0).addClass('icon-primary-expanded');
\r
11157 //For Animation: below commented code is used to manually set height of the ul generatedon the click of parent LI.
\r
11158 //retaining code for future reference
\r
11160 var totalHeight = elem.find('ul')[0].scrollHeight;
\r
11161 addParentHeight(elem, totalHeight);
\r
11162 elem.find('ul').eq(0).css({
\r
11163 height: totalHeight+'px'
\r
11169 element.bind('click', function (evt) {
\r
11170 //first we close others and then we open the clicked element
\r
11171 if (element[0].previousElementSibling) {
\r
11172 closeOthersUp(angular.element(element[0].previousElementSibling));
\r
11174 if (element[0].nextElementSibling) {
\r
11175 closeOthersDown(angular.element(element[0].nextElementSibling));
\r
11177 removeBackground(element);
\r
11178 activeToggle(element);
\r
11180 evt.stopPropagation();
\r
11182 //default root tree element tabindex set zero
\r
11183 if (element.parent().parent().hasClass('b2b-tree') && (element.parent()[0].previousElementSibling === null)) {
\r
11184 element.attr('tabindex', 0);
\r
11186 //check root via class
\r
11187 var isRoot = function (elem) {
\r
11188 if (elem.parent().parent().eq(0).hasClass('b2b-tree')) {
\r
11194 var findRoot = function (elem) {
\r
11195 if (isRoot(elem)) {
\r
11199 findRoot(elem.parent());
\r
11202 var findPreActive = function (elem) {
\r
11204 if (!(elem.hasClass("active"))) {
\r
11207 var childElems = angular.element(elem[0].nextElementSibling.children);
\r
11208 lastE = angular.element(childElems[childElems.length - 1]);
\r
11209 if (lastE.find('a').eq(0).hasClass('active')) {
\r
11210 findPreActive(lastE.find('a').eq(0));
\r
11216 var findUp = function (elem) {
\r
11217 if (isRoot(elem)) {
\r
11221 if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
\r
11222 upE = angular.element(elem[0].previousElementSibling);
\r
11223 if (upE.find('a').eq(0).hasClass('active')) {
\r
11224 findPreActive(upE.find('a').eq(0));
\r
11227 upE = elem.parent().parent();
\r
11231 var downElement = function (elem) {
\r
11232 if (elem.next().hasClass('tree-hide')) {
\r
11233 downElement(elem.next());
\r
11235 downE = elem.next();
\r
11238 var isBottomElem = false;
\r
11239 var downParent = function(liElem){
\r
11240 if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree')){
\r
11241 isBottomElem = true;
\r
11244 if(liElem.next().length !== 0){
\r
11245 downE = liElem.next().eq(0);
\r
11249 downParent(liElem.parent().parent());
\r
11253 var findDown = function (elem) {
\r
11254 if (isRoot(elem.parent()) && !elem.hasClass('active')) {
\r
11255 downE = elem.parent();
\r
11258 if (elem.hasClass('active')) {
\r
11259 downE = elem.next().find('li').eq(0);
\r
11260 if (downE.hasClass('tree-hide')) {
\r
11261 downElement(downE);
\r
11265 downParent(elem.parent());
\r
11266 if(isBottomElem === true){
\r
11267 downE = elem.parent();
\r
11268 isBottomElem = false;
\r
11274 var resetTabPosition = function(element){
\r
11275 findRoot(element);
\r
11276 angular.element(rootE.parent().parent()[0].querySelector("li[tabindex='0']")).attr('tabindex','-1');
\r
11277 var elemToFocus = rootE.parent().parent()[0].querySelector(".bg")|| rootE;
\r
11279 angular.element(elemToFocus).attr('tabindex','0');
\r
11281 // Function to control the expansion of nodes when the user tabs into the tree and
\r
11282 // the slected node is not visible
\r
11283 var expand = function(elemArr){
\r
11284 var elem= elemArr.pop();
\r
11285 var element = elem.find('a').eq(0);
\r
11286 var selectedNode = elem.parent().parent()[0].querySelector(".bg");
\r
11287 if(selectedNode != null){
\r
11289 element = elem.find('a').eq(0);
\r
11290 if(!element.hasClass('active') ){
\r
11293 if (elem[0].previousElementSibling) {
\r
11294 closeOthersUp(angular.element(elem[0].previousElementSibling),true,true);
\r
11296 if (elem[0].nextElementSibling) {
\r
11297 closeOthersDown(angular.element(elem[0].nextElementSibling),true,true);
\r
11300 if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
\r
11301 if(typeof scope.showChild === 'function' ){
\r
11302 scope.showChild();
\r
11304 element.addClass('active');
\r
11305 elem.attr('aria-expanded', 'true');
\r
11306 element.find('i').eq(0).removeClass('icon-primary-collapsed');
\r
11307 element.find('i').eq(0).addClass('icon-primary-expanded');
\r
11311 elem = elemArr.pop();
\r
11319 element.find('a').eq(0).bind('mouseenter', function (evt) {
\r
11320 angular.forEach(document.querySelectorAll('.activeTooltip'), function(value, key) {
\r
11321 angular.element(value).removeClass('activeTooltip')
\r
11323 element.addClass('activeTooltip');
\r
11325 element.find('a').eq(0).bind('mouseleave', function (evt) {
\r
11326 element.removeClass('activeTooltip');
\r
11328 element.bind('focus', function (evt) {
\r
11329 angular.forEach(document.querySelectorAll('.activeTooltip'), function(value, key) {
\r
11330 angular.element(value).removeClass('activeTooltip')
\r
11332 element.addClass('activeTooltip');
\r
11334 element.bind('blur', function (evt) {
\r
11335 element.removeClass('activeTooltip');
\r
11337 element.bind('keydown', function (evt) {
\r
11338 switch (evt.keyCode) {
\r
11339 case keymap.KEY.HOME:
\r
11340 evt.preventDefault();
\r
11341 evt.stopPropagation();
\r
11342 element.attr('tabindex', -1);
\r
11343 findRoot(element);
\r
11344 rootE.eq(0).attr('tabindex', 0);
\r
11345 rootE[0].focus();
\r
11347 case keymap.KEY.LEFT:
\r
11348 evt.preventDefault();
\r
11349 evt.stopPropagation();
\r
11351 if(element.find('a').eq(0).hasClass('active')){
\r
11352 if (element[0].previousElementSibling) {
\r
11353 closeOthersUp(angular.element(element[0].previousElementSibling),true);
\r
11355 if (element[0].nextElementSibling) {
\r
11356 closeOthersDown(angular.element(element[0].nextElementSibling),true);
\r
11358 activeToggle(element,true);
\r
11361 element.attr('tabindex', -1);
\r
11362 parentE = element.parent().parent();
\r
11363 parentE.attr('tabindex', 0);
\r
11364 parentE[0].focus();
\r
11366 case keymap.KEY.UP:
\r
11367 evt.preventDefault();
\r
11368 evt.stopPropagation();
\r
11369 element.attr('tabindex', -1);
\r
11371 upE.eq(0).attr('tabindex', 0);
\r
11374 case keymap.KEY.RIGHT:
\r
11375 evt.preventDefault();
\r
11376 evt.stopPropagation();
\r
11377 if(element.find('i').eq(0).hasClass('icon-primary-circle')){
\r
11380 if (!element.find('a').eq(0).hasClass('active')) {
\r
11381 if (element[0].previousElementSibling) {
\r
11382 closeOthersUp(angular.element(element[0].previousElementSibling),true);
\r
11384 if (element[0].nextElementSibling) {
\r
11385 closeOthersDown(angular.element(element[0].nextElementSibling),true);
\r
11387 activeToggle(element,true);
\r
11391 element.attr('tabindex', -1);
\r
11392 findDown(element.find('a').eq(0));
\r
11393 downE.eq(0).attr('tabindex', 0);
\r
11394 downE[0].focus();
\r
11397 case keymap.KEY.DOWN:
\r
11398 evt.preventDefault();
\r
11399 element.attr('tabindex', -1);
\r
11400 findDown(element.find('a').eq(0));
\r
11401 downE.eq(0).attr('tabindex', 0);
\r
11402 downE[0].focus();
\r
11403 evt.stopPropagation();
\r
11405 case keymap.KEY.ENTER:
\r
11406 var isSelectedElem = element.hasClass('bg');
\r
11407 var enterFunc = function(element){
\r
11408 if (isSelectedElem) {
\r
11409 element.removeClass('bg');
\r
11410 if (element.attr('aria-selected')) {
\r
11411 element.attr('aria-selected', 'false');
\r
11415 element.addClass('bg');
\r
11416 element.attr('aria-selected', 'true');
\r
11419 if (element[0].previousElementSibling) {
\r
11420 closeOthersUp(angular.element(element[0].previousElementSibling));
\r
11422 if (element[0].nextElementSibling) {
\r
11423 closeOthersDown(angular.element(element[0].nextElementSibling));
\r
11426 removeBackground(element);
\r
11427 enterFunc(element);
\r
11428 evt.stopPropagation();
\r
11430 case keymap.KEY.TAB:
\r
11431 $timeout(function(){
\r
11432 resetTabPosition(element);
\r
11434 evt.stopPropagation();
\r
11441 element.bind('keyup',function(evt){
\r
11442 if(evt.keyCode === keymap.KEY.TAB){
\r
11444 var tempElem = element;
\r
11445 var elemArr = [];
\r
11446 while(!tempElem.hasClass('b2b-tree')){
\r
11447 elemArr.push(tempElem);
\r
11448 tempElem = tempElem.parent().parent();
\r
11450 elemArr.push(tempElem);
\r
11452 expand(elemArr);
\r
11454 evt.stopPropagation();
\r
11460 * @ngdoc directive
\r
11461 * @name Navigation.att:Tree nodes with checkboxes
\r
11463 * @param {String} setRole - The value needs to be "tree". This is required to incorporate CATO requirements.
\r
11464 * @param {boolean} groupIt - The value needs to be "false" for top-level tree rendered.
\r
11465 * @param {Object} collection - The JSON object of tree to be rendered.
\r
11467 * <file src="src/treeNodeCheckbox/docs/readme.md" />
\r
11470 * <div class="b2b-tree-checkbox">
\r
11471 * <b2b-tree-node-checkbox collection="treeStructure" set-role="tree" group-it="false"></b2b-tree-node-checkbox>
\r
11474 * <section id="code">
\r
11475 <example module="b2b.att">
\r
11476 <file src="src/treeNodeCheckbox/docs/demo.html" />
\r
11477 <file src="src/treeNodeCheckbox/docs/demo.js" />
\r
11482 angular.module('b2b.att.treeNodeCheckbox', ['b2b.att.utilities'])
\r
11483 .directive('b2bTreeNodeCheckbox', function () {
\r
11492 templateUrl: function (element, attrs) {
\r
11493 if (attrs.groupIt === 'true') {
\r
11494 return "b2bTemplate/treeNodeCheckbox/groupedTree.html";
\r
11496 return "b2bTemplate/treeNodeCheckbox/ungroupedTree.html";
\r
11499 link: function (scope) {
\r
11500 if (!(scope.setRole === 'tree')) {
\r
11501 scope.setRole = 'group';
\r
11506 .directive('b2bTreeMember', ['$compile', '$timeout', 'keymap', function ($compile, $timeout, keymap) {
\r
11514 templateUrl: 'b2bTemplate/treeNodeCheckbox/treeMember.html',
\r
11515 link: function (scope, element, attrs) {
\r
11516 scope.elemArr = [];
\r
11517 var removeRootTabIndex = function (elem) {
\r
11518 if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
\r
11519 elem.attr('tabindex', -1);
\r
11522 removeRootTabIndex(elem.parent());
\r
11524 scope.$watch('member.child', function(newVal, oldVal){
\r
11525 if(newVal !== oldVal){
\r
11526 scope.showChild();
\r
11530 var checkedCount = 0;
\r
11531 var nonCheckedCount = 0;
\r
11532 var checkBoxesCount = 0;
\r
11534 if(element.find('a').eq(0).find('input')){
\r
11535 if(scope.member.indeterminate){
\r
11536 element.find('a').eq(0).find('input').prop('indeterminate', true);
\r
11537 element.attr('aria-checked',"mixed");
\r
11539 element.attr('aria-checked',scope.member.isSelected);
\r
11542 element.find('a').eq(0).find('input').bind('change',function(){
\r
11543 scope.member.indeterminate = false;
\r
11544 downwardModalUpdate(scope.member);
\r
11545 downwardSelection(element);
\r
11546 upwardSelection(element);
\r
11547 element.attr('aria-checked',scope.member.isSelected);
\r
11548 if (scope.member.onSelect !== undefined) {
\r
11549 scope.member.onSelect(scope.member);
\r
11553 element.find('a').eq(0).find('input').bind('click',function(){
\r
11554 var elem = angular.element(this);
\r
11555 if(scope.member.indeterminate){
\r
11556 scope.member.indeterminate = false;
\r
11557 scope.member.isSelected = true;
\r
11558 elem.prop('indeterminate', false);
\r
11559 elem.prop('checked', true);
\r
11560 elem.triggerHandler('change');
\r
11564 var groupNode = false;
\r
11565 var checkedTreeNode = false;
\r
11567 var isCheckboxSelected = function(elem){
\r
11568 checkedTreeNode = false;
\r
11569 checkedTreeNode = angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox').checked;
\r
11572 var findCheckbox = function(elem){
\r
11573 return angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox');
\r
11576 var updateGrpNodeCheckboxes = function(elem, checked){
\r
11577 angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox').checked = checked;
\r
11581 var isGroupNode = function(elem){
\r
11582 groupNode = false;
\r
11583 if(angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.grpTreeCheckbox')){
\r
11584 groupNode = true;
\r
11588 var downwardModalUpdate = function(curMember){
\r
11589 angular.forEach(curMember.child, function(childMember, key) {
\r
11590 childMember.isSelected = curMember.isSelected;
\r
11591 childMember.indeterminate = false;
\r
11592 if(angular.isArray(childMember.child) && scope.member.child.length > 0){
\r
11593 downwardModalUpdate(childMember);
\r
11598 var downwardSelection = function(elem){
\r
11599 if(findCheckbox(elem)){
\r
11600 isCheckboxSelected(elem)
\r
11602 if(angular.element(elem).find('ul').length > 0){
\r
11603 var childNodes = angular.element(elem).find('ul').eq(0).children('li');
\r
11604 for(var i=0; i<childNodes.length; i++){
\r
11605 if(findCheckbox(childNodes[i])){
\r
11606 isGroupNode(childNodes[i]);
\r
11607 angular.element(findCheckbox(childNodes[i])).prop('indeterminate', false);
\r
11608 angular.element(childNodes[i]).attr('aria-checked',checkedTreeNode);
\r
11610 updateGrpNodeCheckboxes(childNodes[i],checkedTreeNode);
\r
11612 angular.element(childNodes[i]).scope().member.isSelected = checkedTreeNode;
\r
11613 angular.element(childNodes[i]).scope().member.indeterminate = false
\r
11614 angular.element(childNodes[i]).scope().$apply();
\r
11616 downwardSelection(childNodes[i]);
\r
11622 var upwardSelection = function(elem){
\r
11623 var childNodes = elem.parent().parent().find('ul').eq(0).children('li');
\r
11624 checkedCount = 0;
\r
11625 nonCheckedCount = 0;
\r
11626 checkBoxesCount = 0;
\r
11627 for(i=0; i<childNodes.length; i++){
\r
11628 if(findCheckbox(childNodes[i])){
\r
11629 isGroupNode(childNodes[i]);
\r
11630 isCheckboxSelected(childNodes[i]);
\r
11631 checkBoxesCount++;
\r
11632 if(checkedTreeNode){
\r
11634 }else if(!angular.element(angular.element(angular.element(childNodes[i]).find('a').eq(0))[0].querySelector('input.treeCheckBox')).prop('indeterminate')){
\r
11635 nonCheckedCount++;
\r
11639 var parentNodeScope;
\r
11640 parentNodeScope = angular.element(elem.parent().parent()).scope();
\r
11641 if(findCheckbox(elem.parent().parent())){
\r
11642 if(nonCheckedCount == checkBoxesCount){
\r
11643 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
\r
11644 if(parentNodeScope && parentNodeScope.member){
\r
11645 parentNodeScope.member.isSelected = false;
\r
11646 parentNodeScope.member.indeterminate = false;
\r
11648 updateGrpNodeCheckboxes(elem.parent().parent(),false);
\r
11650 angular.element(elem.parent().parent()).attr('aria-checked',false);
\r
11651 }else if(checkedCount == checkBoxesCount){
\r
11652 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
\r
11653 if(parentNodeScope && parentNodeScope.member){
\r
11654 parentNodeScope.member.isSelected = true;
\r
11655 parentNodeScope.member.indeterminate = false;
\r
11657 updateGrpNodeCheckboxes(elem.parent().parent(),true);
\r
11659 angular.element(elem.parent().parent()).attr('aria-checked',true);
\r
11661 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', true);
\r
11662 if(parentNodeScope && parentNodeScope.member){
\r
11663 parentNodeScope.member.isSelected = false;
\r
11664 parentNodeScope.member.indeterminate = true;
\r
11666 updateGrpNodeCheckboxes(elem.parent().parent(),false);
\r
11668 angular.element(elem.parent().parent()).attr('aria-checked',"mixed");
\r
11670 if(parentNodeScope && parentNodeScope.member){
\r
11671 parentNodeScope.$apply();
\r
11677 if(elem.parent().parent().attr('role') == "treeitem"){
\r
11678 upwardSelection(elem.parent().parent());
\r
11682 scope.showChild = function () {
\r
11683 if (!element.hasClass('grouped')) {
\r
11684 if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
\r
11685 scope.groupIt = false;
\r
11686 element.addClass('grouped');
\r
11687 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
\r
11688 $compile(element.contents())(scope);
\r
11689 if(scope.member.active && scope.member.active === true){
\r
11690 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
\r
11692 if(scope.member.selected && scope.member.selected === true){
\r
11693 element.attr('tabindex', 0);
\r
11694 removeRootTabIndex(element);
\r
11696 if(scope.member.active && scope.member.active == undefined){
\r
11697 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
\r
11699 } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
\r
11700 element.addClass('grouped');
\r
11701 scope.groupIt = true;
\r
11703 var grpName = '';
\r
11704 if(scope.member.child[0].groupName !== undefined){
\r
11705 grpName = scope.member.child[0].groupName;
\r
11708 var toSlice = scope.member.child[0].name.search(' ');
\r
11709 grpName = scope.member.child[0].name.slice(0, toSlice);
\r
11712 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
\r
11714 for (j = j + i; j < (i + scope.member.divide); j++) {
\r
11715 if (j === scope.member.child.length) {
\r
11716 scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
\r
11719 if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
\r
11720 scope.member.child[j-1].activeGrp = true;
\r
11724 if (i + scope.member.divide > scope.member.child.length) {
\r
11725 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
\r
11726 if(scope.member.child[j].active && scope.member.child[j].active===true){
\r
11727 scope.member.child[j].activeGrp = true;
\r
11731 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
\r
11732 if(scope.member.child[j].active && scope.member.child[j].active===true){
\r
11733 scope.member.child[j].activeGrp = true;
\r
11738 if(scope.member.divide){
\r
11739 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
\r
11741 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
\r
11743 $compile(element.contents())(scope);
\r
11744 if(scope.member.active && scope.member.active === true){
\r
11745 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
\r
11748 if( scope.member.active && scope.member.active == undefined){
\r
11749 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
\r
11753 $timeout(function () {
\r
11754 if(!scope.member.indeterminate){
\r
11755 downwardSelection(element);
\r
11761 if(scope.member.active && scope.member.active == true){
\r
11762 scope.showChild();
\r
11764 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
\r
11765 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
\r
11767 else if(scope.member.child == undefined){
\r
11768 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-circle');
\r
11769 if(scope.$parent.$index === 0) {
\r
11770 element.find('a').eq(0).append('<span class="first-link"></span>');
\r
11774 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
\r
11775 scope.showChild();
\r
11776 var expandFunc = scope.member.onExpand;
\r
11777 if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
\r
11778 var eValue = scope.member.onExpand(scope.member);
\r
11780 if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
\r
11781 scope.member.onCollapse(scope.member);
\r
11785 angular.element(element[0].querySelectorAll('.treeNodeName')).eq(0).bind('click', function (evt) {
\r
11792 .directive('b2bTreeNodeLink', ['keymap', '$timeout', function (keymap, $timeout) {
\r
11795 link: function (scope, element, attr, ctrl) {
\r
11796 var rootE, parentE, upE, downE;
\r
11797 var closeOthersUp = function (elem) {
\r
11799 if (elem.find('a').eq(0).hasClass('active')) {
\r
11800 activeToggle(elem);
\r
11803 if (elem.hasClass('bg')) {
\r
11804 elem.removeClass('bg');
\r
11806 if (elem[0].previousElementSibling !== null) {
\r
11807 closeOthersUp(angular.element(elem[0].previousElementSibling));
\r
11810 var closeOthersDown = function (elem) {
\r
11812 if (elem.find('a').eq(0).hasClass('active')) {
\r
11813 activeToggle(elem);
\r
11816 if (elem.hasClass('bg')) {
\r
11817 elem.removeClass('bg');
\r
11819 if (elem[0].nextElementSibling !== null) {
\r
11820 closeOthersDown(angular.element(elem[0].nextElementSibling));
\r
11824 var removeBackgroundUp = function (elem) {
\r
11826 if (elem.hasClass('b2b-tree-checkbox')) {
\r
11829 elem.parent().parent().removeClass('bg');
\r
11830 removeBackgroundUp(elem.parent().parent());
\r
11834 var removeBackgroundDown = function (elem) {
\r
11836 angular.element(elem[0].querySelector('.bg')).removeClass('bg');
\r
11841 var activeToggle = function (elem) {
\r
11842 var element = elem.find('a').eq(0);
\r
11843 if (element.hasClass('active')) {
\r
11844 elem.removeClass('bg');
\r
11845 if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
\r
11846 element.removeClass('active');
\r
11847 elem.attr('aria-expanded', 'false');
\r
11848 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-expanded');
\r
11849 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
\r
11852 elem.addClass('bg');
\r
11853 if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
\r
11854 element.addClass('active');
\r
11855 elem.attr('aria-expanded', 'true');
\r
11856 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
\r
11857 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-expanded');
\r
11861 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
\r
11863 if (element[0].previousElementSibling) {
\r
11864 closeOthersUp(angular.element(element[0].previousElementSibling));
\r
11866 if (element[0].nextElementSibling) {
\r
11867 closeOthersDown(angular.element(element[0].nextElementSibling));
\r
11870 activeToggle(element);
\r
11872 removeBackgroundDown(element);
\r
11873 removeBackgroundUp(element);
\r
11874 evt.stopPropagation();
\r
11877 if (element.parent().parent().hasClass('b2b-tree-checkbox') && (element.parent()[0].previousElementSibling === null)) {
\r
11878 element.attr('tabindex', 0);
\r
11881 var isRoot = function (elem) {
\r
11882 if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
\r
11888 var findRoot = function (elem) {
\r
11889 if (isRoot(elem)) {
\r
11893 findRoot(elem.parent());
\r
11896 var findPreActive = function (elem) {
\r
11898 if (!(elem.hasClass("active"))) {
\r
11901 var childElems = angular.element(elem[0].nextElementSibling.children);
\r
11902 lastE = angular.element(childElems[childElems.length - 1]);
\r
11903 if (lastE.find('a').eq(0).hasClass('active')) {
\r
11904 findPreActive(lastE.find('a').eq(0));
\r
11910 var findUp = function (elem) {
\r
11911 if (isRoot(elem)) {
\r
11915 if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
\r
11916 upE = angular.element(elem[0].previousElementSibling);
\r
11917 if (upE.find('a').eq(0).hasClass('active')) {
\r
11918 findPreActive(upE.find('a').eq(0));
\r
11921 upE = elem.parent().parent();
\r
11925 var downElement = function (elem) {
\r
11926 if (elem.next().hasClass('tree-hide')) {
\r
11927 downElement(elem.next());
\r
11929 downE = elem.next();
\r
11932 var isBottomElem = false;
\r
11933 var downParent = function(liElem){
\r
11934 if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree-checkbox')){
\r
11935 isBottomElem = true;
\r
11938 if(liElem.next().length !== 0){
\r
11939 downE = liElem.next().eq(0);
\r
11943 downParent(liElem.parent().parent());
\r
11947 var findDown = function (elem) {
\r
11948 if (isRoot(elem.parent()) && !elem.hasClass('active')) {
\r
11949 downE = elem.parent();
\r
11952 if (elem.hasClass('active')) {
\r
11953 downE = elem.next().find('li').eq(0);
\r
11954 if (downE.hasClass('tree-hide')) {
\r
11955 downElement(downE);
\r
11959 downParent(elem.parent());
\r
11960 if(isBottomElem === true){
\r
11961 downE = elem.parent();
\r
11962 isBottomElem = false;
\r
11966 element.bind('keydown', function (evt) {
\r
11967 switch (evt.keyCode) {
\r
11968 case keymap.KEY.HOME:
\r
11969 evt.preventDefault();
\r
11970 evt.stopPropagation();
\r
11971 element.attr('tabindex', -1);
\r
11972 findRoot(element);
\r
11973 rootE.eq(0).attr('tabindex', 0);
\r
11974 rootE[0].focus();
\r
11976 case keymap.KEY.LEFT:
\r
11977 evt.preventDefault();
\r
11978 evt.stopPropagation();
\r
11979 if (!isRoot(element)) {
\r
11980 if(element.find('a').eq(0).hasClass('active')){
\r
11981 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
\r
11984 element.attr('tabindex', -1);
\r
11985 parentE = element.parent().parent();
\r
11986 parentE.attr('tabindex', 0);
\r
11987 parentE[0].focus();
\r
11989 if (element.find('a').eq(0).hasClass('active')) {
\r
11990 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
\r
11994 case keymap.KEY.UP:
\r
11995 evt.preventDefault();
\r
11996 evt.stopPropagation();
\r
11997 element.attr('tabindex', -1);
\r
11999 upE.eq(0).attr('tabindex', 0);
\r
12002 case keymap.KEY.RIGHT:
\r
12003 evt.preventDefault();
\r
12004 evt.stopPropagation();
\r
12005 if(angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')){
\r
12008 if (!element.find('a').eq(0).hasClass('active')) {
\r
12009 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
\r
12012 element.attr('tabindex', -1);
\r
12013 findDown(element.find('a').eq(0));
\r
12014 downE.eq(0).attr('tabindex', 0);
\r
12015 downE[0].focus();
\r
12018 case keymap.KEY.DOWN:
\r
12019 evt.preventDefault();
\r
12020 element.attr('tabindex', -1);
\r
12021 findDown(element.find('a').eq(0));
\r
12022 downE.eq(0).attr('tabindex', 0);
\r
12023 downE[0].focus();
\r
12024 evt.stopPropagation();
\r
12026 case keymap.KEY.SPACE:
\r
12027 case keymap.KEY.ENTER:
\r
12028 evt.preventDefault();
\r
12029 evt.stopPropagation();
\r
12030 if(angular.isDefined(element.scope().member.isSelected)){
\r
12031 element.scope().member.isSelected = !element.scope().member.isSelected;
\r
12032 element.scope().member.indeterminate = false;
\r
12033 element.scope().$apply();
\r
12034 element.find('a').eq(0).find('input').prop('indeterminate', false);
\r
12035 element.find('a').eq(0).find('input').triggerHandler('change');
\r
12047 * DATE: 2014-01-14
\r
12048 * UPDATES AND DOCS AT: http://www.greensock.com
\r
12050 * @license Copyright (c) 2008-2014, GreenSock. All rights reserved.
\r
12051 * This work is subject to the terms at http://www.greensock.com/terms_of_use.html or for
\r
12052 * Club GreenSock members, the software agreement that was issued with your membership.
\r
12054 * @author: Jack Doyle, jack@greensock.com
\r
12056 (window._gsQueue || (window._gsQueue = [])).push( function() {
\r
12060 var _doc = document.documentElement,
\r
12061 _window = window,
\r
12062 _max = function(element, axis) {
\r
12063 var dim = (axis === "x") ? "Width" : "Height",
\r
12064 scroll = "scroll" + dim,
\r
12065 client = "client" + dim,
\r
12066 body = document.body;
\r
12067 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
12070 ScrollToPlugin = window._gsDefine.plugin({
\r
12071 propName: "scrollTo",
\r
12075 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
\r
12076 init: function(target, value, tween) {
\r
12077 this._wdw = (target === _window);
\r
12078 this._target = target;
\r
12079 this._tween = tween;
\r
12080 if (typeof(value) !== "object") {
\r
12081 value = {y:value}; //if we don't receive an object as the parameter, assume the user intends "y".
\r
12083 this._autoKill = (value.autoKill !== false);
\r
12084 this.x = this.xPrev = this.getX();
\r
12085 this.y = this.yPrev = this.getY();
\r
12086 if (value.x != null) {
\r
12087 this._addTween(this, "x", this.x, (value.x === "max") ? _max(target, "x") : value.x, "scrollTo_x", true);
\r
12088 this._overwriteProps.push("scrollTo_x");
\r
12090 this.skipX = true;
\r
12092 if (value.y != null) {
\r
12093 this._addTween(this, "y", this.y, (value.y === "max") ? _max(target, "y") : value.y, "scrollTo_y", true);
\r
12094 this._overwriteProps.push("scrollTo_y");
\r
12096 this.skipY = true;
\r
12101 //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
12102 set: function(v) {
\r
12103 this._super.setRatio.call(this, v);
\r
12105 var x = (this._wdw || !this.skipX) ? this.getX() : this.xPrev,
\r
12106 y = (this._wdw || !this.skipY) ? this.getY() : this.yPrev,
\r
12107 yDif = y - this.yPrev,
\r
12108 xDif = x - this.xPrev;
\r
12110 if (this._autoKill) {
\r
12111 //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
12112 if (!this.skipX && (xDif > 7 || xDif < -7) && x < _max(this._target, "x")) {
\r
12113 this.skipX = true; //if the user scrolls separately, we should stop tweening!
\r
12115 if (!this.skipY && (yDif > 7 || yDif < -7) && y < _max(this._target, "y")) {
\r
12116 this.skipY = true; //if the user scrolls separately, we should stop tweening!
\r
12118 if (this.skipX && this.skipY) {
\r
12119 this._tween.kill();
\r
12123 _window.scrollTo((!this.skipX) ? this.x : x, (!this.skipY) ? this.y : y);
\r
12125 if (!this.skipY) {
\r
12126 this._target.scrollTop = this.y;
\r
12128 if (!this.skipX) {
\r
12129 this._target.scrollLeft = this.x;
\r
12132 this.xPrev = this.x;
\r
12133 this.yPrev = this.y;
\r
12137 p = ScrollToPlugin.prototype;
\r
12139 ScrollToPlugin.max = _max;
\r
12141 p.getX = function() {
\r
12142 return (!this._wdw) ? this._target.scrollLeft : (_window.pageXOffset != null) ? _window.pageXOffset : (_doc.scrollLeft != null) ? _doc.scrollLeft : document.body.scrollLeft;
\r
12145 p.getY = function() {
\r
12146 return (!this._wdw) ? this._target.scrollTop : (_window.pageYOffset != null) ? _window.pageYOffset : (_doc.scrollTop != null) ? _doc.scrollTop : document.body.scrollTop;
\r
12149 p._kill = function(lookup) {
\r
12150 if (lookup.scrollTo_x) {
\r
12151 this.skipX = true;
\r
12153 if (lookup.scrollTo_y) {
\r
12154 this.skipY = true;
\r
12156 return this._super._kill.call(this, lookup);
\r
12159 }); if (window._gsDefine) { window._gsQueue.pop()(); }
\r
12161 * VERSION: 1.12.1
\r
12162 * DATE: 2014-06-26
\r
12163 * UPDATES AND DOCS AT: http://www.greensock.com
\r
12165 * Includes all of the following: TweenLite, TweenMax, TimelineLite, TimelineMax, EasePack, CSSPlugin, RoundPropsPlugin, BezierPlugin, AttrPlugin, DirectionalRotationPlugin
\r
12167 * @license Copyright (c) 2008-2014, GreenSock. All rights reserved.
\r
12168 * This work is subject to the terms at http://www.greensock.com/terms_of_use.html or for
\r
12169 * Club GreenSock members, the software agreement that was issued with your membership.
\r
12171 * @author: Jack Doyle, jack@greensock.com
\r
12174 (window._gsQueue || (window._gsQueue = [])).push( function() {
\r
12178 window._gsDefine("TweenMax", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) {
\r
12180 var _slice = [].slice,
\r
12181 TweenMax = function(target, duration, vars) {
\r
12182 TweenLite.call(this, target, duration, vars);
\r
12184 this._yoyo = (this.vars.yoyo === true);
\r
12185 this._repeat = this.vars.repeat || 0;
\r
12186 this._repeatDelay = this.vars.repeatDelay || 0;
\r
12187 this._dirty = true; //ensures that if there is any repeat, the totalDuration will get recalculated to accurately report it.
\r
12188 this.render = TweenMax.prototype.render; //speed optimization (avoid prototype lookup on this "hot" method)
\r
12190 _tinyNum = 0.0000000001,
\r
12191 TweenLiteInternals = TweenLite._internals,
\r
12192 _isSelector = TweenLiteInternals.isSelector,
\r
12193 _isArray = TweenLiteInternals.isArray,
\r
12194 p = TweenMax.prototype = TweenLite.to({}, 0.1, {}),
\r
12195 _blankArray = [];
\r
12197 TweenMax.version = "1.12.1";
\r
12198 p.constructor = TweenMax;
\r
12199 p.kill()._gc = false;
\r
12200 TweenMax.killTweensOf = TweenMax.killDelayedCallsTo = TweenLite.killTweensOf;
\r
12201 TweenMax.getTweensOf = TweenLite.getTweensOf;
\r
12202 TweenMax.lagSmoothing = TweenLite.lagSmoothing;
\r
12203 TweenMax.ticker = TweenLite.ticker;
\r
12204 TweenMax.render = TweenLite.render;
\r
12206 p.invalidate = function() {
\r
12207 this._yoyo = (this.vars.yoyo === true);
\r
12208 this._repeat = this.vars.repeat || 0;
\r
12209 this._repeatDelay = this.vars.repeatDelay || 0;
\r
12210 this._uncache(true);
\r
12211 return TweenLite.prototype.invalidate.call(this);
\r
12214 p.updateTo = function(vars, resetDuration) {
\r
12215 var curRatio = this.ratio, p;
\r
12216 if (resetDuration && this._startTime < this._timeline._time) {
\r
12217 this._startTime = this._timeline._time;
\r
12218 this._uncache(false);
\r
12220 this._enabled(true, false);
\r
12222 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
12225 for (p in vars) {
\r
12226 this.vars[p] = vars[p];
\r
12228 if (this._initted) {
\r
12229 if (resetDuration) {
\r
12230 this._initted = false;
\r
12233 this._enabled(true, false);
\r
12235 if (this._notifyPluginsOfEnabled && this._firstPT) {
\r
12236 TweenLite._onPluginEvent("_onDisable", this); //in case a plugin like MotionBlur must perform some cleanup tasks
\r
12238 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
12239 var prevTime = this._time;
\r
12240 this.render(0, true, false);
\r
12241 this._initted = false;
\r
12242 this.render(prevTime, true, false);
\r
12243 } else if (this._time > 0) {
\r
12244 this._initted = false;
\r
12246 var inv = 1 / (1 - curRatio),
\r
12247 pt = this._firstPT, endValue;
\r
12249 endValue = pt.s + pt.c;
\r
12251 pt.s = endValue - pt.c;
\r
12260 p.render = function(time, suppressEvents, force) {
\r
12261 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
12262 this.invalidate();
\r
12264 var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
\r
12265 prevTime = this._time,
\r
12266 prevTotalTime = this._totalTime,
\r
12267 prevCycle = this._cycle,
\r
12268 duration = this._duration,
\r
12269 prevRawPrevTime = this._rawPrevTime,
\r
12270 isComplete, callback, pt, cycleDuration, r, type, pow, rawPrevTime, i;
\r
12271 if (time >= totalDur) {
\r
12272 this._totalTime = totalDur;
\r
12273 this._cycle = this._repeat;
\r
12274 if (this._yoyo && (this._cycle & 1) !== 0) {
\r
12276 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
\r
12278 this._time = duration;
\r
12279 this.ratio = this._ease._calcEnd ? this._ease.getRatio(1) : 1;
\r
12281 if (!this._reversed) {
\r
12282 isComplete = true;
\r
12283 callback = "onComplete";
\r
12285 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
12286 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
12289 if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time) {
\r
12291 if (prevRawPrevTime > _tinyNum) {
\r
12292 callback = "onReverseComplete";
\r
12295 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
12298 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
\r
12299 this._totalTime = this._time = this._cycle = 0;
\r
12300 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
\r
12301 if (prevTotalTime !== 0 || (duration === 0 && prevRawPrevTime > 0 && prevRawPrevTime !== _tinyNum)) {
\r
12302 callback = "onReverseComplete";
\r
12303 isComplete = this._reversed;
\r
12306 this._active = false;
\r
12307 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
12308 if (prevRawPrevTime >= 0) {
\r
12311 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
12313 } 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
12317 this._totalTime = this._time = time;
\r
12319 if (this._repeat !== 0) {
\r
12320 cycleDuration = duration + this._repeatDelay;
\r
12321 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
12322 if (this._cycle !== 0) if (this._cycle === this._totalTime / cycleDuration) {
\r
12323 this._cycle--; //otherwise when rendered exactly at the end time, it will act as though it is repeating (at the beginning)
\r
12325 this._time = this._totalTime - (this._cycle * cycleDuration);
\r
12326 if (this._yoyo) if ((this._cycle & 1) !== 0) {
\r
12327 this._time = duration - this._time;
\r
12329 if (this._time > duration) {
\r
12330 this._time = duration;
\r
12331 } else if (this._time < 0) {
\r
12336 if (this._easeType) {
\r
12337 r = this._time / duration;
\r
12338 type = this._easeType;
\r
12339 pow = this._easePower;
\r
12340 if (type === 1 || (type === 3 && r >= 0.5)) {
\r
12343 if (type === 3) {
\r
12348 } else if (pow === 2) {
\r
12350 } else if (pow === 3) {
\r
12352 } else if (pow === 4) {
\r
12353 r *= r * r * r * r;
\r
12356 if (type === 1) {
\r
12357 this.ratio = 1 - r;
\r
12358 } else if (type === 2) {
\r
12360 } else if (this._time / duration < 0.5) {
\r
12361 this.ratio = r / 2;
\r
12363 this.ratio = 1 - (r / 2);
\r
12367 this.ratio = this._ease.getRatio(this._time / duration);
\r
12372 if (prevTime === this._time && !force && prevCycle === this._cycle) {
\r
12373 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
12374 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
\r
12377 } else if (!this._initted) {
\r
12379 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
12381 } 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
12382 this._time = prevTime;
\r
12383 this._totalTime = prevTotalTime;
\r
12384 this._rawPrevTime = prevRawPrevTime;
\r
12385 this._cycle = prevCycle;
\r
12386 TweenLiteInternals.lazyTweens.push(this);
\r
12387 this._lazy = time;
\r
12390 //_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
12391 if (this._time && !isComplete) {
\r
12392 this.ratio = this._ease.getRatio(this._time / duration);
\r
12393 } else if (isComplete && this._ease._calcEnd) {
\r
12394 this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1);
\r
12397 if (this._lazy !== false) {
\r
12398 this._lazy = false;
\r
12401 if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) {
\r
12402 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
12404 if (prevTotalTime === 0) {
\r
12405 if (this._initted === 2 && time > 0) {
\r
12406 //this.invalidate();
\r
12407 this._init(); //will just apply overwriting since _initted of (2) means it was a from() tween that had immediateRender:true
\r
12409 if (this._startAt) {
\r
12411 this._startAt.render(time, suppressEvents, force);
\r
12412 } else if (!callback) {
\r
12413 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
12416 if (this.vars.onStart) if (this._totalTime !== 0 || duration === 0) if (!suppressEvents) {
\r
12417 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
\r
12421 pt = this._firstPT;
\r
12424 pt.t[pt.p](pt.c * this.ratio + pt.s);
\r
12426 pt.t[pt.p] = pt.c * this.ratio + pt.s;
\r
12431 if (this._onUpdate) {
\r
12432 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
12433 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
12435 if (!suppressEvents) if (this._totalTime !== prevTotalTime || isComplete) {
\r
12436 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
\r
12439 if (this._cycle !== prevCycle) if (!suppressEvents) if (!this._gc) if (this.vars.onRepeat) {
\r
12440 this.vars.onRepeat.apply(this.vars.onRepeatScope || this, this.vars.onRepeatParams || _blankArray);
\r
12442 if (callback) if (!this._gc) { //check gc because there's a chance that kill() could be called in an onUpdate
\r
12443 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
12444 this._startAt.render(time, suppressEvents, force);
\r
12446 if (isComplete) {
\r
12447 if (this._timeline.autoRemoveChildren) {
\r
12448 this._enabled(false, false);
\r
12450 this._active = false;
\r
12452 if (!suppressEvents && this.vars[callback]) {
\r
12453 this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
\r
12455 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
12456 this._rawPrevTime = 0;
\r
12461 //---- STATIC FUNCTIONS -----------------------------------------------------------------------------------------------------------
\r
12463 TweenMax.to = function(target, duration, vars) {
\r
12464 return new TweenMax(target, duration, vars);
\r
12467 TweenMax.from = function(target, duration, vars) {
\r
12468 vars.runBackwards = true;
\r
12469 vars.immediateRender = (vars.immediateRender != false);
\r
12470 return new TweenMax(target, duration, vars);
\r
12473 TweenMax.fromTo = function(target, duration, fromVars, toVars) {
\r
12474 toVars.startAt = fromVars;
\r
12475 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
\r
12476 return new TweenMax(target, duration, toVars);
\r
12479 TweenMax.staggerTo = TweenMax.allTo = function(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
\r
12480 stagger = stagger || 0;
\r
12481 var delay = vars.delay || 0,
\r
12483 finalComplete = function() {
\r
12484 if (vars.onComplete) {
\r
12485 vars.onComplete.apply(vars.onCompleteScope || this, arguments);
\r
12487 onCompleteAll.apply(onCompleteAllScope || this, onCompleteAllParams || _blankArray);
\r
12490 if (!_isArray(targets)) {
\r
12491 if (typeof(targets) === "string") {
\r
12492 targets = TweenLite.selector(targets) || targets;
\r
12494 if (_isSelector(targets)) {
\r
12495 targets = _slice.call(targets, 0);
\r
12498 l = targets.length;
\r
12499 for (i = 0; i < l; i++) {
\r
12501 for (p in vars) {
\r
12502 copy[p] = vars[p];
\r
12504 copy.delay = delay;
\r
12505 if (i === l - 1 && onCompleteAll) {
\r
12506 copy.onComplete = finalComplete;
\r
12508 a[i] = new TweenMax(targets[i], duration, copy);
\r
12509 delay += stagger;
\r
12514 TweenMax.staggerFrom = TweenMax.allFrom = function(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
\r
12515 vars.runBackwards = true;
\r
12516 vars.immediateRender = (vars.immediateRender != false);
\r
12517 return TweenMax.staggerTo(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
\r
12520 TweenMax.staggerFromTo = TweenMax.allFromTo = function(targets, duration, fromVars, toVars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
\r
12521 toVars.startAt = fromVars;
\r
12522 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
\r
12523 return TweenMax.staggerTo(targets, duration, toVars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
\r
12526 TweenMax.delayedCall = function(delay, callback, params, scope, useFrames) {
\r
12527 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
12530 TweenMax.set = function(target, vars) {
\r
12531 return new TweenMax(target, 0, vars);
\r
12534 TweenMax.isTweening = function(target) {
\r
12535 return (TweenLite.getTweensOf(target, true).length > 0);
\r
12538 var _getChildrenOf = function(timeline, includeTimelines) {
\r
12541 tween = timeline._first;
\r
12543 if (tween instanceof TweenLite) {
\r
12544 a[cnt++] = tween;
\r
12546 if (includeTimelines) {
\r
12547 a[cnt++] = tween;
\r
12549 a = a.concat(_getChildrenOf(tween, includeTimelines));
\r
12552 tween = tween._next;
\r
12556 getAllTweens = TweenMax.getAllTweens = function(includeTimelines) {
\r
12557 return _getChildrenOf(Animation._rootTimeline, includeTimelines).concat( _getChildrenOf(Animation._rootFramesTimeline, includeTimelines) );
\r
12560 TweenMax.killAll = function(complete, tweens, delayedCalls, timelines) {
\r
12561 if (tweens == null) {
\r
12564 if (delayedCalls == null) {
\r
12565 delayedCalls = true;
\r
12567 var a = getAllTweens((timelines != false)),
\r
12569 allTrue = (tweens && delayedCalls && timelines),
\r
12571 for (i = 0; i < l; i++) {
\r
12573 if (allTrue || (tween instanceof SimpleTimeline) || ((isDC = (tween.target === tween.vars.onComplete)) && delayedCalls) || (tweens && !isDC)) {
\r
12575 tween.totalTime(tween._reversed ? 0 : tween.totalDuration());
\r
12577 tween._enabled(false, false);
\r
12583 TweenMax.killChildTweensOf = function(parent, complete) {
\r
12584 if (parent == null) {
\r
12587 var tl = TweenLiteInternals.tweenLookup,
\r
12588 a, curParent, p, i, l;
\r
12589 if (typeof(parent) === "string") {
\r
12590 parent = TweenLite.selector(parent) || parent;
\r
12592 if (_isSelector(parent)) {
\r
12593 parent = _slice.call(parent, 0);
\r
12595 if (_isArray(parent)) {
\r
12596 i = parent.length;
\r
12597 while (--i > -1) {
\r
12598 TweenMax.killChildTweensOf(parent[i], complete);
\r
12604 curParent = tl[p].target.parentNode;
\r
12605 while (curParent) {
\r
12606 if (curParent === parent) {
\r
12607 a = a.concat(tl[p].tweens);
\r
12609 curParent = curParent.parentNode;
\r
12613 for (i = 0; i < l; i++) {
\r
12615 a[i].totalTime(a[i].totalDuration());
\r
12617 a[i]._enabled(false, false);
\r
12621 var _changePause = function(pause, tweens, delayedCalls, timelines) {
\r
12622 tweens = (tweens !== false);
\r
12623 delayedCalls = (delayedCalls !== false);
\r
12624 timelines = (timelines !== false);
\r
12625 var a = getAllTweens(timelines),
\r
12626 allTrue = (tweens && delayedCalls && timelines),
\r
12629 while (--i > -1) {
\r
12631 if (allTrue || (tween instanceof SimpleTimeline) || ((isDC = (tween.target === tween.vars.onComplete)) && delayedCalls) || (tweens && !isDC)) {
\r
12632 tween.paused(pause);
\r
12637 TweenMax.pauseAll = function(tweens, delayedCalls, timelines) {
\r
12638 _changePause(true, tweens, delayedCalls, timelines);
\r
12641 TweenMax.resumeAll = function(tweens, delayedCalls, timelines) {
\r
12642 _changePause(false, tweens, delayedCalls, timelines);
\r
12645 TweenMax.globalTimeScale = function(value) {
\r
12646 var tl = Animation._rootTimeline,
\r
12647 t = TweenLite.ticker.time;
\r
12648 if (!arguments.length) {
\r
12649 return tl._timeScale;
\r
12651 value = value || _tinyNum; //can't allow zero because it'll throw the math off
\r
12652 tl._startTime = t - ((t - tl._startTime) * tl._timeScale / value);
\r
12653 tl = Animation._rootFramesTimeline;
\r
12654 t = TweenLite.ticker.frame;
\r
12655 tl._startTime = t - ((t - tl._startTime) * tl._timeScale / value);
\r
12656 tl._timeScale = Animation._rootTimeline._timeScale = value;
\r
12661 //---- GETTERS / SETTERS ----------------------------------------------------------------------------------------------------------
\r
12663 p.progress = function(value) {
\r
12664 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
12667 p.totalProgress = function(value) {
\r
12668 return (!arguments.length) ? this._totalTime / this.totalDuration() : this.totalTime( this.totalDuration() * value, false);
\r
12671 p.time = function(value, suppressEvents) {
\r
12672 if (!arguments.length) {
\r
12673 return this._time;
\r
12675 if (this._dirty) {
\r
12676 this.totalDuration();
\r
12678 if (value > this._duration) {
\r
12679 value = this._duration;
\r
12681 if (this._yoyo && (this._cycle & 1) !== 0) {
\r
12682 value = (this._duration - value) + (this._cycle * (this._duration + this._repeatDelay));
\r
12683 } else if (this._repeat !== 0) {
\r
12684 value += this._cycle * (this._duration + this._repeatDelay);
\r
12686 return this.totalTime(value, suppressEvents);
\r
12689 p.duration = function(value) {
\r
12690 if (!arguments.length) {
\r
12691 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
12693 return Animation.prototype.duration.call(this, value);
\r
12696 p.totalDuration = function(value) {
\r
12697 if (!arguments.length) {
\r
12698 if (this._dirty) {
\r
12699 //instead of Infinity, we use 999999999999 so that we can accommodate reverses
\r
12700 this._totalDuration = (this._repeat === -1) ? 999999999999 : this._duration * (this._repeat + 1) + (this._repeatDelay * this._repeat);
\r
12701 this._dirty = false;
\r
12703 return this._totalDuration;
\r
12705 return (this._repeat === -1) ? this : this.duration( (value - (this._repeat * this._repeatDelay)) / (this._repeat + 1) );
\r
12708 p.repeat = function(value) {
\r
12709 if (!arguments.length) {
\r
12710 return this._repeat;
\r
12712 this._repeat = value;
\r
12713 return this._uncache(true);
\r
12716 p.repeatDelay = function(value) {
\r
12717 if (!arguments.length) {
\r
12718 return this._repeatDelay;
\r
12720 this._repeatDelay = value;
\r
12721 return this._uncache(true);
\r
12724 p.yoyo = function(value) {
\r
12725 if (!arguments.length) {
\r
12726 return this._yoyo;
\r
12728 this._yoyo = value;
\r
12745 * ----------------------------------------------------------------
\r
12747 * ----------------------------------------------------------------
\r
12749 window._gsDefine("TimelineLite", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) {
\r
12751 var TimelineLite = function(vars) {
\r
12752 SimpleTimeline.call(this, vars);
\r
12753 this._labels = {};
\r
12754 this.autoRemoveChildren = (this.vars.autoRemoveChildren === true);
\r
12755 this.smoothChildTiming = (this.vars.smoothChildTiming === true);
\r
12756 this._sortChildren = true;
\r
12757 this._onUpdate = this.vars.onUpdate;
\r
12758 var v = this.vars,
\r
12762 if (_isArray(val)) if (val.join("").indexOf("{self}") !== -1) {
\r
12763 v[p] = this._swapSelfInParams(val);
\r
12766 if (_isArray(v.tweens)) {
\r
12767 this.add(v.tweens, 0, v.align, v.stagger);
\r
12770 _tinyNum = 0.0000000001,
\r
12771 _isSelector = TweenLite._internals.isSelector,
\r
12772 _isArray = TweenLite._internals.isArray,
\r
12773 _blankArray = [],
\r
12774 _globals = window._gsDefine.globals,
\r
12775 _copy = function(vars) {
\r
12776 var copy = {}, p;
\r
12777 for (p in vars) {
\r
12778 copy[p] = vars[p];
\r
12782 _pauseCallback = function(tween, callback, params, scope) {
\r
12783 tween._timeline.pause(tween._startTime);
\r
12785 callback.apply(scope || tween._timeline, params || _blankArray);
\r
12788 _slice = _blankArray.slice,
\r
12789 p = TimelineLite.prototype = new SimpleTimeline();
\r
12791 TimelineLite.version = "1.12.1";
\r
12792 p.constructor = TimelineLite;
\r
12793 p.kill()._gc = false;
\r
12795 p.to = function(target, duration, vars, position) {
\r
12796 var Engine = (vars.repeat && _globals.TweenMax) || TweenLite;
\r
12797 return duration ? this.add( new Engine(target, duration, vars), position) : this.set(target, vars, position);
\r
12800 p.from = function(target, duration, vars, position) {
\r
12801 return this.add( ((vars.repeat && _globals.TweenMax) || TweenLite).from(target, duration, vars), position);
\r
12804 p.fromTo = function(target, duration, fromVars, toVars, position) {
\r
12805 var Engine = (toVars.repeat && _globals.TweenMax) || TweenLite;
\r
12806 return duration ? this.add( Engine.fromTo(target, duration, fromVars, toVars), position) : this.set(target, toVars, position);
\r
12809 p.staggerTo = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
\r
12810 var tl = new TimelineLite({onComplete:onCompleteAll, onCompleteParams:onCompleteAllParams, onCompleteScope:onCompleteAllScope, smoothChildTiming:this.smoothChildTiming}),
\r
12812 if (typeof(targets) === "string") {
\r
12813 targets = TweenLite.selector(targets) || targets;
\r
12815 if (_isSelector(targets)) { //senses if the targets object is a selector. If it is, we should translate it into an array.
\r
12816 targets = _slice.call(targets, 0);
\r
12818 stagger = stagger || 0;
\r
12819 for (i = 0; i < targets.length; i++) {
\r
12820 if (vars.startAt) {
\r
12821 vars.startAt = _copy(vars.startAt);
\r
12823 tl.to(targets[i], duration, _copy(vars), i * stagger);
\r
12825 return this.add(tl, position);
\r
12828 p.staggerFrom = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
\r
12829 vars.immediateRender = (vars.immediateRender != false);
\r
12830 vars.runBackwards = true;
\r
12831 return this.staggerTo(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
\r
12834 p.staggerFromTo = function(targets, duration, fromVars, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
\r
12835 toVars.startAt = fromVars;
\r
12836 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
\r
12837 return this.staggerTo(targets, duration, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
\r
12840 p.call = function(callback, params, scope, position) {
\r
12841 return this.add( TweenLite.delayedCall(0, callback, params, scope), position);
\r
12844 p.set = function(target, vars, position) {
\r
12845 position = this._parseTimeOrLabel(position, 0, true);
\r
12846 if (vars.immediateRender == null) {
\r
12847 vars.immediateRender = (position === this._time && !this._paused);
\r
12849 return this.add( new TweenLite(target, 0, vars), position);
\r
12852 TimelineLite.exportRoot = function(vars, ignoreDelayedCalls) {
\r
12853 vars = vars || {};
\r
12854 if (vars.smoothChildTiming == null) {
\r
12855 vars.smoothChildTiming = true;
\r
12857 var tl = new TimelineLite(vars),
\r
12858 root = tl._timeline,
\r
12860 if (ignoreDelayedCalls == null) {
\r
12861 ignoreDelayedCalls = true;
\r
12863 root._remove(tl, true);
\r
12864 tl._startTime = 0;
\r
12865 tl._rawPrevTime = tl._time = tl._totalTime = root._time;
\r
12866 tween = root._first;
\r
12868 next = tween._next;
\r
12869 if (!ignoreDelayedCalls || !(tween instanceof TweenLite && tween.target === tween.vars.onComplete)) {
\r
12870 tl.add(tween, tween._startTime - tween._delay);
\r
12878 p.add = function(value, position, align, stagger) {
\r
12879 var curTime, l, i, child, tl, beforeRawTime;
\r
12880 if (typeof(position) !== "number") {
\r
12881 position = this._parseTimeOrLabel(position, 0, true, value);
\r
12883 if (!(value instanceof Animation)) {
\r
12884 if ((value instanceof Array) || (value && value.push && _isArray(value))) {
\r
12885 align = align || "normal";
\r
12886 stagger = stagger || 0;
\r
12887 curTime = position;
\r
12888 l = value.length;
\r
12889 for (i = 0; i < l; i++) {
\r
12890 if (_isArray(child = value[i])) {
\r
12891 child = new TimelineLite({tweens:child});
\r
12893 this.add(child, curTime);
\r
12894 if (typeof(child) !== "string" && typeof(child) !== "function") {
\r
12895 if (align === "sequence") {
\r
12896 curTime = child._startTime + (child.totalDuration() / child._timeScale);
\r
12897 } else if (align === "start") {
\r
12898 child._startTime -= child.delay();
\r
12901 curTime += stagger;
\r
12903 return this._uncache(true);
\r
12904 } else if (typeof(value) === "string") {
\r
12905 return this.addLabel(value, position);
\r
12906 } else if (typeof(value) === "function") {
\r
12907 value = TweenLite.delayedCall(0, value);
\r
12909 throw("Cannot add " + value + " into the timeline; it is not a tween, timeline, function, or string.");
\r
12913 SimpleTimeline.prototype.add.call(this, value, position);
\r
12915 //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
12916 if (this._gc || this._time === this._duration) if (!this._paused) if (this._duration < this.duration()) {
\r
12917 //in case any of the ancestors had completed but should now be enabled...
\r
12919 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
12920 while (tl._timeline) {
\r
12921 if (beforeRawTime && tl._timeline.smoothChildTiming) {
\r
12922 tl.totalTime(tl._totalTime, true); //moves the timeline (shifts its startTime) if necessary, and also enables it.
\r
12923 } else if (tl._gc) {
\r
12924 tl._enabled(true, false);
\r
12926 tl = tl._timeline;
\r
12933 p.remove = function(value) {
\r
12934 if (value instanceof Animation) {
\r
12935 return this._remove(value, false);
\r
12936 } else if (value instanceof Array || (value && value.push && _isArray(value))) {
\r
12937 var i = value.length;
\r
12938 while (--i > -1) {
\r
12939 this.remove(value[i]);
\r
12942 } else if (typeof(value) === "string") {
\r
12943 return this.removeLabel(value);
\r
12945 return this.kill(null, value);
\r
12948 p._remove = function(tween, skipDisable) {
\r
12949 SimpleTimeline.prototype._remove.call(this, tween, skipDisable);
\r
12950 var last = this._last;
\r
12952 this._time = this._totalTime = this._duration = this._totalDuration = 0;
\r
12953 } else if (this._time > last._startTime + last._totalDuration / last._timeScale) {
\r
12954 this._time = this.duration();
\r
12955 this._totalTime = this._totalDuration;
\r
12960 p.append = function(value, offsetOrLabel) {
\r
12961 return this.add(value, this._parseTimeOrLabel(null, offsetOrLabel, true, value));
\r
12964 p.insert = p.insertMultiple = function(value, position, align, stagger) {
\r
12965 return this.add(value, position || 0, align, stagger);
\r
12968 p.appendMultiple = function(tweens, offsetOrLabel, align, stagger) {
\r
12969 return this.add(tweens, this._parseTimeOrLabel(null, offsetOrLabel, true, tweens), align, stagger);
\r
12972 p.addLabel = function(label, position) {
\r
12973 this._labels[label] = this._parseTimeOrLabel(position);
\r
12977 p.addPause = function(position, callback, params, scope) {
\r
12978 return this.call(_pauseCallback, ["{self}", callback, params, scope], this, position);
\r
12981 p.removeLabel = function(label) {
\r
12982 delete this._labels[label];
\r
12986 p.getLabelTime = function(label) {
\r
12987 return (this._labels[label] != null) ? this._labels[label] : -1;
\r
12990 p._parseTimeOrLabel = function(timeOrLabel, offsetOrLabel, appendIfAbsent, ignore) {
\r
12992 //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
12993 if (ignore instanceof Animation && ignore.timeline === this) {
\r
12994 this.remove(ignore);
\r
12995 } else if (ignore && ((ignore instanceof Array) || (ignore.push && _isArray(ignore)))) {
\r
12996 i = ignore.length;
\r
12997 while (--i > -1) {
\r
12998 if (ignore[i] instanceof Animation && ignore[i].timeline === this) {
\r
12999 this.remove(ignore[i]);
\r
13003 if (typeof(offsetOrLabel) === "string") {
\r
13004 return this._parseTimeOrLabel(offsetOrLabel, (appendIfAbsent && typeof(timeOrLabel) === "number" && this._labels[offsetOrLabel] == null) ? timeOrLabel - this.duration() : 0, appendIfAbsent);
\r
13006 offsetOrLabel = offsetOrLabel || 0;
\r
13007 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
13008 i = timeOrLabel.indexOf("=");
\r
13010 if (this._labels[timeOrLabel] == null) {
\r
13011 return appendIfAbsent ? (this._labels[timeOrLabel] = this.duration() + offsetOrLabel) : offsetOrLabel;
\r
13013 return this._labels[timeOrLabel] + offsetOrLabel;
\r
13015 offsetOrLabel = parseInt(timeOrLabel.charAt(i-1) + "1", 10) * Number(timeOrLabel.substr(i+1));
\r
13016 timeOrLabel = (i > 1) ? this._parseTimeOrLabel(timeOrLabel.substr(0, i-1), 0, appendIfAbsent) : this.duration();
\r
13017 } else if (timeOrLabel == null) {
\r
13018 timeOrLabel = this.duration();
\r
13020 return Number(timeOrLabel) + offsetOrLabel;
\r
13023 p.seek = function(position, suppressEvents) {
\r
13024 return this.totalTime((typeof(position) === "number") ? position : this._parseTimeOrLabel(position), (suppressEvents !== false));
\r
13027 p.stop = function() {
\r
13028 return this.paused(true);
\r
13031 p.gotoAndPlay = function(position, suppressEvents) {
\r
13032 return this.play(position, suppressEvents);
\r
13035 p.gotoAndStop = function(position, suppressEvents) {
\r
13036 return this.pause(position, suppressEvents);
\r
13039 p.render = function(time, suppressEvents, force) {
\r
13041 this._enabled(true, false);
\r
13043 var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
\r
13044 prevTime = this._time,
\r
13045 prevStart = this._startTime,
\r
13046 prevTimeScale = this._timeScale,
\r
13047 prevPaused = this._paused,
\r
13048 tween, isComplete, next, callback, internalForce;
\r
13049 if (time >= totalDur) {
\r
13050 this._totalTime = this._time = totalDur;
\r
13051 if (!this._reversed) if (!this._hasPausedChild()) {
\r
13052 isComplete = true;
\r
13053 callback = "onComplete";
\r
13054 if (this._duration === 0) if (time === 0 || this._rawPrevTime < 0 || this._rawPrevTime === _tinyNum) if (this._rawPrevTime !== time && this._first) {
\r
13055 internalForce = true;
\r
13056 if (this._rawPrevTime > _tinyNum) {
\r
13057 callback = "onReverseComplete";
\r
13061 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
13062 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
13064 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
\r
13065 this._totalTime = this._time = 0;
\r
13066 if (prevTime !== 0 || (this._duration === 0 && this._rawPrevTime !== _tinyNum && (this._rawPrevTime > 0 || (time < 0 && this._rawPrevTime >= 0)))) {
\r
13067 callback = "onReverseComplete";
\r
13068 isComplete = this._reversed;
\r
13071 this._active = false;
\r
13072 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
13073 internalForce = true;
\r
13075 this._rawPrevTime = time;
\r
13077 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
13079 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
13080 if (!this._initted) {
\r
13081 internalForce = true;
\r
13086 this._totalTime = this._time = this._rawPrevTime = time;
\r
13088 if ((this._time === prevTime || !this._first) && !force && !internalForce) {
\r
13090 } else if (!this._initted) {
\r
13091 this._initted = true;
\r
13094 if (!this._active) if (!this._paused && this._time !== prevTime && time > 0) {
\r
13095 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
13098 if (prevTime === 0) if (this.vars.onStart) if (this._time !== 0) if (!suppressEvents) {
\r
13099 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
\r
13102 if (this._time >= prevTime) {
\r
13103 tween = this._first;
\r
13105 next = tween._next; //record it here because the value could change after rendering...
\r
13106 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
\r
13108 } else if (tween._active || (tween._startTime <= this._time && !tween._paused && !tween._gc)) {
\r
13109 if (!tween._reversed) {
\r
13110 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
\r
13112 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
\r
13118 tween = this._last;
\r
13120 next = tween._prev; //record it here because the value could change after rendering...
\r
13121 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
\r
13123 } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) {
\r
13124 if (!tween._reversed) {
\r
13125 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
\r
13127 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
\r
13134 if (this._onUpdate) if (!suppressEvents) {
\r
13135 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
\r
13138 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
13139 if (isComplete) {
\r
13140 if (this._timeline.autoRemoveChildren) {
\r
13141 this._enabled(false, false);
\r
13143 this._active = false;
\r
13145 if (!suppressEvents && this.vars[callback]) {
\r
13146 this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
\r
13151 p._hasPausedChild = function() {
\r
13152 var tween = this._first;
\r
13154 if (tween._paused || ((tween instanceof TimelineLite) && tween._hasPausedChild())) {
\r
13157 tween = tween._next;
\r
13162 p.getChildren = function(nested, tweens, timelines, ignoreBeforeTime) {
\r
13163 ignoreBeforeTime = ignoreBeforeTime || -9999999999;
\r
13165 tween = this._first,
\r
13168 if (tween._startTime < ignoreBeforeTime) {
\r
13170 } else if (tween instanceof TweenLite) {
\r
13171 if (tweens !== false) {
\r
13172 a[cnt++] = tween;
\r
13175 if (timelines !== false) {
\r
13176 a[cnt++] = tween;
\r
13178 if (nested !== false) {
\r
13179 a = a.concat(tween.getChildren(true, tweens, timelines));
\r
13183 tween = tween._next;
\r
13188 p.getTweensOf = function(target, nested) {
\r
13189 var disabled = this._gc,
\r
13194 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
13196 tweens = TweenLite.getTweensOf(target);
\r
13197 i = tweens.length;
\r
13198 while (--i > -1) {
\r
13199 if (tweens[i].timeline === this || (nested && this._contains(tweens[i]))) {
\r
13200 a[cnt++] = tweens[i];
\r
13204 this._enabled(false, true);
\r
13209 p._contains = function(tween) {
\r
13210 var tl = tween.timeline;
\r
13212 if (tl === this) {
\r
13215 tl = tl.timeline;
\r
13220 p.shiftChildren = function(amount, adjustLabels, ignoreBeforeTime) {
\r
13221 ignoreBeforeTime = ignoreBeforeTime || 0;
\r
13222 var tween = this._first,
\r
13223 labels = this._labels,
\r
13226 if (tween._startTime >= ignoreBeforeTime) {
\r
13227 tween._startTime += amount;
\r
13229 tween = tween._next;
\r
13231 if (adjustLabels) {
\r
13232 for (p in labels) {
\r
13233 if (labels[p] >= ignoreBeforeTime) {
\r
13234 labels[p] += amount;
\r
13238 return this._uncache(true);
\r
13241 p._kill = function(vars, target) {
\r
13242 if (!vars && !target) {
\r
13243 return this._enabled(false, false);
\r
13245 var tweens = (!target) ? this.getChildren(true, true, false) : this.getTweensOf(target),
\r
13246 i = tweens.length,
\r
13248 while (--i > -1) {
\r
13249 if (tweens[i]._kill(vars, target)) {
\r
13256 p.clear = function(labels) {
\r
13257 var tweens = this.getChildren(false, true, true),
\r
13258 i = tweens.length;
\r
13259 this._time = this._totalTime = 0;
\r
13260 while (--i > -1) {
\r
13261 tweens[i]._enabled(false, false);
\r
13263 if (labels !== false) {
\r
13264 this._labels = {};
\r
13266 return this._uncache(true);
\r
13269 p.invalidate = function() {
\r
13270 var tween = this._first;
\r
13272 tween.invalidate();
\r
13273 tween = tween._next;
\r
13278 p._enabled = function(enabled, ignoreTimeline) {
\r
13279 if (enabled === this._gc) {
\r
13280 var tween = this._first;
\r
13282 tween._enabled(enabled, true);
\r
13283 tween = tween._next;
\r
13286 return SimpleTimeline.prototype._enabled.call(this, enabled, ignoreTimeline);
\r
13289 p.duration = function(value) {
\r
13290 if (!arguments.length) {
\r
13291 if (this._dirty) {
\r
13292 this.totalDuration(); //just triggers recalculation
\r
13294 return this._duration;
\r
13296 if (this.duration() !== 0 && value !== 0) {
\r
13297 this.timeScale(this._duration / value);
\r
13302 p.totalDuration = function(value) {
\r
13303 if (!arguments.length) {
\r
13304 if (this._dirty) {
\r
13306 tween = this._last,
\r
13307 prevStart = 999999999999,
\r
13310 prev = tween._prev; //record it here in case the tween changes position in the sequence...
\r
13311 if (tween._dirty) {
\r
13312 tween.totalDuration(); //could change the tween._startTime, so make sure the tween's cache is clean before analyzing it.
\r
13314 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
13315 this.add(tween, tween._startTime - tween._delay);
\r
13317 prevStart = tween._startTime;
\r
13319 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
13320 max -= tween._startTime;
\r
13321 if (this._timeline.smoothChildTiming) {
\r
13322 this._startTime += tween._startTime / this._timeScale;
\r
13324 this.shiftChildren(-tween._startTime, false, -9999999999);
\r
13327 end = tween._startTime + (tween._totalDuration / tween._timeScale);
\r
13333 this._duration = this._totalDuration = max;
\r
13334 this._dirty = false;
\r
13336 return this._totalDuration;
\r
13338 if (this.totalDuration() !== 0) if (value !== 0) {
\r
13339 this.timeScale(this._totalDuration / value);
\r
13344 p.usesFrames = function() {
\r
13345 var tl = this._timeline;
\r
13346 while (tl._timeline) {
\r
13347 tl = tl._timeline;
\r
13349 return (tl === Animation._rootFramesTimeline);
\r
13352 p.rawTime = function() {
\r
13353 return this._paused ? this._totalTime : (this._timeline.rawTime() - this._startTime) * this._timeScale;
\r
13356 return TimelineLite;
\r
13373 * ----------------------------------------------------------------
\r
13375 * ----------------------------------------------------------------
\r
13377 window._gsDefine("TimelineMax", ["TimelineLite","TweenLite","easing.Ease"], function(TimelineLite, TweenLite, Ease) {
\r
13379 var TimelineMax = function(vars) {
\r
13380 TimelineLite.call(this, vars);
\r
13381 this._repeat = this.vars.repeat || 0;
\r
13382 this._repeatDelay = this.vars.repeatDelay || 0;
\r
13384 this._yoyo = (this.vars.yoyo === true);
\r
13385 this._dirty = true;
\r
13387 _tinyNum = 0.0000000001,
\r
13388 _blankArray = [],
\r
13389 _easeNone = new Ease(null, null, 1, 0),
\r
13390 p = TimelineMax.prototype = new TimelineLite();
\r
13392 p.constructor = TimelineMax;
\r
13393 p.kill()._gc = false;
\r
13394 TimelineMax.version = "1.12.1";
\r
13396 p.invalidate = function() {
\r
13397 this._yoyo = (this.vars.yoyo === true);
\r
13398 this._repeat = this.vars.repeat || 0;
\r
13399 this._repeatDelay = this.vars.repeatDelay || 0;
\r
13400 this._uncache(true);
\r
13401 return TimelineLite.prototype.invalidate.call(this);
\r
13404 p.addCallback = function(callback, position, params, scope) {
\r
13405 return this.add( TweenLite.delayedCall(0, callback, params, scope), position);
\r
13408 p.removeCallback = function(callback, position) {
\r
13410 if (position == null) {
\r
13411 this._kill(null, callback);
\r
13413 var a = this.getTweensOf(callback, false),
\r
13415 time = this._parseTimeOrLabel(position);
\r
13416 while (--i > -1) {
\r
13417 if (a[i]._startTime === time) {
\r
13418 a[i]._enabled(false, false);
\r
13426 p.tweenTo = function(position, vars) {
\r
13427 vars = vars || {};
\r
13428 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
13430 for (p in vars) {
\r
13431 copy[p] = vars[p];
\r
13433 copy.time = this._parseTimeOrLabel(position);
\r
13434 duration = (Math.abs(Number(copy.time) - this._time) / this._timeScale) || 0.001;
\r
13435 t = new TweenLite(this, duration, copy);
\r
13436 copy.onStart = function() {
\r
13437 t.target.paused(true);
\r
13438 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
13439 t.duration( Math.abs( t.vars.time - t.target.time()) / t.target._timeScale );
\r
13441 if (vars.onStart) { //in case the user had an onStart in the vars - we don't want to overwrite it.
\r
13442 vars.onStart.apply(vars.onStartScope || t, vars.onStartParams || _blankArray);
\r
13448 p.tweenFromTo = function(fromPosition, toPosition, vars) {
\r
13449 vars = vars || {};
\r
13450 fromPosition = this._parseTimeOrLabel(fromPosition);
\r
13451 vars.startAt = {onComplete:this.seek, onCompleteParams:[fromPosition], onCompleteScope:this};
\r
13452 vars.immediateRender = (vars.immediateRender !== false);
\r
13453 var t = this.tweenTo(toPosition, vars);
\r
13454 return t.duration((Math.abs( t.vars.time - fromPosition) / this._timeScale) || 0.001);
\r
13457 p.render = function(time, suppressEvents, force) {
\r
13459 this._enabled(true, false);
\r
13461 var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
\r
13462 dur = this._duration,
\r
13463 prevTime = this._time,
\r
13464 prevTotalTime = this._totalTime,
\r
13465 prevStart = this._startTime,
\r
13466 prevTimeScale = this._timeScale,
\r
13467 prevRawPrevTime = this._rawPrevTime,
\r
13468 prevPaused = this._paused,
\r
13469 prevCycle = this._cycle,
\r
13470 tween, isComplete, next, callback, internalForce, cycleDuration;
\r
13471 if (time >= totalDur) {
\r
13472 if (!this._locked) {
\r
13473 this._totalTime = totalDur;
\r
13474 this._cycle = this._repeat;
\r
13476 if (!this._reversed) if (!this._hasPausedChild()) {
\r
13477 isComplete = true;
\r
13478 callback = "onComplete";
\r
13479 if (this._duration === 0) if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time && this._first) {
\r
13480 internalForce = true;
\r
13481 if (prevRawPrevTime > _tinyNum) {
\r
13482 callback = "onReverseComplete";
\r
13486 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
13487 if (this._yoyo && (this._cycle & 1) !== 0) {
\r
13488 this._time = time = 0;
\r
13490 this._time = dur;
\r
13491 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
13494 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
\r
13495 if (!this._locked) {
\r
13496 this._totalTime = this._cycle = 0;
\r
13499 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
13500 callback = "onReverseComplete";
\r
13501 isComplete = this._reversed;
\r
13504 this._active = false;
\r
13505 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
13506 internalForce = true;
\r
13508 this._rawPrevTime = time;
\r
13510 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
13511 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
13512 if (!this._initted) {
\r
13513 internalForce = true;
\r
13518 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
13519 internalForce = true;
\r
13521 this._time = this._rawPrevTime = time;
\r
13522 if (!this._locked) {
\r
13523 this._totalTime = time;
\r
13524 if (this._repeat !== 0) {
\r
13525 cycleDuration = dur + this._repeatDelay;
\r
13526 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
13527 if (this._cycle !== 0) if (this._cycle === this._totalTime / cycleDuration) {
\r
13528 this._cycle--; //otherwise when rendered exactly at the end time, it will act as though it is repeating (at the beginning)
\r
13530 this._time = this._totalTime - (this._cycle * cycleDuration);
\r
13531 if (this._yoyo) if ((this._cycle & 1) !== 0) {
\r
13532 this._time = dur - this._time;
\r
13534 if (this._time > dur) {
\r
13535 this._time = dur;
\r
13536 time = dur + 0.0001; //to avoid occasional floating point rounding error
\r
13537 } else if (this._time < 0) {
\r
13538 this._time = time = 0;
\r
13540 time = this._time;
\r
13546 if (this._cycle !== prevCycle) if (!this._locked) {
\r
13548 make sure children at the end/beginning of the timeline are rendered properly. If, for example,
\r
13549 a 3-second long timeline rendered at 2.9 seconds previously, and now renders at 3.2 seconds (which
\r
13550 would get transated to 2.8 seconds if the timeline yoyos or 0.2 seconds if it just repeats), there
\r
13551 could be a callback or a short tween that's at 2.95 or 3 seconds in which wouldn't render. So
\r
13552 we need to push the timeline to the end (and/or beginning depending on its yoyo value). Also we must
\r
13553 ensure that zero-duration tweens at the very beginning or end of the TimelineMax work.
\r
13555 var backwards = (this._yoyo && (prevCycle & 1) !== 0),
\r
13556 wrap = (backwards === (this._yoyo && (this._cycle & 1) !== 0)),
\r
13557 recTotalTime = this._totalTime,
\r
13558 recCycle = this._cycle,
\r
13559 recRawPrevTime = this._rawPrevTime,
\r
13560 recTime = this._time;
\r
13562 this._totalTime = prevCycle * dur;
\r
13563 if (this._cycle < prevCycle) {
\r
13564 backwards = !backwards;
\r
13566 this._totalTime += dur;
\r
13568 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
13570 this._rawPrevTime = (dur === 0) ? prevRawPrevTime - 0.0001 : prevRawPrevTime;
\r
13571 this._cycle = prevCycle;
\r
13572 this._locked = true; //prevents changes to totalTime and skips repeat/yoyo behavior when we recursively call render()
\r
13573 prevTime = (backwards) ? 0 : dur;
\r
13574 this.render(prevTime, suppressEvents, (dur === 0));
\r
13575 if (!suppressEvents) if (!this._gc) {
\r
13576 if (this.vars.onRepeat) {
\r
13577 this.vars.onRepeat.apply(this.vars.onRepeatScope || this, this.vars.onRepeatParams || _blankArray);
\r
13581 prevTime = (backwards) ? dur + 0.0001 : -0.0001;
\r
13582 this.render(prevTime, true, false);
\r
13584 this._locked = false;
\r
13585 if (this._paused && !prevPaused) { //if the render() triggered callback that paused this timeline, we should abort (very rare, but possible)
\r
13588 this._time = recTime;
\r
13589 this._totalTime = recTotalTime;
\r
13590 this._cycle = recCycle;
\r
13591 this._rawPrevTime = recRawPrevTime;
\r
13594 if ((this._time === prevTime || !this._first) && !force && !internalForce) {
\r
13595 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
13596 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
\r
13599 } else if (!this._initted) {
\r
13600 this._initted = true;
\r
13603 if (!this._active) if (!this._paused && this._totalTime !== prevTotalTime && time > 0) {
\r
13604 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
13607 if (prevTotalTime === 0) if (this.vars.onStart) if (this._totalTime !== 0) if (!suppressEvents) {
\r
13608 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
\r
13611 if (this._time >= prevTime) {
\r
13612 tween = this._first;
\r
13614 next = tween._next; //record it here because the value could change after rendering...
\r
13615 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
\r
13617 } else if (tween._active || (tween._startTime <= this._time && !tween._paused && !tween._gc)) {
\r
13618 if (!tween._reversed) {
\r
13619 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
\r
13621 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
\r
13628 tween = this._last;
\r
13630 next = tween._prev; //record it here because the value could change after rendering...
\r
13631 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
\r
13633 } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) {
\r
13634 if (!tween._reversed) {
\r
13635 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
\r
13637 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
\r
13644 if (this._onUpdate) if (!suppressEvents) {
\r
13645 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
\r
13647 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
13648 if (isComplete) {
\r
13649 if (this._timeline.autoRemoveChildren) {
\r
13650 this._enabled(false, false);
\r
13652 this._active = false;
\r
13654 if (!suppressEvents && this.vars[callback]) {
\r
13655 this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
\r
13660 p.getActive = function(nested, tweens, timelines) {
\r
13661 if (nested == null) {
\r
13664 if (tweens == null) {
\r
13667 if (timelines == null) {
\r
13668 timelines = false;
\r
13671 all = this.getChildren(nested, tweens, timelines),
\r
13675 for (i = 0; i < l; i++) {
\r
13677 if (tween.isActive()) {
\r
13678 a[cnt++] = tween;
\r
13685 p.getLabelAfter = function(time) {
\r
13686 if (!time) if (time !== 0) { //faster than isNan()
\r
13687 time = this._time;
\r
13689 var labels = this.getLabelsArray(),
\r
13690 l = labels.length,
\r
13692 for (i = 0; i < l; i++) {
\r
13693 if (labels[i].time > time) {
\r
13694 return labels[i].name;
\r
13700 p.getLabelBefore = function(time) {
\r
13701 if (time == null) {
\r
13702 time = this._time;
\r
13704 var labels = this.getLabelsArray(),
\r
13705 i = labels.length;
\r
13706 while (--i > -1) {
\r
13707 if (labels[i].time < time) {
\r
13708 return labels[i].name;
\r
13714 p.getLabelsArray = function() {
\r
13718 for (p in this._labels) {
\r
13719 a[cnt++] = {time:this._labels[p], name:p};
\r
13721 a.sort(function(a,b) {
\r
13722 return a.time - b.time;
\r
13728 //---- GETTERS / SETTERS -------------------------------------------------------------------------------------------------------
\r
13730 p.progress = function(value) {
\r
13731 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
13734 p.totalProgress = function(value) {
\r
13735 return (!arguments.length) ? this._totalTime / this.totalDuration() : this.totalTime( this.totalDuration() * value, false);
\r
13738 p.totalDuration = function(value) {
\r
13739 if (!arguments.length) {
\r
13740 if (this._dirty) {
\r
13741 TimelineLite.prototype.totalDuration.call(this); //just forces refresh
\r
13742 //Instead of Infinity, we use 999999999999 so that we can accommodate reverses.
\r
13743 this._totalDuration = (this._repeat === -1) ? 999999999999 : this._duration * (this._repeat + 1) + (this._repeatDelay * this._repeat);
\r
13745 return this._totalDuration;
\r
13747 return (this._repeat === -1) ? this : this.duration( (value - (this._repeat * this._repeatDelay)) / (this._repeat + 1) );
\r
13750 p.time = function(value, suppressEvents) {
\r
13751 if (!arguments.length) {
\r
13752 return this._time;
\r
13754 if (this._dirty) {
\r
13755 this.totalDuration();
\r
13757 if (value > this._duration) {
\r
13758 value = this._duration;
\r
13760 if (this._yoyo && (this._cycle & 1) !== 0) {
\r
13761 value = (this._duration - value) + (this._cycle * (this._duration + this._repeatDelay));
\r
13762 } else if (this._repeat !== 0) {
\r
13763 value += this._cycle * (this._duration + this._repeatDelay);
\r
13765 return this.totalTime(value, suppressEvents);
\r
13768 p.repeat = function(value) {
\r
13769 if (!arguments.length) {
\r
13770 return this._repeat;
\r
13772 this._repeat = value;
\r
13773 return this._uncache(true);
\r
13776 p.repeatDelay = function(value) {
\r
13777 if (!arguments.length) {
\r
13778 return this._repeatDelay;
\r
13780 this._repeatDelay = value;
\r
13781 return this._uncache(true);
\r
13784 p.yoyo = function(value) {
\r
13785 if (!arguments.length) {
\r
13786 return this._yoyo;
\r
13788 this._yoyo = value;
\r
13792 p.currentLabel = function(value) {
\r
13793 if (!arguments.length) {
\r
13794 return this.getLabelBefore(this._time + 0.00000001);
\r
13796 return this.seek(value, true);
\r
13799 return TimelineMax;
\r
13815 * ----------------------------------------------------------------
\r
13817 * ----------------------------------------------------------------
\r
13821 var _RAD2DEG = 180 / Math.PI,
\r
13826 Segment = function(a, b, c, d) {
\r
13835 _correlate = ",x,y,z,left,top,right,bottom,marginTop,marginLeft,marginRight,marginBottom,paddingLeft,paddingTop,paddingRight,paddingBottom,backgroundPosition,backgroundPosition_y,",
\r
13836 cubicToQuadratic = function(a, b, c, d) {
\r
13841 mab = (a + b) / 2,
\r
13842 mbc = (b + c) / 2,
\r
13843 mcd = (c + d) / 2,
\r
13844 mabc = (mab + mbc) / 2,
\r
13845 mbcd = (mbc + mcd) / 2,
\r
13846 m8 = (mbcd - mabc) / 8;
\r
13847 q1.b = mab + (a - mab) / 4;
\r
13848 q2.b = mabc + m8;
\r
13849 q1.c = q2.a = (q1.b + q2.b) / 2;
\r
13850 q2.c = q3.a = (mabc + mbcd) / 2;
\r
13851 q3.b = mbcd - m8;
\r
13852 q4.b = mcd + (d - mcd) / 4;
\r
13853 q3.c = q4.a = (q3.b + q4.b) / 2;
\r
13854 return [q1, q2, q3, q4];
\r
13856 _calculateControlPoints = function(a, curviness, quad, basic, correlate) {
\r
13857 var l = a.length - 1,
\r
13860 i, p1, p2, p3, seg, m1, m2, mm, cp2, qb, r1, r2, tl;
\r
13861 for (i = 0; i < l; i++) {
\r
13870 tl = ((r2 + r1) * curviness * 0.25) / (basic ? 0.5 : _r3[i] || 0.5);
\r
13871 m1 = p2 - (p2 - p1) * (basic ? curviness * 0.5 : (r1 !== 0 ? tl / r1 : 0));
\r
13872 m2 = p2 + (p3 - p2) * (basic ? curviness * 0.5 : (r2 !== 0 ? tl / r2 : 0));
\r
13873 mm = p2 - (m1 + (((m2 - m1) * ((r1 * 3 / (r1 + r2)) + 0.5) / 4) || 0));
\r
13875 m1 = p2 - (p2 - p1) * curviness * 0.5;
\r
13876 m2 = p2 + (p3 - p2) * curviness * 0.5;
\r
13877 mm = p2 - (m1 + m2) / 2;
\r
13882 seg.c = cp2 = m1;
\r
13886 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
13889 seg.da = p2 - p1;
\r
13890 seg.ca = cp2 - p1;
\r
13891 seg.ba = cp1 - p1;
\r
13894 qb = cubicToQuadratic(p1, cp1, cp2, p2);
\r
13895 a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]);
\r
13905 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
13906 seg.da = seg.d - seg.a;
\r
13907 seg.ca = seg.c - seg.a;
\r
13908 seg.ba = cp1 - seg.a;
\r
13910 qb = cubicToQuadratic(seg.a, cp1, seg.c, seg.d);
\r
13911 a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]);
\r
13914 _parseAnchors = function(values, p, correlate, prepend) {
\r
13916 l, i, p1, p2, p3, tmp;
\r
13918 values = [prepend].concat(values);
\r
13919 i = values.length;
\r
13920 while (--i > -1) {
\r
13921 if (typeof( (tmp = values[i][p]) ) === "string") if (tmp.charAt(1) === "=") {
\r
13922 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
13926 l = values.length - 2;
\r
13928 a[0] = new Segment(values[0][p], 0, 0, values[(l < -1) ? 0 : 1][p]);
\r
13931 for (i = 0; i < l; i++) {
\r
13932 p1 = values[i][p];
\r
13933 p2 = values[i+1][p];
\r
13934 a[i] = new Segment(p1, 0, 0, p2);
\r
13936 p3 = values[i+2][p];
\r
13937 _r1[i] = (_r1[i] || 0) + (p2 - p1) * (p2 - p1);
\r
13938 _r2[i] = (_r2[i] || 0) + (p3 - p2) * (p3 - p2);
\r
13941 a[i] = new Segment(values[i][p], 0, 0, values[i+1][p]);
\r
13944 bezierThrough = function(values, curviness, quadratic, basic, correlate, prepend) {
\r
13947 first = prepend || values[0],
\r
13948 i, p, a, j, r, l, seamless, last;
\r
13949 correlate = (typeof(correlate) === "string") ? ","+correlate+"," : _correlate;
\r
13950 if (curviness == null) {
\r
13953 for (p in values[0]) {
\r
13956 //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
13957 if (values.length > 1) {
\r
13958 last = values[values.length - 1];
\r
13960 i = props.length;
\r
13961 while (--i > -1) {
\r
13963 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
13964 seamless = false;
\r
13969 values = values.concat(); //duplicate the array to avoid contaminating the original which the user may be reusing for other tweens
\r
13971 values.unshift(prepend);
\r
13973 values.push(values[1]);
\r
13974 prepend = values[values.length - 3];
\r
13977 _r1.length = _r2.length = _r3.length = 0;
\r
13978 i = props.length;
\r
13979 while (--i > -1) {
\r
13981 _corProps[p] = (correlate.indexOf(","+p+",") !== -1);
\r
13982 obj[p] = _parseAnchors(values, p, _corProps[p], prepend);
\r
13985 while (--i > -1) {
\r
13986 _r1[i] = Math.sqrt(_r1[i]);
\r
13987 _r2[i] = Math.sqrt(_r2[i]);
\r
13990 i = props.length;
\r
13991 while (--i > -1) {
\r
13992 if (_corProps[p]) {
\r
13993 a = obj[props[i]];
\r
13994 l = a.length - 1;
\r
13995 for (j = 0; j < l; j++) {
\r
13996 r = a[j+1].da / _r2[j] + a[j].da / _r1[j];
\r
13997 _r3[j] = (_r3[j] || 0) + r * r;
\r
14002 while (--i > -1) {
\r
14003 _r3[i] = Math.sqrt(_r3[i]);
\r
14006 i = props.length;
\r
14007 j = quadratic ? 4 : 1;
\r
14008 while (--i > -1) {
\r
14011 _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
14014 a.splice(a.length - j, j);
\r
14019 _parseBezierData = function(values, type, prepend) {
\r
14020 type = type || "soft";
\r
14022 inc = (type === "cubic") ? 3 : 2,
\r
14023 soft = (type === "soft"),
\r
14025 a, b, c, d, cur, i, j, l, p, cnt, tmp;
\r
14026 if (soft && prepend) {
\r
14027 values = [prepend].concat(values);
\r
14029 if (values == null || values.length < inc + 1) { throw "invalid Bezier data"; }
\r
14030 for (p in values[0]) {
\r
14033 i = props.length;
\r
14034 while (--i > -1) {
\r
14036 obj[p] = cur = [];
\r
14038 l = values.length;
\r
14039 for (j = 0; j < l; j++) {
\r
14040 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
14041 if (soft) if (j > 1) if (j < l - 1) {
\r
14042 cur[cnt++] = (a + cur[cnt-2]) / 2;
\r
14046 l = cnt - inc + 1;
\r
14048 for (j = 0; j < l; j += inc) {
\r
14052 d = (inc === 2) ? 0 : cur[j+3];
\r
14053 cur[cnt++] = tmp = (inc === 3) ? new Segment(a, b, c, d) : new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c);
\r
14055 cur.length = cnt;
\r
14059 _addCubicLengths = function(a, steps, resolution) {
\r
14060 var inc = 1 / resolution,
\r
14062 d, d1, s, da, ca, ba, p, i, inv, bez, index;
\r
14063 while (--j > -1) {
\r
14070 for (i = 1; i <= resolution; i++) {
\r
14073 d = d1 - (d1 = (p * p * da + 3 * inv * (p * ca + inv * ba)) * p);
\r
14074 index = j * resolution + i - 1;
\r
14075 steps[index] = (steps[index] || 0) + d * d;
\r
14079 _parseLengthData = function(obj, resolution) {
\r
14080 resolution = resolution >> 0 || 6;
\r
14085 threshold = resolution - 1,
\r
14087 curLS = [], //current length segments array
\r
14090 _addCubicLengths(obj[p], a, resolution);
\r
14093 for (i = 0; i < l; i++) {
\r
14094 d += Math.sqrt(a[i]);
\r
14095 index = i % resolution;
\r
14096 curLS[index] = d;
\r
14097 if (index === threshold) {
\r
14099 index = (i / resolution) >> 0;
\r
14100 segments[index] = curLS;
\r
14101 lengths[index] = total;
\r
14106 return {length:total, lengths:lengths, segments:segments};
\r
14111 BezierPlugin = window._gsDefine.plugin({
\r
14112 propName: "bezier",
\r
14114 version: "1.3.2",
\r
14118 //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
14119 init: function(target, vars, tween) {
\r
14120 this._target = target;
\r
14121 if (vars instanceof Array) {
\r
14122 vars = {values:vars};
\r
14125 this._round = {};
\r
14126 this._props = [];
\r
14127 this._timeRes = (vars.timeResolution == null) ? 6 : parseInt(vars.timeResolution, 10);
\r
14128 var values = vars.values || [],
\r
14130 second = values[0],
\r
14131 autoRotate = vars.autoRotate || tween.vars.orientToBezier,
\r
14132 p, isFunc, i, j, prepend;
\r
14134 this._autoRotate = autoRotate ? (autoRotate instanceof Array) ? autoRotate : [["x","y","rotation",((autoRotate === true) ? 0 : Number(autoRotate) || 0)]] : null;
\r
14135 for (p in second) {
\r
14136 this._props.push(p);
\r
14139 i = this._props.length;
\r
14140 while (--i > -1) {
\r
14141 p = this._props[i];
\r
14143 this._overwriteProps.push(p);
\r
14144 isFunc = this._func[p] = (typeof(target[p]) === "function");
\r
14145 first[p] = (!isFunc) ? parseFloat(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]();
\r
14146 if (!prepend) if (first[p] !== values[0][p]) {
\r
14150 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
14151 this._segCount = this._beziers[p].length;
\r
14153 if (this._timeRes) {
\r
14154 var ld = _parseLengthData(this._beziers, this._timeRes);
\r
14155 this._length = ld.length;
\r
14156 this._lengths = ld.lengths;
\r
14157 this._segments = ld.segments;
\r
14158 this._l1 = this._li = this._s1 = this._si = 0;
\r
14159 this._l2 = this._lengths[0];
\r
14160 this._curSeg = this._segments[0];
\r
14161 this._s2 = this._curSeg[0];
\r
14162 this._prec = 1 / this._curSeg.length;
\r
14165 if ((autoRotate = this._autoRotate)) {
\r
14166 this._initialRotations = [];
\r
14167 if (!(autoRotate[0] instanceof Array)) {
\r
14168 this._autoRotate = autoRotate = [autoRotate];
\r
14170 i = autoRotate.length;
\r
14171 while (--i > -1) {
\r
14172 for (j = 0; j < 3; j++) {
\r
14173 p = autoRotate[i][j];
\r
14174 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
14176 p = autoRotate[i][2];
\r
14177 this._initialRotations[i] = this._func[p] ? this._func[p].call(this._target) : this._target[p];
\r
14180 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
14184 //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
14185 set: function(v) {
\r
14186 var segments = this._segCount,
\r
14187 func = this._func,
\r
14188 target = this._target,
\r
14189 notStart = (v !== this._startRatio),
\r
14190 curIndex, inv, i, p, b, t, val, l, lengths, curSeg;
\r
14191 if (!this._timeRes) {
\r
14192 curIndex = (v < 0) ? 0 : (v >= 1) ? segments - 1 : (segments * v) >> 0;
\r
14193 t = (v - (curIndex * (1 / segments))) * segments;
\r
14195 lengths = this._lengths;
\r
14196 curSeg = this._curSeg;
\r
14197 v *= this._length;
\r
14199 //find the appropriate segment (if the currently cached one isn't correct)
\r
14200 if (v > this._l2 && i < segments - 1) {
\r
14201 l = segments - 1;
\r
14202 while (i < l && (this._l2 = lengths[++i]) <= v) { }
\r
14203 this._l1 = lengths[i-1];
\r
14205 this._curSeg = curSeg = this._segments[i];
\r
14206 this._s2 = curSeg[(this._s1 = this._si = 0)];
\r
14207 } else if (v < this._l1 && i > 0) {
\r
14208 while (i > 0 && (this._l1 = lengths[--i]) >= v) { }
\r
14209 if (i === 0 && v < this._l1) {
\r
14214 this._l2 = lengths[i];
\r
14216 this._curSeg = curSeg = this._segments[i];
\r
14217 this._s1 = curSeg[(this._si = curSeg.length - 1) - 1] || 0;
\r
14218 this._s2 = curSeg[this._si];
\r
14221 //now find the appropriate sub-segment (we split it into the number of pieces that was defined by "precision" and measured each one)
\r
14224 if (v > this._s2 && i < curSeg.length - 1) {
\r
14225 l = curSeg.length - 1;
\r
14226 while (i < l && (this._s2 = curSeg[++i]) <= v) { }
\r
14227 this._s1 = curSeg[i-1];
\r
14229 } else if (v < this._s1 && i > 0) {
\r
14230 while (i > 0 && (this._s1 = curSeg[--i]) >= v) { }
\r
14231 if (i === 0 && v < this._s1) {
\r
14236 this._s2 = curSeg[i];
\r
14239 t = (i + (v - this._s1) / (this._s2 - this._s1)) * this._prec;
\r
14243 i = this._props.length;
\r
14244 while (--i > -1) {
\r
14245 p = this._props[i];
\r
14246 b = this._beziers[p][curIndex];
\r
14247 val = (t * t * b.da + 3 * inv * (t * b.ca + inv * b.ba)) * t + b.a;
\r
14248 if (this._round[p]) {
\r
14249 val = Math.round(val);
\r
14258 if (this._autoRotate) {
\r
14259 var ar = this._autoRotate,
\r
14260 b2, x1, y1, x2, y2, add, conv;
\r
14262 while (--i > -1) {
\r
14264 add = ar[i][3] || 0;
\r
14265 conv = (ar[i][4] === true) ? 1 : _RAD2DEG;
\r
14266 b = this._beziers[ar[i][0]];
\r
14267 b2 = this._beziers[ar[i][1]];
\r
14269 if (b && b2) { //in case one of the properties got overwritten.
\r
14271 b2 = b2[curIndex];
\r
14273 x1 = b.a + (b.b - b.a) * t;
\r
14274 x2 = b.b + (b.c - b.b) * t;
\r
14275 x1 += (x2 - x1) * t;
\r
14276 x2 += ((b.c + (b.d - b.c) * t) - x2) * t;
\r
14278 y1 = b2.a + (b2.b - b2.a) * t;
\r
14279 y2 = b2.b + (b2.c - b2.b) * t;
\r
14280 y1 += (y2 - y1) * t;
\r
14281 y2 += ((b2.c + (b2.d - b2.c) * t) - y2) * t;
\r
14283 val = notStart ? Math.atan2(y2 - y1, x2 - x1) * conv + add : this._initialRotations[i];
\r
14295 p = BezierPlugin.prototype;
\r
14298 BezierPlugin.bezierThrough = bezierThrough;
\r
14299 BezierPlugin.cubicToQuadratic = cubicToQuadratic;
\r
14300 BezierPlugin._autoCSS = true; //indicates that this plugin can be inserted into the "css" object using the autoCSS feature of TweenLite
\r
14301 BezierPlugin.quadraticToCubic = function(a, b, c) {
\r
14302 return new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c);
\r
14305 BezierPlugin._cssRegister = function() {
\r
14306 var CSSPlugin = window._gsDefine.globals.CSSPlugin;
\r
14307 if (!CSSPlugin) {
\r
14310 var _internals = CSSPlugin._internals,
\r
14311 _parseToProxy = _internals._parseToProxy,
\r
14312 _setPluginRatio = _internals._setPluginRatio,
\r
14313 CSSPropTween = _internals.CSSPropTween;
\r
14314 _internals._registerComplexSpecialProp("bezier", {parser:function(t, e, prop, cssp, pt, plugin) {
\r
14315 if (e instanceof Array) {
\r
14318 plugin = new BezierPlugin();
\r
14319 var values = e.values,
\r
14320 l = values.length - 1,
\r
14321 pluginValues = [],
\r
14327 for (i = 0; i <= l; i++) {
\r
14328 data = _parseToProxy(t, values[i], cssp, pt, plugin, (l !== i));
\r
14329 pluginValues[i] = data.end;
\r
14332 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
14334 v.values = pluginValues;
\r
14335 pt = new CSSPropTween(t, "bezier", 0, 0, data.pt, 2);
\r
14337 pt.plugin = plugin;
\r
14338 pt.setRatio = _setPluginRatio;
\r
14339 if (v.autoRotate === 0) {
\r
14340 v.autoRotate = true;
\r
14342 if (v.autoRotate && !(v.autoRotate instanceof Array)) {
\r
14343 i = (v.autoRotate === true) ? 0 : Number(v.autoRotate);
\r
14344 v.autoRotate = (data.end.left != null) ? [["left","top","rotation",i,false]] : (data.end.x != null) ? [["x","y","rotation",i,false]] : false;
\r
14346 if (v.autoRotate) {
\r
14347 if (!cssp._transform) {
\r
14348 cssp._enableTransforms(false);
\r
14350 data.autoRotate = cssp._target._gsTransform;
\r
14352 plugin._onInitTween(data.proxy, v, cssp._tween);
\r
14357 p._roundProps = function(lookup, value) {
\r
14358 var op = this._overwriteProps,
\r
14360 while (--i > -1) {
\r
14361 if (lookup[op[i]] || lookup.bezier || lookup.bezierThrough) {
\r
14362 this._round[op[i]] = value;
\r
14367 p._kill = function(lookup) {
\r
14368 var a = this._props,
\r
14370 for (p in this._beziers) {
\r
14371 if (p in lookup) {
\r
14372 delete this._beziers[p];
\r
14373 delete this._func[p];
\r
14375 while (--i > -1) {
\r
14376 if (a[i] === p) {
\r
14382 return this._super._kill.call(this, lookup);
\r
14401 * ----------------------------------------------------------------
\r
14403 * ----------------------------------------------------------------
\r
14405 window._gsDefine("plugins.CSSPlugin", ["plugins.TweenPlugin","TweenLite"], function(TweenPlugin, TweenLite) {
\r
14407 /** @constructor **/
\r
14408 var CSSPlugin = function() {
\r
14409 TweenPlugin.call(this, "css");
\r
14410 this._overwriteProps.length = 0;
\r
14411 this.setRatio = CSSPlugin.prototype.setRatio; //speed optimization (avoid prototype lookup on this "hot" method)
\r
14413 _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
14414 _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
14415 _cs, //computed style (we store this in a shared variable to conserve memory and make minification tighter
\r
14416 _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
14417 _specialProps = {},
\r
14418 p = CSSPlugin.prototype = new TweenPlugin("css");
\r
14420 p.constructor = CSSPlugin;
\r
14421 CSSPlugin.version = "1.12.1";
\r
14422 CSSPlugin.API = 2;
\r
14423 CSSPlugin.defaultTransformPerspective = 0;
\r
14424 CSSPlugin.defaultSkewType = "compensated";
\r
14425 p = "px"; //we'll reuse the "p" variable to keep file size down
\r
14426 CSSPlugin.suffixMap = {top:p, right:p, bottom:p, left:p, width:p, height:p, fontSize:p, padding:p, margin:p, perspective:p, lineHeight:""};
\r
14429 var _numExp = /(?:\d|\-\d|\.\d|\-\.\d)+/g,
\r
14430 _relNumExp = /(?:\d|\-\d|\.\d|\-\.\d|\+=\d|\-=\d|\+=.\d|\-=\.\d)+/g,
\r
14431 _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
14432 _NaNExp = /[^\d\-\.]/g,
\r
14433 _suffixExp = /(?:\d|\-|\+|=|#|\.)*/g,
\r
14434 _opacityExp = /opacity *= *([^)]*)/i,
\r
14435 _opacityValExp = /opacity:([^;]*)/i,
\r
14436 _alphaFilterExp = /alpha\(opacity *=.+?\)/i,
\r
14437 _rgbhslExp = /^(rgb|hsl)/,
\r
14438 _capsExp = /([A-Z])/g,
\r
14439 _camelExp = /-([a-z])/gi,
\r
14440 _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
14441 _camelFunc = function(s, g) { return g.toUpperCase(); },
\r
14442 _horizExp = /(?:Left|Right|Width)/i,
\r
14443 _ieGetMatrixExp = /(M11|M12|M21|M22)=[\d\-\.e]+/gi,
\r
14444 _ieSetMatrixExp = /progid\:DXImageTransform\.Microsoft\.Matrix\(.+?\)/i,
\r
14445 _commasOutsideParenExp = /,(?=[^\)]*(?:\(|$))/gi, //finds any commas that are not within parenthesis
\r
14446 _DEG2RAD = Math.PI / 180,
\r
14447 _RAD2DEG = 180 / Math.PI,
\r
14450 _tempDiv = _doc.createElement("div"),
\r
14451 _tempImg = _doc.createElement("img"),
\r
14452 _internals = CSSPlugin._internals = {_specialProps:_specialProps}, //provides a hook to a few internal methods that we need to access from inside other plugins
\r
14453 _agent = navigator.userAgent,
\r
14455 _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
14458 _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
14459 _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
14461 _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
14462 var i = _agent.indexOf("Android"),
\r
14463 d = _doc.createElement("div"), a;
\r
14465 _isSafari = (_agent.indexOf("Safari") !== -1 && _agent.indexOf("Chrome") === -1 && (i === -1 || Number(_agent.substr(i+8, 1)) > 3));
\r
14466 _isSafariLT6 = (_isSafari && (Number(_agent.substr(_agent.indexOf("Version/")+8, 1)) < 6));
\r
14467 _isFirefox = (_agent.indexOf("Firefox") !== -1);
\r
14469 if ((/MSIE ([0-9]{1,}[\.0-9]{0,})/).exec(_agent)) {
\r
14470 _ieVers = parseFloat( RegExp.$1 );
\r
14473 d.innerHTML = "<a title='' style='top:1px;opacity:.55;'>a</a>";
\r
14474 a = d.getElementsByTagName("a")[0];
\r
14475 return a ? /^0.55/.test(a.style.opacity) : false;
\r
14477 _getIEOpacity = function(v) {
\r
14478 return (_opacityExp.test( ((typeof(v) === "string") ? v : (v.currentStyle ? v.currentStyle.filter : v.style.filter) || "") ) ? ( parseFloat( RegExp.$1 ) / 100 ) : 1);
\r
14480 _log = function(s) {//for logging messages, but in a way that won't throw errors in old versions of IE.
\r
14481 if (window.console) {
\r
14482 //console.log(s);
\r
14485 _prefixCSS = "", //the non-camelCase vendor prefix like "-o-", "-moz-", "-ms-", or "-webkit-"
\r
14486 _prefix = "", //camelCase vendor prefix like "O", "ms", "Webkit", or "Moz".
\r
14488 // @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
14489 _checkPropPrefix = function(p, e) {
\r
14490 e = e || _tempDiv;
\r
14493 if (s[p] !== undefined) {
\r
14496 p = p.charAt(0).toUpperCase() + p.substr(1);
\r
14497 a = ["O","Moz","ms","Ms","Webkit"];
\r
14499 while (--i > -1 && s[a[i]+p] === undefined) { }
\r
14501 _prefix = (i === 3) ? "ms" : a[i];
\r
14502 _prefixCSS = "-" + _prefix.toLowerCase() + "-";
\r
14503 return _prefix + p;
\r
14508 _getComputedStyle = _doc.defaultView ? _doc.defaultView.getComputedStyle : function() {},
\r
14511 * @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
14512 * var currentLeft = CSSPlugin.getStyle( document.getElementById("myElement"), "left");
\r
14514 * @param {!Object} t Target element whose style property you want to query
\r
14515 * @param {!string} p Property name (like "left" or "top" or "marginTop", etc.)
\r
14516 * @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
14517 * @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
14518 * @param {string=} dflt Default value that should be returned in the place of null, "none", "auto" or "auto auto".
\r
14519 * @return {?string} The current property value
\r
14521 _getStyle = CSSPlugin.getStyle = function(t, p, cs, calc, dflt) {
\r
14523 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
14524 return _getIEOpacity(t);
\r
14526 if (!calc && t.style[p]) {
\r
14528 } else if ((cs = cs || _getComputedStyle(t))) {
\r
14529 rv = cs[p] || cs.getPropertyValue(p) || cs.getPropertyValue(p.replace(_capsExp, "-$1").toLowerCase());
\r
14530 } else if (t.currentStyle) {
\r
14531 rv = t.currentStyle[p];
\r
14533 return (dflt != null && (!rv || rv === "none" || rv === "auto" || rv === "auto auto")) ? dflt : rv;
\r
14537 * @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
14538 * @param {!Object} t Target element
\r
14539 * @param {!string} p Property name (like "left", "top", "marginLeft", etc.)
\r
14540 * @param {!number} v Value
\r
14541 * @param {string=} sfx Suffix (like "px" or "%" or "em")
\r
14542 * @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
14543 * @return {number} value in pixels
\r
14545 _convertToPixels = _internals.convertToPixels = function(t, p, v, sfx, recurse) {
\r
14546 if (sfx === "px" || !sfx) { return v; }
\r
14547 if (sfx === "auto" || !v) { return 0; }
\r
14548 var horiz = _horizExp.test(p),
\r
14550 style = _tempDiv.style,
\r
14552 pix, cache, time;
\r
14556 if (sfx === "%" && p.indexOf("border") !== -1) {
\r
14557 pix = (v / 100) * (horiz ? t.clientWidth : t.clientHeight);
\r
14559 style.cssText = "border:0 solid red;position:" + _getStyle(t, "position") + ";line-height:0;";
\r
14560 if (sfx === "%" || !node.appendChild) {
\r
14561 node = t.parentNode || _doc.body;
\r
14562 cache = node._gsCache;
\r
14563 time = TweenLite.ticker.frame;
\r
14564 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
14565 return cache.width * v / 100;
\r
14567 style[(horiz ? "width" : "height")] = v + sfx;
\r
14569 style[(horiz ? "borderLeftWidth" : "borderTopWidth")] = v + sfx;
\r
14571 node.appendChild(_tempDiv);
\r
14572 pix = parseFloat(_tempDiv[(horiz ? "offsetWidth" : "offsetHeight")]);
\r
14573 node.removeChild(_tempDiv);
\r
14574 if (horiz && sfx === "%" && CSSPlugin.cacheWidths !== false) {
\r
14575 cache = node._gsCache = node._gsCache || {};
\r
14576 cache.time = time;
\r
14577 cache.width = pix / v * 100;
\r
14579 if (pix === 0 && !recurse) {
\r
14580 pix = _convertToPixels(t, p, v, sfx, true);
\r
14583 return neg ? -pix : pix;
\r
14585 _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
14586 if (_getStyle(t, "position", cs) !== "absolute") { return 0; }
\r
14587 var dim = ((p === "left") ? "Left" : "Top"),
\r
14588 v = _getStyle(t, "margin" + dim, cs);
\r
14589 return t["offset" + dim] - (_convertToPixels(t, p, parseFloat(v), v.replace(_suffixExp, "")) || 0);
\r
14592 // @private returns at object containing ALL of the style properties in camelCase and their associated values.
\r
14593 _getAllStyles = function(t, cs) {
\r
14596 if ((cs = cs || _getComputedStyle(t, null))) {
\r
14597 if ((i = cs.length)) {
\r
14598 while (--i > -1) {
\r
14599 s[cs[i].replace(_camelExp, _camelFunc)] = cs.getPropertyValue(cs[i]);
\r
14601 } else { //Opera behaves differently - cs.length is always 0, so we must do a for...in loop.
\r
14606 } else if ((cs = t.currentStyle || t.style)) {
\r
14608 if (typeof(i) === "string" && s[i] === undefined) {
\r
14609 s[i.replace(_camelExp, _camelFunc)] = cs[i];
\r
14613 if (!_supportsOpacity) {
\r
14614 s.opacity = _getIEOpacity(t);
\r
14616 tr = _getTransform(t, cs, false);
\r
14617 s.rotation = tr.rotation;
\r
14618 s.skewX = tr.skewX;
\r
14619 s.scaleX = tr.scaleX;
\r
14620 s.scaleY = tr.scaleY;
\r
14623 if (_supports3D) {
\r
14625 s.rotationX = tr.rotationX;
\r
14626 s.rotationY = tr.rotationY;
\r
14627 s.scaleZ = tr.scaleZ;
\r
14630 delete s.filters;
\r
14635 // @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
14636 _cssDif = function(t, s1, s2, vars, forceLookup) {
\r
14641 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
14642 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
14643 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
14644 mpt = new MiniPropTween(style, p, style[p], mpt);
\r
14649 for (p in vars) { //copy properties (except className)
\r
14650 if (p !== "className") {
\r
14651 difs[p] = vars[p];
\r
14655 return {difs:difs, firstMPT:mpt};
\r
14657 _dimensions = {width:["Left","Right"], height:["Top","Bottom"]},
\r
14658 _margins = ["marginLeft","marginRight","marginTop","marginBottom"],
\r
14661 * @private Gets the width or height of an element
\r
14662 * @param {!Object} t Target element
\r
14663 * @param {!string} p Property name ("width" or "height")
\r
14664 * @param {Object=} cs Computed style object (if one exists). Just a speed optimization.
\r
14665 * @return {number} Dimension (in pixels)
\r
14667 _getDimension = function(t, p, cs) {
\r
14668 var v = parseFloat((p === "width") ? t.offsetWidth : t.offsetHeight),
\r
14669 a = _dimensions[p],
\r
14671 cs = cs || _getComputedStyle(t, null);
\r
14672 while (--i > -1) {
\r
14673 v -= parseFloat( _getStyle(t, "padding" + a[i], cs, true) ) || 0;
\r
14674 v -= parseFloat( _getStyle(t, "border" + a[i] + "Width", cs, true) ) || 0;
\r
14679 // @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
14680 _parsePosition = function(v, recObj) {
\r
14681 if (v == null || v === "" || v === "auto" || v === "auto auto") { //note: Firefox uses "auto auto" as default whereas Chrome uses "auto".
\r
14684 var a = v.split(" "),
\r
14685 x = (v.indexOf("left") !== -1) ? "0%" : (v.indexOf("right") !== -1) ? "100%" : a[0],
\r
14686 y = (v.indexOf("top") !== -1) ? "0%" : (v.indexOf("bottom") !== -1) ? "100%" : a[1];
\r
14689 } else if (y === "center") {
\r
14692 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
14696 recObj.oxp = (x.indexOf("%") !== -1);
\r
14697 recObj.oyp = (y.indexOf("%") !== -1);
\r
14698 recObj.oxr = (x.charAt(1) === "=");
\r
14699 recObj.oyr = (y.charAt(1) === "=");
\r
14700 recObj.ox = parseFloat(x.replace(_NaNExp, ""));
\r
14701 recObj.oy = parseFloat(y.replace(_NaNExp, ""));
\r
14703 return x + " " + y + ((a.length > 2) ? " " + a[2] : "");
\r
14707 * @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
14708 * @param {(number|string)} e End value which is typically a string, but could be a number
\r
14709 * @param {(number|string)} b Beginning value which is typically a string but could be a number
\r
14710 * @return {number} Amount of change between the beginning and ending values (relative values that have a "+=" or "-=" are recognized)
\r
14712 _parseChange = function(e, b) {
\r
14713 return (typeof(e) === "string" && e.charAt(1) === "=") ? parseInt(e.charAt(0) + "1", 10) * parseFloat(e.substr(2)) : parseFloat(e) - parseFloat(b);
\r
14717 * @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
14718 * @param {Object} v Value to be parsed
\r
14719 * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter)
\r
14720 * @return {number} Parsed value
\r
14722 _parseVal = function(v, d) {
\r
14723 return (v == null) ? d : (typeof(v) === "string" && v.charAt(1) === "=") ? parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) + d : parseFloat(v);
\r
14727 * @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
14728 * @param {Object} v Value to be parsed
\r
14729 * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter)
\r
14730 * @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
14731 * @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
14732 * @return {number} parsed angle in radians
\r
14734 _parseAngle = function(v, d, p, directionalEnd) {
\r
14735 var min = 0.000001,
\r
14736 cap, split, dif, result;
\r
14739 } else if (typeof(v) === "number") {
\r
14743 split = v.split("_");
\r
14744 dif = Number(split[0].replace(_NaNExp, "")) * ((v.indexOf("rad") === -1) ? 1 : _RAD2DEG) - ((v.charAt(1) === "=") ? 0 : d);
\r
14745 if (split.length) {
\r
14746 if (directionalEnd) {
\r
14747 directionalEnd[p] = d + dif;
\r
14749 if (v.indexOf("short") !== -1) {
\r
14751 if (dif !== dif % (cap / 2)) {
\r
14752 dif = (dif < 0) ? dif + cap : dif - cap;
\r
14755 if (v.indexOf("_cw") !== -1 && dif < 0) {
\r
14756 dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
\r
14757 } else if (v.indexOf("ccw") !== -1 && dif > 0) {
\r
14758 dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
\r
14761 result = d + dif;
\r
14763 if (result < min && result > -min) {
\r
14769 _colorLookup = {aqua:[0,255,255],
\r
14771 silver:[192,192,192],
\r
14773 maroon:[128,0,0],
\r
14774 teal:[0,128,128],
\r
14777 white:[255,255,255],
\r
14778 fuchsia:[255,0,255],
\r
14779 olive:[128,128,0],
\r
14780 yellow:[255,255,0],
\r
14781 orange:[255,165,0],
\r
14782 gray:[128,128,128],
\r
14783 purple:[128,0,128],
\r
14786 pink:[255,192,203],
\r
14787 cyan:[0,255,255],
\r
14788 transparent:[255,255,255,0]},
\r
14790 _hue = function(h, m1, m2) {
\r
14791 h = (h < 0) ? h + 1 : (h > 1) ? h - 1 : h;
\r
14792 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
14796 * @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
14797 * @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
14798 * @return {Array.<number>} An array containing red, green, and blue (and optionally alpha) in that order.
\r
14800 _parseColor = function(v) {
\r
14801 var c1, c2, c3, h, s, l;
\r
14802 if (!v || v === "") {
\r
14803 return _colorLookup.black;
\r
14805 if (typeof(v) === "number") {
\r
14806 return [v >> 16, (v >> 8) & 255, v & 255];
\r
14808 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
14809 v = v.substr(0, v.length - 1);
\r
14811 if (_colorLookup[v]) {
\r
14812 return _colorLookup[v];
\r
14814 if (v.charAt(0) === "#") {
\r
14815 if (v.length === 4) { //for shorthand like #9F0
\r
14816 c1 = v.charAt(1),
\r
14817 c2 = v.charAt(2),
\r
14818 c3 = v.charAt(3);
\r
14819 v = "#" + c1 + c1 + c2 + c2 + c3 + c3;
\r
14821 v = parseInt(v.substr(1), 16);
\r
14822 return [v >> 16, (v >> 8) & 255, v & 255];
\r
14824 if (v.substr(0, 3) === "hsl") {
\r
14825 v = v.match(_numExp);
\r
14826 h = (Number(v[0]) % 360) / 360;
\r
14827 s = Number(v[1]) / 100;
\r
14828 l = Number(v[2]) / 100;
\r
14829 c2 = (l <= 0.5) ? l * (s + 1) : l + s - l * s;
\r
14831 if (v.length > 3) {
\r
14832 v[3] = Number(v[3]);
\r
14834 v[0] = _hue(h + 1 / 3, c1, c2);
\r
14835 v[1] = _hue(h, c1, c2);
\r
14836 v[2] = _hue(h - 1 / 3, c1, c2);
\r
14839 v = v.match(_numExp) || _colorLookup.transparent;
\r
14840 v[0] = Number(v[0]);
\r
14841 v[1] = Number(v[1]);
\r
14842 v[2] = Number(v[2]);
\r
14843 if (v.length > 3) {
\r
14844 v[3] = Number(v[3]);
\r
14848 _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
14850 for (p in _colorLookup) {
\r
14851 _colorExp += "|" + p + "\\b";
\r
14853 _colorExp = new RegExp(_colorExp+")", "gi");
\r
14856 * @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
14857 * @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
14858 * @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
14859 * @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
14860 * @return {Function} formatter function
\r
14862 var _getFormatter = function(dflt, clr, collapsible, multi) {
\r
14863 if (dflt == null) {
\r
14864 return function(v) {return v;};
\r
14866 var dColor = clr ? (dflt.match(_colorExp) || [""])[0] : "",
\r
14867 dVals = dflt.split(dColor).join("").match(_valuesExp) || [],
\r
14868 pfx = dflt.substr(0, dflt.indexOf(dVals[0])),
\r
14869 sfx = (dflt.charAt(dflt.length - 1) === ")") ? ")" : "",
\r
14870 delim = (dflt.indexOf(" ") !== -1) ? " " : ",",
\r
14871 numVals = dVals.length,
\r
14872 dSfx = (numVals > 0) ? dVals[0].replace(_numExp, "") : "",
\r
14875 return function(v) {return v;};
\r
14878 formatter = function(v) {
\r
14879 var color, vals, i, a;
\r
14880 if (typeof(v) === "number") {
\r
14882 } else if (multi && _commasOutsideParenExp.test(v)) {
\r
14883 a = v.replace(_commasOutsideParenExp, "|").split("|");
\r
14884 for (i = 0; i < a.length; i++) {
\r
14885 a[i] = formatter(a[i]);
\r
14887 return a.join(",");
\r
14889 color = (v.match(_colorExp) || [dColor])[0];
\r
14890 vals = v.split(color).join("").match(_valuesExp) || [];
\r
14892 if (numVals > i--) {
\r
14893 while (++i < numVals) {
\r
14894 vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i];
\r
14897 return pfx + vals.join(delim) + delim + color + sfx + (v.indexOf("inset") !== -1 ? " inset" : "");
\r
14899 return formatter;
\r
14902 formatter = function(v) {
\r
14904 if (typeof(v) === "number") {
\r
14906 } else if (multi && _commasOutsideParenExp.test(v)) {
\r
14907 a = v.replace(_commasOutsideParenExp, "|").split("|");
\r
14908 for (i = 0; i < a.length; i++) {
\r
14909 a[i] = formatter(a[i]);
\r
14911 return a.join(",");
\r
14913 vals = v.match(_valuesExp) || [];
\r
14915 if (numVals > i--) {
\r
14916 while (++i < numVals) {
\r
14917 vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i];
\r
14920 return pfx + vals.join(delim) + sfx;
\r
14922 return formatter;
\r
14926 * @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
14927 * @param {!string} props a comma-delimited list of property names in order from top to left, like "marginTop,marginRight,marginBottom,marginLeft"
\r
14928 * @return {Function} a formatter function
\r
14930 _getEdgeParser = function(props) {
\r
14931 props = props.split(",");
\r
14932 return function(t, e, p, cssp, pt, plugin, vars) {
\r
14933 var a = (e + "").split(" "),
\r
14936 for (i = 0; i < 4; i++) {
\r
14937 vars[props[i]] = a[i] = a[i] || a[(((i - 1) / 2) >> 0)];
\r
14939 return cssp.parse(t, vars, pt, plugin);
\r
14943 // @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
14944 _setPluginRatio = _internals._setPluginRatio = function(v) {
\r
14945 this.plugin.setRatio(v);
\r
14946 var d = this.data,
\r
14948 mpt = d.firstMPT,
\r
14952 val = proxy[mpt.v];
\r
14954 val = Math.round(val);
\r
14955 } else if (val < min && val > -min) {
\r
14958 mpt.t[mpt.p] = val;
\r
14961 if (d.autoRotate) {
\r
14962 d.autoRotate.rotation = proxy.rotation;
\r
14964 //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
14966 mpt = d.firstMPT;
\r
14970 pt.e = pt.s + pt.xs0;
\r
14971 } else if (pt.type === 1) {
\r
14972 str = pt.xs0 + pt.s + pt.xs1;
\r
14973 for (i = 1; i < pt.l; i++) {
\r
14974 str += pt["xn"+i] + pt["xs"+(i+1)];
\r
14984 * @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
14985 * @param {!Object} t target object whose property we're tweening (often a CSSPropTween)
\r
14986 * @param {!string} p property name
\r
14987 * @param {(number|string|object)} v value
\r
14988 * @param {MiniPropTween=} next next MiniPropTween in the linked list
\r
14989 * @param {boolean=} r if true, the tweened value should be rounded to the nearest integer
\r
14991 MiniPropTween = function(t, p, v, next, r) {
\r
14997 next._prev = this;
\r
14998 this._next = next;
\r
15003 * @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
15004 * This method returns an object that has the following properties:
\r
15005 * - 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
15006 * - 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
15007 * - firstMPT: the first MiniPropTween in the linked list
\r
15008 * - 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
15009 * @param {!Object} t target object to be tweened
\r
15010 * @param {!(Object|string)} vars the object containing the information about the tweening values (typically the end/destination values) that should be parsed
\r
15011 * @param {!CSSPlugin} cssp The CSSPlugin instance
\r
15012 * @param {CSSPropTween=} pt the next CSSPropTween in the linked list
\r
15013 * @param {TweenPlugin=} plugin the external TweenPlugin instance that will be handling tweening the numeric values
\r
15014 * @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
15015 * @return An object containing the following properties: proxy, end, firstMPT, and pt (see above for descriptions)
\r
15017 _parseToProxy = _internals._parseToProxy = function(t, vars, cssp, pt, plugin, shallow) {
\r
15021 transform = cssp._transform,
\r
15022 oldForce = _forcePT,
\r
15023 i, p, xp, mpt, firstPT;
\r
15024 cssp._transform = null;
\r
15026 pt = firstPT = cssp.parse(t, vars, pt, plugin);
\r
15027 _forcePT = oldForce;
\r
15028 //break off from the linked list so the new ones are isolated.
\r
15030 cssp._transform = transform;
\r
15032 bpt._prev = null;
\r
15034 bpt._prev._next = null;
\r
15038 while (pt && pt !== bpt) {
\r
15039 if (pt.type <= 1) {
\r
15041 end[p] = pt.s + pt.c;
\r
15044 mpt = new MiniPropTween(pt, "s", p, mpt, pt.r);
\r
15047 if (pt.type === 1) {
\r
15049 while (--i > 0) {
\r
15051 p = pt.p + "_" + xp;
\r
15052 end[p] = pt.data[xp];
\r
15053 start[p] = pt[xp];
\r
15055 mpt = new MiniPropTween(pt, xp, p, mpt, pt.rxp[xp]);
\r
15062 return {proxy:start, end:end, firstMPT:mpt, pt:firstPT};
\r
15068 * @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
15069 * CSSPropTweens have the following optional properties as well (not defined through the constructor):
\r
15070 * - 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
15071 * - 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
15072 * - 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
15073 * - 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
15074 * - 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
15075 * @param {!Object} t Target object whose property will be tweened. Often a DOM element, but not always. It could be anything.
\r
15076 * @param {string} p Property to tween (name). For example, to tween element.width, p would be "width".
\r
15077 * @param {number} s Starting numeric value
\r
15078 * @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
15079 * @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
15080 * @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
15081 * @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
15082 * @param {boolean=} r If true, the value(s) should be rounded
\r
15083 * @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
15084 * @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
15085 * @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
15087 CSSPropTween = _internals.CSSPropTween = function(t, p, s, c, next, type, n, r, pr, b, e) {
\r
15088 this.t = t; //target
\r
15089 this.p = p; //property
\r
15090 this.s = s; //starting value
\r
15091 this.c = c; //change value
\r
15092 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
15093 if (!(t instanceof CSSPropTween)) {
\r
15094 _overwriteProps.push(this.n);
\r
15096 this.r = r; //round (boolean)
\r
15097 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
15100 _hasPriority = true;
\r
15102 this.b = (b === undefined) ? s : b;
\r
15103 this.e = (e === undefined) ? s + c : e;
\r
15105 this._next = next;
\r
15106 next._prev = this;
\r
15111 * 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
15112 * 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
15113 * 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
15114 * 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
15116 * @param {!Object} t Target whose property will be tweened
\r
15117 * @param {!string} p Property that will be tweened (its name, like "left" or "backgroundColor" or "boxShadow")
\r
15118 * @param {string} b Beginning value
\r
15119 * @param {string} e Ending value
\r
15120 * @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
15121 * @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
15122 * @param {?CSSPropTween} pt CSSPropTween instance that is the current head of the linked list (we'll prepend to this).
\r
15123 * @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
15124 * @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
15125 * @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
15126 * @return {CSSPropTween} The first CSSPropTween in the linked list which includes the new one(s) added by the parseComplex() call.
\r
15128 _parseComplex = CSSPlugin.parseComplex = function(t, p, b, e, clrs, dflt, pt, pr, plugin, setRatio) {
\r
15129 //DEBUG: _log("parseComplex: "+p+", b: "+b+", e: "+e);
\r
15130 b = b || dflt || "";
\r
15131 pt = new CSSPropTween(t, p, 0, 0, pt, (setRatio ? 2 : 1), null, false, pr, b, e);
\r
15132 e += ""; //ensures it's a string
\r
15133 var ba = b.split(", ").join(",").split(" "), //beginning array
\r
15134 ea = e.split(", ").join(",").split(" "), //ending array
\r
15136 autoRound = (_autoRound !== false),
\r
15137 i, xi, ni, bv, ev, bnums, enums, bn, rgba, temp, cv, str;
\r
15138 if (e.indexOf(",") !== -1 || b.indexOf(",") !== -1) {
\r
15139 ba = ba.join(" ").replace(_commasOutsideParenExp, ", ").split(" ");
\r
15140 ea = ea.join(" ").replace(_commasOutsideParenExp, ", ").split(" ");
\r
15143 if (l !== ea.length) {
\r
15144 //DEBUG: _log("mismatched formatting detected on " + p + " (" + b + " vs " + e + ")");
\r
15145 ba = (dflt || "").split(" ");
\r
15148 pt.plugin = plugin;
\r
15149 pt.setRatio = setRatio;
\r
15150 for (i = 0; i < l; i++) {
\r
15153 bn = parseFloat(bv);
\r
15155 //if the value begins with a number (most common). It's fine if it has a suffix like px
\r
15156 if (bn || bn === 0) {
\r
15157 pt.appendXtra("", bn, _parseChange(ev, bn), ev.replace(_relNumExp, ""), (autoRound && ev.indexOf("px") !== -1), true);
\r
15159 //if the value is a color
\r
15160 } else if (clrs && (bv.charAt(0) === "#" || _colorLookup[bv] || _rgbhslExp.test(bv))) {
\r
15161 str = ev.charAt(ev.length - 1) === "," ? ")," : ")"; //if there's a comma at the end, retain it.
\r
15162 bv = _parseColor(bv);
\r
15163 ev = _parseColor(ev);
\r
15164 rgba = (bv.length + ev.length > 6);
\r
15165 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
15166 pt["xs" + pt.l] += pt.l ? " transparent" : "transparent";
\r
15167 pt.e = pt.e.split(ea[i]).join("transparent");
\r
15169 if (!_supportsOpacity) { //old versions of IE don't support rgba().
\r
15172 pt.appendXtra((rgba ? "rgba(" : "rgb("), bv[0], ev[0] - bv[0], ",", true, true)
\r
15173 .appendXtra("", bv[1], ev[1] - bv[1], ",", true)
\r
15174 .appendXtra("", bv[2], ev[2] - bv[2], (rgba ? "," : str), true);
\r
15176 bv = (bv.length < 4) ? 1 : bv[3];
\r
15177 pt.appendXtra("", bv, ((ev.length < 4) ? 1 : ev[3]) - bv, str, false);
\r
15182 bnums = bv.match(_numExp); //gets each group of numbers in the beginning value string and drops them into an array
\r
15184 //if no number is found, treat it as a non-tweening value and just append the string to the current xs.
\r
15186 pt["xs" + pt.l] += pt.l ? " " + bv : bv;
\r
15188 //loop through all the numbers that are found and construct the extra values on the pt.
\r
15190 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
15191 if (!enums || enums.length !== bnums.length) {
\r
15192 //DEBUG: _log("mismatched formatting detected on " + p + " (" + b + " vs " + e + ")");
\r
15196 for (xi = 0; xi < bnums.length; xi++) {
\r
15198 temp = bv.indexOf(cv, ni);
\r
15199 pt.appendXtra(bv.substr(ni, temp - ni), Number(cv), _parseChange(enums[xi], cv), "", (autoRound && bv.substr(temp + cv.length, 2) === "px"), (xi === 0));
\r
15200 ni = temp + cv.length;
\r
15202 pt["xs" + pt.l] += bv.substr(ni);
\r
15206 //if there are relative values ("+=" or "-=" prefix), we need to adjust the ending value to eliminate the prefixes and combine the values properly.
\r
15207 if (e.indexOf("=") !== -1) if (pt.data) {
\r
15208 str = pt.xs0 + pt.data.s;
\r
15209 for (i = 1; i < pt.l; i++) {
\r
15210 str += pt["xs" + i] + pt.data["xn" + i];
\r
15212 pt.e = str + pt["xs" + i];
\r
15218 return pt.xfirst || pt;
\r
15223 p = CSSPropTween.prototype;
\r
15224 p.l = p.pr = 0; //length (number of extra properties like xn1, xn2, xn3, etc.
\r
15225 while (--i > 0) {
\r
15227 p["xs" + i] = "";
\r
15230 p._next = p._prev = p.xfirst = p.data = p.plugin = p.setRatio = p.rxp = null;
\r
15234 * 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
15235 * xs0:"rect(", s:10, xs1:"px, ", xn1:5, xs2:"px, ", xn2:0, xs3:"px, ", xn3:20, xn4:"px)"
\r
15236 * And they'd all get joined together when the CSSPlugin renders (in the setRatio() method).
\r
15237 * @param {string=} pfx Prefix (if any)
\r
15238 * @param {!number} s Starting value
\r
15239 * @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
15240 * @param {string=} sfx Suffix (if any)
\r
15241 * @param {boolean=} r Round (if true).
\r
15242 * @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
15243 * @return {CSSPropTween} returns itself so that multiple methods can be chained together.
\r
15245 p.appendXtra = function(pfx, s, c, sfx, r, pad) {
\r
15248 pt["xs" + l] += (pad && l) ? " " + pfx : pfx || "";
\r
15249 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
15250 pt["xs" + l] += s + (sfx || "");
\r
15254 pt.type = pt.setRatio ? 2 : 1;
\r
15255 pt["xs" + pt.l] = sfx || "";
\r
15257 pt.data["xn" + l] = s + c;
\r
15258 pt.rxp["xn" + l] = r; //round extra property (we need to tap into this in the _parseToProxy() method)
\r
15259 pt["xn" + l] = s;
\r
15260 if (!pt.plugin) {
\r
15261 pt.xfirst = new CSSPropTween(pt, "xn" + l, s, c, pt.xfirst || pt, 0, pt.n, r, pt.pr);
\r
15262 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
15266 pt.data = {s:s + c};
\r
15275 * @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
15276 * @param {!string} p Property name (like "boxShadow" or "throwProps")
\r
15277 * @param {Object=} options An object containing any of the following configuration options:
\r
15278 * - defaultValue: the default value
\r
15279 * - 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
15280 * - 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
15281 * - prefix: if true, we'll determine whether or not this property requires a vendor prefix (like Webkit or Moz or ms or O)
\r
15282 * - color: set this to true if the value for this SpecialProp may contain color-related values like rgb(), rgba(), etc.
\r
15283 * - priority: priority in the linked list order. Higher priority SpecialProps will be updated before lower priority ones. The default priority is 0.
\r
15284 * - multi: if true, the formatter should accommodate a comma-delimited list of values, like boxShadow could have multiple boxShadows listed out.
\r
15285 * - 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
15286 * - 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
15288 var SpecialProp = function(p, options) {
\r
15289 options = options || {};
\r
15290 this.p = options.prefix ? _checkPropPrefix(p) || p : p;
\r
15291 _specialProps[p] = _specialProps[this.p] = this;
\r
15292 this.format = options.formatter || _getFormatter(options.defaultValue, options.color, options.collapsible, options.multi);
\r
15293 if (options.parser) {
\r
15294 this.parse = options.parser;
\r
15296 this.clrs = options.color;
\r
15297 this.multi = options.multi;
\r
15298 this.keyword = options.keyword;
\r
15299 this.dflt = options.defaultValue;
\r
15300 this.pr = options.priority || 0;
\r
15303 //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
15304 _registerComplexSpecialProp = _internals._registerComplexSpecialProp = function(p, options, defaults) {
\r
15305 if (typeof(options) !== "object") {
\r
15306 options = {parser:defaults}; //to make backwards compatible with older versions of BezierPlugin and ThrowPropsPlugin
\r
15308 var a = p.split(","),
\r
15309 d = options.defaultValue,
\r
15311 defaults = defaults || [d];
\r
15312 for (i = 0; i < a.length; i++) {
\r
15313 options.prefix = (i === 0 && options.prefix);
\r
15314 options.defaultValue = defaults[i] || d;
\r
15315 temp = new SpecialProp(a[i], options);
\r
15319 //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
15320 _registerPluginProp = function(p) {
\r
15321 if (!_specialProps[p]) {
\r
15322 var pluginName = p.charAt(0).toUpperCase() + p.substr(1) + "Plugin";
\r
15323 _registerComplexSpecialProp(p, {parser:function(t, e, p, cssp, pt, plugin, vars) {
\r
15324 var pluginClass = (window.GreenSockGlobals || window).com.greensock.plugins[pluginName];
\r
15325 if (!pluginClass) {
\r
15326 _log("Error: " + pluginName + " js file not loaded.");
\r
15329 pluginClass._cssRegister();
\r
15330 return _specialProps[p].parse(t, e, p, cssp, pt, plugin, vars);
\r
15336 p = SpecialProp.prototype;
\r
15339 * 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
15340 * @param {!Object} t target element
\r
15341 * @param {(string|number|object)} b beginning value
\r
15342 * @param {(string|number|object)} e ending (destination) value
\r
15343 * @param {CSSPropTween=} pt next CSSPropTween in the linked list
\r
15344 * @param {TweenPlugin=} plugin If another plugin will be tweening the complex value, that TweenPlugin instance goes here.
\r
15345 * @param {function=} setRatio If a custom setRatio() method should be used to handle this complex value, that goes here.
\r
15346 * @return {CSSPropTween=} First CSSPropTween in the linked list
\r
15348 p.parseComplex = function(t, b, e, pt, plugin, setRatio) {
\r
15349 var kwd = this.keyword,
\r
15350 i, ba, ea, l, bi, ei;
\r
15351 //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
15352 if (this.multi) if (_commasOutsideParenExp.test(e) || _commasOutsideParenExp.test(b)) {
\r
15353 ba = b.replace(_commasOutsideParenExp, "|").split("|");
\r
15354 ea = e.replace(_commasOutsideParenExp, "|").split("|");
\r
15355 } else if (kwd) {
\r
15360 l = (ea.length > ba.length) ? ea.length : ba.length;
\r
15361 for (i = 0; i < l; i++) {
\r
15362 b = ba[i] = ba[i] || this.dflt;
\r
15363 e = ea[i] = ea[i] || this.dflt;
\r
15365 bi = b.indexOf(kwd);
\r
15366 ei = e.indexOf(kwd);
\r
15368 e = (ei === -1) ? ea : ba;
\r
15369 e[i] += " " + kwd;
\r
15373 b = ba.join(", ");
\r
15374 e = ea.join(", ");
\r
15376 return _parseComplex(t, this.p, b, e, this.clrs, this.dflt, pt, this.pr, plugin, setRatio);
\r
15380 * 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
15381 * this._firstPT = sp.parse(element, "5px 10px 20px rgb(2550,102,51)", "boxShadow", this);
\r
15382 * 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
15383 * @param {!Object} t Target object whose property is being tweened
\r
15384 * @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
15385 * @param {!string} p Property name
\r
15386 * @param {!CSSPlugin} cssp The CSSPlugin instance that should be associated with this tween.
\r
15387 * @param {?CSSPropTween} pt The CSSPropTween that is the current head of the linked list (we'll prepend to it)
\r
15388 * @param {TweenPlugin=} plugin If a plugin will be used to tween the parsed value, this is the plugin instance.
\r
15389 * @param {Object=} vars Original vars object that contains the data for parsing.
\r
15390 * @return {CSSPropTween} The first CSSPropTween in the linked list which includes the new one(s) added by the parse() call.
\r
15392 p.parse = function(t, e, p, cssp, pt, plugin, vars) {
\r
15393 return this.parseComplex(t.style, this.format(_getStyle(t, this.p, _cs, false, this.dflt)), this.format(e), pt, plugin);
\r
15397 * 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
15398 * 1) Target object whose property should be tweened (typically a DOM element)
\r
15399 * 2) The end/destination value (could be a string, number, object, or whatever you want)
\r
15400 * 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
15402 * 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
15404 * CSSPlugin.registerSpecialProp("myCustomProp", function(target, value, tween) {
\r
15405 * var start = target.style.width;
\r
15406 * return function(ratio) {
\r
15407 * target.style.width = (start + value * ratio) + "px";
\r
15408 * console.log("set width to " + target.style.width);
\r
15412 * Then, when I do this tween, it will trigger my special property:
\r
15414 * TweenLite.to(element, 1, {css:{myCustomProp:100}});
\r
15416 * In the example, of course, we're just changing the width, but you can do anything you want.
\r
15418 * @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
15419 * @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
15420 * @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
15422 CSSPlugin.registerSpecialProp = function(name, onInitTween, priority) {
\r
15423 _registerComplexSpecialProp(name, {parser:function(t, e, p, cssp, pt, plugin, vars) {
\r
15424 var rv = new CSSPropTween(t, p, 0, 0, pt, 2, p, false, priority);
\r
15425 rv.plugin = plugin;
\r
15426 rv.setRatio = onInitTween(t, e, cssp._tween, p);
\r
15428 }, priority:priority});
\r
15438 //transform-related methods and properties
\r
15439 var _transformProps = ("scaleX,scaleY,scaleZ,x,y,z,skewX,skewY,rotation,rotationX,rotationY,perspective").split(","),
\r
15440 _transformProp = _checkPropPrefix("transform"), //the Javascript (camelCase) transform property, like msTransform, WebkitTransform, MozTransform, or OTransform.
\r
15441 _transformPropCSS = _prefixCSS + "transform",
\r
15442 _transformOriginProp = _checkPropPrefix("transformOrigin"),
\r
15443 _supports3D = (_checkPropPrefix("perspective") !== null),
\r
15444 Transform = _internals.Transform = function() {
\r
15449 * 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
15450 * @param {!Object} t target element
\r
15451 * @param {Object=} cs computed style object (optional)
\r
15452 * @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
15453 * @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
15454 * @return {object} object containing all of the transform properties/values like {x:0, y:0, z:0, scaleX:1...}
\r
15456 _getTransform = _internals.getTransform = function(t, cs, rec, parse) {
\r
15457 if (t._gsTransform && rec && !parse) {
\r
15458 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
15460 var tm = rec ? t._gsTransform || new Transform() : new Transform(),
\r
15461 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
15464 minAngle = 179.99,
\r
15465 minPI = minAngle * _DEG2RAD,
\r
15466 zOrigin = _supports3D ? parseFloat(_getStyle(t, _transformOriginProp, cs, false, "0 0 0").split(" ")[2]) || tm.zOrigin || 0 : 0,
\r
15467 s, m, i, n, dec, scaleX, scaleY, rotation, skewX, difX, difY, difR, difS;
\r
15468 if (_transformProp) {
\r
15469 s = _getStyle(t, _transformPropCSS, cs, true);
\r
15470 } else if (t.currentStyle) {
\r
15471 //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
15472 s = t.currentStyle.filter.match(_ieGetMatrixExp);
\r
15473 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
15475 //split the matrix values out into an array (m for matrix)
\r
15476 m = (s || "").match(/(?:\-|\b)[\d\-\.e]+\b/gi) || [];
\r
15478 while (--i > -1) {
\r
15479 n = Number(m[i]);
\r
15480 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
15482 if (m.length === 16) {
\r
15484 //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
15485 var a13 = m[8], a23 = m[9], a33 = m[10],
\r
15486 a14 = m[12], a24 = m[13], a34 = m[14];
\r
15488 //we manually compensate for non-zero z component of transformOrigin to work around bugs in Safari
\r
15489 if (tm.zOrigin) {
\r
15490 a34 = -tm.zOrigin;
\r
15491 a14 = a13*a34-m[12];
\r
15492 a24 = a23*a34-m[13];
\r
15493 a34 = a33*a34+tm.zOrigin-m[14];
\r
15496 //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
15497 if (!rec || parse || tm.rotationX == null) {
\r
15498 var a11 = m[0], a21 = m[1], a31 = m[2], a41 = m[3],
\r
15499 a12 = m[4], a22 = m[5], a32 = m[6], a42 = m[7],
\r
15501 angle = Math.atan2(a32, a33),
\r
15502 xFlip = (angle < -minPI || angle > minPI),
\r
15503 t1, t2, t3, cos, sin, yFlip, zFlip;
\r
15504 tm.rotationX = angle * _RAD2DEG;
\r
15507 cos = Math.cos(-angle);
\r
15508 sin = Math.sin(-angle);
\r
15509 t1 = a12*cos+a13*sin;
\r
15510 t2 = a22*cos+a23*sin;
\r
15511 t3 = a32*cos+a33*sin;
\r
15512 a13 = a12*-sin+a13*cos;
\r
15513 a23 = a22*-sin+a23*cos;
\r
15514 a33 = a32*-sin+a33*cos;
\r
15515 a43 = a42*-sin+a43*cos;
\r
15521 angle = Math.atan2(a13, a11);
\r
15522 tm.rotationY = angle * _RAD2DEG;
\r
15524 yFlip = (angle < -minPI || angle > minPI);
\r
15525 cos = Math.cos(-angle);
\r
15526 sin = Math.sin(-angle);
\r
15527 t1 = a11*cos-a13*sin;
\r
15528 t2 = a21*cos-a23*sin;
\r
15529 t3 = a31*cos-a33*sin;
\r
15530 a23 = a21*sin+a23*cos;
\r
15531 a33 = a31*sin+a33*cos;
\r
15532 a43 = a41*sin+a43*cos;
\r
15538 angle = Math.atan2(a21, a22);
\r
15539 tm.rotation = angle * _RAD2DEG;
\r
15541 zFlip = (angle < -minPI || angle > minPI);
\r
15542 cos = Math.cos(-angle);
\r
15543 sin = Math.sin(-angle);
\r
15544 a11 = a11*cos+a12*sin;
\r
15545 t2 = a21*cos+a22*sin;
\r
15546 a22 = a21*-sin+a22*cos;
\r
15547 a32 = a31*-sin+a32*cos;
\r
15551 if (zFlip && xFlip) {
\r
15552 tm.rotation = tm.rotationX = 0;
\r
15553 } else if (zFlip && yFlip) {
\r
15554 tm.rotation = tm.rotationY = 0;
\r
15555 } else if (yFlip && xFlip) {
\r
15556 tm.rotationY = tm.rotationX = 0;
\r
15559 tm.scaleX = ((Math.sqrt(a11 * a11 + a21 * a21) * rnd + 0.5) | 0) / rnd;
\r
15560 tm.scaleY = ((Math.sqrt(a22 * a22 + a23 * a23) * rnd + 0.5) | 0) / rnd;
\r
15561 tm.scaleZ = ((Math.sqrt(a32 * a32 + a33 * a33) * rnd + 0.5) | 0) / rnd;
\r
15563 tm.perspective = a43 ? 1 / ((a43 < 0) ? -a43 : a43) : 0;
\r
15569 } 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
15570 var k = (m.length >= 6),
\r
15571 a = k ? m[0] : 1,
\r
15574 d = k ? m[3] : 1;
\r
15575 tm.x = m[4] || 0;
\r
15576 tm.y = m[5] || 0;
\r
15577 scaleX = Math.sqrt(a * a + b * b);
\r
15578 scaleY = Math.sqrt(d * d + c * c);
\r
15579 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
15580 skewX = (c || d) ? Math.atan2(c, d) * _RAD2DEG + rotation : tm.skewX || 0;
\r
15581 difX = scaleX - Math.abs(tm.scaleX || 0);
\r
15582 difY = scaleY - Math.abs(tm.scaleY || 0);
\r
15583 if (Math.abs(skewX) > 90 && Math.abs(skewX) < 270) {
\r
15586 skewX += (rotation <= 0) ? 180 : -180;
\r
15587 rotation += (rotation <= 0) ? 180 : -180;
\r
15590 skewX += (skewX <= 0) ? 180 : -180;
\r
15593 difR = (rotation - tm.rotation) % 180; //note: matching ranges would be very small (+/-0.0001) or very close to 180.
\r
15594 difS = (skewX - tm.skewX) % 180;
\r
15595 //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
15596 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
15597 tm.scaleX = scaleX;
\r
15598 tm.scaleY = scaleY;
\r
15599 tm.rotation = rotation;
\r
15600 tm.skewX = skewX;
\r
15602 if (_supports3D) {
\r
15603 tm.rotationX = tm.rotationY = tm.z = 0;
\r
15604 tm.perspective = parseFloat(CSSPlugin.defaultTransformPerspective) || 0;
\r
15608 tm.zOrigin = zOrigin;
\r
15610 //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
15612 if (tm[i] < min) if (tm[i] > -min) {
\r
15616 //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
15618 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
15623 //for setting 2D transforms in IE6, IE7, and IE8 (must use a "filter" to emulate the behavior of modern day browser transforms)
\r
15624 _setIETransformRatio = function(v) {
\r
15625 var t = this.data, //refers to the element's _gsTransform object
\r
15626 ang = -t.rotation * _DEG2RAD,
\r
15627 skew = ang + t.skewX * _DEG2RAD,
\r
15629 a = ((Math.cos(ang) * t.scaleX * rnd) | 0) / rnd,
\r
15630 b = ((Math.sin(ang) * t.scaleX * rnd) | 0) / rnd,
\r
15631 c = ((Math.sin(skew) * -t.scaleY * rnd) | 0) / rnd,
\r
15632 d = ((Math.cos(skew) * t.scaleY * rnd) | 0) / rnd,
\r
15633 style = this.t.style,
\r
15634 cs = this.t.currentStyle,
\r
15639 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
15642 filters = cs.filter;
\r
15643 style.filter = ""; //remove filters so that we can accurately measure offsetWidth/offsetHeight
\r
15644 var w = this.t.offsetWidth,
\r
15645 h = this.t.offsetHeight,
\r
15646 clip = (cs.position !== "absolute"),
\r
15647 m = "progid:DXImageTransform.Microsoft.Matrix(M11=" + a + ", M12=" + b + ", M21=" + c + ", M22=" + d,
\r
15652 //if transformOrigin is being used, adjust the offset x and y
\r
15653 if (t.ox != null) {
\r
15654 dx = ((t.oxp) ? w * t.ox * 0.01 : t.ox) - w / 2;
\r
15655 dy = ((t.oyp) ? h * t.oy * 0.01 : t.oy) - h / 2;
\r
15656 ox += dx - (dx * a + dy * b);
\r
15657 oy += dy - (dx * c + dy * d);
\r
15661 m += ", sizingMethod='auto expand')";
\r
15665 //translate to ensure that transformations occur around the correct origin (default is center).
\r
15666 m += ", Dx=" + (dx - (dx * a + dy * b) + ox) + ", Dy=" + (dy - (dx * c + dy * d) + oy) + ")";
\r
15668 if (filters.indexOf("DXImageTransform.Microsoft.Matrix(") !== -1) {
\r
15669 style.filter = filters.replace(_ieSetMatrixExp, m);
\r
15671 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
15674 //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
15675 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
15676 style.removeAttribute("filter");
\r
15679 //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
15681 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
15683 dx = t.ieOffsetX || 0;
\r
15684 dy = t.ieOffsetY || 0;
\r
15685 t.ieOffsetX = Math.round((w - ((a < 0 ? -a : a) * w + (b < 0 ? -b : b) * h)) / 2 + ox);
\r
15686 t.ieOffsetY = Math.round((h - ((d < 0 ? -d : d) * h + (c < 0 ? -c : c) * w)) / 2 + oy);
\r
15687 for (i = 0; i < 4; i++) {
\r
15688 prop = _margins[i];
\r
15690 //we need to get the current margin in case it is being tweened separately (we want to respect that tween's changes)
\r
15691 val = (marg.indexOf("px") !== -1) ? parseFloat(marg) : _convertToPixels(this.t, prop, parseFloat(marg), marg.replace(_suffixExp, "")) || 0;
\r
15692 if (val !== t[prop]) {
\r
15693 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
15695 dif = (i < 2) ? dx - t.ieOffsetX : dy - t.ieOffsetY;
\r
15697 style[prop] = (t[prop] = Math.round( val - dif * ((i === 0 || i === 2) ? 1 : mult) )) + "px";
\r
15702 _set3DTransformRatio = _internals.set3DTransformRatio = function(v) {
\r
15703 var t = this.data, //refers to the element's _gsTransform object
\r
15704 style = this.t.style,
\r
15705 angle = t.rotation * _DEG2RAD,
\r
15709 perspective = t.perspective,
\r
15710 a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, a42, a43,
\r
15711 zOrigin, rnd, cos, sin, t1, t2, t3, t4;
\r
15712 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
15713 _set2DTransformRatio.call(this, v);
\r
15716 if (_isFirefox) {
\r
15718 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
15719 sx = sz = 0.00002;
\r
15721 if (sy < n && sy > -n) {
\r
15722 sy = sz = 0.00002;
\r
15724 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
15728 if (angle || t.skewX) {
\r
15729 cos = Math.cos(angle);
\r
15730 sin = Math.sin(angle);
\r
15734 angle -= t.skewX * _DEG2RAD;
\r
15735 cos = Math.cos(angle);
\r
15736 sin = Math.sin(angle);
\r
15737 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
15738 t1 = Math.tan(t.skewX * _DEG2RAD);
\r
15739 t1 = Math.sqrt(1 + t1 * t1);
\r
15747 } else if (!t.rotationY && !t.rotationX && sz === 1 && !perspective) { //if we're only translating and/or 2D scaling, this is faster...
\r
15748 style[_transformProp] = "translate3d(" + t.x + "px," + t.y + "px," + t.z +"px)" + ((sx !== 1 || sy !== 1) ? " scale(" + sx + "," + sy + ")" : "");
\r
15755 a13 = a14 = a23 = a24 = a31 = a32 = a34 = a41 = a42 = 0;
\r
15756 a43 = (perspective) ? -1 / perspective : 0;
\r
15757 zOrigin = t.zOrigin;
\r
15759 angle = t.rotationY * _DEG2RAD;
\r
15761 cos = Math.cos(angle);
\r
15762 sin = Math.sin(angle);
\r
15772 angle = t.rotationX * _DEG2RAD;
\r
15774 cos = Math.cos(angle);
\r
15775 sin = Math.sin(angle);
\r
15776 t1 = a12*cos+a13*sin;
\r
15777 t2 = a22*cos+a23*sin;
\r
15778 t3 = a32*cos+a33*sin;
\r
15779 t4 = a42*cos+a43*sin;
\r
15780 a13 = a12*-sin+a13*cos;
\r
15781 a23 = a22*-sin+a23*cos;
\r
15782 a33 = a32*-sin+a33*cos;
\r
15783 a43 = a42*-sin+a43*cos;
\r
15811 a34 = a33*a34+zOrigin;
\r
15813 //we round the x, y, and z slightly differently to allow even larger values.
\r
15814 a14 = (t1 = (a14 += t.x) - (a14 |= 0)) ? ((t1 * rnd + (t1 < 0 ? -0.5 : 0.5)) | 0) / rnd + a14 : a14;
\r
15815 a24 = (t1 = (a24 += t.y) - (a24 |= 0)) ? ((t1 * rnd + (t1 < 0 ? -0.5 : 0.5)) | 0) / rnd + a24 : a24;
\r
15816 a34 = (t1 = (a34 += t.z) - (a34 |= 0)) ? ((t1 * rnd + (t1 < 0 ? -0.5 : 0.5)) | 0) / rnd + a34 : a34;
\r
15817 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
15820 _set2DTransformRatio = _internals.set2DTransformRatio = function(v) {
\r
15821 var t = this.data, //refers to the element's _gsTransform object
\r
15823 style = targ.style,
\r
15824 ang, skew, rnd, sx, sy;
\r
15825 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
15826 this.setRatio = _set3DTransformRatio;
\r
15827 _set3DTransformRatio.call(this, v);
\r
15830 if (!t.rotation && !t.skewX) {
\r
15831 style[_transformProp] = "matrix(" + t.scaleX + ",0,0," + t.scaleY + "," + t.x + "," + t.y + ")";
\r
15833 ang = t.rotation * _DEG2RAD;
\r
15834 skew = ang - t.skewX * _DEG2RAD;
\r
15836 sx = t.scaleX * rnd;
\r
15837 sy = t.scaleY * rnd;
\r
15838 //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
15839 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
15843 _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
15844 if (cssp._transform) { return pt; } //only need to parse the transform once, and only if the browser supports it.
\r
15845 var m1 = cssp._transform = _getTransform(t, _cs, true, vars.parseTransform),
\r
15848 i = _transformProps.length,
\r
15850 endRotations = {},
\r
15851 m2, skewY, copy, orig, has3D, hasChange, dr;
\r
15852 if (typeof(v.transform) === "string" && _transformProp) { //for values like transform:"rotate(60deg) scale(0.5, 0.8)"
\r
15853 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
15854 copy[_transformProp] = v.transform;
\r
15855 copy.display = "block"; //if display is "none", the browser often refuses to report the transform properties correctly.
\r
15856 copy.position = "absolute";
\r
15857 _doc.body.appendChild(_tempDiv);
\r
15858 m2 = _getTransform(_tempDiv, null, false);
\r
15859 _doc.body.removeChild(_tempDiv);
\r
15860 } else if (typeof(v) === "object") { //for values like scaleX, scaleY, rotation, x, y, skewX, and skewY or transform:{...} (object)
\r
15861 m2 = {scaleX:_parseVal((v.scaleX != null) ? v.scaleX : v.scale, m1.scaleX),
\r
15862 scaleY:_parseVal((v.scaleY != null) ? v.scaleY : v.scale, m1.scaleY),
\r
15863 scaleZ:_parseVal(v.scaleZ, m1.scaleZ),
\r
15864 x:_parseVal(v.x, m1.x),
\r
15865 y:_parseVal(v.y, m1.y),
\r
15866 z:_parseVal(v.z, m1.z),
\r
15867 perspective:_parseVal(v.transformPerspective, m1.perspective)};
\r
15868 dr = v.directionalRotation;
\r
15869 if (dr != null) {
\r
15870 if (typeof(dr) === "object") {
\r
15871 for (copy in dr) {
\r
15872 v[copy] = dr[copy];
\r
15878 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
15879 if (_supports3D) {
\r
15880 m2.rotationX = _parseAngle(("rotationX" in v) ? v.rotationX : ("shortRotationX" in v) ? v.shortRotationX + "_short" : m1.rotationX || 0, m1.rotationX, "rotationX", endRotations);
\r
15881 m2.rotationY = _parseAngle(("rotationY" in v) ? v.rotationY : ("shortRotationY" in v) ? v.shortRotationY + "_short" : m1.rotationY || 0, m1.rotationY, "rotationY", endRotations);
\r
15883 m2.skewX = (v.skewX == null) ? m1.skewX : _parseAngle(v.skewX, m1.skewX);
\r
15885 //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
15886 m2.skewY = (v.skewY == null) ? m1.skewY : _parseAngle(v.skewY, m1.skewY);
\r
15887 if ((skewY = m2.skewY - m1.skewY)) {
\r
15888 m2.skewX += skewY;
\r
15889 m2.rotation += skewY;
\r
15893 if (_supports3D && v.force3D != null) {
\r
15894 m1.force3D = v.force3D;
\r
15895 hasChange = true;
\r
15898 m1.skewType = v.skewType || m1.skewType || CSSPlugin.defaultSkewType;
\r
15900 has3D = (m1.force3D || m1.z || m1.rotationX || m1.rotationY || m2.z || m2.rotationX || m2.rotationY || m2.perspective);
\r
15901 if (!has3D && v.scale != null) {
\r
15902 m2.scaleZ = 1; //no need to tween scaleZ.
\r
15905 while (--i > -1) {
\r
15906 p = _transformProps[i];
\r
15907 orig = m2[p] - m1[p];
\r
15908 if (orig > min || orig < -min || _forcePT[p] != null) {
\r
15909 hasChange = true;
\r
15910 pt = new CSSPropTween(m1, p, m1[p], orig, pt);
\r
15911 if (p in endRotations) {
\r
15912 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
15914 pt.xs0 = 0; //ensures the value stays numeric in setRatio()
\r
15915 pt.plugin = plugin;
\r
15916 cssp._overwriteProps.push(pt.n);
\r
15920 orig = v.transformOrigin;
\r
15921 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
15922 if (_transformProp) {
\r
15923 hasChange = true;
\r
15924 p = _transformOriginProp;
\r
15925 orig = (orig || _getStyle(t, p, _cs, false, "50% 50%")) + ""; //cast as string to avoid errors
\r
15926 pt = new CSSPropTween(style, p, 0, 0, pt, -1, "transformOrigin");
\r
15928 pt.plugin = plugin;
\r
15929 if (_supports3D) {
\r
15930 copy = m1.zOrigin;
\r
15931 orig = orig.split(" ");
\r
15932 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
15933 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
15934 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
15936 pt.xs0 = pt.e = m1.zOrigin;
\r
15938 pt.xs0 = pt.e = orig;
\r
15941 //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
15943 _parsePosition(orig + "", m1);
\r
15948 cssp._transformType = (has3D || this._transformType === 3) ? 3 : 2; //quicker than calling cssp._enableTransforms();
\r
15951 }, prefix:true});
\r
15953 _registerComplexSpecialProp("boxShadow", {defaultValue:"0px 0px 0px 0px #999", prefix:true, color:true, multi:true, keyword:"inset"});
\r
15955 _registerComplexSpecialProp("borderRadius", {defaultValue:"0px", parser:function(t, e, p, cssp, pt, plugin) {
\r
15956 e = this.format(e);
\r
15957 var props = ["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],
\r
15959 ea1, i, es2, bs2, bs, es, bn, en, w, h, esfx, bsfx, rel, hn, vn, em;
\r
15960 w = parseFloat(t.offsetWidth);
\r
15961 h = parseFloat(t.offsetHeight);
\r
15962 ea1 = e.split(" ");
\r
15963 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
15964 if (this.p.indexOf("border")) { //older browsers used a prefix
\r
15965 props[i] = _checkPropPrefix(props[i]);
\r
15967 bs = bs2 = _getStyle(t, props[i], _cs, false, "0px");
\r
15968 if (bs.indexOf(" ") !== -1) {
\r
15969 bs2 = bs.split(" ");
\r
15973 es = es2 = ea1[i];
\r
15974 bn = parseFloat(bs);
\r
15975 bsfx = bs.substr((bn + "").length);
\r
15976 rel = (es.charAt(1) === "=");
\r
15978 en = parseInt(es.charAt(0)+"1", 10);
\r
15979 es = es.substr(2);
\r
15980 en *= parseFloat(es);
\r
15981 esfx = es.substr((en + "").length - (en < 0 ? 1 : 0)) || "";
\r
15983 en = parseFloat(es);
\r
15984 esfx = es.substr((en + "").length);
\r
15986 if (esfx === "") {
\r
15987 esfx = _suffixMap[p] || bsfx;
\r
15989 if (esfx !== bsfx) {
\r
15990 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
15991 vn = _convertToPixels(t, "borderTop", bn, bsfx); //vertical number
\r
15992 if (esfx === "%") {
\r
15993 bs = (hn / w * 100) + "%";
\r
15994 bs2 = (vn / h * 100) + "%";
\r
15995 } else if (esfx === "em") {
\r
15996 em = _convertToPixels(t, "borderLeft", 1, "em");
\r
15997 bs = (hn / em) + "em";
\r
15998 bs2 = (vn / em) + "em";
\r
16004 es = (parseFloat(bs) + en) + esfx;
\r
16005 es2 = (parseFloat(bs2) + en) + esfx;
\r
16008 pt = _parseComplex(style, props[i], bs + " " + bs2, es + " " + es2, false, "0px", pt);
\r
16011 }, prefix:true, formatter:_getFormatter("0px 0px 0px 0px", false, true)});
\r
16012 _registerComplexSpecialProp("backgroundPosition", {defaultValue:"0 0", parser:function(t, e, p, cssp, pt, plugin) {
\r
16013 var bp = "background-position",
\r
16014 cs = (_cs || _getComputedStyle(t, null)),
\r
16015 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
16016 es = this.format(e),
\r
16017 ba, ea, i, pct, overlap, src;
\r
16018 if ((bs.indexOf("%") !== -1) !== (es.indexOf("%") !== -1)) {
\r
16019 src = _getStyle(t, "backgroundImage").replace(_urlExp, "");
\r
16020 if (src && src !== "none") {
\r
16021 ba = bs.split(" ");
\r
16022 ea = es.split(" ");
\r
16023 _tempImg.setAttribute("src", src); //set the temp <img>'s src to the background-image so that we can measure its width/height
\r
16025 while (--i > -1) {
\r
16027 pct = (bs.indexOf("%") !== -1);
\r
16028 if (pct !== (ea[i].indexOf("%") !== -1)) {
\r
16029 overlap = (i === 0) ? t.offsetWidth - _tempImg.width : t.offsetHeight - _tempImg.height;
\r
16030 ba[i] = pct ? (parseFloat(bs) / 100 * overlap) + "px" : (parseFloat(bs) / overlap * 100) + "%";
\r
16033 bs = ba.join(" ");
\r
16036 return this.parseComplex(t.style, bs, es, pt, plugin);
\r
16037 }, formatter:_parsePosition});
\r
16038 _registerComplexSpecialProp("backgroundSize", {defaultValue:"0 0", formatter:_parsePosition});
\r
16039 _registerComplexSpecialProp("perspective", {defaultValue:"0px", prefix:true});
\r
16040 _registerComplexSpecialProp("perspectiveOrigin", {defaultValue:"50% 50%", prefix:true});
\r
16041 _registerComplexSpecialProp("transformStyle", {prefix:true});
\r
16042 _registerComplexSpecialProp("backfaceVisibility", {prefix:true});
\r
16043 _registerComplexSpecialProp("userSelect", {prefix:true});
\r
16044 _registerComplexSpecialProp("margin", {parser:_getEdgeParser("marginTop,marginRight,marginBottom,marginLeft")});
\r
16045 _registerComplexSpecialProp("padding", {parser:_getEdgeParser("paddingTop,paddingRight,paddingBottom,paddingLeft")});
\r
16046 _registerComplexSpecialProp("clip", {defaultValue:"rect(0px,0px,0px,0px)", parser:function(t, e, p, cssp, pt, plugin){
\r
16047 var b, cs, delim;
\r
16048 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
16049 cs = t.currentStyle;
\r
16050 delim = _ieVers < 8 ? " " : ",";
\r
16051 b = "rect(" + cs.clipTop + delim + cs.clipRight + delim + cs.clipBottom + delim + cs.clipLeft + ")";
\r
16052 e = this.format(e).split(",").join(delim);
\r
16054 b = this.format(_getStyle(t, this.p, _cs, false, this.dflt));
\r
16055 e = this.format(e);
\r
16057 return this.parseComplex(t.style, b, e, pt, plugin);
\r
16059 _registerComplexSpecialProp("textShadow", {defaultValue:"0px 0px 0px #999", color:true, multi:true});
\r
16060 _registerComplexSpecialProp("autoRound,strictUnits", {parser:function(t, e, p, cssp, pt) {return pt;}}); //just so that we can ignore these properties (not tween them)
\r
16061 _registerComplexSpecialProp("border", {defaultValue:"0px solid #000", parser:function(t, e, p, cssp, pt, plugin) {
\r
16062 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
16063 }, color:true, formatter:function(v) {
\r
16064 var a = v.split(" ");
\r
16065 return a[0] + " " + (a[1] || "solid") + " " + (v.match(_colorExp) || ["#000"])[0];
\r
16067 _registerComplexSpecialProp("borderWidth", {parser:_getEdgeParser("borderTopWidth,borderRightWidth,borderBottomWidth,borderLeftWidth")}); //Firefox doesn't pick up on borderWidth set in style sheets (only inline).
\r
16068 _registerComplexSpecialProp("float,cssFloat,styleFloat", {parser:function(t, e, p, cssp, pt, plugin) {
\r
16070 prop = ("cssFloat" in s) ? "cssFloat" : "styleFloat";
\r
16071 return new CSSPropTween(s, prop, 0, 0, pt, -1, p, false, 0, s[prop], e);
\r
16074 //opacity-related
\r
16075 var _setIEOpacityRatio = function(v) {
\r
16076 var t = this.t, //refers to the element's style property
\r
16077 filters = t.filter || _getStyle(this.data, "filter"),
\r
16078 val = (this.s + this.c * v) | 0,
\r
16080 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
16081 if (filters.indexOf("atrix(") === -1 && filters.indexOf("radient(") === -1 && filters.indexOf("oader(") === -1) {
\r
16082 t.removeAttribute("filter");
\r
16083 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
16085 t.filter = filters.replace(_alphaFilterExp, "");
\r
16091 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
16093 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
16094 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
16095 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
16098 t.filter = filters.replace(_opacityExp, "opacity=" + val);
\r
16102 _registerComplexSpecialProp("opacity,alpha,autoAlpha", {defaultValue:"1", parser:function(t, e, p, cssp, pt, plugin) {
\r
16103 var b = parseFloat(_getStyle(t, "opacity", _cs, false, "1")),
\r
16105 isAutoAlpha = (p === "autoAlpha");
\r
16106 if (typeof(e) === "string" && e.charAt(1) === "=") {
\r
16107 e = ((e.charAt(0) === "-") ? -1 : 1) * parseFloat(e.substr(2)) + b;
\r
16109 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
16112 if (_supportsOpacity) {
\r
16113 pt = new CSSPropTween(style, "opacity", b, e - b, pt);
\r
16115 pt = new CSSPropTween(style, "opacity", b * 100, (e - b) * 100, pt);
\r
16116 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
16117 style.zoom = 1; //helps correct an IE issue.
\r
16119 pt.b = "alpha(opacity=" + pt.s + ")";
\r
16120 pt.e = "alpha(opacity=" + (pt.s + pt.c) + ")";
\r
16122 pt.plugin = plugin;
\r
16123 pt.setRatio = _setIEOpacityRatio;
\r
16125 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
16126 pt = new CSSPropTween(style, "visibility", 0, 0, pt, -1, null, false, 0, ((b !== 0) ? "inherit" : "hidden"), ((e === 0) ? "hidden" : "inherit"));
\r
16127 pt.xs0 = "inherit";
\r
16128 cssp._overwriteProps.push(pt.n);
\r
16129 cssp._overwriteProps.push(p);
\r
16135 var _removeProp = function(s, p) {
\r
16137 if (s.removeProperty) {
\r
16138 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
16139 p = "M" + p.substr(1);
\r
16141 s.removeProperty(p.replace(_capsExp, "-$1").toLowerCase());
\r
16142 } else { //note: old versions of IE use "removeAttribute()" instead of "removeProperty()"
\r
16143 s.removeAttribute(p);
\r
16147 _setClassNameRatio = function(v) {
\r
16148 this.t._gsClassPT = this;
\r
16149 if (v === 1 || v === 0) {
\r
16150 this.t.setAttribute("class", (v === 0) ? this.b : this.e);
\r
16151 var mpt = this.data, //first MiniPropTween
\r
16152 s = this.t.style;
\r
16155 _removeProp(s, mpt.p);
\r
16157 s[mpt.p] = mpt.v;
\r
16161 if (v === 1 && this.t._gsClassPT === this) {
\r
16162 this.t._gsClassPT = null;
\r
16164 } else if (this.t.getAttribute("class") !== this.e) {
\r
16165 this.t.setAttribute("class", this.e);
\r
16168 _registerComplexSpecialProp("className", {parser:function(t, e, p, cssp, pt, plugin, vars) {
\r
16169 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
16170 cssText = t.style.cssText,
\r
16171 difData, bs, cnpt, cnptLookup, mpt;
\r
16172 pt = cssp._classNamePT = new CSSPropTween(t, p, 0, 0, pt, 2);
\r
16173 pt.setRatio = _setClassNameRatio;
\r
16175 _hasPriority = true;
\r
16177 bs = _getAllStyles(t, _cs);
\r
16178 //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
16179 cnpt = t._gsClassPT;
\r
16182 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
16184 cnptLookup[mpt.p] = 1;
\r
16187 cnpt.setRatio(1);
\r
16189 t._gsClassPT = pt;
\r
16190 pt.e = (e.charAt(1) !== "=") ? e : b.replace(new RegExp("\\s*\\b" + e.substr(2) + "\\b"), "") + ((e.charAt(0) === "+") ? " " + e.substr(2) : "");
\r
16191 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
16192 t.setAttribute("class", pt.e);
\r
16193 difData = _cssDif(t, bs, _getAllStyles(t), vars, cnptLookup);
\r
16194 t.setAttribute("class", b);
\r
16195 pt.data = difData.firstMPT;
\r
16196 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
16197 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
16203 var _setClearPropsRatio = function(v) {
\r
16204 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
16205 var s = this.t.style,
\r
16206 transformParse = _specialProps.transform.parse,
\r
16207 a, p, i, clearTransform;
\r
16208 if (this.e === "all") {
\r
16210 clearTransform = true;
\r
16212 a = this.e.split(",");
\r
16214 while (--i > -1) {
\r
16216 if (_specialProps[p]) {
\r
16217 if (_specialProps[p].parse === transformParse) {
\r
16218 clearTransform = true;
\r
16220 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
16223 _removeProp(s, p);
\r
16226 if (clearTransform) {
\r
16227 _removeProp(s, _transformProp);
\r
16228 if (this.t._gsTransform) {
\r
16229 delete this.t._gsTransform;
\r
16235 _registerComplexSpecialProp("clearProps", {parser:function(t, e, p, cssp, pt) {
\r
16236 pt = new CSSPropTween(t, p, 0, 0, pt, 2);
\r
16237 pt.setRatio = _setClearPropsRatio;
\r
16240 pt.data = cssp._tween;
\r
16241 _hasPriority = true;
\r
16245 p = "bezier,throwProps,physicsProps,physics2D".split(",");
\r
16248 _registerPluginProp(p[i]);
\r
16258 p = CSSPlugin.prototype;
\r
16259 p._firstPT = null;
\r
16261 //gets called when the tween renders for the first time. This kicks everything off, recording start/end values, etc.
\r
16262 p._onInitTween = function(target, vars, tween) {
\r
16263 if (!target.nodeType) { //css is only for dom elements
\r
16266 this._target = target;
\r
16267 this._tween = tween;
\r
16268 this._vars = vars;
\r
16269 _autoRound = vars.autoRound;
\r
16270 _hasPriority = false;
\r
16271 _suffixMap = vars.suffixMap || CSSPlugin.suffixMap;
\r
16272 _cs = _getComputedStyle(target, "");
\r
16273 _overwriteProps = this._overwriteProps;
\r
16274 var style = target.style,
\r
16275 v, pt, pt2, first, last, next, zIndex, tpt, threeD;
\r
16276 if (_reqSafariFix) if (style.zIndex === "") {
\r
16277 v = _getStyle(target, "zIndex", _cs);
\r
16278 if (v === "auto" || v === "") {
\r
16279 //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
16280 this._addLazySet(style, "zIndex", 0);
\r
16284 if (typeof(vars) === "string") {
\r
16285 first = style.cssText;
\r
16286 v = _getAllStyles(target, _cs);
\r
16287 style.cssText = first + ";" + vars;
\r
16288 v = _cssDif(target, v, _getAllStyles(target)).difs;
\r
16289 if (!_supportsOpacity && _opacityValExp.test(vars)) {
\r
16290 v.opacity = parseFloat( RegExp.$1 );
\r
16293 style.cssText = first;
\r
16295 this._firstPT = pt = this.parse(target, vars, null);
\r
16297 if (this._transformType) {
\r
16298 threeD = (this._transformType === 3);
\r
16299 if (!_transformProp) {
\r
16300 style.zoom = 1; //helps correct an IE issue.
\r
16301 } else if (_isSafari) {
\r
16302 _reqSafariFix = true;
\r
16303 //if zIndex isn't set, iOS Safari doesn't repaint things correctly sometimes (seemingly at random).
\r
16304 if (style.zIndex === "") {
\r
16305 zIndex = _getStyle(target, "zIndex", _cs);
\r
16306 if (zIndex === "auto" || zIndex === "") {
\r
16307 this._addLazySet(style, "zIndex", 0);
\r
16310 //Setting WebkitBackfaceVisibility corrects 3 bugs:
\r
16311 // 1) [non-Android] Safari skips rendering changes to "top" and "left" that are made on the same frame/render as a transform update.
\r
16312 // 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
16313 // 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
16314 //Note: we allow the user to override the auto-setting by defining WebkitBackfaceVisibility in the vars of the tween.
\r
16315 if (_isSafariLT6) {
\r
16316 this._addLazySet(style, "WebkitBackfaceVisibility", this._vars.WebkitBackfaceVisibility || (threeD ? "visible" : "hidden"));
\r
16320 while (pt2 && pt2._next) {
\r
16323 tpt = new CSSPropTween(target, "transform", 0, 0, null, 2);
\r
16324 this._linkCSSP(tpt, null, pt2);
\r
16325 tpt.setRatio = (threeD && _supports3D) ? _set3DTransformRatio : _transformProp ? _set2DTransformRatio : _setIETransformRatio;
\r
16326 tpt.data = this._transform || _getTransform(target, _cs, true);
\r
16327 _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
16330 if (_hasPriority) {
\r
16331 //reorders the linked list in order of pr (priority)
\r
16335 while (pt2 && pt2.pr > pt.pr) {
\r
16338 if ((pt._prev = pt2 ? pt2._prev : last)) {
\r
16339 pt._prev._next = pt;
\r
16343 if ((pt._next = pt2)) {
\r
16350 this._firstPT = first;
\r
16356 p.parse = function(target, vars, pt, plugin) {
\r
16357 var style = target.style,
\r
16358 p, sp, bn, en, bs, es, bsfx, esfx, isStr, rel;
\r
16359 for (p in vars) {
\r
16360 es = vars[p]; //ending value string
\r
16361 sp = _specialProps[p]; //SpecialProp lookup.
\r
16363 pt = sp.parse(target, es, p, this, pt, plugin, vars);
\r
16366 bs = _getStyle(target, p, _cs) + "";
\r
16367 isStr = (typeof(es) === "string");
\r
16368 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
16370 es = _parseColor(es);
\r
16371 es = ((es.length > 3) ? "rgba(" : "rgb(") + es.join(",") + ")";
\r
16373 pt = _parseComplex(style, p, bs, es, true, "transparent", pt, 0, plugin);
\r
16375 } else if (isStr && (es.indexOf(" ") !== -1 || es.indexOf(",") !== -1)) {
\r
16376 pt = _parseComplex(style, p, bs, es, true, null, pt, 0, plugin);
\r
16379 bn = parseFloat(bs);
\r
16380 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
16382 if (bs === "" || bs === "auto") {
\r
16383 if (p === "width" || p === "height") {
\r
16384 bn = _getDimension(target, p, _cs);
\r
16386 } else if (p === "left" || p === "top") {
\r
16387 bn = _calculateOffset(target, p, _cs);
\r
16390 bn = (p !== "opacity") ? 0 : 1;
\r
16395 rel = (isStr && es.charAt(1) === "=");
\r
16397 en = parseInt(es.charAt(0) + "1", 10);
\r
16398 es = es.substr(2);
\r
16399 en *= parseFloat(es);
\r
16400 esfx = es.replace(_suffixExp, "");
\r
16402 en = parseFloat(es);
\r
16403 esfx = isStr ? es.substr((en + "").length) || "" : "";
\r
16406 if (esfx === "") {
\r
16407 esfx = (p in _suffixMap) ? _suffixMap[p] : bsfx; //populate the end suffix, prioritizing the map, then if none is found, use the beginning suffix.
\r
16410 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
16412 //if the beginning/ending suffixes don't match, normalize them...
\r
16413 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
16414 bn = _convertToPixels(target, p, bn, bsfx);
\r
16415 if (esfx === "%") {
\r
16416 bn /= _convertToPixels(target, p, 100, "%") / 100;
\r
16417 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
16421 } else if (esfx === "em") {
\r
16422 bn /= _convertToPixels(target, p, 1, "em");
\r
16424 //otherwise convert to pixels.
\r
16425 } else if (esfx !== "px") {
\r
16426 en = _convertToPixels(target, p, en, esfx);
\r
16427 esfx = "px"; //we don't use bsfx after this, so we don't need to set it to px too.
\r
16429 if (rel) if (en || en === 0) {
\r
16430 es = (en + bn) + esfx; //the changes we made affect relative calculations, so adjust the end value here.
\r
16438 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
16439 pt = new CSSPropTween(style, p, bn, en - bn, pt, 0, p, (_autoRound !== false && (esfx === "px" || p === "zIndex")), 0, bs, es);
\r
16441 //DEBUG: _log("tween "+p+" from "+pt.b+" ("+bn+esfx+") to "+pt.e+" with suffix: "+pt.xs0);
\r
16442 } else if (style[p] === undefined || !es && (es + "" === "NaN" || es == null)) {
\r
16443 _log("invalid " + p + " tween value: " + vars[p]);
\r
16445 pt = new CSSPropTween(style, p, en || bn || 0, 0, pt, -1, p, false, 0, bs, es);
\r
16446 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
16447 //DEBUG: _log("non-tweening value "+p+": "+pt.xs0);
\r
16451 if (plugin) if (pt && !pt.plugin) {
\r
16452 pt.plugin = plugin;
\r
16459 //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
16460 p.setRatio = function(v) {
\r
16461 var pt = this._firstPT,
\r
16465 //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
16466 if (v === 1 && (this._tween._time === this._tween._duration || this._tween._time === 0)) {
\r
16468 if (pt.type !== 2) {
\r
16469 pt.t[pt.p] = pt.e;
\r
16476 } else if (v || !(this._tween._time === this._tween._duration || this._tween._time === 0) || this._tween._rawPrevTime === -0.000001) {
\r
16478 val = pt.c * v + pt.s;
\r
16480 val = Math.round(val);
\r
16481 } else if (val < min) if (val > -min) {
\r
16485 pt.t[pt.p] = val + pt.xs0;
\r
16486 } else if (pt.type === 1) { //complex value (one that typically has multiple numbers inside a string, like "rect(5px,10px,20px,25px)"
\r
16489 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2;
\r
16490 } else if (i === 3) {
\r
16491 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3;
\r
16492 } else if (i === 4) {
\r
16493 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3 + pt.xn3 + pt.xs4;
\r
16494 } else if (i === 5) {
\r
16495 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
16497 str = pt.xs0 + val + pt.xs1;
\r
16498 for (i = 1; i < pt.l; i++) {
\r
16499 str += pt["xn"+i] + pt["xs"+(i+1)];
\r
16501 pt.t[pt.p] = str;
\r
16504 } else if (pt.type === -1) { //non-tweening value
\r
16505 pt.t[pt.p] = pt.xs0;
\r
16507 } else if (pt.setRatio) { //custom setRatio() for things like SpecialProps, external plugins, etc.
\r
16513 //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
16516 if (pt.type !== 2) {
\r
16517 pt.t[pt.p] = pt.b;
\r
16528 * Forces rendering of the target's transforms (rotation, scale, etc.) whenever the CSSPlugin's setRatio() is called.
\r
16529 * Basically, this tells the CSSPlugin to create a CSSPropTween (type 2) after instantiation that runs last in the linked
\r
16530 * list and calls the appropriate (3D or 2D) rendering function. We separate this into its own method so that we can call
\r
16531 * it from other plugins like BezierPlugin if, for example, it needs to apply an autoRotation and this CSSPlugin
\r
16532 * doesn't have any transform-related properties of its own. You can call this method as many times as you
\r
16533 * want and it won't create duplicate CSSPropTweens.
\r
16535 * @param {boolean} threeD if true, it should apply 3D tweens (otherwise, just 2D ones are fine and typically faster)
\r
16537 p._enableTransforms = function(threeD) {
\r
16538 this._transformType = (threeD || this._transformType === 3) ? 3 : 2;
\r
16539 this._transform = this._transform || _getTransform(this._target, _cs, true); //ensures that the element has a _gsTransform property with the appropriate values.
\r
16542 var lazySet = function(v) {
\r
16543 this.t[this.p] = this.e;
\r
16544 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
16546 /** @private Gives us a way to set a value on the first render (and only the first render). **/
\r
16547 p._addLazySet = function(t, p, v) {
\r
16548 var pt = this._firstPT = new CSSPropTween(t, p, 0, 0, this._firstPT, 2);
\r
16550 pt.setRatio = lazySet;
\r
16555 p._linkCSSP = function(pt, next, prev, remove) {
\r
16561 pt._next._prev = pt._prev;
\r
16564 pt._prev._next = pt._next;
\r
16565 } else if (this._firstPT === pt) {
\r
16566 this._firstPT = pt._next;
\r
16567 remove = true; //just to prevent resetting this._firstPT 5 lines down in case pt._next is null. (optimized for speed)
\r
16571 } else if (!remove && this._firstPT === null) {
\r
16572 this._firstPT = pt;
\r
16580 //we need to make sure that if alpha or autoAlpha is killed, opacity is too. And autoAlpha affects the "visibility" property.
\r
16581 p._kill = function(lookup) {
\r
16582 var copy = lookup,
\r
16584 if (lookup.autoAlpha || lookup.alpha) {
\r
16586 for (p in lookup) { //copy the lookup so that we're not changing the original which may be passed elsewhere.
\r
16587 copy[p] = lookup[p];
\r
16589 copy.opacity = 1;
\r
16590 if (copy.autoAlpha) {
\r
16591 copy.visibility = 1;
\r
16594 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
16595 xfirst = pt.xfirst;
\r
16596 if (xfirst && xfirst._prev) {
\r
16597 this._linkCSSP(xfirst._prev, pt._next, xfirst._prev._prev); //break off the prev
\r
16598 } else if (xfirst === this._firstPT) {
\r
16599 this._firstPT = pt._next;
\r
16602 this._linkCSSP(pt._next, pt._next._next, xfirst._prev);
\r
16604 this._classNamePT = null;
\r
16606 return TweenPlugin.prototype._kill.call(this, copy);
\r
16611 //used by cascadeTo() for gathering all the style properties of each child element into an array for comparison.
\r
16612 var _getChildStyles = function(e, props, targets) {
\r
16613 var children, i, child, type;
\r
16616 while (--i > -1) {
\r
16617 _getChildStyles(e[i], props, targets);
\r
16621 children = e.childNodes;
\r
16622 i = children.length;
\r
16623 while (--i > -1) {
\r
16624 child = children[i];
\r
16625 type = child.type;
\r
16626 if (child.style) {
\r
16627 props.push(_getAllStyles(child));
\r
16629 targets.push(child);
\r
16632 if ((type === 1 || type === 9 || type === 11) && child.childNodes.length) {
\r
16633 _getChildStyles(child, props, targets);
\r
16639 * Typically only useful for className tweens that may affect child elements, this method creates a TweenLite
\r
16640 * and then compares the style properties of all the target's child elements at the tween's start and end, and
\r
16641 * if any are different, it also creates tweens for those and returns an array containing ALL of the resulting
\r
16642 * tweens (so that you can easily add() them to a TimelineLite, for example). The reason this functionality is
\r
16643 * wrapped into a separate static method of CSSPlugin instead of being integrated into all regular className tweens
\r
16644 * is because it creates entirely new tweens that may have completely different targets than the original tween,
\r
16645 * so if they were all lumped into the original tween instance, it would be inconsistent with the rest of the API
\r
16646 * and it would create other problems. For example:
\r
16647 * - 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
16648 * - 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
16649 * - 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
16651 * @param {Object} target object to be tweened
\r
16652 * @param {number} Duration in seconds (or frames for frames-based tweens)
\r
16653 * @param {Object} Object containing the end values, like {className:"newClass", ease:Linear.easeNone}
\r
16654 * @return {Array} An array of TweenLite instances
\r
16656 CSSPlugin.cascadeTo = function(target, duration, vars) {
\r
16657 var tween = TweenLite.to(target, duration, vars),
\r
16658 results = [tween],
\r
16662 _reservedProps = TweenLite._internals.reservedProps,
\r
16664 target = tween._targets || tween.target;
\r
16665 _getChildStyles(target, b, targets);
\r
16666 tween.render(duration, true);
\r
16667 _getChildStyles(target, e);
\r
16668 tween.render(0, true);
\r
16669 tween._enabled(true);
\r
16670 i = targets.length;
\r
16671 while (--i > -1) {
\r
16672 difs = _cssDif(targets[i], b[i], e[i]);
\r
16673 if (difs.firstMPT) {
\r
16674 difs = difs.difs;
\r
16675 for (p in vars) {
\r
16676 if (_reservedProps[p]) {
\r
16677 difs[p] = vars[p];
\r
16680 results.push( TweenLite.to(targets[i], duration, difs) );
\r
16686 TweenPlugin.activate([CSSPlugin]);
\r
16687 return CSSPlugin;
\r
16702 * ----------------------------------------------------------------
\r
16703 * RoundPropsPlugin
\r
16704 * ----------------------------------------------------------------
\r
16708 var RoundPropsPlugin = window._gsDefine.plugin({
\r
16709 propName: "roundProps",
\r
16713 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
\r
16714 init: function(target, value, tween) {
\r
16715 this._tween = tween;
\r
16720 p = RoundPropsPlugin.prototype;
\r
16722 p._onInitAllProps = function() {
\r
16723 var tween = this._tween,
\r
16724 rp = (tween.vars.roundProps instanceof Array) ? tween.vars.roundProps : tween.vars.roundProps.split(","),
\r
16727 rpt = tween._propLookup.roundProps,
\r
16729 while (--i > -1) {
\r
16730 lookup[rp[i]] = 1;
\r
16733 while (--i > -1) {
\r
16735 pt = tween._firstPT;
\r
16737 next = pt._next; //record here, because it may get removed
\r
16739 pt.t._roundProps(lookup, true);
\r
16740 } else if (pt.n === prop) {
\r
16741 this._add(pt.t, prop, pt.s, pt.c);
\r
16742 //remove from linked list
\r
16744 next._prev = pt._prev;
\r
16747 pt._prev._next = next;
\r
16748 } else if (tween._firstPT === pt) {
\r
16749 tween._firstPT = next;
\r
16751 pt._next = pt._prev = null;
\r
16752 tween._propLookup[prop] = rpt;
\r
16760 p._add = function(target, p, s, c) {
\r
16761 this._addTween(target, p, s, s + c, p, true);
\r
16762 this._overwriteProps.push(p);
\r
16777 * ----------------------------------------------------------------
\r
16779 * ----------------------------------------------------------------
\r
16781 window._gsDefine.plugin({
\r
16782 propName: "attr",
\r
16784 version: "0.3.2",
\r
16786 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
\r
16787 init: function(target, value, tween) {
\r
16788 var p, start, end;
\r
16789 if (typeof(target.setAttribute) !== "function") {
\r
16792 this._target = target;
\r
16793 this._proxy = {};
\r
16794 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
16796 for (p in value) {
\r
16797 this._start[p] = this._proxy[p] = start = target.getAttribute(p);
\r
16798 end = this._addTween(this._proxy, p, parseFloat(start), value[p], p);
\r
16799 this._end[p] = end ? end.s + end.c : value[p];
\r
16800 this._overwriteProps.push(p);
\r
16805 //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
16806 set: function(ratio) {
\r
16807 this._super.setRatio.call(this, ratio);
\r
16808 var props = this._overwriteProps,
\r
16809 i = props.length,
\r
16810 lookup = (ratio === 1) ? this._end : ratio ? this._proxy : this._start,
\r
16812 while (--i > -1) {
\r
16814 this._target.setAttribute(p, lookup[p] + "");
\r
16830 * ----------------------------------------------------------------
\r
16831 * DirectionalRotationPlugin
\r
16832 * ----------------------------------------------------------------
\r
16834 window._gsDefine.plugin({
\r
16835 propName: "directionalRotation",
\r
16837 version: "0.2.0",
\r
16839 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
\r
16840 init: function(target, value, tween) {
\r
16841 if (typeof(value) !== "object") {
\r
16842 value = {rotation:value};
\r
16844 this.finals = {};
\r
16845 var cap = (value.useRadians === true) ? Math.PI * 2 : 360,
\r
16847 p, v, start, end, dif, split;
\r
16848 for (p in value) {
\r
16849 if (p !== "useRadians") {
\r
16850 split = (value[p] + "").split("_");
\r
16852 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
16853 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
16854 dif = end - start;
\r
16855 if (split.length) {
\r
16856 v = split.join("_");
\r
16857 if (v.indexOf("short") !== -1) {
\r
16859 if (dif !== dif % (cap / 2)) {
\r
16860 dif = (dif < 0) ? dif + cap : dif - cap;
\r
16863 if (v.indexOf("_cw") !== -1 && dif < 0) {
\r
16864 dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
\r
16865 } else if (v.indexOf("ccw") !== -1 && dif > 0) {
\r
16866 dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
\r
16869 if (dif > min || dif < -min) {
\r
16870 this._addTween(target, p, start, start + dif, p);
\r
16871 this._overwriteProps.push(p);
\r
16878 //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
16879 set: function(ratio) {
\r
16881 if (ratio !== 1) {
\r
16882 this._super.setRatio.call(this, ratio);
\r
16884 pt = this._firstPT;
\r
16887 pt.t[pt.p](this.finals[pt.p]);
\r
16889 pt.t[pt.p] = this.finals[pt.p];
\r
16896 })._autoCSS = true;
\r
16909 * ----------------------------------------------------------------
\r
16911 * ----------------------------------------------------------------
\r
16913 window._gsDefine("easing.Back", ["easing.Ease"], function(Ease) {
\r
16915 var w = (window.GreenSockGlobals || window),
\r
16916 gs = w.com.greensock,
\r
16917 _2PI = Math.PI * 2,
\r
16918 _HALF_PI = Math.PI / 2,
\r
16919 _class = gs._class,
\r
16920 _create = function(n, f) {
\r
16921 var C = _class("easing." + n, function(){}, true),
\r
16922 p = C.prototype = new Ease();
\r
16923 p.constructor = C;
\r
16927 _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
16928 _wrap = function(name, EaseOut, EaseIn, EaseInOut, aliases) {
\r
16929 var C = _class("easing."+name, {
\r
16930 easeOut:new EaseOut(),
\r
16931 easeIn:new EaseIn(),
\r
16932 easeInOut:new EaseInOut()
\r
16934 _easeReg(C, name);
\r
16937 EasePoint = function(time, value, next) {
\r
16941 this.next = next;
\r
16942 next.prev = this;
\r
16943 this.c = next.v - value;
\r
16944 this.gap = next.t - time;
\r
16949 _createBack = function(n, f) {
\r
16950 var C = _class("easing." + n, function(overshoot) {
\r
16951 this._p1 = (overshoot || overshoot === 0) ? overshoot : 1.70158;
\r
16952 this._p2 = this._p1 * 1.525;
\r
16954 p = C.prototype = new Ease();
\r
16955 p.constructor = C;
\r
16957 p.config = function(overshoot) {
\r
16958 return new C(overshoot);
\r
16963 Back = _wrap("Back",
\r
16964 _createBack("BackOut", function(p) {
\r
16965 return ((p = p - 1) * p * ((this._p1 + 1) * p + this._p1) + 1);
\r
16967 _createBack("BackIn", function(p) {
\r
16968 return p * p * ((this._p1 + 1) * p - this._p1);
\r
16970 _createBack("BackInOut", function(p) {
\r
16971 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
16977 SlowMo = _class("easing.SlowMo", function(linearRatio, power, yoyoMode) {
\r
16978 power = (power || power === 0) ? power : 0.7;
\r
16979 if (linearRatio == null) {
\r
16980 linearRatio = 0.7;
\r
16981 } else if (linearRatio > 1) {
\r
16984 this._p = (linearRatio !== 1) ? power : 0;
\r
16985 this._p1 = (1 - linearRatio) / 2;
\r
16986 this._p2 = linearRatio;
\r
16987 this._p3 = this._p1 + this._p2;
\r
16988 this._calcEnd = (yoyoMode === true);
\r
16990 p = SlowMo.prototype = new Ease(),
\r
16991 SteppedEase, RoughEase, _createElastic;
\r
16993 p.constructor = SlowMo;
\r
16994 p.getRatio = function(p) {
\r
16995 var r = p + (0.5 - p) * this._p;
\r
16996 if (p < this._p1) {
\r
16997 return this._calcEnd ? 1 - ((p = 1 - (p / this._p1)) * p) : r - ((p = 1 - (p / this._p1)) * p * p * p * r);
\r
16998 } else if (p > this._p3) {
\r
16999 return this._calcEnd ? 1 - (p = (p - this._p3) / this._p1) * p : r + ((p - r) * (p = (p - this._p3) / this._p1) * p * p * p);
\r
17001 return this._calcEnd ? 1 : r;
\r
17003 SlowMo.ease = new SlowMo(0.7, 0.7);
\r
17005 p.config = SlowMo.config = function(linearRatio, power, yoyoMode) {
\r
17006 return new SlowMo(linearRatio, power, yoyoMode);
\r
17011 SteppedEase = _class("easing.SteppedEase", function(steps) {
\r
17012 steps = steps || 1;
\r
17013 this._p1 = 1 / steps;
\r
17014 this._p2 = steps + 1;
\r
17016 p = SteppedEase.prototype = new Ease();
\r
17017 p.constructor = SteppedEase;
\r
17018 p.getRatio = function(p) {
\r
17021 } else if (p >= 1) {
\r
17024 return ((this._p2 * p) >> 0) * this._p1;
\r
17026 p.config = SteppedEase.config = function(steps) {
\r
17027 return new SteppedEase(steps);
\r
17032 RoughEase = _class("easing.RoughEase", function(vars) {
\r
17033 vars = vars || {};
\r
17034 var taper = vars.taper || "none",
\r
17037 points = (vars.points || 20) | 0,
\r
17039 randomize = (vars.randomize !== false),
\r
17040 clamp = (vars.clamp === true),
\r
17041 template = (vars.template instanceof Ease) ? vars.template : null,
\r
17042 strength = (typeof(vars.strength) === "number") ? vars.strength * 0.4 : 0.4,
\r
17043 x, y, bump, invX, obj, pnt;
\r
17044 while (--i > -1) {
\r
17045 x = randomize ? Math.random() : (1 / points) * i;
\r
17046 y = template ? template.getRatio(x) : x;
\r
17047 if (taper === "none") {
\r
17049 } else if (taper === "out") {
\r
17051 bump = invX * invX * strength;
\r
17052 } else if (taper === "in") {
\r
17053 bump = x * x * strength;
\r
17054 } else if (x < 0.5) { //"both" (start)
\r
17056 bump = invX * invX * 0.5 * strength;
\r
17057 } else { //"both" (end)
\r
17058 invX = (1 - x) * 2;
\r
17059 bump = invX * invX * 0.5 * strength;
\r
17062 y += (Math.random() * bump) - (bump * 0.5);
\r
17063 } else if (i % 2) {
\r
17071 } else if (y < 0) {
\r
17075 a[cnt++] = {x:x, y:y};
\r
17077 a.sort(function(a, b) {
\r
17078 return a.x - b.x;
\r
17081 pnt = new EasePoint(1, 1, null);
\r
17083 while (--i > -1) {
\r
17085 pnt = new EasePoint(obj.x, obj.y, pnt);
\r
17088 this._prev = new EasePoint(0, 0, (pnt.t !== 0) ? pnt : pnt.next);
\r
17090 p = RoughEase.prototype = new Ease();
\r
17091 p.constructor = RoughEase;
\r
17092 p.getRatio = function(p) {
\r
17093 var pnt = this._prev;
\r
17095 while (pnt.next && p >= pnt.t) {
\r
17100 while (pnt.prev && p <= pnt.t) {
\r
17104 this._prev = pnt;
\r
17105 return (pnt.v + ((p - pnt.t) / pnt.gap) * pnt.c);
\r
17107 p.config = function(vars) {
\r
17108 return new RoughEase(vars);
\r
17110 RoughEase.ease = new RoughEase();
\r
17115 _create("BounceOut", function(p) {
\r
17116 if (p < 1 / 2.75) {
\r
17117 return 7.5625 * p * p;
\r
17118 } else if (p < 2 / 2.75) {
\r
17119 return 7.5625 * (p -= 1.5 / 2.75) * p + 0.75;
\r
17120 } else if (p < 2.5 / 2.75) {
\r
17121 return 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375;
\r
17123 return 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375;
\r
17125 _create("BounceIn", function(p) {
\r
17126 if ((p = 1 - p) < 1 / 2.75) {
\r
17127 return 1 - (7.5625 * p * p);
\r
17128 } else if (p < 2 / 2.75) {
\r
17129 return 1 - (7.5625 * (p -= 1.5 / 2.75) * p + 0.75);
\r
17130 } else if (p < 2.5 / 2.75) {
\r
17131 return 1 - (7.5625 * (p -= 2.25 / 2.75) * p + 0.9375);
\r
17133 return 1 - (7.5625 * (p -= 2.625 / 2.75) * p + 0.984375);
\r
17135 _create("BounceInOut", function(p) {
\r
17136 var invert = (p < 0.5);
\r
17142 if (p < 1 / 2.75) {
\r
17143 p = 7.5625 * p * p;
\r
17144 } else if (p < 2 / 2.75) {
\r
17145 p = 7.5625 * (p -= 1.5 / 2.75) * p + 0.75;
\r
17146 } else if (p < 2.5 / 2.75) {
\r
17147 p = 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375;
\r
17149 p = 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375;
\r
17151 return invert ? (1 - p) * 0.5 : p * 0.5 + 0.5;
\r
17158 _create("CircOut", function(p) {
\r
17159 return Math.sqrt(1 - (p = p - 1) * p);
\r
17161 _create("CircIn", function(p) {
\r
17162 return -(Math.sqrt(1 - (p * p)) - 1);
\r
17164 _create("CircInOut", function(p) {
\r
17165 return ((p*=2) < 1) ? -0.5 * (Math.sqrt(1 - p * p) - 1) : 0.5 * (Math.sqrt(1 - (p -= 2) * p) + 1);
\r
17171 _createElastic = function(n, f, def) {
\r
17172 var C = _class("easing." + n, function(amplitude, period) {
\r
17173 this._p1 = amplitude || 1;
\r
17174 this._p2 = period || def;
\r
17175 this._p3 = this._p2 / _2PI * (Math.asin(1 / this._p1) || 0);
\r
17177 p = C.prototype = new Ease();
\r
17178 p.constructor = C;
\r
17180 p.config = function(amplitude, period) {
\r
17181 return new C(amplitude, period);
\r
17186 _createElastic("ElasticOut", function(p) {
\r
17187 return this._p1 * Math.pow(2, -10 * p) * Math.sin( (p - this._p3) * _2PI / this._p2 ) + 1;
\r
17189 _createElastic("ElasticIn", function(p) {
\r
17190 return -(this._p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - this._p3) * _2PI / this._p2 ));
\r
17192 _createElastic("ElasticInOut", function(p) {
\r
17193 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
17200 _create("ExpoOut", function(p) {
\r
17201 return 1 - Math.pow(2, -10 * p);
\r
17203 _create("ExpoIn", function(p) {
\r
17204 return Math.pow(2, 10 * (p - 1)) - 0.001;
\r
17206 _create("ExpoInOut", function(p) {
\r
17207 return ((p *= 2) < 1) ? 0.5 * Math.pow(2, 10 * (p - 1)) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
\r
17214 _create("SineOut", function(p) {
\r
17215 return Math.sin(p * _HALF_PI);
\r
17217 _create("SineIn", function(p) {
\r
17218 return -Math.cos(p * _HALF_PI) + 1;
\r
17220 _create("SineInOut", function(p) {
\r
17221 return -0.5 * (Math.cos(Math.PI * p) - 1);
\r
17225 _class("easing.EaseLookup", {
\r
17226 find:function(s) {
\r
17227 return Ease.map[s];
\r
17231 //register the non-standard eases
\r
17232 _easeReg(w.SlowMo, "SlowMo", "ease,");
\r
17233 _easeReg(RoughEase, "RoughEase", "ease,");
\r
17234 _easeReg(SteppedEase, "SteppedEase", "ease,");
\r
17254 * ----------------------------------------------------------------
\r
17255 * Base classes like TweenLite, SimpleTimeline, Ease, Ticker, etc.
\r
17256 * ----------------------------------------------------------------
\r
17258 (function(window) {
\r
17261 var _globals = window.GreenSockGlobals || window;
\r
17262 if (_globals.TweenLite) {
\r
17263 return; //in case the core set of classes is already loaded, don't instantiate twice.
\r
17265 var _namespace = function(ns) {
\r
17266 var a = ns.split("."),
\r
17268 for (i = 0; i < a.length; i++) {
\r
17269 p[a[i]] = p = p[a[i]] || {};
\r
17273 gs = _namespace("com.greensock"),
\r
17274 _tinyNum = 0.0000000001,
\r
17275 _slice = [].slice,
\r
17276 _emptyFunc = function() {},
\r
17277 _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
17278 var toString = Object.prototype.toString,
\r
17279 array = toString.call([]);
\r
17280 return function(obj) {
\r
17281 return obj != null && (obj instanceof Array || (typeof(obj) === "object" && !!obj.push && toString.call(obj) === array));
\r
17284 a, i, p, _ticker, _tickerActive,
\r
17289 * Defines a GreenSock class, optionally with an array of dependencies that must be instantiated first and passed into the definition.
\r
17290 * This allows users to load GreenSock JS files in any order even if they have interdependencies (like CSSPlugin extends TweenPlugin which is
\r
17291 * inside TweenLite.js, but if CSSPlugin is loaded first, it should wait to run its code until TweenLite.js loads and instantiates TweenPlugin
\r
17292 * and then pass TweenPlugin to CSSPlugin's definition). This is all done automatically and internally.
\r
17294 * Every definition will be added to a "com.greensock" global object (typically window, but if a window.GreenSockGlobals object is found,
\r
17295 * 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
17296 * it is ALSO referenced at window.TweenLite. However some classes aren't considered global, like the base com.greensock.core.Animation class, so
\r
17297 * 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
17298 * 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
17299 * 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
17300 * sandbox the banner one like:
\r
17303 * 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
17305 * <script src="js/greensock/v1.7/TweenMax.js"></script>
\r
17307 * 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
17309 * <script src="js/greensock/v1.6/TweenMax.js"></script>
\r
17311 * gs.TweenLite.to(...); //would use v1.7
\r
17312 * TweenLite.to(...); //would use v1.6
\r
17315 * @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
17316 * @param {!Array.<string>} dependencies An array of dependencies (described as their namespaces minus "com.greensock." prefix). For example ["TweenLite","plugins.TweenPlugin","core.Animation"]
\r
17317 * @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
17318 * @param {boolean=} global If true, the class will be added to the global scope (typically window unless you define a window.GreenSockGlobals object)
\r
17320 Definition = function(ns, dependencies, func, global) {
\r
17321 this.sc = (_defLookup[ns]) ? _defLookup[ns].sc : []; //subclasses
\r
17322 _defLookup[ns] = this;
\r
17323 this.gsClass = null;
\r
17324 this.func = func;
\r
17325 var _classes = [];
\r
17326 this.check = function(init) {
\r
17327 var i = dependencies.length,
\r
17330 while (--i > -1) {
\r
17331 if ((cur = _defLookup[dependencies[i]] || new Definition(dependencies[i], [])).gsClass) {
\r
17332 _classes[i] = cur.gsClass;
\r
17334 } else if (init) {
\r
17335 cur.sc.push(this);
\r
17338 if (missing === 0 && func) {
\r
17339 a = ("com.greensock." + ns).split(".");
\r
17341 cl = _namespace(a.join("."))[n] = this.gsClass = func.apply(func, _classes);
\r
17343 //exports to multiple environments
\r
17345 _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
17346 if (typeof(define) === "function" && define.amd){ //AMD
\r
17347 define((window.GreenSockAMDPath ? window.GreenSockAMDPath + "/" : "") + ns.split(".").join("/"), [], function() { return cl; });
\r
17348 } else if (typeof(module) !== "undefined" && module.exports){ //node
\r
17349 module.exports = cl;
\r
17352 for (i = 0; i < this.sc.length; i++) {
\r
17353 this.sc[i].check();
\r
17357 this.check(true);
\r
17360 //used to create Definition instances (which basically registers a class that has dependencies).
\r
17361 _gsDefine = window._gsDefine = function(ns, dependencies, func, global) {
\r
17362 return new Definition(ns, dependencies, func, global);
\r
17365 //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
17366 _class = gs._class = function(ns, func, global) {
\r
17367 func = func || function() {};
\r
17368 _gsDefine(ns, [], function(){ return func; }, global);
\r
17372 _gsDefine.globals = _globals;
\r
17377 * ----------------------------------------------------------------
\r
17379 * ----------------------------------------------------------------
\r
17381 var _baseParams = [0, 0, 1, 1],
\r
17382 _blankArray = [],
\r
17383 Ease = _class("easing.Ease", function(func, extraParams, type, power) {
\r
17384 this._func = func;
\r
17385 this._type = type || 0;
\r
17386 this._power = power || 0;
\r
17387 this._params = extraParams ? _baseParams.concat(extraParams) : _baseParams;
\r
17389 _easeMap = Ease.map = {},
\r
17390 _easeReg = Ease.register = function(ease, names, types, create) {
\r
17391 var na = names.split(","),
\r
17393 ta = (types || "easeIn,easeOut,easeInOut").split(","),
\r
17394 e, name, j, type;
\r
17395 while (--i > -1) {
\r
17397 e = create ? _class("easing."+name, null, true) : gs.easing[name] || {};
\r
17399 while (--j > -1) {
\r
17401 _easeMap[name + "." + type] = _easeMap[type + name] = e[type] = ease.getRatio ? ease : ease[type] || new ease();
\r
17406 p = Ease.prototype;
\r
17407 p._calcEnd = false;
\r
17408 p.getRatio = function(p) {
\r
17409 if (this._func) {
\r
17410 this._params[0] = p;
\r
17411 return this._func.apply(null, this._params);
\r
17413 var t = this._type,
\r
17414 pw = this._power,
\r
17415 r = (t === 1) ? 1 - p : (t === 2) ? p : (p < 0.5) ? p * 2 : (1 - p) * 2;
\r
17418 } else if (pw === 2) {
\r
17420 } else if (pw === 3) {
\r
17422 } else if (pw === 4) {
\r
17423 r *= r * r * r * r;
\r
17425 return (t === 1) ? 1 - r : (t === 2) ? r : (p < 0.5) ? r / 2 : 1 - (r / 2);
\r
17428 //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
17429 a = ["Linear","Quad","Cubic","Quart","Quint,Strong"];
\r
17431 while (--i > -1) {
\r
17432 p = a[i]+",Power"+i;
\r
17433 _easeReg(new Ease(null,null,1,i), p, "easeOut", true);
\r
17434 _easeReg(new Ease(null,null,2,i), p, "easeIn" + ((i === 0) ? ",easeNone" : ""));
\r
17435 _easeReg(new Ease(null,null,3,i), p, "easeInOut");
\r
17437 _easeMap.linear = gs.easing.Linear.easeIn;
\r
17438 _easeMap.swing = gs.easing.Quad.easeInOut; //for jQuery folks
\r
17442 * ----------------------------------------------------------------
\r
17443 * EventDispatcher
\r
17444 * ----------------------------------------------------------------
\r
17446 var EventDispatcher = _class("events.EventDispatcher", function(target) {
\r
17447 this._listeners = {};
\r
17448 this._eventTarget = target || this;
\r
17450 p = EventDispatcher.prototype;
\r
17452 p.addEventListener = function(type, callback, scope, useParam, priority) {
\r
17453 priority = priority || 0;
\r
17454 var list = this._listeners[type],
\r
17457 if (list == null) {
\r
17458 this._listeners[type] = list = [];
\r
17461 while (--i > -1) {
\r
17462 listener = list[i];
\r
17463 if (listener.c === callback && listener.s === scope) {
\r
17464 list.splice(i, 1);
\r
17465 } else if (index === 0 && listener.pr < priority) {
\r
17469 list.splice(index, 0, {c:callback, s:scope, up:useParam, pr:priority});
\r
17470 if (this === _ticker && !_tickerActive) {
\r
17475 p.removeEventListener = function(type, callback) {
\r
17476 var list = this._listeners[type], i;
\r
17479 while (--i > -1) {
\r
17480 if (list[i].c === callback) {
\r
17481 list.splice(i, 1);
\r
17488 p.dispatchEvent = function(type) {
\r
17489 var list = this._listeners[type],
\r
17493 t = this._eventTarget;
\r
17494 while (--i > -1) {
\r
17495 listener = list[i];
\r
17496 if (listener.up) {
\r
17497 listener.c.call(listener.s || t, {type:type, target:t});
\r
17499 listener.c.call(listener.s || t);
\r
17507 * ----------------------------------------------------------------
\r
17509 * ----------------------------------------------------------------
\r
17511 var _reqAnimFrame = window.requestAnimationFrame,
\r
17512 _cancelAnimFrame = window.cancelAnimationFrame,
\r
17513 _getTime = Date.now || function() {return new Date().getTime();},
\r
17514 _lastUpdate = _getTime();
\r
17516 //now try to determine the requestAnimationFrame and cancelAnimationFrame functions and if none are found, we'll use a setTimeout()/clearTimeout() polyfill.
\r
17517 a = ["ms","moz","webkit","o"];
\r
17519 while (--i > -1 && !_reqAnimFrame) {
\r
17520 _reqAnimFrame = window[a[i] + "RequestAnimationFrame"];
\r
17521 _cancelAnimFrame = window[a[i] + "CancelAnimationFrame"] || window[a[i] + "CancelRequestAnimationFrame"];
\r
17524 _class("Ticker", function(fps, useRAF) {
\r
17525 var _self = this,
\r
17526 _startTime = _getTime(),
\r
17527 _useRAF = (useRAF !== false && _reqAnimFrame),
\r
17528 _lagThreshold = 500,
\r
17529 _adjustedLag = 33,
\r
17530 _fps, _req, _id, _gap, _nextTime,
\r
17531 _tick = function(manual) {
\r
17532 var elapsed = _getTime() - _lastUpdate,
\r
17533 overlap, dispatch;
\r
17534 if (elapsed > _lagThreshold) {
\r
17535 _startTime += elapsed - _adjustedLag;
\r
17537 _lastUpdate += elapsed;
\r
17538 _self.time = (_lastUpdate - _startTime) / 1000;
\r
17539 overlap = _self.time - _nextTime;
\r
17540 if (!_fps || overlap > 0 || manual === true) {
\r
17542 _nextTime += overlap + (overlap >= _gap ? 0.004 : _gap - overlap);
\r
17545 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
17546 _id = _req(_tick);
\r
17549 _self.dispatchEvent("tick");
\r
17553 EventDispatcher.call(_self);
\r
17554 _self.time = _self.frame = 0;
\r
17555 _self.tick = function() {
\r
17559 _self.lagSmoothing = function(threshold, adjustedLag) {
\r
17560 _lagThreshold = threshold || (1 / _tinyNum); //zero should be interpreted as basically unlimited
\r
17561 _adjustedLag = Math.min(adjustedLag, _lagThreshold, 0);
\r
17564 _self.sleep = function() {
\r
17565 if (_id == null) {
\r
17568 if (!_useRAF || !_cancelAnimFrame) {
\r
17569 clearTimeout(_id);
\r
17571 _cancelAnimFrame(_id);
\r
17573 _req = _emptyFunc;
\r
17575 if (_self === _ticker) {
\r
17576 _tickerActive = false;
\r
17580 _self.wake = function() {
\r
17581 if (_id !== null) {
\r
17583 } 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
17584 _lastUpdate = _getTime() - _lagThreshold + 5;
\r
17586 _req = (_fps === 0) ? _emptyFunc : (!_useRAF || !_reqAnimFrame) ? function(f) { return setTimeout(f, ((_nextTime - _self.time) * 1000 + 1) | 0); } : _reqAnimFrame;
\r
17587 if (_self === _ticker) {
\r
17588 _tickerActive = true;
\r
17593 _self.fps = function(value) {
\r
17594 if (!arguments.length) {
\r
17598 _gap = 1 / (_fps || 60);
\r
17599 _nextTime = this.time + _gap;
\r
17603 _self.useRAF = function(value) {
\r
17604 if (!arguments.length) {
\r
17613 //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
17614 setTimeout(function() {
\r
17615 if (_useRAF && (!_id || _self.frame < 5)) {
\r
17616 _self.useRAF(false);
\r
17621 p = gs.Ticker.prototype = new gs.events.EventDispatcher();
\r
17622 p.constructor = gs.Ticker;
\r
17626 * ----------------------------------------------------------------
\r
17628 * ----------------------------------------------------------------
\r
17630 var Animation = _class("core.Animation", function(duration, vars) {
\r
17631 this.vars = vars = vars || {};
\r
17632 this._duration = this._totalDuration = duration || 0;
\r
17633 this._delay = Number(vars.delay) || 0;
\r
17634 this._timeScale = 1;
\r
17635 this._active = (vars.immediateRender === true);
\r
17636 this.data = vars.data;
\r
17637 this._reversed = (vars.reversed === true);
\r
17639 if (!_rootTimeline) {
\r
17642 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
17646 var tl = this.vars.useFrames ? _rootFramesTimeline : _rootTimeline;
\r
17647 tl.add(this, tl._time);
\r
17649 if (this.vars.paused) {
\r
17650 this.paused(true);
\r
17654 _ticker = Animation.ticker = new gs.Ticker();
\r
17655 p = Animation.prototype;
\r
17656 p._dirty = p._gc = p._initted = p._paused = false;
\r
17657 p._totalTime = p._time = 0;
\r
17658 p._rawPrevTime = -1;
\r
17659 p._next = p._last = p._onUpdate = p._timeline = p.timeline = null;
\r
17660 p._paused = false;
\r
17663 //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
17664 var _checkTimeout = function() {
\r
17665 if (_tickerActive && _getTime() - _lastUpdate > 2000) {
\r
17668 setTimeout(_checkTimeout, 2000);
\r
17673 p.play = function(from, suppressEvents) {
\r
17674 if (from != null) {
\r
17675 this.seek(from, suppressEvents);
\r
17677 return this.reversed(false).paused(false);
\r
17680 p.pause = function(atTime, suppressEvents) {
\r
17681 if (atTime != null) {
\r
17682 this.seek(atTime, suppressEvents);
\r
17684 return this.paused(true);
\r
17687 p.resume = function(from, suppressEvents) {
\r
17688 if (from != null) {
\r
17689 this.seek(from, suppressEvents);
\r
17691 return this.paused(false);
\r
17694 p.seek = function(time, suppressEvents) {
\r
17695 return this.totalTime(Number(time), suppressEvents !== false);
\r
17698 p.restart = function(includeDelay, suppressEvents) {
\r
17699 return this.reversed(false).paused(false).totalTime(includeDelay ? -this._delay : 0, (suppressEvents !== false), true);
\r
17702 p.reverse = function(from, suppressEvents) {
\r
17703 if (from != null) {
\r
17704 this.seek((from || this.totalDuration()), suppressEvents);
\r
17706 return this.reversed(true).paused(false);
\r
17709 p.render = function(time, suppressEvents, force) {
\r
17710 //stub - we override this method in subclasses.
\r
17713 p.invalidate = function() {
\r
17717 p.isActive = function() {
\r
17718 var tl = this._timeline, //the 2 root timelines won't have a _timeline; they're always active.
\r
17719 startTime = this._startTime,
\r
17721 return (!tl || (!this._gc && !this._paused && tl.isActive() && (rawTime = tl.rawTime()) >= startTime && rawTime < startTime + this.totalDuration() / this._timeScale));
\r
17724 p._enabled = function (enabled, ignoreTimeline) {
\r
17725 if (!_tickerActive) {
\r
17728 this._gc = !enabled;
\r
17729 this._active = this.isActive();
\r
17730 if (ignoreTimeline !== true) {
\r
17731 if (enabled && !this.timeline) {
\r
17732 this._timeline.add(this, this._startTime - this._delay);
\r
17733 } else if (!enabled && this.timeline) {
\r
17734 this._timeline._remove(this, true);
\r
17741 p._kill = function(vars, target) {
\r
17742 return this._enabled(false, false);
\r
17745 p.kill = function(vars, target) {
\r
17746 this._kill(vars, target);
\r
17750 p._uncache = function(includeSelf) {
\r
17751 var tween = includeSelf ? this : this.timeline;
\r
17753 tween._dirty = true;
\r
17754 tween = tween.timeline;
\r
17759 p._swapSelfInParams = function(params) {
\r
17760 var i = params.length,
\r
17761 copy = params.concat();
\r
17762 while (--i > -1) {
\r
17763 if (params[i] === "{self}") {
\r
17770 //----Animation getters/setters --------------------------------------------------------
\r
17772 p.eventCallback = function(type, callback, params, scope) {
\r
17773 if ((type || "").substr(0,2) === "on") {
\r
17774 var v = this.vars;
\r
17775 if (arguments.length === 1) {
\r
17778 if (callback == null) {
\r
17781 v[type] = callback;
\r
17782 v[type + "Params"] = (_isArray(params) && params.join("").indexOf("{self}") !== -1) ? this._swapSelfInParams(params) : params;
\r
17783 v[type + "Scope"] = scope;
\r
17785 if (type === "onUpdate") {
\r
17786 this._onUpdate = callback;
\r
17792 p.delay = function(value) {
\r
17793 if (!arguments.length) {
\r
17794 return this._delay;
\r
17796 if (this._timeline.smoothChildTiming) {
\r
17797 this.startTime( this._startTime + value - this._delay );
\r
17799 this._delay = value;
\r
17803 p.duration = function(value) {
\r
17804 if (!arguments.length) {
\r
17805 this._dirty = false;
\r
17806 return this._duration;
\r
17808 this._duration = this._totalDuration = value;
\r
17809 this._uncache(true); //true in case it's a TweenMax or TimelineMax that has a repeat - we'll need to refresh the totalDuration.
\r
17810 if (this._timeline.smoothChildTiming) if (this._time > 0) if (this._time < this._duration) if (value !== 0) {
\r
17811 this.totalTime(this._totalTime * (value / this._duration), true);
\r
17816 p.totalDuration = function(value) {
\r
17817 this._dirty = false;
\r
17818 return (!arguments.length) ? this._totalDuration : this.duration(value);
\r
17821 p.time = function(value, suppressEvents) {
\r
17822 if (!arguments.length) {
\r
17823 return this._time;
\r
17825 if (this._dirty) {
\r
17826 this.totalDuration();
\r
17828 return this.totalTime((value > this._duration) ? this._duration : value, suppressEvents);
\r
17831 p.totalTime = function(time, suppressEvents, uncapped) {
\r
17832 if (!_tickerActive) {
\r
17835 if (!arguments.length) {
\r
17836 return this._totalTime;
\r
17838 if (this._timeline) {
\r
17839 if (time < 0 && !uncapped) {
\r
17840 time += this.totalDuration();
\r
17842 if (this._timeline.smoothChildTiming) {
\r
17843 if (this._dirty) {
\r
17844 this.totalDuration();
\r
17846 var totalDuration = this._totalDuration,
\r
17847 tl = this._timeline;
\r
17848 if (time > totalDuration && !uncapped) {
\r
17849 time = totalDuration;
\r
17851 this._startTime = (this._paused ? this._pauseTime : tl._time) - ((!this._reversed ? time : totalDuration - time) / this._timeScale);
\r
17852 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
17853 this._uncache(false);
\r
17855 //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
17856 if (tl._timeline) {
\r
17857 while (tl._timeline) {
\r
17858 if (tl._timeline._time !== (tl._startTime + tl._totalTime) / tl._timeScale) {
\r
17859 tl.totalTime(tl._totalTime, true);
\r
17861 tl = tl._timeline;
\r
17866 this._enabled(true, false);
\r
17868 if (this._totalTime !== time || this._duration === 0) {
\r
17869 this.render(time, suppressEvents, false);
\r
17870 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
17878 p.progress = p.totalProgress = function(value, suppressEvents) {
\r
17879 return (!arguments.length) ? this._time / this.duration() : this.totalTime(this.duration() * value, suppressEvents);
\r
17882 p.startTime = function(value) {
\r
17883 if (!arguments.length) {
\r
17884 return this._startTime;
\r
17886 if (value !== this._startTime) {
\r
17887 this._startTime = value;
\r
17888 if (this.timeline) if (this.timeline._sortChildren) {
\r
17889 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
17895 p.timeScale = function(value) {
\r
17896 if (!arguments.length) {
\r
17897 return this._timeScale;
\r
17899 value = value || _tinyNum; //can't allow zero because it'll throw the math off
\r
17900 if (this._timeline && this._timeline.smoothChildTiming) {
\r
17901 var pauseTime = this._pauseTime,
\r
17902 t = (pauseTime || pauseTime === 0) ? pauseTime : this._timeline.totalTime();
\r
17903 this._startTime = t - ((t - this._startTime) * this._timeScale / value);
\r
17905 this._timeScale = value;
\r
17906 return this._uncache(false);
\r
17909 p.reversed = function(value) {
\r
17910 if (!arguments.length) {
\r
17911 return this._reversed;
\r
17913 if (value != this._reversed) {
\r
17914 this._reversed = value;
\r
17915 this.totalTime(((this._timeline && !this._timeline.smoothChildTiming) ? this.totalDuration() - this._totalTime : this._totalTime), true);
\r
17920 p.paused = function(value) {
\r
17921 if (!arguments.length) {
\r
17922 return this._paused;
\r
17924 if (value != this._paused) if (this._timeline) {
\r
17925 if (!_tickerActive && !value) {
\r
17928 var tl = this._timeline,
\r
17929 raw = tl.rawTime(),
\r
17930 elapsed = raw - this._pauseTime;
\r
17931 if (!value && tl.smoothChildTiming) {
\r
17932 this._startTime += elapsed;
\r
17933 this._uncache(false);
\r
17935 this._pauseTime = value ? raw : null;
\r
17936 this._paused = value;
\r
17937 this._active = this.isActive();
\r
17938 if (!value && elapsed !== 0 && this._initted && this.duration()) {
\r
17939 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
17942 if (this._gc && !value) {
\r
17943 this._enabled(true, false);
\r
17950 * ----------------------------------------------------------------
\r
17952 * ----------------------------------------------------------------
\r
17954 var SimpleTimeline = _class("core.SimpleTimeline", function(vars) {
\r
17955 Animation.call(this, 0, vars);
\r
17956 this.autoRemoveChildren = this.smoothChildTiming = true;
\r
17959 p = SimpleTimeline.prototype = new Animation();
\r
17960 p.constructor = SimpleTimeline;
\r
17961 p.kill()._gc = false;
\r
17962 p._first = p._last = null;
\r
17963 p._sortChildren = false;
\r
17965 p.add = p.insert = function(child, position, align, stagger) {
\r
17966 var prevTween, st;
\r
17967 child._startTime = Number(position || 0) + child._delay;
\r
17968 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
17969 child._pauseTime = child._startTime + ((this.rawTime() - child._startTime) / child._timeScale);
\r
17971 if (child.timeline) {
\r
17972 child.timeline._remove(child, true); //removes from existing timeline so that it can be properly added to this one.
\r
17974 child.timeline = child._timeline = this;
\r
17976 child._enabled(true, true);
\r
17978 prevTween = this._last;
\r
17979 if (this._sortChildren) {
\r
17980 st = child._startTime;
\r
17981 while (prevTween && prevTween._startTime > st) {
\r
17982 prevTween = prevTween._prev;
\r
17986 child._next = prevTween._next;
\r
17987 prevTween._next = child;
\r
17989 child._next = this._first;
\r
17990 this._first = child;
\r
17992 if (child._next) {
\r
17993 child._next._prev = child;
\r
17995 this._last = child;
\r
17997 child._prev = prevTween;
\r
17998 if (this._timeline) {
\r
17999 this._uncache(true);
\r
18004 p._remove = function(tween, skipDisable) {
\r
18005 if (tween.timeline === this) {
\r
18006 if (!skipDisable) {
\r
18007 tween._enabled(false, true);
\r
18009 tween.timeline = null;
\r
18011 if (tween._prev) {
\r
18012 tween._prev._next = tween._next;
\r
18013 } else if (this._first === tween) {
\r
18014 this._first = tween._next;
\r
18016 if (tween._next) {
\r
18017 tween._next._prev = tween._prev;
\r
18018 } else if (this._last === tween) {
\r
18019 this._last = tween._prev;
\r
18022 if (this._timeline) {
\r
18023 this._uncache(true);
\r
18029 p.render = function(time, suppressEvents, force) {
\r
18030 var tween = this._first,
\r
18032 this._totalTime = this._time = this._rawPrevTime = time;
\r
18034 next = tween._next; //record it here because the value could change after rendering...
\r
18035 if (tween._active || (time >= tween._startTime && !tween._paused)) {
\r
18036 if (!tween._reversed) {
\r
18037 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
\r
18039 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
\r
18046 p.rawTime = function() {
\r
18047 if (!_tickerActive) {
\r
18050 return this._totalTime;
\r
18054 * ----------------------------------------------------------------
\r
18056 * ----------------------------------------------------------------
\r
18058 var TweenLite = _class("TweenLite", function(target, duration, vars) {
\r
18059 Animation.call(this, duration, vars);
\r
18060 this.render = TweenLite.prototype.render; //speed optimization (avoid prototype lookup on this "hot" method)
\r
18062 if (target == null) {
\r
18063 throw "Cannot tween a null target.";
\r
18066 this.target = target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target;
\r
18068 var isSelector = (target.jquery || (target.length && target !== window && target[0] && (target[0] === window || (target[0].nodeType && target[0].style && !target.nodeType)))),
\r
18069 overwrite = this.vars.overwrite,
\r
18070 i, targ, targets;
\r
18072 this._overwrite = overwrite = (overwrite == null) ? _overwriteLookup[TweenLite.defaultOverwrite] : (typeof(overwrite) === "number") ? overwrite >> 0 : _overwriteLookup[overwrite];
\r
18074 if ((isSelector || target instanceof Array || (target.push && _isArray(target))) && typeof(target[0]) !== "number") {
\r
18075 this._targets = targets = _slice.call(target, 0);
\r
18076 this._propLookup = [];
\r
18077 this._siblings = [];
\r
18078 for (i = 0; i < targets.length; i++) {
\r
18079 targ = targets[i];
\r
18081 targets.splice(i--, 1);
\r
18083 } else if (typeof(targ) === "string") {
\r
18084 targ = targets[i--] = TweenLite.selector(targ); //in case it's an array of strings
\r
18085 if (typeof(targ) === "string") {
\r
18086 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
18089 } 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
18090 targets.splice(i--, 1);
\r
18091 this._targets = targets = targets.concat(_slice.call(targ, 0));
\r
18094 this._siblings[i] = _register(targ, this, false);
\r
18095 if (overwrite === 1) if (this._siblings[i].length > 1) {
\r
18096 _applyOverwrite(targ, this, null, 1, this._siblings[i]);
\r
18101 this._propLookup = {};
\r
18102 this._siblings = _register(target, this, false);
\r
18103 if (overwrite === 1) if (this._siblings.length > 1) {
\r
18104 _applyOverwrite(target, this, null, 1, this._siblings);
\r
18107 if (this.vars.immediateRender || (duration === 0 && this._delay === 0 && this.vars.immediateRender !== false)) {
\r
18108 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
18109 this.render(-this._delay);
\r
18112 _isSelector = function(v) {
\r
18113 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
18115 _autoCSS = function(vars, target) {
\r
18118 for (p in vars) {
\r
18119 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
18120 css[p] = vars[p];
\r
18127 p = TweenLite.prototype = new Animation();
\r
18128 p.constructor = TweenLite;
\r
18129 p.kill()._gc = false;
\r
18131 //----TweenLite defaults, overwrite management, and root updates ----------------------------------------------------
\r
18134 p._firstPT = p._targets = p._overwrittenProps = p._startAt = null;
\r
18135 p._notifyPluginsOfEnabled = p._lazy = false;
\r
18137 TweenLite.version = "1.12.1";
\r
18138 TweenLite.defaultEase = p._ease = new Ease(null, null, 1, 1);
\r
18139 TweenLite.defaultOverwrite = "auto";
\r
18140 TweenLite.ticker = _ticker;
\r
18141 TweenLite.autoSleep = true;
\r
18142 TweenLite.lagSmoothing = function(threshold, adjustedLag) {
\r
18143 _ticker.lagSmoothing(threshold, adjustedLag);
\r
18145 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
18147 var _lazyTweens = [],
\r
18148 _lazyLookup = {},
\r
18149 _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
18150 _plugins = TweenLite._plugins = {},
\r
18151 _tweenLookup = _internals.tweenLookup = {},
\r
18152 _tweenLookupNum = 0,
\r
18153 _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
18154 _overwriteLookup = {none:0, all:1, auto:2, concurrent:3, allOnStart:4, preexisting:5, "true":1, "false":0},
\r
18155 _rootFramesTimeline = Animation._rootFramesTimeline = new SimpleTimeline(),
\r
18156 _rootTimeline = Animation._rootTimeline = new SimpleTimeline(),
\r
18157 _lazyRender = function() {
\r
18158 var i = _lazyTweens.length;
\r
18159 _lazyLookup = {};
\r
18160 while (--i > -1) {
\r
18161 a = _lazyTweens[i];
\r
18162 if (a && a._lazy !== false) {
\r
18163 a.render(a._lazy, false, true);
\r
18167 _lazyTweens.length = 0;
\r
18170 _rootTimeline._startTime = _ticker.time;
\r
18171 _rootFramesTimeline._startTime = _ticker.frame;
\r
18172 _rootTimeline._active = _rootFramesTimeline._active = true;
\r
18173 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
18175 Animation._updateRoot = TweenLite.render = function() {
\r
18177 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
18180 _rootTimeline.render((_ticker.time - _rootTimeline._startTime) * _rootTimeline._timeScale, false, false);
\r
18181 _rootFramesTimeline.render((_ticker.frame - _rootFramesTimeline._startTime) * _rootFramesTimeline._timeScale, false, false);
\r
18182 if (_lazyTweens.length) {
\r
18185 if (!(_ticker.frame % 120)) { //dump garbage every 120 frames...
\r
18186 for (p in _tweenLookup) {
\r
18187 a = _tweenLookup[p].tweens;
\r
18189 while (--i > -1) {
\r
18194 if (a.length === 0) {
\r
18195 delete _tweenLookup[p];
\r
18198 //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
18199 p = _rootTimeline._first;
\r
18200 if (!p || p._paused) if (TweenLite.autoSleep && !_rootFramesTimeline._first && _ticker._listeners.tick.length === 1) {
\r
18201 while (p && p._paused) {
\r
18211 _ticker.addEventListener("tick", Animation._updateRoot);
\r
18213 var _register = function(target, tween, scrub) {
\r
18214 var id = target._gsTweenID, a, i;
\r
18215 if (!_tweenLookup[id || (target._gsTweenID = id = "t" + (_tweenLookupNum++))]) {
\r
18216 _tweenLookup[id] = {target:target, tweens:[]};
\r
18219 a = _tweenLookup[id].tweens;
\r
18220 a[(i = a.length)] = tween;
\r
18222 while (--i > -1) {
\r
18223 if (a[i] === tween) {
\r
18229 return _tweenLookup[id].tweens;
\r
18232 _applyOverwrite = function(target, tween, props, mode, siblings) {
\r
18233 var i, changed, curTween, l;
\r
18234 if (mode === 1 || mode >= 4) {
\r
18235 l = siblings.length;
\r
18236 for (i = 0; i < l; i++) {
\r
18237 if ((curTween = siblings[i]) !== tween) {
\r
18238 if (!curTween._gc) if (curTween._enabled(false, false)) {
\r
18241 } else if (mode === 5) {
\r
18247 //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
18248 var startTime = tween._startTime + _tinyNum,
\r
18251 zeroDur = (tween._duration === 0),
\r
18253 i = siblings.length;
\r
18254 while (--i > -1) {
\r
18255 if ((curTween = siblings[i]) === tween || curTween._gc || curTween._paused) {
\r
18257 } else if (curTween._timeline !== tween._timeline) {
\r
18258 globalStart = globalStart || _checkOverlap(tween, 0, zeroDur);
\r
18259 if (_checkOverlap(curTween, globalStart, zeroDur) === 0) {
\r
18260 overlaps[oCount++] = curTween;
\r
18262 } else if (curTween._startTime <= startTime) if (curTween._startTime + curTween.totalDuration() / curTween._timeScale > startTime) if (!((zeroDur || !curTween._initted) && startTime - curTween._startTime <= 0.0000000002)) {
\r
18263 overlaps[oCount++] = curTween;
\r
18268 while (--i > -1) {
\r
18269 curTween = overlaps[i];
\r
18270 if (mode === 2) if (curTween._kill(props, target)) {
\r
18273 if (mode !== 2 || (!curTween._firstPT && curTween._initted)) {
\r
18274 if (curTween._enabled(false, false)) { //if all property tweens have been overwritten, kill the tween.
\r
18282 _checkOverlap = function(tween, reference, zeroDur) {
\r
18283 var tl = tween._timeline,
\r
18284 ts = tl._timeScale,
\r
18285 t = tween._startTime;
\r
18286 while (tl._timeline) {
\r
18287 t += tl._startTime;
\r
18288 ts *= tl._timeScale;
\r
18289 if (tl._paused) {
\r
18292 tl = tl._timeline;
\r
18295 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
18299 //---- TweenLite instance methods -----------------------------------------------------------------------------
\r
18301 p._init = function() {
\r
18302 var v = this.vars,
\r
18303 op = this._overwrittenProps,
\r
18304 dur = this._duration,
\r
18305 immediate = !!v.immediateRender,
\r
18307 i, initPlugins, pt, p, startVars;
\r
18309 if (this._startAt) {
\r
18310 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
18311 this._startAt.kill();
\r
18314 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
18315 startVars[p] = v.startAt[p];
\r
18317 startVars.overwrite = false;
\r
18318 startVars.immediateRender = true;
\r
18319 startVars.lazy = (immediate && v.lazy !== false);
\r
18320 startVars.startAt = startVars.delay = null; //no nesting of startAt objects allowed (otherwise it could cause an infinite loop).
\r
18321 this._startAt = TweenLite.to(this.target, 0, startVars);
\r
18323 if (this._time > 0) {
\r
18324 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
18325 } else if (dur !== 0) {
\r
18326 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
18329 } else if (v.runBackwards && dur !== 0) {
\r
18330 //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
18331 if (this._startAt) {
\r
18332 this._startAt.render(-1, true);
\r
18333 this._startAt.kill();
\r
18334 this._startAt = null;
\r
18337 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
18338 if (!_reservedProps[p] || p === "autoCSS") {
\r
18342 pt.overwrite = 0;
\r
18343 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
18344 pt.lazy = (immediate && v.lazy !== false);
\r
18345 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
18346 this._startAt = TweenLite.to(this.target, 0, pt);
\r
18347 if (!immediate) {
\r
18348 this._startAt._init(); //ensures that the initial values are recorded
\r
18349 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
18350 } else if (this._time === 0) {
\r
18356 this._ease = TweenLite.defaultEase;
\r
18357 } else if (ease instanceof Ease) {
\r
18358 this._ease = (v.easeParams instanceof Array) ? ease.config.apply(ease, v.easeParams) : ease;
\r
18360 this._ease = (typeof(ease) === "function") ? new Ease(ease, v.easeParams) : _easeMap[ease] || TweenLite.defaultEase;
\r
18362 this._easeType = this._ease._type;
\r
18363 this._easePower = this._ease._power;
\r
18364 this._firstPT = null;
\r
18366 if (this._targets) {
\r
18367 i = this._targets.length;
\r
18368 while (--i > -1) {
\r
18369 if ( this._initProps( this._targets[i], (this._propLookup[i] = {}), this._siblings[i], (op ? op[i] : null)) ) {
\r
18370 initPlugins = true;
\r
18374 initPlugins = this._initProps(this.target, this._propLookup, this._siblings, op);
\r
18377 if (initPlugins) {
\r
18378 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
18380 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
18381 this._enabled(false, false);
\r
18383 if (v.runBackwards) {
\r
18384 pt = this._firstPT;
\r
18391 this._onUpdate = v.onUpdate;
\r
18392 this._initted = true;
\r
18395 p._initProps = function(target, propLookup, siblings, overwrittenProps) {
\r
18396 var p, i, initPlugins, plugin, pt, v;
\r
18397 if (target == null) {
\r
18401 if (_lazyLookup[target._gsTweenID]) {
\r
18402 _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
18405 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
18406 _autoCSS(this.vars, target);
\r
18408 for (p in this.vars) {
\r
18409 v = this.vars[p];
\r
18410 if (_reservedProps[p]) {
\r
18411 if (v) if ((v instanceof Array) || (v.push && _isArray(v))) if (v.join("").indexOf("{self}") !== -1) {
\r
18412 this.vars[p] = v = this._swapSelfInParams(v, this);
\r
18415 } else if (_plugins[p] && (plugin = new _plugins[p]())._onInitTween(target, this.vars[p], this)) {
\r
18417 //t - target [object]
\r
18418 //p - property [string]
\r
18419 //s - start [number]
\r
18420 //c - change [number]
\r
18421 //f - isFunction [boolean]
\r
18422 //n - name [string]
\r
18423 //pg - isPlugin [boolean]
\r
18424 //pr - priority [number]
\r
18425 this._firstPT = pt = {_next:this._firstPT, t:plugin, p:"setRatio", s:0, c:1, f:true, n:p, pg:true, pr:plugin._priority};
\r
18426 i = plugin._overwriteProps.length;
\r
18427 while (--i > -1) {
\r
18428 propLookup[plugin._overwriteProps[i]] = this._firstPT;
\r
18430 if (plugin._priority || plugin._onInitAllProps) {
\r
18431 initPlugins = true;
\r
18433 if (plugin._onDisable || plugin._onEnable) {
\r
18434 this._notifyPluginsOfEnabled = true;
\r
18438 this._firstPT = propLookup[p] = pt = {_next:this._firstPT, t:target, p:p, f:(typeof(target[p]) === "function"), n:p, pg:false, pr:0};
\r
18439 pt.s = (!pt.f) ? parseFloat(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]();
\r
18440 pt.c = (typeof(v) === "string" && v.charAt(1) === "=") ? parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) : (Number(v) - pt.s) || 0;
\r
18442 if (pt) if (pt._next) {
\r
18443 pt._next._prev = pt;
\r
18447 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
18448 return this._initProps(target, propLookup, siblings, overwrittenProps);
\r
18450 if (this._overwrite > 1) if (this._firstPT) if (siblings.length > 1) if (_applyOverwrite(target, this, propLookup, this._overwrite, siblings)) {
\r
18451 this._kill(propLookup, target);
\r
18452 return this._initProps(target, propLookup, siblings, overwrittenProps);
\r
18454 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
18455 _lazyLookup[target._gsTweenID] = true;
\r
18457 return initPlugins;
\r
18460 p.render = function(time, suppressEvents, force) {
\r
18461 var prevTime = this._time,
\r
18462 duration = this._duration,
\r
18463 prevRawPrevTime = this._rawPrevTime,
\r
18464 isComplete, callback, pt, rawPrevTime;
\r
18465 if (time >= duration) {
\r
18466 this._totalTime = this._time = duration;
\r
18467 this.ratio = this._ease._calcEnd ? this._ease.getRatio(1) : 1;
\r
18468 if (!this._reversed ) {
\r
18469 isComplete = true;
\r
18470 callback = "onComplete";
\r
18472 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
18473 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
18476 if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time) {
\r
18478 if (prevRawPrevTime > _tinyNum) {
\r
18479 callback = "onReverseComplete";
\r
18482 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
18485 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
\r
18486 this._totalTime = this._time = 0;
\r
18487 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
\r
18488 if (prevTime !== 0 || (duration === 0 && prevRawPrevTime > 0 && prevRawPrevTime !== _tinyNum)) {
\r
18489 callback = "onReverseComplete";
\r
18490 isComplete = this._reversed;
\r
18493 this._active = false;
\r
18494 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
18495 if (prevRawPrevTime >= 0) {
\r
18498 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
18500 } 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
18504 this._totalTime = this._time = time;
\r
18506 if (this._easeType) {
\r
18507 var r = time / duration, type = this._easeType, pow = this._easePower;
\r
18508 if (type === 1 || (type === 3 && r >= 0.5)) {
\r
18511 if (type === 3) {
\r
18516 } else if (pow === 2) {
\r
18518 } else if (pow === 3) {
\r
18520 } else if (pow === 4) {
\r
18521 r *= r * r * r * r;
\r
18524 if (type === 1) {
\r
18525 this.ratio = 1 - r;
\r
18526 } else if (type === 2) {
\r
18528 } else if (time / duration < 0.5) {
\r
18529 this.ratio = r / 2;
\r
18531 this.ratio = 1 - (r / 2);
\r
18535 this.ratio = this._ease.getRatio(time / duration);
\r
18539 if (this._time === prevTime && !force) {
\r
18541 } else if (!this._initted) {
\r
18543 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
18545 } else if (!force && this._firstPT && ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration))) {
\r
18546 this._time = this._totalTime = prevTime;
\r
18547 this._rawPrevTime = prevRawPrevTime;
\r
18548 _lazyTweens.push(this);
\r
18549 this._lazy = time;
\r
18552 //_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
18553 if (this._time && !isComplete) {
\r
18554 this.ratio = this._ease.getRatio(this._time / duration);
\r
18555 } else if (isComplete && this._ease._calcEnd) {
\r
18556 this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1);
\r
18559 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
18560 this._lazy = false;
\r
18562 if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) {
\r
18563 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
18565 if (prevTime === 0) {
\r
18566 if (this._startAt) {
\r
18568 this._startAt.render(time, suppressEvents, force);
\r
18569 } else if (!callback) {
\r
18570 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
18573 if (this.vars.onStart) if (this._time !== 0 || duration === 0) if (!suppressEvents) {
\r
18574 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
\r
18578 pt = this._firstPT;
\r
18581 pt.t[pt.p](pt.c * this.ratio + pt.s);
\r
18583 pt.t[pt.p] = pt.c * this.ratio + pt.s;
\r
18588 if (this._onUpdate) {
\r
18589 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
18590 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
18592 if (!suppressEvents) if (this._time !== prevTime || isComplete) {
\r
18593 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
\r
18597 if (callback) if (!this._gc) { //check _gc because there's a chance that kill() could be called in an onUpdate
\r
18598 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
18599 this._startAt.render(time, suppressEvents, force);
\r
18601 if (isComplete) {
\r
18602 if (this._timeline.autoRemoveChildren) {
\r
18603 this._enabled(false, false);
\r
18605 this._active = false;
\r
18607 if (!suppressEvents && this.vars[callback]) {
\r
18608 this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
\r
18610 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
18611 this._rawPrevTime = 0;
\r
18617 p._kill = function(vars, target) {
\r
18618 if (vars === "all") {
\r
18621 if (vars == null) if (target == null || target === this.target) {
\r
18622 this._lazy = false;
\r
18623 return this._enabled(false, false);
\r
18625 target = (typeof(target) !== "string") ? (target || this._targets || this.target) : TweenLite.selector(target) || target;
\r
18626 var i, overwrittenProps, p, pt, propLookup, changed, killProps, record;
\r
18627 if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") {
\r
18628 i = target.length;
\r
18629 while (--i > -1) {
\r
18630 if (this._kill(vars, target[i])) {
\r
18635 if (this._targets) {
\r
18636 i = this._targets.length;
\r
18637 while (--i > -1) {
\r
18638 if (target === this._targets[i]) {
\r
18639 propLookup = this._propLookup[i] || {};
\r
18640 this._overwrittenProps = this._overwrittenProps || [];
\r
18641 overwrittenProps = this._overwrittenProps[i] = vars ? this._overwrittenProps[i] || {} : "all";
\r
18645 } else if (target !== this.target) {
\r
18648 propLookup = this._propLookup;
\r
18649 overwrittenProps = this._overwrittenProps = vars ? this._overwrittenProps || {} : "all";
\r
18652 if (propLookup) {
\r
18653 killProps = vars || propLookup;
\r
18654 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
18655 for (p in killProps) {
\r
18656 if ((pt = propLookup[p])) {
\r
18657 if (pt.pg && pt.t._kill(killProps)) {
\r
18658 changed = true; //some plugins need to be notified so they can perform cleanup tasks first
\r
18660 if (!pt.pg || pt.t._overwriteProps.length === 0) {
\r
18662 pt._prev._next = pt._next;
\r
18663 } else if (pt === this._firstPT) {
\r
18664 this._firstPT = pt._next;
\r
18667 pt._next._prev = pt._prev;
\r
18669 pt._next = pt._prev = null;
\r
18671 delete propLookup[p];
\r
18674 overwrittenProps[p] = 1;
\r
18677 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
18678 this._enabled(false, false);
\r
18685 p.invalidate = function() {
\r
18686 if (this._notifyPluginsOfEnabled) {
\r
18687 TweenLite._onPluginEvent("_onDisable", this);
\r
18689 this._firstPT = null;
\r
18690 this._overwrittenProps = null;
\r
18691 this._onUpdate = null;
\r
18692 this._startAt = null;
\r
18693 this._initted = this._active = this._notifyPluginsOfEnabled = this._lazy = false;
\r
18694 this._propLookup = (this._targets) ? {} : [];
\r
18698 p._enabled = function(enabled, ignoreTimeline) {
\r
18699 if (!_tickerActive) {
\r
18702 if (enabled && this._gc) {
\r
18703 var targets = this._targets,
\r
18706 i = targets.length;
\r
18707 while (--i > -1) {
\r
18708 this._siblings[i] = _register(targets[i], this, true);
\r
18711 this._siblings = _register(this.target, this, true);
\r
18714 Animation.prototype._enabled.call(this, enabled, ignoreTimeline);
\r
18715 if (this._notifyPluginsOfEnabled) if (this._firstPT) {
\r
18716 return TweenLite._onPluginEvent((enabled ? "_onEnable" : "_onDisable"), this);
\r
18722 //----TweenLite static methods -----------------------------------------------------
\r
18724 TweenLite.to = function(target, duration, vars) {
\r
18725 return new TweenLite(target, duration, vars);
\r
18728 TweenLite.from = function(target, duration, vars) {
\r
18729 vars.runBackwards = true;
\r
18730 vars.immediateRender = (vars.immediateRender != false);
\r
18731 return new TweenLite(target, duration, vars);
\r
18734 TweenLite.fromTo = function(target, duration, fromVars, toVars) {
\r
18735 toVars.startAt = fromVars;
\r
18736 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
\r
18737 return new TweenLite(target, duration, toVars);
\r
18740 TweenLite.delayedCall = function(delay, callback, params, scope, useFrames) {
\r
18741 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
18744 TweenLite.set = function(target, vars) {
\r
18745 return new TweenLite(target, 0, vars);
\r
18748 TweenLite.getTweensOf = function(target, onlyActive) {
\r
18749 if (target == null) { return []; }
\r
18750 target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target;
\r
18752 if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") {
\r
18753 i = target.length;
\r
18755 while (--i > -1) {
\r
18756 a = a.concat(TweenLite.getTweensOf(target[i], onlyActive));
\r
18759 //now get rid of any duplicates (tweens of arrays of objects could cause duplicates)
\r
18760 while (--i > -1) {
\r
18763 while (--j > -1) {
\r
18764 if (t === a[j]) {
\r
18770 a = _register(target).concat();
\r
18772 while (--i > -1) {
\r
18773 if (a[i]._gc || (onlyActive && !a[i].isActive())) {
\r
18781 TweenLite.killTweensOf = TweenLite.killDelayedCallsTo = function(target, onlyActive, vars) {
\r
18782 if (typeof(onlyActive) === "object") {
\r
18783 vars = onlyActive; //for backwards compatibility (before "onlyActive" parameter was inserted)
\r
18784 onlyActive = false;
\r
18786 var a = TweenLite.getTweensOf(target, onlyActive),
\r
18788 while (--i > -1) {
\r
18789 a[i]._kill(vars, target);
\r
18796 * ----------------------------------------------------------------
\r
18797 * 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
18798 * ----------------------------------------------------------------
\r
18800 var TweenPlugin = _class("plugins.TweenPlugin", function(props, priority) {
\r
18801 this._overwriteProps = (props || "").split(",");
\r
18802 this._propName = this._overwriteProps[0];
\r
18803 this._priority = priority || 0;
\r
18804 this._super = TweenPlugin.prototype;
\r
18807 p = TweenPlugin.prototype;
\r
18808 TweenPlugin.version = "1.10.1";
\r
18809 TweenPlugin.API = 2;
\r
18810 p._firstPT = null;
\r
18812 p._addTween = function(target, prop, start, end, overwriteProp, round) {
\r
18814 if (end != null && (c = (typeof(end) === "number" || end.charAt(1) !== "=") ? Number(end) - start : parseInt(end.charAt(0) + "1", 10) * Number(end.substr(2)))) {
\r
18815 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
18817 pt._next._prev = pt;
\r
18823 p.setRatio = function(v) {
\r
18824 var pt = this._firstPT,
\r
18828 val = pt.c * v + pt.s;
\r
18830 val = Math.round(val);
\r
18831 } else if (val < min) if (val > -min) { //prevents issues with converting very small numbers to strings in the browser
\r
18837 pt.t[pt.p] = val;
\r
18843 p._kill = function(lookup) {
\r
18844 var a = this._overwriteProps,
\r
18845 pt = this._firstPT,
\r
18847 if (lookup[this._propName] != null) {
\r
18848 this._overwriteProps = [];
\r
18851 while (--i > -1) {
\r
18852 if (lookup[a[i]] != null) {
\r
18858 if (lookup[pt.n] != null) {
\r
18860 pt._next._prev = pt._prev;
\r
18863 pt._prev._next = pt._next;
\r
18865 } else if (this._firstPT === pt) {
\r
18866 this._firstPT = pt._next;
\r
18874 p._roundProps = function(lookup, value) {
\r
18875 var pt = this._firstPT;
\r
18877 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
18884 TweenLite._onPluginEvent = function(type, tween) {
\r
18885 var pt = tween._firstPT,
\r
18886 changed, pt2, first, last, next;
\r
18887 if (type === "_onInitAllProps") {
\r
18888 //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
18892 while (pt2 && pt2.pr > pt.pr) {
\r
18895 if ((pt._prev = pt2 ? pt2._prev : last)) {
\r
18896 pt._prev._next = pt;
\r
18900 if ((pt._next = pt2)) {
\r
18907 pt = tween._firstPT = first;
\r
18910 if (pt.pg) if (typeof(pt.t[type]) === "function") if (pt.t[type]()) {
\r
18918 TweenPlugin.activate = function(plugins) {
\r
18919 var i = plugins.length;
\r
18920 while (--i > -1) {
\r
18921 if (plugins[i].API === TweenPlugin.API) {
\r
18922 _plugins[(new plugins[i]())._propName] = plugins[i];
\r
18928 //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
18929 _gsDefine.plugin = function(config) {
\r
18930 if (!config || !config.propName || !config.init || !config.API) { throw "illegal plugin definition."; }
\r
18931 var propName = config.propName,
\r
18932 priority = config.priority || 0,
\r
18933 overwriteProps = config.overwriteProps,
\r
18934 map = {init:"_onInitTween", set:"setRatio", kill:"_kill", round:"_roundProps", initAll:"_onInitAllProps"},
\r
18935 Plugin = _class("plugins." + propName.charAt(0).toUpperCase() + propName.substr(1) + "Plugin",
\r
18937 TweenPlugin.call(this, propName, priority);
\r
18938 this._overwriteProps = overwriteProps || [];
\r
18939 }, (config.global === true)),
\r
18940 p = Plugin.prototype = new TweenPlugin(propName),
\r
18942 p.constructor = Plugin;
\r
18943 Plugin.API = config.API;
\r
18944 for (prop in map) {
\r
18945 if (typeof(config[prop]) === "function") {
\r
18946 p[map[prop]] = config[prop];
\r
18949 Plugin.version = config.version;
\r
18950 TweenPlugin.activate([Plugin]);
\r
18955 //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
18956 a = window._gsQueue;
\r
18958 for (i = 0; i < a.length; i++) {
\r
18961 for (p in _defLookup) {
\r
18962 if (!_defLookup[p].func) {
\r
18963 //window.console.log("GSAP encountered missing dependency: com.greensock." + p);
\r
18968 _tickerActive = false; //ensures that the first official animation forces a ticker.tick() to update the time when it is instantiated
\r
18972 angular.module('b2b.att.collapse', ['b2b.att.transition'])
\r
18974 // The collapsible directive indicates a block of html that will expand and collapse
\r
18975 .directive('b2bCollapse', ['$transition', function($transition) {
\r
18976 // CSS transitions don't work with height: auto, so we have to manually change the height to a
\r
18977 // specific value and then once the animation completes, we can reset the height to auto.
\r
18978 // Unfortunately if you do this while the CSS transitions are specified (i.e. in the CSS class
\r
18979 // "collapse") then you trigger a change to height 0 in between.
\r
18980 // The fix is to remove the "collapse" CSS class while changing the height back to auto - phew!
\r
18985 marginBottom: null,
\r
18986 paddingTop: null,
\r
18987 paddingBottom: null,
\r
18994 paddingBottom: 0,
\r
18999 var fixUpHeight = function(scope, element, height) {
\r
19000 // We remove the collapse CSS class to prevent a transition when we change to height: auto
\r
19001 element.removeClass('b2bCollapse');
\r
19002 element.css({height: height});
\r
19003 //adjusting for any margin or padding
\r
19004 if (height === 0) {
\r
19005 element.css(props.closed);
\r
19007 element.css(props.open);
\r
19009 // It appears that reading offsetWidth makes the browser realise that we have changed the
\r
19010 // height already :-/
\r
19011 var x = element[0].offsetWidth;
\r
19012 element.addClass('b2bCollapse');
\r
19016 link: function(scope, element, attrs) {
\r
19018 var initialAnimSkip = true;
\r
19019 scope.$watch(function() {
\r
19020 return element[0].scrollHeight;
\r
19021 }, function(value) {
\r
19022 //The listener is called when scrollHeight changes
\r
19023 //It actually does on 2 scenarios:
\r
19024 // 1. Parent is set to display none
\r
19025 // 2. angular bindings inside are resolved
\r
19026 //When we have a change of scrollHeight we are setting again the correct height if the group is opened
\r
19027 if (element[0].scrollHeight !== 0) {
\r
19028 if (!isCollapsed) {
\r
19029 if (initialAnimSkip) {
\r
19030 fixUpHeight(scope, element, element[0].scrollHeight + 'px');
\r
19032 fixUpHeight(scope, element, 'auto');
\r
19033 element.css({overflow: 'visible'});
\r
19039 scope.$watch(attrs.b2bCollapse, function(value) {
\r
19048 var currentTransition;
\r
19049 var doTransition = function(change) {
\r
19050 if (currentTransition) {
\r
19051 currentTransition.cancel();
\r
19053 currentTransition = $transition(element, change);
\r
19054 currentTransition.then(
\r
19056 currentTransition = undefined;
\r
19059 currentTransition = undefined;
\r
19062 return currentTransition;
\r
19065 var expand = function() {
\r
19066 scope.postTransition = true;
\r
19067 if (initialAnimSkip) {
\r
19068 initialAnimSkip = false;
\r
19069 if (!isCollapsed) {
\r
19070 fixUpHeight(scope, element, 'auto');
\r
19073 //doTransition({ height : element[0].scrollHeight + 'px' })
\r
19074 doTransition(angular.extend({height: element[0].scrollHeight + 'px'}, props.open))
\r
19075 .then(function() {
\r
19076 // This check ensures that we don't accidentally update the height if the user has closed
\r
19077 // the group while the animation was still running
\r
19078 if (!isCollapsed) {
\r
19079 fixUpHeight(scope, element, 'auto');
\r
19083 isCollapsed = false;
\r
19086 var collapse = function() {
\r
19087 isCollapsed = true;
\r
19088 if (initialAnimSkip) {
\r
19089 initialAnimSkip = false;
\r
19090 fixUpHeight(scope, element, 0);
\r
19092 fixUpHeight(scope, element, element[0].scrollHeight + 'px');
\r
19093 doTransition(angular.extend({height: 0}, props.closed)).then(function() {
\r
19094 scope.postTransition = false;
\r
19096 element.css({overflow: 'hidden'});
\r
19102 angular.module('b2b.att.position', [])
\r
19104 .factory('$position', ['$document', '$window', function ($document, $window) {
\r
19105 function getStyle(el, cssprop) {
\r
19106 if (el.currentStyle) { //IE
\r
19107 return el.currentStyle[cssprop];
\r
19108 } else if ($window.getComputedStyle) {
\r
19109 return $window.getComputedStyle(el)[cssprop];
\r
19111 // finally try and get inline style
\r
19112 return el.style[cssprop];
\r
19116 * Checks if a given element is statically positioned
\r
19117 * @param element - raw DOM element
\r
19119 function isStaticPositioned(element) {
\r
19120 return (getStyle(element, "position") || 'static') === 'static';
\r
19124 * returns the closest, non-statically positioned parentOffset of a given element
\r
19127 var parentOffsetEl = function (element) {
\r
19128 var docDomEl = $document[0];
\r
19129 var offsetParent = element.offsetParent || docDomEl;
\r
19130 while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent)) {
\r
19131 offsetParent = offsetParent.offsetParent;
\r
19133 return offsetParent || docDomEl;
\r
19138 * Provides read-only equivalent of jQuery's position function:
\r
19139 * http://api.jquery.com/position/
\r
19141 position: function (element) {
\r
19142 var elBCR = this.offset(element);
\r
19143 var offsetParentBCR = {
\r
19147 var offsetParentEl = parentOffsetEl(element[0]);
\r
19148 if (offsetParentEl !== $document[0]) {
\r
19149 offsetParentBCR = this.offset(angular.element(offsetParentEl));
\r
19150 offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
\r
19151 offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
\r
19155 width: element.prop('offsetWidth'),
\r
19156 height: element.prop('offsetHeight'),
\r
19157 top: elBCR.top - offsetParentBCR.top,
\r
19158 left: elBCR.left - offsetParentBCR.left
\r
19163 * Provides read-only equivalent of jQuery's offset function:
\r
19164 * http://api.jquery.com/offset/
\r
19166 offset: function (element) {
\r
19167 var boundingClientRect = element[0].getBoundingClientRect();
\r
19169 width: element.prop('offsetWidth'),
\r
19170 height: element.prop('offsetHeight'),
\r
19171 top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
\r
19172 left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
\r
19177 * Provides functionality to check whether an element is in view port.
\r
19179 isElementInViewport: function (element) {
\r
19181 var rect = element[0].getBoundingClientRect();
\r
19184 rect.left >= 0 &&
\r
19185 rect.bottom <= ($window.innerHeight || $document[0].documentElement.clientHeight) &&
\r
19186 rect.right <= ($window.innerWidth || $document[0].documentElement.clientWidth)
\r
19195 .factory('$isElement', [function () {
\r
19196 var isElement = function (currentElem, targetElem, alternateElem) {
\r
19197 if (currentElem[0] === targetElem[0]) {
\r
19199 } else if (currentElem[0] === alternateElem[0]) {
\r
19202 return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
\r
19206 return isElement;
\r
19209 .directive('attPosition', ['$position', function ($position) {
\r
19212 link: function (scope, elem, attr) {
\r
19213 scope.$watchCollection(function () {
\r
19214 return $position.position(elem);
\r
19215 }, function (value) {
\r
19216 scope[attr.attPosition] = value;
\r
19222 angular.module('b2b.att.transition', [])
\r
19224 .factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
\r
19226 var $transition = function(element, trigger, options) {
\r
19227 options = options || {};
\r
19228 var deferred = $q.defer();
\r
19229 var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
\r
19231 var transitionEndHandler = function() {
\r
19232 $rootScope.$apply(function() {
\r
19233 element.unbind(endEventName, transitionEndHandler);
\r
19234 deferred.resolve(element);
\r
19238 if (endEventName) {
\r
19239 element.bind(endEventName, transitionEndHandler);
\r
19242 // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
\r
19243 $timeout(function() {
\r
19244 if ( angular.isString(trigger) ) {
\r
19245 element.addClass(trigger);
\r
19246 } else if ( angular.isFunction(trigger) ) {
\r
19247 trigger(element);
\r
19248 } else if ( angular.isObject(trigger) ) {
\r
19249 element.css(trigger);
\r
19251 //If browser does not support transitions, instantly resolve
\r
19252 if ( !endEventName ) {
\r
19253 deferred.resolve(element);
\r
19257 // Add our custom cancel function to the promise that is returned
\r
19258 // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
\r
19259 // i.e. it will therefore never raise a transitionEnd event for that transition
\r
19260 deferred.promise.cancel = function() {
\r
19261 if ( endEventName ) {
\r
19262 element.unbind(endEventName, transitionEndHandler);
\r
19264 deferred.reject('Transition cancelled');
\r
19267 return deferred.promise;
\r
19270 // Work out the name of the transitionEnd event
\r
19271 var transElement = document.createElement('trans');
\r
19272 var transitionEndEventNames = {
\r
19273 'WebkitTransition': 'webkitTransitionEnd',
\r
19274 'MozTransition': 'transitionend',
\r
19275 'OTransition': 'oTransitionEnd',
\r
19276 'transition': 'transitionend'
\r
19278 var animationEndEventNames = {
\r
19279 'WebkitTransition': 'webkitAnimationEnd',
\r
19280 'MozTransition': 'animationend',
\r
19281 'OTransition': 'oAnimationEnd',
\r
19282 'transition': 'animationend'
\r
19284 function findEndEventName(endEventNames) {
\r
19285 for (var name in endEventNames){
\r
19286 if (transElement.style[name] !== undefined) {
\r
19287 return endEventNames[name];
\r
19291 $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
\r
19292 $transition.animationEndEventName = findEndEventName(animationEndEventNames);
\r
19293 return $transition;
\r
19296 .factory('$scrollTo', ['$window', function($window) {
\r
19297 var $scrollTo = function(offsetLeft, offsetTop, duration) {
\r
19298 TweenMax.to($window, duration || 1, {scrollTo: {y: offsetTop, x: offsetLeft}, ease: Power4.easeOut});
\r
19300 return $scrollTo;
\r
19302 .factory('animation', function(){
\r
19305 .factory('$progressBar', function(){
\r
19307 //Provides a function to pass in code for closure purposes
\r
19308 var loadingAnimationCreator = function(onUpdateCallback){
\r
19310 //Use closure to setup some resuable code
\r
19311 var loadingAnimation = function(callback, duration){
\r
19312 TweenMax.to({}, duration, {
\r
19313 onUpdateParams: ["{self}"],
\r
19314 onUpdate: onUpdateCallback,
\r
19315 onComplete: callback
\r
19318 //Returns a function that takes a callback function and a duration for the animation
\r
19319 return (function(){
\r
19320 return loadingAnimation;
\r
19324 return loadingAnimationCreator;
\r
19326 .factory('$height', function(){
\r
19327 var heightAnimation = function(element,duration,height,alpha){
\r
19328 TweenMax.to(element,
\r
19330 {height:height, autoAlpha:alpha},
\r
19333 return heightAnimation;
\r
19335 angular.module('b2b.att.utilities', ['ngSanitize'])
\r
19336 .constant('b2bUtilitiesConfig', {
\r
19343 enableSearch: false,
\r
19344 searchTimer: 200,
\r
19345 circularTraversal: false
\r
19347 .constant('b2bWhenScrollEndsConstants', {
\r
19348 'threshold': 100,
\r
19352 // All breakpoints ranges from >= min and < max
\r
19353 .constant('b2bAwdBreakpoints', {
\r
19369 .filter('groupBy', function ($timeout) {
\r
19370 //Custom GroupBy Filter for treeNav, returns key string and value.childarray as set of grouped elements
\r
19371 return function (data, key) {
\r
19372 if (!key) return data;
\r
19373 var outputPropertyName = '__groupBy__' + key;
\r
19374 if (!data[outputPropertyName]) {
\r
19376 for (var i = 0; i < data.length; i++) {
\r
19377 if (!result[data[i][key]])
\r
19378 result[data[i][key]] = {};
\r
19379 if (!result[data[i][key]].childArray) {
\r
19380 result[data[i][key]].childArray = [];
\r
19382 result[data[i][key]].childArray.push(data[i]);
\r
19383 if (data[i].activeGrp && data[i].activeGrp == true) {
\r
19384 console.log('make ' + data[i].grpChild + ' active');
\r
19385 result[data[i][key]].showGroup = true;
\r
19388 Object.defineProperty(result, 'length', {enumerable: false,value: Object.keys(result).length});
\r
19389 Object.defineProperty(data, outputPropertyName, {enumerable: false,configurable: true,writable:false,value:result});
\r
19390 $timeout(function(){delete data[outputPropertyName];},0,false);
\r
19392 return data[outputPropertyName];
\r
19395 .filter('searchObjectPropertiesFilter', [function() {
\r
19396 return function(items, searchText, attrs) {
\r
19400 var filtered = [];
\r
19401 searchText = searchText.toLowerCase();
\r
19402 angular.forEach(items, function(item) {
\r
19403 angular.forEach(attrs, function(attr) {
\r
19404 if (item.hasOwnProperty(attr) && item[attr].toLowerCase().includes(searchText)) {
\r
19405 filtered.push(item);
\r
19413 .filter('unsafe',[ '$sce', function ($sce) {
\r
19414 return function(val){
\r
19415 return $sce.trustAsHtml(val);
\r
19418 .filter('b2bHighlight', function () {
\r
19419 function escapeRegexp(queryToEscape) {
\r
19420 return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
\r
19422 return function (matchItem, query, className) {
\r
19423 return query && matchItem ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<span class=\"' + className + '\">$&</span>') : matchItem;
\r
19427 Copyright © 2013 Matt Diamond
\r
19428 https://github.com/cwilso/AudioRecorder/blob/master/js/recorderjs/recorder.js
\r
19430 .factory('b2bRecorder', function() {
\r
19432 var Recorder = function(source, cfg) {
\r
19433 var WORKER_PATH = 'recorderWorker.js';
\r
19434 var config = cfg || {};
\r
19435 var bufferLen = config.bufferLen || 4096;
\r
19436 this.context = source.context;
\r
19437 if(!this.context.createScriptProcessor) {
\r
19438 this.node = this.context.createJavacriptProcessor(bufferLen, 2, 2);
\r
19440 this.node = this.context.createScriptProcessor(bufferLen, 2, 2);
\r
19442 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
19443 var blob = new Blob([workerCode]);
\r
19445 var worker = new Worker(window.URL.createObjectURL(blob)); //TODO: Use a blob instead
\r
19446 worker.postMessage({
\r
19449 sampleRate: this.context.sampleRate
\r
19452 var recording = false,
\r
19455 this.node.onaudioprocess = function(e) {
\r
19456 if (!recording) return;
\r
19457 worker.postMessage({
\r
19458 command: 'record',
\r
19460 e.inputBuffer.getChannelData(0),
\r
19461 e.inputBuffer.getChannelData(1)
\r
19466 this.configure = function(cfg) {
\r
19467 for (var prop in cfg) {//TODO: look into using angular.extend() here
\r
19468 if (cfg.hasOwnProperty(prop)) {
\r
19469 config[prop] = cfg[prop];
\r
19474 this.record = function() {
\r
19475 recording = true;
\r
19478 this.stop = function() {
\r
19479 recording = false;
\r
19482 this.clear = function() {
\r
19483 worker.postMessage({ command: 'clear' });
\r
19484 window.URL.revokeObjectURL(blob);
\r
19487 this.getBuffers = function(cb) {
\r
19488 currCallback = cb || config.callback;
\r
19489 worker.postMessage({ command: 'getBuffers' });
\r
19492 this.exportWAV = function(cb, type) {
\r
19493 currCallback = cb || config.callback;
\r
19494 type = type || config.type || 'audio/wav';
\r
19495 if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
\r
19496 worker.postMessage({
\r
19497 command: 'exportWAV',
\r
19502 this.exportMonoWAV = function(cb, type) {
\r
19503 currCallback = cb || config.callback;
\r
19504 type = type || config.type || 'audio/wav';
\r
19505 if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
\r
19506 worker.postMessage({
\r
19507 command: 'exportMonoWAV',
\r
19512 worker.onmessage = function(e) {
\r
19513 var blob = e.data;
\r
19514 currCallback(blob);
\r
19517 source.connect(this.node);
\r
19518 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
19525 .factory('b2bViewport', function() {
\r
19526 /* Source: https://gist.github.com/bjankord/2399828 */
\r
19527 var _viewportWidth = function() {
\r
19529 var webkit = (!(window.webkitConvertPointFromNodeToPage == null));
\r
19533 var vpwtest = document.createElement( "div" );
\r
19534 // Sets test div to width 100%, !important overrides any other misc. box model styles that may be set in the CSS
\r
19535 vpwtest.style.cssText = "width:100% !important; margin:0 !important; padding:0 !important; border:none !important;";
\r
19536 document.documentElement.insertBefore( vpwtest, document.documentElement.firstChild );
\r
19537 vpw = vpwtest.offsetWidth;
\r
19538 document.documentElement.removeChild( vpwtest );
\r
19541 else if ( window.innerWidth === undefined ) {
\r
19542 vpw = document.documentElement.clientWidth;
\r
19546 vpw = window.innerWidth;
\r
19552 viewportWidth: _viewportWidth
\r
19555 .directive('b2bWhenScrollEnds', function(b2bWhenScrollEndsConstants, $log) {
\r
19558 link: function (scope, element, attrs) {
\r
19560 * Exposed Attributes:
\r
19561 * threshold - integer - number of pixels before scrollbar hits end that callback is called
\r
19562 * width - integer - override for element's width (px)
\r
19563 * height - integer - override for element's height (px)
\r
19564 * axis - string - x/y for scroll bar axis
\r
19566 var threshold = parseInt(attrs.threshold, 10) || b2bWhenScrollEndsConstants.threshold;
\r
19568 if (!attrs.axis || attrs.axis === '') {
\r
19569 $log.warn('axis attribute must be defined for b2bWhenScrollEnds.');
\r
19573 if (attrs.axis === 'x') {
\r
19574 visibleWidth = parseInt(attrs.width, 10) || b2bWhenScrollEndsConstants.width;
\r
19575 if (element.css('width')) {
\r
19576 visibleWidth = element.css('width').split('px')[0];
\r
19579 element[0].addEventListener('scroll', function() {
\r
19580 var scrollableWidth = element.prop('scrollWidth');
\r
19581 if (scrollableWidth === undefined) {
\r
19582 scrollableWidth = 1;
\r
19584 var hiddenContentWidth = scrollableWidth - visibleWidth;
\r
19586 if (hiddenContentWidth - element[0].scrollLeft <= threshold) {
\r
19587 /* Scroll almost at bottom, load more rows */
\r
19588 scope.$apply(attrs.b2bWhenScrollEnds);
\r
19591 } else if (attrs.axis === 'y') {
\r
19592 visibleHeight = parseInt(attrs.height, 10) || b2bWhenScrollEndsConstants.height;
\r
19593 if (element.css('width')) {
\r
19594 visibleHeight = element.css('height').split('px')[0];
\r
19597 element[0].addEventListener('scroll', function() {
\r
19598 var scrollableHeight = element.prop('scrollHeight');
\r
19599 if (scrollableHeight === undefined) {
\r
19600 scrollableHeight = 1;
\r
19602 var hiddenContentHeight = scrollableHeight - visibleHeight;
\r
19604 if (hiddenContentHeight - element[0].scrollTop <= threshold) {
\r
19605 /* Scroll almost at bottom, load more rows */
\r
19606 scope.$apply(attrs.b2bWhenScrollEnds);
\r
19614 .factory('$windowBind', ['$window', '$timeout', function($window, $timeout) {
\r
19615 var win = angular.element($window);
\r
19616 var _scroll = function (flag, callbackFunc, scope) {
\r
19617 scope.$watch(flag, function (val) {
\r
19618 $timeout(function () {
\r
19620 win.bind('scroll', callbackFunc);
\r
19622 win.unbind('scroll', callbackFunc);
\r
19628 var throttle = function(type, name, obj) {
\r
19629 obj = obj || window;
\r
19630 var running = false;
\r
19631 var func = function() {
\r
19632 if (running) { return; }
\r
19634 requestAnimationFrame(function() {
\r
19635 obj.dispatchEvent(new CustomEvent(name));
\r
19639 obj.addEventListener(type, func);
\r
19642 var _resize = function(callbackFunc, scope) {
\r
19643 throttle("resize", "optimizedResize");
\r
19644 window.addEventListener("optimizedResize", function(event) {
\r
19646 //win.bind(event, callbackFunc);
\r
19647 if (!scope.$$phase) {
\r
19653 var _click = function (flag, callbackFunc, scope) {
\r
19654 scope.$watch(flag, function (val) {
\r
19655 $timeout(function () {
\r
19657 win.bind('click', callbackFunc);
\r
19659 win.unbind('click', callbackFunc);
\r
19665 var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
\r
19666 if (timeoutFlag) {
\r
19667 if (!(timeoutValue)) {
\r
19668 timeoutValue = 0;
\r
19670 scope.$watch(flag, function (newVal, oldVal) {
\r
19671 if (newVal !== oldVal) {
\r
19672 $timeout(function () {
\r
19674 win.bind(event, callbackFunc);
\r
19676 win.unbind(event, callbackFunc);
\r
19678 }, timeoutValue);
\r
19682 scope.$watch(flag, function (newVal, oldVal) {
\r
19683 if (newVal !== oldVal) {
\r
19685 win.bind(event, callbackFunc);
\r
19687 win.unbind(event, callbackFunc);
\r
19702 .factory('keymap', function () {
\r
19724 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
19726 isControl: function (e) {
\r
19727 var k = e.keyCode;
\r
19729 case this.KEY.COMMAND:
\r
19730 case this.KEY.SHIFT:
\r
19731 case this.KEY.CTRL:
\r
19732 case this.KEY.ALT:
\r
19743 isFunctionKey: function (k) {
\r
19744 k = k.keyCode ? k.keyCode : k;
\r
19745 return k >= 112 && k <= 123;
\r
19747 isVerticalMovement: function (k) {
\r
19748 return ~[this.KEY.UP, this.KEY.DOWN].indexOf(k);
\r
19750 isHorizontalMovement: function (k) {
\r
19751 return ~[this.KEY.LEFT, this.KEY.RIGHT, this.KEY.BACKSPACE, this.KEY.DELETE].indexOf(k);
\r
19753 isAllowedKey: function (k) {
\r
19754 return (~[this.KEY.SPACE, this.KEY.ESC, this.KEY.ENTER].indexOf(k)) || this.isHorizontalMovement(k) || this.isVerticalMovement(k);
\r
19756 isNumericKey: function (e) {
\r
19757 var k = e.keyCode;
\r
19758 if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105)) {
\r
19764 isAlphaNumericKey: function (e) {
\r
19765 var k = e.keyCode;
\r
19766 if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105) || (k >= 65 && k <= 90)) {
\r
19775 .factory('$isElement', [function () {
\r
19776 var isElement = function (currentElem, targetElem, alternateElem) {
\r
19777 if (currentElem[0] === targetElem[0]) {
\r
19779 } else if (currentElem[0] === alternateElem[0]) {
\r
19782 return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
\r
19786 return isElement;
\r
19789 .factory('events', function () {
\r
19790 var _stopPropagation = function (evt) {
\r
19791 if (evt.stopPropagation) {
\r
19792 evt.stopPropagation();
\r
19794 evt.returnValue = false;
\r
19797 var _preventDefault = function (evt) {
\r
19798 if (evt.preventDefault) {
\r
19799 evt.preventDefault();
\r
19801 evt.returnValue = false;
\r
19805 stopPropagation: _stopPropagation,
\r
19806 preventDefault: _preventDefault
\r
19811 .factory('$documentBind', ['$document', '$timeout', function ($document, $timeout) {
\r
19812 var _click = function (flag, callbackFunc, scope) {
\r
19813 scope.$watch(flag, function (val) {
\r
19814 $timeout(function () {
\r
19816 $document.bind('click', callbackFunc);
\r
19818 $document.unbind('click', callbackFunc);
\r
19824 var _scroll = function (flag, callbackFunc, scope) {
\r
19825 scope.$watch(flag, function (val) {
\r
19826 $timeout(function () {
\r
19828 $document.bind('scroll', callbackFunc);
\r
19830 $document.unbind('scroll', callbackFunc);
\r
19836 var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
\r
19837 if (timeoutFlag) {
\r
19838 if (!(timeoutValue)) {
\r
19839 timeoutValue = 0;
\r
19841 scope.$watch(flag, function (newVal, oldVal) {
\r
19842 if (newVal !== oldVal) {
\r
19843 $timeout(function () {
\r
19845 $document.bind(event, callbackFunc);
\r
19847 $document.unbind(event, callbackFunc);
\r
19849 }, timeoutValue);
\r
19853 scope.$watch(flag, function (newVal, oldVal) {
\r
19854 if (newVal !== oldVal) {
\r
19856 $document.bind(event, callbackFunc);
\r
19858 $document.unbind(event, callbackFunc);
\r
19872 .directive('b2bOnlyNums', function (keymap) {
\r
19875 require: 'ngModel',
\r
19876 link: function (scope, elm, attrs, ctrl) {
\r
19877 var maxChars = attrs.b2bOnlyNums ? attrs.b2bOnlyNums : 4;
\r
19878 elm.on('keydown', function (event) {
\r
19879 if ((event.which >= 48 && event.which <= 57) || (event.which >= 96 && event.which <= 105)) {
\r
19880 // check for maximum characters allowed
\r
19881 if (elm.val().length < maxChars){
\r
19884 event.preventDefault();
\r
19887 } else if ([8, 9, 13, 27, 37, 38, 39, 40].indexOf(event.which) > -1) {
\r
19888 // to allow backspace, tab, enter, escape, arrows
\r
19890 } else if (event.altKey || event.ctrlKey) {
\r
19891 // to allow alter, control, and shift keys
\r
19894 // to stop others
\r
19895 event.preventDefault();
\r
19900 var validateString = function (value) {
\r
19901 if (angular.isUndefined(value) || value === null || value === '') {
\r
19902 return ctrl.$modelValue;
\r
19906 ctrl.$parsers.unshift(validateString);
\r
19911 .directive('b2bKeyupClick', [ function () {
\r
19914 link: function (scope, elem, attr) {
\r
19915 var keyCode = [];
\r
19916 attr.$observe('b2bKeyupClick', function (value) {
\r
19918 keyCode = value.split(',');
\r
19921 elem.bind('keydown keyup', function (ev) {
\r
19922 var keyCodeCondition = function () {
\r
19923 var flag = false;
\r
19924 if (!(ev.keyCode)) {
\r
19926 ev.keyCode = ev.which;
\r
19927 } else if (ev.charCode) {
\r
19928 ev.keyCode = ev.charCode;
\r
19931 if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
\r
19936 if (ev.type === 'keydown' && keyCodeCondition()) {
\r
19937 ev.preventDefault();
\r
19939 else if (ev.type === 'keyup' && keyCodeCondition()) {
\r
19947 .factory('b2bDOMHelper', function() {
\r
19949 var _isTabable = function(node) {
\r
19950 var element = angular.element(node);
\r
19951 var tagName = element[0].tagName.toUpperCase();
\r
19953 if (isHidden(element)) {
\r
19956 if (element.attr('tabindex') !== undefined) {
\r
19957 return (parseInt(element.attr('tabindex'), 10) >= 0);
\r
19959 if (tagName === 'A' || tagName === 'AREA' || tagName === 'BUTTON' || tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
\r
19960 if (tagName === 'A' || tagName === 'AREA') {
\r
19961 // anchors/areas without href are not focusable
\r
19962 return (element[0].href !== '');
\r
19964 return !(element[0].disabled || element[0].readOnly);
\r
19969 function isValidChild(child) {
\r
19970 return child.nodeType == 1 && child.nodeName != 'SCRIPT' && child.nodeName != 'STYLE';
\r
19973 function isHidden(obj) {
\r
19974 var elem = angular.element(obj);
\r
19975 var elemStyle = undefined;
\r
19976 if(obj instanceof HTMLElement){
\r
19977 elemStyle = window.getComputedStyle(obj);
\r
19980 elemStyle = window.getComputedStyle(obj[0]);
\r
19982 return elem.hasClass('ng-hide') || elem.css('display') === 'none' || elemStyle.display === 'none' || elemStyle.visibility === 'hidden' || elem.css('visibility') === 'hidden';
\r
19985 function hasValidParent(obj) {
\r
19986 return (isValidChild(obj) && obj.parentElement.nodeName !== 'BODY');
\r
19989 function traverse(obj, fromTop) {
\r
19990 var obj = obj || document.getElementsByTagName('body')[0];
\r
19991 if (isValidChild(obj) && _isTabable(obj)) {
\r
19994 // If object is hidden, skip it's children
\r
19995 if (isValidChild(obj) && isHidden(obj)) {
\r
19996 return undefined;
\r
19998 // If object is hidden, skip it's children
\r
19999 if (angular.element(obj).hasClass('ng-hide')) {
\r
20000 return undefined;
\r
20002 if (obj.hasChildNodes()) {
\r
20005 child = obj.firstChild;
\r
20007 child = obj.lastChild;
\r
20010 var res = traverse(child, fromTop);
\r
20016 child = child.nextSibling;
\r
20018 child = child.previousSibling;
\r
20024 return undefined;
\r
20028 var _previousElement = function(el, isFocusable){
\r
20031 if (el.hasOwnProperty('length')) {
\r
20035 var parent = elem.parentElement;
\r
20036 var previousElem = undefined;
\r
20038 if(isFocusable) {
\r
20039 if (hasValidParent(elem)) {
\r
20040 var siblings = angular.element(parent).children();
\r
20041 if (siblings.length > 0) {
\r
20042 // Good practice to splice out the elem from siblings if there, saving some time.
\r
20043 // We allow for a quick check for jumping to parent first before removing.
\r
20044 if (siblings[0] === elem) {
\r
20045 // If we are looking at immidiate parent and elem is first child, we need to go higher
\r
20046 var e = _previousElement(angular.element(elem).parent(), isFocusable);
\r
20047 if (_isTabable(e)) {
\r
20051 // I need to filter myself and any nodes next to me from the siblings
\r
20052 var indexOfElem = Array.prototype.indexOf.call(siblings, elem);
\r
20053 siblings = Array.prototype.filter.call(siblings, function(item, itemIndex) {
\r
20054 if (!angular.equals(elem, item) && itemIndex < indexOfElem) {
\r
20059 // We need to search backwards
\r
20060 for (var i = 0; i <= siblings.length-1; i++) {//for (var i = siblings.length-1; i >= 0; i--) {
\r
20061 var ret = traverse(siblings[i], false);
\r
20062 if (ret !== undefined) {
\r
20067 var e = _previousElement(angular.element(elem).parent(), isFocusable);
\r
20068 if (_isTabable(e)) {
\r
20074 var siblings = angular.element(parent).children();
\r
20075 if (siblings.length > 1) {
\r
20076 // Since indexOf is on Array.prototype and parent.children is a NodeList, we have to use call()
\r
20077 var index = Array.prototype.indexOf.call(siblings, elem);
\r
20078 previousElem = siblings[index-1];
\r
20081 return previousElem;
\r
20084 var _lastTabableElement = function(el) {
\r
20085 /* This will return the first tabable element from the parent el */
\r
20087 if (el.hasOwnProperty('length')) {
\r
20091 return traverse(elem, false);
\r
20094 var _firstTabableElement = function(el) {
\r
20095 /* This will return the first tabable element from the parent el */
\r
20097 if (el.hasOwnProperty('length')) {
\r
20101 return traverse(elem, true);
\r
20104 var _isInDOM = function(obj) {
\r
20105 return document.documentElement.contains(obj);
\r
20109 firstTabableElement: _firstTabableElement,
\r
20110 lastTabableElement: _lastTabableElement,
\r
20111 previousElement: _previousElement,
\r
20112 isInDOM: _isInDOM,
\r
20113 isTabable: _isTabable,
\r
20114 isHidden: isHidden
\r
20118 .factory('trapFocusInElement', ['$document', '$isElement', 'DOMHelper', 'keymap', function ($document, $isElement, DOMHelper, keymap) {
\r
20119 var elementStack = [];
\r
20120 var stackHead = undefined;
\r
20121 var trapFocusInElement = function (flag) {
\r
20122 var bodyElements = $document.find('body').children();
\r
20124 var firstTabableElement = angular.element(DOMHelper.firstTabableElement(stackHead));
\r
20125 var lastTabableElement = angular.element(DOMHelper.lastTabableElement(stackHead));
\r
20127 var trapKeyboardFocusInFirstElement = function (e) {
\r
20128 if (!e.keyCode) {
\r
20129 e.keyCode = e.which;
\r
20132 if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
\r
20133 lastTabableElement[0].focus();
\r
20134 e.preventDefault(e);
\r
20135 e.stopPropagation(e);
\r
20140 var trapKeyboardFocusInLastElement = function (e) {
\r
20141 if (!e.keyCode) {
\r
20142 e.keyCode = e.which;
\r
20145 if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
\r
20146 firstTabableElement[0].focus();
\r
20147 e.preventDefault(e);
\r
20148 e.stopPropagation(e);
\r
20153 for (var i = 0; i < bodyElements.length; i++) {
\r
20154 if (bodyElements[i] !== stackHead[0]) {
\r
20155 bodyElements.eq(i).attr('aria-hidden', true);
\r
20158 firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
\r
20159 lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
\r
20161 for (var j = 0; j < bodyElements.length; j++) {
\r
20162 if (bodyElements[j] !== stackHead[0]) {
\r
20163 bodyElements.eq(j).removeAttr('aria-hidden');
\r
20166 firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
\r
20167 lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
\r
20170 var toggleTrapFocusInElement = function (flag, element) {
\r
20171 if (angular.isDefined(flag) && angular.isDefined(element)) {
\r
20172 if (angular.isUndefined(stackHead)) {
\r
20173 stackHead = element;
\r
20174 trapFocusInElement(flag);
\r
20177 trapFocusInElement(false);
\r
20178 elementStack.push(stackHead);
\r
20179 stackHead = element;
\r
20180 trapFocusInElement(true);
\r
20182 if (stackHead.prop('$$hashKey') === element.prop('$$hashKey')) {
\r
20183 trapFocusInElement(false);
\r
20184 stackHead = elementStack.pop();
\r
20185 if (angular.isDefined(stackHead)) {
\r
20186 trapFocusInElement(true);
\r
20194 return toggleTrapFocusInElement;
\r
20197 .factory('DOMHelper', function () {
\r
20199 var _isTabable = function (node) {
\r
20200 var element = angular.element(node);
\r
20201 var tagName = element[0].tagName.toUpperCase();
\r
20203 if (isHidden(element)) {
\r
20206 if (element.attr('tabindex') !== undefined) {
\r
20207 return (parseInt(element.attr('tabindex'), 10) >= 0);
\r
20209 if (tagName === 'A' || tagName === 'AREA' || tagName === 'BUTTON' || tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
\r
20210 if (tagName === 'A' || tagName === 'AREA') {
\r
20211 // anchors/areas without href are not focusable
\r
20212 return (element[0].href !== '');
\r
20214 return !(element[0].disabled || element[0].readOnly);
\r
20219 function isValidChild(child) {
\r
20220 return child.nodeType == 1 && child.nodeName != 'SCRIPT' && child.nodeName != 'STYLE';
\r
20223 function isHidden(obj) {
\r
20224 var elem = angular.element(obj);
\r
20227 style = window.getComputedStyle(obj);
\r
20230 style = window.getComputedStyle(obj[0]);
\r
20233 // getComputedStyle() for Zepto object returns null
\r
20234 if (style === null){
\r
20235 return elem.hasClass('ng-hide') || elem.css('display') === 'none';
\r
20238 return elem.hasClass('ng-hide') || elem.css('display') === 'none' || style.display === 'none' || style.display === 'hidden';
\r
20241 function traverse(obj, fromTop) {
\r
20242 var obj = obj || document.getElementsByTagName('body')[0];
\r
20244 if (isValidChild(obj) && _isTabable(obj)) {
\r
20248 // If object is hidden, skip it's children
\r
20249 if (isValidChild(obj) && isHidden(obj)) {
\r
20250 return undefined;
\r
20252 // If object is hidden, skip it's children
\r
20253 if (angular.element(obj).hasClass('ng-hide')) {
\r
20254 return undefined;
\r
20257 if (obj.hasChildNodes()) {
\r
20260 child = obj.firstChild;
\r
20262 child = obj.lastChild;
\r
20265 var res = traverse(child, fromTop);
\r
20270 child = child.nextSibling;
\r
20272 child = child.previousSibling;
\r
20277 return undefined;
\r
20281 var _lastTabableElement = function (el) {
\r
20282 /* This will return the last tabable element from the parent el */
\r
20284 if (el.hasOwnProperty('length')) {
\r
20288 return traverse(elem, false);
\r
20291 var _firstTabableElement = function (el) {
\r
20292 /* This will return the first tabable element from the parent el */
\r
20294 if (el.hasOwnProperty('length')) {
\r
20298 return traverse(elem, true);
\r
20302 firstTabableElement: _firstTabableElement,
\r
20303 lastTabableElement: _lastTabableElement,
\r
20304 isTabable: _isTabable
\r
20308 .factory('windowOrientation', ['$window', function ($window) {
\r
20309 var _isPotrait = function () {
\r
20310 if ($window.innerHeight > $window.innerWidth) {
\r
20316 var _isLandscape = function () {
\r
20317 if ($window.innerHeight < $window.innerWidth) {
\r
20325 isPotrait: _isPotrait,
\r
20326 isLandscape: _isLandscape
\r
20330 .directive('b2bNextElement', function() {
\r
20333 transclude: false,
\r
20334 link: function (scope, elem, attr, ctrls) {
\r
20336 var keys = attr.b2bNextElement.split(',');
\r
20338 elem.bind('keydown', function (e) {
\r
20339 var nextElement = elem.next();
\r
20340 if(e.keyCode == 39 || e.keyCode == 40){ // if e.keyCode in keys
\r
20341 if(nextElement.length) {
\r
20342 e.preventDefault();
\r
20343 nextElement[0].focus();
\r
20351 .directive('b2bAccessibilityClick', [function () {
\r
20354 link: function (scope, elem, attr, ctrl) {
\r
20355 var keyCode = [];
\r
20356 attr.$observe('b2bAccessibilityClick', function (value) {
\r
20358 keyCode = value.split(',');
\r
20361 elem.bind('keydown keypress', function (ev) {
\r
20362 var keyCodeCondition = function () {
\r
20363 var flag = false;
\r
20364 if (!(ev.keyCode)) {
\r
20366 ev.keyCode = ev.which;
\r
20367 } else if (ev.charCode) {
\r
20368 ev.keyCode = ev.charCode;
\r
20371 if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
\r
20376 if (keyCode.length > 0 && keyCodeCondition()) {
\r
20378 ev.preventDefault();
\r
20385 .directive('b2bReset', ['$compile', function ($compile) {
\r
20388 require: ['?ngModel', 'b2bReset'],
\r
20389 controller: ['$scope', function ($scope) {
\r
20390 var resetButton = angular.element('<button type="button" class="reset-field" tabindex="-1" aria-label="Click to reset" aria-hidden="true" role="button"></button>');
\r
20392 this.getResetButton = function () {
\r
20393 return resetButton;
\r
20396 link: function (scope, element, attrs, ctrls) {
\r
20398 var ngModelCtrl = ctrls[0];
\r
20399 var ctrl = ctrls[1];
\r
20401 var resetButton = ctrl.getResetButton();
\r
20404 resetButton.on('click', function () {
\r
20405 element[0].value = '';
\r
20407 if (ngModelCtrl) {
\r
20408 if (attrs.b2bReset) {
\r
20409 ngModelCtrl.$setViewValue(attrs.b2bReset);
\r
20411 ngModelCtrl.$setViewValue('');
\r
20413 element[0].value = ngModelCtrl.$viewValue;
\r
20414 ngModelCtrl.$render();
\r
20417 element[0].focus();
\r
20418 element[0].select();
\r
20421 var addResetButton = function () {
\r
20422 element.after(resetButton);
\r
20423 element.unbind('focus input', addResetButton);
\r
20426 element.bind('focus input', addResetButton);
\r
20431 .directive('b2bPrevElement', ['b2bDOMHelper', 'keymap', function (b2bDOMHelper, keymap) {
\r
20434 transclude: false,
\r
20435 link: function (scope, elem, attr) {
\r
20437 elem.bind('keydown', function (e) {
\r
20438 if(e.keyCode == 37 || e.keyCode == 38){
\r
20439 var prev = b2bDOMHelper.previousElement(elem, false);
\r
20440 if(prev !== undefined) {
\r
20441 e.preventDefault();
\r
20450 * @param {integer} delay - Timeout before first and last focusable elements are found
\r
20451 * @param {boolean} trigger - A variable on scope that will trigger refinding first/last focusable elements
\r
20453 .directive('b2bTrapFocusInsideElement', ['$timeout', 'b2bDOMHelper', 'keymap', 'events', function ($timeout, b2bDOMHelper, keymap, events) {
\r
20456 transclude: false,
\r
20457 link: function (scope, elem, attr) {
\r
20459 var delay = parseInt(attr.delay, 10) || 10;
\r
20461 /* Before opening modal, find the focused element */
\r
20462 var firstTabableElement = undefined,
\r
20463 lastTabableElement = undefined;
\r
20465 function init() {
\r
20466 $timeout(function () {
\r
20467 firstTabableElement = b2bDOMHelper.firstTabableElement(elem);
\r
20468 lastTabableElement = b2bDOMHelper.lastTabableElement(elem);
\r
20469 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
\r
20470 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
\r
20471 }, delay, false);
\r
20474 if (attr.trigger !== undefined) {
\r
20475 scope.$watch('trigger', function() {
\r
20476 if (scope.trigger) {
\r
20482 var firstTabableElementKeyhandler = function(e) {
\r
20483 if (!e.keyCode) {
\r
20484 e.keyCode = e.which;
\r
20486 if (e.keyCode === keymap.KEY.TAB && e.shiftKey) {
\r
20487 if (attr.trapFocusInsideElement === 'true') {
\r
20488 var temp = b2bDOMHelper.lastTabableElement(elem);
\r
20489 if (lastTabableElement !== temp) {
\r
20490 // Unbind keydown handler on lastTabableElement
\r
20491 angular.element(lastTabableElement).unbind('keydown', lastTabableElementKeyhandler);
\r
20492 lastTabableElement = temp;
\r
20493 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
\r
20496 lastTabableElement.focus();
\r
20497 events.preventDefault(e);
\r
20498 events.stopPropagation(e);
\r
20502 var lastTabableElementKeyhandler = function(e) {
\r
20503 if (!e.keyCode) {
\r
20504 e.keyCode = e.which;
\r
20506 if (e.keyCode === keymap.KEY.TAB && !e.shiftKey) {
\r
20507 if (attr.trapFocusInsideElement === 'true') {
\r
20508 var temp = b2bDOMHelper.firstTabableElement(elem);
\r
20509 if (firstTabableElement !== temp) {
\r
20510 // Unbind keydown handler on firstTabableElement
\r
20511 angular.element(firstTabableElement).unbind('keydown', firstTabableElementKeyhandler);
\r
20512 firstTabableElement = temp;
\r
20513 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
\r
20516 firstTabableElement.focus();
\r
20517 events.preventDefault(e);
\r
20518 events.stopPropagation(e);
\r
20527 .factory('trapFocusInElement', ['$document', '$isElement', 'DOMHelper', 'keymap', '$interval', function ($document, $isElement, DOMHelper, keymap, $interval) {
\r
20528 var elementStack = [];
\r
20529 var stackHead = undefined;
\r
20530 var stopInterval = undefined;
\r
20531 var intervalRequired = false;
\r
20532 var interval = 1000;
\r
20533 var firstTabableElement, lastTabableElement;
\r
20535 var trapKeyboardFocusInFirstElement = function (e) {
\r
20536 if (!e.keyCode) {
\r
20537 e.keyCode = e.which;
\r
20540 if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
\r
20541 lastTabableElement[0].focus();
\r
20542 e.preventDefault(e);
\r
20543 e.stopPropagation(e);
\r
20548 var trapKeyboardFocusInLastElement = function (e) {
\r
20549 if (!e.keyCode) {
\r
20550 e.keyCode = e.which;
\r
20553 if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
\r
20554 firstTabableElement[0].focus();
\r
20555 e.preventDefault(e);
\r
20556 e.stopPropagation(e);
\r
20560 var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
\r
20561 var bodyElements = $document.find('body').children();
\r
20563 firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(DOMHelper.firstTabableElement(stackHead));
\r
20564 lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(DOMHelper.lastTabableElement(stackHead));
\r
20567 for (var i = 0; i < bodyElements.length; i++) {
\r
20568 if (bodyElements[i] !== stackHead[0]) {
\r
20569 bodyElements.eq(i).attr('aria-hidden', true);
\r
20572 firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
\r
20573 lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
\r
20575 for (var j = 0; j < bodyElements.length; j++) {
\r
20576 if (bodyElements[j] !== stackHead[0]) {
\r
20577 bodyElements.eq(j).removeAttr('aria-hidden');
\r
20580 firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
\r
20581 lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
\r
20584 if (intervalRequired && flag) {
\r
20585 stopInterval = $interval(function () {
\r
20586 var firstTabableElementTemp = angular.element(DOMHelper.firstTabableElement(stackHead));
\r
20587 var lastTabableElementTemp = angular.element(DOMHelper.lastTabableElement(stackHead));
\r
20588 if (firstTabableElementTemp[0] !== firstTabableElement[0] || lastTabableElementTemp[0] !== lastTabableElement[0]) {
\r
20589 $interval.cancel(stopInterval);
\r
20590 stopInterval = undefined;
\r
20591 trapFocusInElement(false, firstTabableElement, lastTabableElement);
\r
20592 trapFocusInElement(true, firstTabableElementTemp, lastTabableElementTemp);
\r
20596 if (stopInterval) {
\r
20597 $interval.cancel(stopInterval);
\r
20598 stopInterval = undefined;
\r
20602 var toggleTrapFocusInElement = function (flag, element, intervalRequiredParam, intervalParam) {
\r
20603 intervalRequired = intervalRequiredParam ? intervalRequiredParam : intervalRequired;
\r
20604 interval = intervalParam ? intervalParam : interval;
\r
20605 if (angular.isDefined(flag) && angular.isDefined(element)) {
\r
20606 if (flag && angular.isUndefined(stackHead)) {
\r
20607 stackHead = element;
\r
20608 trapFocusInElement(flag);
\r
20611 trapFocusInElement(false);
\r
20612 elementStack.push(stackHead);
\r
20613 stackHead = element;
\r
20614 trapFocusInElement(true);
\r
20616 if (angular.isDefined(stackHead) && (stackHead[0] === element[0])) {
\r
20617 trapFocusInElement(false);
\r
20618 stackHead = elementStack.pop();
\r
20619 if (angular.isDefined(stackHead)) {
\r
20620 trapFocusInElement(true);
\r
20626 if (angular.isDefined(stackHead)) {
\r
20627 trapFocusInElement(false, firstTabableElement, lastTabableElement);
\r
20628 trapFocusInElement(true);
\r
20633 return toggleTrapFocusInElement;
\r
20636 .directive('b2bSetNextFocusOn', ['b2bDOMHelper', '$timeout', function(b2bDOMHelper, $timeout) {
\r
20640 link: function (scope, elem, attr) {
\r
20641 elem.bind('click', function(){
\r
20642 var firstFocusableElement = undefined;
\r
20643 var containerElem = undefined;
\r
20644 var containerArray = [];
\r
20645 var timeout = parseInt(attr.setNextFocusTimeout, 0) | 100;
\r
20646 var index = parseInt(attr.b2bSetNextFocusIndex, 0) | 0;
\r
20649 *Fix for IE7 and lower
\r
20650 *polyfill src: https://github.com/HubSpot/pace/issues/102
\r
20652 if (!document.querySelectorAll) {
\r
20653 document.querySelectorAll = function (selectors) {
\r
20654 var style = document.createElement('style'), elements = [], element;
\r
20655 document.documentElement.firstChild.appendChild(style);
\r
20656 document._qsa = [];
\r
20658 style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
\r
20659 window.scrollBy(0, 0);
\r
20660 style.parentNode.removeChild(style);
\r
20662 while (document._qsa.length) {
\r
20663 element = document._qsa.shift();
\r
20664 element.style.removeAttribute('x-qsa');
\r
20665 elements.push(element);
\r
20667 document._qsa = null;
\r
20672 if (attr.b2bSetNextFocusOn === '') {
\r
20675 containerArray = attr.b2bSetNextFocusOn.split(' ');
\r
20677 $timeout(function(){
\r
20679 do { // cycles thru containerArray until finds a match in DOM to set focus to
\r
20680 containerElem = document.querySelectorAll(containerArray[i])[index];
\r
20682 } while ( (!containerElem) && (i < containerArray.length) );
\r
20683 if(containerElem){
\r
20684 if (!angular.isDefined(firstFocusableElement)) {
\r
20685 firstFocusableElement = b2bDOMHelper.firstTabableElement(containerElem);
\r
20687 firstFocusableElement.focus();
\r
20689 }, timeout, false)
\r
20696 .directive('b2bInputAllow', [function() {
\r
20699 require: 'ngModel',
\r
20700 link: function (scope, elem, attr, ctrl) {
\r
20701 var regexExpression = null;
\r
20702 attr.$observe('b2bInputAllow', function (value) {
\r
20704 regexExpression = new RegExp(value);
\r
20707 var isValid = function(str) {
\r
20708 if (regexExpression !== null) {
\r
20709 return regexExpression.test(str);
\r
20713 elem.bind('keypress', function($event) {
\r
20714 var charcode = String.fromCharCode($event.which || $event.keyCode);
\r
20715 if (!isValid(charcode)) {
\r
20716 $event.preventDefault();
\r
20717 $event.stopPropagation();
\r
20720 elem.bind('input', function (evt) {
\r
20721 var inputString = ctrl.$viewValue;
\r
20722 if (isValid(inputString)) {
\r
20723 ctrl.$setViewValue(inputString);
\r
20732 .directive('b2bInputDeny', [function() {
\r
20735 require: 'ngModel',
\r
20736 link: function (scope, elem, attr, ctrl) {
\r
20737 var regexExpression = null;
\r
20738 attr.$observe('b2bInputDeny', function (value) {
\r
20740 regexExpression = new RegExp(value, 'g');
\r
20743 elem.bind('input', function () {
\r
20744 var inputString = ctrl.$viewValue && ctrl.$viewValue.replace(regexExpression, '');
\r
20745 if (inputString !== ctrl.$viewValue) {
\r
20746 ctrl.$setViewValue(inputString);
\r
20755 .directive('b2bDragonInput', [function() {
\r
20758 require: 'ngModel',
\r
20759 link: function (scope, elem, attr, ctrl) {
\r
20760 elem.on('focus keyup', function(){
\r
20761 elem.triggerHandler('change');
\r
20767 .directive('b2bKey', ['b2bUtilitiesConfig', '$timeout', 'keymap', function (b2bUtilitiesConfig, $timeout, keymap) {
\r
20770 controller: ['$scope', '$element', '$attrs', function ($scope, $element,attr) {
\r
20771 this.childElements = [];
\r
20772 this.disableNodes = {};
\r
20773 this.enableSearch = attr.enableSearch !== undefined ? true : b2bUtilitiesConfig.enableSearch;
\r
20774 this.circularTraversal = attr.circularTraversal !== undefined ? true : b2bUtilitiesConfig.circularTraversal;
\r
20775 this.counter = -1;
\r
20776 if (this.enableSearch) {
\r
20777 this.searchKeys = [];
\r
20779 var searchString = '';
\r
20781 var selfCtrl = this;
\r
20783 this.childElementsList = [];
\r
20785 this.b2bKeyID = "";
\r
20787 if (angular.isDefined(attr.b2bKey)) {
\r
20788 this.b2bKeyID = attr.b2bKey;
\r
20791 this.calculateChildElementsList = function () {
\r
20792 return $element[0].querySelectorAll("[b2b-key-item='" + this.b2bKeyID + "']:not([disabled])");
\r
20795 this.resetChildElementsList = function () {
\r
20796 return $timeout(function () {
\r
20797 selfCtrl.childElementsList = selfCtrl.calculateChildElementsList();
\r
20801 this.resetChildElementsList();
\r
20803 $scope.$on('b2b-key-reset-child-elements-list', function () {
\r
20804 selfCtrl.resetChildElementsList();
\r
20808 this.registerElement = function (childElement, searchKey) {
\r
20809 this.childElements.push(childElement);
\r
20810 if (this.enableSearch) {
\r
20811 this.searchKeys.push(searchKey);
\r
20813 var count = this.childElements.length - 1;
\r
20814 this.maxLength = count + 1;
\r
20817 this.toggleDisable = function (count, state) {
\r
20818 this.disableNodes[count] = state;
\r
20820 this.searchElement = function (searchExp) {
\r
20821 var regex = new RegExp("\\b" + searchExp, "gi");
\r
20822 var position = this.searchKeys.regexIndexOf(regex, this.counter + 1, true);
\r
20823 if (position > -1) {
\r
20824 this.counter = position;
\r
20825 this.moveFocus(this.counter);
\r
20828 this.startTimer = function (time) {
\r
20829 if (searchString === '') {
\r
20830 $timeout(function () {
\r
20831 searchString = '';
\r
20835 this.resetCounter = function (count) {
\r
20836 this.counter = count;
\r
20838 this.moveNext = function (count) {
\r
20839 this.counter = (this.counter + count) < this.maxLength ? this.counter + count : (this.circularTraversal ? 0 : this.counter);
\r
20840 if (this.disableNodes[this.counter]) {
\r
20841 if ((this.counter + count) < this.maxLength) {
\r
20842 this.moveNext(count);
\r
20845 this.moveFocus(this.counter);
\r
20848 this.movePrev = function (count) {
\r
20849 this.counter = (this.counter - count) > -1 ? this.counter - count : (this.circularTraversal ? this.maxLength-1 : this.counter);
\r
20850 if (this.disableNodes[this.counter]) {
\r
20851 if ((this.counter - count) > -1) {
\r
20852 this.movePrev(count);
\r
20855 this.moveFocus(this.counter);
\r
20858 this.moveFocus = function (index) {
\r
20859 this.childElements[index][0].focus();
\r
20862 this.keyDownHandler = function (ev, count) {
\r
20863 if (angular.isDefined(count) && !isNaN(count) && count !== this.counter) {
\r
20864 this.resetCounter(count);
\r
20866 if (!ev.keyCode) {
\r
20868 ev.keyCode = ev.which;
\r
20869 } else if (ev.charCode) {
\r
20870 ev.keyCode = ev.charCode;
\r
20873 if (ev.keyCode) {
\r
20874 if (this.prev && this.prev.indexOf(ev.keyCode.toString()) > -1) {
\r
20875 this.movePrev(1);
\r
20876 ev.preventDefault();
\r
20877 ev.stopPropagation();
\r
20878 } else if (this.next && this.next.indexOf(ev.keyCode.toString()) > -1) {
\r
20879 this.moveNext(1);
\r
20880 ev.preventDefault();
\r
20881 ev.stopPropagation();
\r
20882 } else if (this.up && this.up.indexOf(ev.keyCode.toString()) > -1) {
\r
20883 if (this.type === 'table') {
\r
20884 this.movePrev(this.columns);
\r
20885 ev.preventDefault();
\r
20886 ev.stopPropagation();
\r
20888 } else if (this.down && this.down.indexOf(ev.keyCode.toString()) > -1) {
\r
20889 if (this.type === 'table') {
\r
20890 this.moveNext(this.columns);
\r
20891 ev.preventDefault();
\r
20892 ev.stopPropagation();
\r
20894 } else if (ev.keyCode === keymap.KEY.HOME) {
\r
20895 var firstIndex = 0;
\r
20896 while (this.disableNodes[firstIndex] !== false) {
\r
20899 var count = this.counter - firstIndex;
\r
20900 this.movePrev(count);
\r
20901 ev.preventDefault();
\r
20902 ev.stopPropagation();
\r
20903 } else if (ev.keyCode === keymap.KEY.END) {
\r
20904 var lastIndex = this.childElements.length - 1;
\r
20905 while (this.disableNodes[lastIndex] !== false) {
\r
20908 var count = lastIndex - this.counter;
\r
20909 this.moveNext(count);
\r
20910 ev.preventDefault();
\r
20911 ev.stopPropagation();
\r
20912 } else if (ev.keyCode >= 48 && ev.keyCode <= 105) {
\r
20913 if (this.enableSearch) {
\r
20914 this.startTimer(b2bUtilitiesConfig.searchTimer);
\r
20915 searchString = searchString + (keymap.MAP[ev.keyCode] || '');
\r
20916 this.searchElement(searchString);
\r
20917 ev.preventDefault();
\r
20918 ev.stopPropagation();
\r
20924 link: function (scope, elem, attr, ctrl) {
\r
20925 ctrl.prev = attr.prev ? attr.prev.split(',') : b2bUtilitiesConfig.prev.split(',');
\r
20926 ctrl.next = attr.next ? attr.next.split(',') : b2bUtilitiesConfig.next.split(',');
\r
20927 ctrl.type = attr.type ? attr.type : b2bUtilitiesConfig.type;
\r
20928 if (ctrl.type === 'table') {
\r
20929 ctrl.up = attr.up ? attr.up.split(',') : b2bUtilitiesConfig.up.split(',');
\r
20930 ctrl.down = attr.down ? attr.down.split(',') : b2bUtilitiesConfig.down.split(',');
\r
20931 ctrl.columns = attr.columns ? parseInt(attr.columns, 10) : b2bUtilitiesConfig.columns;
\r
20934 elem.bind('keydown', function (ev) {
\r
20935 ctrl.keyDownHandler(ev);
\r
20941 .directive('b2bKeyItem', [function () {
\r
20944 link: function (scope, elem, attr, ctrl) {
\r
20945 var parentCtrl = (elem.parent() && elem.parent().controller('b2bKey')) || undefined;
\r
20946 if (angular.isDefined(parentCtrl)) {
\r
20947 var count = parentCtrl.registerElement(elem, attr.searchKey);
\r
20948 elem.bind('keydown', function (ev) {
\r
20949 parentCtrl.keyDownHandler(ev, count);
\r
20951 scope.$watch(attr.b2bKeyItem, function (value) {
\r
20952 value = value === undefined ? true : value;
\r
20953 parentCtrl.toggleDisable(count, !value);
\r
20955 scope.$on('$destroy', function () {
\r
20956 parentCtrl.toggleDisable(count, true);
\r
20963 .directive('b2bElementFocus', [function () {
\r
20966 link: function (scope, elem, attr, ctrl) {
\r
20967 scope.$watch(attr.b2bElementFocus, function (value) {
\r
20968 if (value === true) {
\r
20977 .directive('b2bAppendElement', ['$compile', function ($compile) {
\r
20980 link: function (scope, elem, attr, ctrl) {
\r
20981 var parameters = attr.b2bAppendElement.split(':');
\r
20982 if (parameters.length === 1) {
\r
20983 elem.append(scope.$eval(parameters[0]));
\r
20984 } else if (parameters.length === 2) {
\r
20985 if (parameters[1] === 'compile') {
\r
20986 var element = angular.element('<span>' + scope.$eval(parameters[0]) + '</span>');
\r
20987 elem.append($compile(element)(scope));
\r
20995 .directive('b2bKeyItemRefreshInNgRepeat', [function () {
\r
20998 require: '^^b2bKey',
\r
20999 link: function (scope, elem, attr, parentCtrl) {
\r
21000 if (angular.isDefined(parentCtrl)) {
\r
21002 var attrToObserve = 'attrToObserve';
\r
21004 if (attr.b2bKeyItemRefreshInNgRepeat) {
\r
21005 attrToObserve = 'b2bKeyItemRefreshInNgRepeat';
\r
21008 attr.$observe(attrToObserve, function (newVal, oldVal) {
\r
21009 if (newVal && newVal !== oldVal) {
\r
21010 parentCtrl.resetChildElementsList();
\r
21018 .filter('b2bMultiSepartorHighlight', function($sce) {
\r
21019 return function(text, searchText, searchSeperator) {
\r
21020 var splitText = function(string) {
\r
21021 if(angular.isDefined(searchSeperator)){
\r
21022 if (string.indexOf(searchSeperator) > -1) {
\r
21023 return string.split(searchSeperator);
\r
21032 var newText = splitText(text);
\r
21033 var newPhrase = splitText(searchText);
\r
21034 if (angular.isArray(newPhrase)) {
\r
21035 for (var i = 0; i < newText.length; i++) {
\r
21037 text = newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
\r
21038 '<span class="b2b-search-hightlight">$1</span>');
\r
21040 text = text + searchSeperator + ' ' + (newPhrase[i] ? newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
\r
21041 '<span class="b2b-search-hightlight">$1</span>') : newText[i]);
\r
21045 text = text.replace(new RegExp('(' + searchText + ')', 'gi'),
\r
21046 '<span class="b2b-search-hightlight">$1</span>');
\r
21049 return $sce.trustAsHtml(text)
\r
21053 .factory('b2bUserAgent', [function() {
\r
21054 var _isMobile = function() {
\r
21055 return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
\r
21057 var _notMobile = function() {
\r
21058 return !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
\r
21060 var _isIE = function() {
\r
21061 return /msie|trident/i.test(navigator.userAgent);
\r
21063 var _isFF = function() {
\r
21064 return /Firefox/.test(navigator.userAgent);
\r
21066 var _isChrome = function() {
\r
21067 return /Google Inc/.test(navigator.vendor);
\r
21069 var _isSafari = function() {
\r
21070 return /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
\r
21074 isMobile: _isMobile,
\r
21075 notMobile: _notMobile,
\r
21078 isChrome: _isChrome,
\r
21079 isSafari: _isSafari
\r
21082 .run(['$document', 'b2bUserAgent', function($document, b2bUserAgent) {
\r
21083 var html = $document.find('html').eq(0);
\r
21084 if (b2bUserAgent.isIE()) {
\r
21085 html.addClass('isIE');
\r
21087 html.removeClass('isIE');
\r
21093 String.prototype.toSnakeCase = function () {
\r
21094 return this.replace(/([A-Z])/g, function ($1) {
\r
21095 return "-" + $1.toLowerCase();
\r
21098 var concat = function (character, times) {
\r
21099 character = character || '';
\r
21100 times = (!isNaN(times) && times) || 0;
\r
21101 var finalChar = '';
\r
21102 for (var i = 0; i < times; i++) {
\r
21103 finalChar += character;
\r
21105 return finalChar;
\r
21108 // direction: true for left and false for right
\r
21109 var pad = function (actualString, width, character, direction) {
\r
21110 actualString = actualString || '';
\r
21111 width = (!isNaN(width) && width) || 0;
\r
21112 character = character || '';
\r
21113 if (width > actualString.length) {
\r
21115 return concat(character, (width - actualString.length)) + actualString;
\r
21117 return actualString + concat(character, (width - actualString.length));
\r
21120 return actualString;
\r
21123 String.prototype.lPad = function (width, character) {
\r
21124 return pad(this, width, character, true);
\r
21127 String.prototype.rPad = function (width, character) {
\r
21128 return pad(this, width, character, false);
\r
21131 if (!Array.prototype.indexOf) {
\r
21132 Array.prototype.indexOf = function (val) {
\r
21133 for (var index = 0; index < this.length; index++) {
\r
21134 if (this[index] === val) {
\r
21142 if (!Array.prototype.regexIndexOf) {
\r
21143 Object.defineProperty(Array.prototype, 'regexIndexOf', {
\r
21144 enumerable: false,
\r
21145 value: function (regex, startIndex, loop) {
\r
21146 startIndex = startIndex && startIndex > -1 ? startIndex : 0;
\r
21147 for (var index = startIndex; index < this.length; index++) {
\r
21148 if (this[index].toString().match(regex)) {
\r
21153 for (var index = 0; index < startIndex; index++) {
\r
21154 if (this[index].toString().match(regex)) {
\r
21164 angular.module("b2bTemplate/audioPlayer/audioPlayer.html", []).run(["$templateCache", function($templateCache) {
\r
21165 $templateCache.put("b2bTemplate/audioPlayer/audioPlayer.html",
\r
21166 "<div class=\"b2b-audio\">\n" +
\r
21167 " <audio preload=\"auto\">\n" +
\r
21168 " <source ng-src=\"{{audio.mp3 | trustedAudioUrl}}\" type=\"audio/mp3\"></source>\n" +
\r
21169 " <i>Your browser does not support the audio element.</i>\n" +
\r
21172 " <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
21173 " <i class=\"icoControls-pointer\" ng-show='!isPlayInProgress'></i>\n" +
\r
21174 " <i class=\"icoControls-pause\" ng-show='isPlayInProgress'></i>\n" +
\r
21177 " <div class=\"seek-bar-container-wrapper\">\n" +
\r
21178 " <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
21179 " <div class=\"timing-container\">\n" +
\r
21180 " <span class=\"timing-container-left\">{{timeFormatter(audio.currentTime)}}</span>\n" +
\r
21181 " <span class=\"timing-container-right\">{{timeFormatter(audio.duration)}}</span>\n" +
\r
21182 " <div class=\"timing-container-spacer\"></div>\n" +
\r
21186 " <b2b-flyout>\n" +
\r
21187 " <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
21188 " <i class=\"icoControls-mutespeakers\" ng-show=\"audio.currentVolume === 0\"></i>\n" +
\r
21189 " <i class=\"icoControls-volumedown\" ng-show=\"audio.currentVolume > 0 && audio.currentVolume <= 50\"></i>\n" +
\r
21190 " <i class=\"icoControls-volumeup\" ng-show=\"audio.currentVolume > 50\"></i>\n" +
\r
21193 " <b2b-flyout-content horizontal-placement=\"center\" flyout-style=\"width:70px; height:190px;\" vertical-placement=\"above\">\n" +
\r
21194 " <div class=\"b2b-audio-popover text-center\">\n" +
\r
21195 " <span>Max</span>\n" +
\r
21196 " <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
21197 " <div class=\"min-label\">Min</div>\n" +
\r
21199 " </b2b-flyout-content>\n" +
\r
21200 " </b2b-flyout>\n" +
\r
21204 angular.module("b2bTemplate/audioRecorder/audioRecorder.html", []).run(["$templateCache", function($templateCache) {
\r
21205 $templateCache.put("b2bTemplate/audioRecorder/audioRecorder.html",
\r
21206 "<div class=\"b2b-audio-recorder row\">\n" +
\r
21207 " <div class=\"b2b-elapsed-time span11\">\n" +
\r
21208 " <div ng-if=\"isRecording\">\n" +
\r
21209 " <span style=\"padding-right: 25px;\">{{config.whileRecordingMessage}}</span>\n" +
\r
21210 " <span>{{timeFormatter(elapsedTime)}}</span>\n" +
\r
21212 " <span ng-if=\"!isRecording\">{{config.startRecordingMessage}}</span>\n" +
\r
21214 " <div class=\"b2b-controls\" title=\"{{isRecording ? 'Stop' : 'REC'}}\" b2b-accessibility-click=\"13,32\" ng-click=\"toggleRecording()\" role=\"button\">\n" +
\r
21215 " <i ng-if=\"isRecording\" class=\"icoControls-stop\" ></i>\n" +
\r
21216 " <i ng-if=\"!isRecording\" class=\"icoControls-record\"></i>\n" +
\r
21221 angular.module("b2bTemplate/backToTop/backToTop.html", []).run(["$templateCache", function($templateCache) {
\r
21222 $templateCache.put("b2bTemplate/backToTop/backToTop.html",
\r
21223 "<button class=\"btn-arrow b2b-backtotop-button\" type=\"button\" aria-label=\"Back to top\">\n" +
\r
21224 " <div class=\"btn-secondary b2b-top-btn\">\n" +
\r
21225 " <i class=\"icoControls-upPRIMARY\" role=\"img\"></i>\n" +
\r
21231 angular.module("b2bTemplate/boardstrip/b2bAddBoard.html", []).run(["$templateCache", function($templateCache) {
\r
21232 $templateCache.put("b2bTemplate/boardstrip/b2bAddBoard.html",
\r
21233 "<div tabindex=\"0\" role=\"menuitem\" b2b-accessibility-click=\"13,32\" ng-click=\"addBoard()\" aria-label=\"Add Board\" class=\"boardstrip-item--add\">\n" +
\r
21234 " <div class=\"centered\"><i aria-hidden=\"true\" class=\"icoControls-add-maximize\"></i> Add board</div>\n" +
\r
21238 angular.module("b2bTemplate/boardstrip/b2bBoard.html", []).run(["$templateCache", function($templateCache) {
\r
21239 $templateCache.put("b2bTemplate/boardstrip/b2bBoard.html",
\r
21240 "<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
21241 " <div ng-transclude></div>\n" +
\r
21242 " <div class=\"board-caret\" ng-if=\"getCurrentIndex()===boardIndex\">\n" +
\r
21243 " <div class=\"board-caret-indicator\"></div>\n" +
\r
21244 " <div class=\"board-caret-arrow-up\"></div>\n" +
\r
21249 angular.module("b2bTemplate/boardstrip/b2bBoardstrip.html", []).run(["$templateCache", function($templateCache) {
\r
21250 $templateCache.put("b2bTemplate/boardstrip/b2bBoardstrip.html",
\r
21251 "<div class=\"b2b-boardstrip\">\n" +
\r
21252 " <div class=\"boardstrip-reel\" role=\"menu\">\n" +
\r
21253 " <div class=\"prev-items\">\n" +
\r
21254 " <!-- <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
21255 " <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"prevBoard()\" ng-disabled=\"!isPrevBoard()\">\n" +
\r
21256 " <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-left\"></i>\n" +
\r
21258 " <span class=\"offscreen-text\">Previous boards</span>\n" +
\r
21261 " <div b2b-add-board on-add-board=\"onAddBoard()\"></div>\n" +
\r
21262 " <div class=\"board-viewport\"><ul role=\"menu\" class=\"boardstrip-container\" ng-transclude></ul></div>\n" +
\r
21263 " <div class=\"next-items\">\n" +
\r
21264 " <!-- <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
21265 " <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"nextBoard()\" ng-disabled=\"!isNextBoard()\">\n" +
\r
21266 " <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-right\"></i>\n" +
\r
21268 " <span class=\"offscreen-text\">Next boards</span>\n" +
\r
21276 angular.module("b2bTemplate/calendar/datepicker-popup.html", []).run(["$templateCache", function($templateCache) {
\r
21277 $templateCache.put("b2bTemplate/calendar/datepicker-popup.html",
\r
21278 "<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
21279 " <div class=\"datepicker-days\" style=\"display: block;\">\n" +
\r
21280 " <div ng-repeat=\"header in headers\" class=\"text-left\" style=\"width: 100%;\" b2b-append-element=\"header\"></div>\n" +
\r
21281 " <table class=\"table-condensed\">\n" +
\r
21284 " <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
21285 " <th id=\"month\" tabindex=\"-1\" aria-label=\"{{title}}\" class=\"datepicker-switch\" colspan=\"{{rows[0].length - 2}}\">{{title}}</th>\n" +
\r
21286 " <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
21288 " <tr ng-show=\"labels.length > 0\">\n" +
\r
21289 " <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
\r
21293 " <tr ng-repeat=\"row in rows\">\n" +
\r
21294 " <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
21295 " <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
21299 " <tr ng-repeat=\"footer in footers\">\n" +
\r
21300 " <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"footer\"></th>\n" +
\r
21308 angular.module("b2bTemplate/calendar/datepicker.html", []).run(["$templateCache", function($templateCache) {
\r
21309 $templateCache.put("b2bTemplate/calendar/datepicker.html",
\r
21311 " <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
\r
21315 angular.module("b2bTemplate/coachmark/coachmark.html", []).run(["$templateCache", function($templateCache) {
\r
21316 $templateCache.put("b2bTemplate/coachmark/coachmark.html",
\r
21317 "<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
21318 " <i class=\"b2b-coachmark-caret\"></i>\n" +
\r
21319 " <div class=\"b2b-coachmark-header\">\n" +
\r
21320 " <div class=\"b2b-coachmark-countlabel\"><span ng-if=\"coachmarkIndex !== 0\">{{coachmarkIndex}} of {{(coachmarks.length-1)}}<span></div>\n" +
\r
21321 " <div class=\"corner-button\">\n" +
\r
21322 " <button type=\"button\" ng-focus=\"closeButtonFocus()\" class=\"close\" title=\"close\" aria-label=\"Close\" ng-click=\"closeCoachmark()\"></button>\n" +
\r
21325 " <div class=\"b2b-coachmark-content\"> \n" +
\r
21326 " <i class=\"icon-misc-dimmer\"></i>\n" +
\r
21327 " <div class=\"b2b-coachmark-content-header\"><span class=\"offscreen-text\">{{currentCoachmark.offscreenText}}</span>{{currentCoachmark.contentHeader}}</div>\n" +
\r
21328 " <div class=\"b2b-coachmark-description\">{{currentCoachmark.content}}</div>\n" +
\r
21329 " <div class=\"b2b-coachmark-btn-group\">\n" +
\r
21330 " <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
21331 " <button class=\"btn btn-alt\" ng-if=\"currentCoachmark.buttonLabel !== '' && currentCoachmark.buttonLabel !== undefined\" ng-click=\"actionCoachmark(currentCoachmark.buttonLabel)\">{{currentCoachmark.buttonLabel}}</button>\n" +
\r
21337 angular.module("b2bTemplate/dropdowns/b2bDropdownDesktop.html", []).run(["$templateCache", function($templateCache) {
\r
21338 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownDesktop.html",
\r
21339 "<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
21340 " <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
21341 " <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
21342 " <div ng-class=\"{'selectWrapper': (isInputDropdown), 'moduleWrapper': (!isInputDropdown)}\">\n" +
\r
21343 " <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
21344 " <ul class=\"module-optinalcta\" ng-if=\"toggleFlag && optionalCta\" tabindex=\"-1\" aria-hidden=\"false\">\n" +
\r
21345 " <li class=\"module-list-item\" tabindex=\"-1\" role=\"menuitem\" value=\"\" aria-label=\"Optinal CTA\" aria-selected=\"true\">\n" +
\r
21346 " <span class=\"module-data\" b2b-append-element=\"optionalCta\"></span>\n" +
\r
21350 "<i class=\"icon-primary-down\" aria-hidden=\"true\"></i>\n" +
\r
21354 angular.module("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html", []).run(["$templateCache", function($templateCache) {
\r
21355 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html",
\r
21356 "<li b2b-dropdown-group-desktop class=\"optgroup-wrapper\">{{groupHeader}}\n" +
\r
21357 " <ul class=\"optgroup\" role=\"group\" aria-label=\"{{groupHeader}}\"></ul>\n" +
\r
21361 angular.module("b2bTemplate/dropdowns/b2bDropdownListDesktop.html", []).run(["$templateCache", function($templateCache) {
\r
21362 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownListDesktop.html",
\r
21363 "<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
21366 angular.module("b2bTemplate/fileUpload/fileUpload.html", []).run(["$templateCache", function($templateCache) {
\r
21367 $templateCache.put("b2bTemplate/fileUpload/fileUpload.html",
\r
21368 "<label class=\"b2b-file-container\">\n" +
\r
21369 " <span class=\"b2b-upload-link\" ng-transclude></span>\n" +
\r
21370 " <input type=\"file\" b2b-file-change>\n" +
\r
21374 angular.module("b2bTemplate/flyout/flyout.html", []).run(["$templateCache", function($templateCache) {
\r
21375 $templateCache.put("b2bTemplate/flyout/flyout.html",
\r
21376 "<span class=\"b2b-flyout\" b2b-flyout-trap-focus-inside>\n" +
\r
21377 " <span ng-transclude></span>\n" +
\r
21381 angular.module("b2bTemplate/flyout/flyoutContent.html", []).run(["$templateCache", function($templateCache) {
\r
21382 $templateCache.put("b2bTemplate/flyout/flyoutContent.html",
\r
21383 "<div class=\"b2b-flyout-container\" aria-live=\"polite\" aria-atomic=\"false\" ng-class=\"{'b2b-flyout-left':horizontalPlacement==='left',\n" +
\r
21384 " 'b2b-flyout-center':horizontalPlacement==='center', \n" +
\r
21385 " 'b2b-flyout-right':horizontalPlacement==='right',\n" +
\r
21386 " 'b2b-flyout-centerLeft':horizontalPlacement==='centerLeft',\n" +
\r
21387 " 'b2b-flyout-centerRight':horizontalPlacement==='centerRight', \n" +
\r
21388 " 'b2b-flyout-above':verticalPlacement==='above', \n" +
\r
21389 " 'b2b-flyout-below':verticalPlacement==='below',\n" +
\r
21390 " 'open-flyout': openFlyout,\n" +
\r
21391 " 'b2b-close-flyout': !openFlyout}\">\n" +
\r
21392 " <i class=\"b2b-flyout-caret\" ng-class=\"{'open-flyout': openFlyout, \n" +
\r
21393 " 'b2b-flyout-caret-above':verticalPlacement==='above',\n" +
\r
21394 " 'b2b-flyout-caret-below':verticalPlacement==='below'}\"></i>\n" +
\r
21395 " <span ng-transclude></span>\n" +
\r
21399 angular.module("b2bTemplate/footer/footer_column_switch_tpl.html", []).run(["$templateCache", function($templateCache) {
\r
21400 $templateCache.put("b2bTemplate/footer/footer_column_switch_tpl.html",
\r
21401 "<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
21402 " <h3 class=\"b2b-footer-header\">{{item.categoryName}}</h3>\n" +
\r
21404 " <li ng-repeat=\"i in item.values\">\n" +
\r
21405 " <a href=\"{{i.href}}\" title=\"{{item.categoryName}} - {{i.displayLink}}\">{{i.displayLink}}</a> \n" +
\r
21411 "<div ng-transclude></div>\n" +
\r
21415 angular.module("b2bTemplate/horizontalTable/horizontalTable.html", []).run(["$templateCache", function($templateCache) {
\r
21416 $templateCache.put("b2bTemplate/horizontalTable/horizontalTable.html",
\r
21417 "<div class=\"b2b-horizontal-table\">\n" +
\r
21418 " <div class=\"b2b-horizontal-table-arrows row span12\">\n" +
\r
21419 " <div class=\"span4\">\n" +
\r
21420 " <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
21423 " <span class=\"span5 b2b-horizontal-table-column-info\" aria-live=\"polite\" tabindex=\"-1\">\n" +
\r
21424 " Displaying {{getColumnSet()[0]}} - {{getColumnSet()[1]}} of {{numOfCols}} (total) columns\n" +
\r
21427 " <div class=\"span3\">\n" +
\r
21428 " <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
21431 " <div class=\"b2b-horizontal-table-inner-container\">\n" +
\r
21432 " <span ng-transclude></span>\n" +
\r
21439 angular.module("b2bTemplate/hourPicker/b2bHourpicker.html", []).run(["$templateCache", function($templateCache) {
\r
21440 $templateCache.put("b2bTemplate/hourPicker/b2bHourpicker.html",
\r
21441 "<div class=\"hp-container\">\n" +
\r
21442 " <div class=\"hp-selected\">\n" +
\r
21443 " <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
21445 " <div b2b-hourpicker-panel></div>\n" +
\r
21449 angular.module("b2bTemplate/hourPicker/b2bHourpickerPanel.html", []).run(["$templateCache", function($templateCache) {
\r
21450 $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerPanel.html",
\r
21451 "<form name=\"{{'hourpickerForm' + $id}}\">\n" +
\r
21452 " <div class=\"hp-checkbox\" role=\"group\">\n" +
\r
21453 " <label class=\"checkbox\" for=\"checkbox_{{dayOption.title}}_{{$id}}\" aria-label=\"{{dayOption.title}}\" ng-repeat=\"dayOption in hourpicker.dayOptions\">\n" +
\r
21454 " <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
21457 " <div class=\"row hp-dropdowns\">\n" +
\r
21458 " <div class=\"span4\">\n" +
\r
21459 " <label for=\"{{'hourpickerStartTime' + $id}}\">From</label>\n" +
\r
21460 " <select id=\"{{'hourpickerStartTime' + $parent.$id}}\" b2b-dropdown type=\"\" ng-model=\"hourpickerPanelValue.startTime\">\n" +
\r
21461 " <option b2b-dropdown-list value=\"\">From</option>\n" +
\r
21462 " <option b2b-dropdown-list option-repeat=\"startTimeOption in hourpicker.startTimeOptions\" value=\"{{startTimeOption}}\">{{startTimeOption}}</option>\n" +
\r
21465 " <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
\r
21466 " <label class=\"radio\" for=\"hourpickerStartMeridiem_AM_{{$id}}\">\n" +
\r
21467 " <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
21469 " <label class=\"radio\" for=\"hourpickerStartMeridiem_PM_{{$id}}\">\n" +
\r
21470 " <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
21474 " <div class=\"row hp-dropdowns\">\n" +
\r
21475 " <div class=\"span4\">\n" +
\r
21476 " <label for=\"{{'hourpickerEndTime' + $id}}\">To</label>\n" +
\r
21477 " <select id=\"{{'hourpickerEndTime' + $parent.$id}}\" b2b-dropdown ng-model=\"hourpickerPanelValue.endTime\">\n" +
\r
21478 " <option b2b-dropdown-list value=\"\">To</option>\n" +
\r
21479 " <option b2b-dropdown-list option-repeat=\"endTimeOption in hourpicker.endTimeOptions\" value=\"{{endTimeOption}}\">{{endTimeOption}}</option>\n" +
\r
21482 " <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
\r
21483 " <label class=\"radio\" for=\"hourpickerEndMeridiem_AM_{{$id}}\">\n" +
\r
21484 " <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
21486 " <label class=\"radio\" for=\"hourpickerEndMeridiem_PM_{{$id}}\">\n" +
\r
21487 " <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
21491 " <div class=\"row hp-buttons\">\n" +
\r
21492 " <div class=\"span12\">\n" +
\r
21493 " <div style=\"float:right\">\n" +
\r
21494 " <button class=\"btn btn-secondary btn-small\" ng-click=\"resetHourpickerPanelValue()\">Clear</button>\n" +
\r
21495 " <button class=\"btn btn-alt btn-small\" ng-disabled = \"disableAddBtn\" ng-click=\"updateHourpickerValue()\">{{hourpicker.editMode > -1 ? 'Update' : 'Add'}}</button>\n" +
\r
21502 angular.module("b2bTemplate/hourPicker/b2bHourpickerValue.html", []).run(["$templateCache", function($templateCache) {
\r
21503 $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerValue.html",
\r
21504 "<div class=\"selected-days\">\n" +
\r
21505 " <span class=\"day\">{{hourpickerValue.days}} {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}</span>\n" +
\r
21506 " <span style=\"float:right\">\n" +
\r
21507 " <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
21508 " <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
21510 " <div style=\"clear:both\"></div>\n" +
\r
21514 angular.module("b2bTemplate/leftNavigation/leftNavigation.html", []).run(["$templateCache", function($templateCache) {
\r
21515 $templateCache.put("b2bTemplate/leftNavigation/leftNavigation.html",
\r
21516 "<div class=\"b2b-nav-menu\" ng-init=\"(showmenu = true)\" ng-class=\"{false: 'left-menu-collapsed'}[showmenu]\">\n" +
\r
21517 " <div class=\"b2b-subnav-container\">\n" +
\r
21518 " <ul class=\"b2b-subnav-content\">\n" +
\r
21520 " <div ng-class=\"{true: 'leftmenu-arrow-expand', false: 'leftmenu-arrow-collapse'}[showmenu]\">"+
\r
21521 " <a ng-click=\"toggleDrawer(showmenu);(showmenu = !showmenu) \" class=\"text-right\">" +
\r
21522 " <i ng-class=\"{true: 'icon-controls-left', false: 'icon-controls-right'}[showmenu]\"> </i></a>" +
\r
21525 " <li ng-repeat=\"menu in menuData\" ui-sref=\"{{menu.state}}\">" +
\r
21526 " <span ng-class=\"{true: 'menu-icon', false: 'menu-icon-collapse'}[showmenu]\"><span class=\"{{menu.imageSrc}}\"></span> </span>" +
\r
21527 " <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
21528 " {{menu.name}}" +
\r
21529 " <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
21531 " <div role=\"region\" aria-hidden=\"{{(isOpen($index))?false:true;}}\">\n" +
\r
21532 " <ul ng-class=\"{expand: idx==$index}\">\n" +
\r
21533 " <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
21542 angular.module("b2bTemplate/listbox/listbox.html", []).run(["$templateCache", function($templateCache) {
\r
21543 $templateCache.put("b2bTemplate/listbox/listbox.html",
\r
21544 "<div class=\"b2b-list-box\" tabindex=\"0\" role=\"listbox\" ng-transclude>\n" +
\r
21548 angular.module("b2bTemplate/modalsAndAlerts/b2b-backdrop.html", []).run(["$templateCache", function($templateCache) {
\r
21549 $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-backdrop.html",
\r
21550 "<div class=\"b2b-modal-backdrop fade in hidden-by-modal\" aria-hidden=\"true\" tabindex=\"-1\"></div>");
\r
21553 angular.module("b2bTemplate/modalsAndAlerts/b2b-window.html", []).run(["$templateCache", function($templateCache) {
\r
21554 $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-window.html",
\r
21555 "<div class=\"modalwrapper active {{windowClass}}\" ng-class=\"{'modal-landscape': isModalLandscape}\" role=\"{{isNotifDialog?'alertdialog':'dialog'}}\" tabindex=\"-1\" aria-labelledby=\"{{title}}\" aria-describedby=\"{{content}}\">\n" +
\r
21556 " <div class=\"modal fade in {{sizeClass}}\" ng-transclude></div>\n" +
\r
21560 angular.module("b2bTemplate/monthSelector/monthSelector-popup.html", []).run(["$templateCache", function($templateCache) {
\r
21561 $templateCache.put("b2bTemplate/monthSelector/monthSelector-popup.html",
\r
21562 "<div id=\"monthselector{{$id}}\" class=\"datepicker dropdown-menu monthselector\" \n" +
\r
21563 " ng-class=\"{'datepicker-dropdown datepicker-orient-top': !inline, 'datepicker-orient-left': !inline && orientation === 'left', 'datepicker-orient-right': !inline && orientation === 'right'}\" \n" +
\r
21564 " ng-style=\"{position: inline && 'relative', 'z-index': inline && '0', top: !inline && position.top + 'px' || 0, left: !inline && position.left + 'px'}\" \n" +
\r
21565 " style=\"display: block;\" aria-hidden=\"false\" role=\"dialog presentation\" tabindex=\"-1\">\n" +
\r
21566 " <div class=\"datepicker-days\" style=\"display: block;\">\n" +
\r
21567 " <span class=\"offscreen-text\" aria-live=\"polite\" aria-atomic=\"true\">{{title}} displaying</span>\n" +
\r
21568 " <table class=\"table-condensed\" role=\"grid\">\n" +
\r
21570 " <tr ng-repeat=\"header in headers\">\n" +
\r
21571 " <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"header\"></th>\n" +
\r
21574 " <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
21575 " <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
21576 " <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
21578 " <tr ng-show=\"labels.length > 0\">\n" +
\r
21579 " <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
\r
21582 " <tbody b2b-key type=\"table\" columns=\"4\" >\n" +
\r
21583 " <tr ng-repeat=\"row in rows\">\n" +
\r
21584 " <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
21585 " <div aria-hidden=\"true\" tabindex=\"-1\" class=\"show-date\" ng-if=\"!(dt.oldMonth || dt.nextMonth)\">{{dt.label | limitTo: 3}}</div>\n" +
\r
21590 " <tr ng-repeat=\"footer in footers\">\n" +
\r
21591 " <th colspan=\"7\" class=\"text-left\" b2b-append-element=\"footer\"></th>\n" +
\r
21599 angular.module("b2bTemplate/monthSelector/monthSelector.html", []).run(["$templateCache", function($templateCache) {
\r
21600 $templateCache.put("b2bTemplate/monthSelector/monthSelector.html",
\r
21602 " <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
\r
21606 angular.module("b2bTemplate/monthSelector/monthSelectorLink.html", []).run(["$templateCache", function($templateCache) {
\r
21607 $templateCache.put("b2bTemplate/monthSelector/monthSelectorLink.html",
\r
21609 " <span class=\"span12\" ng-transclude></span>\n" +
\r
21613 angular.module("b2bTemplate/pagination/b2b-pagination.html", []).run(["$templateCache", function($templateCache) {
\r
21614 $templateCache.put("b2bTemplate/pagination/b2b-pagination.html",
\r
21615 "<div class=\"b2b-pager\">\n" +
\r
21616 " <div ng-if=\"notMobile && totalPages > 1\">\n" +
\r
21617 " <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
21618 " <i class=\"icon-primary-left\"></i>\n" +
\r
21620 " <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
21621 " 1<span class=\"offscreen-text\" ng-if=\"currentPage === 1\"> is selected</span>\n" +
\r
21623 " <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
21625 " <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage > 6\">...</span>\n" +
\r
21627 " <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
21629 " <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage <= totalPages - 5\">...</span>\n" +
\r
21631 " <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
21633 " <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
21635 " <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
21636 " <i class=\"icon-primary-right\"></i>\n" +
\r
21639 " <span class=\"fieldLabel\" ng-show=\"totalPages > 10 && showInput === true\"> \n" +
\r
21640 " <label for=\"{{inputId}}\">Go to Page:</label>\n" +
\r
21641 " <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
21642 " <button class=\"btn-arrow\" ng-click=\"gotoBtnClick()\" ng-disabled=\"$parent.gotoPage !== 0 || $parent.gotoPage !== undefined\" aria-label=\"Go to\">\n" +
\r
21643 " <div class=\"btn btn-small btn-secondary\">\n" +
\r
21644 " <i class=\"icon-primary-right\"></i>\n" +
\r
21649 " <div ng-if=\"isMobile && totalPages > 1\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\" ng-class=\"isMobile ? 'b2b-mobile-view': '' \">\n" +
\r
21650 " <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
21656 angular.module("b2bTemplate/paneSelector/paneSelector.html", []).run(["$templateCache", function($templateCache) {
\r
21657 $templateCache.put("b2bTemplate/paneSelector/paneSelector.html",
\r
21658 "<div class=\"panes\" ng-transclude></div>");
\r
21661 angular.module("b2bTemplate/paneSelector/paneSelectorPane.html", []).run(["$templateCache", function($templateCache) {
\r
21662 $templateCache.put("b2bTemplate/paneSelector/paneSelectorPane.html",
\r
21663 "<div class=\"pane-block\" ng-transclude></div>");
\r
21666 angular.module("b2bTemplate/profileCard/profileCard-addUser.html", []).run(["$templateCache", function($templateCache) {
\r
21667 $templateCache.put("b2bTemplate/profileCard/profileCard-addUser.html",
\r
21668 "<div class=\"span3 b2b-profile-card b2b-add-user\">\n" +
\r
21669 " <div class=\"atcenter\">\n" +
\r
21670 " <div class=\"circle\"><i class=\"icon-primary-add-maximize\"></i></div>\n" +
\r
21671 " <div>Create new user</div>\n" +
\r
21676 angular.module("b2bTemplate/profileCard/profileCard.html", []).run(["$templateCache", function($templateCache) {
\r
21677 $templateCache.put("b2bTemplate/profileCard/profileCard.html",
\r
21678 "<div class=\"span3 b2b-profile-card\">\n" +
\r
21679 " <div class=\"top-block\">\n" +
\r
21680 " <div class=\"profile-image\">\n" +
\r
21681 " <img ng-if=\"image\" profile-name=\"{{profile.name}}\" ng-src=\"{{profile.img}}\" alt=\"{{profile.name}}\">\n" +
\r
21682 " <span ng-if=\"!image\" class=\"default-img\">{{initials}}</span>\n" +
\r
21684 " <h4 class=\"name\">{{profile.name}}</h4>\n" +
\r
21686 " <p class=\"status\">\n" +
\r
21687 " <span class=\"status-icon\" ng-class=\"{'status-green':colorIcon==='green','status-red':colorIcon==='red','status-blue':colorIcon==='blue','status-yellow':colorIcon==='yellow'}\"> \n" +
\r
21689 " <span>{{profile.state}}<span ng-if=\"badge\" class=\"status-badge\">Admin</span></span>\n" +
\r
21693 " <div class=\"bottom-block\">\n" +
\r
21694 " <div class=\"profile-details\">\n" +
\r
21695 " <label>Username</label>\n" +
\r
21696 " <div ng-if=\"shouldClip(profile.userName)\" ng-mouseover=\"showUserNameTooltip = true;\" ng-mouseleave=\"showUserNameTooltip = false;\">\n" +
\r
21697 " <div ng-if=\"shouldClip(profile.userName)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showUserNameTooltip\">\n" +
\r
21698 " {{profile.userName.slice(0, 25)+'...'}}\n" +
\r
21699 " <div class=\"arrow\"></div>\n" +
\r
21700 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
\r
21701 " <div class=\"tooltip-size-control\">\n" +
\r
21702 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
\r
21703 " {{profile.userName}}\n" +
\r
21709 " <div ng-if=\"!shouldClip(profile.userName)\">\n" +
\r
21710 " {{profile.userName}}\n" +
\r
21712 " <label>Email</label>\n" +
\r
21713 " <div ng-if=\"shouldClip(profile.email)\" ng-mouseover=\"showEmailTooltip = true;\" ng-mouseleave=\"showEmailTooltip = false;\">\n" +
\r
21714 " <div ng-if=\"shouldClip(profile.email)\" class=\"tooltip\" data-placement=\"bottom\" b2b-tooltip show-tooltip=\"showEmailTooltip\">\n" +
\r
21715 " {{profile.email.slice(0, 25)+'...'}}\n" +
\r
21716 " <div class=\"arrow\"></div>\n" +
\r
21717 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
\r
21718 " <div class=\"tooltip-size-control\">\n" +
\r
21719 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
\r
21720 " {{profile.email}}\n" +
\r
21726 " <div ng-if=\"!shouldClip(profile.email)\">\n" +
\r
21727 " {{profile.email}}\n" +
\r
21729 " <label>Role</label>\n" +
\r
21730 " <div ng-if=\"shouldClip(profile.role)\" ng-mouseover=\"showRoleTooltip = true;\" ng-mouseleave=\"showRoleTooltip = false;\">\n" +
\r
21731 " <div ng-if=\"shouldClip(profile.role)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showRoleTooltip\">\n" +
\r
21732 " {{profile.role.slice(0, 25)+'...'}}\n" +
\r
21733 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
\r
21734 " <div class=\"tooltip-size-control\">\n" +
\r
21735 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
\r
21736 " {{profile.role}}\n" +
\r
21742 " <div ng-if=\"!shouldClip(profile.role)\">\n" +
\r
21743 " {{profile.role}}\n" +
\r
21745 " <label>Last login</label>\n" +
\r
21746 " <div ng-if=\"shouldClip(profile.lastLogin)\" ng-mouseover=\"showLastLoginTooltip = true;\" ng-mouseleave=\"showLastLoginTooltip = false;\">\n" +
\r
21747 " <div ng-if=\"shouldClip(profile.lastLogin)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showLastLoginTooltip\">\n" +
\r
21748 " {{profile.lastLogin.slice(0, 25)+'...'}}\n" +
\r
21749 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
\r
21750 " <div class=\"tooltip-size-control\">\n" +
\r
21751 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
\r
21752 " {{profile.lastLogin}}\n" +
\r
21758 " <div ng-if=\"!shouldClip(profile.lastLogin)\">\n" +
\r
21759 " {{profile.lastLogin}}\n" +
\r
21766 angular.module("b2bTemplate/searchField/searchField.html", []).run(["$templateCache", function($templateCache) {
\r
21767 $templateCache.put("b2bTemplate/searchField/searchField.html",
\r
21768 "<div class=\"search-bar\">\n" +
\r
21769 " <div class='input-container' ng-blur=\"blurInput()\">\n" +
\r
21770 " <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
21771 " <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
21773 " <div class=\"search-suggestion-wrapper\" ng-if=\"filterList.length >=0\" ng-show=\"showListFlag\">\n" +
\r
21774 " <ul class=\"search-suggestion-list\" role=\"listbox\"> \n" +
\r
21775 " <li class=\"no-result\" ng-if=\"filterList.length == 0 && configObj.display\">{{configObj.resultText}}</li>\n" +
\r
21776 " <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
21777 " {{item.title}} \n" +
\r
21784 angular.module("b2bTemplate/seekBar/seekBar.html", []).run(["$templateCache", function($templateCache) {
\r
21785 $templateCache.put("b2bTemplate/seekBar/seekBar.html",
\r
21786 "<div class=\"b2b-seek-bar-container\" ng-class=\"{vertical:verticalSeekBar}\">\n" +
\r
21787 " <div class=\"b2b-seek-bar-track-container\">\n" +
\r
21788 " <div class=\"b2b-seek-bar-track\"></div>\n" +
\r
21789 " <div class=\"b2b-seek-bar-track-fill\"></div>\n" +
\r
21791 " <div class=\"b2b-seek-bar-knob-container\" role=\"menu\" >\n" +
\r
21792 " <div class=\"b2b-seek-bar-knob\" tabindex=\"0\" role=\"menuitem\"></div>\n" +
\r
21797 angular.module("b2bTemplate/slider/slider.html", []).run(["$templateCache", function($templateCache) {
\r
21798 $templateCache.put("b2bTemplate/slider/slider.html",
\r
21799 "<div class=\"b2b-slider-container\" ng-class=\"{'vertical':verticalSlider}\">\n" +
\r
21800 " <div class=\"slider-track-container\">\n" +
\r
21801 " <div class=\"slider-track\"></div>\n" +
\r
21802 " <div class=\"slider-track-fill\" ng-style=\"{backgroundColor:trackFillColor}\"></div>\n" +
\r
21804 " <div class=\"slider-knob-container\" ng-class=\"{'slider-knob-hidden':hideKnob}\">\n" +
\r
21805 " <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
21810 angular.module("b2bTemplate/spinButton/spinButton.html", []).run(["$templateCache", function($templateCache) {
\r
21811 $templateCache.put("b2bTemplate/spinButton/spinButton.html",
\r
21812 "<div class=\"btn-group btn-spinbutton-toggle\" ng-class=\"{'disabled': disabledFlag}\">\n" +
\r
21813 " <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
21814 " <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
21815 " <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
21819 angular.module("b2bTemplate/statusTracker/statusTracker.html", []).run(["$templateCache", function($templateCache) {
\r
21820 $templateCache.put("b2bTemplate/statusTracker/statusTracker.html",
\r
21821 "<div class=\"b2b-status-tracker row\">\n" +
\r
21822 " <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
21823 " <div class=\"btn btn-small btn-secondary\">\n" +
\r
21824 " <i class=\"icon-primary-left\"></i>\n" +
\r
21827 " <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
21828 " <p class=\"b2b-status-tracker-heading\">{{status.heading}}</p>\n" +
\r
21829 " <div class=\"progress\">\n" +
\r
21830 " <div class=\"progress-bar\">\n" +
\r
21831 " <span class=\"hidden-spoken\">\n" +
\r
21832 " {{status.complete ? 'Complete' : 'Incomplete'}}\n" +
\r
21836 " <div class=\"b2b-status-tracker-estimate\">\n" +
\r
21837 " <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
21841 " <div class=\"b2b-status-tracker-description\">\n" +
\r
21842 " {{status.description}}\n" +
\r
21845 " <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
21846 " <div class=\"btn btn-small btn-secondary\">\n" +
\r
21847 " <i class=\"icon-primary-right\"></i>\n" +
\r
21853 angular.module("b2bTemplate/stepTracker/stepTracker.html", []).run(["$templateCache", function($templateCache) {
\r
21854 $templateCache.put("b2bTemplate/stepTracker/stepTracker.html",
\r
21855 "<div class=\"b2b-step-tracker\">\n" +
\r
21856 " <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
21857 " <div class=\"btn btn-left btn-small btn-secondary\"><i class=\"icon-primary-left\"></i></div>\n" +
\r
21859 " <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
21860 " <div class=\"btn btn-small btn-right btn-secondary\"><i class=\"icon-primary-right\"></i></div>\n" +
\r
21862 " <ul class=\"b2b-steps\">\n" +
\r
21863 " <li ng-class=\"{'b2b-step-done':$index < currentIndex - 1 ,'b2b-step-on':$index == currentIndex - 1}\" \n" +
\r
21864 " ng-repeat=\"stepsItem in stepsItemsObject\" ng-show=\"isInViewport($index)\">\n" +
\r
21865 " <p class=\"b2b-step-text\" data-sm-text=\"{{stepsItem.dataMobile}}\" data-large-text=\"{{stepsItem.dataDesktop}}\">{{stepsItem.text}}</p>\n" +
\r
21866 " <span class=\"hidden-spoken\">\n" +
\r
21867 " {{($index < currentIndex - 1)? 'Complete. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
\r
21868 " {{($index == currentIndex - 1)? 'In Progress. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
\r
21869 " {{($index > currentIndex - 1)? 'Incomplete. '+stepsItem.text+' '+stepsItem.dataMobile:''}}\n" +
\r
21876 angular.module("b2bTemplate/switches/switches-spanish.html", []).run(["$templateCache", function($templateCache) {
\r
21877 $templateCache.put("b2bTemplate/switches/switches-spanish.html",
\r
21878 "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
\r
21879 " <span class=\"btn-slider-on\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"activo\">activo</i></span>\n" +
\r
21880 " <span class=\"switch-handle\"></span>\n" +
\r
21881 " <span class=\"btn-slider-off\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"inactivo\">inactivo</i></span>\n" +
\r
21885 angular.module("b2bTemplate/switches/switches.html", []).run(["$templateCache", function($templateCache) {
\r
21886 $templateCache.put("b2bTemplate/switches/switches.html",
\r
21887 "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
\r
21888 " <span class=\"btn-slider-on\">On</span>\n" +
\r
21889 " <span class=\"switch-handle\"></span>\n" +
\r
21890 " <span class=\"btn-slider-off\">Off</span>\n" +
\r
21894 angular.module("b2bTemplate/tableMessages/tableMessage.html", []).run(["$templateCache", function($templateCache) {
\r
21895 $templateCache.put("b2bTemplate/tableMessages/tableMessage.html",
\r
21896 "<div class=\"b2b-table-message\">\n" +
\r
21897 " <div class=\"b2b-message\" ng-if=\"msgType === 'noMatchingResults'\">\n" +
\r
21898 " <div class=\"b2b-magnify-glass\"></div>\n" +
\r
21900 " <div ng-transclude></div>\n" +
\r
21903 " <div class=\"b2b-message\" ng-if=\"msgType == 'infoCouldNotLoad'\">\n" +
\r
21904 " <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
21905 " <div>Oops!</div>\n" +
\r
21906 " <div>The information could not load at this time.</div>\n" +
\r
21907 " <div>Please <a href=\"javascript:void(0)\" title=\"Refresh the page\" ng-click=\"refreshAction($event)\">refresh the page</a>\n" +
\r
21910 " <div class=\"b2b-message\" ng-if=\"msgType == 'magnifySearch'\">\n" +
\r
21911 " <div class=\"b2b-magnify-glass\"></div>\n" +
\r
21913 " <p class=\"b2b-message-title\">Please input values to\n" +
\r
21914 " <br/> begin your search.</p>\n" +
\r
21917 " <div class=\"b2b-message\" ng-if=\"msgType === 'loadingTable'\">\n" +
\r
21918 " <div class=\"icon-primary-spinner-ddh b2b-loading-dots\"></div>\n" +
\r
21919 " <div ng-transclude></div>\n" +
\r
21925 angular.module("b2bTemplate/tableScrollbar/tableScrollbar.html", []).run(["$templateCache", function($templateCache) {
\r
21926 $templateCache.put("b2bTemplate/tableScrollbar/tableScrollbar.html",
\r
21927 "<div class=\"b2b-table-scrollbar\">\n" +
\r
21928 " <div class=\"b2b-scrollbar-arrows\">\n" +
\r
21929 " <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
21930 " <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
21932 " <div class=\"b2b-table-inner-container\">\n" +
\r
21933 " <span ng-transclude></span>\n" +
\r
21938 angular.module("b2bTemplate/tables/b2bTable.html", []).run(["$templateCache", function($templateCache) {
\r
21939 $templateCache.put("b2bTemplate/tables/b2bTable.html",
\r
21940 "<table ng-class=\"{'striped': responsive, 'complex-table': responsive && active}\" ng-transclude></table>");
\r
21943 angular.module("b2bTemplate/tables/b2bTableBody.html", []).run(["$templateCache", function($templateCache) {
\r
21944 $templateCache.put("b2bTemplate/tables/b2bTableBody.html",
\r
21945 "<td ng-hide=\"isHidden()\" ng-transclude></td>");
\r
21948 angular.module("b2bTemplate/tables/b2bTableHeaderSortable.html", []).run(["$templateCache", function($templateCache) {
\r
21949 $templateCache.put("b2bTemplate/tables/b2bTableHeaderSortable.html",
\r
21950 "<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
21951 " <span ng-transclude></span>\n" +
\r
21952 " <i ng-class=\"{'icon-primary-arrows-sort-arrow active': sortPattern === 'ascending', 'icon-primary-arrows-sort-arrow active down': sortPattern === 'descending'}\"></i>\n" +
\r
21956 angular.module("b2bTemplate/tables/b2bTableHeaderUnsortable.html", []).run(["$templateCache", function($templateCache) {
\r
21957 $templateCache.put("b2bTemplate/tables/b2bTableHeaderUnsortable.html",
\r
21958 "<th scope=\"col\" ng-hide=\"isHidden()\" ng-transclude></th>");
\r
21961 angular.module("b2bTemplate/tabs/b2bTab.html", []).run(["$templateCache", function($templateCache) {
\r
21962 $templateCache.put("b2bTemplate/tabs/b2bTab.html",
\r
21963 "<li role=\"tab\" aria-selected=\"{{isTabActive()}}\" aria-controls=\"{{tabPanelId}}\" class=\"tab\" \n" +
\r
21964 " ng-class=\"{'active': isTabActive()}\" ng-click=\"clickTab()\" ng-hide=\"tabItem.disabled\">\n" +
\r
21965 " <a href=\"javascript:void(0)\" tabindex=\"{{(isTabActive() && tabItem.disabled)?-1:0}}\"\n" +
\r
21966 " ng-disabled=\"tabItem.disabled\" aria-expanded=\"{{(isTabActive() && !tabItem.disabled)}}\" \n" +
\r
21967 " b2b-accessibility-click=\"13,32\" ng-transclude></a>\n" +
\r
21968 " <span class=\"hidden-spoken\" ng-if=\"isTabActive()\">Active tab</span>\n" +
\r
21972 angular.module("b2bTemplate/tabs/b2bTabset.html", []).run(["$templateCache", function($templateCache) {
\r
21973 $templateCache.put("b2bTemplate/tabs/b2bTabset.html",
\r
21974 "<ul class=\"tabs promo-tabs\" role=\"tablist\" ng-transclude></ul>");
\r
21977 angular.module("b2bTemplate/treeNav/groupedTree.html", []).run(["$templateCache", function($templateCache) {
\r
21978 $templateCache.put("b2bTemplate/treeNav/groupedTree.html",
\r
21979 "<ul role=\"group\">\n" +
\r
21980 " <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
21981 " <ul role=\"group\">\n" +
\r
21982 " <b2b-member ng-repeat='member in value.childArray' member='member' time-delay='{{timeDelay}}' group-it='groupIt'></b2b-member>\n" +
\r
21988 angular.module("b2bTemplate/treeNav/treeMember.html", []).run(["$templateCache", function($templateCache) {
\r
21989 $templateCache.put("b2bTemplate/treeNav/treeMember.html",
\r
21990 "<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
21991 " <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
21992 " <span class=\"{{!member.child?'end':''}} b2b-tree-node-icon\">\n" +
\r
21993 " <i class=\"b2b-tree-expandCollapse-icon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
\r
21995 " <div id=\"description_{{$id}}\" class=\"offscreen-text\">\n" +
\r
21996 " {{member.descriptionText}}\n" +
\r
21998 " <div class=\"b2b-tree-tooltip\" ng-if=\"member.showTooltip\">\n" +
\r
21999 " <span class=\"b2b-tree-arrow-left\"></span>\n" +
\r
22000 " <div class=\"b2b-tree-tooltip-content\">\n" +
\r
22001 " {{member.tooltipContent}}\n" +
\r
22008 angular.module("b2bTemplate/treeNav/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
\r
22009 $templateCache.put("b2bTemplate/treeNav/ungroupedTree.html",
\r
22010 "<ul role=\"{{setRole}}\"><b2b-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-member></ul>");
\r
22013 angular.module("b2bTemplate/treeNodeCheckbox/groupedTree.html", []).run(["$templateCache", function($templateCache) {
\r
22014 $templateCache.put("b2bTemplate/treeNodeCheckbox/groupedTree.html",
\r
22015 "<ul role=\"group\">\n" +
\r
22016 " <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
22017 " <span class=\"ng-hide\">\n" +
\r
22018 " <label class=\"checkbox\">\n" +
\r
22019 " <input type=\"checkbox\" tabindex=\"-1\" class=\"treeCheckBox grpTreeCheckbox\" style=\"margin-top:2px;\"/><i class=\"skin\"></i><span> {{(key)?key:''}}</span>\n" +
\r
22023 " {{(key)?key:''}} \n" +
\r
22025 " <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
22026 " <ul role=\"group\">\n" +
\r
22027 " <b2b-tree-member ng-repeat='member in value.childArray' member='member' group-it='groupIt'></b2b-tree-member>\n" +
\r
22033 angular.module("b2bTemplate/treeNodeCheckbox/treeMember.html", []).run(["$templateCache", function($templateCache) {
\r
22034 $templateCache.put("b2bTemplate/treeNodeCheckbox/treeMember.html",
\r
22035 "<li role=\"treeitem\" aria-expanded=\"{{(member.active?true:false)}}\" aria-label=\"{{member.name}}\" ng-class=\"{'bg':member.selected}\" b2b-tree-node-link>\n" +
\r
22036 " <a tabindex=\"-1\" title=\"{{member.name}}\" href=\"javascript:void(0)\" ng-class=\"{'active':member.active}\">\n" +
\r
22037 " <span ng-show=\"member.displayCheckbox\">\n" +
\r
22038 " <label class=\"checkbox\">\n" +
\r
22039 " <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
22042 " <span ng-show=\"!member.displayCheckbox\">\n" +
\r
22043 " {{member.name}} \n" +
\r
22045 " <span class=\"nodeIcon {{!member.child?'end':''}}\">\n" +
\r
22046 " <i class=\"expandCollapseIcon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
\r
22052 angular.module("b2bTemplate/treeNodeCheckbox/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
\r
22053 $templateCache.put("b2bTemplate/treeNodeCheckbox/ungroupedTree.html",
\r
22054 "<ul role=\"{{setRole}}\"><b2b-tree-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-tree-member></ul>");
\r