UNPKG

pip-webui

Version:

HTML5 UI for LOB applications

1,163 lines (989 loc) 1.37 MB
/** * @file Registration of all WebUI core services * @copyright Digital Living Software Corp. 2014-2016 */ /* global angular */ (function () { 'use strict'; angular.module('pipCore', [ 'pipUtils', 'pipErrors', 'pipTransactions', 'pipTranslate', 'pipState', 'pipTimer', 'pipAssert', 'pipDebug', 'pipDateFormat', 'pipDateTimeFilters', 'pipTranslateFilters', 'pipClearErrors', 'pipTheme', 'pipFocused', 'pipSelected', 'pipInfiniteScroll', 'pipDraggable', 'pipUnsavedChanges', 'pipFabTooltipVisibility', ]); })(); /** * @file Special error handling for forms * @copyright Digital Living Software Corp. 2014-2016 */ /* global angular */ (function () { 'use strict'; var thisModule = angular.module('pipClearErrors', []); thisModule.directive('pipClearErrors', function () { return { restrict: 'A', require: ['ngModel', '^?form'], link: function ($scope, $element, $attrs, $ctrls) { var fieldController = $ctrls[0], formController = $ctrls[1]; $scope.$watch($attrs.ngModel, function (newValue) { clearFieldErrors(); clearFormErrors(); }); //------------------- function clearFieldErrors() { var errors = fieldController.$error; for (var prop in errors) { if (errors.hasOwnProperty(prop) && prop.substring(0, 6) == 'ERROR_') { fieldController.$setValidity(prop, true); } }; } function clearFormErrors() { formController.$serverError = {}; }; } }; }); })(); /** * @file Drag & drop attachable behavior * @description * Based on https://github.com/fatlinesofcode/pipDraggable * @copyright Digital Living Software Corp. 2014-2016 */ /* global angular */ (function () { 'use strict'; var thisModule = angular.module("pipDraggable", ['pipUtils']); thisModule.service('pipDraggable', function () { var scope = this; scope.inputEvent = function (event) { if (angular.isDefined(event.touches)) { return event.touches[0]; } //Checking both is not redundent. If only check if touches isDefined, angularjs isDefnied will return error and stop the remaining scripty if event.originalEvent is not defined. else if (angular.isDefined(event.originalEvent) && angular.isDefined(event.originalEvent.touches)) { return event.originalEvent.touches[0]; } return event; }; }); thisModule.directive('pipDrag', ['$rootScope', '$parse', '$document', '$window', 'pipDraggable', 'pipUtils', function ($rootScope, $parse, $document, $window, pipDraggable, pipUtils) { return { restrict: 'A', link: function (scope, element, attrs) { scope.value = attrs.ngDrag; var offset, _centerAnchor = false, _mx, _my, _tx, _ty, _mrx, _mry; var _hasTouch = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch; var _pressEvents = 'touchstart mousedown'; var _moveEvents = 'touchmove mousemove'; var _releaseEvents = 'touchend mouseup'; var _dragHandle; // to identify the element in order to prevent getting superflous events when a single element has both drag and drop directives on it. var _myid = scope.$id; var _data = null; var _dragOffset = null; var _dragEnabled = false; var _pressTimer = null; var onDragStartCallback = $parse(attrs.pipDragStart) || null; var onDragStopCallback = $parse(attrs.pipDragStop) || null; var onDragSuccessCallback = $parse(attrs.pipDragSuccess) || null; var allowTransform = angular.isDefined(attrs.allowTransform) ? scope.$eval(attrs.allowTransform) : false; var getDragData = $parse(attrs.pipDragData); var verticalScroll = pipUtils.toBoolean(attrs.pipVerticalScroll) || true, horizontalScroll = pipUtils.toBoolean(attrs.pipHorizontalScroll) || true, activationDistance = parseFloat(attrs.pipActivationDistance) || 75, scrollDistance = parseFloat(attrs.pipScrollDistance) || 50, scrollParent = false, scrollContainer = angular.element(window), scrollContainerGetter = $parse(attrs.pipScrollContainer); // deregistration function for mouse move events in $rootScope triggered by jqLite trigger handler var _deregisterRootMoveListener = angular.noop; var initialize = function () { element.attr('pip-draggable', 'false'); // prevent native drag // check to see if drag handle(s) was specified // if querySelectorAll is available, we use this instead of find // as JQLite find is limited to tagnames if (element[0].querySelectorAll) { var dragHandles = angular.element(element[0].querySelectorAll('[pip-drag-handle]')); } else { var dragHandles = element.find('[pip-drag-handle]'); } if (dragHandles.length) { _dragHandle = dragHandles; } toggleListeners(true); // Initialize scroll container if (scrollParent) { scrollContainer = angular.element($element.parent()); } else if (attrs.pipScrollContainer) { scrollContainer = angular.element(scrollContainerGetter(scope)); } else { scrollContainer = angular.element(window); } }; var toggleListeners = function (enable) { if (!enable)return; // add listeners. scope.$on('$destroy', onDestroy); scope.$watch(attrs.pipDrag, onEnableChange); scope.$watch(attrs.pipCenterAnchor, onCenterAnchor); // wire up touch events if (_dragHandle) { // handle(s) specified, use those to initiate drag _dragHandle.on(_pressEvents, onpress); } else { // no handle(s) specified, use the element as the handle element.on(_pressEvents, onpress); } if (!_hasTouch && element[0].nodeName.toLowerCase() == "img") { element.on('mousedown', function () { return false; }); // prevent native drag for images } }; var onDestroy = function (enable) { toggleListeners(false); }; var onEnableChange = function (newVal, oldVal) { _dragEnabled = (newVal); }; var onCenterAnchor = function (newVal, oldVal) { if (angular.isDefined(newVal)) _centerAnchor = (newVal || 'true'); }; var isClickableElement = function (evt) { return ( angular.isDefined(angular.element(evt.target).attr("pip-cancel-drag")) ); }; /* * When the element is clicked start the drag behaviour * On touch devices as a small delay so as not to prevent native window scrolling */ var onpress = function (evt) { if (!_dragEnabled)return; if (isClickableElement(evt)) { return; } if (evt.type == "mousedown" && evt.button != 0) { // Do not start dragging on right-click return; } if (_hasTouch) { cancelPress(); _pressTimer = setTimeout(function () { cancelPress(); onlongpress(evt); }, 100); $document.on(_moveEvents, cancelPress); $document.on(_releaseEvents, cancelPress); } else { onlongpress(evt); } }; var cancelPress = function () { clearTimeout(_pressTimer); $document.off(_moveEvents, cancelPress); $document.off(_releaseEvents, cancelPress); }; var onlongpress = function (evt) { if (!_dragEnabled)return; evt.preventDefault(); offset = element[0].getBoundingClientRect(); if (allowTransform) _dragOffset = offset; else { _dragOffset = {left: document.body.scrollLeft, top: document.body.scrollTop}; } element.centerX = element[0].offsetWidth / 2; element.centerY = element[0].offsetHeight / 2; _mx = pipDraggable.inputEvent(evt).pageX; _my = pipDraggable.inputEvent(evt).pageY; _mrx = _mx - offset.left; _mry = _my - offset.top; if (_centerAnchor) { _tx = _mx - element.centerX - $window.pageXOffset; _ty = _my - element.centerY - $window.pageYOffset; } else { _tx = _mx - _mrx - $window.pageXOffset; _ty = _my - _mry - $window.pageYOffset; } $document.on(_moveEvents, onmove); $document.on(_releaseEvents, onrelease); // This event is used to receive manually triggered mouse move events // jqLite unfortunately only supports triggerHandler(...) // See http://api.jquery.com/triggerHandler/ // _deregisterRootMoveListener = $rootScope.$on('draggable:_triggerHandlerMove', onmove); _deregisterRootMoveListener = $rootScope.$on('draggable:_triggerHandlerMove', function (event, origEvent) { onmove(origEvent); }); }; var onmove = function (evt) { if (!_dragEnabled)return; evt.preventDefault(); if (!element.hasClass('pip-dragging')) { _data = getDragData(scope); element.addClass('pip-dragging'); $rootScope.$broadcast('draggable:start', { x: _mx, y: _my, tx: _tx, ty: _ty, event: evt, element: element, data: _data }); if (onDragStartCallback) { scope.$apply(function () { onDragStartCallback(scope, {$data: _data, $event: evt}); }); } } _mx = pipDraggable.inputEvent(evt).pageX; _my = pipDraggable.inputEvent(evt).pageY; if (horizontalScroll || verticalScroll) { dragToScroll(); } if (_centerAnchor) { _tx = _mx - element.centerX - _dragOffset.left; _ty = _my - element.centerY - _dragOffset.top; } else { _tx = _mx - _mrx - _dragOffset.left; _ty = _my - _mry - _dragOffset.top; } moveElement(_tx, _ty); $rootScope.$broadcast('draggable:move', { x: _mx, y: _my, tx: _tx, ty: _ty, event: evt, element: element, data: _data, uid: _myid, dragOffset: _dragOffset }); }; var onrelease = function (evt) { if (!_dragEnabled) return; evt.preventDefault(); $rootScope.$broadcast('draggable:end', { x: _mx, y: _my, tx: _tx, ty: _ty, event: evt, element: element, data: _data, callback: onDragComplete, uid: _myid }); element.removeClass('pip-dragging'); element.parent().find('.pip-drag-enter').removeClass('pip-drag-enter'); reset(); $document.off(_moveEvents, onmove); $document.off(_releaseEvents, onrelease); if (onDragStopCallback) { scope.$apply(function () { onDragStopCallback(scope, {$data: _data, $event: evt}); }); } _deregisterRootMoveListener(); }; var onDragComplete = function (evt) { if (!onDragSuccessCallback)return; scope.$apply(function () { onDragSuccessCallback(scope, {$data: _data, $event: evt}); }); }; var reset = function () { if (allowTransform) element.css({transform: '', 'z-index': '', '-webkit-transform': '', '-ms-transform': ''}); else element.css({'position': '', top: '', left: '', 'z-index': '', width: ''}); }; var moveElement = function (x, y) { var eWidth = element.css('width'); if (allowTransform) { element.css({ transform: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ' + x + ', ' + y + ', 0, 1)', 'z-index': 99999, '-webkit-transform': 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ' + x + ', ' + y + ', 0, 1)', '-ms-transform': 'matrix(1, 0, 0, 1, ' + x + ', ' + y + ')' }); } else { element.css({ 'left': x + 'px', 'top': y + 'px', 'position': 'fixed', 'z-index': 100, width: eWidth }); } }; var dragToScroll = function () { var scrollX = 0, scrollY = 0, offset = function (element) { return element.offset() || {left: 0, top: 0}; }; if (horizontalScroll) { var containerLeft = offset(scrollContainer).left, containerWidth = scrollContainer.innerWidth(), containerRight = containerLeft + containerWidth; if ((_mx - containerLeft) < activationDistance) { scrollX = -scrollDistance; } else if ((containerRight - _mx) < activationDistance) { scrollX = scrollDistance; } } if (verticalScroll) { var containerTop = offset(scrollContainer).top, containerHeight = scrollContainer.innerHeight(), containerBottom = containerTop + containerHeight; if ((_my - containerTop) < activationDistance) { scrollY = -scrollDistance + 30; } else if ((containerBottom - _my) < activationDistance) { scrollY = scrollDistance - 30; } } if (scrollX !== 0 || scrollY !== 0) { var containerScrollLeft = scrollContainer.scrollLeft(), containerScrollTop = scrollContainer.scrollTop(); scrollContainer.scrollLeft(containerScrollLeft + scrollX); scrollContainer.scrollTop(containerScrollTop + scrollY); } }; initialize(); } }; }]); thisModule.directive('pipDrop', ['$parse', '$timeout', '$window', '$document', 'pipDraggable', function ($parse, $timeout, $window, $document, pipDraggable) { return { restrict: 'A', link: function (scope, element, attrs) { scope.value = attrs.pipDrop; scope.isTouching = false; var _lastDropTouch = null; var _myid = scope.$id; var _dropEnabled = false; var onDropCallback = $parse(attrs.pipDropSuccess);// || function(){}; var onDragStartCallback = $parse(attrs.pipDragStart); var onDragStopCallback = $parse(attrs.pipDragStop); var onDragMoveCallback = $parse(attrs.pipDragMove); var initialize = function () { toggleListeners(true); }; var toggleListeners = function (enable) { // remove listeners if (!enable)return; // add listeners. scope.$watch(attrs.pipDrop, onEnableChange); scope.$on('$destroy', onDestroy); scope.$on('draggable:start', onDragStart); scope.$on('draggable:move', onDragMove); scope.$on('draggable:end', onDragEnd); }; var onDestroy = function (enable) { toggleListeners(false); }; var onEnableChange = function (newVal, oldVal) { _dropEnabled = newVal; }; var onDragStart = function (evt, obj) { if (!_dropEnabled)return; isTouching(obj.x, obj.y, obj.element); if (attrs.pipDragStart) { $timeout(function () { onDragStartCallback(scope, {$data: obj.data, $event: obj}); }); } }; var onDragMove = function (evt, obj) { if (!_dropEnabled)return; isTouching(obj.x, obj.y, obj.element); if (attrs.pipDragMove) { $timeout(function () { onDragMoveCallback(scope, {$data: obj.data, $event: obj}); }); } }; var onDragEnd = function (evt, obj) { // don't listen to drop events if this is the element being dragged // only update the styles and return if (!_dropEnabled || _myid === obj.uid) { updateDragStyles(false, obj.element); return; } if (isTouching(obj.x, obj.y, obj.element)) { // call the pipDraggable pipDragSuccess element callback if (obj.callback) { obj.callback(obj); } if (attrs.pipDropSuccess) { $timeout(function () { onDropCallback(scope, { $data: obj.data, $event: obj, $target: scope.$eval(scope.value) }); }); } } if (attrs.pipDragStop) { $timeout(function () { onDragStopCallback(scope, {$data: obj.data, $event: obj}); }); } updateDragStyles(false, obj.element); }; var isTouching = function (mouseX, mouseY, dragElement) { var touching = hitTest(mouseX, mouseY); scope.isTouching = touching; if (touching) { _lastDropTouch = element; } updateDragStyles(touching, dragElement); return touching; }; var updateDragStyles = function (touching, dragElement) { if (touching) { element.addClass('pip-drag-enter'); dragElement.addClass('pip-drag-over'); } else if (_lastDropTouch == element) { _lastDropTouch = null; element.removeClass('pip-drag-enter'); dragElement.removeClass('pip-drag-over'); } }; var hitTest = function (x, y) { var bounds = element[0].getBoundingClientRect(); x -= $document[0].body.scrollLeft + $document[0].documentElement.scrollLeft; y -= $document[0].body.scrollTop + $document[0].documentElement.scrollTop; return x >= bounds.left && x <= bounds.right && y <= bounds.bottom && y >= bounds.top; }; initialize(); } }; }]); //thisModule.directive('pipDragClone', ['$parse', '$timeout', 'pipDraggable', function ($parse, $timeout, pipDraggable) { // return { // restrict: 'A', // link: function (scope, element, attrs) { // var img, _allowClone = true; // var _dragOffset = null; // scope.clonedData = {}; // var initialize = function () { // // img = element.find('img'); // element.attr('pip-draggable', 'false'); // img.attr('draggable', 'false'); // reset(); // toggleListeners(true); // }; // // // var toggleListeners = function (enable) { // // remove listeners // // if (!enable)return; // // add listeners. // scope.$on('draggable:start', onDragStart); // scope.$on('draggable:move', onDragMove); // scope.$on('draggable:end', onDragEnd); // preventContextMenu(); // // }; // var preventContextMenu = function () { // // element.off('mousedown touchstart touchmove touchend touchcancel', absorbEvent_); // img.off('mousedown touchstart touchmove touchend touchcancel', absorbEvent_); // // element.on('mousedown touchstart touchmove touchend touchcancel', absorbEvent_); // img.on('mousedown touchstart touchmove touchend touchcancel', absorbEvent_); // }; // var onDragStart = function (evt, obj, elm) { // _allowClone = true; // if (angular.isDefined(obj.data.allowClone)) { // _allowClone = obj.data.allowClone; // } // if (_allowClone) { // scope.$apply(function () { // scope.clonedData = obj.data; // }); // element.css('width', obj.element[0].offsetWidth); // element.css('height', obj.element[0].offsetHeight); // // moveElement(obj.tx, obj.ty); // } // // }; // var onDragMove = function (evt, obj) { // if (_allowClone) { // // _tx = obj.tx + obj.dragOffset.left; // _ty = obj.ty + obj.dragOffset.top; // // moveElement(_tx, _ty); // } // }; // var onDragEnd = function (evt, obj) { // //moveElement(obj.tx,obj.ty); // if (_allowClone) { // reset(); // } // }; // // var reset = function () { // element.css({left: 0, top: 0, position: 'fixed', 'z-index': -1, visibility: 'hidden'}); // }; // var moveElement = function (x, y) { // element.css({ // transform: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ' + x + ', ' + y + ', 0, 1)', // 'z-index': 99999, // 'visibility': 'visible', // '-webkit-transform': 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ' + x + ', ' + y + ', 0, 1)', // '-ms-transform': 'matrix(1, 0, 0, 1, ' + x + ', ' + y + ')' // //,margin: '0' don't monkey with the margin, // }); // }; // // var absorbEvent_ = function (event) { // var e = event;//.originalEvent; // e.preventDefault && e.preventDefault(); // e.stopPropagation && e.stopPropagation(); // e.cancelBubble = true; // e.returnValue = false; // return false; // }; // // initialize(); // } // }; //}]); thisModule.directive('pipPreventDrag', ['$parse', '$timeout', function ($parse, $timeout) { return { restrict: 'A', link: function (scope, element, attrs) { var initialize = function () { element.attr('pip-draggable', 'false'); toggleListeners(true); }; var toggleListeners = function (enable) { // remove listeners if (!enable)return; // add listeners. element.on('mousedown touchstart touchmove touchend touchcancel', absorbEvent_); }; var absorbEvent_ = function (event) { var e = event.originalEvent; e.preventDefault && e.preventDefault(); e.stopPropagation && e.stopPropagation(); e.cancelBubble = true; e.returnValue = false; return false; }; initialize(); } }; }]); thisModule.directive('pipCancelDrag', function () { return { restrict: 'A', link: function (scope, element, attrs) { element.find('*').attr('pip-cancel-drag', 'pip-cancel-drag'); } }; }); })(); /** * @file Directive to show confirmation dialog when user tries to leave page with unsaved changes. * @copyright Digital Living Software Corp. 2014-2016 */ /* global angular */ (function(){ 'use strict'; var thisModule = angular.module("pipFabTooltipVisibility", []); thisModule.directive("pipFabTooltipVisibility", ['$parse', '$timeout', function ($parse, $timeout) { return { restrict: 'A', scope: false, controller: ['$scope', '$attrs', function($scope, $attrs) { var trigGetter = $parse($attrs.pipFabTooltipVisibility), showGetter = $parse($attrs.pipFabShowTooltip), showSetter = showGetter.assign; $scope.$watch(trigGetter, function(isOpen) { if (isOpen) { $timeout(function() { showSetter($scope, isOpen); }, 600); } else { showSetter($scope, isOpen); } }); }] }; }]); })(); /** * @file Keyboard navigation over few focusable controls * @copyright Digital Living Software Corp. 2014-2016 */ /* global angular */ (function () { 'use strict'; var thisModule = angular.module("pipFocused", []); thisModule.directive('pipFocused', ['$timeout', '$mdConstant', function($timeout, $mdConstant) { return { require: "?ngModel", link: function ($scope, $element, $attrs) { var controls, controlsLength, withHidden = $attrs.pipWithHidden; $timeout(init); $element.on('keydown', keydownListener); $scope.$watch($attrs.ngModel, function () { $timeout(init); }, true); function init() { var selector = withHidden ? '.pip-focusable' : '.pip-focusable:visible'; controls = $element.find(selector); controlsLength = controls.length; // add needed event listeners controls.on('focus', function () { $element.addClass('pip-focused-container'); $(this).addClass('md-focused'); }).on('focusout', function () { $element.removeClass('pip-focused-container'); }); } function keydownListener(e) { var keyCode = e.which || e.keyCode; // Check control keyCode if (keyCode == $mdConstant.KEY_CODE.LEFT_ARROW || keyCode == $mdConstant.KEY_CODE.UP_ARROW || keyCode == $mdConstant.KEY_CODE.RIGHT_ARROW || keyCode == $mdConstant.KEY_CODE.DOWN_ARROW) { e.preventDefault(); var increment = (keyCode == $mdConstant.KEY_CODE.RIGHT_ARROW || keyCode == $mdConstant.KEY_CODE.DOWN_ARROW) ? 1 : -1, moveToControl = controls.index(controls.filter(".md-focused")) + increment; // Move focus to next control if (moveToControl >= 0 && moveToControl < controlsLength) { controls[moveToControl].focus(); } } } } }; }]); })(); /** * @file Infinite scrolling behavior * @description * Modified from https://github.com/sroze/ngInfiniteScroll * @copyright Digital Living Software Corp. 2014-2016 */ /* global angular */ (function () { 'use strict'; var thisModule = angular.module("pipInfiniteScroll", []); thisModule.directive('pipInfiniteScroll', ['$rootScope', '$window', '$interval', '$parse', function($rootScope, $window, $interval, $parse) { var THROTTLE_MILLISECONDS = 500; return { scope: { pipInfiniteScroll: '&', pipScrollContainer: '=', pipScrollDistance: '=', pipScrollDisabled: '=', pipScrollUseDocumentBottom: '=', pipScrollListenForEvent: '@' }, link: function($scope, $element, $attrs) { var checkWhenEnabled = null, scrollContainer, immediateCheck = true, scrollDistance = null, scrollEnabled = null, unregisterEventListener = null, useDocumentBottom = false, windowElement = angular.element($window); function height(element) { element = element[0] || element; if (isNaN(element.offsetHeight)) { return element.document.documentElement.clientHeight; } else { return element.offsetHeight; } }; function offsetTop(element) { if (!element[0].getBoundingClientRect || element.css('none')) { return; } return element[0].getBoundingClientRect().top + pageYOffset(element); }; function pageYOffset(element) { element = element[0] || element; if (isNaN(window.pageYOffset)) { return element.document.documentElement.scrollTop; } else { return element.ownerDocument.defaultView.pageYOffset; } }; var onContainerScroll = function() { var containerBottom, containerTopOffset, elementBottom, remaining, shouldScroll; if (scrollContainer === windowElement) { containerBottom = height(scrollContainer) + pageYOffset(scrollContainer[0].document.documentElement); elementBottom = offsetTop($element) + height($element); } else { containerBottom = height(scrollContainer); containerTopOffset = 0; if (offsetTop(scrollContainer) !== void 0) { containerTopOffset = offsetTop(scrollContainer); } elementBottom = offsetTop($element) - containerTopOffset + height($element); } if (useDocumentBottom) { elementBottom = height(($element[0].ownerDocument || $element[0].document).documentElement); } remaining = elementBottom - containerBottom; shouldScroll = remaining <= height(scrollContainer) * scrollDistance + 1; if (shouldScroll) { checkWhenEnabled = true; if (scrollEnabled) { if ($scope.$$phase || $rootScope.$$phase) { return $scope.pipInfiniteScroll(); } else { return $scope.$apply($scope.pipInfiniteScroll); } } } else { return checkWhenEnabled = false; } }; if (THROTTLE_MILLISECONDS != null) { onContainerScroll = _.throttle(onContainerScroll, THROTTLE_MILLISECONDS); } $scope.$on('$destroy', function() { scrollContainer.unbind('scroll', onContainerScroll); if (unregisterEventListener != null) { unregisterEventListener(); return unregisterEventListener = null; } }); function handleScrollDistance(v) { return scrollDistance = parseFloat(v) || 0; }; $scope.$watch('pipScrollDistance', handleScrollDistance); handleScrollDistance($scope.pipScrollDistance); function handleScrollDisabled(v) { scrollEnabled = !v; if (scrollEnabled && checkWhenEnabled) { checkWhenEnabled = false; return onContainerScroll(); } }; $scope.$watch('pipScrollDisabled', handleScrollDisabled); handleScrollDisabled($scope.pipScrollDisabled); function handleScrollUseDocumentBottom(v) { return useDocumentBottom = v; }; $scope.$watch('pipScrollUseDocumentBottom', handleScrollUseDocumentBottom); handleScrollUseDocumentBottom($scope.pipScrollUseDocumentBottom); function changeContainer(newContainer) { if (scrollContainer != null) { scrollContainer.unbind('scroll', onContainerScroll); } scrollContainer = newContainer; if (newContainer != null) { return scrollContainer.bind('scroll', onContainerScroll); } }; changeContainer(windowElement); if ($scope.pipScrollListenForEvent) { unregisterEventListener = $rootScope.$on($scope.pipScrollListenForEvent, onContainerScroll); } function handleScrollContainer(newContainer) { if ((newContainer == null) || newContainer.length === 0) { return; } if (newContainer instanceof HTMLElement) { newContainer = angular.element(newContainer); } else if (typeof newContainer.append === 'function') { newContainer = angular.element(newContainer[newContainer.length - 1]); } else if (typeof newContainer === 'string') { newContainer = $element.parents().find(newContainer); } if (newContainer != null && (!Array.isArray(newContainer) || (Array.isArray(newContainer) && newContainer.length > 0))) { return changeContainer(newContainer); } else { throw new Error("Invalid pip-scroll-container attribute."); } }; $scope.$watch('pipScrollContainer', function (newContainer) { if (newContainer !== scrollContainer) handleScrollContainer(newContainer); }); handleScrollContainer($scope.pipScrollContainer || []); if ($attrs.pipScrollParent != null) { changeContainer(angular.element($element.parent())); } if ($attrs.pipScrolImmediateCheck != null) { immediateCheck = $scope.$eval($attrs.pipScrolImmediateCheck); } return $interval((function() { if (immediateCheck) { return onContainerScroll(); } }), 0, 1); } } }] ); })(); /** * @file Keyboard navigation with scrolling over non-focusable controls * @copyright Digital Living Software Corp. 2014-2016 */ /* global angular */ (function () { 'use strict'; var thisModule = angular.module("pipSelected", ['pipUtils']); thisModule.directive('pipSelected',['$parse', 'pipUtils', '$mdConstant', '$timeout', function ($parse, pipUtils, $mdConstant, $timeout) { return { restrict: 'A', scope: false, link: function ($scope, $element, $attrs) { var indexGetter = $attrs.pipSelected ? $parse($attrs.pipSelected) : null, indexSetter = indexGetter ? indexGetter.assign : null, idGetter = $attrs.pipSelectedId ? $parse($attrs.pipSelectedId) : null, idSetter = idGetter ? idGetter.assign : null, changeGetter = $attrs.pipSelect ? $parse($attrs.pipSelect) : null, enterSpaceGetter = $attrs.pipEnterSpacePress ? $parse($attrs.pipEnterSpacePress) : null, noScroll = pipUtils.toBoolean($attrs.pipNoScroll), modifier = pipUtils.toBoolean($attrs.pipSkipHidden) ? ':visible' : '', className = pipUtils.toBoolean($attrs.pipTreeList) ? '.pip-selectable-tree' : '.pip-selectable', selectedIndex = indexGetter($scope), currentElementTabinex = $element.attr('tabindex'), pipSelectedWatch = $attrs.pipSelectedWatch; // Set tabindex if it's not set yet $element.attr('tabindex', currentElementTabinex || 0); // Watch selected item index if (!pipUtils.toBoolean($attrs.pipTreeList)) { $scope.$watch(indexGetter, function(newSelectedIndex) { selectItem({itemIndex: newSelectedIndex}); }); } else { $scope.$watch(idGetter, function(newSelectedId) { setTimeout(function () { selectItem({itemId: newSelectedId, raiseEvent: true}); }, 0); }); } // Watch resync selection if (pipSelectedWatch) { $scope.$watch(pipSelectedWatch, function() { // Delay update to allow ng-repeat to update DOM setTimeout(function() { selectedIndex = indexGetter($scope); selectItem({itemIndex: selectedIndex}); }, 100); }); } // Select item defined by index selectItem({itemIndex: selectedIndex, items: $element.find(className)}); // Functions and listeners function selectItem(itemParams) { var itemIndex = itemParams.itemIndex, itemId = itemParams.itemId, items = itemParams.items || $element.find(className + modifier), itemsLength = items.length, item = (function () { if (itemParams.item) return itemParams.item; if (itemIndex === undefined && itemIndex === -1) { itemIndex = items.index(items.filter('[pip-id=' + itemId + ']')); } if (itemIndex >= 0 && itemIndex < itemsLength) { return items[itemIndex] } }()), raiseEvent = itemParams.raiseEvent; if (item) { $element.find(className).removeClass('selected md-focused'); item = angular.element(item) .addClass('selected md-focused') .focus(); // todo сдвигает список тут, на первом проходе if (!noScroll) scrollToItem(item); if (raiseEvent) defineSelectedIndex(items); } }; function defineSelectedIndex(items) { var oldSelectedIndex = selectedIndex; selectedIndex = -1; for (var index = 0; index < items.length; index++) { if ($(items[index]).hasClass('selected')) { selectedIndex = index; break; } } // Execute callback to notify about item select if (oldSelectedIndex != selectedIndex && selectedIndex !== -1) { $scope.$apply(updateIndex); } function updateIndex() { var selectedItem = angular.element(items[selectedIndex]), selectedId = selectedItem.attr('pip-id'); if (indexSetter) indexSetter($scope, selectedIndex); if (idSetter) idSetter($scope, selectedId); if (changeGetter) { changeGetter($scope, { $event: { target: $element, item: selectedItem, index: selectedIndex, id: selectedId, newIndex: selectedIndex, oldIndex: oldSelectedIndex } }); } }; }; function scrollToItem($item) { if (noScroll) return; var containerTop = $element.offset().top,