pip-webui
Version:
HTML5 UI for LOB applications
1,163 lines (989 loc) • 174 kB
JavaScript
/**
* @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,