//==============================================================================
// Copyright 2012-2024 Moraware, Inc.
//
// Purpose: Support for Drag & Drop.
//
//==============================================================================
/* eslint no-redeclare: "off", no-unused-vars: "off", no-useless-assignment: "off",
no-use-before-define: "off" */
/*global
eventPageX
eventPageY
JSON
checkAttribute
highlightGroupMembers
removeClass
trim
// Found in FontIconIds.js
FontIconId_Enum
getRawFontId
// SharedClientFunctions.js
consoleLog
consoleLogBlockOpen
consoleLogBlockClose
// ShopSharedFunctions.js
mjtElemData
mjtGetAncestorWithClass
mjtGetAncestorWithTag
mjtPageXOffset
mjtPageYOffset
// ShopSharedFullFunctions.js
posTop
*/
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function mwDDCancelTouchMove(event) {
event.preventDefault();
}
/*
Copyright (c) 2005 Tim Taylor Consulting
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/
var ToolManEventTypes_Enum = {
tmetMoveEnd: 1,
tmetCartTouched: 2
};
var ToolMan = {
getEventsFactory: function () {
if (!ToolMan.prv_eventsFactory) { throw "ToolMan Events module isn't loaded"; }
return ToolMan.prv_eventsFactory;
},
getCSSFactory: function () {
if (!ToolMan.prv_cssFactory) { throw "ToolMan CSS module isn't loaded"; }
return ToolMan.prv_cssFactory;
},
getCoordinatesFactory: function () {
if (!ToolMan.prv_coordinatesFactory) { throw "ToolMan Coordinates module isn't loaded"; }
return ToolMan.prv_coordinatesFactory;
},
getDragFactory: function () {
if (!ToolMan.prv_dragFactory) { throw "ToolMan Drag module isn't loaded"; }
return ToolMan.prv_dragFactory;
},
dragsort: function () {
if (!ToolMan.prv_dragsortFactory) { throw "ToolMan DragSort module isn't loaded"; }
return ToolMan.prv_dragsortFactory;
},
m_nextCartListenerId: 1,
registerCartListener: function (params_) {
var objListElem = params_.listElement,
tmpEventTypeId = params_.eventTypeId,
fnHandler = params_.fnHandler,
tmpCartListenerId = this.m_nextCartListenerId++,
objHandlerDescriptor = {
cartListenerId: tmpCartListenerId,
eventTypeId: tmpEventTypeId,
fnHandler: fnHandler
},
objCartListenerData = mjtElemData(objListElem, 'cartListenerData'),
objMapEventToHandlerList,
arrHandlersOfEvent;
// First, check if the listener info exists on the list...
if (!objCartListenerData) {
// It doesn't, so create and set it...
objCartListenerData = {
mapEventToHandlerList: {},
mapEventListeners: {}
};
mjtElemData(objListElem, 'cartListenerData', objCartListenerData);
}
// See if there's a list of handlers for the given event...
objMapEventToHandlerList = objCartListenerData.mapEventToHandlerList;
arrHandlersOfEvent = objMapEventToHandlerList[tmpEventTypeId];
if (!arrHandlersOfEvent) {
// Not yet, so add it...
arrHandlersOfEvent = [];
objMapEventToHandlerList[tmpEventTypeId] = arrHandlersOfEvent;
}
// Add this handler to the list of handlers associated with the event...
arrHandlersOfEvent.push(objHandlerDescriptor);
// Now add the handler to the detailed map of handlers
objCartListenerData.mapEventListeners[tmpCartListenerId + ':' + tmpEventTypeId] =
objHandlerDescriptor;
return tmpCartListenerId;
},
notifyCartListeners: function (list_, eventTypeId_, eventParams_) {
var objListenerInfo = mjtElemData(list_, 'cartListenerData'),
objMapEventToHandlerList =
objListenerInfo ?
objListenerInfo.mapEventToHandlerList :
0,
arrHandlersOfEvent =
objMapEventToHandlerList ?
objMapEventToHandlerList[eventTypeId_] :
0,
idxHandlerInfo;
if (objListenerInfo) {
if (arrHandlersOfEvent) {
for (idxHandlerInfo = 0; idxHandlerInfo < arrHandlersOfEvent.length; ++idxHandlerInfo) {
arrHandlersOfEvent[idxHandlerInfo].fnHandler(eventTypeId_, eventParams_);
}
}
}
},
helpers: function () {
return ToolMan.prv_helpers;
}
};
ToolMan.m_nextCartListenerId = 1;
var g_disableDragDropClassName = 'clsDisableDragDrop';
//------------------------------------------------------------------------------
//
// Tests if the given element is a descendant of an element flagged to disable
// drag-and-drop.
//
//------------------------------------------------------------------------------
function mwDDIsMarkedDragDropDisabled(element_) {
var rc = false;
if (element_) {
rc =
!!mjtGetAncestorWithClass(
element_,
g_disableDragDropClassName);
}
return rc;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function mwDDAddDragDropDisabledClass(element_) {
if (element_) {
element_.classList.add(g_disableDragDropClassName);
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function mwDDRemoveDragDropDisabledClass(element_) {
if (element_) {
element_.classList.remove(g_disableDragDropClassName);
}
}
ToolMan.prv_helpers = {
map: function (array, func) {
for (var i = 0, n = array.length; i < n; i++) { func(array[i]); }
},
nextItem: function (item, nodeName, stayWithinParent) {
var next = item.nextSibling;
var itemParentGroupId = item.getAttribute("mjtparentgroupid");
var nextIsAncestor = false;
var keepGoing = true;
while (keepGoing) {
keepGoing = false;
while (null !== next) {
if (next.nodeName === nodeName) {
// Test if this list item contains a nested list.
if (!nextIsAncestor && null !== itemParentGroupId && next.getAttribute && null !== next.getAttribute("mjtgroupingid") && next.getAttribute("mjtgroupingid") === itemParentGroupId) {
// If it does, return the last item in the list.
var subLists = next.getElementsByTagName("ul");
if (null !== subLists && subLists.length > 0) {
var subList = subLists[0];
var subItems = subList.getElementsByTagName("li");
if (null !== subItems && subItems.length > 0) {
next = subItems[0];
}
}
}
break;
}
nextIsAncestor = false;
next = next.nextSibling;
}
if (!stayWithinParent && null === next && null !== item.parentNode && null !== item.parentNode.parentNode) {
// Look up the parent hierarchy now to see if this list is embedded within another list.
//
// If it is, continue by looking at the siblings of the grouping item.
var grandParent = item.parentNode.parentNode;
if (grandParent.nodeName === "LI" && null !== grandParent.getAttribute("mjtgroupingid")) {
// Grandparent is a grouping item, so look at the grandparent's next sibling
next = grandParent;
nextIsAncestor = true;
keepGoing = true;
}
}
}
return next;
},
// Returns true if the given item is a grouping list item.
isGroupingItem: function (item) {
return null === item ?
false :
null !== item.getAttribute("mjtgroupingid");
},
// Finds the previous item, descending into grouping items if they group the same type of items
// to which the current item belongs.
previousItem: function (item, nodeName, groupingIdToUse) {
var previous = item.previousSibling;
var itemParentGroupId = groupingIdToUse;//item.getAttribute("mjtparentgroupid");
var previousIsAncestor = false;
var keepGoing = true;
while (keepGoing) {
keepGoing = false;
while (previous !== null) {
if (previous.nodeName === nodeName) {
// Test if this is a grouping item (i.e. an item that contains another list) for
// the appropriate group type..
if (!previousIsAncestor && null !== itemParentGroupId && previous.getAttribute && null !== previous.getAttribute("mjtgroupingid") && previous.getAttribute("mjtgroupingid") === itemParentGroupId) {
// If it does, return the last item in the sublist.
var subLists = previous.getElementsByTagName("ul");
if (null !== subLists && subLists.length > 0) {
var subList = subLists[0];
var subItems = subList.getElementsByTagName("li");
if (null !== subItems && subItems.length > 0) {
previous = subItems[subItems.length - 1];
}
}
}
break;
}
previousIsAncestor = false;
previous = previous.previousSibling;
}
if (null === previous && null !== item.parentNode && null !== item.parentNode.parentNode) {
// Look up the parent hierarchy now to see if this list is embedded within another list.
// If so, continue looking at the siblings of the list.
var grandParent = item.parentNode.parentNode;
if (grandParent.nodeName === "LI" && null !== grandParent.getAttribute("mjtgroupingid")) {
previous = grandParent;
previousIsAncestor = true;
keepGoing = null !== previous;
} else {
break;
}
}
}
return previous;
},
//--------------------------------------------------------------------------
//
// Returns true iff the item is actually removed and reinserted. (It's
// still possible that there is no net change in sequence)
//
//--------------------------------------------------------------------------
moveBefore: function (movedItem_, referenceItem_, referenceItemParent_) {
var rc = false;
if (null === movedItem_) {
// Can't move a NULL item.
} else if (movedItem_ === referenceItem_) {
// Don't bother trying to move an item in front of itself.
} else if (referenceItem_ !== movedItem_.nextSibling) {
// Don't bother trying to move an item in front of the item it's
// already in front of
var parent1 = movedItem_.parentNode;
parent1.removeChild(movedItem_);
referenceItemParent_.insertBefore(movedItem_, referenceItem_);
ToolMan.helpers().removeOuterListItemIfNecessary(parent1);
rc = true;
}
return rc;
},
// If the given item is a UL inside an LI, and has no LI's of its own, the containing LI will be removed.
removeOuterListItemIfNecessary: function (parent) {
if (null !== parent && "UL" === parent.nodeName) {
var grandParent = parent.parentNode;
if (null !== grandParent && "LI" === grandParent.nodeName) {
var isListItem = false;
var i = 0;
for (i = 0; i < parent.childNodes.length && !isListItem; ++i) {
if (parent.childNodes[i].nodeName === "LI") {
isListItem = true;
}
}
if (!isListItem) {
// Remove the empty outer LI.
grandParent.parentNode.removeChild(grandParent);
}
}
}
},
moveAfter: function (item1, item2) {
var parent1 = item1.parentNode;
parent1.removeChild(item1);
var parent2 = null === item2 ? parent1 : item2.parentNode;
parent2.insertBefore(item1, item2 ? item2.nextSibling : null);
// TODO: Obsolete?: removeEmptyParentIfNecessary(parent1);
}
};
/* Below to the next notice are the contents of events.js */
/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */
var firstTarget = null;
function toolMan_GetEventTarget(event) {
var rc = null;
if (!event) {
event = window.event;
}
if (event.target) {
if (event.target.nodeType === 3) { // "Text" node
rc = event.target.parentNode;
} else {
rc = event.target;
}
} else if (event.srcElement) {
rc = event.srcElement;
}
return rc;
}
ToolMan.prv_eventsFactory = {
efFix: function (event) {
if (!event) {
event = window.event;
}
var rcTarget = toolMan_GetEventTarget(event);
if (rcTarget) {
event.target = rcTarget;
}
return event;
},
efRegister: function (element, type, func) {
if (element.addEventListener) {
element.addEventListener(type, func, false);
} else if (element.attachEvent) {
if (!element.prv_listeners) {
element.prv_listeners = [];
}
if (!element.prv_listeners[type]) {
element.prv_listeners[type] = [];
}
var workaroundFunc = function () {
try {
func.apply(element, []);
} catch (e) {
// TODO: how to do this on IE 5.0??
}
};
element.prv_listeners[type][func] = workaroundFunc;
element.attachEvent('on' + type, workaroundFunc);
}
},
efUnregister: function (element, type, func) {
if (element.removeEventListener) {
element.removeEventListener(type, func, false);
} else if (element.detachEvent) {
if (element.prv_listeners &&
element.prv_listeners[type] &&
element.prv_listeners[type][func]) {
element.detachEvent(
'on' + type,
element.prv_listeners[type][func]);
}
}
}
};
/* Below to the next notice are contents of css.js */
/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */
// TODO: write unit tests
ToolMan.prv_cssFactory = {
readStyle: function (element, property) {
if (element.style[property]) {
return element.style[property];
} else if (element.currentStyle) {
return element.currentStyle[property];
} else if (document.defaultView && document.defaultView.getComputedStyle) {
var style = document.defaultView.getComputedStyle(element, null);
return style.getPropertyValue(property);
} else {
return null;
}
}
};
/* Below to the next notice are contents of coordinates.js */
/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */
/* FIXME: assumes position styles are specified in 'px' */
function ToolManCoordinate(factory, x, y) {
this.factory = factory;
this.x = isNaN(x) ? 0 : x;
this.y = isNaN(y) ? 0 : y;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function tmEventPosFromEvent(event, touch_) {
var pos;
if (touch_ && event.touches && event.touches.length) {
var targetItem = event.touches.item(0);
pos = [
targetItem.pageX,
targetItem.pageY
];
} else {
pos =
[
eventPageX(event),
eventPageY(event)
];
pos = [
pos[0] - mjtPageXOffset(),
pos[1] - mjtPageYOffset()
];
}
return pos;
}
ToolMan.prv_coordinatesFactory = {
create: function (x, y) {
// FIXME: Safari won't parse 'throw' and aborts trying to do anything with this file
//if (isNaN(x) || isNaN(y)) throw "invalid x,y: " + x + "," + y
return new ToolManCoordinate(this, x, y);
},
origin: function () {
return this.create(0, 0);
},
//--------------------------------------------------------------------------
//
// FIXME: Safari 1.2, returns (0,0) on absolutely positioned elements
//
//--------------------------------------------------------------------------
topLeftPosition: function (element) {
var left = parseInt(ToolMan.getCSSFactory().readStyle(element, "left"), 10);
left = isNaN(left) ? 0 : left;
var top = parseInt(ToolMan.getCSSFactory().readStyle(element, "top"), 10);
top = isNaN(top) ? 0 : top;
return this.create(left, top);
},
bottomRightPosition: function (element) {
return this.topLeftPosition(element).plus(this.prv_size(element));
},
topLeftOffset: function (element) {
var offset = this.prv_offset(element);
var parent = element.offsetParent;
while (parent) {
offset = offset.plus(this.prv_offset(parent));
parent = parent.offsetParent;
}
// Now account for scrolling. The offset values
// do not take into account the possibility that the parent
// is scrolled. It also isn't enough to test if the offsetParents
// are scrolled, because in Firefox (for example) the offsetParent
// isn't necessarily the same as the parentNode, and it may be the
// parentNode that is scrolled.
parent = element.parentNode;
while (parent) {
if ("HTML" !== parent.nodeName && "BODY" !== parent.nodeName) {
// Apparently, the scrolling of the
// HTML and BODY elements are accounted for already.
offset = offset.minus(this.prv_scrollOffset(parent));
}
parent = parent.parentNode;
}
return offset;
},
bottomRightOffset: function (element) {
return this.topLeftOffset(element).plus(
this.create(element.offsetWidth, element.offsetHeight));
},
scrollOffset: function () {
if (window.pageXOffset) {
return this.create(window.pageXOffset, window.pageYOffset);
} else if (document.documentElement) {
return this.create(
document.body.scrollLeft + document.documentElement.scrollLeft,
document.body.scrollTop + document.documentElement.scrollTop);
} else if (document.body.scrollLeft >= 0) {
return this.create(document.body.scrollLeft, document.body.scrollTop);
} else {
return this.create(0, 0);
}
},
clientSize: function () {
if (window.innerHeight >= 0) {
return this.create(window.innerWidth, window.innerHeight);
} else if (document.documentElement) {
return this.create(document.documentElement.clientWidth,
document.documentElement.clientHeight);
} else if (document.body.clientHeight >= 0) {
return this.create(document.body.clientWidth,
document.body.clientHeight);
} else {
return this.create(0, 0);
}
},
//--------------------------------------------------------------------------
//
// Mouse coordinate relative to the window (technically the
// browser client area) i.e. the part showing your page
//
// NOTE: in Safari the coordinate is relative to the document
//
//--------------------------------------------------------------------------
mousePosition: function (event) {
event = ToolMan.getEventsFactory().efFix(event);
return this.create(event.clientX, event.clientY);
},
//--------------------------------------------------------------------------
//
// Mouse coordinate relative to the document
//
//--------------------------------------------------------------------------
getMouseOffset: function (event, touch_) {
var pos = tmEventPosFromEvent(event, touch_);
event = ToolMan.getEventsFactory().efFix(event);
if (pos[0] >= 0 || pos[0] < 0) {
return this.create(pos[0], pos[1]);
} else if (event.clientX >= 0 || event.clientX < 0) {
return this.mousePosition(event).plus(this.scrollOffset());
}
},
prv_size: function (element) {
// TODO: move to a Dimension class
return this.create(element.offsetWidth, element.offsetHeight);
},
prv_offset: function (element) {
return this.create(element.offsetLeft, element.offsetTop);
},
prv_scrollOffset: function (element) {
return this.create(element.scrollLeft, element.scrollTop);
}
};
ToolManCoordinate.prototype = {
toString: function () {
return "(" + this.x + "," + this.y + ")";
},
plus: function (that) {
return this.factory.create(this.x + that.x, this.y + that.y);
},
minus: function (that) {
return this.factory.create(this.x - that.x, this.y - that.y);
},
min: function (that) {
return this.factory.create(
Math.min(this.x, that.x), Math.min(this.y, that.y));
},
max: function (that) {
return this.factory.create(
Math.max(this.x, that.x), Math.max(this.y, that.y));
},
constrainTo: function (one, two) {
var min = one.min(two);
var max = one.max(two);
return this.max(min).min(max);
},
distance: function (that) {
return Math.sqrt(Math.pow(this.x - that.x, 2) + Math.pow(this.y - that.y, 2));
},
reposition: function (element) {
element.style.top = this.y + "px";
element.style.left = this.x + "px";
}
};
/* Below to the next notice are contents of drag.js */
/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */
function ToolManDragEvent(type, event, group, touch_) {
this.type = type;
this.group = group;
this.mousePosition = ToolMan.getCoordinatesFactory().mousePosition(event);
this.mouseOffset = ToolMan.getCoordinatesFactory().getMouseOffset(event, touch_);
this.transformedMouseOffset = this.mouseOffset;
this.topLeftPosition = ToolMan.getCoordinatesFactory().topLeftPosition(group.element);
this.topLeftOffset = ToolMan.getCoordinatesFactory().topLeftOffset(group.element);
}
ToolManDragEvent.prototype = {
toString: function () {
return "mouse: " + this.mousePosition + this.mouseOffset + " " +
"xmouse: " + this.transformedMouseOffset + " " +
"left,top: " + this.topLeftPosition + this.topLeftOffset;
}
};
function ToolManDragGroup(factory, element) {
this.factory = factory;
this.element = element;
this.prv_handle = null;
this.prv_thresholdDistance = 0;
this.prv_transforms = [];
// TODO: refactor into a helper object, move into events.js
this.prv_listeners = [];
this.prv_listeners.draginit = [];
this.prv_listeners.dragstart = [];
this.prv_listeners.dragmove = [];
this.prv_listeners.dragend = [];
}
ToolMan.prv_dragFactory = {
createSimpleGroup: function (element, handle) {
handle = handle ? handle : element;
var group = this.createGroup(element);
group.setHandle(handle);
group.transparentDrag();
group.onTopWhileDragging();
return group;
},
createGroup: function (element) {
var group = new ToolManDragGroup(this/*factory*/, element/*element*/);
var position = ToolMan.getCSSFactory().readStyle(element, 'position');
if (position === 'absolute') {
// For Safari 1.2
ToolMan.getCoordinatesFactory().topLeftOffset(element).reposition(element);
}
// TODO: only if ToolMan.isDebugging()
//group.register('draginit', this.prv_showDragEventStatus)
//group.register('dragmove', this.prv_showDragEventStatus)
//group.register('dragend', this.prv_showDragEventStatus)
return group;
},
prv_showDragEventStatus: function (dragEvent) {
window.status = dragEvent.toString();
},
constraints: function () {
return this.prv_constraintFactory;
},
prv_createEvent: function (type, event, group, touch_) {
return new ToolManDragEvent(type, event, group, touch_);
}
};
ToolManDragGroup.prototype = {
//--------------------------------------------------------------------------
//
// TODO:
// - unregister(type, func)
// - move custom event listener stuff into Event library
// - keyboard nudging of "selected" group
//
//--------------------------------------------------------------------------
setHandle: function (handle) {
var tmpEvents = ToolMan.getEventsFactory();
handle.toolManDragGroup = this;
tmpEvents.efRegister(handle, 'mousedown', this.prv_dragInitNoTouch);
tmpEvents.efRegister(handle, 'touchstart', this.prv_dragInitTouch);
handle.onmousedown = function () {
return false;
};
if (this.element !== handle) {
tmpEvents.efUnregister(this.element, 'mousedown', this.prv_dragInitNoTouch);
tmpEvents.efUnregister(this.element, 'touchstart', this.prv_dragInitTouch);
}
},
tmdgRegister: function (type, func) {
this.prv_listeners[type][this.prv_listeners[type].length] = func;
},
addTransform: function (transformFunc) {
this.prv_transforms[this.prv_transforms.length] = transformFunc;
},
verticalOnly: function () {
this.addTransform(this.factory.constraints().vertical());
},
horizontalOnly: function () {
this.addTransform(this.factory.constraints().horizontal());
},
setThreshold: function (thresholdDistance) {
this.prv_thresholdDistance = thresholdDistance;
},
transparentDrag: function (opacity) {
this.tmdgRegister('dragstart', function (dragEvent) {
var element = dragEvent.group.element;
if (element.className.indexOf('transparentDragClass') < 0) {
element.className = trim(element.className + ' transparentDragClass');
}
});
this.tmdgRegister('dragend', function (dragEvent) {
var element = dragEvent.group.element;
element.className = removeClass('' + element.className, 'transparentDragClass');
});
},
onTopWhileDragging: function (zIndex) {
zIndex = typeof zIndex !== "undefined" ? zIndex : 100000;
var originalZIndex = ToolMan.getCSSFactory().readStyle(this.element, "z-index");
this.tmdgRegister('dragstart', function (dragEvent) {
dragEvent.group.element.style.zIndex = zIndex;
});
this.tmdgRegister('dragend', function (dragEvent) {
if (typeof originalZIndex === "undefined") {
// Don't try to set the z-index to an undefined value.
} else {
dragEvent.group.element.style.zIndex = originalZIndex;
}
});
},
prv_dragInit: function (event, touch_) {
if (null === firstTarget) {
var doAllowIt = true,
group = this.toolManDragGroup;
var tmpEventTarget = toolMan_GetEventTarget(event);
// Notify any listeners that the move has ended...
group.signalEventToExternalListeners.call(
this,
ToolManEventTypes_Enum.tmetCartTouched, // eventTypeId_
{
eventTarget: tmpEventTarget
}); // eventParams_
if (null !== checkAttribute(this, "mjtfixedcartitem") && "1" === checkAttribute(this, "mjtfixedcartitem").getAttribute("mjtfixedcartitem")) {
doAllowIt = false;
} else {
doAllowIt =
tmpEventTarget &&
tmpEventTarget.className &&
tmpEventTarget.className.indexOf('boxListDragHandle') > -1 &&
!mwDDIsMarkedDragDropDisabled(tmpEventTarget);
}
if (doAllowIt) {
firstTarget = this;
if ("static" === ToolMan.getCSSFactory().readStyle(this, "position")) {
this.style.position = 'relative';
}
highlightGroupMembers(
this, // groupMember
true, // turnOnHighlight
!!mjtGetAncestorWithClass(this,'clsStyleDragDropByClass')); // styleByClass_
var tmpEvents=ToolMan.getEventsFactory();
event = tmpEvents.efFix(event);
document.toolManDragGroup = group;
var dragEvent = group.factory.prv_createEvent('draginit', event, group, touch_);
group.prv_isThresholdExceeded = false;
group.prv_initialMouseOffset = dragEvent.mouseOffset;
group.prv_grabOffset = dragEvent.mouseOffset.minus(dragEvent.topLeftOffset);
tmpEvents.efRegister(document, 'mousemove', group.prv_dragNoTouch);
tmpEvents.efRegister(document, 'touchmove', group.prv_dragTouch);
document.onmousemove = function () {
return false;
};
document.ontouchmove = function () {
return false;
};
tmpEvents.efRegister(document, 'mouseup', group.prv_dragEndNoTouch);
tmpEvents.efRegister(document, 'touchend', group.prv_dragEndTouch);
group.prv_notifyListeners(dragEvent);
}
}
},
prv_dragInitTouch: function (event) {
var rc = this.toolManDragGroup.prv_dragInit.call(this, event, 1/*touch_*/);
return rc;
},
prv_dragInitNoTouch: function (event) {
var rc = this.toolManDragGroup.prv_dragInit.call(this, event, 0/*touch_*/);
return rc;
},
prv_drag: function (event, touch_) {
event = ToolMan.getEventsFactory().efFix(event);
var tmpCoordinatesFactory = ToolMan.getCoordinatesFactory();
var group = this.toolManDragGroup;
if (!group) {
return;
}
var dragEvent = group.factory.prv_createEvent('dragmove', event, group, touch_);
var newTopLeftOffset = dragEvent.mouseOffset.minus(group.prv_grabOffset);
// TODO: replace with DragThreshold object
if (!group.prv_isThresholdExceeded) {
var distance = dragEvent.mouseOffset.distance(group.prv_initialMouseOffset);
if (distance < group.prv_thresholdDistance) {
return;
}
group.prv_isThresholdExceeded = true;
group.prv_notifyListeners(
group.factory.prv_createEvent('dragstart', event, group, touch_));
}
for (var i in group.prv_transforms) {
var transform = group.prv_transforms[i];
newTopLeftOffset = transform(newTopLeftOffset, dragEvent);
}
var dragDelta = newTopLeftOffset.minus(dragEvent.topLeftOffset);
var newTopLeftPosition = dragEvent.topLeftPosition.plus(dragDelta);
newTopLeftPosition.reposition(group.element);
dragEvent.transformedMouseOffset = newTopLeftOffset.plus(group.prv_grabOffset);
group.prv_notifyListeners(dragEvent);
var errorDelta = newTopLeftOffset.minus(tmpCoordinatesFactory.topLeftOffset(group.element));
if (errorDelta.x !== 0 || errorDelta.y !== 0) {
var adjustedPos = tmpCoordinatesFactory.topLeftPosition(group.element).plus(errorDelta);
adjustedPos.reposition(group.element);
}
},
prv_dragTouch: function (event) {
return this.toolManDragGroup.prv_drag.call(this, event, 1/*touch_*/);
},
prv_dragNoTouch: function (event) {
return this.toolManDragGroup.prv_drag.call(this, event, 0/*touch_*/);
},
prv_dragEnd: function (event, touch_) {
var tmpTarget = firstTarget,
tmpEvents = ToolMan.getEventsFactory();
firstTarget = null;
event = tmpEvents.efFix(event);
var group = this.toolManDragGroup;
var dragEvent = group.factory.prv_createEvent('dragend', event, group, touch_);
group.prv_notifyListeners(dragEvent);
// Notify any external listeners that the move has ended...
group.signalEventToExternalListeners.call(
this,
ToolManEventTypes_Enum.tmetMoveEnd, // eventTypeId_
{
movedElement: tmpTarget
}); // eventParams_
this.toolManDragGroup = null;
tmpEvents.efUnregister(document, 'mousemove', group.prv_dragNoTouch);
document.onmousemove = null;
tmpEvents.efUnregister(document, 'touchmove', group.prv_dragTouch);
document.ontouchmove = null;
tmpEvents.efUnregister(document, 'mouseup', group.prv_dragEndNoTouch);
tmpEvents.efUnregister(document, 'touchend', group.prv_dragEndTouch);
if (null !== tmpTarget) {
var tmpStyleByClass = !!mjtGetAncestorWithClass(tmpTarget, 'clsStyleDragDropByClass');
highlightGroupMembers(tmpTarget, true, tmpStyleByClass);
highlightGroupMembers(tmpTarget, false, tmpStyleByClass);
// Reset the position of the dragged item to static.
tmpTarget.style.position = 'static';
}
},
signalEventToExternalListeners:
function (eventTypeId_, eventParams_) {
var objToolManDragGroup = this.toolManDragGroup,
objListElem =
mjtGetAncestorWithTag(
objToolManDragGroup.element, // element_
'UL'); // tag_
if (objListElem) {
ToolMan.notifyCartListeners(
objListElem, // list_
eventTypeId_, // eventTypeId_
eventParams_); // eventParams_
}
},
prv_dragEndTouch: function (event) {
return this.toolManDragGroup.prv_dragEnd.call(this, event, 1/*touch_*/);
},
prv_dragEndNoTouch: function (event) {
return this.toolManDragGroup.prv_dragEnd.call(this, event, 0/*touch_*/);
},
prv_notifyListeners: function (dragEvent) {
var listeners = this.prv_listeners[dragEvent.type];
for (var i in listeners) {
listeners[i](dragEvent);
}
}
};
function myFuncX(item) {
item.toolManDragGroup.verticalOnly();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function highlightSingleElement(params_) {
var tmpStyleByClass = params_.styleByClass,
objTargetElement = params_.targetElement,
tmpTurnOnHighlight = params_.turnOnHighlight;
if (tmpStyleByClass) {
if (tmpTurnOnHighlight) {
objTargetElement.classList.add('clsDragging');
} else {
objTargetElement.classList.remove('clsDragging');
}
} else {
if (tmpTurnOnHighlight) {
objTargetElement.classList.add('dragSortTargetHighlight');
} else {
objTargetElement.classList.remove('dragSortTargetHighlight');
}
}
}
//------------------------------------------------------------------------------
//
// Toggles highlighting for members of a group.
//
// groupMember (Element): One potential member of a group
// turnOnHighlight (Boolean): true if the highlight should be turned on.
// styleByClass_ (Boolean): true to highlight with the list's own
// clsDragging class; false to use the generic
// dragSortTargetHighlight class
//
//------------------------------------------------------------------------------
function highlightGroupMembers(groupMember, turnOnHighlight, styleByClass_) {
// Ensure turnOnHighlight is actually a boolean so that
// strict comparisons with other booleans will be accurate.
turnOnHighlight = !!turnOnHighlight;
if (null !== groupMember) {
highlightSingleElement({
targetElement: groupMember,
turnOnHighlight: turnOnHighlight,
styleByClass: styleByClass_
});
var parentGroupId = groupMember.getAttribute("mjtparentgroupid");
var groupingCount = 0;
if (null !== parentGroupId) {
// Now, find all sibling and parent elements with the same group id.
var siblings;
var topList = null;
if (groupMember.parentNode.nodeName === "UL" && groupMember.parentNode.parentNode.nodeName === "LI") {
topList = groupMember.parentNode.parentNode.parentNode;
} else {
topList = groupMember.parentNode;
}
siblings = topList.getElementsByTagName("li");
var i = 0;
var sibling;
var tmpParentGroupId;
for (i = 0; i < siblings.length; ++i) {
sibling = siblings[i];
tmpParentGroupId = sibling.getAttribute("mjtparentgroupid");
if (null !== tmpParentGroupId && tmpParentGroupId === parentGroupId) {
++groupingCount;
highlightSingleElement({
targetElement: sibling,
turnOnHighlight: turnOnHighlight,
styleByClass: styleByClass_
});
}
}
// Now that the items are highlighted, make sure the headers and footers are added/removed based on turnOnHighlight.
for (i = 0; i < siblings.length; ++i) {
sibling = siblings[i];
tmpParentGroupId = sibling.getAttribute("mjtparentgroupid");
if (null !== tmpParentGroupId) {
// It's a groupable list item. Check if it's grouped or not...
var isGrouped =
null !== sibling.parentNode &&
null !== sibling.parentNode.getAttribute("mjtgroupingid");
if (isGrouped !== turnOnHighlight) {
// Either it's grouped and we're turning off highlighting, or it's not grouped and
// we're turning on highlighting. If we're turning off highlighting, we will still
// want to leave it grouped if there is more than one item grouped together.
if (turnOnHighlight && groupingCount > 1 && parentGroupId === tmpParentGroupId) {
var tmpOuterLI = document.createElement("LI");
tmpOuterLI.setAttribute("mjtgroupingid", tmpParentGroupId);
var tmpOldParent = sibling.parentNode;
tmpOldParent.insertBefore(tmpOuterLI, sibling);
tmpOldParent.removeChild(sibling);
tmpOuterLI.className = "boxListInnerItem";
// Create the new drag element by setting the inner html, rather than
// using the dom so that when the node is copied to/from the select
// subdialog, the touch-move handler will be copied as well.
tmpOuterLI.innerHTML =
'
' +
'
';
var tmpInnerUL = document.createElement("UL");
tmpInnerUL.setAttribute("mjtgroupingid", tmpParentGroupId);
tmpOuterLI.appendChild(tmpInnerUL);
tmpInnerUL.className = "innerList";
tmpInnerUL.appendChild(sibling);
ToolMan.dragsort().makeListSortable(tmpOuterLI.parentNode, myFuncX);
}
}
}
}
if (!turnOnHighlight) {
// Finally, remove any EMPTY header/footer pairs...
for (i = topList.childNodes.length - 1; i >= 0; --i) {
var tmpList = topList.childNodes[i];
if ("LI" === tmpList.nodeName && null !== tmpList.getAttribute("mjtgroupingid")) {
var subLists = tmpList.getElementsByTagName("UL");
if (null !== subLists && subLists.length > 0) {
var subList = subLists[0];
var subItems = subList.getElementsByTagName("LI");
if (null === subItems || 0 === subItems.length) {
tmpList.parentNode.removeChild(tmpList);
}
}
}
}
}
}
}
}
ToolMan.prv_dragFactory.prv_constraintFactory = {
vertical: function () {
return function (coordinate, dragEvent) {
var x = dragEvent.topLeftOffset.x;
return coordinate.x !== x ?
coordinate.factory.create(x, coordinate.y) :
coordinate;
};
},
horizontal: function () {
return function (coordinate, dragEvent) {
var y = dragEvent.topLeftOffset.y;
return coordinate.y !== y ?
coordinate.factory.create(coordinate.x, y) :
coordinate;
};
}
};
/* Below to the next notice are contents of dragsort.js */
/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */
ToolMan.prv_dragsortFactory = {
makeSortable: function (item) {
var group = ToolMan.getDragFactory().createSimpleGroup(item);
group.tmdgRegister('dragstart', this.prv_onDragStart);
group.tmdgRegister('dragmove', this.prv_onDragMove);
group.tmdgRegister('dragend', this.prv_onDragEnd);
return group;
},
//--------------------------------------------------------------------------
//
// Iterates over a list's items, making them sortable, applying
// optional functions to each item.
//
// example: makeListSortable(myList, myFunc1, myFunc2, ... , myFuncN)
//
//--------------------------------------------------------------------------
makeListSortable: function (list) {
var helpers = ToolMan.helpers(),
tmpCoordinatesFactory = ToolMan.getCoordinatesFactory(),
items = list.getElementsByTagName("li");
helpers.map(items, function (item) {
var dragGroup = ToolMan.dragsort().makeSortable(item);
dragGroup.setThreshold(4);
var min, max;
dragGroup.addTransform(function (coordinate, dragEvent) {
return coordinate;
});
dragGroup.tmdgRegister('dragstart', function () {
var items = list.getElementsByTagName("li");
min = max = tmpCoordinatesFactory.topLeftOffset(items[0]);
for (var i = 1, n = items.length; i < n; i++) {
var offset = tmpCoordinatesFactory.topLeftOffset(items[i]);
min = min.min(offset);
max = max.max(offset);
}
});
});
for (var i = 1, n = arguments.length; i < n; i++) {
helpers.map(items, arguments[i]);
}
},
//--------------------------------------------------------------------------
// Builds a two-level array representation of the ids of List Items in
// a potentially two-level list.
//
// Empty ids are not added to the list!
//
// list - an unordered list, whose list items may contain a nested
// unordered list.
//
// itemProcessingFunction - an optional function that will be applied
// to each item's id to extract the id to be
// in the resulting encoding. (This is used
// to strip the "cartrow_{cartNameSuffix_}" prefix
// that is usedin shopping carts.)
//
// cartNameSuffix_ - Suffix being used to distinguish this cart from
// any others that may be on the same page.
//
// treatAsFlatArray_ - Instead of a two-level array, just create a single level array.
//
//--------------------------------------------------------------------------
getNestedListStructureArray: function (list, itemProcessingFunction, cartNameSuffix_, treatAsFlatArray_) {
if (!cartNameSuffix_) {
cartNameSuffix_ = '';
}
var rc = [];
if (null !== list) {
var i = 0,
items = list.childNodes,
item,
subItem,
subRc,
subLists;
if (!itemProcessingFunction) {
itemProcessingFunction = function (item_) { return item_; };
}
for (i = 0; i < items.length; ++i) {
item = items[i];
if ("LI" === item.nodeName) {
subRc = [];
if (null === item.getAttribute("mjtgroupingid")) {
// It's just an item. Use it's id as the subRc...
subItem = itemProcessingFunction(item.id, cartNameSuffix_);
if (subItem.length) {
if (treatAsFlatArray_) {
rc.push(subItem);
} else {
subRc = [subItem];
}
}
} else {
// It's a grouping item, so iterate the child items...
subLists = item.getElementsByTagName("ul");
if (null !== subLists && subLists.length > 0) {
var subList = subLists[0],
j = 0,
subItems = subList.childNodes;
for (j = 0; j < subItems.length; ++j) {
subItem = subItems[j];
if ("LI" === subItem.nodeName) {
subItem = itemProcessingFunction(subItem.id, cartNameSuffix_);
if (subItem.length) {
if (treatAsFlatArray_) {
rc.push(subItem);
} else {
subRc.push(subItem);
}
}
}
}
}
}
if (subRc.length) {
rc.push(subRc);
}
}
}
}
return rc;
},
//--------------------------------------------------------------------------
//
// Builds a string representation of the ids of List Items in
// a potentially two-level list.
//
// It uses "," to delimit the first level list items' ids, and
// should any of those items themselves contain lists, those
// lists' items' ids will be delimited with semi-colons, ";".
//
// list - an unordered list, whose list items may contain a nested
// unordered list.
//
// itemProcessingFunction - an optional function that will be applied
// to each item's id to extract the id to be
// in the resulting encoding. (This is used
// to strip the "cartrow_{cartNameSuffix_}" prefix
// that is usedin shopping carts.)
//
// cartNameSuffix_ - Suffix being used to distinguish this cart from
// any others that may be on the same page.
//
//--------------------------------------------------------------------------
encodeNestedListStructure: function (list, itemProcessingFunction, cartNameSuffix_) {
var arrRc = this.getNestedListStructureArray(list, itemProcessingFunction, cartNameSuffix_),
arrInner,
rc = '',
i = 0,
j = 0,
outerListDelimiter = ",",
innerListDelimiter = ";";
for (i = 0; i < arrRc.length; ++i) {
arrInner = arrRc[i];
if (i) {
rc += outerListDelimiter;
}
for (j = 0; j < arrInner.length; ++j) {
if (j) {
rc += innerListDelimiter;
}
rc += arrInner[j];
}
}
return rc;
},
//--------------------------------------------------------------------------
//
// Determines the index of the given item in a flattened version of the
// encoded list description.
//
//--------------------------------------------------------------------------
flatIndexOfItem: function (encodedList, itemId) {
var rc = -1;
if (encodedList.length > 0) {
var colInd = 0,
outer = encodedList.split(",");
for (var i = 0; rc < 0 && i < outer.length; ++i) {
var inner = outer[i].split(";");
for (var j = 0; j < inner.length; ++j) {
if (inner[j] === itemId) {
rc = colInd;
break;
}
++colInd;
}
}
}
return rc;
},
prv_onDragStart: function (dragEvent) {
},
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
prv_scrollTimer: 0,
prv_clearScrollTimer: function () {
var objDragSort = ToolMan.dragsort();
if (objDragSort.prv_scrollTimer) {
clearTimeout(objDragSort.prv_scrollTimer);
objDragSort.prv_scrollTimer = 0;
}
},
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
prv_restartScrollTimer: function (dialogDiv_, objItemContainerUL_, xformedMousePos_) {
var objDragSort = ToolMan.dragsort();
objDragSort.prv_clearScrollTimer();
objDragSort.prv_scrollTimer =
setTimeout(
function () {
objDragSort.prv_scrollDialogIfNecessary(
dialogDiv_,
objItemContainerUL_,
xformedMousePos_);
},
10);
},
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
prv_scrollDialogIfNecessary: function (dialogDiv_, objItemContainerUL_, xformedMousePos_) {
var tmpOriginalScrollTop = dialogDiv_.scrollTop,
tmpScrollHeight = dialogDiv_.scrollHeight,
tmpDialogHeight = dialogDiv_.offsetHeight,
tmpDialogTop = posTop(dialogDiv_, 1, 1, 1),
tmpTrueTop = posTop(objItemContainerUL_, 1, 1, 0),
tmpULHeight = objItemContainerUL_.offsetHeight,
tmpMouseY = xformedMousePos_.y,
tmpScrollDelta = 0,
// The following values are somewhat arbitrary.
TOP_BOTTOM_THRESHOLD_PX = 10, // Distance from top/bottom beyond which to scroll faster
SCROLL_INCREMENT_SLOW_PX = 5,
SCROLL_INCREMENT_FAST_PX = 10;
if (tmpMouseY <= tmpDialogTop && tmpOriginalScrollTop) {
// The mouse is above the top of the dialog interior and there's room to scroll.
// We can scroll if the container is also above the interior top.
if (tmpTrueTop <= tmpDialogTop) {
if (tmpMouseY < tmpDialogTop - TOP_BOTTOM_THRESHOLD_PX) {
tmpScrollDelta = -SCROLL_INCREMENT_FAST_PX;
} else {
tmpScrollDelta = -SCROLL_INCREMENT_SLOW_PX;
}
}
} else if (tmpMouseY >= tmpDialogTop + tmpDialogHeight) {
// The mouse is under lower than the bottom of the dialog interior.
// We can scroll if the container is also under the interior bottom...
if (tmpTrueTop + tmpULHeight >= tmpDialogTop + tmpDialogHeight) {
if (tmpMouseY >= tmpDialogTop + tmpDialogHeight + TOP_BOTTOM_THRESHOLD_PX) {
tmpScrollDelta = SCROLL_INCREMENT_FAST_PX;
} else {
tmpScrollDelta = SCROLL_INCREMENT_SLOW_PX;
}
}
}
if (tmpScrollDelta) {
var tmpRawScrollTopToUse = tmpOriginalScrollTop + tmpScrollDelta,
tmpForcedPositiveScrollTop =
Math.max(
0,
tmpRawScrollTopToUse),
tmpMaxScrollTop =
tmpScrollHeight - tmpDialogHeight,
tmpScrollTopToUse =
Math.min(
tmpMaxScrollTop,
tmpForcedPositiveScrollTop);
if (tmpScrollTopToUse !== tmpOriginalScrollTop) {
dialogDiv_.scrollTop = tmpScrollTopToUse;
if (tmpScrollTopToUse === dialogDiv_.scrollTop) {
// Keep scrolling
ToolMan.dragsort().prv_restartScrollTimer(
dialogDiv_,
objItemContainerUL_,
xformedMousePos_);
}
}
}
},
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
prv_scrollDialogOfItemIfNecessary: function (xformedMousePos_, draggedItem_) {
if (draggedItem_) {
var objAncestorElem = draggedItem_,
objDialogDiv,
objItemContainerUL,
objClassList;
while (objAncestorElem && !objDialogDiv) {
objAncestorElem = objAncestorElem.parentElement;
if (objAncestorElem) {
objClassList = objAncestorElem.classList;
if (objClassList.contains('uiv2dialogContentScrolling')) {
// Found the scrolling parent.
objDialogDiv = objAncestorElem;
} else if (objClassList.contains('uiv2dialogContent')) {
// Found dialog content div, but it's not scrolling, so just halt.
break;
}
if (objAncestorElem.nodeName === "UL") {
objItemContainerUL = objAncestorElem;
}
}
}
if (objDialogDiv && objItemContainerUL) {
ToolMan.dragsort().prv_scrollDialogIfNecessary(
objDialogDiv,
objItemContainerUL,
xformedMousePos_);
}
}
},
prv_onDragMove: function (dragEvent) {
var objDragSort = ToolMan.dragsort();
objDragSort.prv_clearScrollTimer();
var didMove = false;
var helpers = ToolMan.helpers();
var tmpCoordinatesFactory = ToolMan.getCoordinatesFactory();
var item = dragEvent.group.element;
var xmouse = dragEvent.transformedMouseOffset;
var moveBefore = null;
var moveBeforeParent = null;
var parentgroupid = item.getAttribute("mjtparentgroupid");
var previous = helpers.previousItem(item, item.nodeName, parentgroupid);
var lastPrevious = null;
var bottomRight;
var topLeft;
while (null !== previous) {
bottomRight = tmpCoordinatesFactory.bottomRightOffset(previous);
topLeft = tmpCoordinatesFactory.topLeftOffset(previous);
if (xmouse.y < topLeft.y) {
// Keep looking back...the mouse is above "previous"
lastPrevious = previous;
previous = helpers.previousItem(previous, item.nodeName, parentgroupid);
} else if (xmouse.y > bottomRight.y) {
// The mouse is after "previous" so we definitely don't need to search further.
if (null !== lastPrevious) {
// If we've already looked at one before this one, it must be between the two, and hence
// before the previous one. However, we need to test if "previous" and "lastPrevious"
// have different parents before doing anything. Only if they have the same parent can
// we simply move the item before the last previous.
if (previous.parentNode === lastPrevious.parentNode) {
// It's between two items with the same parent.
moveBefore = lastPrevious;
moveBeforeParent = lastPrevious.parentNode;
} else {
// It's between two items with different parents.
if (xmouse.y < tmpCoordinatesFactory.bottomRightOffset(previous.parentNode.parentNode).y) {
// It's in the upper, outer list item
moveBefore = null;
moveBeforeParent = previous.parentNode;
} else {
// It's in the space between.
moveBefore = lastPrevious;
moveBeforeParent = lastPrevious.parentNode;
}
break;
}
} else {
// If this is the first item we've looked at, it may be way after this one, however,
// if the mouse is inside the parent, and "previous" doesn't have a next sibling,
// this one should go after it...
var hasSubsequentListSibling = false;
var tmpNext = previous;
while (null !== (tmpNext = tmpNext.nextSibling)) {
if ("LI" === tmpNext.nodeName) {
hasSubsequentListSibling = true;
break;
}
}
if (!hasSubsequentListSibling) {
// There is no list item sibling, so check if the mouse is inside the parent,
// in which case this item should be moved after "previous" within its parent.
var parentBottom = tmpCoordinatesFactory.bottomRightOffset(previous.parentNode);
if (xmouse.y < parentBottom.y) {
moveBefore = null;
moveBeforeParent = previous.parentNode;
}
}
}
break;
} else {
// The mouse is within the bounds of "previous"
// If "previous" is a grouping item, make the item the first within the previous group...
if (helpers.isGroupingItem(previous) && parentgroupid === previous.getAttribute("mjtgroupingid")) {
if (null !== lastPrevious) {
// There was a previous before this, so we do want it to be first in the group.
var subLists = previous.getElementsByTagName("UL");
if (null === subLists || subLists.length > 0) {
// No Sublists!
} else {
moveBeforeParent = subLists[0];
var subItems = moveBeforeParent.getElementsByTagName("LI");
if (null !== subItems && subItems.length > 0) {
moveBefore = subItems[0];
}
}
} else {
//xxx It's in a grouping item, but there was no previous. (Impossible!?)
}
} else {
moveBeforeParent = previous.parentNode;
if (xmouse.y > (topLeft.y + bottomRight.y) / 2) {
// If it's in the bottom half, put it immediately after "previous"...
moveBefore = previous.nextSibling;
} else {
// It's in the top half, so put it immediately before "previous"
moveBefore = previous;
}
}
break;
}
}
if (null === previous && null !== lastPrevious) {
// We've gone above the top item, so it should be set to the first item...
if (helpers.isGroupingItem(item.parentNode)) {
moveBeforeParent = item.parentNode.parentNode.parentNode;
} else {
moveBeforeParent = item.parentNode;
}
moveBefore = moveBeforeParent.getElementsByTagName("LI")[0];
}
var lastNext = null;
// Reaching this point, we know that the mouse is not in or before a previous item.
if (null === moveBeforeParent) {
// If we get here, it's not a previous item. Check if it's a next item...
var next = helpers.nextItem(item, item.nodeName, false);
while (null !== next) {
topLeft = tmpCoordinatesFactory.topLeftOffset(next);
bottomRight = tmpCoordinatesFactory.bottomRightOffset(next);
if (xmouse.y > bottomRight.y) {
// Keep looking forward...the mouse is after this item
lastNext = next;
next = helpers.nextItem(next, item.nodeName, false);
} else if (xmouse.y < topLeft.y) {
// The mouse is before "next" so we defintely don't need to search further.
if (null !== lastNext) {
// If we've already looked at one before this one, it must be between the two, and hence
// before this one. However, we need to test if "next" and "lastNext" have different
// parents before doing anything. Only if they have the same parent can we simply move
// the item before the "next".
if (next.parentNode === lastNext.parentNode) {
// It's between two items with the same parent.
moveBefore = next;
moveBeforeParent = next.parentNode;
}
}
break;
} else {
// The mouse is within the bounds of "next"
if (helpers.isGroupingItem(next) && parentgroupid === next.getAttribute("mjtgroupingid")) {
// The mouse is within a grouping item...
next = helpers.nextItem(next, item.nodeName, false);
} else {
// The mouse is within a non-grouping item. See if it's before or after it...
moveBeforeParent = next.parentNode;
if (xmouse.y < (topLeft.y + bottomRight.y) / 2) {
// Move it immediately before "next"
moveBefore = next;
} else {
// Move it immediately after "next"
moveBefore = next.nextSibling;
}
break;
}
}
}
if (null === moveBeforeParent && null === next && null !== lastNext) {
// We've gone below the last item, so the dragged item should be made the last item.
if (helpers.isGroupingItem(item.parentNode)) {
moveBeforeParent = item.parentNode.parentNode.parentNode;
} else {
moveBeforeParent = item.parentNode;
}
}
if (null === moveBeforeParent) {
// Not moving anything.
} else {
helpers.moveBefore(item, moveBefore, moveBeforeParent);
}
} else {
// Scan through previous resulted in a move.
}
if (null === moveBeforeParent && null !== lastNext) {
// Probably after everything...
moveBeforeParent = item.parentNode;
}
if (null !== moveBeforeParent) {
helpers.moveBefore(item, moveBefore, moveBeforeParent);
}
objDragSort.prv_scrollDialogOfItemIfNecessary(xmouse, item);
},
prv_onDragEnd: function (dragEvent) {
ToolMan.dragsort().prv_clearScrollTimer();
ToolMan.getCoordinatesFactory().create(0, 0).reposition(dragEvent.group.element);
}
};
// Miscellaneous declarations needed for stuff to work TO DO: get rid of these
var dragsort = ToolMan.dragsort();