Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
import { AlloyComponent, Attachment, Behaviour, Boxes, Button, DragCoord, Dragging, DraggingTypes, GuiFactory, Memento, Unselecting } from '@ephox/alloy';
import { ClientRect } from '@ephox/dom-globals';
import { Arr, Cell, Option } from '@ephox/katamari';
import { PlatformDetection } from '@ephox/sand';
import { Compare, Css, Element, Position, Traverse } from '@ephox/sugar';
import Editor from 'tinymce/core/api/Editor';
const platform = PlatformDetection.detect();
const snapWidth = 40;
const snapOffset = snapWidth / 2;
// const insertDebugDiv = (left, top, width, height, color, clazz) => {
// const debugArea = Element.fromHtml(`<div class="${clazz}"></div>`);
// Css.setAll(debugArea, {
// 'left': left.toString() + 'px',
// 'top': top.toString() + 'px',
// 'background-color': color,
// 'position': 'absolute',
// 'width': width.toString() + 'px',
// 'height': height.toString() + 'px',
// 'opacity': '0.2'
// });
// Insert.append(Body.body(), debugArea);
export const initAndShow = (editor: Editor, e: EditorEvent, buildMenu: () => MenuItems, backstage: UiFactoryBackstage, contextmenu: AlloyComponent, isTriggeredByKeyboardEvent: boolean): void => {
const detection = PlatformDetection.detect();
const isiOS = detection.os.isiOS();
const isOSX = detection.os.isOSX();
const isAndroid = detection.os.isAndroid();
const open = () => {
const items = buildMenu();
show(editor, e, items, backstage, contextmenu, isTriggeredByKeyboardEvent);
};
// On iOS/iPadOS if we've long pressed on a ranged selection then we've already selected the content
// and just need to open the menu. Otherwise we need to wait for a selection change to occur as long
// press triggers a ranged selection on iOS.
if ((isOSX || isiOS) && !isTriggeredByKeyboardEvent) {
const openiOS = () => {
setupiOSOverrides(editor);
open();
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
import Editor from '../api/Editor';
import { normalizeNbspsInEditor } from './Nbsps';
import { PlatformDetection } from '@ephox/sand';
import { Throttler } from '@ephox/katamari';
const browser = PlatformDetection.detect().browser;
const setupIeInput = (editor: Editor) => {
// We need to delay this since the normalization should happen after typing a letter
// for example typing ab in a paragraph would otherwise result in a a b
const keypressThrotter = Throttler.first(() => {
// We only care about non composing inputs since moving the caret or modifying the text node will blow away the IME
if (!editor.composing) {
normalizeNbspsInEditor(editor);
}
}, 0);
if (browser.isIE()) {
// IE doesn't have the input event so we need to fake that with a keypress on IE keypress is only fired on alpha numeric keys
editor.on('keypress', (e) => {
keypressThrotter.throttle();
});
const setTabviewHeight = (tabview: Element, height: number) => {
// Set both height and flex-basis as some browsers don't support flex-basis. However don't set it on
// IE 11 since it incorrectly includes margins in the flex-basis calculations so it can't be relied on.
Css.set(tabview, 'height', height + 'px');
if (!PlatformDetection.detect().browser.isIE()) {
Css.set(tabview, 'flex-basis', height + 'px');
} else {
Css.remove(tabview, 'flex-basis');
}
};
const registerEditorEvents = function (editor: Editor, throttledStore) {
const browser = PlatformDetection.detect().browser;
if (browser.isIE()) {
registerFocusOut(editor);
} else {
registerMouseUp(editor, throttledStore);
}
editor.on('keyup NodeChange', function (e) {
if (!isManualNodeChange(e)) {
SelectionBookmark.store(editor);
}
});
};
const register = (editor: Editor, registryContextToolbars, sink, extras) => {
const isTouch = PlatformDetection.detect().deviceType.isTouch;
const contextbar = GuiFactory.build(
renderContextToolbar({
sink,
onEscape: () => {
editor.focus();
return Option.some(true);
}
})
);
const getBounds = () => ContextToolbarBounds.getContextToolbarBounds(editor);
const isRangeOverlapping = (aTop: number, aBottom: number, bTop: number, bBottom: number) => {
return Math.max(aTop, bTop) <= Math.min(aBottom, bBottom);
};
const renderUI = function () {
const targetNode = editor.getElement();
const cssUrls = CssUrls.derive(editor);
if (Settings.isSkinDisabled(editor) === false) {
editor.contentCSS.push(cssUrls.content);
DOMUtils.DOM.styleSheetLoader.load(cssUrls.ui, SkinLoaded.fireSkinLoaded(editor));
} else {
SkinLoaded.fireSkinLoaded(editor)();
}
const doScrollIntoView = function () {
editor.fire('ScrollIntoView');
};
const realm = PlatformDetection.detect().os.isAndroid() ? AndroidRealm(doScrollIntoView) : IosRealm(doScrollIntoView);
const original = Element.fromDom(targetNode);
Attachment.attachSystemAfter(original, realm.system());
const findFocusIn = function (elem) {
return Focus.search(elem).bind(function (focused) {
return realm.system().getByDom(focused).toOption();
});
};
const outerWindow = targetNode.ownerDocument.defaultView;
const orientation = Orientation.onChange(outerWindow, {
onChange () {
const alloy = realm.system();
alloy.broadcastOn([ TinyChannels.orientationChanged() ], { width: Orientation.getActualWidth(outerWindow) });
},
onReady: Fun.noop
});
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
import { KeyboardEvent } from '@ephox/dom-globals';
import { PlatformDetection } from '@ephox/sand';
import Editor from '../api/Editor';
import UndoManager from '../api/UndoManager';
import { EditorEvent } from '../api/util/EventDispatcher';
import VK from '../api/util/VK';
import InsertNewLine from '../newline/InsertNewLine';
const platform = PlatformDetection.detect();
const browser = platform.browser;
const isTouch = platform.deviceType.isTouch();
const isSafari = browser.isSafari();
const endTypingLevel = function (undoManager: UndoManager) {
if (undoManager.typing) {
undoManager.typing = false;
undoManager.add();
}
};
const handleEnterKeyEvent = function (editor: Editor, event: EditorEvent) {
if (event.isDefaultPrevented() || isSafari && isTouch) {
return;
}
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
import { document, Range } from '@ephox/dom-globals';
import { Option } from '@ephox/katamari';
import { PlatformDetection } from '@ephox/sand';
import { Compare, Element, Node, Text, Traverse, Selection } from '@ephox/sugar';
import Editor from '../api/Editor';
import NodeType from '../dom/NodeType';
const browser = PlatformDetection.detect().browser;
const clamp = function (offset, element) {
const max = Node.isText(element) ? Text.get(element).length : Traverse.children(element).length + 1;
if (offset > max) {
return max;
} else if (offset < 0) {
return 0;
}
return offset;
};
const normalizeRng = function (rng) {
return Selection.range(
rng.start(),
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
import { Toggling } from '@ephox/alloy';
import { Arr, Fun } from '@ephox/katamari';
import { PlatformDetection } from '@ephox/sand';
import { Compare, DomEvent, Element, Focus, Node, Traverse } from '@ephox/sugar';
import TappingEvent from '../../util/TappingEvent';
import { HTMLInputElement } from '@ephox/dom-globals';
const isAndroid6 = PlatformDetection.detect().os.version.major >= 6;
/*
`selectionchange` on the iframe document. If the selection is *ranged*, then we add the margin, because we
assume that the context menu has appeared. If it is collapsed, then the context menu shouldn't appear
(there is no selected text to format), so we reset the margin to `0px`. Note, when adding a margin,
we add `23px` --- this is most likely based on trial and error. We may need to work out how to get
this value properly.
2. `select` on the outer document. This will also need to add the margin if the selection is ranged within
an input or textarea
*/
const initEvents = function (editorApi, toolstrip, alloy) {
const tapping = TappingEvent.monitor(editorApi);
const outerDoc = Traverse.owner(toolstrip);