dnd-tree-view
Version:
HTML5 tree view widget
1,073 lines (1,002 loc) • 41.1 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.TreeView = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// 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 objectCreate = Object.create || objectCreatePolyfill
var objectKeys = Object.keys || objectKeysPolyfill
var bind = Function.prototype.bind || functionBindPolyfill
function EventEmitter() {
if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) {
this._events = objectCreate(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
var defaultMaxListeners = 10;
var hasDefineProperty;
try {
var o = {};
if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 });
hasDefineProperty = o.x === 0;
} catch (err) { hasDefineProperty = false }
if (hasDefineProperty) {
Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true,
get: function() {
return defaultMaxListeners;
},
set: function(arg) {
// check whether the input is a positive number (whose value is zero or
// greater and not a NaN).
if (typeof arg !== 'number' || arg < 0 || arg !== arg)
throw new TypeError('"defaultMaxListeners" must be a positive number');
defaultMaxListeners = arg;
}
});
} else {
EventEmitter.defaultMaxListeners = defaultMaxListeners;
}
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || isNaN(n))
throw new TypeError('"n" argument must be a positive number');
this._maxListeners = n;
return this;
};
function $getMaxListeners(that) {
if (that._maxListeners === undefined)
return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return $getMaxListeners(this);
};
// These standalone emit* functions are used to optimize calling of event
// handlers for fast cases because emit() itself often has a variable number of
// arguments and can be deoptimized because of that. These functions always have
// the same number of arguments and thus do not get deoptimized, so the code
// inside them can execute faster.
function emitNone(handler, isFn, self) {
if (isFn)
handler.call(self);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self);
}
}
function emitOne(handler, isFn, self, arg1) {
if (isFn)
handler.call(self, arg1);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1);
}
}
function emitTwo(handler, isFn, self, arg1, arg2) {
if (isFn)
handler.call(self, arg1, arg2);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1, arg2);
}
}
function emitThree(handler, isFn, self, arg1, arg2, arg3) {
if (isFn)
handler.call(self, arg1, arg2, arg3);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1, arg2, arg3);
}
}
function emitMany(handler, isFn, self, args) {
if (isFn)
handler.apply(self, args);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].apply(self, args);
}
}
EventEmitter.prototype.emit = function emit(type) {
var er, handler, len, args, i, events;
var doError = (type === 'error');
events = this._events;
if (events)
doError = (doError && events.error == null);
else if (!doError)
return false;
// If there is no 'error' event listener then throw.
if (doError) {
if (arguments.length > 1)
er = arguments[1];
if (er instanceof Error) {
throw er; // Unhandled 'error' event
} else {
// At least give some kind of context to the user
var err = new Error('Unhandled "error" event. (' + er + ')');
err.context = er;
throw err;
}
return false;
}
handler = events[type];
if (!handler)
return false;
var isFn = typeof handler === 'function';
len = arguments.length;
switch (len) {
// fast cases
case 1:
emitNone(handler, isFn, this);
break;
case 2:
emitOne(handler, isFn, this, arguments[1]);
break;
case 3:
emitTwo(handler, isFn, this, arguments[1], arguments[2]);
break;
case 4:
emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
break;
// slower
default:
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
emitMany(handler, isFn, this, args);
}
return true;
};
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
events = target._events;
if (!events) {
events = target._events = objectCreate(null);
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (events.newListener) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
// Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = target._events;
}
existing = events[type];
}
if (!existing) {
// Optimize the case of one listener. Don't need the extra array object.
existing = events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
} else {
// If we've already got an array, just append.
if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
}
// Check for listener leak
if (!existing.warned) {
m = $getMaxListeners(target);
if (m && m > 0 && existing.length > m) {
existing.warned = true;
var w = new Error('Possible EventEmitter memory leak detected. ' +
existing.length + ' "' + String(type) + '" listeners ' +
'added. Use emitter.setMaxListeners() to ' +
'increase limit.');
w.name = 'MaxListenersExceededWarning';
w.emitter = target;
w.type = type;
w.count = existing.length;
if (typeof console === 'object' && console.warn) {
console.warn('%s: %s', w.name, w.message);
}
}
}
}
return target;
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener =
function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
function onceWrapper() {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
switch (arguments.length) {
case 0:
return this.listener.call(this.target);
case 1:
return this.listener.call(this.target, arguments[0]);
case 2:
return this.listener.call(this.target, arguments[0], arguments[1]);
case 3:
return this.listener.call(this.target, arguments[0], arguments[1],
arguments[2]);
default:
var args = new Array(arguments.length);
for (var i = 0; i < args.length; ++i)
args[i] = arguments[i];
this.listener.apply(this.target, args);
}
}
}
function _onceWrap(target, type, listener) {
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
var wrapped = bind.call(onceWrapper, state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
EventEmitter.prototype.once = function once(type, listener) {
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
this.on(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.prependOnceListener =
function prependOnceListener(type, listener) {
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
this.prependListener(type, _onceWrap(this, type, listener));
return this;
};
// Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
var list, events, position, i, originalListener;
if (typeof listener !== 'function')
throw new TypeError('"listener" argument must be a function');
events = this._events;
if (!events)
return this;
list = events[type];
if (!list)
return this;
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0)
this._events = objectCreate(null);
else {
delete events[type];
if (events.removeListener)
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
position = -1;
for (i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || list[i].listener === listener) {
originalListener = list[i].listener;
position = i;
break;
}
}
if (position < 0)
return this;
if (position === 0)
list.shift();
else
spliceOne(list, position);
if (list.length === 1)
events[type] = list[0];
if (events.removeListener)
this.emit('removeListener', type, originalListener || listener);
}
return this;
};
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
var listeners, events, i;
events = this._events;
if (!events)
return this;
// not listening for removeListener, no need to emit
if (!events.removeListener) {
if (arguments.length === 0) {
this._events = objectCreate(null);
this._eventsCount = 0;
} else if (events[type]) {
if (--this._eventsCount === 0)
this._events = objectCreate(null);
else
delete events[type];
}
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
var keys = objectKeys(events);
var key;
for (i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = objectCreate(null);
this._eventsCount = 0;
return this;
}
listeners = events[type];
if (typeof listeners === 'function') {
this.removeListener(type, listeners);
} else if (listeners) {
// LIFO order
for (i = listeners.length - 1; i >= 0; i--) {
this.removeListener(type, listeners[i]);
}
}
return this;
};
function _listeners(target, type, unwrap) {
var events = target._events;
if (!events)
return [];
var evlistener = events[type];
if (!evlistener)
return [];
if (typeof evlistener === 'function')
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}
EventEmitter.prototype.listeners = function listeners(type) {
return _listeners(this, type, true);
};
EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false);
};
EventEmitter.listenerCount = function(emitter, type) {
if (typeof emitter.listenerCount === 'function') {
return emitter.listenerCount(type);
} else {
return listenerCount.call(emitter, type);
}
};
EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
var events = this._events;
if (events) {
var evlistener = events[type];
if (typeof evlistener === 'function') {
return 1;
} else if (evlistener) {
return evlistener.length;
}
}
return 0;
}
EventEmitter.prototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
};
// About 1.5x faster than the two-arg version of Array#splice().
function spliceOne(list, index) {
for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
list[i] = list[k];
list.pop();
}
function arrayClone(arr, n) {
var copy = new Array(n);
for (var i = 0; i < n; ++i)
copy[i] = arr[i];
return copy;
}
function unwrapListeners(arr) {
var ret = new Array(arr.length);
for (var i = 0; i < ret.length; ++i) {
ret[i] = arr[i].listener || arr[i];
}
return ret;
}
function objectCreatePolyfill(proto) {
var F = function() {};
F.prototype = proto;
return new F;
}
function objectKeysPolyfill(obj) {
var keys = [];
for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
keys.push(k);
}
return k;
}
function functionBindPolyfill(context) {
var fn = this;
return function () {
return fn.apply(context, arguments);
};
}
},{}],2:[function(require,module,exports){
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var events_1 = require("events");
var TreeView = /** @class */ (function (_super) {
__extends(TreeView, _super);
function TreeView(container, options) {
var _this = _super.call(this) || this;
_this.onClick = function (event) {
// Toggle groups
var element = event.target;
if (element.className === "toggle") {
if (element.parentElement.tagName === "LI" && element.parentElement.classList.contains("group")) {
element.parentElement.classList.toggle("collapsed");
return;
}
}
// Update selection
if (element.tagName === "BUTTON" || element.tagName === "INPUT" || element.tagName === "SELECT")
return;
if (_this.updateSelection(event))
_this.emit("selectionChange");
};
_this.onDoubleClick = function (event) {
if (_this.selectedNodes.length !== 1)
return;
var element = event.target;
if (element.tagName === "BUTTON" || element.tagName === "INPUT" || element.tagName === "SELECT")
return;
if (element.className === "toggle")
return;
_this.emit("activate");
};
_this.onKeyDown = function (event) {
if (document.activeElement !== _this.treeRoot)
return;
if (_this.firstSelectedNode == null) {
// TODO: Remove once we have this.focusedNode
if (event.keyCode === 40) {
_this.addToSelection(_this.treeRoot.firstElementChild);
_this.emit("selectionChange");
event.preventDefault();
}
return;
}
switch (event.keyCode) {
case 38: // up
case 40: // down
_this.moveVertically(event.keyCode === 40 ? 1 : -1);
event.preventDefault();
break;
case 37: // left
case 39: // right
_this.moveHorizontally(event.keyCode === 39 ? 1 : -1);
event.preventDefault();
break;
case 13:
if (_this.selectedNodes.length !== 1)
return;
_this.emit("activate");
event.preventDefault();
break;
}
};
_this.moveHorizontally = function (offset) {
// TODO: this.focusedNode;
var node = _this.firstSelectedNode;
if (offset === -1) {
if (!node.classList.contains("group") || node.classList.contains("collapsed")) {
if (!node.parentElement.classList.contains("children"))
return;
node = node.parentElement.previousElementSibling;
}
else if (node.classList.contains("group")) {
node.classList.add("collapsed");
}
}
else {
if (node.classList.contains("group")) {
if (node.classList.contains("collapsed"))
node.classList.remove("collapsed");
else
node = node.nextSibling.firstChild;
}
}
if (node == null)
return;
_this.clearSelection();
_this.addToSelection(node);
_this.scrollIntoView(node);
_this.emit("selectionChange");
};
_this.onDragStart = function (event) {
var element = event.target;
if (element.tagName !== "LI")
return false;
if (!element.classList.contains("item") && !element.classList.contains("group"))
return false;
if (_this.selectedNodes.indexOf(element) === -1) {
_this.clearSelection();
_this.addToSelection(element);
_this.emit("selectionChange");
}
if (_this.dragStartCallback != null && !_this.dragStartCallback(event, element))
return false;
_this.isDraggingNodes = true;
return true;
};
_this.onDragEnd = function (event) {
_this.previousDropLocation = null;
_this.isDraggingNodes = false;
};
_this.onDragOver = function (event) {
var dropLocation = _this.getDropLocation(event);
// Prevent dropping onto null
if (dropLocation == null)
return false;
// If we're dragging nodes from the current tree view
// Prevent dropping into descendant
if (_this.isDraggingNodes) {
if (dropLocation.where === "inside" && _this.selectedNodes.indexOf(dropLocation.target) !== -1)
return false;
for (var _i = 0, _a = _this.selectedNodes; _i < _a.length; _i++) {
var selectedNode = _a[_i];
if (selectedNode.classList.contains("group") && selectedNode.nextSibling.contains(dropLocation.target))
return false;
}
}
_this.hasDraggedOverAfterLeaving = true;
if (_this.previousDropLocation == null || _this.previousDropLocation.where != dropLocation.where || _this.previousDropLocation.target != dropLocation.target) {
_this.clearDropClasses();
dropLocation.target.classList.add("drop-" + dropLocation.where);
}
_this.previousDropLocation = dropLocation;
event.preventDefault();
};
_this.onDragLeave = function (event) {
_this.hasDraggedOverAfterLeaving = false;
setTimeout(function () { if (!_this.hasDraggedOverAfterLeaving)
_this.clearDropClasses(); }, 300);
};
_this.onDrop = function (event) {
_this.previousDropLocation = null;
event.preventDefault();
var dropLocation = _this.getDropLocation(event);
if (dropLocation == null)
return;
_this.clearDropClasses();
if (!_this.isDraggingNodes) {
_this.dropCallback(event, dropLocation, null);
return false;
}
var children = _this.selectedNodes[0].parentElement.children;
var orderedNodes = [];
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (_this.selectedNodes.indexOf(child) !== -1)
orderedNodes.push(child);
}
var reparent = (_this.dropCallback != null) ? _this.dropCallback(event, dropLocation, orderedNodes) : true;
if (!reparent)
return;
var newParent;
var referenceElt;
switch (dropLocation.where) {
case "inside":
if (!dropLocation.target.classList.contains("group"))
return;
newParent = dropLocation.target.nextSibling;
referenceElt = null;
break;
case "below":
newParent = dropLocation.target.parentElement;
referenceElt = dropLocation.target.nextSibling;
if (referenceElt != null && referenceElt.tagName === "OL")
referenceElt = referenceElt.nextSibling;
break;
case "above":
newParent = dropLocation.target.parentElement;
referenceElt = dropLocation.target;
break;
}
var draggedChildren;
for (var _i = 0, orderedNodes_1 = orderedNodes; _i < orderedNodes_1.length; _i++) {
var selectedNode = orderedNodes_1[_i];
if (selectedNode.classList.contains("group")) {
draggedChildren = selectedNode.nextSibling;
draggedChildren.parentElement.removeChild(draggedChildren);
}
if (referenceElt === selectedNode) {
referenceElt = selectedNode.nextSibling;
}
selectedNode.parentElement.removeChild(selectedNode);
newParent.insertBefore(selectedNode, referenceElt);
referenceElt = selectedNode.nextSibling;
if (draggedChildren != null) {
newParent.insertBefore(draggedChildren, referenceElt);
referenceElt = draggedChildren.nextSibling;
}
}
};
if (options == null)
options = {};
_this.multipleSelection = (options.multipleSelection != null) ? options.multipleSelection : true;
_this.dragStartCallback = options.dragStartCallback;
_this.dropCallback = options.dropCallback;
_this.treeRoot = document.createElement("ol");
_this.treeRoot.tabIndex = 0;
_this.treeRoot.classList.add("tree");
container.appendChild(_this.treeRoot);
_this.selectedNodes = [];
_this.firstSelectedNode = null;
_this.treeRoot.addEventListener("click", _this.onClick);
_this.treeRoot.addEventListener("dblclick", _this.onDoubleClick);
_this.treeRoot.addEventListener("keydown", _this.onKeyDown);
container.addEventListener("keydown", function (event) {
if (event.keyCode === 37 || event.keyCode === 39)
event.preventDefault();
});
if (_this.dragStartCallback != null) {
_this.treeRoot.addEventListener("dragstart", _this.onDragStart);
_this.treeRoot.addEventListener("dragend", _this.onDragEnd);
}
if (_this.dropCallback != null) {
_this.treeRoot.addEventListener("dragover", _this.onDragOver);
_this.treeRoot.addEventListener("dragleave", _this.onDragLeave);
_this.treeRoot.addEventListener("drop", _this.onDrop);
}
return _this;
}
TreeView.prototype.clearSelection = function () {
for (var _i = 0, _a = this.selectedNodes; _i < _a.length; _i++) {
var selectedNode = _a[_i];
selectedNode.classList.remove("selected");
}
this.selectedNodes.length = 0;
this.firstSelectedNode = null;
};
TreeView.prototype.addToSelection = function (element) {
if (this.selectedNodes.indexOf(element) !== -1)
return;
this.selectedNodes.push(element);
element.classList.add("selected");
if (this.selectedNodes.length === 1)
this.firstSelectedNode = element;
};
TreeView.prototype.scrollIntoView = function (element) {
var ancestor = element.parentElement;
while (ancestor != null && ancestor.className === "children") {
ancestor.previousElementSibling.classList.remove("collapsed");
ancestor = ancestor.parentElement;
}
var elementRect = element.getBoundingClientRect();
var containerRect = this.treeRoot.parentElement.getBoundingClientRect();
if (elementRect.top < containerRect.top)
element.scrollIntoView(true);
else if (elementRect.bottom > containerRect.bottom)
element.scrollIntoView(false);
};
TreeView.prototype.clear = function () {
this.treeRoot.innerHTML = "";
this.selectedNodes.length = 0;
this.firstSelectedNode = null;
this.hasDraggedOverAfterLeaving = false;
this.isDraggingNodes = false;
};
TreeView.prototype.append = function (element, type, parentGroupElement) {
if (type !== "item" && type !== "group")
throw new Error("Invalid type");
var childrenElt;
var siblingsElt;
if (parentGroupElement != null) {
if (parentGroupElement.tagName !== "LI" || !parentGroupElement.classList.contains("group"))
throw new Error("Invalid parent group");
siblingsElt = parentGroupElement.nextSibling;
}
else {
siblingsElt = this.treeRoot;
}
if (!element.classList.contains(type)) {
element.classList.add(type);
if (this.dragStartCallback != null)
element.draggable = true;
if (type === "group") {
var toggleElt = document.createElement("div");
toggleElt.classList.add("toggle");
element.insertBefore(toggleElt, element.firstChild);
childrenElt = document.createElement("ol");
childrenElt.classList.add("children");
}
}
else if (type === "group") {
childrenElt = element.nextSibling;
}
siblingsElt.appendChild(element);
if (childrenElt != null)
siblingsElt.appendChild(childrenElt);
return element;
};
TreeView.prototype.insertBefore = function (element, type, referenceElement) {
if (type !== "item" && type !== "group")
throw new Error("Invalid type");
if (referenceElement == null)
throw new Error("A reference element is required");
if (referenceElement.tagName !== "LI")
throw new Error("Invalid reference element");
var childrenElt;
if (!element.classList.contains(type)) {
element.classList.add(type);
if (this.dragStartCallback != null)
element.draggable = true;
if (type === "group") {
var toggleElt = document.createElement("div");
toggleElt.classList.add("toggle");
element.insertBefore(toggleElt, element.firstChild);
childrenElt = document.createElement("ol");
childrenElt.classList.add("children");
}
}
else if (type === "group") {
childrenElt = element.nextSibling;
}
referenceElement.parentElement.insertBefore(element, referenceElement);
if (childrenElt != null)
referenceElement.parentElement.insertBefore(childrenElt, element.nextSibling);
return element;
};
TreeView.prototype.insertAt = function (element, type, index, parentElement) {
var referenceElt;
if (index != null) {
referenceElt =
(parentElement != null)
? parentElement.nextSibling.querySelector(":scope > li:nth-of-type(" + (index + 1) + ")")
: this.treeRoot.querySelector(":scope > li:nth-of-type(" + (index + 1) + ")");
}
if (referenceElt != null)
this.insertBefore(element, type, referenceElt);
else
this.append(element, type, parentElement);
};
TreeView.prototype.remove = function (element) {
var selectedIndex = this.selectedNodes.indexOf(element);
if (selectedIndex !== -1) {
element.classList.remove("selected");
this.selectedNodes.splice(selectedIndex, 1);
}
if (this.firstSelectedNode === element)
this.firstSelectedNode = this.selectedNodes[0];
if (element.classList.contains("group")) {
var childrenElement = element.nextSibling;
var removedSelectedNodes = [];
for (var _i = 0, _a = this.selectedNodes; _i < _a.length; _i++) {
var selectedNode = _a[_i];
if (childrenElement.contains(selectedNode)) {
removedSelectedNodes.push(selectedNode);
}
}
for (var _b = 0, removedSelectedNodes_1 = removedSelectedNodes; _b < removedSelectedNodes_1.length; _b++) {
var removedSelectedNode = removedSelectedNodes_1[_b];
removedSelectedNode.classList.remove("selected");
this.selectedNodes.splice(this.selectedNodes.indexOf(removedSelectedNode), 1);
if (this.firstSelectedNode === removedSelectedNode)
this.firstSelectedNode = this.selectedNodes[0];
}
element.parentElement.removeChild(childrenElement);
}
element.parentElement.removeChild(element);
};
// Returns whether the selection changed
TreeView.prototype.updateSelection = function (event) {
var selectionChanged = false;
if ((!this.multipleSelection || (!event.shiftKey && !event.ctrlKey)) && this.selectedNodes.length > 0) {
this.clearSelection();
selectionChanged = true;
}
var ancestorElement = event.target;
while (ancestorElement.tagName !== "LI" || (!ancestorElement.classList.contains("item") && !ancestorElement.classList.contains("group"))) {
if (ancestorElement === this.treeRoot)
return selectionChanged;
ancestorElement = ancestorElement.parentElement;
}
var element = ancestorElement;
if (this.selectedNodes.length > 0 && this.selectedNodes[0].parentElement !== element.parentElement) {
return selectionChanged;
}
if (this.multipleSelection && event.shiftKey && this.selectedNodes.length > 0) {
var startElement = this.firstSelectedNode;
var elements = [];
var inside = false;
for (var i = 0; i < element.parentElement.children.length; i++) {
var child = element.parentElement.children[i];
if (child === startElement || child === element) {
if (inside || startElement === element) {
elements.push(child);
break;
}
inside = true;
}
if (inside && child.tagName === "LI")
elements.push(child);
}
this.clearSelection();
this.selectedNodes = elements;
this.firstSelectedNode = startElement;
for (var _i = 0, _a = this.selectedNodes; _i < _a.length; _i++) {
var selectedNode = _a[_i];
selectedNode.classList.add("selected");
}
return true;
}
var index;
if (event.ctrlKey && (index = this.selectedNodes.indexOf(element)) !== -1) {
this.selectedNodes.splice(index, 1);
element.classList.remove("selected");
if (this.firstSelectedNode === element) {
this.firstSelectedNode = this.selectedNodes[0];
}
return true;
}
this.addToSelection(element);
return true;
};
TreeView.prototype.moveVertically = function (offset) {
// TODO: this.focusedNode;
var node = this.firstSelectedNode;
if (offset === -1) {
if (node.previousElementSibling != null) {
var target = node.previousElementSibling;
while (target.classList.contains("children")) {
if (!target.previousElementSibling.classList.contains("collapsed") && target.childElementCount > 0)
target = target.lastElementChild;
else
target = target.previousElementSibling;
}
node = target;
}
else if (node.parentElement.classList.contains("children"))
node = node.parentElement.previousElementSibling;
else
return;
}
else {
var walkUp = false;
if (node.classList.contains("group")) {
if (!node.classList.contains("collapsed") && node.nextElementSibling.childElementCount > 0)
node = node.nextElementSibling.firstElementChild;
else if (node.nextElementSibling.nextElementSibling != null)
node = node.nextElementSibling.nextElementSibling;
else
walkUp = true;
}
else {
if (node.nextElementSibling != null)
node = node.nextElementSibling;
else
walkUp = true;
}
if (walkUp) {
if (node.parentElement.classList.contains("children")) {
var target = node.parentElement;
while (target.nextElementSibling == null) {
target = target.parentElement;
if (!target.classList.contains("children"))
return;
}
node = target.nextElementSibling;
}
else
return;
}
}
if (node == null)
return;
this.clearSelection();
this.addToSelection(node);
this.scrollIntoView(node);
this.emit("selectionChange");
};
;
TreeView.prototype.getDropLocation = function (event) {
var element = event.target;
if (element.tagName === "OL" && element.classList.contains("children")) {
element = element.parentElement;
}
if (element === this.treeRoot) {
element = element.lastChild;
if (element == null)
return { target: this.treeRoot, where: "inside" };
if (element.tagName === "OL")
element = element.previousSibling;
return { target: element, where: "below" };
}
while (element.tagName !== "LI" || (!element.classList.contains("item") && !element.classList.contains("group"))) {
if (element === this.treeRoot)
return null;
element = element.parentElement;
}
var where = this.getInsertionPoint(element, event.pageY);
if (where === "below") {
if (element.classList.contains("item") && element.nextSibling != null && element.nextSibling.tagName === "LI") {
element = element.nextSibling;
where = "above";
}
else if (element.classList.contains("group") && element.nextSibling.nextSibling != null && element.nextSibling.nextSibling.tagName === "LI") {
element = element.nextSibling.nextSibling;
where = "above";
}
}
return { target: element, where: where };
};
TreeView.prototype.getInsertionPoint = function (element, y) {
var rect = element.getBoundingClientRect();
var offset = y - rect.top;
if (offset < rect.height / 4)
return "above";
if (offset > rect.height * 3 / 4)
return (element.classList.contains("group") && element.nextSibling.childElementCount > 0) ? "inside" : "below";
return element.classList.contains("item") ? "below" : "inside";
};
TreeView.prototype.clearDropClasses = function () {
var dropAbove = this.treeRoot.querySelector(".drop-above");
if (dropAbove != null)
dropAbove.classList.remove("drop-above");
var dropInside = this.treeRoot.querySelector(".drop-inside");
if (dropInside != null)
dropInside.classList.remove("drop-inside");
var dropBelow = this.treeRoot.querySelector(".drop-below");
if (dropBelow != null)
dropBelow.classList.remove("drop-below");
// For the rare case where we're dropping a foreign item into an empty tree view
this.treeRoot.classList.remove("drop-inside");
};
return TreeView;
}(events_1.EventEmitter));
module.exports = TreeView;
},{"events":1}]},{},[2])(2)
});