@ryusei/code
Version:
<div align="center"> <a href="https://code.ryuseijs.com"> <img alt="RyuseiCode" src="https://code.ryuseijs.com/images/svg/logo.svg" width="70"> </a>
2,065 lines (1,777 loc) • 378 kB
JavaScript
/*!
* RyuseiCode.js
* Version : 0.1.17
* License : MIT
* Copyright: 2021 Naotoshi Fujita
*/
'use strict';
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
Object.defineProperty(exports, '__esModule', {
value: true
});
/**
* The collection of i18n strings.
*
* @since 0.1.0
*/
var I18N$4 = {
copy: 'Copy',
cut: 'Cut',
paste: 'Paste',
selectAll: 'Select All',
close: 'Close',
confirm: 'OK',
activate: 'Activate',
notice: 'Notice',
cancel: 'Cancel',
failedToCopy: 'Can not copy on your environment.',
scrollbar: 'Drag to Scroll',
inputLabel: 'Edit contents',
location: 'Line: %s, Column: %s'
};
/**
* Icon settings as [ path, stroke?, linecap? ].
*
* @since 0.1.0
*/
var ICONS$1 = {
arrowUp: ['m18.6 10.3c-6.59-6.85-6.59-6.85-6.59-6.85m-6.59 6.85 6.59-6.85m0 17v-17', 3],
arrowDown: ['m5.41 13.7 6.59 6.85m6.59-6.85c-6.59 6.85-6.59 6.85-6.59 6.85m0-17v17', 3],
close: ['m19 18-14-13m0 13 14-13', 3]
};
/**
* The map for kay bindings (`[ key, ctrl, shift, alt ]`).
*
* @since 0.1.0
*/
var KEYMAP$6 = {
selectAll: ['A', true]
};
/**
* The collection of modifier keys.
*
* @since 0.1.0
*/
var MODIFIER_KEYS = {
"default": ['Ctrl', 'Shift', 'Alt'],
mac: ['⌘', '⇧', '⌥']
};
/**
* Default values for the editor options.
*
* @since 0.1.0
*/
var DEFAULT_OPTIONS$6 = {
language: 'javascript',
placeholder: 'Enter code here…',
minWidth: '200px',
maxWidth: '100%',
minHeight: '16em',
maxHeight: '40em',
indent: ' ',
tabSize: 2,
tabIndex: 0,
keymap: KEYMAP$6,
maxInitialLines: 200,
icons: ICONS$1,
i18n: I18N$4
};
/**
* Checks if the array includes the value or not.
* `Array#includes` is not supported by IE.
*
* @param array - An array.
* @param value - A value to search for.
*
* @return `true` if the array includes the value, or otherwise `false`.
*/
function includes(array, value) {
return array.indexOf(value) > -1;
}
/**
* Checks if the given subject is an object or not.
*
* @param subject - A subject to check.
*
* @return `true` if the subject is an object, or otherwise `false`.
*/
function isObject$1(subject) {
return subject !== null && typeof subject === 'object';
}
/**
* Checks if the given subject is an array or not.
*
* @param subject - A subject to check.
*
* @return `true` if the subject is an array, or otherwise `false`.
*/
function isArray(subject) {
return Array.isArray(subject);
}
/**
* Checks if the given subject is a function or not.
*
* @param subject - A subject to check.
*
* @return `true` if the subject is a function, or otherwise `false`.
*/
function isFunction(subject) {
return typeof subject === 'function';
}
/**
* Checks if the given subject is a string or not.
*
* @param subject - A subject to check.
*
* @return `true` if the subject is a string, or otherwise `false`.
*/
function isString(subject) {
return typeof subject === 'string';
}
/**
* Checks if the given subject is `undefined` or not.
*
* @param subject - A subject to check.
*
* @return `true` if the subject is `undefined`, or otherwise `false`.
*/
function isUndefined$1(subject) {
return typeof subject === 'undefined';
}
/**
* Checks if the given subject is a Text node or not.
*
* @param subject - A subject to check.
*
* @return `true` if the subject is a Text node, or otherwise `false`.
*/
function isText(subject) {
return subject instanceof Text;
}
/**
* Checks if the given subject is a HTMLElement instance or not.
*
* @param subject - A subject to check.
*
* @return `true` if the subject is a HTMLElement instance, or otherwise `false`.
*/
function isHTMLElement(subject) {
return subject instanceof HTMLElement;
}
/**
* Checks if the given subject is a BR element or not.
*
* @param subject - A subject to check.
*
* @return `true` if the subject is a BR element, or otherwise `false`.
*/
function isBr(subject) {
return subject instanceof HTMLBRElement;
}
/**
* Push the provided value to an array if the value is not an array.
*
* @param value - A value to push.
* @param nest - Optional. Whether to push the value to an array if the value is already an array.
*
* @return An array containing the value, or the value itself if it is already an array.
* If the `nest` is `true` and the first child of the array is not an array,
* this returns an array with the provided array.
*/
function toArray(value, nest) {
if (nest === void 0) {
nest = false;
}
if (isArray(value)) {
if (nest && !isArray(value[0])) {
return [value];
}
return value;
}
return [value];
}
var arrayProto = Array.prototype;
/**
* The slice method for an array-like object.
*
* @param arrayLike - An array-like object.
* @param start - Optional. A start index.
* @param end - Optional. A end index.
*
* @return An array with sliced elements.
*/
function slice(arrayLike, start, end) {
return arrayProto.slice.call(arrayLike, start, end);
}
/**
* The splice method for an array-like object.
*
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
*
* @param arrayLike - An array-like object.
* @param start - A start index.
* @param deleteCount - Optional. A number of elements to remove from the `start` index.
* @param args - Optional. Any number of items to add.
*
* @return An array with deleted items.
*/
function _splice(arrayLike, start, deleteCount) {
var _arrayProto$splice;
for (var _len2 = arguments.length, args = new Array(_len2 > 3 ? _len2 - 3 : 0), _key2 = 3; _key2 < _len2; _key2++) {
args[_key2 - 3] = arguments[_key2];
}
return (_arrayProto$splice = arrayProto.splice).call.apply(_arrayProto$splice, [arrayLike, start, deleteCount].concat(args));
}
/**
* Returns the active element.
* This is just an alias of `document.activeElement`.
*
* @return An active element.
*/
function activeElement() {
return document.activeElement;
}
/**
* Toggles the provided class or classes by following the `add` boolean.
*
* @param elm - An element whose classes are toggled.
* @param classes - A class or class names.
* @param add - Whether to add or remove a class.
*/
function _toggleClass(elm, classes, add) {
if (elm) {
toArray(classes).forEach(function (name) {
if (name) {
elm.classList[add ? 'add' : 'remove'](name);
}
});
}
}
/**
* Adds classes to the element.
*
* @param elm - An element to add classes to.
* @param classes - Classes to add.
*/
function addClass(elm, classes) {
_toggleClass(elm, classes, true);
}
/**
* Appends children to the parent element.
*
* @param parent - A parent element.
* @param children - A child or children to append to the parent.
*/
function _append(parent, children) {
toArray(children).forEach(parent.appendChild.bind(parent));
}
/**
* Iterates over the provided object by own enumerable keys with calling the iteratee function.
*
* @param object - An object to iterate over.
* @param iteratee - An iteratee function that takes the value and key as arguments.
*
* @return A provided object itself.
*/
function forOwn$1(object, iteratee) {
if (object) {
var keys = Object.keys(object);
for (var i = 0; i < keys.length; i++) {
if (iteratee(object[keys[i]], keys[i]) === false) {
break;
}
}
}
return object;
}
/**
* Assigns all own enumerable properties of all source objects to the provided object.
* `undefined` in source objects will be skipped.
*
* @param object - An object to assign properties to.
* @param sources - Objects to assign properties from.
*
* @return An object assigned properties of the sources to.
*/
function assign$1(object) {
for (var _len3 = arguments.length, sources = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
sources[_key3 - 1] = arguments[_key3];
}
sources.forEach(function (source) {
if (isObject$1(source)) {
forOwn$1(source, function (value, key) {
if (!isUndefined$1(source[key])) {
object[key] = source[key];
}
});
}
});
return object;
}
/**
* Sets new attributes to the passed element if the `attrs` is an object literal,
* or gets an attribute value from it if the `attrs` is a string.
*
* @param elm - An element to set or get an attribute.
* @param attrs - An attribute name as a string or new attributes as an object literal.
*/
function attr(elm, attrs) {
if (elm) {
if (isString(attrs)) {
return elm.getAttribute(attrs) || '';
}
if (isObject$1(attrs)) {
forOwn$1(attrs, function (value, key) {
if (value === null) {
elm.removeAttribute(key);
} else {
elm.setAttribute(key, String(value));
}
});
}
}
}
/**
* Inserts a node or nodes before the specified reference node.
*
* @param nodes - A node or nodes to insert.
* @param ref - A reference node.
*/
function before$1(nodes, ref) {
toArray(nodes).forEach(function (node) {
if (node) {
var parent = node.parentNode || ref && ref.parentNode;
if (parent) {
parent.insertBefore(node, ref);
}
}
});
}
/**
* Checks if the element matches the provided selector, or passes the predicate function.
*
* @since 0.1.0
*
* @param elm - An element to test.
* @param selector - A selector string to match.
*
* @return `true` if the element matches the selector.
*/
function matches(elm, selector) {
Element.prototype.matches = Element.prototype.matches || Element.prototype.msMatchesSelector;
return elm.matches(selector);
}
/**
* With starting at the given element,
* finds the closest parent element that matches the selector.
*
* @since 0.1.0
*
* @param elm - A start element.
* @param selector - A selector to search for.
*
* @return The closest element if found, or `null` if not.
*
* @throws TypeError
*/
function closest(elm, selector) {
if (isFunction(elm.closest)) {
return elm.closest(selector);
}
while (elm) {
if (matches(elm, selector)) {
return elm;
}
elm = elm.parentElement;
}
return null;
}
/**
* Creates a HTML element.
*
* @param tag - A tag name.
* @param attrs - Optional. An object with attributes to apply the created element to, or a string with classes.
* @param parent - Optional. A parent element where the created element is appended.
*/
function _create(tag, attrs, parent) {
var elm = document.createElement(tag);
if (attrs) {
if (isString(attrs) || isArray(attrs)) {
addClass(elm, attrs);
} else {
attr(elm, attrs);
}
}
if (parent) {
_append(parent, elm);
}
return elm;
}
/**
* The `create` function whose tag argument is fixed to `div`.
*
* @param attrs - Optional. An object with attributes to apply the created element to, or a string with classes.
* @param parent - Optional. A parent element where the created element is appended.
*/
function div(attrs, parent) {
return _create('div', attrs, parent);
}
/**
* Focuses the provided element without scrolling the ascendant element.
*
* @param elm - An element to focus.
*/
function _focus5(elm) {
if (isFunction(elm['setActive'])) {
elm['setActive']();
} else {
elm.focus({
preventScroll: true
});
}
}
/**
* Checks if the element contains the specified class or not.
*
* @param elm - An element to check.
* @param className - A class name that may be contained by the element.
*
* @return `true` if the element contains the class, or otherwise `false`.
*/
function hasClass(elm, className) {
return elm && elm.classList.contains(className);
}
/**
* Returns client height of the element.
*
* @param elm - An element to get height.
*/
function height(elm) {
return elm.clientHeight;
}
/**
* Sets or gets HTML of the provided element.
*
* @param elm - A element to get or set HTML.
* @param html - Optional. HTML to set.
*/
function html$2(elm, html) {
if (elm) {
if (isUndefined$1(html)) {
return elm.innerHTML;
}
if (elm.innerHTML !== html) {
elm.innerHTML = html;
}
}
}
/**
* Checks if the default action of the event is prevented or not.
*
* @param e - An Event object.
*
* @return `true` if the default action is prevented, or otherwise `false`.
*/
function isPrevented(e) {
return e && e.defaultPrevented;
}
/**
* Joins the provided object as a single line for DOM attributes.
*
* @param attrs - An object literal for attributes.
*
* @return A single string containing all attributes.
*/
function joinAttrs(attrs) {
var result = '';
forOwn$1(attrs, function (value, prop) {
if (prop && (value || value === false || value === 0)) {
result += " " + prop + "=\"" + value + "\"";
}
});
return result.trim();
}
/**
* Stores registered handlers which has a key.
*
* @since 0.1.0
*/
var handlerMap = new WeakMap();
/**
* Attaches a handler to the event.
*
* @param elm - An element, a window or a document.
* @param events - An event name or names.
* @param callback - A handler to attach.
* @param key - Optional. The key for identifying the registered handler.
*/
function on(elm, events, callback, key) {
events.split(' ').forEach(function (event) {
elm.addEventListener(event, callback);
if (key) {
var handlers = handlerMap.get(key) || [];
handlers.push({
elm: elm,
events: events,
callback: callback
});
handlerMap.set(key, handlers);
}
});
}
/**
* Detaches a handler from the event or events.
*
* @param elm - An element where events are removed.
* @param events - Optional. An event name or names.
* @param callbackOrKey - Optional. A handler to remove or an object key.
*/
function off(elm, events, callbackOrKey) {
if (isFunction(callbackOrKey)) {
events.split(' ').forEach(function (event) {
elm.removeEventListener(event, callbackOrKey);
});
} else {
var handlers = handlerMap.get(callbackOrKey);
if (handlers) {
handlers.forEach(function (handler) {
off(handler.elm, handler.events, handler.callback);
});
handlerMap["delete"](callbackOrKey);
}
}
}
/**
* Prepends children to the specified parent node.
*
* @param parent - A parent node.
* @param children - A child or children to prepend to the parent.
*/
function prepend(parent, children) {
toArray(children).forEach(function (child) {
parent.insertBefore(child, parent.firstChild);
});
}
/**
* Call the `preventDefault()` of the provided event.
*
* @param e - An Event object.
* @param stopPropagation - Optional. Whether to stop the event propergation or not.
*/
function prevent(e, stopPropagation) {
if (e) {
if (e.cancelable) {
e.preventDefault();
}
if (stopPropagation) {
e.stopPropagation();
}
}
}
/**
* Returns an element that matches the provided selector.
*
* @param parent - A parent element to start searching from.
* @param selector - A selector to query.
*
* @return A found element or `null`.
*/
function query(parent, selector) {
return parent.querySelector(selector);
}
/**
* Returns elements that match the provided selector.
*
* @param parent - A parent element to start searching from.
* @param selector - A selector to query.
*
* @return The NodeList object that contains matched elements.
*/
function queryAll(parent, selector) {
return parent.querySelectorAll(selector);
}
/**
* Returns a DOMRect object of the provided element or the selection range.
*
* @param target - An element or a range instance.
*/
function rect(target) {
return target.getBoundingClientRect();
}
/**
* Removes the provided node from its parent.
*
* @param nodes - A node or nodes to remove.
*/
function _remove(nodes) {
toArray(nodes).forEach(function (node) {
if (node && node.parentNode) {
node.parentNode.removeChild(node);
}
});
}
/**
* Removes classes from the element.
*
* @param elm - An element to remove classes from.
* @param classes - Classes to remove.
*/
function removeClass(elm, classes) {
_toggleClass(elm, classes, false);
}
/**
* Applies inline styles to the provided element by an object literal.
*
* @param elm - An element to apply styles to.
* @param styles - An object literal with styles.
*/
function styles(elm, styles) {
if (isString(styles)) {
return getComputedStyle(elm)[styles];
}
forOwn$1(styles, function (value, key) {
if (!isUndefined$1(value)) {
elm.style[key] = String(value);
}
});
}
/**
* Returns an open tag with provided classes.
*
* @param classes - Classes.
* @param attrs - Optional. An object with attributes.
* @param tag - Optional. A tag name.
*/
function tag(classes, attrs, tag) {
if (attrs === void 0) {
attrs = {};
}
return "<" + (tag || 'div') + " " + joinAttrs(assign$1(attrs, {
"class": toArray(classes).filter(Boolean).join(' ')
})) + ">";
}
/**
* Sets or gets a text content of the provided node.
*
* @param node - A node to get or set a text.
* @param text - Optional. A text to set.
*/
function text(node, text) {
if (node) {
if (isUndefined$1(text)) {
return node.textContent;
}
node.textContent = text;
}
}
/**
* Appends `px` to the value.
* If the value is already string, just returns it.
*
* @param value - A value to append `px` to.
*/
function unit(value) {
return isString(value) ? value : value + "px";
}
/**
* Checks if the client is Android or not.
*
* @return `true` if the client is Android, or otherwise `false`.
*/
function isAndroid() {
return /android/i.test(navigator.userAgent);
}
/**
* Checks is the browser is based on the Gecko engine or not.
*
* @return `true` if the browser is the browser is based on the Gecko (Firefox), or otherwise `false`.
*/
function isGecko() {
return !!window['InstallTrigger'];
}
/**
* Checks if the client is iOS or not.
*
* @return `true` if the client is iOS, or otherwise `false`.
*/
function isIOS() {
var _navigator = navigator,
userAgent = _navigator.userAgent;
return /iPad|iPhone|iPod/.test(userAgent) || userAgent.indexOf('Mac') > -1 && navigator.maxTouchPoints > 1;
}
/**
* Checks is the browser is IE or not.
*
* @return `true` if the browser is IE, or otherwise `false`.
*/
function isIE() {
return (
/*@cc_on!@*/
!!document['documentMode']
);
}
/**
* Checks is the platform is Mac or not.
*
* @return `true` if the platform is Mac, or otherwise `false`.
*/
function isMac() {
return /Mac/i.test(navigator.platform);
}
/**
* Checks if the device is likely mobile or not.
*
* @return `true` if the device is likely mobile, or otherwise `false`.
*/
function isMobile() {
return isAndroid() || isIOS();
}
/**
* The project code.
*
* @since 0.1.0
*/
var PROJECT_CODE = 'ryuseicode';
/**
* The abbreviated project code.
*
* @since 0.1.0
*/
var PROJECT_CODE_SHORT = 'rc';
/**
* Throws an error if the provided condition is falsy.
*
* @param condition - If falsy, an error is thrown.
* @param message - Optional. A message to display.
*/
function assert$1(condition, message) {
if (message === void 0) {
message = '';
}
if (!condition) {
throw new Error("[" + PROJECT_CODE + "] " + message);
}
}
/**
* Returns a function that invokes the provided function at most once in the specified duration.
*
* @since 0.1.0
*
* @param func - A function to throttle.
* @param interval - A throttle duration in milliseconds.
* @param initialCall - Optional. Determines whether to call the function initially.
* @param debounce - Optional. If `true`, the function returns a debounced function instead of throttled one.
* @param raf - Optional. Determines whether to use the `requestAnimationFrame` or not.
*
* @return A throttled function.
*/
function throttle(func, interval, initialCall, debounce, raf) {
var id;
var invoker;
function throttled() {
if (debounce) {
cancel();
}
for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
args[_key4] = arguments[_key4];
}
invoker = invoke.bind.apply(invoke, [this].concat(args));
if (!id) {
if (isUndefined$1(id) && initialCall) {
invoker();
} else {
id = raf ? requestAnimationFrame(invoker) : setTimeout(invoker, interval);
}
}
}
function invoke() {
for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
args[_key5] = arguments[_key5];
}
func.apply(this, args);
cancel();
}
function cancel() {
raf ? cancelAnimationFrame(id) : clearTimeout(id);
id = null;
}
throttled.cancel = cancel;
throttled.invoke = function () {
if (id) {
invoker();
}
};
return throttled;
}
/**
* Returns a debounced function that invokes the provided function only after the internal timer expires.
* The timer is reset whenever the debounced function is called.
*
* @param func - A callback function.
* @param duration - Debounce duration in milliseconds.
*
* @return A debounced function.
*/
function debounce(func, duration) {
return throttle(func, duration, false, true);
}
/**
* Fires the provided function on the next tick.
*
* @param func - A function to call.
*/
function nextTick(func) {
setTimeout(func);
}
/**
* Implements the `throttle` function via requestAnimationFrame.
*
* @param func - A function to throttle.
* @param initialCall - Optional. Determines whether to call the function initially.
*
* @return A throttled function.
*/
function rafThrottle(func, initialCall) {
return throttle(func, 0, initialCall, false, true);
}
/**
* The collection of forward arrow keys.
*
* @private
* @since 0.1.0
*/
var ARROW_FORWARD = ['ArrowDown', 'ArrowRight'];
/**
* The collection of backward arrow keys.
*
* @private
* @since 0.1.0
*/
var ARROW_BACKWARD = ['ArrowUp', 'ArrowLeft'];
/**
* The collection of all arrow keys.
*
* @private
* @since 0.1.0
*/
var ARROW_KEYS = [].concat(ARROW_FORWARD, ARROW_BACKWARD);
/**
* The map for normalizing differences of keys in browsers.
*
* @link https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
*
* @private
* @since 0.1.0
*/
var NORMALIZATION_MAP = {
Up: 'ArrowUp',
Down: 'ArrowDown',
Right: 'ArrowRight',
Left: 'ArrowLeft',
Del: 'Delete',
Esc: 'Escape',
Spacebar: ' '
};
/**
* Normalizes the provided key for different browsers.
*
* @param key - A key to normalize.
*/
function normalizeKey(key) {
return NORMALIZATION_MAP[key] || key;
}
/**
* Checks if the keyboard event matches the provided matcher or not.
*
* @param e - A KeyboardEvent object.
* @param matchers - A KeyMatcher tuple or an array with matchers.
*
* @return `true` if the keyboard event satisfies the matcher, or otherwise `false`.
*/
function matchesKey(e, matchers) {
var key = normalizeKey(e.key).toUpperCase();
return matchers && toArray(matchers, true).some(function (matcher) {
return key === matcher[0].toUpperCase() && !matcher[1] === !e.ctrlKey && !matcher[2] === !e.shiftKey && !matcher[3] === !e.altKey;
});
}
/**
* Checks if the subject number is between `minOrMax` and `maxOrMin`.
*
* @param number - A subject number to check.
* @param minOrMax - A min or max number.
* @param maxOrMin - A max or min number.
* @param exclusive - Optional. Whether to exclude `x` or `y`.
*/
function between(number, minOrMax, maxOrMin, exclusive) {
var min = Math.min(minOrMax, maxOrMin);
var max = Math.max(minOrMax, maxOrMin);
return exclusive ? min < number && number < max : min <= number && number <= max;
}
var max$1 = Math.max,
min$1 = Math.min;
/**
* Clamps a number.
*
* @param number - A subject number to check.
* @param x - A min or max number.
* @param y - A min or max number.
*/
function clamp(number, x, y) {
var minimum = min$1(x, y);
var maximum = max$1(x, y);
return min$1(max$1(minimum, number), maximum);
}
var min = Math.min,
max = Math.max,
floor = Math.floor,
ceil = Math.ceil,
abs = Math.abs,
round = Math.round;
/**
* Compares the provided 2 positions.
*
* @return If the `position1` is preceding, returns a negative number,
* or if it is following, returns a positive one. If they are same, returns `0`.
*/
function compare(position1, position2) {
return position1[0] - position2[0] || position1[1] - position2[1];
}
/**
* The alias of document.createRange.
* The Range constructor is not supported by IE.
*
* @since 0.1.0
*
* @return A Range instance.
*/
function createRange() {
return document.createRange();
}
/**
* The alias of window.getSelection.
*
* @since 0.1.0
*
* @return A Selection instance.
*/
function getSelection() {
return window.getSelection();
}
/**
* Finds a node that the offset number belongs to.
*
* @param elm - An element to find in.
* @param offset - An offset index.
*
* @return An object that contains a found node and a offset number.
*/
function findSelectionBoundary(elm, offset) {
var children = elm.childNodes;
if (!children.length && !offset) {
return {
node: elm,
offset: 0
};
}
if (offset <= elm.textContent.length) {
for (var i = 0; i < children.length; i++) {
var node = children[i];
var length = node.textContent.length;
if (isText(node)) {
if (offset <= length) {
return {
node: node,
offset: offset
};
}
} else if (node instanceof Element) {
var found = findSelectionBoundary(node, offset);
if (found) {
return found;
}
}
offset -= length;
}
}
return null;
}
/**
* Sets a selection by an anchor and a focus object.
* Note that the Range constructor does not supported by IE.
*
* @param anchor - An anchor boundary object.
* @param focus - A focus boundary object.
*/
function setSelection(anchor, focus) {
if (anchor && focus) {
var selection = getSelection();
if (selection.setBaseAndExtent) {
selection.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset);
} else {
var range = selection.rangeCount > 0 ? selection.getRangeAt(0) : document.createRange();
range.setStart(anchor.node, anchor.offset);
range.setEnd(focus.node, focus.offset);
selection.removeAllRanges();
selection.addRange(range);
}
}
}
/**
* Converts the provided string in the camel case to the kebab case.
*
* @param string - A string to convert.
*/
function camelToKebab(string) {
return string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
}
/**
* Counts the search string occurrence in the provided sting.
*
* @param string - A string to search in.
* @param search - A string to search for.
* @param from - An index to search from.
* @param to - An index to search to.
*
* @return A number of occurrence.
*/
function count(string, search, from, to) {
if (from === void 0) {
from = 0;
}
if (to === void 0) {
to = string.length;
}
if (from || to !== string.length) {
string = string.slice(from, to);
}
return (string.match(new RegExp(search, 'g')) || []).length;
}
/**
* Checks if the string ends with the `search` string or not.
*
* @param string - A string to check.
* @param search - A string to search.
*
* @return `true` if the string ends with the `search`, or otherwise `false`.
*/
function endsWith(string, search) {
return string.slice(-search.length) === search;
}
/**
* Converts essential HTML special characters to HTML entities.
*
* @param string - A string to escape.
*
* @return An escaped string.
*/
function escapeHtml(string) {
return string.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
}
/**
* Escapes string for the RegExp source.
*
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
*
* @param string - A string to escape.
*/
function escapeRegExp(string) {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
}
/**
* Formats a string.
*
* @param string - A string to format.
* @param replacements - A replacement or replacements.
*
* @return A formatted string.
*/
function format(string) {
for (var i = 0; i < (arguments.length <= 1 ? 0 : arguments.length - 1); i++) {
string = string.replace('%s', String(i + 1 < 1 || arguments.length <= i + 1 ? undefined : arguments[i + 1]));
}
return string;
}
/**
* Returns the index within the provided string of the nth occurrence.
* The optional `from` index determines the start position to search the target from.
*
* @param string - A string to search in.
* @param search - A string to search startsWith
* @param nth - A number of the occurrence.
* @param from - Optional. A start index to search from.
*
* @return An index if the nth occurrence of the `search` string is found, or `-1` if not.
*/
function nthIndexOf(string, search, nth, from) {
if (from === void 0) {
from = 0;
}
var index = from - 1;
var count = nth;
while ((index !== -1 || nth === count) && count--) {
index = string.indexOf(search, index + 1);
}
return index;
}
/**
* Returns a new string filled with a number of copies of the provided string.
*
* @param string - A string to repeat.
* @param count - An integer for determining the number of repeats.
*
* @return A new string containing copies of the provided string.
*/
function repeat(string, count) {
if (!String.prototype.repeat) {
var result = '';
while (count > 0) {
if (count % 2) {
result += string;
}
count = floor(count / 2);
string += string;
}
return result;
}
return string.repeat(count);
}
/**
* Checks if the string starts with the `search` string or not.
*
* @param string - A string to check.
* @param search - A string to search.
*
* @return `true` if the string starts with the `search`, or otherwise `false`.
*/
function startsWith$1(string, search) {
return string.slice(0, search.length) === search;
}
var ids = {};
/**
* Returns a sequential unique ID as "{ prefix }-{ number }".
*
* @param prefix - A prefix for the ID.
*/
function uniqueId(prefix) {
var number = (ids[prefix] || 0) + 1;
var idNumber = number < 10 ? "0" + number : number;
ids[prefix] = number;
return "" + prefix + idNumber;
}
/**
* The base class for a component.
*
* @since 0.1.0
*/
var Component = /*#__PURE__*/function () {
/**
* The Component constructor.
*
* @param Editor - An Editor instance.
*/
function Component(Editor) {
this.Editor = Editor;
this.event = Editor.event;
this.options = Editor.options;
this.language = Editor.language;
}
/**
* Called when the component is mounted.
*
* @param elements - A collection of editor elements.
*/
var _proto2 = Component.prototype;
_proto2.mount = function mount(elements) {
var _this2 = this;
this.elements = elements;
forOwn$1(this.Editor.Components, function (_Component, key) {
_this2[key] = _Component;
});
}
/**
* Called when the editor is destroyed.
*
* @internal
*/
;
_proto2.destroy = function destroy() {
off(null, '', this);
}
/**
* Attaches an event handler to an event or events with passing this instance as a key.
* They can only be detached by the `off()` member method.
*
* @param events - An event name, names split by spaces, or an array with names.
* @param callback - A callback function.
* @param thisArg - Optional. Specifies the `this` parameter of the callback function.
* @param priority - Optional. A priority number for the order in which the callbacks are invoked.
*/
;
_proto2.on = function on(events, callback, thisArg, priority) {
this.event.on(events, thisArg ? callback.bind(thisArg) : callback, this, priority);
}
/**
* Detaches handlers registered by `on()` without removing handlers attached by other components.
*
* @param events - An event name, names split by spaces, or an array with names.
*/
;
_proto2.off = function off(events) {
this.event.off(events, this);
}
/**
* Triggers handlers attached to the event.
*
* @param event - An event name.
* @param args - Optional. Any number of arguments to pass to callback functions.
*/
;
_proto2.emit = function emit(event) {
var _this$event;
for (var _len6 = arguments.length, args = new Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) {
args[_key6 - 1] = arguments[_key6];
}
(_this$event = this.event).emit.apply(_this$event, [event].concat(args));
}
/**
* Listens to native events.
* This method stores all listeners and automatically removes them on destruction.
*
* @param elm - A document, a window or an element.
* @param events - An event name or names split by spaces.
* @param callback - A callback function.
* @param thisArg - Optional. Specifies the `this` parameter of the callback function.
*/
;
_proto2.bind = function bind(elm, events, callback, thisArg) {
on(elm, events, thisArg ? callback.bind(thisArg) : callback, this);
}
/**
* Returns a Language or LanguageConfig object at the focus or specified position.
* This method can return different objects depending on the position
* if the language allows to embed other languages, such as HTML and PHP.
*
* @param position - Optional. Specifies the position to get the language at.
*
* @return A main Language object or sub language config object.
*/
;
_proto2.getLanguage = function getLanguage(position) {
position = position || this.Selection.focus;
var language = this.language;
var info = this.lines.getInfoAt(position);
if (info && info.language && language.use && language.use[info.language]) {
return language.use[info.language].config;
}
return language;
}
/**
* Attempts to invoke the public method of the specified extension.
* In terms of the "loose coupling", you'd better try not to use this method.
* Using events is enough in most cases.
*
* @example
* ```ts
* // Attempts to show the "search" toolbar.
* Editor.invoke( 'Toolbar', 'show', 'search' );
* ```
*
* @param name - A name of the extension.
* @param method - A method name to invoke.
* @param args - Optional. Arguments for the method.
*
* @return The return value of the method.
*/
;
_proto2.invoke = function invoke(name, method) {
var _this$Editor;
for (var _len7 = arguments.length, args = new Array(_len7 > 2 ? _len7 - 2 : 0), _key7 = 2; _key7 < _len7; _key7++) {
args[_key7 - 2] = arguments[_key7];
}
return (_this$Editor = this.Editor).invoke.apply(_this$Editor, [name, method].concat(args));
}
/**
* Returns the specified extension.
* In terms of the "loose coupling", you'd better try not to use this method.
* Using events is enough in most cases.
*
* @param name - A name of an extension.
*
* @return An extension if found, or otherwise `undefined`.
*/
;
_proto2.require = function require(name) {
return this.Editor.require(name);
}
/**
* Adds default icon strings. They can be still overridden by options.
* The IconSettings is a tuple as `[ string, number?, string? ]` corresponding with `[ path, stroke?, linecap? ]`.
*
* @example
* ```ts
* this.addIcons( {
* myIcon: [
* 'm19 18-14-13m0 13 14-13',
* 3,
* ],
* } );
* ```
*
* @param icons - Icon settings to add.
*/
;
_proto2.addIcons = function addIcons(icons) {
var options = this.options;
options.icons = assign$1({}, icons, options.icons);
}
/**
* Adds default i18n strings. They can be still overridden by options.
*
* @example
* ```ts
* this.addI18n( {
* myMessage: 'Hello!',
* } );
* ```
*
* @param i18n - Additional i18n strings.
*/
;
_proto2.addI18n = function addI18n(i18n) {
var options = this.options;
options.i18n = assign$1({}, i18n, options.i18n);
}
/**
* Adds default shortcuts to the keymap object. They can be still overridden by options.
* Call this method before RyuseiCode mounts components so that the Keymap component recognizes shortcuts.
*
* @example
* ```js
* class MyExtension extends Component {
* constructor( Editor ) {
* super( Editor );
*
* this.addKeyBindings( {
* myShortcut: [ 'P', true, true ],
* } );
* }
* }
* ```
*
* @param shortcuts - Additional shortcuts.
*/
;
_proto2.addKeyBindings = function addKeyBindings(shortcuts) {
var options = this.options;
options.keymap = assign$1({}, shortcuts, options.keymap);
}
/**
* Returns options for each extension, merging provided default values.
*
* @example
* ```js
* class MyExtension extends Component {
* constructor( Editor ) {
* super( Editor );
*
* const extensionOptions = this.getOptions( 'myExtension', { option1: true } );
* }
* }
* ```
*
* @param name - An option name.
* @param defaults - Default values.
*
* @return A merged options, or `null`.
*/
;
_proto2.getOptions = function getOptions(name, defaults) {
var options = this.options[name];
if (isUndefined$1(options) || options === true) {
return defaults || {};
}
if (isObject$1(options)) {
return assign$1({}, defaults, options);
}
assert$1(false);
}
/**
* Returns the latest Lines instance.
* This is an alias of `Code#Lines`.
*
* @return The Lines instance.
*/
;
_createClass(Component, [{
key: "lines",
get: function get() {
return this.Code.Lines;
}
/**
* Returns the i18n collection.
* This is an alias of `this.options.i18n`.
*
* @return The object with i18n strings.
*/
}, {
key: "i18n",
get: function get() {
return this.options.i18n;
}
}]);
return Component;
}();
var CLASS_ROOT = PROJECT_CODE;
var CLASS_VIEW = PROJECT_CODE + "__view";
var CLASS_BODY = PROJECT_CODE + "__body";
var CLASS_SCROLLER = PROJECT_CODE + "__scroller";
var CLASS_CONTAINER = PROJECT_CODE + "__container";
var CLASS_EDITOR = PROJECT_CODE + "__editor";
var CLASS_SCROLLBARS = PROJECT_CODE + "__scrollbars";
var CLASS_SCROLLBAR = PROJECT_CODE + "__scrollbar";
var CLASS_LINES = PROJECT_CODE + "__lines";
var CLASS_LINE = PROJECT_CODE + "__line";
var CLASS_SOURCE = PROJECT_CODE + "__source";
var CLASS_BACKGROUND = PROJECT_CODE + "__background";
var CLASS_CARETS = PROJECT_CODE + "__carets";
var CLASS_CARET = PROJECT_CODE + "__caret";
var CLASS_MARKERS = PROJECT_CODE + "__markers";
var CLASS_MARKER = PROJECT_CODE + "__marker";
var CLASS_OVERLAY = PROJECT_CODE + "__overlay";
var CLASS_CONTEXT_MENU = PROJECT_CODE + "__context-menu";
var CLASS_CONTEXT_MENU_GROUP = CLASS_CONTEXT_MENU + "__group";
var CLASS_CONTEXT_MENU_LIST = CLASS_CONTEXT_MENU + "__list";
var CLASS_CONTEXT_MENU_ITEM = CLASS_CONTEXT_MENU + "__item";
var CLASS_CONTEXT_MENU_BUTTON = CLASS_CONTEXT_MENU + "__button";
var CLASS_CONTEXT_MENU_LABEL = CLASS_CONTEXT_MENU_BUTTON + "__label";
var CLASS_CONTEXT_MENU_SHORTCUT = CLASS_CONTEXT_MENU_BUTTON + "__shortcut";
var CLASS_TOKEN = PROJECT_CODE_SHORT + "__token";
var CLASS_INPUT = PROJECT_CODE + "__input";
var CLASS_BUTTON = PROJECT_CODE + "__button";
var CLASS_ICON = PROJECT_CODE + "__icon";
var CLASS_PLACEHOLDER = PROJECT_CODE + "__placeholder";
var CLASS_ACTIVE = 'is-active';
var CLASS_RENDERED = 'is-rendered';
var CLASS_INITIALIZED = 'is-initialized';
var CLASS_ANCHOR = 'is-anchor';
var CLASS_FOCUS = 'is-focus';
var CLASS_PRESERVED = 'is-preserved';
var CLASS_FOCUSED = 'is-focused';
var CLASS_READONLY = 'is-readonly';
var CLASS_DRAGGING = 'is-dragging';
var CLASS_EMPTY = 'is-empty';
var CLASS_MOBILE = 'is-mobile';
var EVENT_MOUNT = 'mount';
var EVENT_MOUNTED = 'mounted';
var EVENT_FOCUS = 'focus';
var EVENT_BLUR = 'blur';
var EVENT_READONLY = 'readOnly';
var EVENT_KEYDOWN = 'keydown';
var EVENT_INPUT = 'input';
var EVENT_NEWLINE = 'newline';
var EVENT_CHANGE = 'change';
var EVENT_CHANGED = 'changed';
var EVENT_COMPOSITION_START = 'compositionStart';
var EVENT_COMPOSITION_UPDATE = 'compositionUpdate';
var EVENT_COMPOSITION_END = 'compositionEnd';
var EVENT_ANCHOR_LINE_CHANGED = 'anchorLineChanged';
var EVENT_FOCUS_LINE_CHANGED = 'focusLineChanged';
var EVENT_COPY = 'copy';
var EVENT_CUT = 'cut';
var EVENT_PASTE = 'paste';
var EVENT_KEYMAP = 'keymap';
var EVENT_CHUNK_MOVED = 'chunkMoved';
var EVENT_CHUNK_SUPPLIED = 'chunkSupplied';
var EVENT_SELECTING = 'selecting';
var EVENT_SELECTED = 'selected';
var EVENT_SELECTION_CHANGE = 'selectionChanged';
var EVENT_SCROLL = 'scroll';
var EVENT_SCROLLED = 'scrolled';
var EVENT_SCROLLER_SCROLL = 'scrollerScroll';
var EVENT_WINDOW_SCROLL = 'windowScroll';
var EVENT_RESIZE = 'resize';
var EVENT_SCROLL_WIDTH_CHANGED = 'scrollWidthChanged';
var EVENT_SCROLL_HEIGHT_CHANGED = 'scrollHeightChanged';
var EVENT_SYNCED = 'synced';
var EVENT_CONTEXT_MENU_OPENED = 'contextMenuOpened';
var EVENT_CONTEXT_MENU_CLOSED = 'contextMenuClosed';
var EVENT_CONTEXT_MENU_CLICKED = 'contextMenuClicked';
var EVENT_RESET = 'reset';
var EVENT_INIT_STYLE = 'initStyle';
var EVENT_FONT_LOADED = 'fontLoaded';
var EVENT_DESTROYED = 'destroyed';
/**
* The editor is not active.
*/
var IDLE = 0;
/**
* The selection is collapsed.
*/
var COLLAPSED = 1;
/**
* The selection will change soon. The native selection has not been updated at this timing.
*/
var START = 2;
/**
* The selection has just changed after the `START` state. The native selection has been updated.
*/
var CHANGED = 3;
/**
* The selection has been programmatically updated.
*/
var UPDATE = 4;
/**
* An user is selecting a document.
*/
var SELECTING = 5;
/**
* The existing selection is being extended.
*/
var EXTEND = 6;
/**
* User finishes the selection. The native selection has not been updated at this timing (in Gecko).
*/
var END = 7;
/**
* The selection is settled and it is not collapsed.
*/
var SELECTED = 8;
/**
* All contents are selected.
*/
var SELECTED_ALL = 9;
/**
* The selection is right-clicked.
*/
var CLICKED_RIGHT = 10;
var STATES = /*#__PURE__*/Object.freeze({
__proto__: null,
IDLE: IDLE,
COLLAPSED: COLLAPSED,
START: START,
CHANGED: CHANGED,
UPDATE: UPDATE,
SELECTING: SELECTING,
EXTEND: EXTEND,
END: END,
SELECTED: SELECTED,
SELECTED_ALL: SELECTED_ALL,
CLICKED_RIGHT: CLICKED_RIGHT
});
/**
* The offset amount for the horizontal position of the caret.
*
* @since 0.1.0
*/
var HORIZONTAL_OFFSET = -1;
/**
* The debounce duration for the `blink` method.
*
* @since 0.1.0
*/
var BLINK_DEBOUNCE_DURATION = 30;
/**
* The class for creating and controlling the caret element.
*
* @since 0.1.0
*/
var CustomCaret = /*#__PURE__*/function () {
/**
* The Caret constructor.
*
* @param Editor - An Editor instance.
* @param id - An ID for the caret.
* @param parent - A parent element where the caret is appended.
*/
function CustomCaret(Editor, id, parent) {
var _this3 = this;
this.Editor = Editor;
this.caret = div([CLASS_CARET, CLASS_CARET + "--" + id], parent);
this.blink = debounce(this.blink.bind(this), BLINK_DEBOUNCE_DURATION);
Editor.event.on(EVENT_RESIZE, function () {
if (_this3.position) {
_this3.move(_this3.position);
}
});
}
/**
* Moves the caret to the specified position.
*
* @param position - A position to set as [ row, col ].
*/
var _proto3 = CustomCaret.prototype;
_proto3.move = function move(position) {
var Measure = this.Editor.Components.Measure;
var rect = Measure.getOffset(position);
styles(this.caret, {
top: unit(rect.top),
left: unit(rect.left + HORIZONTAL_OFFSET),
animation: 'none'
});
this.blink();
this.position = position;
}
/**
* Displays the caret.
*/
;
_proto3.show = function show() {
addClass(this.caret, CLASS_ACTIVE);
}
/**
* Hides the caret.
*/
;
_proto3.hide = function hide() {
removeClass(this.caret, CLASS_ACTIVE);
}
/**
* Starts the blink animation by removing the `none` value from the `animation`.
*/
;
_proto3.blink = function blink() {
styles(this.caret, {
animation: ''
});
};
return CustomCaret;
}();
/**
* The ID of the primary caret.
*
* @since 0.1.0
*/
var PRIMARY_CARET_ID = 'primary';
/**
* The component for generating and handling carets.
*
* @since 0.1.0
*/
var Caret = /*#__PURE__*/function (_Component2) {
_inheritsLoose(Caret, _Component2);
function Caret() {
var _this4;
_this4 = _Component2.apply(this, arguments) || this;
/**
* Stores the all registered Caret instances.
*/
_this4.carets = {};
return _this4;
}
/**
* Mounts the component.
* Uses the native caret on IE and mobile devices.
*
* @internal
*
* @param elements - A collection of essential editor elements.
*/
var _proto4 = Caret.prototype;
_proto4.mount = function mount(elements) {
_Component2.prototype.mount.call(this, elements);
this.create();
if (!isIE() && !isMobile()) {
this.register(PRIMARY_CARET_ID);
this.primary = this.get(PRIMARY_CARET_ID);
this.listen();
}
}
/**
* Creates a wrapper element that contains carets.
*/
;
_proto4.create = function create() {
this.wrapper = div({
"class": CLASS_CARETS,
role: 'presentation',
'aria-hidden': true
}, this.elements.editor);
}
/**
* Listens to some events.
*/
;
_proto4.listen = function listen() {
var _this5 = this;
var editable = this.elements.editable;
var primary = this.primary,
Editor = this.Editor;
this.bind(editable, 'focus', function () {
if (!Editor.readOnly) {
primary.show();
}
});
this.bind(editable, 'blur', function () {
primary.hide();
});
this.update = rafThrottle(this.update.bind(this), true);
this.on(EVENT_READONLY, function (e, readOnly) {
if (readOnly) {
primary.hide();
} else {
if (Editor.isFocused()) {
_this5.update();
primary.show();
}
}
});
this.on(EVENT_SELECTED, this.onSelected, this);
this.on(EVENT_SELECTING, this.update);
}
/**
* Called when the selection state is changed.
*
* @param e - An EventBusEvent object.
* @param Selection - A Selection instance.
*/
;
_proto4.onSelected = function onSelected(e, Selection) {
if (!this.Editor.readOnly) {
if (Selection.is(CHANGED, COLLAPSED, SELECTED)) {
this.update();
}
}
}
/**
* Updates the primary caret position on the animation frame.
*/
;
_proto4.update = function update() {
this.primary.move(this.Selection.get(false).end);
}
/**
* Registers a new caret.
*
* @param id - The ID for the caret to register.
*
* @return The registered CustomCaret instance.
*/
;
_proto4.register = function register(id) {
var carets = this.carets;
assert$1(!carets[id]);
var caret = new CustomCaret(this.Editor, id, this.wrapper);
carets[id] = caret;
return caret;
}
/**
* Returns the primary or the specific CustomCaret instance.
*
* @param id - Optional. A caret ID.
*
* @return A CustomCaret instance if available, or otherwise `undefined`.
*/
;
_proto4.get = function get(id) {
if (id === void 0) {
id = PRIMARY_CARET_ID;
}
return this.carets[id];
}
/**
* Returns the DOMRect object of the primary caret.
*