- 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:
@@ -14,7 +14,7 @@
|
||||
"node": true,
|
||||
|
||||
// Allow ES8.
|
||||
"esversion": 8,
|
||||
"esversion": 9,
|
||||
|
||||
/*
|
||||
* ENFORCING OPTIONS
|
||||
|
||||
@@ -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']
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
399
js/app/lib/dragSelect.js
Normal 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;
|
||||
});
|
||||
29
js/app/lib/eventHandler.js
Normal file
29
js/app/lib/eventHandler.js
Normal 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();
|
||||
});
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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
|
||||
};
|
||||
});
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
@@ -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']
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -206,7 +206,7 @@ define([
|
||||
this.debug = (msg,...data) => {
|
||||
if(this._config.debug){
|
||||
data = (data || []);
|
||||
console.info(msg, ...data);
|
||||
console.debug(msg, ...data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
399
public/js/v1.5.5/app/lib/dragSelect.js
Normal file
399
public/js/v1.5.5/app/lib/dragSelect.js
Normal 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;
|
||||
});
|
||||
29
public/js/v1.5.5/app/lib/eventHandler.js
Normal file
29
public/js/v1.5.5/app/lib/eventHandler.js
Normal 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();
|
||||
});
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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
|
||||
};
|
||||
});
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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)}});
|
||||
@@ -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{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user