- Improved 'dragSelect' on map, added "autoScroll" while dragging, closed #879

- Fixed system position not saved properly after multiple systems were dragged at once
This commit is contained in:
Mark Friedrich
2019-11-30 12:57:36 +01:00
parent 81c21d07ef
commit ee85b1af56
30 changed files with 1284 additions and 851 deletions

View File

@@ -14,7 +14,7 @@
"node": true,
// Allow ES8.
"esversion": 8,
"esversion": 9,
/*
* ENFORCING OPTIONS

View File

@@ -46,7 +46,6 @@ requirejs.config({
bootbox: 'lib/bootbox.min', // v5.2.0 Bootbox.js - custom dialogs - http://bootboxjs.com
easyPieChart: 'lib/jquery.easypiechart.min', // v2.1.6 Easy Pie Chart - HTML 5 pie charts - http://rendro.github.io/easy-pie-chart
peityInlineChart: 'lib/jquery.peity.min', // v3.3.0 Inline Chart - http://benpickles.github.io/peity/
dragToSelect: 'lib/jquery.dragToSelect', // v1.1 Drag to Select - http://andreaslagerkvist.com/jquery/drag-to-select
hoverIntent: 'lib/jquery.hoverIntent.min', // v1.10.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
select2: 'lib/select2.min', // v4.0.3 Drop Down customization - https://select2.github.io
validator: 'lib/validator.min', // v0.10.1 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator
@@ -148,9 +147,6 @@ requirejs.config({
peityInlineChart: {
deps: ['jquery']
},
dragToSelect: {
deps: ['jquery']
},
hoverIntent: {
deps: ['jquery']
},

View File

@@ -143,7 +143,7 @@ define([], () => {
if(this.config.debug){
data = (data || []);
data.unshift(this.config.name);
console.info('debug: CACHE %o | ' + msg, ...data);
console.debug('debug: CACHE %o | ' + msg, ...data);
}
};

View File

@@ -29,6 +29,9 @@ define([], () => {
'line-height': '19px',
'font-family': '"Fira Code", "Lucida Console"',
},
'debug': {
'color': '#d747d6'
},
'ok': {
'color': '#5cb85c'
},
@@ -113,8 +116,8 @@ define([], () => {
let setLineStyleByLogType = (logType, args) => {
if(args.length){
let lineStyle = getStyleByLogType('global') + getStyleByLogType(logType);
lineStyle += ['ok', 'log', 'info', 'pf'].includes(logType) ? getStyleByLogType('indentDefault') : '';
let bullet = ['ok', 'log', 'info', 'pf'].includes(logType) ? '●' : '';
lineStyle += ['debug', 'ok', 'log', 'info', 'pf'].includes(logType) ? getStyleByLogType('indentDefault') : '';
let bullet = ['debug', 'ok', 'log', 'info', 'pf'].includes(logType) ? '●' : '';
if(typeof args[0] === 'string'){
// prepend placeholder to existing message
@@ -150,6 +153,12 @@ define([], () => {
}
};
origConsole.debug = (...args) => {
setMessageStyleByLogType('debug', args);
setLineStyleByLogType('debug', args);
info.apply(origConsole, args);
};
origConsole.ok = (...args) => {
setMessageStyleByLogType('ok', args);
setLineStyleByLogType('ok', args);

View File

@@ -206,7 +206,7 @@ define([
this.debug = (msg,...data) => {
if(this._config.debug){
data = (data || []);
console.info(msg, ...data);
console.debug(msg, ...data);
}
};
}

399
js/app/lib/dragSelect.js Normal file
View File

@@ -0,0 +1,399 @@
define(['app/lib/eventHandler'], (EventHandler) => {
'use strict';
let DragSelect = class DragSelect {
constructor(config){
this._config = Object.assign({}, this.constructor.defaultConfig, config);
this._instanceId = ++this.constructor.instanceCount;
this._instanceName = [this._config.namespace, this._instanceId].join('-');
this._animationFrameId = null;
this._mouseIsDown = false;
this._cursorPosition = {x: 0, y: 0};
this._selectBoxDimHash = undefined;
this._deselectedElements = [];
this._targetDim = {
left: 0,
top: 0,
width: 10,
height: 10
};
this._selectBoxOrigin = {
left: 0,
top: 0
};
this.init();
this.debug = (msg,...data) => {
if(this._config.debug){
data = (data || []);
console.debug(msg, ...data);
}
};
this.debugEvent = e => {
if(this._config.debugEvents){
let arrow = '?';
switch(e.type){
case 'mousedown': arrow = '⯆'; break;
case 'mousemove': arrow = '⯈'; break;
case 'mouseup': arrow = '⯅'; break;
}
this.debug('ON ' + arrow + ' %s currentTarget: %o event: %o', e.type, e.currentTarget, e);
}
};
}
/**
* set/update target target element dimension
* -> must be updated if target element is scrolled/or pushed by e.g. slide menu
*/
setTargetDimensions(){
let domRect = this._config.target.getBoundingClientRect();
Object.assign(this._targetDim, this.filterDomRect(domRect));
}
/**
* set/update boundary element dimension [optional]
* -> required for 'intersection' check e.g. for scrollable target
* @returns {DOMRect}
*/
setBoundaryDimensions(){
if(this._config.boundary){
let boundary = this._config.target.closest(this._config.boundary);
if(boundary){
return (this._boundaryDim = boundary.getBoundingClientRect());
}
}
delete this._boundaryDim;
}
/**
* set/update current cursor coordinates
* @param e
*/
setCursorPosition(e){
Object.assign(this._cursorPosition, {
x: e.pageX,
y: e.pageY
});
}
init(){
this.initSelectBox();
this.setTargetDimensions();
EventHandler.addEventListener(this._config.target, this.getNamespaceEvent('mousedown'), this.onMouseDown.bind(this), {passive: false});
EventHandler.addEventListener(this._config.container, this.getNamespaceEvent('mousemove'), this.onMouseMove.bind(this), {passive: false});
EventHandler.addEventListener(this._config.container, this.getNamespaceEvent('mouseup'), this.onMouseUp.bind(this), {passive: true});
}
// Mouse events -----------------------------------------------------------------------------------------------
onMouseDown(e){
if(e.which === 1){
this.debugEvent(e);
this.setTargetDimensions();
this.showSelectBox(e);
this._mouseIsDown = true;
}
}
onMouseMove(e){
if(this._mouseIsDown){
e.preventDefault();
if(this._animationFrameId){
cancelAnimationFrame(this._animationFrameId);
}
this._animationFrameId = requestAnimationFrame(() => {
this.debugEvent(e);
this.setCursorPosition(e);
this.update();
this._animationFrameId = null;
});
}
}
onMouseUp(e){
this.debugEvent(e);
this.selectElements();
this.hideSelectBox();
this._mouseIsDown = false;
this._deselectedElements = [];
}
// SelectBox handler ------------------------------------------------------------------------------------------
/**
* create new selectBox and append to DOM
* -> hidden by CSS until selectBox gets updated
*/
initSelectBox(){
this._selectBox = document.createElement('div');
this._selectBox.id = this._instanceName;
this._selectBox.classList.add(this._config.selectBoxClass);
this._selectBox.style.position = 'absolute';
this._config.target.after(this._selectBox);
}
/**
* show selectBox -> apply CSS for positioning and dimension
* @param e
*/
showSelectBox(e){
Object.assign(this._selectBoxOrigin, {
left: e.pageX - this._targetDim.left,
top: e.pageY - this._targetDim.top
});
Object.assign(this._selectBox.style,{
left: this._selectBoxOrigin.left + 'px',
top: this._selectBoxOrigin.top + 'px',
width: '1px',
height: '1px'
});
this._selectBox.classList.add(this._config.activeClass);
this.callback('onShow');
}
/**
* update selectBox position and dimension based on cursorPosition
* @returns {boolean}
*/
updateSelectBox(){
let updated = false;
if(!this.isActiveSelectBox()){
return updated;
}
let left = this._cursorPosition.x - this._targetDim.left;
let top = this._cursorPosition.y - this._targetDim.top;
let tempWidth = this._selectBoxOrigin.left - left;
let tempHeight = this._selectBoxOrigin.top - top;
let newLeft = this._selectBoxOrigin.left;
let newTop = this._selectBoxOrigin.top;
let newWidth = left - this._selectBoxOrigin.left;
let newHeight = top - this._selectBoxOrigin.top;
if(newWidth < 0){
newLeft = newLeft - tempWidth;
newWidth = newWidth * -1;
}
if(newHeight < 0){
newTop = newTop - tempHeight;
newHeight = newHeight * -1;
}
// check if dimension has changed -> improve performance
let dimensionHash = [newWidth, newHeight].join('_');
if(this._selectBoxDimHash !== dimensionHash){
this._selectBoxDimHash = dimensionHash;
Object.assign(this._selectBox.style,{
left: newLeft + 'px',
top: newTop + 'px',
width: newWidth + 'px',
height: newHeight + 'px'
});
// set drag position data (which corner)
this._selectBox.dataset.origin = this.getSelectBoxDragOrigin(left, top, newLeft, newTop).join('|');
updated = true;
this.callback('onUpdate');
this.dispatch(this._selectBox, 'update', this);
}
return updated;
}
/**
* hide selectBox
*/
hideSelectBox(){
if(!this.isActiveSelectBox()){
return;
}
if(this.callback('onHide', this._deselectedElements) !== false){
this._selectBox.classList.remove(this._config.activeClass);
}
}
/**
* cursor position corner for selectBox
* @param left
* @param top
* @param newLeft
* @param newTop
* @returns {[string, string]}
*/
getSelectBoxDragOrigin(left, top, newLeft, newTop){
let position = [];
if(
left === newLeft &&
top === newTop
){
position = ['top', 'left'];
}else if(top === newTop){
position = ['top', 'right'];
}else if(left === newLeft){
position = ['bottom', 'left'];
}else{
position = ['bottom', 'right'];
}
return position;
}
/**
* check if there is currently an active selectBox visible
* @returns {boolean}
*/
isActiveSelectBox(){
return this._selectBox.classList.contains(this._config.activeClass) &&
!this._config.target.classList.contains(this._config.disabledClass);
}
// Element select methods -------------------------------------------------------------------------------------
selectableElements(){
return this._config.target.querySelectorAll(this._config.selectables);
}
selectElements(){
if(!this.isActiveSelectBox()){
return;
}
let selectables = this.selectableElements();
let selectBoxDim = this.filterDomRect(this._selectBox.getBoundingClientRect());
selectables.forEach(el => {
let elDim = this.filterDomRect(el.getBoundingClientRect());
if(this.percentCovered(selectBoxDim, elDim) > this._config.percentCovered){
el.classList.add(this._config.selectedClass);
// remove element from "deselected" elements (e.g on add -> remove -> add scenario)
this._deselectedElements = this._deselectedElements.filter(tempEl => tempEl !== el);
}else{
if(el.classList.contains(this._config.selectedClass)){
el.classList.remove(this._config.selectedClass);
// add to "deselected" elements, if not already in array
if(this._deselectedElements.findIndex(tempEl => tempEl === el) === -1){
this._deselectedElements.push(el);
}
}
}
});
}
percentCovered(dim1, dim2){
if(
(dim1.left <= dim2.left) &&
(dim1.top <= dim2.top) &&
((dim1.left + dim1.width) >= (dim2.left + dim2.width)) &&
((dim1.top + dim1.height) > (dim2.top + dim2.height))
){
// The whole thing is covering the whole other thing
return 100;
}else{
// Only parts may be covered, calculate percentage
dim1.right = dim1.left + dim1.width;
dim1.bottom = dim1.top + dim1.height;
dim2.right = dim2.left + dim2.width;
dim2.bottom = dim2.top + dim2.height;
let l = Math.max(dim1.left, dim2.left);
let r = Math.min(dim1.right, dim2.right);
let t = Math.max(dim1.top, dim2.top);
let b = Math.min(dim1.bottom, dim2.bottom);
if(b >= t && r >= l){
return (((r - l) * (b - t)) / (dim2.width * dim2.height)) * 100;
}
}
return 0;
}
// Boundary intersection methods ------------------------------------------------------------------------------
getIntersection(){
let intersection = [];
if(this.isActiveSelectBox && this._boundaryDim){
let selectBoxDim = this._selectBox.getBoundingClientRect();
if(this._boundaryDim.top > selectBoxDim.top){
intersection.push('top');
}
if(this._boundaryDim.bottom < selectBoxDim.bottom){
intersection.push('bottom');
}
if(this._boundaryDim.left > selectBoxDim.left){
intersection.push('left');
}
if(this._boundaryDim.right < selectBoxDim.right){
intersection.push('right');
}
}
return intersection;
}
// Util methods -----------------------------------------------------------------------------------------------
update(){
this.setTargetDimensions();
this.setBoundaryDimensions();
if(this.updateSelectBox() && this._config.selectOnDrag){
this.selectElements();
}
}
getNamespaceEvent(type){
return [type, this._instanceName].join('.');
}
filterDomRect(domRect, filteredKeys = ['left', 'top', 'width', 'height']){
return filteredKeys.reduce((obj, key) => ({ ...obj, [key]: domRect[key] }), {});
}
callback(callback, ...args){
if(this._config[callback] instanceof Function){
return this._config[callback](...args);
}
}
dispatch(target, type, data = null){
let event = new CustomEvent([type, this._config.namespace].join(':'), {
bubbles: true,
detail: data
});
target.dispatchEvent(event);
}
};
DragSelect.defaultConfig = {
container: document,
target: document.body,
namespace: 'dragSelect',
activeClass: 'active',
disabledClass: 'disabled',
selectables: 'div',
selectedClass: 'dragSelect-selected',
selectBoxClass: 'dragSelect-selectBox',
boundary: undefined, // optional selector for boundary parent box (e.g. scrollable viewport)
selectOnDrag: true,
percentCovered: 25,
onShow: undefined,
onHide: undefined,
onUpdate: undefined,
debug: false,
debugEvents: false
};
DragSelect.instanceCount = 0;
return DragSelect;
});

View File

@@ -0,0 +1,29 @@
define([], () => {
'use strict';
let EventHandler = class EventHandler {
constructor(){
this._listeners = new Map();
}
addEventListener(target, type, listener, options){
this._listeners.set(type, listener);
target.addEventListener(this.constructor.eventParts(type).event, listener, options);
}
removeEventListener(target, type){
target.removeEventListener(this.constructor.eventParts(type).event, this._listeners.get(type));
this._listeners.delete(type);
}
static eventParts(type){
return type.split('.').reduce((acc, val, i) => {
acc[i ? 'namespace' : 'event'] = val;
return acc;
}, {});
}
};
return new EventHandler();
});

View File

@@ -110,7 +110,7 @@ define([
* @private
*/
let _dragFilter = systemId => {
let filterClasses = ['jtk-drag', 'pf-system-locked'];
let filterClasses = ['jtk-drag', MapUtil.config.systemLockedClass];
return ![...document.getElementById(systemId).classList].some(className => filterClasses.indexOf(className) >= 0);
};

View File

@@ -7,6 +7,8 @@ define([
'app/init',
'app/util',
'app/key',
'app/lib/dragSelect',
'app/lib/eventHandler',
'bootbox',
'app/map/util',
'app/map/contextmenu',
@@ -16,9 +18,8 @@ define([
'app/map/layout',
'app/map/magnetizing',
'app/map/scrollbar',
'dragToSelect',
'app/map/local'
], ($, Init, Util, Key, bootbox, MapUtil, MapContextMenu, MapOverlay, MapOverlayUtil, System, Layout, Magnetizer, Scrollbar) => {
], ($, Init, Util, Key, DragSelect, EventHandler, bootbox, MapUtil, MapContextMenu, MapOverlay, MapOverlayUtil, System, Layout, Magnetizer, Scrollbar) => {
'use strict';
@@ -33,7 +34,6 @@ define([
systemClass: 'pf-system', // class for all systems
systemActiveClass: 'pf-system-active', // class for an active system on a map
systemSelectedClass: 'pf-system-selected', // class for selected systems on a map
systemLockedClass: 'pf-system-locked', // class for locked systems on a map
systemHeadClass: 'pf-system-head', // class for system head
systemHeadNameClass: 'pf-system-head-name', // class for system name
systemHeadCounterClass: 'pf-system-head-counter', // class for system user counter
@@ -1990,13 +1990,10 @@ define([
}
dragSystem.find('.' + config.systemHeadNameClass).editable('option', 'placement', placement);
// drag system is not always selected
let selectedSystems = mapContainer.getSelectedSystems().get();
selectedSystems = selectedSystems.concat(dragSystem.get());
selectedSystems = $.unique( selectedSystems );
// repaint connections (and overlays) -> just in case something fails...
revalidate(map, selectedSystems);
// update all dragged systems -> added to DragSelection
params.selection.forEach(elData => {
MapUtil.markAsChanged($(elData[0]));
});
}
});
@@ -2114,7 +2111,7 @@ define([
if( system.data('locked') === true ){
system.data('locked', false);
system.removeClass( config.systemLockedClass );
system.removeClass(MapUtil.config.systemLockedClass);
// enable draggable
map.setDraggable(system, true);
@@ -2124,7 +2121,7 @@ define([
}
}else{
system.data('locked', true);
system.addClass( config.systemLockedClass );
system.addClass(MapUtil.config.systemLockedClass);
// enable draggable
map.setDraggable(system, false);
@@ -2433,11 +2430,17 @@ define([
});
});
// init drag-frame selection
mapContainer.dragToSelect({
selectOnMove: true,
selectables: '.' + config.systemClass,
onHide: function(selectBox, deselectedSystems){
// init drag-frame selection ----------------------------------------------------------------------------------
let dragSelect = new DragSelect({
target: mapContainer[0],
selectables: '.' + config.systemClass + ':not(.' + MapUtil.config.systemLockedClass + '):not(.' + MapUtil.config.systemHiddenClass + ')',
selectedClass: MapUtil.config.systemSelectedClass,
selectBoxClass: 'pf-map-drag-to-select',
boundary: '.mCSB_container_wrapper',
onShow: () => {
Util.triggerMenuAction(document, 'Close');
},
onHide: (deselectedSystems) => {
let selectedSystems = mapContainer.getSelectedSystems();
if(selectedSystems.length > 0){
@@ -2446,23 +2449,19 @@ define([
// convert former group draggable systems so single draggable
for(let i = 0; i < selectedSystems.length; i++){
map.addToDragSelection( selectedSystems[i] );
map.addToDragSelection(selectedSystems[i]);
}
}
// convert former group draggable systems so single draggable
for(let j = 0; j < deselectedSystems.length; j++){
map.removeFromDragSelection( deselectedSystems[j] );
for(let i = 0; i < deselectedSystems.length; i++){
map.removeFromDragSelection(deselectedSystems[i]);
}
},
onShow: function(){
Util.triggerMenuAction(document, 'Close');
},
onRefresh: function(){
}
debug: false,
debugEvents: false
});
// system body expand -----------------------------------------------------------------------------------------
mapContainer.hoverIntent({
over: function(e){
@@ -3179,9 +3178,35 @@ define([
let mapElement = mapWrapper.find('.' + config.mapClass);
let mapId = mapElement.data('id');
let dragSelect;
Scrollbar.initScrollbar(mapWrapper, {
callbacks: {
onInit: function(){
let scrollWrapper = this;
// ++++++++++++++++++++++++++++++++++++++++++++++++++
EventHandler.addEventListener(this, 'update:dragSelect', function(e){
e.stopPropagation();
dragSelect = e.detail;
let intersection = dragSelect.getIntersection();
let originData = dragSelect._selectBox.dataset.origin;
let dragOrigin = originData ? originData.split('|', 2) : [];
let position = [null, null];
let inverseDirection = (directions, i) => directions[((i + 2) % directions.length + directions.length) % directions.length];
let allDirections = ['top', 'right', 'bottom', 'left'];
allDirections.forEach((direction, i, allDirections) => {
if(dragOrigin.includes(direction) && intersection.includes(direction)){
position[i % 2] = direction;
}else if(dragOrigin.includes(direction) && intersection.includes(inverseDirection(allDirections, i))){
// reverse scroll (e.g. 1. drag&select scroll bottom end then move back to top)
position[i % 2] = inverseDirection(allDirections, i);
}
});
Scrollbar.autoScroll(scrollWrapper, position);
}, {capture: true});
// init 'space' key + 'mouse' down for map scroll -------------------------------------------------
let scrollStart = [0, 0];
let mouseStart = [0, 0];
@@ -3265,11 +3290,11 @@ define([
}
};
this.addEventListener('keydown', keyDownHandler, { capture: false });
this.addEventListener('keyup', keyUpHandler, { capture: false });
this.addEventListener('mousemove', mouseMoveHandler, { capture: false });
this.addEventListener('mousedown', mouseDownHandler, { capture: false });
this.addEventListener('mouseup', mouseUpHandler, { capture: false });
this.addEventListener('keydown', keyDownHandler, {capture: false});
this.addEventListener('keyup', keyUpHandler, {capture: false});
this.addEventListener('mousemove', mouseMoveHandler, {capture: false});
this.addEventListener('mousedown', mouseDownHandler, {capture: false});
this.addEventListener('mouseup', mouseUpHandler, {capture: false});
},
onScroll: function(){
// scroll complete
@@ -3289,6 +3314,11 @@ define([
// hide all system head tooltips
$(this).find('.' + config.systemHeadClass + ' .fa').tooltip('hide');
},
whileScrolling: function(){
if(dragSelect){
dragSelect.update();
}
}
}
});

View File

@@ -5,6 +5,14 @@ define([
], ($) => {
'use strict';
let config = {
autoScrollClass: 'auto-scroll',
autoScrollTopClass: 'auto-scroll-top',
autoScrollLeftClass: 'auto-scroll-left',
autoScrollBottomClass: 'auto-scroll-bottom',
autoScrollRightClass: 'auto-scroll-right',
};
let defaultConfig = {
axis: 'yx',
theme: 'light-3' ,
@@ -18,7 +26,12 @@ define([
callbacks: {
onTotalScrollOffset: 0,
onTotalScrollBackOffset: 0,
alwaysTriggerOffsets: true
alwaysTriggerOffsets: true,
onScroll: function(){
if($(this).data('mCS').trigger === 'internal'){
autoScrollOff(this);
}
}
},
advanced: {
@@ -44,15 +57,99 @@ define([
autoHideScrollbar: false
};
let defaultScrollToOptions = {
scrollInertia: 2000,
scrollEasing: 'easeInOutSmooth',
timeout: 0
};
/**
* init map scrollbar
* @param scrollWrapper
* @param config
* @param customConfig
*/
let initScrollbar = (scrollWrapper, config) => {
config = $.extend(true, {}, defaultConfig, config);
let initScrollbar = (scrollWrapper, customConfig) => {
customConfig = $.extend(true, {}, defaultConfig, customConfig);
// wrap callbacks -> callbacks from defaultConfig should run first
customConfig.callbacks = wrapObjectFunctions(customConfig.callbacks, defaultConfig.callbacks);
scrollWrapper.mCustomScrollbar(customConfig);
};
scrollWrapper.mCustomScrollbar(config);
/**
* @param scrollWrapper
* @param position
* @param options
*/
let autoScroll = (scrollWrapper, position, options) => {
if(position.some(position => position !== null)){
// scroll position -> start auto scroll
autoScrollOn(scrollWrapper, position, options);
}else{
// no scroll position -> stop auto scroll
autoScrollOff(scrollWrapper);
}
};
/**
* @param scrollWrapper
* @param position
* @param options
*/
let autoScrollOn = (scrollWrapper, position, options) => {
let scrollToOptions = Object.assign({}, defaultScrollToOptions, options);
let scrollInertia = 0;
let autoScrollClasses = [];
['top', 'left', 'bottom', 'right'].forEach((direction, i) => {
if(position.includes(direction)){
autoScrollClasses.push(config['autoScroll' + direction.capitalize() + 'Class']);
if(i % 2){ // left || right
scrollInertia = scrollToOptions.scrollInertia * scrollWrapper.mcs.leftPct / 100;
}else{ // top || bottom
scrollInertia = scrollToOptions.scrollInertia * scrollWrapper.mcs.topPct / 100;
}
if(i === 2 || i === 3){ // bottom || right
scrollInertia = scrollToOptions.scrollInertia - scrollInertia;
}
}
});
if(autoScrollClasses.length){
// scroll position -> check if scroll direction changed
let compareClasses = getAutoScrollClasses();
let currentClasses = [...scrollWrapper.classList].filter(cls => compareClasses.includes(cls));
let newClasses = autoScrollClasses.diff(currentClasses);
let oldClasses = currentClasses.diff(autoScrollClasses);
if(newClasses.length || oldClasses.length){
// changed scroll direction (e.g. null -> y; y -> x; y -> xy, xy -> null)
// -> stop current autos scroll and start with new scroll direction
autoScrollOff(scrollWrapper, oldClasses);
scrollWrapper.classList.add(...newClasses);
scrollToOptions.scrollInertia = scrollInertia;
$(scrollWrapper).mCustomScrollbar('scrollTo', position, scrollToOptions);
}
}else{
// no scroll position -> stop auto scroll
autoScrollOff(scrollWrapper);
}
};
/**
* @param scrollWrapper
* @param classes
*/
let autoScrollOff = (scrollWrapper, classes) => {
classes = classes || getAutoScrollClasses();
scrollWrapper.classList.remove(...classes);
$(scrollWrapper).mCustomScrollbar('stop');
};
/**
* @returns {[string, string, string, string]}
*/
let getAutoScrollClasses = () => {
return [config.autoScrollTopClass, config.autoScrollLeftClass, config.autoScrollBottomClass, config.autoScrollRightClass];
};
/**
@@ -174,6 +271,28 @@ define([
*/
let adjustPos = (position, dimension) => mapObject(roundPos(position), (v, k) => Math.max(1, Math.min(dimension[k], v)) );
/**
* wrap functions that exists in both objects (same key)
* -> o2 fkt run 1st (returned value ignored)
* -> o1 fkt run 2nd
* @param o1
* @param o2
* @returns {any}
*/
let wrapObjectFunctions = (o1 = {}, o2 = {}) => {
return mapObject(o1, function(v1, k1){
// check both obj has the same key and are functions
if([v1, o2[k1]].every(v => typeof v === 'function')){
return function(...args){
// run 'default' fkt first, then orig fkt
o2[k1].apply(this, ...args);
return v1.apply(this, ...args);
};
}
return v1;
});
};
/**
* like Array.map() for objects
* -> callback f is called for each property
@@ -187,6 +306,7 @@ define([
return {
initScrollbar: initScrollbar,
scrollToPosition: scrollToPosition,
scrollToCenter: scrollToCenter
scrollToCenter: scrollToCenter,
autoScroll: autoScroll
};
});

View File

@@ -33,6 +33,7 @@ define([
systemClass: 'pf-system', // class for all systems
systemActiveClass: 'pf-system-active', // class for an active system on a map
systemSelectedClass: 'pf-system-selected', // class for selected systems on on map
systemLockedClass: 'pf-system-locked', // class for locked systems on a map
systemHiddenClass: 'pf-system-hidden', // class for hidden (filtered) systems
// dataTable

View File

@@ -1,358 +0,0 @@
/*
@title:
Drag to Select
@version:
1.1
@author:
Andreas Lagerkvist
@date:
2009-04-06
@url:
http://andreaslagerkvist.com/jquery/drag-to-select/
@license:
http://creativecommons.org/licenses/by/3.0/
@copyright:
2008 Andreas Lagerkvist (andreaslagerkvist.com)
@requires:
jquery, jquery.dragToSelect.css
@does:
Use this plug-in to allow your users to select certain elements by dragging a "select box". Works very similar to how you can drag-n-select files and folders in most OS:es.
@howto:
$('#my-files').dragToSelect(selectables: 'li'); would make every li in the #my-files-element selectable by dragging. The li:s will recieve a "selected"-class when they are within range of the select box when user drops.
Make sure a parent-element of the selectables has position: relative as well as overflow: auto or scroll.
@exampleHTML:
<ul>
<li><img src="http://exscale.se/__files/3d/lamp-and-mates/lamp-and-mates-01_small.jpg" alt="Lamp and Mates" /></li>
<li><img src="http://exscale.se/__files/3d/stugan-winter_small.jpg" alt="The Cottage - Winter time" /></li>
<li><img src="http://exscale.se/__files/3d/ps2_small.jpg" alt="PS2" /></li>
</ul>
@exampleJS:
$('#jquery-drag-to-select-example').dragToSelect({
selectables: 'li',
onHide: function () {
alert($('#jquery-drag-to-select-example li.selected').length + ' selected');
}
});
*/
$.fn.dragToSelect = function (conf) {
var c = typeof(conf) == 'object' ? conf : {};
// Config
var config = $.extend({
className: 'pf-map-drag-to-select',
activeClass: 'active',
disabledClass: 'disabled',
selectedClass: 'pf-system-selected',
ignoreLockedClass: 'pf-system-locked', // do not select locked systems
ignoreVisibleClass: 'pf-system-hidden', // do not select invisible systems
scrollTH: 10,
percentCovered: 25,
selectables: false,
autoScroll: false,
selectOnMove: false,
onShow: function () {return true;},
onHide: function () {return true;},
onRefresh: function () {return true;}
}, c);
var realParent = $(this);
var parent = realParent;
// container for lasso element
// -> the only reason for NOT using the .pf-map is because of the zoom [scale()] feature or .pf-map
var lassoContainer = realParent.parent();
var animationFrameId;
var mouseIsDown = false;
var lastMousePosition = { x: 0, y: 0 };
// deselected items
var deselectedItems = $();
/*
do {
if (/auto|scroll|hidden/.test(parent.css('overflow'))) {
break;
}
parent = parent.parent();
} while (parent[0].parentNode);
*/
// Does user want to disable dragToSelect
if (conf == 'disable') {
parent.addClass(config.disabledClass);
return this;
}
else if (conf == 'enable') {
parent.removeClass(config.disabledClass);
return this;
}
var parentDim = {
left: 0,
top: 0,
width: 10,
height: 10
};
// set parent dimensions
// -> should be updated in case of left/right menu is open
var setParentDimensions = (parent) => {
var parentOffset = parent.offset();
parentDim = {
left: parentOffset.left,
top: parentOffset.top,
width: parent.width(),
height: parent.height()
};
}
setParentDimensions(parent);
// Current origin of select box
var selectBoxOrigin = {
left: 0,
top: 0
};
// Create select box
var selectBox = $('<div>')
.appendTo(lassoContainer)
.attr('class', config.className)
.css('position', 'absolute');
// Shows the select box
var showSelectBox = function (e) {
if (parent.is('.' + config.disabledClass)) {
return;
}
selectBoxOrigin.left = e.pageX - parentDim.left + parent[0].scrollLeft;
selectBoxOrigin.top = e.pageY - parentDim.top + parent[0].scrollTop;
var css = {
left: selectBoxOrigin.left + 'px',
top: selectBoxOrigin.top + 'px',
width: '1px',
height: '1px'
};
selectBox.addClass(config.activeClass).css(css);
config.onShow();
};
// Refreshes the select box dimensions and possibly position
var refreshSelectBox = function () {
var refreshed = false;
if (!selectBox.is('.' + config.activeClass) || parent.is('.' + config.disabledClass)) {
return refreshed;
}
var left = lastMousePosition.x - parentDim.left + parent[0].scrollLeft;
var top = lastMousePosition.y - parentDim.top + parent[0].scrollTop;
var tempWidth = selectBoxOrigin.left - left;
var tempHeight = selectBoxOrigin.top - top;
let newLeft = selectBoxOrigin.left;// - leftScroll;
let newTop = selectBoxOrigin.top;// - topScroll;
var newWidth = left - selectBoxOrigin.left;
var newHeight = top - selectBoxOrigin.top;
if(newWidth < 0){
newLeft = newLeft - tempWidth;
newWidth = newWidth * -1;
}
if(newHeight < 0){
newTop = newTop - tempHeight;
newHeight = newHeight * -1;
}
// check if dimension has changed -> save performance
var dimensionHash = [newWidth, newHeight].join('_');
if(selectBox.data('dimension-hash') !== dimensionHash){
selectBox.data('dimension-hash', dimensionHash);
var css = {
left: newLeft + 'px',
top: newTop + 'px',
width: newWidth + 'px',
height: newHeight + 'px'
};
selectBox.css(css);
config.onRefresh();
refreshed = true;
}
return refreshed;
};
// Hides the select box
var hideSelectBox = function () {
if (!selectBox.is('.' + config.activeClass) || parent.is('.' + config.disabledClass)) {
return;
}
if (config.onHide(selectBox, deselectedItems) !== false) {
selectBox.removeClass(config.activeClass);
}
};
// Selects all the elements in the select box's range
var selectElementsInRange = function () {
if (!selectBox.is('.' + config.activeClass) || parent.is('.' + config.disabledClass)) {
return;
}
var selectables = realParent.find(config.selectables + ':not(.' + config.ignoreLockedClass + ')'+ ':not(.' + config.ignoreVisibleClass + ')');
var selectBoxOffset = selectBox.offset();
var selectBoxDim = {
left: selectBoxOffset.left,
top: selectBoxOffset.top,
width: selectBox.width(),
height: selectBox.height()
};
selectables.each(function (i) {
var el = $(this);
var elOffset = el.offset();
var elDim = {
left: elOffset.left,
top: elOffset.top,
width: el.width(),
height: el.height()
};
if (percentCovered(selectBoxDim, elDim) > config.percentCovered) {
el.addClass(config.selectedClass);
// remove element from "deselected" elements (e.g on add -> remove -> add scenario)
deselectedItems = deselectedItems.not(el);
}else {
if(el.hasClass(config.selectedClass)){
el.removeClass(config.selectedClass);
deselectedItems = deselectedItems.add(el);
}
}
});
};
// Returns the amount (in %) that dim1 covers dim2
var percentCovered = function (dim1, dim2) {
// The whole thing is covering the whole other thing
if (
(dim1.left <= dim2.left) &&
(dim1.top <= dim2.top) &&
((dim1.left + dim1.width) >= (dim2.left + dim2.width)) &&
((dim1.top + dim1.height) > (dim2.top + dim2.height))
) {
return 100;
}
// Only parts may be covered, calculate percentage
else {
dim1.right = dim1.left + dim1.width;
dim1.bottom = dim1.top + dim1.height;
dim2.right = dim2.left + dim2.width;
dim2.bottom = dim2.top + dim2.height;
var l = Math.max(dim1.left, dim2.left);
var r = Math.min(dim1.right, dim2.right);
var t = Math.max(dim1.top, dim2.top);
var b = Math.min(dim1.bottom, dim2.bottom);
if (b >= t && r >= l) {
/* $('<div/>').appendTo(document.body).css({
background: 'red',
position: 'absolute',
left: l + 'px',
top: t + 'px',
width: (r - l) + 'px',
height: (b - t) + 'px',
zIndex: 100
}); */
var percent = (((r - l) * (b - t)) / (dim2.width * dim2.height)) * 100;
// alert(percent + '% covered')
return percent;
}
}
// Nothing covered, return 0
return 0;
};
// Event functions ----------------------------------------------------------------------------
var mousemoveCallback = function(){
if(mouseIsDown){
var refreshed = refreshSelectBox();
if(refreshed && config.selectables && config.selectOnMove){
selectElementsInRange();
}
// recursive re-call on next render
animationFrameId = requestAnimationFrame(mousemoveCallback);
}
}
var mouseupCallback = function(){
if(config.selectables){
selectElementsInRange();
}
hideSelectBox();
// stop animation frame and "reset" to default
cancelAnimationFrame(animationFrameId);
mouseIsDown = false;
// reset deselected item array
deselectedItems = $();
}
// Do the right stuff then return this --------------------------------------------------------
selectBox.mousemove(function(e){
setParentDimensions(parent);
lastMousePosition.x = e.pageX;
lastMousePosition.y = e.pageY;
e.preventDefault();
}).mouseup(mouseupCallback);
parent.mousedown(function(e){
if(
e.which === 1 && // left mouse down
e.target === realParent[0] // prevent while dragging a system :)
){
// Make sure user isn't clicking scrollbar (or disallow clicks far to the right actually)
if ((e.pageX + 20) > $(document.body).width()) {
return;
}
showSelectBox(e);
mouseIsDown = true;
animationFrameId = requestAnimationFrame(mousemoveCallback);
}
e.preventDefault();
}).mousemove(function(e){
setParentDimensions(parent);
lastMousePosition.x = e.pageX;
lastMousePosition.y = e.pageY;
e.preventDefault();
}).mouseup(mouseupCallback);
// Be nice
return this;
};

View File

@@ -1,9 +0,0 @@
/*!
* hoverIntent v1.9.0 // 2017.09.01 // jQuery v1.7.0+
* http://briancherne.github.io/jquery-hoverIntent/
*
* You may use hoverIntent under the terms of the MIT license. Basically that
* means you are free to use hoverIntent as long as this header is left intact.
* Copyright 2007-2017 Brian Cherne
*/
!function(factory){"use strict";"function"==typeof define&&define.amd?define(["jquery"],factory):jQuery&&!jQuery.fn.hoverIntent&&factory(jQuery)}(function($){"use strict";var cX,cY,_cfg={interval:100,sensitivity:6,timeout:0},INSTANCE_COUNT=0,track=function(ev){cX=ev.pageX,cY=ev.pageY},compare=function(ev,$el,s,cfg){if(Math.sqrt((s.pX-cX)*(s.pX-cX)+(s.pY-cY)*(s.pY-cY))<cfg.sensitivity)return $el.off(s.event,track),delete s.timeoutId,s.isActive=!0,ev.pageX=cX,ev.pageY=cY,delete s.pX,delete s.pY,cfg.over.apply($el[0],[ev]);s.pX=cX,s.pY=cY,s.timeoutId=setTimeout(function(){compare(ev,$el,s,cfg)},cfg.interval)},delay=function(ev,$el,s,out){return delete $el.data("hoverIntent")[s.id],out.apply($el[0],[ev])};$.fn.hoverIntent=function(handlerIn,handlerOut,selector){var instanceId=INSTANCE_COUNT++,cfg=$.extend({},_cfg);$.isPlainObject(handlerIn)?(cfg=$.extend(cfg,handlerIn),$.isFunction(cfg.out)||(cfg.out=cfg.over)):cfg=$.isFunction(handlerOut)?$.extend(cfg,{over:handlerIn,out:handlerOut,selector:selector}):$.extend(cfg,{over:handlerIn,out:handlerIn,selector:handlerOut});var handleHover=function(e){var ev=$.extend({},e),$el=$(this),hoverIntentData=$el.data("hoverIntent");hoverIntentData||$el.data("hoverIntent",hoverIntentData={});var state=hoverIntentData[instanceId];state||(hoverIntentData[instanceId]=state={id:instanceId}),state.timeoutId&&(state.timeoutId=clearTimeout(state.timeoutId));var mousemove=state.event="mousemove.hoverIntent.hoverIntent"+instanceId;if("mouseenter"===e.type){if(state.isActive)return;state.pX=ev.pageX,state.pY=ev.pageY,$el.off(mousemove,track).on(mousemove,track),state.timeoutId=setTimeout(function(){compare(ev,$el,state,cfg)},cfg.interval)}else{if(!state.isActive)return;$el.off(mousemove,track),state.timeoutId=setTimeout(function(){delay(ev,$el,state,cfg.out)},cfg.timeout)}};return this.on({"mouseenter.hoverIntent":handleHover,"mouseleave.hoverIntent":handleHover},cfg.selector)}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -46,7 +46,6 @@ requirejs.config({
bootbox: 'lib/bootbox.min', // v5.2.0 Bootbox.js - custom dialogs - http://bootboxjs.com
easyPieChart: 'lib/jquery.easypiechart.min', // v2.1.6 Easy Pie Chart - HTML 5 pie charts - http://rendro.github.io/easy-pie-chart
peityInlineChart: 'lib/jquery.peity.min', // v3.3.0 Inline Chart - http://benpickles.github.io/peity/
dragToSelect: 'lib/jquery.dragToSelect', // v1.1 Drag to Select - http://andreaslagerkvist.com/jquery/drag-to-select
hoverIntent: 'lib/jquery.hoverIntent.min', // v1.10.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html
select2: 'lib/select2.min', // v4.0.3 Drop Down customization - https://select2.github.io
validator: 'lib/validator.min', // v0.10.1 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator
@@ -148,9 +147,6 @@ requirejs.config({
peityInlineChart: {
deps: ['jquery']
},
dragToSelect: {
deps: ['jquery']
},
hoverIntent: {
deps: ['jquery']
},

View File

@@ -143,7 +143,7 @@ define([], () => {
if(this.config.debug){
data = (data || []);
data.unshift(this.config.name);
console.info('debug: CACHE %o | ' + msg, ...data);
console.debug('debug: CACHE %o | ' + msg, ...data);
}
};

View File

@@ -29,6 +29,9 @@ define([], () => {
'line-height': '19px',
'font-family': '"Fira Code", "Lucida Console"',
},
'debug': {
'color': '#d747d6'
},
'ok': {
'color': '#5cb85c'
},
@@ -113,8 +116,8 @@ define([], () => {
let setLineStyleByLogType = (logType, args) => {
if(args.length){
let lineStyle = getStyleByLogType('global') + getStyleByLogType(logType);
lineStyle += ['ok', 'log', 'info', 'pf'].includes(logType) ? getStyleByLogType('indentDefault') : '';
let bullet = ['ok', 'log', 'info', 'pf'].includes(logType) ? '●' : '';
lineStyle += ['debug', 'ok', 'log', 'info', 'pf'].includes(logType) ? getStyleByLogType('indentDefault') : '';
let bullet = ['debug', 'ok', 'log', 'info', 'pf'].includes(logType) ? '●' : '';
if(typeof args[0] === 'string'){
// prepend placeholder to existing message
@@ -150,6 +153,12 @@ define([], () => {
}
};
origConsole.debug = (...args) => {
setMessageStyleByLogType('debug', args);
setLineStyleByLogType('debug', args);
info.apply(origConsole, args);
};
origConsole.ok = (...args) => {
setMessageStyleByLogType('ok', args);
setLineStyleByLogType('ok', args);

View File

@@ -206,7 +206,7 @@ define([
this.debug = (msg,...data) => {
if(this._config.debug){
data = (data || []);
console.info(msg, ...data);
console.debug(msg, ...data);
}
};
}

View File

@@ -0,0 +1,399 @@
define(['app/lib/eventHandler'], (EventHandler) => {
'use strict';
let DragSelect = class DragSelect {
constructor(config){
this._config = Object.assign({}, this.constructor.defaultConfig, config);
this._instanceId = ++this.constructor.instanceCount;
this._instanceName = [this._config.namespace, this._instanceId].join('-');
this._animationFrameId = null;
this._mouseIsDown = false;
this._cursorPosition = {x: 0, y: 0};
this._selectBoxDimHash = undefined;
this._deselectedElements = [];
this._targetDim = {
left: 0,
top: 0,
width: 10,
height: 10
};
this._selectBoxOrigin = {
left: 0,
top: 0
};
this.init();
this.debug = (msg,...data) => {
if(this._config.debug){
data = (data || []);
console.debug(msg, ...data);
}
};
this.debugEvent = e => {
if(this._config.debugEvents){
let arrow = '?';
switch(e.type){
case 'mousedown': arrow = '⯆'; break;
case 'mousemove': arrow = '⯈'; break;
case 'mouseup': arrow = '⯅'; break;
}
this.debug('ON ' + arrow + ' %s currentTarget: %o event: %o', e.type, e.currentTarget, e);
}
};
}
/**
* set/update target target element dimension
* -> must be updated if target element is scrolled/or pushed by e.g. slide menu
*/
setTargetDimensions(){
let domRect = this._config.target.getBoundingClientRect();
Object.assign(this._targetDim, this.filterDomRect(domRect));
}
/**
* set/update boundary element dimension [optional]
* -> required for 'intersection' check e.g. for scrollable target
* @returns {DOMRect}
*/
setBoundaryDimensions(){
if(this._config.boundary){
let boundary = this._config.target.closest(this._config.boundary);
if(boundary){
return (this._boundaryDim = boundary.getBoundingClientRect());
}
}
delete this._boundaryDim;
}
/**
* set/update current cursor coordinates
* @param e
*/
setCursorPosition(e){
Object.assign(this._cursorPosition, {
x: e.pageX,
y: e.pageY
});
}
init(){
this.initSelectBox();
this.setTargetDimensions();
EventHandler.addEventListener(this._config.target, this.getNamespaceEvent('mousedown'), this.onMouseDown.bind(this), {passive: false});
EventHandler.addEventListener(this._config.container, this.getNamespaceEvent('mousemove'), this.onMouseMove.bind(this), {passive: false});
EventHandler.addEventListener(this._config.container, this.getNamespaceEvent('mouseup'), this.onMouseUp.bind(this), {passive: true});
}
// Mouse events -----------------------------------------------------------------------------------------------
onMouseDown(e){
if(e.which === 1){
this.debugEvent(e);
this.setTargetDimensions();
this.showSelectBox(e);
this._mouseIsDown = true;
}
}
onMouseMove(e){
if(this._mouseIsDown){
e.preventDefault();
if(this._animationFrameId){
cancelAnimationFrame(this._animationFrameId);
}
this._animationFrameId = requestAnimationFrame(() => {
this.debugEvent(e);
this.setCursorPosition(e);
this.update();
this._animationFrameId = null;
});
}
}
onMouseUp(e){
this.debugEvent(e);
this.selectElements();
this.hideSelectBox();
this._mouseIsDown = false;
this._deselectedElements = [];
}
// SelectBox handler ------------------------------------------------------------------------------------------
/**
* create new selectBox and append to DOM
* -> hidden by CSS until selectBox gets updated
*/
initSelectBox(){
this._selectBox = document.createElement('div');
this._selectBox.id = this._instanceName;
this._selectBox.classList.add(this._config.selectBoxClass);
this._selectBox.style.position = 'absolute';
this._config.target.after(this._selectBox);
}
/**
* show selectBox -> apply CSS for positioning and dimension
* @param e
*/
showSelectBox(e){
Object.assign(this._selectBoxOrigin, {
left: e.pageX - this._targetDim.left,
top: e.pageY - this._targetDim.top
});
Object.assign(this._selectBox.style,{
left: this._selectBoxOrigin.left + 'px',
top: this._selectBoxOrigin.top + 'px',
width: '1px',
height: '1px'
});
this._selectBox.classList.add(this._config.activeClass);
this.callback('onShow');
}
/**
* update selectBox position and dimension based on cursorPosition
* @returns {boolean}
*/
updateSelectBox(){
let updated = false;
if(!this.isActiveSelectBox()){
return updated;
}
let left = this._cursorPosition.x - this._targetDim.left;
let top = this._cursorPosition.y - this._targetDim.top;
let tempWidth = this._selectBoxOrigin.left - left;
let tempHeight = this._selectBoxOrigin.top - top;
let newLeft = this._selectBoxOrigin.left;
let newTop = this._selectBoxOrigin.top;
let newWidth = left - this._selectBoxOrigin.left;
let newHeight = top - this._selectBoxOrigin.top;
if(newWidth < 0){
newLeft = newLeft - tempWidth;
newWidth = newWidth * -1;
}
if(newHeight < 0){
newTop = newTop - tempHeight;
newHeight = newHeight * -1;
}
// check if dimension has changed -> improve performance
let dimensionHash = [newWidth, newHeight].join('_');
if(this._selectBoxDimHash !== dimensionHash){
this._selectBoxDimHash = dimensionHash;
Object.assign(this._selectBox.style,{
left: newLeft + 'px',
top: newTop + 'px',
width: newWidth + 'px',
height: newHeight + 'px'
});
// set drag position data (which corner)
this._selectBox.dataset.origin = this.getSelectBoxDragOrigin(left, top, newLeft, newTop).join('|');
updated = true;
this.callback('onUpdate');
this.dispatch(this._selectBox, 'update', this);
}
return updated;
}
/**
* hide selectBox
*/
hideSelectBox(){
if(!this.isActiveSelectBox()){
return;
}
if(this.callback('onHide', this._deselectedElements) !== false){
this._selectBox.classList.remove(this._config.activeClass);
}
}
/**
* cursor position corner for selectBox
* @param left
* @param top
* @param newLeft
* @param newTop
* @returns {[string, string]}
*/
getSelectBoxDragOrigin(left, top, newLeft, newTop){
let position = [];
if(
left === newLeft &&
top === newTop
){
position = ['top', 'left'];
}else if(top === newTop){
position = ['top', 'right'];
}else if(left === newLeft){
position = ['bottom', 'left'];
}else{
position = ['bottom', 'right'];
}
return position;
}
/**
* check if there is currently an active selectBox visible
* @returns {boolean}
*/
isActiveSelectBox(){
return this._selectBox.classList.contains(this._config.activeClass) &&
!this._config.target.classList.contains(this._config.disabledClass);
}
// Element select methods -------------------------------------------------------------------------------------
selectableElements(){
return this._config.target.querySelectorAll(this._config.selectables);
}
selectElements(){
if(!this.isActiveSelectBox()){
return;
}
let selectables = this.selectableElements();
let selectBoxDim = this.filterDomRect(this._selectBox.getBoundingClientRect());
selectables.forEach(el => {
let elDim = this.filterDomRect(el.getBoundingClientRect());
if(this.percentCovered(selectBoxDim, elDim) > this._config.percentCovered){
el.classList.add(this._config.selectedClass);
// remove element from "deselected" elements (e.g on add -> remove -> add scenario)
this._deselectedElements = this._deselectedElements.filter(tempEl => tempEl !== el);
}else{
if(el.classList.contains(this._config.selectedClass)){
el.classList.remove(this._config.selectedClass);
// add to "deselected" elements, if not already in array
if(this._deselectedElements.findIndex(tempEl => tempEl === el) === -1){
this._deselectedElements.push(el);
}
}
}
});
}
percentCovered(dim1, dim2){
if(
(dim1.left <= dim2.left) &&
(dim1.top <= dim2.top) &&
((dim1.left + dim1.width) >= (dim2.left + dim2.width)) &&
((dim1.top + dim1.height) > (dim2.top + dim2.height))
){
// The whole thing is covering the whole other thing
return 100;
}else{
// Only parts may be covered, calculate percentage
dim1.right = dim1.left + dim1.width;
dim1.bottom = dim1.top + dim1.height;
dim2.right = dim2.left + dim2.width;
dim2.bottom = dim2.top + dim2.height;
let l = Math.max(dim1.left, dim2.left);
let r = Math.min(dim1.right, dim2.right);
let t = Math.max(dim1.top, dim2.top);
let b = Math.min(dim1.bottom, dim2.bottom);
if(b >= t && r >= l){
return (((r - l) * (b - t)) / (dim2.width * dim2.height)) * 100;
}
}
return 0;
}
// Boundary intersection methods ------------------------------------------------------------------------------
getIntersection(){
let intersection = [];
if(this.isActiveSelectBox && this._boundaryDim){
let selectBoxDim = this._selectBox.getBoundingClientRect();
if(this._boundaryDim.top > selectBoxDim.top){
intersection.push('top');
}
if(this._boundaryDim.bottom < selectBoxDim.bottom){
intersection.push('bottom');
}
if(this._boundaryDim.left > selectBoxDim.left){
intersection.push('left');
}
if(this._boundaryDim.right < selectBoxDim.right){
intersection.push('right');
}
}
return intersection;
}
// Util methods -----------------------------------------------------------------------------------------------
update(){
this.setTargetDimensions();
this.setBoundaryDimensions();
if(this.updateSelectBox() && this._config.selectOnDrag){
this.selectElements();
}
}
getNamespaceEvent(type){
return [type, this._instanceName].join('.');
}
filterDomRect(domRect, filteredKeys = ['left', 'top', 'width', 'height']){
return filteredKeys.reduce((obj, key) => ({ ...obj, [key]: domRect[key] }), {});
}
callback(callback, ...args){
if(this._config[callback] instanceof Function){
return this._config[callback](...args);
}
}
dispatch(target, type, data = null){
let event = new CustomEvent([type, this._config.namespace].join(':'), {
bubbles: true,
detail: data
});
target.dispatchEvent(event);
}
};
DragSelect.defaultConfig = {
container: document,
target: document.body,
namespace: 'dragSelect',
activeClass: 'active',
disabledClass: 'disabled',
selectables: 'div',
selectedClass: 'dragSelect-selected',
selectBoxClass: 'dragSelect-selectBox',
boundary: undefined, // optional selector for boundary parent box (e.g. scrollable viewport)
selectOnDrag: true,
percentCovered: 25,
onShow: undefined,
onHide: undefined,
onUpdate: undefined,
debug: false,
debugEvents: false
};
DragSelect.instanceCount = 0;
return DragSelect;
});

View File

@@ -0,0 +1,29 @@
define([], () => {
'use strict';
let EventHandler = class EventHandler {
constructor(){
this._listeners = new Map();
}
addEventListener(target, type, listener, options){
this._listeners.set(type, listener);
target.addEventListener(this.constructor.eventParts(type).event, listener, options);
}
removeEventListener(target, type){
target.removeEventListener(this.constructor.eventParts(type).event, this._listeners.get(type));
this._listeners.delete(type);
}
static eventParts(type){
return type.split('.').reduce((acc, val, i) => {
acc[i ? 'namespace' : 'event'] = val;
return acc;
}, {});
}
};
return new EventHandler();
});

View File

@@ -110,7 +110,7 @@ define([
* @private
*/
let _dragFilter = systemId => {
let filterClasses = ['jtk-drag', 'pf-system-locked'];
let filterClasses = ['jtk-drag', MapUtil.config.systemLockedClass];
return ![...document.getElementById(systemId).classList].some(className => filterClasses.indexOf(className) >= 0);
};

View File

@@ -7,6 +7,8 @@ define([
'app/init',
'app/util',
'app/key',
'app/lib/dragSelect',
'app/lib/eventHandler',
'bootbox',
'app/map/util',
'app/map/contextmenu',
@@ -16,9 +18,8 @@ define([
'app/map/layout',
'app/map/magnetizing',
'app/map/scrollbar',
'dragToSelect',
'app/map/local'
], ($, Init, Util, Key, bootbox, MapUtil, MapContextMenu, MapOverlay, MapOverlayUtil, System, Layout, Magnetizer, Scrollbar) => {
], ($, Init, Util, Key, DragSelect, EventHandler, bootbox, MapUtil, MapContextMenu, MapOverlay, MapOverlayUtil, System, Layout, Magnetizer, Scrollbar) => {
'use strict';
@@ -33,7 +34,6 @@ define([
systemClass: 'pf-system', // class for all systems
systemActiveClass: 'pf-system-active', // class for an active system on a map
systemSelectedClass: 'pf-system-selected', // class for selected systems on a map
systemLockedClass: 'pf-system-locked', // class for locked systems on a map
systemHeadClass: 'pf-system-head', // class for system head
systemHeadNameClass: 'pf-system-head-name', // class for system name
systemHeadCounterClass: 'pf-system-head-counter', // class for system user counter
@@ -1990,13 +1990,10 @@ define([
}
dragSystem.find('.' + config.systemHeadNameClass).editable('option', 'placement', placement);
// drag system is not always selected
let selectedSystems = mapContainer.getSelectedSystems().get();
selectedSystems = selectedSystems.concat(dragSystem.get());
selectedSystems = $.unique( selectedSystems );
// repaint connections (and overlays) -> just in case something fails...
revalidate(map, selectedSystems);
// update all dragged systems -> added to DragSelection
params.selection.forEach(elData => {
MapUtil.markAsChanged($(elData[0]));
});
}
});
@@ -2114,7 +2111,7 @@ define([
if( system.data('locked') === true ){
system.data('locked', false);
system.removeClass( config.systemLockedClass );
system.removeClass(MapUtil.config.systemLockedClass);
// enable draggable
map.setDraggable(system, true);
@@ -2124,7 +2121,7 @@ define([
}
}else{
system.data('locked', true);
system.addClass( config.systemLockedClass );
system.addClass(MapUtil.config.systemLockedClass);
// enable draggable
map.setDraggable(system, false);
@@ -2433,11 +2430,17 @@ define([
});
});
// init drag-frame selection
mapContainer.dragToSelect({
selectOnMove: true,
selectables: '.' + config.systemClass,
onHide: function(selectBox, deselectedSystems){
// init drag-frame selection ----------------------------------------------------------------------------------
let dragSelect = new DragSelect({
target: mapContainer[0],
selectables: '.' + config.systemClass + ':not(.' + MapUtil.config.systemLockedClass + '):not(.' + MapUtil.config.systemHiddenClass + ')',
selectedClass: MapUtil.config.systemSelectedClass,
selectBoxClass: 'pf-map-drag-to-select',
boundary: '.mCSB_container_wrapper',
onShow: () => {
Util.triggerMenuAction(document, 'Close');
},
onHide: (deselectedSystems) => {
let selectedSystems = mapContainer.getSelectedSystems();
if(selectedSystems.length > 0){
@@ -2446,23 +2449,19 @@ define([
// convert former group draggable systems so single draggable
for(let i = 0; i < selectedSystems.length; i++){
map.addToDragSelection( selectedSystems[i] );
map.addToDragSelection(selectedSystems[i]);
}
}
// convert former group draggable systems so single draggable
for(let j = 0; j < deselectedSystems.length; j++){
map.removeFromDragSelection( deselectedSystems[j] );
for(let i = 0; i < deselectedSystems.length; i++){
map.removeFromDragSelection(deselectedSystems[i]);
}
},
onShow: function(){
Util.triggerMenuAction(document, 'Close');
},
onRefresh: function(){
}
debug: false,
debugEvents: false
});
// system body expand -----------------------------------------------------------------------------------------
mapContainer.hoverIntent({
over: function(e){
@@ -3179,9 +3178,35 @@ define([
let mapElement = mapWrapper.find('.' + config.mapClass);
let mapId = mapElement.data('id');
let dragSelect;
Scrollbar.initScrollbar(mapWrapper, {
callbacks: {
onInit: function(){
let scrollWrapper = this;
// ++++++++++++++++++++++++++++++++++++++++++++++++++
EventHandler.addEventListener(this, 'update:dragSelect', function(e){
e.stopPropagation();
dragSelect = e.detail;
let intersection = dragSelect.getIntersection();
let originData = dragSelect._selectBox.dataset.origin;
let dragOrigin = originData ? originData.split('|', 2) : [];
let position = [null, null];
let inverseDirection = (directions, i) => directions[((i + 2) % directions.length + directions.length) % directions.length];
let allDirections = ['top', 'right', 'bottom', 'left'];
allDirections.forEach((direction, i, allDirections) => {
if(dragOrigin.includes(direction) && intersection.includes(direction)){
position[i % 2] = direction;
}else if(dragOrigin.includes(direction) && intersection.includes(inverseDirection(allDirections, i))){
// reverse scroll (e.g. 1. drag&select scroll bottom end then move back to top)
position[i % 2] = inverseDirection(allDirections, i);
}
});
Scrollbar.autoScroll(scrollWrapper, position);
}, {capture: true});
// init 'space' key + 'mouse' down for map scroll -------------------------------------------------
let scrollStart = [0, 0];
let mouseStart = [0, 0];
@@ -3265,11 +3290,11 @@ define([
}
};
this.addEventListener('keydown', keyDownHandler, { capture: false });
this.addEventListener('keyup', keyUpHandler, { capture: false });
this.addEventListener('mousemove', mouseMoveHandler, { capture: false });
this.addEventListener('mousedown', mouseDownHandler, { capture: false });
this.addEventListener('mouseup', mouseUpHandler, { capture: false });
this.addEventListener('keydown', keyDownHandler, {capture: false});
this.addEventListener('keyup', keyUpHandler, {capture: false});
this.addEventListener('mousemove', mouseMoveHandler, {capture: false});
this.addEventListener('mousedown', mouseDownHandler, {capture: false});
this.addEventListener('mouseup', mouseUpHandler, {capture: false});
},
onScroll: function(){
// scroll complete
@@ -3289,6 +3314,11 @@ define([
// hide all system head tooltips
$(this).find('.' + config.systemHeadClass + ' .fa').tooltip('hide');
},
whileScrolling: function(){
if(dragSelect){
dragSelect.update();
}
}
}
});

View File

@@ -5,6 +5,14 @@ define([
], ($) => {
'use strict';
let config = {
autoScrollClass: 'auto-scroll',
autoScrollTopClass: 'auto-scroll-top',
autoScrollLeftClass: 'auto-scroll-left',
autoScrollBottomClass: 'auto-scroll-bottom',
autoScrollRightClass: 'auto-scroll-right',
};
let defaultConfig = {
axis: 'yx',
theme: 'light-3' ,
@@ -18,7 +26,12 @@ define([
callbacks: {
onTotalScrollOffset: 0,
onTotalScrollBackOffset: 0,
alwaysTriggerOffsets: true
alwaysTriggerOffsets: true,
onScroll: function(){
if($(this).data('mCS').trigger === 'internal'){
autoScrollOff(this);
}
}
},
advanced: {
@@ -44,15 +57,99 @@ define([
autoHideScrollbar: false
};
let defaultScrollToOptions = {
scrollInertia: 2000,
scrollEasing: 'easeInOutSmooth',
timeout: 0
};
/**
* init map scrollbar
* @param scrollWrapper
* @param config
* @param customConfig
*/
let initScrollbar = (scrollWrapper, config) => {
config = $.extend(true, {}, defaultConfig, config);
let initScrollbar = (scrollWrapper, customConfig) => {
customConfig = $.extend(true, {}, defaultConfig, customConfig);
// wrap callbacks -> callbacks from defaultConfig should run first
customConfig.callbacks = wrapObjectFunctions(customConfig.callbacks, defaultConfig.callbacks);
scrollWrapper.mCustomScrollbar(customConfig);
};
scrollWrapper.mCustomScrollbar(config);
/**
* @param scrollWrapper
* @param position
* @param options
*/
let autoScroll = (scrollWrapper, position, options) => {
if(position.some(position => position !== null)){
// scroll position -> start auto scroll
autoScrollOn(scrollWrapper, position, options);
}else{
// no scroll position -> stop auto scroll
autoScrollOff(scrollWrapper);
}
};
/**
* @param scrollWrapper
* @param position
* @param options
*/
let autoScrollOn = (scrollWrapper, position, options) => {
let scrollToOptions = Object.assign({}, defaultScrollToOptions, options);
let scrollInertia = 0;
let autoScrollClasses = [];
['top', 'left', 'bottom', 'right'].forEach((direction, i) => {
if(position.includes(direction)){
autoScrollClasses.push(config['autoScroll' + direction.capitalize() + 'Class']);
if(i % 2){ // left || right
scrollInertia = scrollToOptions.scrollInertia * scrollWrapper.mcs.leftPct / 100;
}else{ // top || bottom
scrollInertia = scrollToOptions.scrollInertia * scrollWrapper.mcs.topPct / 100;
}
if(i === 2 || i === 3){ // bottom || right
scrollInertia = scrollToOptions.scrollInertia - scrollInertia;
}
}
});
if(autoScrollClasses.length){
// scroll position -> check if scroll direction changed
let compareClasses = getAutoScrollClasses();
let currentClasses = [...scrollWrapper.classList].filter(cls => compareClasses.includes(cls));
let newClasses = autoScrollClasses.diff(currentClasses);
let oldClasses = currentClasses.diff(autoScrollClasses);
if(newClasses.length || oldClasses.length){
// changed scroll direction (e.g. null -> y; y -> x; y -> xy, xy -> null)
// -> stop current autos scroll and start with new scroll direction
autoScrollOff(scrollWrapper, oldClasses);
scrollWrapper.classList.add(...newClasses);
scrollToOptions.scrollInertia = scrollInertia;
$(scrollWrapper).mCustomScrollbar('scrollTo', position, scrollToOptions);
}
}else{
// no scroll position -> stop auto scroll
autoScrollOff(scrollWrapper);
}
};
/**
* @param scrollWrapper
* @param classes
*/
let autoScrollOff = (scrollWrapper, classes) => {
classes = classes || getAutoScrollClasses();
scrollWrapper.classList.remove(...classes);
$(scrollWrapper).mCustomScrollbar('stop');
};
/**
* @returns {[string, string, string, string]}
*/
let getAutoScrollClasses = () => {
return [config.autoScrollTopClass, config.autoScrollLeftClass, config.autoScrollBottomClass, config.autoScrollRightClass];
};
/**
@@ -174,6 +271,28 @@ define([
*/
let adjustPos = (position, dimension) => mapObject(roundPos(position), (v, k) => Math.max(1, Math.min(dimension[k], v)) );
/**
* wrap functions that exists in both objects (same key)
* -> o2 fkt run 1st (returned value ignored)
* -> o1 fkt run 2nd
* @param o1
* @param o2
* @returns {any}
*/
let wrapObjectFunctions = (o1 = {}, o2 = {}) => {
return mapObject(o1, function(v1, k1){
// check both obj has the same key and are functions
if([v1, o2[k1]].every(v => typeof v === 'function')){
return function(...args){
// run 'default' fkt first, then orig fkt
o2[k1].apply(this, ...args);
return v1.apply(this, ...args);
};
}
return v1;
});
};
/**
* like Array.map() for objects
* -> callback f is called for each property
@@ -187,6 +306,7 @@ define([
return {
initScrollbar: initScrollbar,
scrollToPosition: scrollToPosition,
scrollToCenter: scrollToCenter
scrollToCenter: scrollToCenter,
autoScroll: autoScroll
};
});

View File

@@ -33,6 +33,7 @@ define([
systemClass: 'pf-system', // class for all systems
systemActiveClass: 'pf-system-active', // class for an active system on a map
systemSelectedClass: 'pf-system-selected', // class for selected systems on on map
systemLockedClass: 'pf-system-locked', // class for locked systems on a map
systemHiddenClass: 'pf-system-hidden', // class for hidden (filtered) systems
// dataTable

View File

@@ -1,358 +0,0 @@
/*
@title:
Drag to Select
@version:
1.1
@author:
Andreas Lagerkvist
@date:
2009-04-06
@url:
http://andreaslagerkvist.com/jquery/drag-to-select/
@license:
http://creativecommons.org/licenses/by/3.0/
@copyright:
2008 Andreas Lagerkvist (andreaslagerkvist.com)
@requires:
jquery, jquery.dragToSelect.css
@does:
Use this plug-in to allow your users to select certain elements by dragging a "select box". Works very similar to how you can drag-n-select files and folders in most OS:es.
@howto:
$('#my-files').dragToSelect(selectables: 'li'); would make every li in the #my-files-element selectable by dragging. The li:s will recieve a "selected"-class when they are within range of the select box when user drops.
Make sure a parent-element of the selectables has position: relative as well as overflow: auto or scroll.
@exampleHTML:
<ul>
<li><img src="http://exscale.se/__files/3d/lamp-and-mates/lamp-and-mates-01_small.jpg" alt="Lamp and Mates" /></li>
<li><img src="http://exscale.se/__files/3d/stugan-winter_small.jpg" alt="The Cottage - Winter time" /></li>
<li><img src="http://exscale.se/__files/3d/ps2_small.jpg" alt="PS2" /></li>
</ul>
@exampleJS:
$('#jquery-drag-to-select-example').dragToSelect({
selectables: 'li',
onHide: function () {
alert($('#jquery-drag-to-select-example li.selected').length + ' selected');
}
});
*/
$.fn.dragToSelect = function (conf) {
var c = typeof(conf) == 'object' ? conf : {};
// Config
var config = $.extend({
className: 'pf-map-drag-to-select',
activeClass: 'active',
disabledClass: 'disabled',
selectedClass: 'pf-system-selected',
ignoreLockedClass: 'pf-system-locked', // do not select locked systems
ignoreVisibleClass: 'pf-system-hidden', // do not select invisible systems
scrollTH: 10,
percentCovered: 25,
selectables: false,
autoScroll: false,
selectOnMove: false,
onShow: function () {return true;},
onHide: function () {return true;},
onRefresh: function () {return true;}
}, c);
var realParent = $(this);
var parent = realParent;
// container for lasso element
// -> the only reason for NOT using the .pf-map is because of the zoom [scale()] feature or .pf-map
var lassoContainer = realParent.parent();
var animationFrameId;
var mouseIsDown = false;
var lastMousePosition = { x: 0, y: 0 };
// deselected items
var deselectedItems = $();
/*
do {
if (/auto|scroll|hidden/.test(parent.css('overflow'))) {
break;
}
parent = parent.parent();
} while (parent[0].parentNode);
*/
// Does user want to disable dragToSelect
if (conf == 'disable') {
parent.addClass(config.disabledClass);
return this;
}
else if (conf == 'enable') {
parent.removeClass(config.disabledClass);
return this;
}
var parentDim = {
left: 0,
top: 0,
width: 10,
height: 10
};
// set parent dimensions
// -> should be updated in case of left/right menu is open
var setParentDimensions = (parent) => {
var parentOffset = parent.offset();
parentDim = {
left: parentOffset.left,
top: parentOffset.top,
width: parent.width(),
height: parent.height()
};
}
setParentDimensions(parent);
// Current origin of select box
var selectBoxOrigin = {
left: 0,
top: 0
};
// Create select box
var selectBox = $('<div>')
.appendTo(lassoContainer)
.attr('class', config.className)
.css('position', 'absolute');
// Shows the select box
var showSelectBox = function (e) {
if (parent.is('.' + config.disabledClass)) {
return;
}
selectBoxOrigin.left = e.pageX - parentDim.left + parent[0].scrollLeft;
selectBoxOrigin.top = e.pageY - parentDim.top + parent[0].scrollTop;
var css = {
left: selectBoxOrigin.left + 'px',
top: selectBoxOrigin.top + 'px',
width: '1px',
height: '1px'
};
selectBox.addClass(config.activeClass).css(css);
config.onShow();
};
// Refreshes the select box dimensions and possibly position
var refreshSelectBox = function () {
var refreshed = false;
if (!selectBox.is('.' + config.activeClass) || parent.is('.' + config.disabledClass)) {
return refreshed;
}
var left = lastMousePosition.x - parentDim.left + parent[0].scrollLeft;
var top = lastMousePosition.y - parentDim.top + parent[0].scrollTop;
var tempWidth = selectBoxOrigin.left - left;
var tempHeight = selectBoxOrigin.top - top;
let newLeft = selectBoxOrigin.left;// - leftScroll;
let newTop = selectBoxOrigin.top;// - topScroll;
var newWidth = left - selectBoxOrigin.left;
var newHeight = top - selectBoxOrigin.top;
if(newWidth < 0){
newLeft = newLeft - tempWidth;
newWidth = newWidth * -1;
}
if(newHeight < 0){
newTop = newTop - tempHeight;
newHeight = newHeight * -1;
}
// check if dimension has changed -> save performance
var dimensionHash = [newWidth, newHeight].join('_');
if(selectBox.data('dimension-hash') !== dimensionHash){
selectBox.data('dimension-hash', dimensionHash);
var css = {
left: newLeft + 'px',
top: newTop + 'px',
width: newWidth + 'px',
height: newHeight + 'px'
};
selectBox.css(css);
config.onRefresh();
refreshed = true;
}
return refreshed;
};
// Hides the select box
var hideSelectBox = function () {
if (!selectBox.is('.' + config.activeClass) || parent.is('.' + config.disabledClass)) {
return;
}
if (config.onHide(selectBox, deselectedItems) !== false) {
selectBox.removeClass(config.activeClass);
}
};
// Selects all the elements in the select box's range
var selectElementsInRange = function () {
if (!selectBox.is('.' + config.activeClass) || parent.is('.' + config.disabledClass)) {
return;
}
var selectables = realParent.find(config.selectables + ':not(.' + config.ignoreLockedClass + ')'+ ':not(.' + config.ignoreVisibleClass + ')');
var selectBoxOffset = selectBox.offset();
var selectBoxDim = {
left: selectBoxOffset.left,
top: selectBoxOffset.top,
width: selectBox.width(),
height: selectBox.height()
};
selectables.each(function (i) {
var el = $(this);
var elOffset = el.offset();
var elDim = {
left: elOffset.left,
top: elOffset.top,
width: el.width(),
height: el.height()
};
if (percentCovered(selectBoxDim, elDim) > config.percentCovered) {
el.addClass(config.selectedClass);
// remove element from "deselected" elements (e.g on add -> remove -> add scenario)
deselectedItems = deselectedItems.not(el);
}else {
if(el.hasClass(config.selectedClass)){
el.removeClass(config.selectedClass);
deselectedItems = deselectedItems.add(el);
}
}
});
};
// Returns the amount (in %) that dim1 covers dim2
var percentCovered = function (dim1, dim2) {
// The whole thing is covering the whole other thing
if (
(dim1.left <= dim2.left) &&
(dim1.top <= dim2.top) &&
((dim1.left + dim1.width) >= (dim2.left + dim2.width)) &&
((dim1.top + dim1.height) > (dim2.top + dim2.height))
) {
return 100;
}
// Only parts may be covered, calculate percentage
else {
dim1.right = dim1.left + dim1.width;
dim1.bottom = dim1.top + dim1.height;
dim2.right = dim2.left + dim2.width;
dim2.bottom = dim2.top + dim2.height;
var l = Math.max(dim1.left, dim2.left);
var r = Math.min(dim1.right, dim2.right);
var t = Math.max(dim1.top, dim2.top);
var b = Math.min(dim1.bottom, dim2.bottom);
if (b >= t && r >= l) {
/* $('<div/>').appendTo(document.body).css({
background: 'red',
position: 'absolute',
left: l + 'px',
top: t + 'px',
width: (r - l) + 'px',
height: (b - t) + 'px',
zIndex: 100
}); */
var percent = (((r - l) * (b - t)) / (dim2.width * dim2.height)) * 100;
// alert(percent + '% covered')
return percent;
}
}
// Nothing covered, return 0
return 0;
};
// Event functions ----------------------------------------------------------------------------
var mousemoveCallback = function(){
if(mouseIsDown){
var refreshed = refreshSelectBox();
if(refreshed && config.selectables && config.selectOnMove){
selectElementsInRange();
}
// recursive re-call on next render
animationFrameId = requestAnimationFrame(mousemoveCallback);
}
}
var mouseupCallback = function(){
if(config.selectables){
selectElementsInRange();
}
hideSelectBox();
// stop animation frame and "reset" to default
cancelAnimationFrame(animationFrameId);
mouseIsDown = false;
// reset deselected item array
deselectedItems = $();
}
// Do the right stuff then return this --------------------------------------------------------
selectBox.mousemove(function(e){
setParentDimensions(parent);
lastMousePosition.x = e.pageX;
lastMousePosition.y = e.pageY;
e.preventDefault();
}).mouseup(mouseupCallback);
parent.mousedown(function(e){
if(
e.which === 1 && // left mouse down
e.target === realParent[0] // prevent while dragging a system :)
){
// Make sure user isn't clicking scrollbar (or disallow clicks far to the right actually)
if ((e.pageX + 20) > $(document.body).width()) {
return;
}
showSelectBox(e);
mouseIsDown = true;
animationFrameId = requestAnimationFrame(mousemoveCallback);
}
e.preventDefault();
}).mousemove(function(e){
setParentDimensions(parent);
lastMousePosition.x = e.pageX;
lastMousePosition.y = e.pageY;
e.preventDefault();
}).mouseup(mouseupCallback);
// Be nice
return this;
};

View File

@@ -1,9 +0,0 @@
/*!
* hoverIntent v1.9.0 // 2017.09.01 // jQuery v1.7.0+
* http://briancherne.github.io/jquery-hoverIntent/
*
* You may use hoverIntent under the terms of the MIT license. Basically that
* means you are free to use hoverIntent as long as this header is left intact.
* Copyright 2007-2017 Brian Cherne
*/
!function(factory){"use strict";"function"==typeof define&&define.amd?define(["jquery"],factory):jQuery&&!jQuery.fn.hoverIntent&&factory(jQuery)}(function($){"use strict";var cX,cY,_cfg={interval:100,sensitivity:6,timeout:0},INSTANCE_COUNT=0,track=function(ev){cX=ev.pageX,cY=ev.pageY},compare=function(ev,$el,s,cfg){if(Math.sqrt((s.pX-cX)*(s.pX-cX)+(s.pY-cY)*(s.pY-cY))<cfg.sensitivity)return $el.off(s.event,track),delete s.timeoutId,s.isActive=!0,ev.pageX=cX,ev.pageY=cY,delete s.pX,delete s.pY,cfg.over.apply($el[0],[ev]);s.pX=cX,s.pY=cY,s.timeoutId=setTimeout(function(){compare(ev,$el,s,cfg)},cfg.interval)},delay=function(ev,$el,s,out){return delete $el.data("hoverIntent")[s.id],out.apply($el[0],[ev])};$.fn.hoverIntent=function(handlerIn,handlerOut,selector){var instanceId=INSTANCE_COUNT++,cfg=$.extend({},_cfg);$.isPlainObject(handlerIn)?(cfg=$.extend(cfg,handlerIn),$.isFunction(cfg.out)||(cfg.out=cfg.over)):cfg=$.isFunction(handlerOut)?$.extend(cfg,{over:handlerIn,out:handlerOut,selector:selector}):$.extend(cfg,{over:handlerIn,out:handlerIn,selector:handlerOut});var handleHover=function(e){var ev=$.extend({},e),$el=$(this),hoverIntentData=$el.data("hoverIntent");hoverIntentData||$el.data("hoverIntent",hoverIntentData={});var state=hoverIntentData[instanceId];state||(hoverIntentData[instanceId]=state={id:instanceId}),state.timeoutId&&(state.timeoutId=clearTimeout(state.timeoutId));var mousemove=state.event="mousemove.hoverIntent.hoverIntent"+instanceId;if("mouseenter"===e.type){if(state.isActive)return;state.pX=ev.pageX,state.pY=ev.pageY,$el.off(mousemove,track).on(mousemove,track),state.timeoutId=setTimeout(function(){compare(ev,$el,state,cfg)},cfg.interval)}else{if(!state.isActive)return;$el.off(mousemove,track),state.timeoutId=setTimeout(function(){delay(ev,$el,state,cfg.out)},cfg.timeout)}};return this.on({"mouseenter.hoverIntent":handleHover,"mouseleave.hoverIntent":handleHover},cfg.selector)}});

View File

@@ -863,6 +863,25 @@ $mapBubbleWidth: 30px;
}
}
// DratSelect =========================================================================================================
.pf-map-drag-to-select {
background: $teal-dark !important;
display: block;
visibility: hidden; // triggered by js
opacity: 0; // triggered by js
z-index: 9000;
border: 1px dashed $gray-lighter;
pointer-events: none; // required for "mouseover" event which should be ignored
will-change: left, top, width, height, opacity;
@include border-radius(5px);
@include transition(opacity 0.15s linear);
&.active {
visibility: visible;
opacity: 0.3;
}
}
// dialogs ============================================================================================================
.ui-dialog-content{
label{

View File

@@ -1,19 +0,0 @@
// styles for drag-select
div.pf-map-drag-to-select {
background: $teal-dark !important;
display: block;
visibility: hidden; // triggered by js
opacity: 0; // triggered by js
z-index: 9000;
border: 1px dashed $gray-lighter;
will-change: left, top, width, height, opacity;
@include border-radius(5px);
@include transition(opacity 0.15s linear);
&.active {
visibility: visible;
opacity: 0.3;
}
}

View File

@@ -36,7 +36,6 @@
@import "library/pnotify/_pnotify.custom"; // PNotify custom styles
@import "library/slidebars/_slidebars"; // Slidebars Navigation
@import "library/easy-pie-chart/_easyPieChart"; // Easy Pie Chart 2.1.6
@import "library/drag-to-select/_dragToSelect"; // Drag to Select 1.1
@import "library/select2/_core"; // Select2 4.0.3
@import "library/blue-imp-gallery/_blueimp-gallery"; // Blue Imp Gallery
@import "library/blue-imp-gallery/_bootstrap-image-gallery"; // Blue Imp Gallery Bootstrap