221 lines
5.9 KiB
JavaScript
221 lines
5.9 KiB
JavaScript
/*eslint no-loop-func:0*/
|
|
|
|
const CLICK_ATTRIBUTE_NAME = "_discourse_click_widget";
|
|
const DOUBLE_CLICK_ATTRIBUTE_NAME = "_discourse_double_click_widget";
|
|
const CLICK_OUTSIDE_ATTRIBUTE_NAME = "_discourse_click_outside_widget";
|
|
const MOUSE_DOWN_OUTSIDE_ATTRIBUTE_NAME =
|
|
"_discourse_mouse_down_outside_widget";
|
|
const KEY_UP_ATTRIBUTE_NAME = "_discourse_key_up_widget";
|
|
const KEY_DOWN_ATTRIBUTE_NAME = "_discourse_key_down_widget";
|
|
const DRAG_ATTRIBUTE_NAME = "_discourse_drag_widget";
|
|
const INPUT_ATTRIBUTE_NAME = "_discourse_input_widget";
|
|
const CHANGE_ATTRIBUTE_NAME = "_discourse_change_widget";
|
|
const MOUSE_DOWN_ATTRIBUTE_NAME = "_discourse_mouse_down_widget";
|
|
const MOUSE_UP_ATTRIBUTE_NAME = "_discourse_mouse_up_widget";
|
|
const MOUSE_MOVE_ATTRIBUTE_NAME = "_discourse_mouse_move_widget";
|
|
|
|
function buildHook(attributeName, setAttr) {
|
|
return class {
|
|
constructor(widget) {
|
|
this.widget = widget;
|
|
}
|
|
|
|
hook(node) {
|
|
if (setAttr) {
|
|
node.setAttribute(setAttr, true);
|
|
}
|
|
node[attributeName] = this.widget;
|
|
}
|
|
|
|
unhook(node) {
|
|
if (setAttr) {
|
|
node.removeAttribute(setAttr, true);
|
|
}
|
|
node[attributeName] = null;
|
|
}
|
|
};
|
|
}
|
|
|
|
export const WidgetClickHook = buildHook(CLICK_ATTRIBUTE_NAME);
|
|
export const WidgetDoubleClickHook = buildHook(DOUBLE_CLICK_ATTRIBUTE_NAME);
|
|
export const WidgetClickOutsideHook = buildHook(
|
|
CLICK_OUTSIDE_ATTRIBUTE_NAME,
|
|
"data-click-outside"
|
|
);
|
|
export const WidgetMouseDownOutsideHook = buildHook(
|
|
MOUSE_DOWN_OUTSIDE_ATTRIBUTE_NAME,
|
|
"data-mouse-down-outside"
|
|
);
|
|
export const WidgetKeyUpHook = buildHook(KEY_UP_ATTRIBUTE_NAME);
|
|
export const WidgetKeyDownHook = buildHook(KEY_DOWN_ATTRIBUTE_NAME);
|
|
export const WidgetDragHook = buildHook(DRAG_ATTRIBUTE_NAME);
|
|
export const WidgetInputHook = buildHook(INPUT_ATTRIBUTE_NAME);
|
|
export const WidgetChangeHook = buildHook(CHANGE_ATTRIBUTE_NAME);
|
|
export const WidgetMouseUpHook = buildHook(MOUSE_UP_ATTRIBUTE_NAME);
|
|
export const WidgetMouseDownHook = buildHook(MOUSE_DOWN_ATTRIBUTE_NAME);
|
|
export const WidgetMouseMoveHook = buildHook(MOUSE_MOVE_ATTRIBUTE_NAME);
|
|
|
|
function nodeCallback(node, attrName, cb, options = { rerender: true }) {
|
|
const { rerender } = options;
|
|
const widget = findWidget(node, attrName);
|
|
|
|
if (widget) {
|
|
if (rerender) {
|
|
widget.rerenderResult(() => cb(widget));
|
|
} else {
|
|
cb(widget);
|
|
}
|
|
}
|
|
}
|
|
|
|
function findWidget(node, attrName) {
|
|
while (node) {
|
|
const widget = node[attrName];
|
|
if (widget) {
|
|
return widget;
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
}
|
|
|
|
let _watchingDocument = false;
|
|
let _dragging;
|
|
|
|
const DRAG_NAME = "mousemove.discourse-widget-drag";
|
|
|
|
function cancelDrag(e) {
|
|
$("body").removeClass("widget-dragging");
|
|
$(document).off(DRAG_NAME);
|
|
|
|
// We leave the touchmove event cause touch needs it always bound on iOS
|
|
|
|
if (_dragging) {
|
|
if (_dragging.dragEnd) {
|
|
_dragging.dragEnd(e);
|
|
}
|
|
_dragging = null;
|
|
}
|
|
}
|
|
|
|
WidgetClickHook.setupDocumentCallback = function() {
|
|
if (_watchingDocument) {
|
|
return;
|
|
}
|
|
|
|
let widget;
|
|
let onDrag = dragE => {
|
|
const tt = dragE.targetTouches[0];
|
|
if (tt && widget) {
|
|
dragE.preventDefault();
|
|
dragE.stopPropagation();
|
|
widget.drag(tt);
|
|
}
|
|
};
|
|
|
|
document.addEventListener("touchmove", onDrag, {
|
|
passive: false,
|
|
capture: true
|
|
});
|
|
|
|
$(document).on(
|
|
"mousedown.discource-widget-drag, touchstart.discourse-widget-drag",
|
|
e => {
|
|
cancelDrag(e);
|
|
widget = findWidget(e.target, DRAG_ATTRIBUTE_NAME);
|
|
if (widget) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
_dragging = widget;
|
|
$("body").addClass("widget-dragging");
|
|
$(document).on(DRAG_NAME, dragE => {
|
|
if (widget) {
|
|
widget.drag(dragE);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
$(document).on(
|
|
"mouseup.discourse-widget-drag, touchend.discourse-widget-drag",
|
|
e => {
|
|
widget = null;
|
|
cancelDrag(e);
|
|
}
|
|
);
|
|
|
|
$(document).on("dblclick.discourse-widget", e => {
|
|
nodeCallback(e.target, DOUBLE_CLICK_ATTRIBUTE_NAME, w => w.doubleClick(e));
|
|
});
|
|
|
|
$(document).on("click.discourse-widget", e => {
|
|
nodeCallback(e.target, CLICK_ATTRIBUTE_NAME, w => w.click(e));
|
|
|
|
let node = e.target;
|
|
const $outside = $("[data-click-outside]");
|
|
$outside.each((i, outNode) => {
|
|
if (
|
|
outNode.contains(node) ||
|
|
(outNode === node && outNode.style.position === "absolute")
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const widget2 = outNode[CLICK_OUTSIDE_ATTRIBUTE_NAME];
|
|
if (widget2) {
|
|
widget2.clickOutside(e);
|
|
}
|
|
});
|
|
});
|
|
|
|
$(document).on("mousedown.discourse-widget", e => {
|
|
let node = e.target;
|
|
const $outside = $("[data-mouse-down-outside]");
|
|
$outside.each((i, outNode) => {
|
|
if (outNode.contains(node)) {
|
|
return;
|
|
}
|
|
const widget2 = outNode[MOUSE_DOWN_OUTSIDE_ATTRIBUTE_NAME];
|
|
if (widget2) {
|
|
widget2.mouseDownOutside(e);
|
|
}
|
|
});
|
|
});
|
|
|
|
$(document).on("keyup.discourse-widget", e => {
|
|
nodeCallback(e.target, KEY_UP_ATTRIBUTE_NAME, w => w.keyUp(e));
|
|
});
|
|
|
|
$(document).on("keydown.discourse-widget", e => {
|
|
nodeCallback(e.target, KEY_DOWN_ATTRIBUTE_NAME, w => w.keyDown(e));
|
|
});
|
|
|
|
$(document).on("input.discourse-widget", e => {
|
|
nodeCallback(e.target, INPUT_ATTRIBUTE_NAME, w => w.input(e), {
|
|
rerender: false
|
|
});
|
|
});
|
|
|
|
$(document).on("change.discourse-widget", e => {
|
|
nodeCallback(e.target, CHANGE_ATTRIBUTE_NAME, w => w.change(e), {
|
|
rerender: false
|
|
});
|
|
});
|
|
|
|
$(document).on("mousedown.discourse-widget", e => {
|
|
nodeCallback(e.target, MOUSE_DOWN_ATTRIBUTE_NAME, w => {
|
|
w.mouseDown(e);
|
|
});
|
|
});
|
|
|
|
$(document).on("mouseup.discourse-widget", e => {
|
|
nodeCallback(e.target, MOUSE_UP_ATTRIBUTE_NAME, w => w.mouseUp(e));
|
|
});
|
|
|
|
$(document).on("mousemove.discourse-widget", e => {
|
|
nodeCallback(e.target, MOUSE_MOVE_ATTRIBUTE_NAME, w => w.mouseMove(e));
|
|
});
|
|
|
|
_watchingDocument = true;
|
|
};
|