Download extensions/sd-model-organizer/javascript/tinymce/models/dom/model.js from dikdimon/ext: direct link, hf CLI and curl.
- Browser
- Download file 294 kB
-
https://huggingface.co/dikdimon/ext/resolve/main/extensions/sd-model-organizer/javascript/tinymce/models/dom/model.js
- Command line
-
hf download hf://dikdimon/ext/extensions/sd-model-organizer/javascript/tinymce/models/dom/model.js
-
curl -L -o model.js https://huggingface.co/dikdimon/ext/resolve/main/extensions/sd-model-organizer/javascript/tinymce/models/dom/model.js
294 kB
| /** | |
| * TinyMCE version 6.4.1 (2023-03-29) | |
| */ | |
| (function () { | |
| 'use strict'; | |
| var global$1 = tinymce.util.Tools.resolve('tinymce.ModelManager'); | |
| const hasProto = (v, constructor, predicate) => { | |
| var _a; | |
| if (predicate(v, constructor.prototype)) { | |
| return true; | |
| } else { | |
| return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name; | |
| } | |
| }; | |
| const typeOf = x => { | |
| const t = typeof x; | |
| if (x === null) { | |
| return 'null'; | |
| } else if (t === 'object' && Array.isArray(x)) { | |
| return 'array'; | |
| } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) { | |
| return 'string'; | |
| } else { | |
| return t; | |
| } | |
| }; | |
| const isType$1 = type => value => typeOf(value) === type; | |
| const isSimpleType = type => value => typeof value === type; | |
| const eq$2 = t => a => t === a; | |
| const isString = isType$1('string'); | |
| const isObject = isType$1('object'); | |
| const isArray = isType$1('array'); | |
| const isNull = eq$2(null); | |
| const isBoolean = isSimpleType('boolean'); | |
| const isUndefined = eq$2(undefined); | |
| const isNullable = a => a === null || a === undefined; | |
| const isNonNullable = a => !isNullable(a); | |
| const isFunction = isSimpleType('function'); | |
| const isNumber = isSimpleType('number'); | |
| const noop = () => { | |
| }; | |
| const compose = (fa, fb) => { | |
| return (...args) => { | |
| return fa(fb.apply(null, args)); | |
| }; | |
| }; | |
| const compose1 = (fbc, fab) => a => fbc(fab(a)); | |
| const constant = value => { | |
| return () => { | |
| return value; | |
| }; | |
| }; | |
| const identity = x => { | |
| return x; | |
| }; | |
| const tripleEquals = (a, b) => { | |
| return a === b; | |
| }; | |
| function curry(fn, ...initialArgs) { | |
| return (...restArgs) => { | |
| const all = initialArgs.concat(restArgs); | |
| return fn.apply(null, all); | |
| }; | |
| } | |
| const not = f => t => !f(t); | |
| const die = msg => { | |
| return () => { | |
| throw new Error(msg); | |
| }; | |
| }; | |
| const apply = f => { | |
| return f(); | |
| }; | |
| const never = constant(false); | |
| const always = constant(true); | |
| class Optional { | |
| constructor(tag, value) { | |
| this.tag = tag; | |
| this.value = value; | |
| } | |
| static some(value) { | |
| return new Optional(true, value); | |
| } | |
| static none() { | |
| return Optional.singletonNone; | |
| } | |
| fold(onNone, onSome) { | |
| if (this.tag) { | |
| return onSome(this.value); | |
| } else { | |
| return onNone(); | |
| } | |
| } | |
| isSome() { | |
| return this.tag; | |
| } | |
| isNone() { | |
| return !this.tag; | |
| } | |
| map(mapper) { | |
| if (this.tag) { | |
| return Optional.some(mapper(this.value)); | |
| } else { | |
| return Optional.none(); | |
| } | |
| } | |
| bind(binder) { | |
| if (this.tag) { | |
| return binder(this.value); | |
| } else { | |
| return Optional.none(); | |
| } | |
| } | |
| exists(predicate) { | |
| return this.tag && predicate(this.value); | |
| } | |
| forall(predicate) { | |
| return !this.tag || predicate(this.value); | |
| } | |
| filter(predicate) { | |
| if (!this.tag || predicate(this.value)) { | |
| return this; | |
| } else { | |
| return Optional.none(); | |
| } | |
| } | |
| getOr(replacement) { | |
| return this.tag ? this.value : replacement; | |
| } | |
| or(replacement) { | |
| return this.tag ? this : replacement; | |
| } | |
| getOrThunk(thunk) { | |
| return this.tag ? this.value : thunk(); | |
| } | |
| orThunk(thunk) { | |
| return this.tag ? this : thunk(); | |
| } | |
| getOrDie(message) { | |
| if (!this.tag) { | |
| throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None'); | |
| } else { | |
| return this.value; | |
| } | |
| } | |
| static from(value) { | |
| return isNonNullable(value) ? Optional.some(value) : Optional.none(); | |
| } | |
| getOrNull() { | |
| return this.tag ? this.value : null; | |
| } | |
| getOrUndefined() { | |
| return this.value; | |
| } | |
| each(worker) { | |
| if (this.tag) { | |
| worker(this.value); | |
| } | |
| } | |
| toArray() { | |
| return this.tag ? [this.value] : []; | |
| } | |
| toString() { | |
| return this.tag ? `some(${ this.value })` : 'none()'; | |
| } | |
| } | |
| Optional.singletonNone = new Optional(false); | |
| const nativeSlice = Array.prototype.slice; | |
| const nativeIndexOf = Array.prototype.indexOf; | |
| const nativePush = Array.prototype.push; | |
| const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t); | |
| const contains$2 = (xs, x) => rawIndexOf(xs, x) > -1; | |
| const exists = (xs, pred) => { | |
| for (let i = 0, len = xs.length; i < len; i++) { | |
| const x = xs[i]; | |
| if (pred(x, i)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| }; | |
| const range$1 = (num, f) => { | |
| const r = []; | |
| for (let i = 0; i < num; i++) { | |
| r.push(f(i)); | |
| } | |
| return r; | |
| }; | |
| const map$1 = (xs, f) => { | |
| const len = xs.length; | |
| const r = new Array(len); | |
| for (let i = 0; i < len; i++) { | |
| const x = xs[i]; | |
| r[i] = f(x, i); | |
| } | |
| return r; | |
| }; | |
| const each$2 = (xs, f) => { | |
| for (let i = 0, len = xs.length; i < len; i++) { | |
| const x = xs[i]; | |
| f(x, i); | |
| } | |
| }; | |
| const eachr = (xs, f) => { | |
| for (let i = xs.length - 1; i >= 0; i--) { | |
| const x = xs[i]; | |
| f(x, i); | |
| } | |
| }; | |
| const partition = (xs, pred) => { | |
| const pass = []; | |
| const fail = []; | |
| for (let i = 0, len = xs.length; i < len; i++) { | |
| const x = xs[i]; | |
| const arr = pred(x, i) ? pass : fail; | |
| arr.push(x); | |
| } | |
| return { | |
| pass, | |
| fail | |
| }; | |
| }; | |
| const filter$2 = (xs, pred) => { | |
| const r = []; | |
| for (let i = 0, len = xs.length; i < len; i++) { | |
| const x = xs[i]; | |
| if (pred(x, i)) { | |
| r.push(x); | |
| } | |
| } | |
| return r; | |
| }; | |
| const foldr = (xs, f, acc) => { | |
| eachr(xs, (x, i) => { | |
| acc = f(acc, x, i); | |
| }); | |
| return acc; | |
| }; | |
| const foldl = (xs, f, acc) => { | |
| each$2(xs, (x, i) => { | |
| acc = f(acc, x, i); | |
| }); | |
| return acc; | |
| }; | |
| const findUntil = (xs, pred, until) => { | |
| for (let i = 0, len = xs.length; i < len; i++) { | |
| const x = xs[i]; | |
| if (pred(x, i)) { | |
| return Optional.some(x); | |
| } else if (until(x, i)) { | |
| break; | |
| } | |
| } | |
| return Optional.none(); | |
| }; | |
| const find$1 = (xs, pred) => { | |
| return findUntil(xs, pred, never); | |
| }; | |
| const findIndex = (xs, pred) => { | |
| for (let i = 0, len = xs.length; i < len; i++) { | |
| const x = xs[i]; | |
| if (pred(x, i)) { | |
| return Optional.some(i); | |
| } | |
| } | |
| return Optional.none(); | |
| }; | |
| const flatten = xs => { | |
| const r = []; | |
| for (let i = 0, len = xs.length; i < len; ++i) { | |
| if (!isArray(xs[i])) { | |
| throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs); | |
| } | |
| nativePush.apply(r, xs[i]); | |
| } | |
| return r; | |
| }; | |
| const bind$2 = (xs, f) => flatten(map$1(xs, f)); | |
| const forall = (xs, pred) => { | |
| for (let i = 0, len = xs.length; i < len; ++i) { | |
| const x = xs[i]; | |
| if (pred(x, i) !== true) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| }; | |
| const reverse = xs => { | |
| const r = nativeSlice.call(xs, 0); | |
| r.reverse(); | |
| return r; | |
| }; | |
| const mapToObject = (xs, f) => { | |
| const r = {}; | |
| for (let i = 0, len = xs.length; i < len; i++) { | |
| const x = xs[i]; | |
| r[String(x)] = f(x, i); | |
| } | |
| return r; | |
| }; | |
| const sort$1 = (xs, comparator) => { | |
| const copy = nativeSlice.call(xs, 0); | |
| copy.sort(comparator); | |
| return copy; | |
| }; | |
| const get$d = (xs, i) => i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none(); | |
| const head = xs => get$d(xs, 0); | |
| const last$2 = xs => get$d(xs, xs.length - 1); | |
| const findMap = (arr, f) => { | |
| for (let i = 0; i < arr.length; i++) { | |
| const r = f(arr[i], i); | |
| if (r.isSome()) { | |
| return r; | |
| } | |
| } | |
| return Optional.none(); | |
| }; | |
| const keys = Object.keys; | |
| const hasOwnProperty = Object.hasOwnProperty; | |
| const each$1 = (obj, f) => { | |
| const props = keys(obj); | |
| for (let k = 0, len = props.length; k < len; k++) { | |
| const i = props[k]; | |
| const x = obj[i]; | |
| f(x, i); | |
| } | |
| }; | |
| const map = (obj, f) => { | |
| return tupleMap(obj, (x, i) => ({ | |
| k: i, | |
| v: f(x, i) | |
| })); | |
| }; | |
| const tupleMap = (obj, f) => { | |
| const r = {}; | |
| each$1(obj, (x, i) => { | |
| const tuple = f(x, i); | |
| r[tuple.k] = tuple.v; | |
| }); | |
| return r; | |
| }; | |
| const objAcc = r => (x, i) => { | |
| r[i] = x; | |
| }; | |
| const internalFilter = (obj, pred, onTrue, onFalse) => { | |
| each$1(obj, (x, i) => { | |
| (pred(x, i) ? onTrue : onFalse)(x, i); | |
| }); | |
| }; | |
| const filter$1 = (obj, pred) => { | |
| const t = {}; | |
| internalFilter(obj, pred, objAcc(t), noop); | |
| return t; | |
| }; | |
| const mapToArray = (obj, f) => { | |
| const r = []; | |
| each$1(obj, (value, name) => { | |
| r.push(f(value, name)); | |
| }); | |
| return r; | |
| }; | |
| const values = obj => { | |
| return mapToArray(obj, identity); | |
| }; | |
| const get$c = (obj, key) => { | |
| return has$1(obj, key) ? Optional.from(obj[key]) : Optional.none(); | |
| }; | |
| const has$1 = (obj, key) => hasOwnProperty.call(obj, key); | |
| const hasNonNullableKey = (obj, key) => has$1(obj, key) && obj[key] !== undefined && obj[key] !== null; | |
| const isEmpty = r => { | |
| for (const x in r) { | |
| if (hasOwnProperty.call(r, x)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| }; | |
| const Global = typeof window !== 'undefined' ? window : Function('return this;')(); | |
| const path = (parts, scope) => { | |
| let o = scope !== undefined && scope !== null ? scope : Global; | |
| for (let i = 0; i < parts.length && o !== undefined && o !== null; ++i) { | |
| o = o[parts[i]]; | |
| } | |
| return o; | |
| }; | |
| const resolve$2 = (p, scope) => { | |
| const parts = p.split('.'); | |
| return path(parts, scope); | |
| }; | |
| const unsafe = (name, scope) => { | |
| return resolve$2(name, scope); | |
| }; | |
| const getOrDie = (name, scope) => { | |
| const actual = unsafe(name, scope); | |
| if (actual === undefined || actual === null) { | |
| throw new Error(name + ' not available on this browser'); | |
| } | |
| return actual; | |
| }; | |
| const getPrototypeOf = Object.getPrototypeOf; | |
| const sandHTMLElement = scope => { | |
| return getOrDie('HTMLElement', scope); | |
| }; | |
| const isPrototypeOf = x => { | |
| const scope = resolve$2('ownerDocument.defaultView', x); | |
| return isObject(x) && (sandHTMLElement(scope).prototype.isPrototypeOf(x) || /^HTML\w*Element$/.test(getPrototypeOf(x).constructor.name)); | |
| }; | |
| const COMMENT = 8; | |
| const DOCUMENT = 9; | |
| const DOCUMENT_FRAGMENT = 11; | |
| const ELEMENT = 1; | |
| const TEXT = 3; | |
| const name = element => { | |
| const r = element.dom.nodeName; | |
| return r.toLowerCase(); | |
| }; | |
| const type = element => element.dom.nodeType; | |
| const isType = t => element => type(element) === t; | |
| const isComment = element => type(element) === COMMENT || name(element) === '#comment'; | |
| const isHTMLElement = element => isElement(element) && isPrototypeOf(element.dom); | |
| const isElement = isType(ELEMENT); | |
| const isText = isType(TEXT); | |
| const isDocument = isType(DOCUMENT); | |
| const isDocumentFragment = isType(DOCUMENT_FRAGMENT); | |
| const isTag = tag => e => isElement(e) && name(e) === tag; | |
| const rawSet = (dom, key, value) => { | |
| if (isString(value) || isBoolean(value) || isNumber(value)) { | |
| dom.setAttribute(key, value + ''); | |
| } else { | |
| console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom); | |
| throw new Error('Attribute value was not simple'); | |
| } | |
| }; | |
| const set$2 = (element, key, value) => { | |
| rawSet(element.dom, key, value); | |
| }; | |
| const setAll$1 = (element, attrs) => { | |
| const dom = element.dom; | |
| each$1(attrs, (v, k) => { | |
| rawSet(dom, k, v); | |
| }); | |
| }; | |
| const setOptions = (element, attrs) => { | |
| each$1(attrs, (v, k) => { | |
| v.fold(() => { | |
| remove$7(element, k); | |
| }, value => { | |
| rawSet(element.dom, k, value); | |
| }); | |
| }); | |
| }; | |
| const get$b = (element, key) => { | |
| const v = element.dom.getAttribute(key); | |
| return v === null ? undefined : v; | |
| }; | |
| const getOpt = (element, key) => Optional.from(get$b(element, key)); | |
| const remove$7 = (element, key) => { | |
| element.dom.removeAttribute(key); | |
| }; | |
| const clone$2 = element => foldl(element.dom.attributes, (acc, attr) => { | |
| acc[attr.name] = attr.value; | |
| return acc; | |
| }, {}); | |
| const fromHtml$1 = (html, scope) => { | |
| const doc = scope || document; | |
| const div = doc.createElement('div'); | |
| div.innerHTML = html; | |
| if (!div.hasChildNodes() || div.childNodes.length > 1) { | |
| const message = 'HTML does not have a single root node'; | |
| console.error(message, html); | |
| throw new Error(message); | |
| } | |
| return fromDom$1(div.childNodes[0]); | |
| }; | |
| const fromTag = (tag, scope) => { | |
| const doc = scope || document; | |
| const node = doc.createElement(tag); | |
| return fromDom$1(node); | |
| }; | |
| const fromText = (text, scope) => { | |
| const doc = scope || document; | |
| const node = doc.createTextNode(text); | |
| return fromDom$1(node); | |
| }; | |
| const fromDom$1 = node => { | |
| if (node === null || node === undefined) { | |
| throw new Error('Node cannot be null or undefined'); | |
| } | |
| return { dom: node }; | |
| }; | |
| const fromPoint$1 = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom$1); | |
| const SugarElement = { | |
| fromHtml: fromHtml$1, | |
| fromTag, | |
| fromText, | |
| fromDom: fromDom$1, | |
| fromPoint: fromPoint$1 | |
| }; | |
| const is$2 = (element, selector) => { | |
| const dom = element.dom; | |
| if (dom.nodeType !== ELEMENT) { | |
| return false; | |
| } else { | |
| const elem = dom; | |
| if (elem.matches !== undefined) { | |
| return elem.matches(selector); | |
| } else if (elem.msMatchesSelector !== undefined) { | |
| return elem.msMatchesSelector(selector); | |
| } else if (elem.webkitMatchesSelector !== undefined) { | |
| return elem.webkitMatchesSelector(selector); | |
| } else if (elem.mozMatchesSelector !== undefined) { | |
| return elem.mozMatchesSelector(selector); | |
| } else { | |
| throw new Error('Browser lacks native selectors'); | |
| } | |
| } | |
| }; | |
| const bypassSelector = dom => dom.nodeType !== ELEMENT && dom.nodeType !== DOCUMENT && dom.nodeType !== DOCUMENT_FRAGMENT || dom.childElementCount === 0; | |
| const all$1 = (selector, scope) => { | |
| const base = scope === undefined ? document : scope.dom; | |
| return bypassSelector(base) ? [] : map$1(base.querySelectorAll(selector), SugarElement.fromDom); | |
| }; | |
| const one = (selector, scope) => { | |
| const base = scope === undefined ? document : scope.dom; | |
| return bypassSelector(base) ? Optional.none() : Optional.from(base.querySelector(selector)).map(SugarElement.fromDom); | |
| }; | |
| const eq$1 = (e1, e2) => e1.dom === e2.dom; | |
| const contains$1 = (e1, e2) => { | |
| const d1 = e1.dom; | |
| const d2 = e2.dom; | |
| return d1 === d2 ? false : d1.contains(d2); | |
| }; | |
| const is$1 = is$2; | |
| const owner = element => SugarElement.fromDom(element.dom.ownerDocument); | |
| const documentOrOwner = dos => isDocument(dos) ? dos : owner(dos); | |
| const documentElement = element => SugarElement.fromDom(documentOrOwner(element).dom.documentElement); | |
| const defaultView = element => SugarElement.fromDom(documentOrOwner(element).dom.defaultView); | |
| const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom); | |
| const parentElement = element => Optional.from(element.dom.parentElement).map(SugarElement.fromDom); | |
| const parents = (element, isRoot) => { | |
| const stop = isFunction(isRoot) ? isRoot : never; | |
| let dom = element.dom; | |
| const ret = []; | |
| while (dom.parentNode !== null && dom.parentNode !== undefined) { | |
| const rawParent = dom.parentNode; | |
| const p = SugarElement.fromDom(rawParent); | |
| ret.push(p); | |
| if (stop(p) === true) { | |
| break; | |
| } else { | |
| dom = rawParent; | |
| } | |
| } | |
| return ret; | |
| }; | |
| const prevSibling = element => Optional.from(element.dom.previousSibling).map(SugarElement.fromDom); | |
| const nextSibling = element => Optional.from(element.dom.nextSibling).map(SugarElement.fromDom); | |
| const children$2 = element => map$1(element.dom.childNodes, SugarElement.fromDom); | |
| const child$2 = (element, index) => { | |
| const cs = element.dom.childNodes; | |
| return Optional.from(cs[index]).map(SugarElement.fromDom); | |
| }; | |
| const firstChild = element => child$2(element, 0); | |
| const before$3 = (marker, element) => { | |
| const parent$1 = parent(marker); | |
| parent$1.each(v => { | |
| v.dom.insertBefore(element.dom, marker.dom); | |
| }); | |
| }; | |
| const after$5 = (marker, element) => { | |
| const sibling = nextSibling(marker); | |
| sibling.fold(() => { | |
| const parent$1 = parent(marker); | |
| parent$1.each(v => { | |
| append$1(v, element); | |
| }); | |
| }, v => { | |
| before$3(v, element); | |
| }); | |
| }; | |
| const prepend = (parent, element) => { | |
| const firstChild$1 = firstChild(parent); | |
| firstChild$1.fold(() => { | |
| append$1(parent, element); | |
| }, v => { | |
| parent.dom.insertBefore(element.dom, v.dom); | |
| }); | |
| }; | |
| const append$1 = (parent, element) => { | |
| parent.dom.appendChild(element.dom); | |
| }; | |
| const appendAt = (parent, element, index) => { | |
| child$2(parent, index).fold(() => { | |
| append$1(parent, element); | |
| }, v => { | |
| before$3(v, element); | |
| }); | |
| }; | |
| const wrap = (element, wrapper) => { | |
| before$3(element, wrapper); | |
| append$1(wrapper, element); | |
| }; | |
| const after$4 = (marker, elements) => { | |
| each$2(elements, (x, i) => { | |
| const e = i === 0 ? marker : elements[i - 1]; | |
| after$5(e, x); | |
| }); | |
| }; | |
| const append = (parent, elements) => { | |
| each$2(elements, x => { | |
| append$1(parent, x); | |
| }); | |
| }; | |
| const empty = element => { | |
| element.dom.textContent = ''; | |
| each$2(children$2(element), rogue => { | |
| remove$6(rogue); | |
| }); | |
| }; | |
| const remove$6 = element => { | |
| const dom = element.dom; | |
| if (dom.parentNode !== null) { | |
| dom.parentNode.removeChild(dom); | |
| } | |
| }; | |
| const unwrap = wrapper => { | |
| const children = children$2(wrapper); | |
| if (children.length > 0) { | |
| after$4(wrapper, children); | |
| } | |
| remove$6(wrapper); | |
| }; | |
| const clone$1 = (original, isDeep) => SugarElement.fromDom(original.dom.cloneNode(isDeep)); | |
| const shallow = original => clone$1(original, false); | |
| const deep = original => clone$1(original, true); | |
| const shallowAs = (original, tag) => { | |
| const nu = SugarElement.fromTag(tag); | |
| const attributes = clone$2(original); | |
| setAll$1(nu, attributes); | |
| return nu; | |
| }; | |
| const copy$2 = (original, tag) => { | |
| const nu = shallowAs(original, tag); | |
| const cloneChildren = children$2(deep(original)); | |
| append(nu, cloneChildren); | |
| return nu; | |
| }; | |
| const mutate$1 = (original, tag) => { | |
| const nu = shallowAs(original, tag); | |
| after$5(original, nu); | |
| const children = children$2(original); | |
| append(nu, children); | |
| remove$6(original); | |
| return nu; | |
| }; | |
| const validSectionList = [ | |
| 'tfoot', | |
| 'thead', | |
| 'tbody', | |
| 'colgroup' | |
| ]; | |
| const isValidSection = parentName => contains$2(validSectionList, parentName); | |
| const grid = (rows, columns) => ({ | |
| rows, | |
| columns | |
| }); | |
| const address = (row, column) => ({ | |
| row, | |
| column | |
| }); | |
| const detail = (element, rowspan, colspan) => ({ | |
| element, | |
| rowspan, | |
| colspan | |
| }); | |
| const detailnew = (element, rowspan, colspan, isNew) => ({ | |
| element, | |
| rowspan, | |
| colspan, | |
| isNew | |
| }); | |
| const extended = (element, rowspan, colspan, row, column, isLocked) => ({ | |
| element, | |
| rowspan, | |
| colspan, | |
| row, | |
| column, | |
| isLocked | |
| }); | |
| const rowdetail = (element, cells, section) => ({ | |
| element, | |
| cells, | |
| section | |
| }); | |
| const rowdetailnew = (element, cells, section, isNew) => ({ | |
| element, | |
| cells, | |
| section, | |
| isNew | |
| }); | |
| const elementnew = (element, isNew, isLocked) => ({ | |
| element, | |
| isNew, | |
| isLocked | |
| }); | |
| const rowcells = (element, cells, section, isNew) => ({ | |
| element, | |
| cells, | |
| section, | |
| isNew | |
| }); | |
| const bounds = (startRow, startCol, finishRow, finishCol) => ({ | |
| startRow, | |
| startCol, | |
| finishRow, | |
| finishCol | |
| }); | |
| const columnext = (element, colspan, column) => ({ | |
| element, | |
| colspan, | |
| column | |
| }); | |
| const colgroup = (element, columns) => ({ | |
| element, | |
| columns | |
| }); | |
| const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host); | |
| const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode); | |
| const isSupported$1 = constant(supported); | |
| const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner; | |
| const getShadowRoot = e => { | |
| const r = getRootNode(e); | |
| return isShadowRoot(r) ? Optional.some(r) : Optional.none(); | |
| }; | |
| const getShadowHost = e => SugarElement.fromDom(e.dom.host); | |
| const getOriginalEventTarget = event => { | |
| if (isSupported$1() && isNonNullable(event.target)) { | |
| const el = SugarElement.fromDom(event.target); | |
| if (isElement(el) && isOpenShadowHost(el)) { | |
| if (event.composed && event.composedPath) { | |
| const composedPath = event.composedPath(); | |
| if (composedPath) { | |
| return head(composedPath); | |
| } | |
| } | |
| } | |
| } | |
| return Optional.from(event.target); | |
| }; | |
| const isOpenShadowHost = element => isNonNullable(element.dom.shadowRoot); | |
| const inBody = element => { | |
| const dom = isText(element) ? element.dom.parentNode : element.dom; | |
| if (dom === undefined || dom === null || dom.ownerDocument === null) { | |
| return false; | |
| } | |
| const doc = dom.ownerDocument; | |
| return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost)); | |
| }; | |
| const body$1 = () => getBody$1(SugarElement.fromDom(document)); | |
| const getBody$1 = doc => { | |
| const b = doc.dom.body; | |
| if (b === null || b === undefined) { | |
| throw new Error('Body is not available yet'); | |
| } | |
| return SugarElement.fromDom(b); | |
| }; | |
| const ancestors$4 = (scope, predicate, isRoot) => filter$2(parents(scope, isRoot), predicate); | |
| const children$1 = (scope, predicate) => filter$2(children$2(scope), predicate); | |
| const descendants$1 = (scope, predicate) => { | |
| let result = []; | |
| each$2(children$2(scope), x => { | |
| if (predicate(x)) { | |
| result = result.concat([x]); | |
| } | |
| result = result.concat(descendants$1(x, predicate)); | |
| }); | |
| return result; | |
| }; | |
| const ancestors$3 = (scope, selector, isRoot) => ancestors$4(scope, e => is$2(e, selector), isRoot); | |
| const children = (scope, selector) => children$1(scope, e => is$2(e, selector)); | |
| const descendants = (scope, selector) => all$1(selector, scope); | |
| var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => { | |
| if (is(scope, a)) { | |
| return Optional.some(scope); | |
| } else if (isFunction(isRoot) && isRoot(scope)) { | |
| return Optional.none(); | |
| } else { | |
| return ancestor(scope, a, isRoot); | |
| } | |
| }; | |
| const ancestor$2 = (scope, predicate, isRoot) => { | |
| let element = scope.dom; | |
| const stop = isFunction(isRoot) ? isRoot : never; | |
| while (element.parentNode) { | |
| element = element.parentNode; | |
| const el = SugarElement.fromDom(element); | |
| if (predicate(el)) { | |
| return Optional.some(el); | |
| } else if (stop(el)) { | |
| break; | |
| } | |
| } | |
| return Optional.none(); | |
| }; | |
| const closest$2 = (scope, predicate, isRoot) => { | |
| const is = (s, test) => test(s); | |
| return ClosestOrAncestor(is, ancestor$2, scope, predicate, isRoot); | |
| }; | |
| const child$1 = (scope, predicate) => { | |
| const pred = node => predicate(SugarElement.fromDom(node)); | |
| const result = find$1(scope.dom.childNodes, pred); | |
| return result.map(SugarElement.fromDom); | |
| }; | |
| const descendant$1 = (scope, predicate) => { | |
| const descend = node => { | |
| for (let i = 0; i < node.childNodes.length; i++) { | |
| const child = SugarElement.fromDom(node.childNodes[i]); | |
| if (predicate(child)) { | |
| return Optional.some(child); | |
| } | |
| const res = descend(node.childNodes[i]); | |
| if (res.isSome()) { | |
| return res; | |
| } | |
| } | |
| return Optional.none(); | |
| }; | |
| return descend(scope.dom); | |
| }; | |
| const ancestor$1 = (scope, selector, isRoot) => ancestor$2(scope, e => is$2(e, selector), isRoot); | |
| const child = (scope, selector) => child$1(scope, e => is$2(e, selector)); | |
| const descendant = (scope, selector) => one(selector, scope); | |
| const closest$1 = (scope, selector, isRoot) => { | |
| const is = (element, selector) => is$2(element, selector); | |
| return ClosestOrAncestor(is, ancestor$1, scope, selector, isRoot); | |
| }; | |
| const is = (lhs, rhs, comparator = tripleEquals) => lhs.exists(left => comparator(left, rhs)); | |
| const cat = arr => { | |
| const r = []; | |
| const push = x => { | |
| r.push(x); | |
| }; | |
| for (let i = 0; i < arr.length; i++) { | |
| arr[i].each(push); | |
| } | |
| return r; | |
| }; | |
| const bindFrom = (a, f) => a !== undefined && a !== null ? f(a) : Optional.none(); | |
| const someIf = (b, a) => b ? Optional.some(a) : Optional.none(); | |
| const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr; | |
| const contains = (str, substr, start = 0, end) => { | |
| const idx = str.indexOf(substr, start); | |
| if (idx !== -1) { | |
| return isUndefined(end) ? true : idx + substr.length <= end; | |
| } else { | |
| return false; | |
| } | |
| }; | |
| const startsWith = (str, prefix) => { | |
| return checkRange(str, prefix, 0); | |
| }; | |
| const endsWith = (str, suffix) => { | |
| return checkRange(str, suffix, str.length - suffix.length); | |
| }; | |
| const blank = r => s => s.replace(r, ''); | |
| const trim = blank(/^\s+|\s+$/g); | |
| const isNotEmpty = s => s.length > 0; | |
| const toFloat = value => { | |
| const num = parseFloat(value); | |
| return isNaN(num) ? Optional.none() : Optional.some(num); | |
| }; | |
| const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue); | |
| const internalSet = (dom, property, value) => { | |
| if (!isString(value)) { | |
| console.error('Invalid call to CSS.set. Property ', property, ':: Value ', value, ':: Element ', dom); | |
| throw new Error('CSS value must be a string: ' + value); | |
| } | |
| if (isSupported(dom)) { | |
| dom.style.setProperty(property, value); | |
| } | |
| }; | |
| const internalRemove = (dom, property) => { | |
| if (isSupported(dom)) { | |
| dom.style.removeProperty(property); | |
| } | |
| }; | |
| const set$1 = (element, property, value) => { | |
| const dom = element.dom; | |
| internalSet(dom, property, value); | |
| }; | |
| const setAll = (element, css) => { | |
| const dom = element.dom; | |
| each$1(css, (v, k) => { | |
| internalSet(dom, k, v); | |
| }); | |
| }; | |
| const get$a = (element, property) => { | |
| const dom = element.dom; | |
| const styles = window.getComputedStyle(dom); | |
| const r = styles.getPropertyValue(property); | |
| return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r; | |
| }; | |
| const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : ''; | |
| const getRaw$2 = (element, property) => { | |
| const dom = element.dom; | |
| const raw = getUnsafeProperty(dom, property); | |
| return Optional.from(raw).filter(r => r.length > 0); | |
| }; | |
| const remove$5 = (element, property) => { | |
| const dom = element.dom; | |
| internalRemove(dom, property); | |
| if (is(getOpt(element, 'style').map(trim), '')) { | |
| remove$7(element, 'style'); | |
| } | |
| }; | |
| const copy$1 = (source, target) => { | |
| const sourceDom = source.dom; | |
| const targetDom = target.dom; | |
| if (isSupported(sourceDom) && isSupported(targetDom)) { | |
| targetDom.style.cssText = sourceDom.style.cssText; | |
| } | |
| }; | |
| const getAttrValue = (cell, name, fallback = 0) => getOpt(cell, name).map(value => parseInt(value, 10)).getOr(fallback); | |
| const getSpan = (cell, type) => getAttrValue(cell, type, 1); | |
| const hasColspan = cellOrCol => { | |
| if (isTag('col')(cellOrCol)) { | |
| return getAttrValue(cellOrCol, 'span', 1) > 1; | |
| } else { | |
| return getSpan(cellOrCol, 'colspan') > 1; | |
| } | |
| }; | |
| const hasRowspan = cell => getSpan(cell, 'rowspan') > 1; | |
| const getCssValue = (element, property) => parseInt(get$a(element, property), 10); | |
| const minWidth = constant(10); | |
| const minHeight = constant(10); | |
| const firstLayer = (scope, selector) => { | |
| return filterFirstLayer(scope, selector, always); | |
| }; | |
| const filterFirstLayer = (scope, selector, predicate) => { | |
| return bind$2(children$2(scope), x => { | |
| if (is$2(x, selector)) { | |
| return predicate(x) ? [x] : []; | |
| } else { | |
| return filterFirstLayer(x, selector, predicate); | |
| } | |
| }); | |
| }; | |
| const lookup = (tags, element, isRoot = never) => { | |
| if (isRoot(element)) { | |
| return Optional.none(); | |
| } | |
| if (contains$2(tags, name(element))) { | |
| return Optional.some(element); | |
| } | |
| const isRootOrUpperTable = elm => is$2(elm, 'table') || isRoot(elm); | |
| return ancestor$1(element, tags.join(','), isRootOrUpperTable); | |
| }; | |
| const cell = (element, isRoot) => lookup([ | |
| 'td', | |
| 'th' | |
| ], element, isRoot); | |
| const cells$1 = ancestor => firstLayer(ancestor, 'th,td'); | |
| const columns$1 = ancestor => { | |
| if (is$2(ancestor, 'colgroup')) { | |
| return children(ancestor, 'col'); | |
| } else { | |
| return bind$2(columnGroups(ancestor), columnGroup => children(columnGroup, 'col')); | |
| } | |
| }; | |
| const table = (element, isRoot) => closest$1(element, 'table', isRoot); | |
| const rows$1 = ancestor => firstLayer(ancestor, 'tr'); | |
| const columnGroups = ancestor => table(ancestor).fold(constant([]), table => children(table, 'colgroup')); | |
| const fromRowsOrColGroups = (elems, getSection) => map$1(elems, row => { | |
| if (name(row) === 'colgroup') { | |
| const cells = map$1(columns$1(row), column => { | |
| const colspan = getAttrValue(column, 'span', 1); | |
| return detail(column, 1, colspan); | |
| }); | |
| return rowdetail(row, cells, 'colgroup'); | |
| } else { | |
| const cells = map$1(cells$1(row), cell => { | |
| const rowspan = getAttrValue(cell, 'rowspan', 1); | |
| const colspan = getAttrValue(cell, 'colspan', 1); | |
| return detail(cell, rowspan, colspan); | |
| }); | |
| return rowdetail(row, cells, getSection(row)); | |
| } | |
| }); | |
| const getParentSection = group => parent(group).map(parent => { | |
| const parentName = name(parent); | |
| return isValidSection(parentName) ? parentName : 'tbody'; | |
| }).getOr('tbody'); | |
| const fromTable$1 = table => { | |
| const rows = rows$1(table); | |
| const columnGroups$1 = columnGroups(table); | |
| const elems = [ | |
| ...columnGroups$1, | |
| ...rows | |
| ]; | |
| return fromRowsOrColGroups(elems, getParentSection); | |
| }; | |
| const fromPastedRows = (elems, section) => fromRowsOrColGroups(elems, () => section); | |
| const cached = f => { | |
| let called = false; | |
| let r; | |
| return (...args) => { | |
| if (!called) { | |
| called = true; | |
| r = f.apply(null, args); | |
| } | |
| return r; | |
| }; | |
| }; | |
| const DeviceType = (os, browser, userAgent, mediaMatch) => { | |
| const isiPad = os.isiOS() && /ipad/i.test(userAgent) === true; | |
| const isiPhone = os.isiOS() && !isiPad; | |
| const isMobile = os.isiOS() || os.isAndroid(); | |
| const isTouch = isMobile || mediaMatch('(pointer:coarse)'); | |
| const isTablet = isiPad || !isiPhone && isMobile && mediaMatch('(min-device-width:768px)'); | |
| const isPhone = isiPhone || isMobile && !isTablet; | |
| const iOSwebview = browser.isSafari() && os.isiOS() && /safari/i.test(userAgent) === false; | |
| const isDesktop = !isPhone && !isTablet && !iOSwebview; | |
| return { | |
| isiPad: constant(isiPad), | |
| isiPhone: constant(isiPhone), | |
| isTablet: constant(isTablet), | |
| isPhone: constant(isPhone), | |
| isTouch: constant(isTouch), | |
| isAndroid: os.isAndroid, | |
| isiOS: os.isiOS, | |
| isWebView: constant(iOSwebview), | |
| isDesktop: constant(isDesktop) | |
| }; | |
| }; | |
| const firstMatch = (regexes, s) => { | |
| for (let i = 0; i < regexes.length; i++) { | |
| const x = regexes[i]; | |
| if (x.test(s)) { | |
| return x; | |
| } | |
| } | |
| return undefined; | |
| }; | |
| const find = (regexes, agent) => { | |
| const r = firstMatch(regexes, agent); | |
| if (!r) { | |
| return { | |
| major: 0, | |
| minor: 0 | |
| }; | |
| } | |
| const group = i => { | |
| return Number(agent.replace(r, '$' + i)); | |
| }; | |
| return nu$2(group(1), group(2)); | |
| }; | |
| const detect$5 = (versionRegexes, agent) => { | |
| const cleanedAgent = String(agent).toLowerCase(); | |
| if (versionRegexes.length === 0) { | |
| return unknown$2(); | |
| } | |
| return find(versionRegexes, cleanedAgent); | |
| }; | |
| const unknown$2 = () => { | |
| return nu$2(0, 0); | |
| }; | |
| const nu$2 = (major, minor) => { | |
| return { | |
| major, | |
| minor | |
| }; | |
| }; | |
| const Version = { | |
| nu: nu$2, | |
| detect: detect$5, | |
| unknown: unknown$2 | |
| }; | |
| const detectBrowser$1 = (browsers, userAgentData) => { | |
| return findMap(userAgentData.brands, uaBrand => { | |
| const lcBrand = uaBrand.brand.toLowerCase(); | |
| return find$1(browsers, browser => { | |
| var _a; | |
| return lcBrand === ((_a = browser.brand) === null || _a === void 0 ? void 0 : _a.toLowerCase()); | |
| }).map(info => ({ | |
| current: info.name, | |
| version: Version.nu(parseInt(uaBrand.version, 10), 0) | |
| })); | |
| }); | |
| }; | |
| const detect$4 = (candidates, userAgent) => { | |
| const agent = String(userAgent).toLowerCase(); | |
| return find$1(candidates, candidate => { | |
| return candidate.search(agent); | |
| }); | |
| }; | |
| const detectBrowser = (browsers, userAgent) => { | |
| return detect$4(browsers, userAgent).map(browser => { | |
| const version = Version.detect(browser.versionRegexes, userAgent); | |
| return { | |
| current: browser.name, | |
| version | |
| }; | |
| }); | |
| }; | |
| const detectOs = (oses, userAgent) => { | |
| return detect$4(oses, userAgent).map(os => { | |
| const version = Version.detect(os.versionRegexes, userAgent); | |
| return { | |
| current: os.name, | |
| version | |
| }; | |
| }); | |
| }; | |
| const normalVersionRegex = /.*?version\/\ ?([0-9]+)\.([0-9]+).*/; | |
| const checkContains = target => { | |
| return uastring => { | |
| return contains(uastring, target); | |
| }; | |
| }; | |
| const browsers = [ | |
| { | |
| name: 'Edge', | |
| versionRegexes: [/.*?edge\/ ?([0-9]+)\.([0-9]+)$/], | |
| search: uastring => { | |
| return contains(uastring, 'edge/') && contains(uastring, 'chrome') && contains(uastring, 'safari') && contains(uastring, 'applewebkit'); | |
| } | |
| }, | |
| { | |
| name: 'Chromium', | |
| brand: 'Chromium', | |
| versionRegexes: [ | |
| /.*?chrome\/([0-9]+)\.([0-9]+).*/, | |
| normalVersionRegex | |
| ], | |
| search: uastring => { | |
| return contains(uastring, 'chrome') && !contains(uastring, 'chromeframe'); | |
| } | |
| }, | |
| { | |
| name: 'IE', | |
| versionRegexes: [ | |
| /.*?msie\ ?([0-9]+)\.([0-9]+).*/, | |
| /.*?rv:([0-9]+)\.([0-9]+).*/ | |
| ], | |
| search: uastring => { | |
| return contains(uastring, 'msie') || contains(uastring, 'trident'); | |
| } | |
| }, | |
| { | |
| name: 'Opera', | |
| versionRegexes: [ | |
| normalVersionRegex, | |
| /.*?opera\/([0-9]+)\.([0-9]+).*/ | |
| ], | |
| search: checkContains('opera') | |
| }, | |
| { | |
| name: 'Firefox', | |
| versionRegexes: [/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/], | |
| search: checkContains('firefox') | |
| }, | |
| { | |
| name: 'Safari', | |
| versionRegexes: [ | |
| normalVersionRegex, | |
| /.*?cpu os ([0-9]+)_([0-9]+).*/ | |
| ], | |
| search: uastring => { | |
| return (contains(uastring, 'safari') || contains(uastring, 'mobile/')) && contains(uastring, 'applewebkit'); | |
| } | |
| } | |
| ]; | |
| const oses = [ | |
| { | |
| name: 'Windows', | |
| search: checkContains('win'), | |
| versionRegexes: [/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/] | |
| }, | |
| { | |
| name: 'iOS', | |
| search: uastring => { | |
| return contains(uastring, 'iphone') || contains(uastring, 'ipad'); | |
| }, | |
| versionRegexes: [ | |
| /.*?version\/\ ?([0-9]+)\.([0-9]+).*/, | |
| /.*cpu os ([0-9]+)_([0-9]+).*/, | |
| /.*cpu iphone os ([0-9]+)_([0-9]+).*/ | |
| ] | |
| }, | |
| { | |
| name: 'Android', | |
| search: checkContains('android'), | |
| versionRegexes: [/.*?android\ ?([0-9]+)\.([0-9]+).*/] | |
| }, | |
| { | |
| name: 'macOS', | |
| search: checkContains('mac os x'), | |
| versionRegexes: [/.*?mac\ os\ x\ ?([0-9]+)_([0-9]+).*/] | |
| }, | |
| { | |
| name: 'Linux', | |
| search: checkContains('linux'), | |
| versionRegexes: [] | |
| }, | |
| { | |
| name: 'Solaris', | |
| search: checkContains('sunos'), | |
| versionRegexes: [] | |
| }, | |
| { | |
| name: 'FreeBSD', | |
| search: checkContains('freebsd'), | |
| versionRegexes: [] | |
| }, | |
| { | |
| name: 'ChromeOS', | |
| search: checkContains('cros'), | |
| versionRegexes: [/.*?chrome\/([0-9]+)\.([0-9]+).*/] | |
| } | |
| ]; | |
| const PlatformInfo = { | |
| browsers: constant(browsers), | |
| oses: constant(oses) | |
| }; | |
| const edge = 'Edge'; | |
| const chromium = 'Chromium'; | |
| const ie = 'IE'; | |
| const opera = 'Opera'; | |
| const firefox = 'Firefox'; | |
| const safari = 'Safari'; | |
| const unknown$1 = () => { | |
| return nu$1({ | |
| current: undefined, | |
| version: Version.unknown() | |
| }); | |
| }; | |
| const nu$1 = info => { | |
| const current = info.current; | |
| const version = info.version; | |
| const isBrowser = name => () => current === name; | |
| return { | |
| current, | |
| version, | |
| isEdge: isBrowser(edge), | |
| isChromium: isBrowser(chromium), | |
| isIE: isBrowser(ie), | |
| isOpera: isBrowser(opera), | |
| isFirefox: isBrowser(firefox), | |
| isSafari: isBrowser(safari) | |
| }; | |
| }; | |
| const Browser = { | |
| unknown: unknown$1, | |
| nu: nu$1, | |
| edge: constant(edge), | |
| chromium: constant(chromium), | |
| ie: constant(ie), | |
| opera: constant(opera), | |
| firefox: constant(firefox), | |
| safari: constant(safari) | |
| }; | |
| const windows = 'Windows'; | |
| const ios = 'iOS'; | |
| const android = 'Android'; | |
| const linux = 'Linux'; | |
| const macos = 'macOS'; | |
| const solaris = 'Solaris'; | |
| const freebsd = 'FreeBSD'; | |
| const chromeos = 'ChromeOS'; | |
| const unknown = () => { | |
| return nu({ | |
| current: undefined, | |
| version: Version.unknown() | |
| }); | |
| }; | |
| const nu = info => { | |
| const current = info.current; | |
| const version = info.version; | |
| const isOS = name => () => current === name; | |
| return { | |
| current, | |
| version, | |
| isWindows: isOS(windows), | |
| isiOS: isOS(ios), | |
| isAndroid: isOS(android), | |
| isMacOS: isOS(macos), | |
| isLinux: isOS(linux), | |
| isSolaris: isOS(solaris), | |
| isFreeBSD: isOS(freebsd), | |
| isChromeOS: isOS(chromeos) | |
| }; | |
| }; | |
| const OperatingSystem = { | |
| unknown, | |
| nu, | |
| windows: constant(windows), | |
| ios: constant(ios), | |
| android: constant(android), | |
| linux: constant(linux), | |
| macos: constant(macos), | |
| solaris: constant(solaris), | |
| freebsd: constant(freebsd), | |
| chromeos: constant(chromeos) | |
| }; | |
| const detect$3 = (userAgent, userAgentDataOpt, mediaMatch) => { | |
| const browsers = PlatformInfo.browsers(); | |
| const oses = PlatformInfo.oses(); | |
| const browser = userAgentDataOpt.bind(userAgentData => detectBrowser$1(browsers, userAgentData)).orThunk(() => detectBrowser(browsers, userAgent)).fold(Browser.unknown, Browser.nu); | |
| const os = detectOs(oses, userAgent).fold(OperatingSystem.unknown, OperatingSystem.nu); | |
| const deviceType = DeviceType(os, browser, userAgent, mediaMatch); | |
| return { | |
| browser, | |
| os, | |
| deviceType | |
| }; | |
| }; | |
| const PlatformDetection = { detect: detect$3 }; | |
| const mediaMatch = query => window.matchMedia(query).matches; | |
| let platform = cached(() => PlatformDetection.detect(navigator.userAgent, Optional.from(navigator.userAgentData), mediaMatch)); | |
| const detect$2 = () => platform(); | |
| const Dimension = (name, getOffset) => { | |
| const set = (element, h) => { | |
| if (!isNumber(h) && !h.match(/^[0-9]+$/)) { | |
| throw new Error(name + '.set accepts only positive integer values. Value was ' + h); | |
| } | |
| const dom = element.dom; | |
| if (isSupported(dom)) { | |
| dom.style[name] = h + 'px'; | |
| } | |
| }; | |
| const get = element => { | |
| const r = getOffset(element); | |
| if (r <= 0 || r === null) { | |
| const css = get$a(element, name); | |
| return parseFloat(css) || 0; | |
| } | |
| return r; | |
| }; | |
| const getOuter = get; | |
| const aggregate = (element, properties) => foldl(properties, (acc, property) => { | |
| const val = get$a(element, property); | |
| const value = val === undefined ? 0 : parseInt(val, 10); | |
| return isNaN(value) ? acc : acc + value; | |
| }, 0); | |
| const max = (element, value, properties) => { | |
| const cumulativeInclusions = aggregate(element, properties); | |
| const absoluteMax = value > cumulativeInclusions ? value - cumulativeInclusions : 0; | |
| return absoluteMax; | |
| }; | |
| return { | |
| set, | |
| get, | |
| getOuter, | |
| aggregate, | |
| max | |
| }; | |
| }; | |
| const toNumber = (px, fallback) => toFloat(px).getOr(fallback); | |
| const getProp = (element, name, fallback) => toNumber(get$a(element, name), fallback); | |
| const calcContentBoxSize = (element, size, upper, lower) => { | |
| const paddingUpper = getProp(element, `padding-${ upper }`, 0); | |
| const paddingLower = getProp(element, `padding-${ lower }`, 0); | |
| const borderUpper = getProp(element, `border-${ upper }-width`, 0); | |
| const borderLower = getProp(element, `border-${ lower }-width`, 0); | |
| return size - paddingUpper - paddingLower - borderUpper - borderLower; | |
| }; | |
| const getCalculatedWidth = (element, boxSizing) => { | |
| const dom = element.dom; | |
| const width = dom.getBoundingClientRect().width || dom.offsetWidth; | |
| return boxSizing === 'border-box' ? width : calcContentBoxSize(element, width, 'left', 'right'); | |
| }; | |
| const getHeight$1 = element => getProp(element, 'height', element.dom.offsetHeight); | |
| const getWidth = element => getProp(element, 'width', element.dom.offsetWidth); | |
| const getInnerWidth = element => getCalculatedWidth(element, 'content-box'); | |
| const api$2 = Dimension('width', element => element.dom.offsetWidth); | |
| const get$9 = element => api$2.get(element); | |
| const getOuter$2 = element => api$2.getOuter(element); | |
| const getInner = getInnerWidth; | |
| const getRuntime$1 = getWidth; | |
| const addCells = (gridRow, index, cells) => { | |
| const existingCells = gridRow.cells; | |
| const before = existingCells.slice(0, index); | |
| const after = existingCells.slice(index); | |
| const newCells = before.concat(cells).concat(after); | |
| return setCells(gridRow, newCells); | |
| }; | |
| const addCell = (gridRow, index, cell) => addCells(gridRow, index, [cell]); | |
| const mutateCell = (gridRow, index, cell) => { | |
| const cells = gridRow.cells; | |
| cells[index] = cell; | |
| }; | |
| const setCells = (gridRow, cells) => rowcells(gridRow.element, cells, gridRow.section, gridRow.isNew); | |
| const mapCells = (gridRow, f) => { | |
| const cells = gridRow.cells; | |
| const r = map$1(cells, f); | |
| return rowcells(gridRow.element, r, gridRow.section, gridRow.isNew); | |
| }; | |
| const getCell = (gridRow, index) => gridRow.cells[index]; | |
| const getCellElement = (gridRow, index) => getCell(gridRow, index).element; | |
| const cellLength = gridRow => gridRow.cells.length; | |
| const extractGridDetails = grid => { | |
| const result = partition(grid, row => row.section === 'colgroup'); | |
| return { | |
| rows: result.fail, | |
| cols: result.pass | |
| }; | |
| }; | |
| const clone = (gridRow, cloneRow, cloneCell) => { | |
| const newCells = map$1(gridRow.cells, cloneCell); | |
| return rowcells(cloneRow(gridRow.element), newCells, gridRow.section, true); | |
| }; | |
| const LOCKED_COL_ATTR = 'data-snooker-locked-cols'; | |
| const getLockedColumnsFromTable = table => getOpt(table, LOCKED_COL_ATTR).bind(lockedColStr => Optional.from(lockedColStr.match(/\d+/g))).map(lockedCols => mapToObject(lockedCols, always)); | |
| const getLockedColumnsFromGrid = grid => { | |
| const locked = foldl(extractGridDetails(grid).rows, (acc, row) => { | |
| each$2(row.cells, (cell, idx) => { | |
| if (cell.isLocked) { | |
| acc[idx] = true; | |
| } | |
| }); | |
| return acc; | |
| }, {}); | |
| const lockedArr = mapToArray(locked, (_val, key) => parseInt(key, 10)); | |
| return sort$1(lockedArr); | |
| }; | |
| const key = (row, column) => { | |
| return row + ',' + column; | |
| }; | |
| const getAt = (warehouse, row, column) => Optional.from(warehouse.access[key(row, column)]); | |
| const findItem = (warehouse, item, comparator) => { | |
| const filtered = filterItems(warehouse, detail => { | |
| return comparator(item, detail.element); | |
| }); | |
| return filtered.length > 0 ? Optional.some(filtered[0]) : Optional.none(); | |
| }; | |
| const filterItems = (warehouse, predicate) => { | |
| const all = bind$2(warehouse.all, r => { | |
| return r.cells; | |
| }); | |
| return filter$2(all, predicate); | |
| }; | |
| const generateColumns = rowData => { | |
| const columnsGroup = {}; | |
| let index = 0; | |
| each$2(rowData.cells, column => { | |
| const colspan = column.colspan; | |
| range$1(colspan, columnIndex => { | |
| const colIndex = index + columnIndex; | |
| columnsGroup[colIndex] = columnext(column.element, colspan, colIndex); | |
| }); | |
| index += colspan; | |
| }); | |
| return columnsGroup; | |
| }; | |
| const generate$1 = list => { | |
| const access = {}; | |
| const cells = []; | |
| const tableOpt = head(list).map(rowData => rowData.element).bind(table); | |
| const lockedColumns = tableOpt.bind(getLockedColumnsFromTable).getOr({}); | |
| let maxRows = 0; | |
| let maxColumns = 0; | |
| let rowCount = 0; | |
| const { | |
| pass: colgroupRows, | |
| fail: rows | |
| } = partition(list, rowData => rowData.section === 'colgroup'); | |
| each$2(rows, rowData => { | |
| const currentRow = []; | |
| each$2(rowData.cells, rowCell => { | |
| let start = 0; | |
| while (access[key(rowCount, start)] !== undefined) { | |
| start++; | |
| } | |
| const isLocked = hasNonNullableKey(lockedColumns, start.toString()); | |
| const current = extended(rowCell.element, rowCell.rowspan, rowCell.colspan, rowCount, start, isLocked); | |
| for (let occupiedColumnPosition = 0; occupiedColumnPosition < rowCell.colspan; occupiedColumnPosition++) { | |
| for (let occupiedRowPosition = 0; occupiedRowPosition < rowCell.rowspan; occupiedRowPosition++) { | |
| const rowPosition = rowCount + occupiedRowPosition; | |
| const columnPosition = start + occupiedColumnPosition; | |
| const newpos = key(rowPosition, columnPosition); | |
| access[newpos] = current; | |
| maxColumns = Math.max(maxColumns, columnPosition + 1); | |
| } | |
| } | |
| currentRow.push(current); | |
| }); | |
| maxRows++; | |
| cells.push(rowdetail(rowData.element, currentRow, rowData.section)); | |
| rowCount++; | |
| }); | |
| const {columns, colgroups} = last$2(colgroupRows).map(rowData => { | |
| const columns = generateColumns(rowData); | |
| const colgroup$1 = colgroup(rowData.element, values(columns)); | |
| return { | |
| colgroups: [colgroup$1], | |
| columns | |
| }; | |
| }).getOrThunk(() => ({ | |
| colgroups: [], | |
| columns: {} | |
| })); | |
| const grid$1 = grid(maxRows, maxColumns); | |
| return { | |
| grid: grid$1, | |
| access, | |
| all: cells, | |
| columns, | |
| colgroups | |
| }; | |
| }; | |
| const fromTable = table => { | |
| const list = fromTable$1(table); | |
| return generate$1(list); | |
| }; | |
| const justCells = warehouse => bind$2(warehouse.all, w => w.cells); | |
| const justColumns = warehouse => values(warehouse.columns); | |
| const hasColumns = warehouse => keys(warehouse.columns).length > 0; | |
| const getColumnAt = (warehouse, columnIndex) => Optional.from(warehouse.columns[columnIndex]); | |
| const Warehouse = { | |
| fromTable, | |
| generate: generate$1, | |
| getAt, | |
| findItem, | |
| filterItems, | |
| justCells, | |
| justColumns, | |
| hasColumns, | |
| getColumnAt | |
| }; | |
| const columns = (warehouse, isValidCell = always) => { | |
| const grid = warehouse.grid; | |
| const cols = range$1(grid.columns, identity); | |
| const rowsArr = range$1(grid.rows, identity); | |
| return map$1(cols, col => { | |
| const getBlock = () => bind$2(rowsArr, r => Warehouse.getAt(warehouse, r, col).filter(detail => detail.column === col).toArray()); | |
| const isValid = detail => detail.colspan === 1 && isValidCell(detail.element); | |
| const getFallback = () => Warehouse.getAt(warehouse, 0, col); | |
| return decide(getBlock, isValid, getFallback); | |
| }); | |
| }; | |
| const decide = (getBlock, isValid, getFallback) => { | |
| const inBlock = getBlock(); | |
| const validInBlock = find$1(inBlock, isValid); | |
| const detailOption = validInBlock.orThunk(() => Optional.from(inBlock[0]).orThunk(getFallback)); | |
| return detailOption.map(detail => detail.element); | |
| }; | |
| const rows = warehouse => { | |
| const grid = warehouse.grid; | |
| const rowsArr = range$1(grid.rows, identity); | |
| const cols = range$1(grid.columns, identity); | |
| return map$1(rowsArr, row => { | |
| const getBlock = () => bind$2(cols, c => Warehouse.getAt(warehouse, row, c).filter(detail => detail.row === row).fold(constant([]), detail => [detail])); | |
| const isSingle = detail => detail.rowspan === 1; | |
| const getFallback = () => Warehouse.getAt(warehouse, row, 0); | |
| return decide(getBlock, isSingle, getFallback); | |
| }); | |
| }; | |
| const deduce = (xs, index) => { | |
| if (index < 0 || index >= xs.length - 1) { | |
| return Optional.none(); | |
| } | |
| const current = xs[index].fold(() => { | |
| const rest = reverse(xs.slice(0, index)); | |
| return findMap(rest, (a, i) => a.map(aa => ({ | |
| value: aa, | |
| delta: i + 1 | |
| }))); | |
| }, c => Optional.some({ | |
| value: c, | |
| delta: 0 | |
| })); | |
| const next = xs[index + 1].fold(() => { | |
| const rest = xs.slice(index + 1); | |
| return findMap(rest, (a, i) => a.map(aa => ({ | |
| value: aa, | |
| delta: i + 1 | |
| }))); | |
| }, n => Optional.some({ | |
| value: n, | |
| delta: 1 | |
| })); | |
| return current.bind(c => next.map(n => { | |
| const extras = n.delta + c.delta; | |
| return Math.abs(n.value - c.value) / extras; | |
| })); | |
| }; | |
| const onDirection = (isLtr, isRtl) => element => getDirection(element) === 'rtl' ? isRtl : isLtr; | |
| const getDirection = element => get$a(element, 'direction') === 'rtl' ? 'rtl' : 'ltr'; | |
| const api$1 = Dimension('height', element => { | |
| const dom = element.dom; | |
| return inBody(element) ? dom.getBoundingClientRect().height : dom.offsetHeight; | |
| }); | |
| const get$8 = element => api$1.get(element); | |
| const getOuter$1 = element => api$1.getOuter(element); | |
| const getRuntime = getHeight$1; | |
| const r = (left, top) => { | |
| const translate = (x, y) => r(left + x, top + y); | |
| return { | |
| left, | |
| top, | |
| translate | |
| }; | |
| }; | |
| const SugarPosition = r; | |
| const boxPosition = dom => { | |
| const box = dom.getBoundingClientRect(); | |
| return SugarPosition(box.left, box.top); | |
| }; | |
| const firstDefinedOrZero = (a, b) => { | |
| if (a !== undefined) { | |
| return a; | |
| } else { | |
| return b !== undefined ? b : 0; | |
| } | |
| }; | |
| const absolute = element => { | |
| const doc = element.dom.ownerDocument; | |
| const body = doc.body; | |
| const win = doc.defaultView; | |
| const html = doc.documentElement; | |
| if (body === element.dom) { | |
| return SugarPosition(body.offsetLeft, body.offsetTop); | |
| } | |
| const scrollTop = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageYOffset, html.scrollTop); | |
| const scrollLeft = firstDefinedOrZero(win === null || win === void 0 ? void 0 : win.pageXOffset, html.scrollLeft); | |
| const clientTop = firstDefinedOrZero(html.clientTop, body.clientTop); | |
| const clientLeft = firstDefinedOrZero(html.clientLeft, body.clientLeft); | |
| return viewport(element).translate(scrollLeft - clientLeft, scrollTop - clientTop); | |
| }; | |
| const viewport = element => { | |
| const dom = element.dom; | |
| const doc = dom.ownerDocument; | |
| const body = doc.body; | |
| if (body === dom) { | |
| return SugarPosition(body.offsetLeft, body.offsetTop); | |
| } | |
| if (!inBody(element)) { | |
| return SugarPosition(0, 0); | |
| } | |
| return boxPosition(dom); | |
| }; | |
| const rowInfo = (row, y) => ({ | |
| row, | |
| y | |
| }); | |
| const colInfo = (col, x) => ({ | |
| col, | |
| x | |
| }); | |
| const rtlEdge = cell => { | |
| const pos = absolute(cell); | |
| return pos.left + getOuter$2(cell); | |
| }; | |
| const ltrEdge = cell => { | |
| return absolute(cell).left; | |
| }; | |
| const getLeftEdge = (index, cell) => { | |
| return colInfo(index, ltrEdge(cell)); | |
| }; | |
| const getRightEdge = (index, cell) => { | |
| return colInfo(index, rtlEdge(cell)); | |
| }; | |
| const getTop$1 = cell => { | |
| return absolute(cell).top; | |
| }; | |
| const getTopEdge = (index, cell) => { | |
| return rowInfo(index, getTop$1(cell)); | |
| }; | |
| const getBottomEdge = (index, cell) => { | |
| return rowInfo(index, getTop$1(cell) + getOuter$1(cell)); | |
| }; | |
| const findPositions = (getInnerEdge, getOuterEdge, array) => { | |
| if (array.length === 0) { | |
| return []; | |
| } | |
| const lines = map$1(array.slice(1), (cellOption, index) => { | |
| return cellOption.map(cell => { | |
| return getInnerEdge(index, cell); | |
| }); | |
| }); | |
| const lastLine = array[array.length - 1].map(cell => { | |
| return getOuterEdge(array.length - 1, cell); | |
| }); | |
| return lines.concat([lastLine]); | |
| }; | |
| const negate = step => { | |
| return -step; | |
| }; | |
| const height = { | |
| delta: identity, | |
| positions: optElements => findPositions(getTopEdge, getBottomEdge, optElements), | |
| edge: getTop$1 | |
| }; | |
| const ltr$1 = { | |
| delta: identity, | |
| edge: ltrEdge, | |
| positions: optElements => findPositions(getLeftEdge, getRightEdge, optElements) | |
| }; | |
| const rtl$1 = { | |
| delta: negate, | |
| edge: rtlEdge, | |
| positions: optElements => findPositions(getRightEdge, getLeftEdge, optElements) | |
| }; | |
| const detect$1 = onDirection(ltr$1, rtl$1); | |
| const width = { | |
| delta: (amount, table) => detect$1(table).delta(amount, table), | |
| positions: (cols, table) => detect$1(table).positions(cols, table), | |
| edge: cell => detect$1(cell).edge(cell) | |
| }; | |
| const units = { | |
| unsupportedLength: [ | |
| 'em', | |
| 'ex', | |
| 'cap', | |
| 'ch', | |
| 'ic', | |
| 'rem', | |
| 'lh', | |
| 'rlh', | |
| 'vw', | |
| 'vh', | |
| 'vi', | |
| 'vb', | |
| 'vmin', | |
| 'vmax', | |
| 'cm', | |
| 'mm', | |
| 'Q', | |
| 'in', | |
| 'pc', | |
| 'pt', | |
| 'px' | |
| ], | |
| fixed: [ | |
| 'px', | |
| 'pt' | |
| ], | |
| relative: ['%'], | |
| empty: [''] | |
| }; | |
| const pattern = (() => { | |
| const decimalDigits = '[0-9]+'; | |
| const signedInteger = '[+-]?' + decimalDigits; | |
| const exponentPart = '[eE]' + signedInteger; | |
| const dot = '\\.'; | |
| const opt = input => `(?:${ input })?`; | |
| const unsignedDecimalLiteral = [ | |
| 'Infinity', | |
| decimalDigits + dot + opt(decimalDigits) + opt(exponentPart), | |
| dot + decimalDigits + opt(exponentPart), | |
| decimalDigits + opt(exponentPart) | |
| ].join('|'); | |
| const float = `[+-]?(?:${ unsignedDecimalLiteral })`; | |
| return new RegExp(`^(${ float })(.*)$`); | |
| })(); | |
| const isUnit = (unit, accepted) => exists(accepted, acc => exists(units[acc], check => unit === check)); | |
| const parse = (input, accepted) => { | |
| const match = Optional.from(pattern.exec(input)); | |
| return match.bind(array => { | |
| const value = Number(array[1]); | |
| const unitRaw = array[2]; | |
| if (isUnit(unitRaw, accepted)) { | |
| return Optional.some({ | |
| value, | |
| unit: unitRaw | |
| }); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }); | |
| }; | |
| const rPercentageBasedSizeRegex = /(\d+(\.\d+)?)%/; | |
| const rPixelBasedSizeRegex = /(\d+(\.\d+)?)px|em/; | |
| const isCol$2 = isTag('col'); | |
| const getPercentSize = (elm, outerGetter, innerGetter) => { | |
| const relativeParent = parentElement(elm).getOrThunk(() => getBody$1(owner(elm))); | |
| return outerGetter(elm) / innerGetter(relativeParent) * 100; | |
| }; | |
| const setPixelWidth = (cell, amount) => { | |
| set$1(cell, 'width', amount + 'px'); | |
| }; | |
| const setPercentageWidth = (cell, amount) => { | |
| set$1(cell, 'width', amount + '%'); | |
| }; | |
| const setHeight = (cell, amount) => { | |
| set$1(cell, 'height', amount + 'px'); | |
| }; | |
| const getHeightValue = cell => getRuntime(cell) + 'px'; | |
| const convert = (cell, number, getter, setter) => { | |
| const newSize = table(cell).map(table => { | |
| const total = getter(table); | |
| return Math.floor(number / 100 * total); | |
| }).getOr(number); | |
| setter(cell, newSize); | |
| return newSize; | |
| }; | |
| const normalizePixelSize = (value, cell, getter, setter) => { | |
| const number = parseFloat(value); | |
| return endsWith(value, '%') && name(cell) !== 'table' ? convert(cell, number, getter, setter) : number; | |
| }; | |
| const getTotalHeight = cell => { | |
| const value = getHeightValue(cell); | |
| if (!value) { | |
| return get$8(cell); | |
| } | |
| return normalizePixelSize(value, cell, get$8, setHeight); | |
| }; | |
| const get$7 = (cell, type, f) => { | |
| const v = f(cell); | |
| const span = getSpan(cell, type); | |
| return v / span; | |
| }; | |
| const getRaw$1 = (element, prop) => { | |
| return getRaw$2(element, prop).orThunk(() => { | |
| return getOpt(element, prop).map(val => val + 'px'); | |
| }); | |
| }; | |
| const getRawWidth$1 = element => getRaw$1(element, 'width'); | |
| const getRawHeight = element => getRaw$1(element, 'height'); | |
| const getPercentageWidth = cell => getPercentSize(cell, get$9, getInner); | |
| const getPixelWidth$1 = cell => isCol$2(cell) ? get$9(cell) : getRuntime$1(cell); | |
| const getHeight = cell => { | |
| return get$7(cell, 'rowspan', getTotalHeight); | |
| }; | |
| const getGenericWidth = cell => { | |
| const width = getRawWidth$1(cell); | |
| return width.bind(w => parse(w, [ | |
| 'fixed', | |
| 'relative', | |
| 'empty' | |
| ])); | |
| }; | |
| const setGenericWidth = (cell, amount, unit) => { | |
| set$1(cell, 'width', amount + unit); | |
| }; | |
| const getPixelTableWidth = table => get$9(table) + 'px'; | |
| const getPercentTableWidth = table => getPercentSize(table, get$9, getInner) + '%'; | |
| const isPercentSizing$1 = table => getRawWidth$1(table).exists(size => rPercentageBasedSizeRegex.test(size)); | |
| const isPixelSizing$1 = table => getRawWidth$1(table).exists(size => rPixelBasedSizeRegex.test(size)); | |
| const isNoneSizing$1 = table => getRawWidth$1(table).isNone(); | |
| const percentageBasedSizeRegex = constant(rPercentageBasedSizeRegex); | |
| const isCol$1 = isTag('col'); | |
| const getRawW = cell => { | |
| return getRawWidth$1(cell).getOrThunk(() => getPixelWidth$1(cell) + 'px'); | |
| }; | |
| const getRawH = cell => { | |
| return getRawHeight(cell).getOrThunk(() => getHeight(cell) + 'px'); | |
| }; | |
| const justCols = warehouse => map$1(Warehouse.justColumns(warehouse), column => Optional.from(column.element)); | |
| const isValidColumn = cell => { | |
| const browser = detect$2().browser; | |
| const supportsColWidths = browser.isChromium() || browser.isFirefox(); | |
| return isCol$1(cell) ? supportsColWidths : true; | |
| }; | |
| const getDimension = (cellOpt, index, backups, filter, getter, fallback) => cellOpt.filter(filter).fold(() => fallback(deduce(backups, index)), cell => getter(cell)); | |
| const getWidthFrom = (warehouse, table, getWidth, fallback) => { | |
| const columnCells = columns(warehouse); | |
| const columns$1 = Warehouse.hasColumns(warehouse) ? justCols(warehouse) : columnCells; | |
| const backups = [Optional.some(width.edge(table))].concat(map$1(width.positions(columnCells, table), pos => pos.map(p => p.x))); | |
| const colFilter = not(hasColspan); | |
| return map$1(columns$1, (cellOption, c) => { | |
| return getDimension(cellOption, c, backups, colFilter, column => { | |
| if (isValidColumn(column)) { | |
| return getWidth(column); | |
| } else { | |
| const cell = bindFrom(columnCells[c], identity); | |
| return getDimension(cell, c, backups, colFilter, cell => fallback(Optional.some(get$9(cell))), fallback); | |
| } | |
| }, fallback); | |
| }); | |
| }; | |
| const getDeduced = deduced => { | |
| return deduced.map(d => { | |
| return d + 'px'; | |
| }).getOr(''); | |
| }; | |
| const getRawWidths = (warehouse, table) => { | |
| return getWidthFrom(warehouse, table, getRawW, getDeduced); | |
| }; | |
| const getPercentageWidths = (warehouse, table, tableSize) => { | |
| return getWidthFrom(warehouse, table, getPercentageWidth, deduced => { | |
| return deduced.fold(() => { | |
| return tableSize.minCellWidth(); | |
| }, cellWidth => { | |
| return cellWidth / tableSize.pixelWidth() * 100; | |
| }); | |
| }); | |
| }; | |
| const getPixelWidths = (warehouse, table, tableSize) => { | |
| return getWidthFrom(warehouse, table, getPixelWidth$1, deduced => { | |
| return deduced.getOrThunk(tableSize.minCellWidth); | |
| }); | |
| }; | |
| const getHeightFrom = (warehouse, table, direction, getHeight, fallback) => { | |
| const rows$1 = rows(warehouse); | |
| const backups = [Optional.some(direction.edge(table))].concat(map$1(direction.positions(rows$1, table), pos => pos.map(p => p.y))); | |
| return map$1(rows$1, (cellOption, c) => { | |
| return getDimension(cellOption, c, backups, not(hasRowspan), getHeight, fallback); | |
| }); | |
| }; | |
| const getPixelHeights = (warehouse, table, direction) => { | |
| return getHeightFrom(warehouse, table, direction, getHeight, deduced => { | |
| return deduced.getOrThunk(minHeight); | |
| }); | |
| }; | |
| const getRawHeights = (warehouse, table, direction) => { | |
| return getHeightFrom(warehouse, table, direction, getRawH, getDeduced); | |
| }; | |
| const widthLookup = (table, getter) => () => { | |
| if (inBody(table)) { | |
| return getter(table); | |
| } else { | |
| return parseFloat(getRaw$2(table, 'width').getOr('0')); | |
| } | |
| }; | |
| const noneSize = table => { | |
| const getWidth = widthLookup(table, get$9); | |
| const zero = constant(0); | |
| const getWidths = (warehouse, tableSize) => getPixelWidths(warehouse, table, tableSize); | |
| return { | |
| width: getWidth, | |
| pixelWidth: getWidth, | |
| getWidths, | |
| getCellDelta: zero, | |
| singleColumnWidth: constant([0]), | |
| minCellWidth: zero, | |
| setElementWidth: noop, | |
| adjustTableWidth: noop, | |
| isRelative: true, | |
| label: 'none' | |
| }; | |
| }; | |
| const percentageSize = table => { | |
| const getFloatWidth = widthLookup(table, elem => parseFloat(getPercentTableWidth(elem))); | |
| const getWidth = widthLookup(table, get$9); | |
| const getCellDelta = delta => delta / getWidth() * 100; | |
| const singleColumnWidth = (w, _delta) => [100 - w]; | |
| const minCellWidth = () => minWidth() / getWidth() * 100; | |
| const adjustTableWidth = delta => { | |
| const currentWidth = getFloatWidth(); | |
| const change = delta / 100 * currentWidth; | |
| const newWidth = currentWidth + change; | |
| setPercentageWidth(table, newWidth); | |
| }; | |
| const getWidths = (warehouse, tableSize) => getPercentageWidths(warehouse, table, tableSize); | |
| return { | |
| width: getFloatWidth, | |
| pixelWidth: getWidth, | |
| getWidths, | |
| getCellDelta, | |
| singleColumnWidth, | |
| minCellWidth, | |
| setElementWidth: setPercentageWidth, | |
| adjustTableWidth, | |
| isRelative: true, | |
| label: 'percent' | |
| }; | |
| }; | |
| const pixelSize = table => { | |
| const getWidth = widthLookup(table, get$9); | |
| const getCellDelta = identity; | |
| const singleColumnWidth = (w, delta) => { | |
| const newNext = Math.max(minWidth(), w + delta); | |
| return [newNext - w]; | |
| }; | |
| const adjustTableWidth = delta => { | |
| const newWidth = getWidth() + delta; | |
| setPixelWidth(table, newWidth); | |
| }; | |
| const getWidths = (warehouse, tableSize) => getPixelWidths(warehouse, table, tableSize); | |
| return { | |
| width: getWidth, | |
| pixelWidth: getWidth, | |
| getWidths, | |
| getCellDelta, | |
| singleColumnWidth, | |
| minCellWidth: minWidth, | |
| setElementWidth: setPixelWidth, | |
| adjustTableWidth, | |
| isRelative: false, | |
| label: 'pixel' | |
| }; | |
| }; | |
| const chooseSize = (element, width) => { | |
| const percentMatch = percentageBasedSizeRegex().exec(width); | |
| if (percentMatch !== null) { | |
| return percentageSize(element); | |
| } else { | |
| return pixelSize(element); | |
| } | |
| }; | |
| const getTableSize = table => { | |
| const width = getRawWidth$1(table); | |
| return width.fold(() => noneSize(table), w => chooseSize(table, w)); | |
| }; | |
| const TableSize = { | |
| getTableSize, | |
| pixelSize, | |
| percentageSize, | |
| noneSize | |
| }; | |
| const statsStruct = (minRow, minCol, maxRow, maxCol, allCells, selectedCells) => ({ | |
| minRow, | |
| minCol, | |
| maxRow, | |
| maxCol, | |
| allCells, | |
| selectedCells | |
| }); | |
| const findSelectedStats = (house, isSelected) => { | |
| const totalColumns = house.grid.columns; | |
| const totalRows = house.grid.rows; | |
| let minRow = totalRows; | |
| let minCol = totalColumns; | |
| let maxRow = 0; | |
| let maxCol = 0; | |
| const allCells = []; | |
| const selectedCells = []; | |
| each$1(house.access, detail => { | |
| allCells.push(detail); | |
| if (isSelected(detail)) { | |
| selectedCells.push(detail); | |
| const startRow = detail.row; | |
| const endRow = startRow + detail.rowspan - 1; | |
| const startCol = detail.column; | |
| const endCol = startCol + detail.colspan - 1; | |
| if (startRow < minRow) { | |
| minRow = startRow; | |
| } else if (endRow > maxRow) { | |
| maxRow = endRow; | |
| } | |
| if (startCol < minCol) { | |
| minCol = startCol; | |
| } else if (endCol > maxCol) { | |
| maxCol = endCol; | |
| } | |
| } | |
| }); | |
| return statsStruct(minRow, minCol, maxRow, maxCol, allCells, selectedCells); | |
| }; | |
| const makeCell = (list, seenSelected, rowIndex) => { | |
| const row = list[rowIndex].element; | |
| const td = SugarElement.fromTag('td'); | |
| append$1(td, SugarElement.fromTag('br')); | |
| const f = seenSelected ? append$1 : prepend; | |
| f(row, td); | |
| }; | |
| const fillInGaps = (list, house, stats, isSelected) => { | |
| const rows = filter$2(list, row => row.section !== 'colgroup'); | |
| const totalColumns = house.grid.columns; | |
| const totalRows = house.grid.rows; | |
| for (let i = 0; i < totalRows; i++) { | |
| let seenSelected = false; | |
| for (let j = 0; j < totalColumns; j++) { | |
| if (!(i < stats.minRow || i > stats.maxRow || j < stats.minCol || j > stats.maxCol)) { | |
| const needCell = Warehouse.getAt(house, i, j).filter(isSelected).isNone(); | |
| if (needCell) { | |
| makeCell(rows, seenSelected, i); | |
| } else { | |
| seenSelected = true; | |
| } | |
| } | |
| } | |
| } | |
| }; | |
| const clean = (replica, stats, house, widthDelta) => { | |
| each$1(house.columns, col => { | |
| if (col.column < stats.minCol || col.column > stats.maxCol) { | |
| remove$6(col.element); | |
| } | |
| }); | |
| const emptyRows = filter$2(firstLayer(replica, 'tr'), row => row.dom.childElementCount === 0); | |
| each$2(emptyRows, remove$6); | |
| if (stats.minCol === stats.maxCol || stats.minRow === stats.maxRow) { | |
| each$2(firstLayer(replica, 'th,td'), cell => { | |
| remove$7(cell, 'rowspan'); | |
| remove$7(cell, 'colspan'); | |
| }); | |
| } | |
| remove$7(replica, LOCKED_COL_ATTR); | |
| remove$7(replica, 'data-snooker-col-series'); | |
| const tableSize = TableSize.getTableSize(replica); | |
| tableSize.adjustTableWidth(widthDelta); | |
| }; | |
| const getTableWidthDelta = (table, warehouse, tableSize, stats) => { | |
| if (stats.minCol === 0 && warehouse.grid.columns === stats.maxCol + 1) { | |
| return 0; | |
| } | |
| const colWidths = getPixelWidths(warehouse, table, tableSize); | |
| const allColsWidth = foldl(colWidths, (acc, width) => acc + width, 0); | |
| const selectedColsWidth = foldl(colWidths.slice(stats.minCol, stats.maxCol + 1), (acc, width) => acc + width, 0); | |
| const newWidth = selectedColsWidth / allColsWidth * tableSize.pixelWidth(); | |
| const delta = newWidth - tableSize.pixelWidth(); | |
| return tableSize.getCellDelta(delta); | |
| }; | |
| const extract$1 = (table, selectedSelector) => { | |
| const isSelected = detail => is$2(detail.element, selectedSelector); | |
| const replica = deep(table); | |
| const list = fromTable$1(replica); | |
| const tableSize = TableSize.getTableSize(table); | |
| const replicaHouse = Warehouse.generate(list); | |
| const replicaStats = findSelectedStats(replicaHouse, isSelected); | |
| const selector = 'th:not(' + selectedSelector + ')' + ',td:not(' + selectedSelector + ')'; | |
| const unselectedCells = filterFirstLayer(replica, 'th,td', cell => is$2(cell, selector)); | |
| each$2(unselectedCells, remove$6); | |
| fillInGaps(list, replicaHouse, replicaStats, isSelected); | |
| const house = Warehouse.fromTable(table); | |
| const widthDelta = getTableWidthDelta(table, house, tableSize, replicaStats); | |
| clean(replica, replicaStats, replicaHouse, widthDelta); | |
| return replica; | |
| }; | |
| const nbsp = '\xA0'; | |
| const NodeValue = (is, name) => { | |
| const get = element => { | |
| if (!is(element)) { | |
| throw new Error('Can only get ' + name + ' value of a ' + name + ' node'); | |
| } | |
| return getOption(element).getOr(''); | |
| }; | |
| const getOption = element => is(element) ? Optional.from(element.dom.nodeValue) : Optional.none(); | |
| const set = (element, value) => { | |
| if (!is(element)) { | |
| throw new Error('Can only set raw ' + name + ' value of a ' + name + ' node'); | |
| } | |
| element.dom.nodeValue = value; | |
| }; | |
| return { | |
| get, | |
| getOption, | |
| set | |
| }; | |
| }; | |
| const api = NodeValue(isText, 'text'); | |
| const get$6 = element => api.get(element); | |
| const getOption = element => api.getOption(element); | |
| const set = (element, value) => api.set(element, value); | |
| const getEnd = element => name(element) === 'img' ? 1 : getOption(element).fold(() => children$2(element).length, v => v.length); | |
| const isTextNodeWithCursorPosition = el => getOption(el).filter(text => text.trim().length !== 0 || text.indexOf(nbsp) > -1).isSome(); | |
| const isContentEditableFalse = elem => isHTMLElement(elem) && get$b(elem, 'contenteditable') === 'false'; | |
| const elementsWithCursorPosition = [ | |
| 'img', | |
| 'br' | |
| ]; | |
| const isCursorPosition = elem => { | |
| const hasCursorPosition = isTextNodeWithCursorPosition(elem); | |
| return hasCursorPosition || contains$2(elementsWithCursorPosition, name(elem)) || isContentEditableFalse(elem); | |
| }; | |
| const first = element => descendant$1(element, isCursorPosition); | |
| const last$1 = element => descendantRtl(element, isCursorPosition); | |
| const descendantRtl = (scope, predicate) => { | |
| const descend = element => { | |
| const children = children$2(element); | |
| for (let i = children.length - 1; i >= 0; i--) { | |
| const child = children[i]; | |
| if (predicate(child)) { | |
| return Optional.some(child); | |
| } | |
| const res = descend(child); | |
| if (res.isSome()) { | |
| return res; | |
| } | |
| } | |
| return Optional.none(); | |
| }; | |
| return descend(scope); | |
| }; | |
| const transferableAttributes = { | |
| scope: [ | |
| 'row', | |
| 'col' | |
| ] | |
| }; | |
| const createCell = doc => () => { | |
| const td = SugarElement.fromTag('td', doc.dom); | |
| append$1(td, SugarElement.fromTag('br', doc.dom)); | |
| return td; | |
| }; | |
| const createCol = doc => () => { | |
| return SugarElement.fromTag('col', doc.dom); | |
| }; | |
| const createColgroup = doc => () => { | |
| return SugarElement.fromTag('colgroup', doc.dom); | |
| }; | |
| const createRow$1 = doc => () => { | |
| return SugarElement.fromTag('tr', doc.dom); | |
| }; | |
| const replace$1 = (cell, tag, attrs) => { | |
| const replica = copy$2(cell, tag); | |
| each$1(attrs, (v, k) => { | |
| if (v === null) { | |
| remove$7(replica, k); | |
| } else { | |
| set$2(replica, k, v); | |
| } | |
| }); | |
| return replica; | |
| }; | |
| const pasteReplace = cell => { | |
| return cell; | |
| }; | |
| const cloneFormats = (oldCell, newCell, formats) => { | |
| const first$1 = first(oldCell); | |
| return first$1.map(firstText => { | |
| const formatSelector = formats.join(','); | |
| const parents = ancestors$3(firstText, formatSelector, element => { | |
| return eq$1(element, oldCell); | |
| }); | |
| return foldr(parents, (last, parent) => { | |
| const clonedFormat = shallow(parent); | |
| append$1(last, clonedFormat); | |
| return clonedFormat; | |
| }, newCell); | |
| }).getOr(newCell); | |
| }; | |
| const cloneAppropriateAttributes = (original, clone) => { | |
| each$1(transferableAttributes, (validAttributes, attributeName) => getOpt(original, attributeName).filter(attribute => contains$2(validAttributes, attribute)).each(attribute => set$2(clone, attributeName, attribute))); | |
| }; | |
| const cellOperations = (mutate, doc, formatsToClone) => { | |
| const cloneCss = (prev, clone) => { | |
| copy$1(prev.element, clone); | |
| remove$5(clone, 'height'); | |
| if (prev.colspan !== 1) { | |
| remove$5(clone, 'width'); | |
| } | |
| }; | |
| const newCell = prev => { | |
| const td = SugarElement.fromTag(name(prev.element), doc.dom); | |
| const formats = formatsToClone.getOr([ | |
| 'strong', | |
| 'em', | |
| 'b', | |
| 'i', | |
| 'span', | |
| 'font', | |
| 'h1', | |
| 'h2', | |
| 'h3', | |
| 'h4', | |
| 'h5', | |
| 'h6', | |
| 'p', | |
| 'div' | |
| ]); | |
| const lastNode = formats.length > 0 ? cloneFormats(prev.element, td, formats) : td; | |
| append$1(lastNode, SugarElement.fromTag('br')); | |
| cloneCss(prev, td); | |
| cloneAppropriateAttributes(prev.element, td); | |
| mutate(prev.element, td); | |
| return td; | |
| }; | |
| const newCol = prev => { | |
| const col = SugarElement.fromTag(name(prev.element), doc.dom); | |
| cloneCss(prev, col); | |
| mutate(prev.element, col); | |
| return col; | |
| }; | |
| return { | |
| col: newCol, | |
| colgroup: createColgroup(doc), | |
| row: createRow$1(doc), | |
| cell: newCell, | |
| replace: replace$1, | |
| colGap: createCol(doc), | |
| gap: createCell(doc) | |
| }; | |
| }; | |
| const paste$1 = doc => { | |
| return { | |
| col: createCol(doc), | |
| colgroup: createColgroup(doc), | |
| row: createRow$1(doc), | |
| cell: createCell(doc), | |
| replace: pasteReplace, | |
| colGap: createCol(doc), | |
| gap: createCell(doc) | |
| }; | |
| }; | |
| const fromHtml = (html, scope) => { | |
| const doc = scope || document; | |
| const div = doc.createElement('div'); | |
| div.innerHTML = html; | |
| return children$2(SugarElement.fromDom(div)); | |
| }; | |
| const fromDom = nodes => map$1(nodes, SugarElement.fromDom); | |
| const closest = target => closest$1(target, '[contenteditable]'); | |
| const isEditable$1 = (element, assumeEditable = false) => { | |
| if (inBody(element)) { | |
| return element.dom.isContentEditable; | |
| } else { | |
| return closest(element).fold(constant(assumeEditable), editable => getRaw(editable) === 'true'); | |
| } | |
| }; | |
| const getRaw = element => element.dom.contentEditable; | |
| const getBody = editor => SugarElement.fromDom(editor.getBody()); | |
| const getIsRoot = editor => element => eq$1(element, getBody(editor)); | |
| const removeDataStyle = table => { | |
| remove$7(table, 'data-mce-style'); | |
| const removeStyleAttribute = element => remove$7(element, 'data-mce-style'); | |
| each$2(cells$1(table), removeStyleAttribute); | |
| each$2(columns$1(table), removeStyleAttribute); | |
| each$2(rows$1(table), removeStyleAttribute); | |
| }; | |
| const getSelectionStart = editor => SugarElement.fromDom(editor.selection.getStart()); | |
| const getPixelWidth = elm => elm.getBoundingClientRect().width; | |
| const getPixelHeight = elm => elm.getBoundingClientRect().height; | |
| const getRawWidth = (editor, elm) => { | |
| const raw = editor.dom.getStyle(elm, 'width') || editor.dom.getAttrib(elm, 'width'); | |
| return Optional.from(raw).filter(isNotEmpty); | |
| }; | |
| const isPercentage$1 = value => /^(\d+(\.\d+)?)%$/.test(value); | |
| const isPixel = value => /^(\d+(\.\d+)?)px$/.test(value); | |
| const isInEditableContext$1 = cell => closest$2(cell, isTag('table')).exists(isEditable$1); | |
| const inSelection = (bounds, detail) => { | |
| const leftEdge = detail.column; | |
| const rightEdge = detail.column + detail.colspan - 1; | |
| const topEdge = detail.row; | |
| const bottomEdge = detail.row + detail.rowspan - 1; | |
| return leftEdge <= bounds.finishCol && rightEdge >= bounds.startCol && (topEdge <= bounds.finishRow && bottomEdge >= bounds.startRow); | |
| }; | |
| const isWithin = (bounds, detail) => { | |
| return detail.column >= bounds.startCol && detail.column + detail.colspan - 1 <= bounds.finishCol && detail.row >= bounds.startRow && detail.row + detail.rowspan - 1 <= bounds.finishRow; | |
| }; | |
| const isRectangular = (warehouse, bounds) => { | |
| let isRect = true; | |
| const detailIsWithin = curry(isWithin, bounds); | |
| for (let i = bounds.startRow; i <= bounds.finishRow; i++) { | |
| for (let j = bounds.startCol; j <= bounds.finishCol; j++) { | |
| isRect = isRect && Warehouse.getAt(warehouse, i, j).exists(detailIsWithin); | |
| } | |
| } | |
| return isRect ? Optional.some(bounds) : Optional.none(); | |
| }; | |
| const getBounds = (detailA, detailB) => { | |
| return bounds(Math.min(detailA.row, detailB.row), Math.min(detailA.column, detailB.column), Math.max(detailA.row + detailA.rowspan - 1, detailB.row + detailB.rowspan - 1), Math.max(detailA.column + detailA.colspan - 1, detailB.column + detailB.colspan - 1)); | |
| }; | |
| const getAnyBox = (warehouse, startCell, finishCell) => { | |
| const startCoords = Warehouse.findItem(warehouse, startCell, eq$1); | |
| const finishCoords = Warehouse.findItem(warehouse, finishCell, eq$1); | |
| return startCoords.bind(sc => { | |
| return finishCoords.map(fc => { | |
| return getBounds(sc, fc); | |
| }); | |
| }); | |
| }; | |
| const getBox$1 = (warehouse, startCell, finishCell) => { | |
| return getAnyBox(warehouse, startCell, finishCell).bind(bounds => { | |
| return isRectangular(warehouse, bounds); | |
| }); | |
| }; | |
| const moveBy$1 = (warehouse, cell, row, column) => { | |
| return Warehouse.findItem(warehouse, cell, eq$1).bind(detail => { | |
| const startRow = row > 0 ? detail.row + detail.rowspan - 1 : detail.row; | |
| const startCol = column > 0 ? detail.column + detail.colspan - 1 : detail.column; | |
| const dest = Warehouse.getAt(warehouse, startRow + row, startCol + column); | |
| return dest.map(d => { | |
| return d.element; | |
| }); | |
| }); | |
| }; | |
| const intercepts$1 = (warehouse, start, finish) => { | |
| return getAnyBox(warehouse, start, finish).map(bounds => { | |
| const inside = Warehouse.filterItems(warehouse, curry(inSelection, bounds)); | |
| return map$1(inside, detail => { | |
| return detail.element; | |
| }); | |
| }); | |
| }; | |
| const parentCell = (warehouse, innerCell) => { | |
| const isContainedBy = (c1, c2) => { | |
| return contains$1(c2, c1); | |
| }; | |
| return Warehouse.findItem(warehouse, innerCell, isContainedBy).map(detail => { | |
| return detail.element; | |
| }); | |
| }; | |
| const moveBy = (cell, deltaRow, deltaColumn) => { | |
| return table(cell).bind(table => { | |
| const warehouse = getWarehouse(table); | |
| return moveBy$1(warehouse, cell, deltaRow, deltaColumn); | |
| }); | |
| }; | |
| const intercepts = (table, first, last) => { | |
| const warehouse = getWarehouse(table); | |
| return intercepts$1(warehouse, first, last); | |
| }; | |
| const nestedIntercepts = (table, first, firstTable, last, lastTable) => { | |
| const warehouse = getWarehouse(table); | |
| const optStartCell = eq$1(table, firstTable) ? Optional.some(first) : parentCell(warehouse, first); | |
| const optLastCell = eq$1(table, lastTable) ? Optional.some(last) : parentCell(warehouse, last); | |
| return optStartCell.bind(startCell => optLastCell.bind(lastCell => intercepts$1(warehouse, startCell, lastCell))); | |
| }; | |
| const getBox = (table, first, last) => { | |
| const warehouse = getWarehouse(table); | |
| return getBox$1(warehouse, first, last); | |
| }; | |
| const getWarehouse = Warehouse.fromTable; | |
| var TagBoundaries = [ | |
| 'body', | |
| 'p', | |
| 'div', | |
| 'article', | |
| 'aside', | |
| 'figcaption', | |
| 'figure', | |
| 'footer', | |
| 'header', | |
| 'nav', | |
| 'section', | |
| 'ol', | |
| 'ul', | |
| 'li', | |
| 'table', | |
| 'thead', | |
| 'tbody', | |
| 'tfoot', | |
| 'caption', | |
| 'tr', | |
| 'td', | |
| 'th', | |
| 'h1', | |
| 'h2', | |
| 'h3', | |
| 'h4', | |
| 'h5', | |
| 'h6', | |
| 'blockquote', | |
| 'pre', | |
| 'address' | |
| ]; | |
| var DomUniverse = () => { | |
| const clone = element => { | |
| return SugarElement.fromDom(element.dom.cloneNode(false)); | |
| }; | |
| const document = element => documentOrOwner(element).dom; | |
| const isBoundary = element => { | |
| if (!isElement(element)) { | |
| return false; | |
| } | |
| if (name(element) === 'body') { | |
| return true; | |
| } | |
| return contains$2(TagBoundaries, name(element)); | |
| }; | |
| const isEmptyTag = element => { | |
| if (!isElement(element)) { | |
| return false; | |
| } | |
| return contains$2([ | |
| 'br', | |
| 'img', | |
| 'hr', | |
| 'input' | |
| ], name(element)); | |
| }; | |
| const isNonEditable = element => isElement(element) && get$b(element, 'contenteditable') === 'false'; | |
| const comparePosition = (element, other) => { | |
| return element.dom.compareDocumentPosition(other.dom); | |
| }; | |
| const copyAttributesTo = (source, destination) => { | |
| const as = clone$2(source); | |
| setAll$1(destination, as); | |
| }; | |
| const isSpecial = element => { | |
| const tag = name(element); | |
| return contains$2([ | |
| 'script', | |
| 'noscript', | |
| 'iframe', | |
| 'noframes', | |
| 'noembed', | |
| 'title', | |
| 'style', | |
| 'textarea', | |
| 'xmp' | |
| ], tag); | |
| }; | |
| const getLanguage = element => isElement(element) ? getOpt(element, 'lang') : Optional.none(); | |
| return { | |
| up: constant({ | |
| selector: ancestor$1, | |
| closest: closest$1, | |
| predicate: ancestor$2, | |
| all: parents | |
| }), | |
| down: constant({ | |
| selector: descendants, | |
| predicate: descendants$1 | |
| }), | |
| styles: constant({ | |
| get: get$a, | |
| getRaw: getRaw$2, | |
| set: set$1, | |
| remove: remove$5 | |
| }), | |
| attrs: constant({ | |
| get: get$b, | |
| set: set$2, | |
| remove: remove$7, | |
| copyTo: copyAttributesTo | |
| }), | |
| insert: constant({ | |
| before: before$3, | |
| after: after$5, | |
| afterAll: after$4, | |
| append: append$1, | |
| appendAll: append, | |
| prepend: prepend, | |
| wrap: wrap | |
| }), | |
| remove: constant({ | |
| unwrap: unwrap, | |
| remove: remove$6 | |
| }), | |
| create: constant({ | |
| nu: SugarElement.fromTag, | |
| clone, | |
| text: SugarElement.fromText | |
| }), | |
| query: constant({ | |
| comparePosition, | |
| prevSibling: prevSibling, | |
| nextSibling: nextSibling | |
| }), | |
| property: constant({ | |
| children: children$2, | |
| name: name, | |
| parent: parent, | |
| document, | |
| isText: isText, | |
| isComment: isComment, | |
| isElement: isElement, | |
| isSpecial, | |
| getLanguage, | |
| getText: get$6, | |
| setText: set, | |
| isBoundary, | |
| isEmptyTag, | |
| isNonEditable | |
| }), | |
| eq: eq$1, | |
| is: is$1 | |
| }; | |
| }; | |
| const all = (universe, look, elements, f) => { | |
| const head = elements[0]; | |
| const tail = elements.slice(1); | |
| return f(universe, look, head, tail); | |
| }; | |
| const oneAll = (universe, look, elements) => { | |
| return elements.length > 0 ? all(universe, look, elements, unsafeOne) : Optional.none(); | |
| }; | |
| const unsafeOne = (universe, look, head, tail) => { | |
| const start = look(universe, head); | |
| return foldr(tail, (b, a) => { | |
| const current = look(universe, a); | |
| return commonElement(universe, b, current); | |
| }, start); | |
| }; | |
| const commonElement = (universe, start, end) => { | |
| return start.bind(s => { | |
| return end.filter(curry(universe.eq, s)); | |
| }); | |
| }; | |
| const eq = (universe, item) => { | |
| return curry(universe.eq, item); | |
| }; | |
| const ancestors$2 = (universe, start, end, isRoot = never) => { | |
| const ps1 = [start].concat(universe.up().all(start)); | |
| const ps2 = [end].concat(universe.up().all(end)); | |
| const prune = path => { | |
| const index = findIndex(path, isRoot); | |
| return index.fold(() => { | |
| return path; | |
| }, ind => { | |
| return path.slice(0, ind + 1); | |
| }); | |
| }; | |
| const pruned1 = prune(ps1); | |
| const pruned2 = prune(ps2); | |
| const shared = find$1(pruned1, x => { | |
| return exists(pruned2, eq(universe, x)); | |
| }); | |
| return { | |
| firstpath: pruned1, | |
| secondpath: pruned2, | |
| shared | |
| }; | |
| }; | |
| const sharedOne$1 = oneAll; | |
| const ancestors$1 = ancestors$2; | |
| const universe$3 = DomUniverse(); | |
| const sharedOne = (look, elements) => { | |
| return sharedOne$1(universe$3, (_universe, element) => { | |
| return look(element); | |
| }, elements); | |
| }; | |
| const ancestors = (start, finish, isRoot) => { | |
| return ancestors$1(universe$3, start, finish, isRoot); | |
| }; | |
| const lookupTable = container => { | |
| return ancestor$1(container, 'table'); | |
| }; | |
| const identify = (start, finish, isRoot) => { | |
| const getIsRoot = rootTable => { | |
| return element => { | |
| return isRoot !== undefined && isRoot(element) || eq$1(element, rootTable); | |
| }; | |
| }; | |
| if (eq$1(start, finish)) { | |
| return Optional.some({ | |
| boxes: Optional.some([start]), | |
| start, | |
| finish | |
| }); | |
| } else { | |
| return lookupTable(start).bind(startTable => { | |
| return lookupTable(finish).bind(finishTable => { | |
| if (eq$1(startTable, finishTable)) { | |
| return Optional.some({ | |
| boxes: intercepts(startTable, start, finish), | |
| start, | |
| finish | |
| }); | |
| } else if (contains$1(startTable, finishTable)) { | |
| const ancestorCells = ancestors$3(finish, 'td,th', getIsRoot(startTable)); | |
| const finishCell = ancestorCells.length > 0 ? ancestorCells[ancestorCells.length - 1] : finish; | |
| return Optional.some({ | |
| boxes: nestedIntercepts(startTable, start, startTable, finish, finishTable), | |
| start, | |
| finish: finishCell | |
| }); | |
| } else if (contains$1(finishTable, startTable)) { | |
| const ancestorCells = ancestors$3(start, 'td,th', getIsRoot(finishTable)); | |
| const startCell = ancestorCells.length > 0 ? ancestorCells[ancestorCells.length - 1] : start; | |
| return Optional.some({ | |
| boxes: nestedIntercepts(finishTable, start, startTable, finish, finishTable), | |
| start, | |
| finish: startCell | |
| }); | |
| } else { | |
| return ancestors(start, finish).shared.bind(lca => { | |
| return closest$1(lca, 'table', isRoot).bind(lcaTable => { | |
| const finishAncestorCells = ancestors$3(finish, 'td,th', getIsRoot(lcaTable)); | |
| const finishCell = finishAncestorCells.length > 0 ? finishAncestorCells[finishAncestorCells.length - 1] : finish; | |
| const startAncestorCells = ancestors$3(start, 'td,th', getIsRoot(lcaTable)); | |
| const startCell = startAncestorCells.length > 0 ? startAncestorCells[startAncestorCells.length - 1] : start; | |
| return Optional.some({ | |
| boxes: nestedIntercepts(lcaTable, start, startTable, finish, finishTable), | |
| start: startCell, | |
| finish: finishCell | |
| }); | |
| }); | |
| }); | |
| } | |
| }); | |
| }); | |
| } | |
| }; | |
| const retrieve$1 = (container, selector) => { | |
| const sels = descendants(container, selector); | |
| return sels.length > 0 ? Optional.some(sels) : Optional.none(); | |
| }; | |
| const getLast = (boxes, lastSelectedSelector) => { | |
| return find$1(boxes, box => { | |
| return is$2(box, lastSelectedSelector); | |
| }); | |
| }; | |
| const getEdges = (container, firstSelectedSelector, lastSelectedSelector) => { | |
| return descendant(container, firstSelectedSelector).bind(first => { | |
| return descendant(container, lastSelectedSelector).bind(last => { | |
| return sharedOne(lookupTable, [ | |
| first, | |
| last | |
| ]).map(table => { | |
| return { | |
| first, | |
| last, | |
| table | |
| }; | |
| }); | |
| }); | |
| }); | |
| }; | |
| const expandTo = (finish, firstSelectedSelector) => { | |
| return ancestor$1(finish, 'table').bind(table => { | |
| return descendant(table, firstSelectedSelector).bind(start => { | |
| return identify(start, finish).bind(identified => { | |
| return identified.boxes.map(boxes => { | |
| return { | |
| boxes, | |
| start: identified.start, | |
| finish: identified.finish | |
| }; | |
| }); | |
| }); | |
| }); | |
| }); | |
| }; | |
| const shiftSelection = (boxes, deltaRow, deltaColumn, firstSelectedSelector, lastSelectedSelector) => { | |
| return getLast(boxes, lastSelectedSelector).bind(last => { | |
| return moveBy(last, deltaRow, deltaColumn).bind(finish => { | |
| return expandTo(finish, firstSelectedSelector); | |
| }); | |
| }); | |
| }; | |
| const retrieve = (container, selector) => { | |
| return retrieve$1(container, selector); | |
| }; | |
| const retrieveBox = (container, firstSelectedSelector, lastSelectedSelector) => { | |
| return getEdges(container, firstSelectedSelector, lastSelectedSelector).bind(edges => { | |
| const isRoot = ancestor => { | |
| return eq$1(container, ancestor); | |
| }; | |
| const sectionSelector = 'thead,tfoot,tbody,table'; | |
| const firstAncestor = ancestor$1(edges.first, sectionSelector, isRoot); | |
| const lastAncestor = ancestor$1(edges.last, sectionSelector, isRoot); | |
| return firstAncestor.bind(fA => { | |
| return lastAncestor.bind(lA => { | |
| return eq$1(fA, lA) ? getBox(edges.table, edges.first, edges.last) : Optional.none(); | |
| }); | |
| }); | |
| }); | |
| }; | |
| const selection = identity; | |
| const unmergable = selectedCells => { | |
| const hasSpan = (elem, type) => getOpt(elem, type).exists(span => parseInt(span, 10) > 1); | |
| const hasRowOrColSpan = elem => hasSpan(elem, 'rowspan') || hasSpan(elem, 'colspan'); | |
| return selectedCells.length > 0 && forall(selectedCells, hasRowOrColSpan) ? Optional.some(selectedCells) : Optional.none(); | |
| }; | |
| const mergable = (table, selectedCells, ephemera) => { | |
| if (selectedCells.length <= 1) { | |
| return Optional.none(); | |
| } else { | |
| return retrieveBox(table, ephemera.firstSelectedSelector, ephemera.lastSelectedSelector).map(bounds => ({ | |
| bounds, | |
| cells: selectedCells | |
| })); | |
| } | |
| }; | |
| const strSelected = 'data-mce-selected'; | |
| const strSelectedSelector = 'td[' + strSelected + '],th[' + strSelected + ']'; | |
| const strAttributeSelector = '[' + strSelected + ']'; | |
| const strFirstSelected = 'data-mce-first-selected'; | |
| const strFirstSelectedSelector = 'td[' + strFirstSelected + '],th[' + strFirstSelected + ']'; | |
| const strLastSelected = 'data-mce-last-selected'; | |
| const strLastSelectedSelector = 'td[' + strLastSelected + '],th[' + strLastSelected + ']'; | |
| const attributeSelector = strAttributeSelector; | |
| const ephemera = { | |
| selected: strSelected, | |
| selectedSelector: strSelectedSelector, | |
| firstSelected: strFirstSelected, | |
| firstSelectedSelector: strFirstSelectedSelector, | |
| lastSelected: strLastSelected, | |
| lastSelectedSelector: strLastSelectedSelector | |
| }; | |
| const forMenu = (selectedCells, table, cell) => ({ | |
| element: cell, | |
| mergable: mergable(table, selectedCells, ephemera), | |
| unmergable: unmergable(selectedCells), | |
| selection: selection(selectedCells) | |
| }); | |
| const paste = (element, clipboard, generators) => ({ | |
| element, | |
| clipboard, | |
| generators | |
| }); | |
| const pasteRows = (selectedCells, _cell, clipboard, generators) => ({ | |
| selection: selection(selectedCells), | |
| clipboard, | |
| generators | |
| }); | |
| const getSelectionCellFallback = element => table(element).bind(table => retrieve(table, ephemera.firstSelectedSelector)).fold(constant(element), cells => cells[0]); | |
| const getSelectionFromSelector = selector => (initCell, isRoot) => { | |
| const cellName = name(initCell); | |
| const cell = cellName === 'col' || cellName === 'colgroup' ? getSelectionCellFallback(initCell) : initCell; | |
| return closest$1(cell, selector, isRoot); | |
| }; | |
| const getSelectionCellOrCaption = getSelectionFromSelector('th,td,caption'); | |
| const getSelectionCell = getSelectionFromSelector('th,td'); | |
| const getCellsFromSelection = editor => fromDom(editor.model.table.getSelectedCells()); | |
| const getCellsFromFakeSelection = editor => filter$2(getCellsFromSelection(editor), cell => is$2(cell, ephemera.selectedSelector)); | |
| const extractSelected = cells => { | |
| return table(cells[0]).map(table => { | |
| const replica = extract$1(table, attributeSelector); | |
| removeDataStyle(replica); | |
| return [replica]; | |
| }); | |
| }; | |
| const serializeElements = (editor, elements) => map$1(elements, elm => editor.selection.serializer.serialize(elm.dom, {})).join(''); | |
| const getTextContent = elements => map$1(elements, element => element.dom.innerText).join(''); | |
| const registerEvents = (editor, actions) => { | |
| editor.on('BeforeGetContent', e => { | |
| const multiCellContext = cells => { | |
| e.preventDefault(); | |
| extractSelected(cells).each(elements => { | |
| e.content = e.format === 'text' ? getTextContent(elements) : serializeElements(editor, elements); | |
| }); | |
| }; | |
| if (e.selection === true) { | |
| const cells = getCellsFromFakeSelection(editor); | |
| if (cells.length >= 1) { | |
| multiCellContext(cells); | |
| } | |
| } | |
| }); | |
| editor.on('BeforeSetContent', e => { | |
| if (e.selection === true && e.paste === true) { | |
| const selectedCells = getCellsFromSelection(editor); | |
| head(selectedCells).each(cell => { | |
| table(cell).each(table => { | |
| const elements = filter$2(fromHtml(e.content), content => { | |
| return name(content) !== 'meta'; | |
| }); | |
| const isTable = isTag('table'); | |
| if (elements.length === 1 && isTable(elements[0])) { | |
| e.preventDefault(); | |
| const doc = SugarElement.fromDom(editor.getDoc()); | |
| const generators = paste$1(doc); | |
| const targets = paste(cell, elements[0], generators); | |
| actions.pasteCells(table, targets).each(() => { | |
| editor.focus(); | |
| }); | |
| } | |
| }); | |
| }); | |
| } | |
| }); | |
| }; | |
| const point = (element, offset) => ({ | |
| element, | |
| offset | |
| }); | |
| const scan$1 = (universe, element, direction) => { | |
| if (universe.property().isText(element) && universe.property().getText(element).trim().length === 0 || universe.property().isComment(element)) { | |
| return direction(element).bind(elem => { | |
| return scan$1(universe, elem, direction).orThunk(() => { | |
| return Optional.some(elem); | |
| }); | |
| }); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }; | |
| const toEnd = (universe, element) => { | |
| if (universe.property().isText(element)) { | |
| return universe.property().getText(element).length; | |
| } | |
| const children = universe.property().children(element); | |
| return children.length; | |
| }; | |
| const freefallRtl$2 = (universe, element) => { | |
| const candidate = scan$1(universe, element, universe.query().prevSibling).getOr(element); | |
| if (universe.property().isText(candidate)) { | |
| return point(candidate, toEnd(universe, candidate)); | |
| } | |
| const children = universe.property().children(candidate); | |
| return children.length > 0 ? freefallRtl$2(universe, children[children.length - 1]) : point(candidate, toEnd(universe, candidate)); | |
| }; | |
| const freefallRtl$1 = freefallRtl$2; | |
| const universe$2 = DomUniverse(); | |
| const freefallRtl = element => { | |
| return freefallRtl$1(universe$2, element); | |
| }; | |
| const halve = (main, other) => { | |
| if (!hasColspan(main)) { | |
| const width = getGenericWidth(main); | |
| width.each(w => { | |
| const newWidth = w.value / 2; | |
| setGenericWidth(main, newWidth, w.unit); | |
| setGenericWidth(other, newWidth, w.unit); | |
| }); | |
| } | |
| }; | |
| const zero = array => map$1(array, constant(0)); | |
| const surround = (sizes, startIndex, endIndex, results, f) => f(sizes.slice(0, startIndex)).concat(results).concat(f(sizes.slice(endIndex))); | |
| const clampDeltaHelper = predicate => (sizes, index, delta, minCellSize) => { | |
| if (!predicate(delta)) { | |
| return delta; | |
| } else { | |
| const newSize = Math.max(minCellSize, sizes[index] - Math.abs(delta)); | |
| const diff = Math.abs(newSize - sizes[index]); | |
| return delta >= 0 ? diff : -diff; | |
| } | |
| }; | |
| const clampNegativeDelta = clampDeltaHelper(delta => delta < 0); | |
| const clampDelta = clampDeltaHelper(always); | |
| const resizeTable = () => { | |
| const calcFixedDeltas = (sizes, index, next, delta, minCellSize) => { | |
| const clampedDelta = clampNegativeDelta(sizes, index, delta, minCellSize); | |
| return surround(sizes, index, next + 1, [ | |
| clampedDelta, | |
| 0 | |
| ], zero); | |
| }; | |
| const calcRelativeDeltas = (sizes, index, delta, minCellSize) => { | |
| const ratio = (100 + delta) / 100; | |
| const newThis = Math.max(minCellSize, (sizes[index] + delta) / ratio); | |
| return map$1(sizes, (size, idx) => { | |
| const newSize = idx === index ? newThis : size / ratio; | |
| return newSize - size; | |
| }); | |
| }; | |
| const calcLeftEdgeDeltas = (sizes, index, next, delta, minCellSize, isRelative) => { | |
| if (isRelative) { | |
| return calcRelativeDeltas(sizes, index, delta, minCellSize); | |
| } else { | |
| return calcFixedDeltas(sizes, index, next, delta, minCellSize); | |
| } | |
| }; | |
| const calcMiddleDeltas = (sizes, _prev, index, next, delta, minCellSize, isRelative) => calcLeftEdgeDeltas(sizes, index, next, delta, minCellSize, isRelative); | |
| const resizeTable = (resizer, delta) => resizer(delta); | |
| const calcRightEdgeDeltas = (sizes, _prev, index, delta, minCellSize, isRelative) => { | |
| if (isRelative) { | |
| return calcRelativeDeltas(sizes, index, delta, minCellSize); | |
| } else { | |
| const clampedDelta = clampNegativeDelta(sizes, index, delta, minCellSize); | |
| return zero(sizes.slice(0, index)).concat([clampedDelta]); | |
| } | |
| }; | |
| const calcRedestributedWidths = (sizes, totalWidth, pixelDelta, isRelative) => { | |
| if (isRelative) { | |
| const tableWidth = totalWidth + pixelDelta; | |
| const ratio = tableWidth / totalWidth; | |
| const newSizes = map$1(sizes, size => size / ratio); | |
| return { | |
| delta: ratio * 100 - 100, | |
| newSizes | |
| }; | |
| } else { | |
| return { | |
| delta: pixelDelta, | |
| newSizes: sizes | |
| }; | |
| } | |
| }; | |
| return { | |
| resizeTable, | |
| clampTableDelta: clampNegativeDelta, | |
| calcLeftEdgeDeltas, | |
| calcMiddleDeltas, | |
| calcRightEdgeDeltas, | |
| calcRedestributedWidths | |
| }; | |
| }; | |
| const preserveTable = () => { | |
| const calcLeftEdgeDeltas = (sizes, index, next, delta, minCellSize) => { | |
| const idx = delta >= 0 ? next : index; | |
| const clampedDelta = clampDelta(sizes, idx, delta, minCellSize); | |
| return surround(sizes, index, next + 1, [ | |
| clampedDelta, | |
| -clampedDelta | |
| ], zero); | |
| }; | |
| const calcMiddleDeltas = (sizes, _prev, index, next, delta, minCellSize) => calcLeftEdgeDeltas(sizes, index, next, delta, minCellSize); | |
| const resizeTable = (resizer, delta, isLastColumn) => { | |
| if (isLastColumn) { | |
| resizer(delta); | |
| } | |
| }; | |
| const calcRightEdgeDeltas = (sizes, _prev, _index, delta, _minCellSize, isRelative) => { | |
| if (isRelative) { | |
| return zero(sizes); | |
| } else { | |
| const diff = delta / sizes.length; | |
| return map$1(sizes, constant(diff)); | |
| } | |
| }; | |
| const clampTableDelta = (sizes, index, delta, minCellSize, isLastColumn) => { | |
| if (isLastColumn) { | |
| if (delta >= 0) { | |
| return delta; | |
| } else { | |
| const maxDelta = foldl(sizes, (a, b) => a + b - minCellSize, 0); | |
| return Math.max(-maxDelta, delta); | |
| } | |
| } else { | |
| return clampNegativeDelta(sizes, index, delta, minCellSize); | |
| } | |
| }; | |
| const calcRedestributedWidths = (sizes, _totalWidth, _pixelDelta, _isRelative) => ({ | |
| delta: 0, | |
| newSizes: sizes | |
| }); | |
| return { | |
| resizeTable, | |
| clampTableDelta, | |
| calcLeftEdgeDeltas, | |
| calcMiddleDeltas, | |
| calcRightEdgeDeltas, | |
| calcRedestributedWidths | |
| }; | |
| }; | |
| const getGridSize = table => { | |
| const warehouse = Warehouse.fromTable(table); | |
| return warehouse.grid; | |
| }; | |
| const isHeaderCell = isTag('th'); | |
| const isHeaderCells = cells => forall(cells, cell => isHeaderCell(cell.element)); | |
| const getRowHeaderType = (isHeaderRow, isHeaderCells) => { | |
| if (isHeaderRow && isHeaderCells) { | |
| return 'sectionCells'; | |
| } else if (isHeaderRow) { | |
| return 'section'; | |
| } else { | |
| return 'cells'; | |
| } | |
| }; | |
| const getRowType = row => { | |
| const isHeaderRow = row.section === 'thead'; | |
| const isHeaderCells = is(findCommonCellType(row.cells), 'th'); | |
| if (row.section === 'tfoot') { | |
| return { type: 'footer' }; | |
| } else if (isHeaderRow || isHeaderCells) { | |
| return { | |
| type: 'header', | |
| subType: getRowHeaderType(isHeaderRow, isHeaderCells) | |
| }; | |
| } else { | |
| return { type: 'body' }; | |
| } | |
| }; | |
| const findCommonCellType = cells => { | |
| const headerCells = filter$2(cells, cell => isHeaderCell(cell.element)); | |
| if (headerCells.length === 0) { | |
| return Optional.some('td'); | |
| } else if (headerCells.length === cells.length) { | |
| return Optional.some('th'); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }; | |
| const findCommonRowType = rows => { | |
| const rowTypes = map$1(rows, row => getRowType(row).type); | |
| const hasHeader = contains$2(rowTypes, 'header'); | |
| const hasFooter = contains$2(rowTypes, 'footer'); | |
| if (!hasHeader && !hasFooter) { | |
| return Optional.some('body'); | |
| } else { | |
| const hasBody = contains$2(rowTypes, 'body'); | |
| if (hasHeader && !hasBody && !hasFooter) { | |
| return Optional.some('header'); | |
| } else if (!hasHeader && !hasBody && hasFooter) { | |
| return Optional.some('footer'); | |
| } else { | |
| return Optional.none(); | |
| } | |
| } | |
| }; | |
| const findTableRowHeaderType = warehouse => findMap(warehouse.all, row => { | |
| const rowType = getRowType(row); | |
| return rowType.type === 'header' ? Optional.from(rowType.subType) : Optional.none(); | |
| }); | |
| const transformCell = (cell, comparator, substitution) => elementnew(substitution(cell.element, comparator), true, cell.isLocked); | |
| const transformRow = (row, section) => row.section !== section ? rowcells(row.element, row.cells, section, row.isNew) : row; | |
| const section = () => ({ | |
| transformRow, | |
| transformCell: (cell, comparator, substitution) => { | |
| const newCell = substitution(cell.element, comparator); | |
| const fixedCell = name(newCell) !== 'td' ? mutate$1(newCell, 'td') : newCell; | |
| return elementnew(fixedCell, cell.isNew, cell.isLocked); | |
| } | |
| }); | |
| const sectionCells = () => ({ | |
| transformRow, | |
| transformCell | |
| }); | |
| const cells = () => ({ | |
| transformRow: (row, section) => { | |
| const newSection = section === 'thead' ? 'tbody' : section; | |
| return transformRow(row, newSection); | |
| }, | |
| transformCell | |
| }); | |
| const fallback = () => ({ | |
| transformRow: identity, | |
| transformCell | |
| }); | |
| const getTableSectionType = (table, fallback) => { | |
| const warehouse = Warehouse.fromTable(table); | |
| const type = findTableRowHeaderType(warehouse).getOr(fallback); | |
| switch (type) { | |
| case 'section': | |
| return section(); | |
| case 'sectionCells': | |
| return sectionCells(); | |
| case 'cells': | |
| return cells(); | |
| } | |
| }; | |
| const TableSection = { | |
| getTableSectionType, | |
| section, | |
| sectionCells, | |
| cells, | |
| fallback | |
| }; | |
| const setIfNot = (element, property, value, ignore) => { | |
| if (value === ignore) { | |
| remove$7(element, property); | |
| } else { | |
| set$2(element, property, value); | |
| } | |
| }; | |
| const insert$1 = (table, selector, element) => { | |
| last$2(children(table, selector)).fold(() => prepend(table, element), child => after$5(child, element)); | |
| }; | |
| const generateSection = (table, sectionName) => { | |
| const section = child(table, sectionName).getOrThunk(() => { | |
| const newSection = SugarElement.fromTag(sectionName, owner(table).dom); | |
| if (sectionName === 'thead') { | |
| insert$1(table, 'caption,colgroup', newSection); | |
| } else if (sectionName === 'colgroup') { | |
| insert$1(table, 'caption', newSection); | |
| } else { | |
| append$1(table, newSection); | |
| } | |
| return newSection; | |
| }); | |
| empty(section); | |
| return section; | |
| }; | |
| const render$1 = (table, grid) => { | |
| const newRows = []; | |
| const newCells = []; | |
| const syncRows = gridSection => map$1(gridSection, row => { | |
| if (row.isNew) { | |
| newRows.push(row.element); | |
| } | |
| const tr = row.element; | |
| empty(tr); | |
| each$2(row.cells, cell => { | |
| if (cell.isNew) { | |
| newCells.push(cell.element); | |
| } | |
| setIfNot(cell.element, 'colspan', cell.colspan, 1); | |
| setIfNot(cell.element, 'rowspan', cell.rowspan, 1); | |
| append$1(tr, cell.element); | |
| }); | |
| return tr; | |
| }); | |
| const syncColGroup = gridSection => bind$2(gridSection, colGroup => map$1(colGroup.cells, col => { | |
| setIfNot(col.element, 'span', col.colspan, 1); | |
| return col.element; | |
| })); | |
| const renderSection = (gridSection, sectionName) => { | |
| const section = generateSection(table, sectionName); | |
| const sync = sectionName === 'colgroup' ? syncColGroup : syncRows; | |
| const sectionElems = sync(gridSection); | |
| append(section, sectionElems); | |
| }; | |
| const removeSection = sectionName => { | |
| child(table, sectionName).each(remove$6); | |
| }; | |
| const renderOrRemoveSection = (gridSection, sectionName) => { | |
| if (gridSection.length > 0) { | |
| renderSection(gridSection, sectionName); | |
| } else { | |
| removeSection(sectionName); | |
| } | |
| }; | |
| const headSection = []; | |
| const bodySection = []; | |
| const footSection = []; | |
| const columnGroupsSection = []; | |
| each$2(grid, row => { | |
| switch (row.section) { | |
| case 'thead': | |
| headSection.push(row); | |
| break; | |
| case 'tbody': | |
| bodySection.push(row); | |
| break; | |
| case 'tfoot': | |
| footSection.push(row); | |
| break; | |
| case 'colgroup': | |
| columnGroupsSection.push(row); | |
| break; | |
| } | |
| }); | |
| renderOrRemoveSection(columnGroupsSection, 'colgroup'); | |
| renderOrRemoveSection(headSection, 'thead'); | |
| renderOrRemoveSection(bodySection, 'tbody'); | |
| renderOrRemoveSection(footSection, 'tfoot'); | |
| return { | |
| newRows, | |
| newCells | |
| }; | |
| }; | |
| const copy = grid => map$1(grid, row => { | |
| const tr = shallow(row.element); | |
| each$2(row.cells, cell => { | |
| const clonedCell = deep(cell.element); | |
| setIfNot(clonedCell, 'colspan', cell.colspan, 1); | |
| setIfNot(clonedCell, 'rowspan', cell.rowspan, 1); | |
| append$1(tr, clonedCell); | |
| }); | |
| return tr; | |
| }); | |
| const getColumn = (grid, index) => { | |
| return map$1(grid, row => { | |
| return getCell(row, index); | |
| }); | |
| }; | |
| const getRow = (grid, index) => { | |
| return grid[index]; | |
| }; | |
| const findDiff = (xs, comp) => { | |
| if (xs.length === 0) { | |
| return 0; | |
| } | |
| const first = xs[0]; | |
| const index = findIndex(xs, x => { | |
| return !comp(first.element, x.element); | |
| }); | |
| return index.getOr(xs.length); | |
| }; | |
| const subgrid = (grid, row, column, comparator) => { | |
| const gridRow = getRow(grid, row); | |
| const isColRow = gridRow.section === 'colgroup'; | |
| const colspan = findDiff(gridRow.cells.slice(column), comparator); | |
| const rowspan = isColRow ? 1 : findDiff(getColumn(grid.slice(row), column), comparator); | |
| return { | |
| colspan, | |
| rowspan | |
| }; | |
| }; | |
| const toDetails = (grid, comparator) => { | |
| const seen = map$1(grid, row => map$1(row.cells, never)); | |
| const updateSeen = (rowIndex, columnIndex, rowspan, colspan) => { | |
| for (let row = rowIndex; row < rowIndex + rowspan; row++) { | |
| for (let column = columnIndex; column < columnIndex + colspan; column++) { | |
| seen[row][column] = true; | |
| } | |
| } | |
| }; | |
| return map$1(grid, (row, rowIndex) => { | |
| const details = bind$2(row.cells, (cell, columnIndex) => { | |
| if (seen[rowIndex][columnIndex] === false) { | |
| const result = subgrid(grid, rowIndex, columnIndex, comparator); | |
| updateSeen(rowIndex, columnIndex, result.rowspan, result.colspan); | |
| return [detailnew(cell.element, result.rowspan, result.colspan, cell.isNew)]; | |
| } else { | |
| return []; | |
| } | |
| }); | |
| return rowdetailnew(row.element, details, row.section, row.isNew); | |
| }); | |
| }; | |
| const toGrid = (warehouse, generators, isNew) => { | |
| const grid = []; | |
| each$2(warehouse.colgroups, colgroup => { | |
| const colgroupCols = []; | |
| for (let columnIndex = 0; columnIndex < warehouse.grid.columns; columnIndex++) { | |
| const element = Warehouse.getColumnAt(warehouse, columnIndex).map(column => elementnew(column.element, isNew, false)).getOrThunk(() => elementnew(generators.colGap(), true, false)); | |
| colgroupCols.push(element); | |
| } | |
| grid.push(rowcells(colgroup.element, colgroupCols, 'colgroup', isNew)); | |
| }); | |
| for (let rowIndex = 0; rowIndex < warehouse.grid.rows; rowIndex++) { | |
| const rowCells = []; | |
| for (let columnIndex = 0; columnIndex < warehouse.grid.columns; columnIndex++) { | |
| const element = Warehouse.getAt(warehouse, rowIndex, columnIndex).map(item => elementnew(item.element, isNew, item.isLocked)).getOrThunk(() => elementnew(generators.gap(), true, false)); | |
| rowCells.push(element); | |
| } | |
| const rowDetail = warehouse.all[rowIndex]; | |
| const row = rowcells(rowDetail.element, rowCells, rowDetail.section, isNew); | |
| grid.push(row); | |
| } | |
| return grid; | |
| }; | |
| const fromWarehouse = (warehouse, generators) => toGrid(warehouse, generators, false); | |
| const toDetailList = grid => toDetails(grid, eq$1); | |
| const findInWarehouse = (warehouse, element) => findMap(warehouse.all, r => find$1(r.cells, e => eq$1(element, e.element))); | |
| const extractCells = (warehouse, target, predicate) => { | |
| const details = map$1(target.selection, cell$1 => { | |
| return cell(cell$1).bind(lc => findInWarehouse(warehouse, lc)).filter(predicate); | |
| }); | |
| const cells = cat(details); | |
| return someIf(cells.length > 0, cells); | |
| }; | |
| const run = (operation, extract, adjustment, postAction, genWrappers) => (table, target, generators, behaviours) => { | |
| const warehouse = Warehouse.fromTable(table); | |
| const tableSection = Optional.from(behaviours === null || behaviours === void 0 ? void 0 : behaviours.section).getOrThunk(TableSection.fallback); | |
| const output = extract(warehouse, target).map(info => { | |
| const model = fromWarehouse(warehouse, generators); | |
| const result = operation(model, info, eq$1, genWrappers(generators), tableSection); | |
| const lockedColumns = getLockedColumnsFromGrid(result.grid); | |
| const grid = toDetailList(result.grid); | |
| return { | |
| info, | |
| grid, | |
| cursor: result.cursor, | |
| lockedColumns | |
| }; | |
| }); | |
| return output.bind(out => { | |
| const newElements = render$1(table, out.grid); | |
| const tableSizing = Optional.from(behaviours === null || behaviours === void 0 ? void 0 : behaviours.sizing).getOrThunk(() => TableSize.getTableSize(table)); | |
| const resizing = Optional.from(behaviours === null || behaviours === void 0 ? void 0 : behaviours.resize).getOrThunk(preserveTable); | |
| adjustment(table, out.grid, out.info, { | |
| sizing: tableSizing, | |
| resize: resizing, | |
| section: tableSection | |
| }); | |
| postAction(table); | |
| remove$7(table, LOCKED_COL_ATTR); | |
| if (out.lockedColumns.length > 0) { | |
| set$2(table, LOCKED_COL_ATTR, out.lockedColumns.join(',')); | |
| } | |
| return Optional.some({ | |
| cursor: out.cursor, | |
| newRows: newElements.newRows, | |
| newCells: newElements.newCells | |
| }); | |
| }); | |
| }; | |
| const onPaste = (warehouse, target) => cell(target.element).bind(cell => findInWarehouse(warehouse, cell).map(details => { | |
| const value = { | |
| ...details, | |
| generators: target.generators, | |
| clipboard: target.clipboard | |
| }; | |
| return value; | |
| })); | |
| const onPasteByEditor = (warehouse, target) => extractCells(warehouse, target, always).map(cells => ({ | |
| cells, | |
| generators: target.generators, | |
| clipboard: target.clipboard | |
| })); | |
| const onMergable = (_warehouse, target) => target.mergable; | |
| const onUnmergable = (_warehouse, target) => target.unmergable; | |
| const onCells = (warehouse, target) => extractCells(warehouse, target, always); | |
| const onUnlockedCells = (warehouse, target) => extractCells(warehouse, target, detail => !detail.isLocked); | |
| const isUnlockedTableCell = (warehouse, cell) => findInWarehouse(warehouse, cell).exists(detail => !detail.isLocked); | |
| const allUnlocked = (warehouse, cells) => forall(cells, cell => isUnlockedTableCell(warehouse, cell)); | |
| const onUnlockedMergable = (warehouse, target) => onMergable(warehouse, target).filter(mergeable => allUnlocked(warehouse, mergeable.cells)); | |
| const onUnlockedUnmergable = (warehouse, target) => onUnmergable(warehouse, target).filter(cells => allUnlocked(warehouse, cells)); | |
| const merge$2 = (grid, bounds, comparator, substitution) => { | |
| const rows = extractGridDetails(grid).rows; | |
| if (rows.length === 0) { | |
| return grid; | |
| } | |
| for (let i = bounds.startRow; i <= bounds.finishRow; i++) { | |
| for (let j = bounds.startCol; j <= bounds.finishCol; j++) { | |
| const row = rows[i]; | |
| const isLocked = getCell(row, j).isLocked; | |
| mutateCell(row, j, elementnew(substitution(), false, isLocked)); | |
| } | |
| } | |
| return grid; | |
| }; | |
| const unmerge = (grid, target, comparator, substitution) => { | |
| const rows = extractGridDetails(grid).rows; | |
| let first = true; | |
| for (let i = 0; i < rows.length; i++) { | |
| for (let j = 0; j < cellLength(rows[0]); j++) { | |
| const row = rows[i]; | |
| const currentCell = getCell(row, j); | |
| const currentCellElm = currentCell.element; | |
| const isToReplace = comparator(currentCellElm, target); | |
| if (isToReplace && !first) { | |
| mutateCell(row, j, elementnew(substitution(), true, currentCell.isLocked)); | |
| } else if (isToReplace) { | |
| first = false; | |
| } | |
| } | |
| } | |
| return grid; | |
| }; | |
| const uniqueCells = (row, comparator) => { | |
| return foldl(row, (rest, cell) => { | |
| return exists(rest, currentCell => { | |
| return comparator(currentCell.element, cell.element); | |
| }) ? rest : rest.concat([cell]); | |
| }, []); | |
| }; | |
| const splitCols = (grid, index, comparator, substitution) => { | |
| if (index > 0 && index < grid[0].cells.length) { | |
| each$2(grid, row => { | |
| const prevCell = row.cells[index - 1]; | |
| let offset = 0; | |
| const substitute = substitution(); | |
| while (row.cells.length > index + offset && comparator(prevCell.element, row.cells[index + offset].element)) { | |
| mutateCell(row, index + offset, elementnew(substitute, true, row.cells[index + offset].isLocked)); | |
| offset++; | |
| } | |
| }); | |
| } | |
| return grid; | |
| }; | |
| const splitRows = (grid, index, comparator, substitution) => { | |
| const rows = extractGridDetails(grid).rows; | |
| if (index > 0 && index < rows.length) { | |
| const rowPrevCells = rows[index - 1].cells; | |
| const cells = uniqueCells(rowPrevCells, comparator); | |
| each$2(cells, cell => { | |
| let replacement = Optional.none(); | |
| for (let i = index; i < rows.length; i++) { | |
| for (let j = 0; j < cellLength(rows[0]); j++) { | |
| const row = rows[i]; | |
| const current = getCell(row, j); | |
| const isToReplace = comparator(current.element, cell.element); | |
| if (isToReplace) { | |
| if (replacement.isNone()) { | |
| replacement = Optional.some(substitution()); | |
| } | |
| replacement.each(sub => { | |
| mutateCell(row, j, elementnew(sub, true, current.isLocked)); | |
| }); | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| return grid; | |
| }; | |
| const value$1 = value => { | |
| const applyHelper = fn => fn(value); | |
| const constHelper = constant(value); | |
| const outputHelper = () => output; | |
| const output = { | |
| tag: true, | |
| inner: value, | |
| fold: (_onError, onValue) => onValue(value), | |
| isValue: always, | |
| isError: never, | |
| map: mapper => Result.value(mapper(value)), | |
| mapError: outputHelper, | |
| bind: applyHelper, | |
| exists: applyHelper, | |
| forall: applyHelper, | |
| getOr: constHelper, | |
| or: outputHelper, | |
| getOrThunk: constHelper, | |
| orThunk: outputHelper, | |
| getOrDie: constHelper, | |
| each: fn => { | |
| fn(value); | |
| }, | |
| toOptional: () => Optional.some(value) | |
| }; | |
| return output; | |
| }; | |
| const error = error => { | |
| const outputHelper = () => output; | |
| const output = { | |
| tag: false, | |
| inner: error, | |
| fold: (onError, _onValue) => onError(error), | |
| isValue: never, | |
| isError: always, | |
| map: outputHelper, | |
| mapError: mapper => Result.error(mapper(error)), | |
| bind: outputHelper, | |
| exists: never, | |
| forall: always, | |
| getOr: identity, | |
| or: identity, | |
| getOrThunk: apply, | |
| orThunk: apply, | |
| getOrDie: die(String(error)), | |
| each: noop, | |
| toOptional: Optional.none | |
| }; | |
| return output; | |
| }; | |
| const fromOption = (optional, err) => optional.fold(() => error(err), value$1); | |
| const Result = { | |
| value: value$1, | |
| error, | |
| fromOption | |
| }; | |
| const measure = (startAddress, gridA, gridB) => { | |
| if (startAddress.row >= gridA.length || startAddress.column > cellLength(gridA[0])) { | |
| return Result.error('invalid start address out of table bounds, row: ' + startAddress.row + ', column: ' + startAddress.column); | |
| } | |
| const rowRemainder = gridA.slice(startAddress.row); | |
| const colRemainder = rowRemainder[0].cells.slice(startAddress.column); | |
| const colRequired = cellLength(gridB[0]); | |
| const rowRequired = gridB.length; | |
| return Result.value({ | |
| rowDelta: rowRemainder.length - rowRequired, | |
| colDelta: colRemainder.length - colRequired | |
| }); | |
| }; | |
| const measureWidth = (gridA, gridB) => { | |
| const colLengthA = cellLength(gridA[0]); | |
| const colLengthB = cellLength(gridB[0]); | |
| return { | |
| rowDelta: 0, | |
| colDelta: colLengthA - colLengthB | |
| }; | |
| }; | |
| const measureHeight = (gridA, gridB) => { | |
| const rowLengthA = gridA.length; | |
| const rowLengthB = gridB.length; | |
| return { | |
| rowDelta: rowLengthA - rowLengthB, | |
| colDelta: 0 | |
| }; | |
| }; | |
| const generateElements = (amount, row, generators, isLocked) => { | |
| const generator = row.section === 'colgroup' ? generators.col : generators.cell; | |
| return range$1(amount, idx => elementnew(generator(), true, isLocked(idx))); | |
| }; | |
| const rowFill = (grid, amount, generators, lockedColumns) => { | |
| const exampleRow = grid[grid.length - 1]; | |
| return grid.concat(range$1(amount, () => { | |
| const generator = exampleRow.section === 'colgroup' ? generators.colgroup : generators.row; | |
| const row = clone(exampleRow, generator, identity); | |
| const elements = generateElements(row.cells.length, row, generators, idx => has$1(lockedColumns, idx.toString())); | |
| return setCells(row, elements); | |
| })); | |
| }; | |
| const colFill = (grid, amount, generators, startIndex) => map$1(grid, row => { | |
| const newChildren = generateElements(amount, row, generators, never); | |
| return addCells(row, startIndex, newChildren); | |
| }); | |
| const lockedColFill = (grid, generators, lockedColumns) => map$1(grid, row => { | |
| return foldl(lockedColumns, (acc, colNum) => { | |
| const newChild = generateElements(1, row, generators, always)[0]; | |
| return addCell(acc, colNum, newChild); | |
| }, row); | |
| }); | |
| const tailor = (gridA, delta, generators) => { | |
| const fillCols = delta.colDelta < 0 ? colFill : identity; | |
| const fillRows = delta.rowDelta < 0 ? rowFill : identity; | |
| const lockedColumns = getLockedColumnsFromGrid(gridA); | |
| const gridWidth = cellLength(gridA[0]); | |
| const isLastColLocked = exists(lockedColumns, locked => locked === gridWidth - 1); | |
| const modifiedCols = fillCols(gridA, Math.abs(delta.colDelta), generators, isLastColLocked ? gridWidth - 1 : gridWidth); | |
| const newLockedColumns = getLockedColumnsFromGrid(modifiedCols); | |
| return fillRows(modifiedCols, Math.abs(delta.rowDelta), generators, mapToObject(newLockedColumns, always)); | |
| }; | |
| const isSpanning = (grid, row, col, comparator) => { | |
| const candidate = getCell(grid[row], col); | |
| const matching = curry(comparator, candidate.element); | |
| const currentRow = grid[row]; | |
| return grid.length > 1 && cellLength(currentRow) > 1 && (col > 0 && matching(getCellElement(currentRow, col - 1)) || col < currentRow.cells.length - 1 && matching(getCellElement(currentRow, col + 1)) || row > 0 && matching(getCellElement(grid[row - 1], col)) || row < grid.length - 1 && matching(getCellElement(grid[row + 1], col))); | |
| }; | |
| const mergeTables = (startAddress, gridA, gridBRows, generator, comparator, lockedColumns) => { | |
| const startRow = startAddress.row; | |
| const startCol = startAddress.column; | |
| const mergeHeight = gridBRows.length; | |
| const mergeWidth = cellLength(gridBRows[0]); | |
| const endRow = startRow + mergeHeight; | |
| const endCol = startCol + mergeWidth + lockedColumns.length; | |
| const lockedColumnObj = mapToObject(lockedColumns, always); | |
| for (let r = startRow; r < endRow; r++) { | |
| let skippedCol = 0; | |
| for (let c = startCol; c < endCol; c++) { | |
| if (lockedColumnObj[c]) { | |
| skippedCol++; | |
| continue; | |
| } | |
| if (isSpanning(gridA, r, c, comparator)) { | |
| unmerge(gridA, getCellElement(gridA[r], c), comparator, generator.cell); | |
| } | |
| const gridBColIndex = c - startCol - skippedCol; | |
| const newCell = getCell(gridBRows[r - startRow], gridBColIndex); | |
| const newCellElm = newCell.element; | |
| const replacement = generator.replace(newCellElm); | |
| mutateCell(gridA[r], c, elementnew(replacement, true, newCell.isLocked)); | |
| } | |
| } | |
| return gridA; | |
| }; | |
| const getValidStartAddress = (currentStartAddress, grid, lockedColumns) => { | |
| const gridColLength = cellLength(grid[0]); | |
| const adjustedRowAddress = extractGridDetails(grid).cols.length + currentStartAddress.row; | |
| const possibleColAddresses = range$1(gridColLength - currentStartAddress.column, num => num + currentStartAddress.column); | |
| const validColAddress = find$1(possibleColAddresses, num => forall(lockedColumns, col => col !== num)).getOr(gridColLength - 1); | |
| return { | |
| row: adjustedRowAddress, | |
| column: validColAddress | |
| }; | |
| }; | |
| const getLockedColumnsWithinBounds = (startAddress, rows, lockedColumns) => filter$2(lockedColumns, colNum => colNum >= startAddress.column && colNum <= cellLength(rows[0]) + startAddress.column); | |
| const merge$1 = (startAddress, gridA, gridB, generator, comparator) => { | |
| const lockedColumns = getLockedColumnsFromGrid(gridA); | |
| const validStartAddress = getValidStartAddress(startAddress, gridA, lockedColumns); | |
| const gridBRows = extractGridDetails(gridB).rows; | |
| const lockedColumnsWithinBounds = getLockedColumnsWithinBounds(validStartAddress, gridBRows, lockedColumns); | |
| const result = measure(validStartAddress, gridA, gridBRows); | |
| return result.map(diff => { | |
| const delta = { | |
| ...diff, | |
| colDelta: diff.colDelta - lockedColumnsWithinBounds.length | |
| }; | |
| const fittedGrid = tailor(gridA, delta, generator); | |
| const newLockedColumns = getLockedColumnsFromGrid(fittedGrid); | |
| const newLockedColumnsWithinBounds = getLockedColumnsWithinBounds(validStartAddress, gridBRows, newLockedColumns); | |
| return mergeTables(validStartAddress, fittedGrid, gridBRows, generator, comparator, newLockedColumnsWithinBounds); | |
| }); | |
| }; | |
| const insertCols = (index, gridA, gridB, generator, comparator) => { | |
| splitCols(gridA, index, comparator, generator.cell); | |
| const delta = measureHeight(gridB, gridA); | |
| const fittedNewGrid = tailor(gridB, delta, generator); | |
| const secondDelta = measureHeight(gridA, fittedNewGrid); | |
| const fittedOldGrid = tailor(gridA, secondDelta, generator); | |
| return map$1(fittedOldGrid, (gridRow, i) => { | |
| return addCells(gridRow, index, fittedNewGrid[i].cells); | |
| }); | |
| }; | |
| const insertRows = (index, gridA, gridB, generator, comparator) => { | |
| splitRows(gridA, index, comparator, generator.cell); | |
| const locked = getLockedColumnsFromGrid(gridA); | |
| const diff = measureWidth(gridA, gridB); | |
| const delta = { | |
| ...diff, | |
| colDelta: diff.colDelta - locked.length | |
| }; | |
| const fittedOldGrid = tailor(gridA, delta, generator); | |
| const { | |
| cols: oldCols, | |
| rows: oldRows | |
| } = extractGridDetails(fittedOldGrid); | |
| const newLocked = getLockedColumnsFromGrid(fittedOldGrid); | |
| const secondDiff = measureWidth(gridB, gridA); | |
| const secondDelta = { | |
| ...secondDiff, | |
| colDelta: secondDiff.colDelta + newLocked.length | |
| }; | |
| const fittedGridB = lockedColFill(gridB, generator, newLocked); | |
| const fittedNewGrid = tailor(fittedGridB, secondDelta, generator); | |
| return [ | |
| ...oldCols, | |
| ...oldRows.slice(0, index), | |
| ...fittedNewGrid, | |
| ...oldRows.slice(index, oldRows.length) | |
| ]; | |
| }; | |
| const cloneRow = (row, cloneCell, comparator, substitution) => clone(row, elem => substitution(elem, comparator), cloneCell); | |
| const insertRowAt = (grid, index, example, comparator, substitution) => { | |
| const {rows, cols} = extractGridDetails(grid); | |
| const before = rows.slice(0, index); | |
| const after = rows.slice(index); | |
| const newRow = cloneRow(rows[example], (ex, c) => { | |
| const withinSpan = index > 0 && index < rows.length && comparator(getCellElement(rows[index - 1], c), getCellElement(rows[index], c)); | |
| const ret = withinSpan ? getCell(rows[index], c) : elementnew(substitution(ex.element, comparator), true, ex.isLocked); | |
| return ret; | |
| }, comparator, substitution); | |
| return [ | |
| ...cols, | |
| ...before, | |
| newRow, | |
| ...after | |
| ]; | |
| }; | |
| const getElementFor = (row, column, section, withinSpan, example, comparator, substitution) => { | |
| if (section === 'colgroup' || !withinSpan) { | |
| const cell = getCell(row, example); | |
| return elementnew(substitution(cell.element, comparator), true, false); | |
| } else { | |
| return getCell(row, column); | |
| } | |
| }; | |
| const insertColumnAt = (grid, index, example, comparator, substitution) => map$1(grid, row => { | |
| const withinSpan = index > 0 && index < cellLength(row) && comparator(getCellElement(row, index - 1), getCellElement(row, index)); | |
| const sub = getElementFor(row, index, row.section, withinSpan, example, comparator, substitution); | |
| return addCell(row, index, sub); | |
| }); | |
| const deleteColumnsAt = (grid, columns) => bind$2(grid, row => { | |
| const existingCells = row.cells; | |
| const cells = foldr(columns, (acc, column) => column >= 0 && column < acc.length ? acc.slice(0, column).concat(acc.slice(column + 1)) : acc, existingCells); | |
| return cells.length > 0 ? [rowcells(row.element, cells, row.section, row.isNew)] : []; | |
| }); | |
| const deleteRowsAt = (grid, start, finish) => { | |
| const {rows, cols} = extractGridDetails(grid); | |
| return [ | |
| ...cols, | |
| ...rows.slice(0, start), | |
| ...rows.slice(finish + 1) | |
| ]; | |
| }; | |
| const notInStartRow = (grid, rowIndex, colIndex, comparator) => getCellElement(grid[rowIndex], colIndex) !== undefined && (rowIndex > 0 && comparator(getCellElement(grid[rowIndex - 1], colIndex), getCellElement(grid[rowIndex], colIndex))); | |
| const notInStartColumn = (row, index, comparator) => index > 0 && comparator(getCellElement(row, index - 1), getCellElement(row, index)); | |
| const isDuplicatedCell = (grid, rowIndex, colIndex, comparator) => notInStartRow(grid, rowIndex, colIndex, comparator) || notInStartColumn(grid[rowIndex], colIndex, comparator); | |
| const rowReplacerPredicate = (targetRow, columnHeaders) => { | |
| const entireTableIsHeader = forall(columnHeaders, identity) && isHeaderCells(targetRow.cells); | |
| return entireTableIsHeader ? always : (cell, _rowIndex, colIndex) => { | |
| const type = name(cell.element); | |
| return !(type === 'th' && columnHeaders[colIndex]); | |
| }; | |
| }; | |
| const columnReplacePredicate = (targetColumn, rowHeaders) => { | |
| const entireTableIsHeader = forall(rowHeaders, identity) && isHeaderCells(targetColumn); | |
| return entireTableIsHeader ? always : (cell, rowIndex, _colIndex) => { | |
| const type = name(cell.element); | |
| return !(type === 'th' && rowHeaders[rowIndex]); | |
| }; | |
| }; | |
| const determineScope = (applyScope, cell, newScope, isInHeader) => { | |
| const hasSpan = scope => scope === 'row' ? hasRowspan(cell) : hasColspan(cell); | |
| const getScope = scope => hasSpan(scope) ? `${ scope }group` : scope; | |
| if (applyScope) { | |
| return isHeaderCell(cell) ? getScope(newScope) : null; | |
| } else if (isInHeader && isHeaderCell(cell)) { | |
| const oppositeScope = newScope === 'row' ? 'col' : 'row'; | |
| return getScope(oppositeScope); | |
| } else { | |
| return null; | |
| } | |
| }; | |
| const rowScopeGenerator = (applyScope, columnHeaders) => (cell, rowIndex, columnIndex) => Optional.some(determineScope(applyScope, cell.element, 'col', columnHeaders[columnIndex])); | |
| const columnScopeGenerator = (applyScope, rowHeaders) => (cell, rowIndex) => Optional.some(determineScope(applyScope, cell.element, 'row', rowHeaders[rowIndex])); | |
| const replace = (cell, comparator, substitute) => elementnew(substitute(cell.element, comparator), true, cell.isLocked); | |
| const replaceIn = (grid, targets, comparator, substitute, replacer, genScope, shouldReplace) => { | |
| const isTarget = cell => { | |
| return exists(targets, target => { | |
| return comparator(cell.element, target.element); | |
| }); | |
| }; | |
| return map$1(grid, (row, rowIndex) => { | |
| return mapCells(row, (cell, colIndex) => { | |
| if (isTarget(cell)) { | |
| const newCell = shouldReplace(cell, rowIndex, colIndex) ? replacer(cell, comparator, substitute) : cell; | |
| genScope(newCell, rowIndex, colIndex).each(scope => { | |
| setOptions(newCell.element, { scope: Optional.from(scope) }); | |
| }); | |
| return newCell; | |
| } else { | |
| return cell; | |
| } | |
| }); | |
| }); | |
| }; | |
| const getColumnCells = (rows, columnIndex, comparator) => bind$2(rows, (row, i) => { | |
| return isDuplicatedCell(rows, i, columnIndex, comparator) ? [] : [getCell(row, columnIndex)]; | |
| }); | |
| const getRowCells = (rows, rowIndex, comparator) => { | |
| const targetRow = rows[rowIndex]; | |
| return bind$2(targetRow.cells, (item, i) => { | |
| return isDuplicatedCell(rows, rowIndex, i, comparator) ? [] : [item]; | |
| }); | |
| }; | |
| const replaceColumns = (grid, indexes, applyScope, comparator, substitution) => { | |
| const rows = extractGridDetails(grid).rows; | |
| const targets = bind$2(indexes, index => getColumnCells(rows, index, comparator)); | |
| const rowHeaders = map$1(rows, row => isHeaderCells(row.cells)); | |
| const shouldReplaceCell = columnReplacePredicate(targets, rowHeaders); | |
| const scopeGenerator = columnScopeGenerator(applyScope, rowHeaders); | |
| return replaceIn(grid, targets, comparator, substitution, replace, scopeGenerator, shouldReplaceCell); | |
| }; | |
| const replaceRows = (grid, indexes, section, applyScope, comparator, substitution, tableSection) => { | |
| const {cols, rows} = extractGridDetails(grid); | |
| const targetRow = rows[indexes[0]]; | |
| const targets = bind$2(indexes, index => getRowCells(rows, index, comparator)); | |
| const columnHeaders = map$1(targetRow.cells, (_cell, index) => isHeaderCells(getColumnCells(rows, index, comparator))); | |
| const newRows = [...rows]; | |
| each$2(indexes, index => { | |
| newRows[index] = tableSection.transformRow(rows[index], section); | |
| }); | |
| const newGrid = [ | |
| ...cols, | |
| ...newRows | |
| ]; | |
| const shouldReplaceCell = rowReplacerPredicate(targetRow, columnHeaders); | |
| const scopeGenerator = rowScopeGenerator(applyScope, columnHeaders); | |
| return replaceIn(newGrid, targets, comparator, substitution, tableSection.transformCell, scopeGenerator, shouldReplaceCell); | |
| }; | |
| const replaceCells = (grid, details, comparator, substitution) => { | |
| const rows = extractGridDetails(grid).rows; | |
| const targetCells = map$1(details, detail => getCell(rows[detail.row], detail.column)); | |
| return replaceIn(grid, targetCells, comparator, substitution, replace, Optional.none, always); | |
| }; | |
| const generate = cases => { | |
| if (!isArray(cases)) { | |
| throw new Error('cases must be an array'); | |
| } | |
| if (cases.length === 0) { | |
| throw new Error('there must be at least one case'); | |
| } | |
| const constructors = []; | |
| const adt = {}; | |
| each$2(cases, (acase, count) => { | |
| const keys$1 = keys(acase); | |
| if (keys$1.length !== 1) { | |
| throw new Error('one and only one name per case'); | |
| } | |
| const key = keys$1[0]; | |
| const value = acase[key]; | |
| if (adt[key] !== undefined) { | |
| throw new Error('duplicate key detected:' + key); | |
| } else if (key === 'cata') { | |
| throw new Error('cannot have a case named cata (sorry)'); | |
| } else if (!isArray(value)) { | |
| throw new Error('case arguments must be an array'); | |
| } | |
| constructors.push(key); | |
| adt[key] = (...args) => { | |
| const argLength = args.length; | |
| if (argLength !== value.length) { | |
| throw new Error('Wrong number of arguments to case ' + key + '. Expected ' + value.length + ' (' + value + '), got ' + argLength); | |
| } | |
| const match = branches => { | |
| const branchKeys = keys(branches); | |
| if (constructors.length !== branchKeys.length) { | |
| throw new Error('Wrong number of arguments to match. Expected: ' + constructors.join(',') + '\nActual: ' + branchKeys.join(',')); | |
| } | |
| const allReqd = forall(constructors, reqKey => { | |
| return contains$2(branchKeys, reqKey); | |
| }); | |
| if (!allReqd) { | |
| throw new Error('Not all branches were specified when using match. Specified: ' + branchKeys.join(', ') + '\nRequired: ' + constructors.join(', ')); | |
| } | |
| return branches[key].apply(null, args); | |
| }; | |
| return { | |
| fold: (...foldArgs) => { | |
| if (foldArgs.length !== cases.length) { | |
| throw new Error('Wrong number of arguments to fold. Expected ' + cases.length + ', got ' + foldArgs.length); | |
| } | |
| const target = foldArgs[count]; | |
| return target.apply(null, args); | |
| }, | |
| match, | |
| log: label => { | |
| console.log(label, { | |
| constructors, | |
| constructor: key, | |
| params: args | |
| }); | |
| } | |
| }; | |
| }; | |
| }); | |
| return adt; | |
| }; | |
| const Adt = { generate }; | |
| const adt$6 = Adt.generate([ | |
| { none: [] }, | |
| { only: ['index'] }, | |
| { | |
| left: [ | |
| 'index', | |
| 'next' | |
| ] | |
| }, | |
| { | |
| middle: [ | |
| 'prev', | |
| 'index', | |
| 'next' | |
| ] | |
| }, | |
| { | |
| right: [ | |
| 'prev', | |
| 'index' | |
| ] | |
| } | |
| ]); | |
| const ColumnContext = { ...adt$6 }; | |
| const neighbours = (input, index) => { | |
| if (input.length === 0) { | |
| return ColumnContext.none(); | |
| } | |
| if (input.length === 1) { | |
| return ColumnContext.only(0); | |
| } | |
| if (index === 0) { | |
| return ColumnContext.left(0, 1); | |
| } | |
| if (index === input.length - 1) { | |
| return ColumnContext.right(index - 1, index); | |
| } | |
| if (index > 0 && index < input.length - 1) { | |
| return ColumnContext.middle(index - 1, index, index + 1); | |
| } | |
| return ColumnContext.none(); | |
| }; | |
| const determine = (input, column, step, tableSize, resize) => { | |
| const result = input.slice(0); | |
| const context = neighbours(input, column); | |
| const onNone = constant(map$1(result, constant(0))); | |
| const onOnly = index => tableSize.singleColumnWidth(result[index], step); | |
| const onLeft = (index, next) => resize.calcLeftEdgeDeltas(result, index, next, step, tableSize.minCellWidth(), tableSize.isRelative); | |
| const onMiddle = (prev, index, next) => resize.calcMiddleDeltas(result, prev, index, next, step, tableSize.minCellWidth(), tableSize.isRelative); | |
| const onRight = (prev, index) => resize.calcRightEdgeDeltas(result, prev, index, step, tableSize.minCellWidth(), tableSize.isRelative); | |
| return context.fold(onNone, onOnly, onLeft, onMiddle, onRight); | |
| }; | |
| const total = (start, end, measures) => { | |
| let r = 0; | |
| for (let i = start; i < end; i++) { | |
| r += measures[i] !== undefined ? measures[i] : 0; | |
| } | |
| return r; | |
| }; | |
| const recalculateWidthForCells = (warehouse, widths) => { | |
| const all = Warehouse.justCells(warehouse); | |
| return map$1(all, cell => { | |
| const width = total(cell.column, cell.column + cell.colspan, widths); | |
| return { | |
| element: cell.element, | |
| width, | |
| colspan: cell.colspan | |
| }; | |
| }); | |
| }; | |
| const recalculateWidthForColumns = (warehouse, widths) => { | |
| const groups = Warehouse.justColumns(warehouse); | |
| return map$1(groups, (column, index) => ({ | |
| element: column.element, | |
| width: widths[index], | |
| colspan: column.colspan | |
| })); | |
| }; | |
| const recalculateHeightForCells = (warehouse, heights) => { | |
| const all = Warehouse.justCells(warehouse); | |
| return map$1(all, cell => { | |
| const height = total(cell.row, cell.row + cell.rowspan, heights); | |
| return { | |
| element: cell.element, | |
| height, | |
| rowspan: cell.rowspan | |
| }; | |
| }); | |
| }; | |
| const matchRowHeight = (warehouse, heights) => { | |
| return map$1(warehouse.all, (row, i) => { | |
| return { | |
| element: row.element, | |
| height: heights[i] | |
| }; | |
| }); | |
| }; | |
| const sumUp = newSize => foldr(newSize, (b, a) => b + a, 0); | |
| const recalculate = (warehouse, widths) => { | |
| if (Warehouse.hasColumns(warehouse)) { | |
| return recalculateWidthForColumns(warehouse, widths); | |
| } else { | |
| return recalculateWidthForCells(warehouse, widths); | |
| } | |
| }; | |
| const recalculateAndApply = (warehouse, widths, tableSize) => { | |
| const newSizes = recalculate(warehouse, widths); | |
| each$2(newSizes, cell => { | |
| tableSize.setElementWidth(cell.element, cell.width); | |
| }); | |
| }; | |
| const adjustWidth = (table, delta, index, resizing, tableSize) => { | |
| const warehouse = Warehouse.fromTable(table); | |
| const step = tableSize.getCellDelta(delta); | |
| const widths = tableSize.getWidths(warehouse, tableSize); | |
| const isLastColumn = index === warehouse.grid.columns - 1; | |
| const clampedStep = resizing.clampTableDelta(widths, index, step, tableSize.minCellWidth(), isLastColumn); | |
| const deltas = determine(widths, index, clampedStep, tableSize, resizing); | |
| const newWidths = map$1(deltas, (dx, i) => dx + widths[i]); | |
| recalculateAndApply(warehouse, newWidths, tableSize); | |
| resizing.resizeTable(tableSize.adjustTableWidth, clampedStep, isLastColumn); | |
| }; | |
| const adjustHeight = (table, delta, index, direction) => { | |
| const warehouse = Warehouse.fromTable(table); | |
| const heights = getPixelHeights(warehouse, table, direction); | |
| const newHeights = map$1(heights, (dy, i) => index === i ? Math.max(delta + dy, minHeight()) : dy); | |
| const newCellSizes = recalculateHeightForCells(warehouse, newHeights); | |
| const newRowSizes = matchRowHeight(warehouse, newHeights); | |
| each$2(newRowSizes, row => { | |
| setHeight(row.element, row.height); | |
| }); | |
| each$2(newCellSizes, cell => { | |
| setHeight(cell.element, cell.height); | |
| }); | |
| const total = sumUp(newHeights); | |
| setHeight(table, total); | |
| }; | |
| const adjustAndRedistributeWidths$1 = (_table, list, details, tableSize, resizeBehaviour) => { | |
| const warehouse = Warehouse.generate(list); | |
| const sizes = tableSize.getWidths(warehouse, tableSize); | |
| const tablePixelWidth = tableSize.pixelWidth(); | |
| const {newSizes, delta} = resizeBehaviour.calcRedestributedWidths(sizes, tablePixelWidth, details.pixelDelta, tableSize.isRelative); | |
| recalculateAndApply(warehouse, newSizes, tableSize); | |
| tableSize.adjustTableWidth(delta); | |
| }; | |
| const adjustWidthTo = (_table, list, _info, tableSize) => { | |
| const warehouse = Warehouse.generate(list); | |
| const widths = tableSize.getWidths(warehouse, tableSize); | |
| recalculateAndApply(warehouse, widths, tableSize); | |
| }; | |
| const uniqueColumns = details => { | |
| const uniqueCheck = (rest, detail) => { | |
| const columnExists = exists(rest, currentDetail => currentDetail.column === detail.column); | |
| return columnExists ? rest : rest.concat([detail]); | |
| }; | |
| return foldl(details, uniqueCheck, []).sort((detailA, detailB) => detailA.column - detailB.column); | |
| }; | |
| const isCol = isTag('col'); | |
| const isColgroup = isTag('colgroup'); | |
| const isRow$1 = element => name(element) === 'tr' || isColgroup(element); | |
| const elementToData = element => { | |
| const colspan = getAttrValue(element, 'colspan', 1); | |
| const rowspan = getAttrValue(element, 'rowspan', 1); | |
| return { | |
| element, | |
| colspan, | |
| rowspan | |
| }; | |
| }; | |
| const modification = (generators, toData = elementToData) => { | |
| const nuCell = data => isCol(data.element) ? generators.col(data) : generators.cell(data); | |
| const nuRow = data => isColgroup(data.element) ? generators.colgroup(data) : generators.row(data); | |
| const add = element => { | |
| if (isRow$1(element)) { | |
| return nuRow({ element }); | |
| } else { | |
| const cell = element; | |
| const replacement = nuCell(toData(cell)); | |
| recent = Optional.some({ | |
| item: cell, | |
| replacement | |
| }); | |
| return replacement; | |
| } | |
| }; | |
| let recent = Optional.none(); | |
| const getOrInit = (element, comparator) => { | |
| return recent.fold(() => { | |
| return add(element); | |
| }, p => { | |
| return comparator(element, p.item) ? p.replacement : add(element); | |
| }); | |
| }; | |
| return { getOrInit }; | |
| }; | |
| const transform$1 = tag => { | |
| return generators => { | |
| const list = []; | |
| const find = (element, comparator) => { | |
| return find$1(list, x => { | |
| return comparator(x.item, element); | |
| }); | |
| }; | |
| const makeNew = element => { | |
| const attrs = tag === 'td' ? { scope: null } : {}; | |
| const cell = generators.replace(element, tag, attrs); | |
| list.push({ | |
| item: element, | |
| sub: cell | |
| }); | |
| return cell; | |
| }; | |
| const replaceOrInit = (element, comparator) => { | |
| if (isRow$1(element) || isCol(element)) { | |
| return element; | |
| } else { | |
| const cell = element; | |
| return find(cell, comparator).fold(() => { | |
| return makeNew(cell); | |
| }, p => { | |
| return comparator(element, p.item) ? p.sub : makeNew(cell); | |
| }); | |
| } | |
| }; | |
| return { replaceOrInit }; | |
| }; | |
| }; | |
| const getScopeAttribute = cell => getOpt(cell, 'scope').map(attribute => attribute.substr(0, 3)); | |
| const merging = generators => { | |
| const unmerge = cell => { | |
| const scope = getScopeAttribute(cell); | |
| scope.each(attribute => set$2(cell, 'scope', attribute)); | |
| return () => { | |
| const raw = generators.cell({ | |
| element: cell, | |
| colspan: 1, | |
| rowspan: 1 | |
| }); | |
| remove$5(raw, 'width'); | |
| remove$5(cell, 'width'); | |
| scope.each(attribute => set$2(raw, 'scope', attribute)); | |
| return raw; | |
| }; | |
| }; | |
| const merge = cells => { | |
| const getScopeProperty = () => { | |
| const stringAttributes = cat(map$1(cells, getScopeAttribute)); | |
| if (stringAttributes.length === 0) { | |
| return Optional.none(); | |
| } else { | |
| const baseScope = stringAttributes[0]; | |
| const scopes = [ | |
| 'row', | |
| 'col' | |
| ]; | |
| const isMixed = exists(stringAttributes, attribute => { | |
| return attribute !== baseScope && contains$2(scopes, attribute); | |
| }); | |
| return isMixed ? Optional.none() : Optional.from(baseScope); | |
| } | |
| }; | |
| remove$5(cells[0], 'width'); | |
| getScopeProperty().fold(() => remove$7(cells[0], 'scope'), attribute => set$2(cells[0], 'scope', attribute + 'group')); | |
| return constant(cells[0]); | |
| }; | |
| return { | |
| unmerge, | |
| merge | |
| }; | |
| }; | |
| const Generators = { | |
| modification, | |
| transform: transform$1, | |
| merging | |
| }; | |
| const blockList = [ | |
| 'body', | |
| 'p', | |
| 'div', | |
| 'article', | |
| 'aside', | |
| 'figcaption', | |
| 'figure', | |
| 'footer', | |
| 'header', | |
| 'nav', | |
| 'section', | |
| 'ol', | |
| 'ul', | |
| 'table', | |
| 'thead', | |
| 'tfoot', | |
| 'tbody', | |
| 'caption', | |
| 'tr', | |
| 'td', | |
| 'th', | |
| 'h1', | |
| 'h2', | |
| 'h3', | |
| 'h4', | |
| 'h5', | |
| 'h6', | |
| 'blockquote', | |
| 'pre', | |
| 'address' | |
| ]; | |
| const isList$1 = (universe, item) => { | |
| const tagName = universe.property().name(item); | |
| return contains$2([ | |
| 'ol', | |
| 'ul' | |
| ], tagName); | |
| }; | |
| const isBlock$1 = (universe, item) => { | |
| const tagName = universe.property().name(item); | |
| return contains$2(blockList, tagName); | |
| }; | |
| const isEmptyTag$1 = (universe, item) => { | |
| return contains$2([ | |
| 'br', | |
| 'img', | |
| 'hr', | |
| 'input' | |
| ], universe.property().name(item)); | |
| }; | |
| const universe$1 = DomUniverse(); | |
| const isBlock = element => { | |
| return isBlock$1(universe$1, element); | |
| }; | |
| const isList = element => { | |
| return isList$1(universe$1, element); | |
| }; | |
| const isEmptyTag = element => { | |
| return isEmptyTag$1(universe$1, element); | |
| }; | |
| const merge = cells => { | |
| const isBr = isTag('br'); | |
| const advancedBr = children => { | |
| return forall(children, c => { | |
| return isBr(c) || isText(c) && get$6(c).trim().length === 0; | |
| }); | |
| }; | |
| const isListItem = el => { | |
| return name(el) === 'li' || ancestor$2(el, isList).isSome(); | |
| }; | |
| const siblingIsBlock = el => { | |
| return nextSibling(el).map(rightSibling => { | |
| if (isBlock(rightSibling)) { | |
| return true; | |
| } | |
| if (isEmptyTag(rightSibling)) { | |
| return name(rightSibling) === 'img' ? false : true; | |
| } | |
| return false; | |
| }).getOr(false); | |
| }; | |
| const markCell = cell => { | |
| return last$1(cell).bind(rightEdge => { | |
| const rightSiblingIsBlock = siblingIsBlock(rightEdge); | |
| return parent(rightEdge).map(parent => { | |
| return rightSiblingIsBlock === true || isListItem(parent) || isBr(rightEdge) || isBlock(parent) && !eq$1(cell, parent) ? [] : [SugarElement.fromTag('br')]; | |
| }); | |
| }).getOr([]); | |
| }; | |
| const markContent = () => { | |
| const content = bind$2(cells, cell => { | |
| const children = children$2(cell); | |
| return advancedBr(children) ? [] : children.concat(markCell(cell)); | |
| }); | |
| return content.length === 0 ? [SugarElement.fromTag('br')] : content; | |
| }; | |
| const contents = markContent(); | |
| empty(cells[0]); | |
| append(cells[0], contents); | |
| }; | |
| const isEditable = elem => isEditable$1(elem, true); | |
| const prune = table => { | |
| const cells = cells$1(table); | |
| if (cells.length === 0) { | |
| remove$6(table); | |
| } | |
| }; | |
| const outcome = (grid, cursor) => ({ | |
| grid, | |
| cursor | |
| }); | |
| const findEditableCursorPosition = rows => findMap(rows, row => findMap(row.cells, cell => { | |
| const elem = cell.element; | |
| return someIf(isEditable(elem), elem); | |
| })); | |
| const elementFromGrid = (grid, row, column) => { | |
| var _a, _b; | |
| const rows = extractGridDetails(grid).rows; | |
| return Optional.from((_b = (_a = rows[row]) === null || _a === void 0 ? void 0 : _a.cells[column]) === null || _b === void 0 ? void 0 : _b.element).filter(isEditable).orThunk(() => findEditableCursorPosition(rows)); | |
| }; | |
| const bundle = (grid, row, column) => { | |
| const cursorElement = elementFromGrid(grid, row, column); | |
| return outcome(grid, cursorElement); | |
| }; | |
| const uniqueRows = details => { | |
| const rowCompilation = (rest, detail) => { | |
| const rowExists = exists(rest, currentDetail => currentDetail.row === detail.row); | |
| return rowExists ? rest : rest.concat([detail]); | |
| }; | |
| return foldl(details, rowCompilation, []).sort((detailA, detailB) => detailA.row - detailB.row); | |
| }; | |
| const opInsertRowsBefore = (grid, details, comparator, genWrappers) => { | |
| const targetIndex = details[0].row; | |
| const rows = uniqueRows(details); | |
| const newGrid = foldr(rows, (acc, row) => { | |
| const newG = insertRowAt(acc.grid, targetIndex, row.row + acc.delta, comparator, genWrappers.getOrInit); | |
| return { | |
| grid: newG, | |
| delta: acc.delta + 1 | |
| }; | |
| }, { | |
| grid, | |
| delta: 0 | |
| }).grid; | |
| return bundle(newGrid, targetIndex, details[0].column); | |
| }; | |
| const opInsertRowsAfter = (grid, details, comparator, genWrappers) => { | |
| const rows = uniqueRows(details); | |
| const target = rows[rows.length - 1]; | |
| const targetIndex = target.row + target.rowspan; | |
| const newGrid = foldr(rows, (newG, row) => { | |
| return insertRowAt(newG, targetIndex, row.row, comparator, genWrappers.getOrInit); | |
| }, grid); | |
| return bundle(newGrid, targetIndex, details[0].column); | |
| }; | |
| const opInsertColumnsBefore = (grid, extractDetail, comparator, genWrappers) => { | |
| const details = extractDetail.details; | |
| const columns = uniqueColumns(details); | |
| const targetIndex = columns[0].column; | |
| const newGrid = foldr(columns, (acc, col) => { | |
| const newG = insertColumnAt(acc.grid, targetIndex, col.column + acc.delta, comparator, genWrappers.getOrInit); | |
| return { | |
| grid: newG, | |
| delta: acc.delta + 1 | |
| }; | |
| }, { | |
| grid, | |
| delta: 0 | |
| }).grid; | |
| return bundle(newGrid, details[0].row, targetIndex); | |
| }; | |
| const opInsertColumnsAfter = (grid, extractDetail, comparator, genWrappers) => { | |
| const details = extractDetail.details; | |
| const target = details[details.length - 1]; | |
| const targetIndex = target.column + target.colspan; | |
| const columns = uniqueColumns(details); | |
| const newGrid = foldr(columns, (newG, col) => { | |
| return insertColumnAt(newG, targetIndex, col.column, comparator, genWrappers.getOrInit); | |
| }, grid); | |
| return bundle(newGrid, details[0].row, targetIndex); | |
| }; | |
| const opMakeColumnsHeader = (initialGrid, details, comparator, genWrappers) => { | |
| const columns = uniqueColumns(details); | |
| const columnIndexes = map$1(columns, detail => detail.column); | |
| const newGrid = replaceColumns(initialGrid, columnIndexes, true, comparator, genWrappers.replaceOrInit); | |
| return bundle(newGrid, details[0].row, details[0].column); | |
| }; | |
| const opMakeCellsHeader = (initialGrid, details, comparator, genWrappers) => { | |
| const newGrid = replaceCells(initialGrid, details, comparator, genWrappers.replaceOrInit); | |
| return bundle(newGrid, details[0].row, details[0].column); | |
| }; | |
| const opUnmakeColumnsHeader = (initialGrid, details, comparator, genWrappers) => { | |
| const columns = uniqueColumns(details); | |
| const columnIndexes = map$1(columns, detail => detail.column); | |
| const newGrid = replaceColumns(initialGrid, columnIndexes, false, comparator, genWrappers.replaceOrInit); | |
| return bundle(newGrid, details[0].row, details[0].column); | |
| }; | |
| const opUnmakeCellsHeader = (initialGrid, details, comparator, genWrappers) => { | |
| const newGrid = replaceCells(initialGrid, details, comparator, genWrappers.replaceOrInit); | |
| return bundle(newGrid, details[0].row, details[0].column); | |
| }; | |
| const makeRowsSection = (section, applyScope) => (initialGrid, details, comparator, genWrappers, tableSection) => { | |
| const rows = uniqueRows(details); | |
| const rowIndexes = map$1(rows, detail => detail.row); | |
| const newGrid = replaceRows(initialGrid, rowIndexes, section, applyScope, comparator, genWrappers.replaceOrInit, tableSection); | |
| return bundle(newGrid, details[0].row, details[0].column); | |
| }; | |
| const opMakeRowsHeader = makeRowsSection('thead', true); | |
| const opMakeRowsBody = makeRowsSection('tbody', false); | |
| const opMakeRowsFooter = makeRowsSection('tfoot', false); | |
| const opEraseColumns = (grid, extractDetail, _comparator, _genWrappers) => { | |
| const columns = uniqueColumns(extractDetail.details); | |
| const newGrid = deleteColumnsAt(grid, map$1(columns, column => column.column)); | |
| const maxColIndex = newGrid.length > 0 ? newGrid[0].cells.length - 1 : 0; | |
| return bundle(newGrid, columns[0].row, Math.min(columns[0].column, maxColIndex)); | |
| }; | |
| const opEraseRows = (grid, details, _comparator, _genWrappers) => { | |
| const rows = uniqueRows(details); | |
| const newGrid = deleteRowsAt(grid, rows[0].row, rows[rows.length - 1].row); | |
| const maxRowIndex = newGrid.length > 0 ? newGrid.length - 1 : 0; | |
| return bundle(newGrid, Math.min(details[0].row, maxRowIndex), details[0].column); | |
| }; | |
| const opMergeCells = (grid, mergable, comparator, genWrappers) => { | |
| const cells = mergable.cells; | |
| merge(cells); | |
| const newGrid = merge$2(grid, mergable.bounds, comparator, genWrappers.merge(cells)); | |
| return outcome(newGrid, Optional.from(cells[0])); | |
| }; | |
| const opUnmergeCells = (grid, unmergable, comparator, genWrappers) => { | |
| const unmerge$1 = (b, cell) => unmerge(b, cell, comparator, genWrappers.unmerge(cell)); | |
| const newGrid = foldr(unmergable, unmerge$1, grid); | |
| return outcome(newGrid, Optional.from(unmergable[0])); | |
| }; | |
| const opPasteCells = (grid, pasteDetails, comparator, _genWrappers) => { | |
| const gridify = (table, generators) => { | |
| const wh = Warehouse.fromTable(table); | |
| return toGrid(wh, generators, true); | |
| }; | |
| const gridB = gridify(pasteDetails.clipboard, pasteDetails.generators); | |
| const startAddress = address(pasteDetails.row, pasteDetails.column); | |
| const mergedGrid = merge$1(startAddress, grid, gridB, pasteDetails.generators, comparator); | |
| return mergedGrid.fold(() => outcome(grid, Optional.some(pasteDetails.element)), newGrid => { | |
| return bundle(newGrid, pasteDetails.row, pasteDetails.column); | |
| }); | |
| }; | |
| const gridifyRows = (rows, generators, context) => { | |
| const pasteDetails = fromPastedRows(rows, context.section); | |
| const wh = Warehouse.generate(pasteDetails); | |
| return toGrid(wh, generators, true); | |
| }; | |
| const opPasteColsBefore = (grid, pasteDetails, comparator, _genWrappers) => { | |
| const rows = extractGridDetails(grid).rows; | |
| const index = pasteDetails.cells[0].column; | |
| const context = rows[pasteDetails.cells[0].row]; | |
| const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context); | |
| const mergedGrid = insertCols(index, grid, gridB, pasteDetails.generators, comparator); | |
| return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column); | |
| }; | |
| const opPasteColsAfter = (grid, pasteDetails, comparator, _genWrappers) => { | |
| const rows = extractGridDetails(grid).rows; | |
| const index = pasteDetails.cells[pasteDetails.cells.length - 1].column + pasteDetails.cells[pasteDetails.cells.length - 1].colspan; | |
| const context = rows[pasteDetails.cells[0].row]; | |
| const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context); | |
| const mergedGrid = insertCols(index, grid, gridB, pasteDetails.generators, comparator); | |
| return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column); | |
| }; | |
| const opPasteRowsBefore = (grid, pasteDetails, comparator, _genWrappers) => { | |
| const rows = extractGridDetails(grid).rows; | |
| const index = pasteDetails.cells[0].row; | |
| const context = rows[index]; | |
| const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context); | |
| const mergedGrid = insertRows(index, grid, gridB, pasteDetails.generators, comparator); | |
| return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column); | |
| }; | |
| const opPasteRowsAfter = (grid, pasteDetails, comparator, _genWrappers) => { | |
| const rows = extractGridDetails(grid).rows; | |
| const index = pasteDetails.cells[pasteDetails.cells.length - 1].row + pasteDetails.cells[pasteDetails.cells.length - 1].rowspan; | |
| const context = rows[pasteDetails.cells[0].row]; | |
| const gridB = gridifyRows(pasteDetails.clipboard, pasteDetails.generators, context); | |
| const mergedGrid = insertRows(index, grid, gridB, pasteDetails.generators, comparator); | |
| return bundle(mergedGrid, pasteDetails.cells[0].row, pasteDetails.cells[0].column); | |
| }; | |
| const opGetColumnsType = (table, target) => { | |
| const house = Warehouse.fromTable(table); | |
| const details = onCells(house, target); | |
| return details.bind(selectedCells => { | |
| const lastSelectedCell = selectedCells[selectedCells.length - 1]; | |
| const minColRange = selectedCells[0].column; | |
| const maxColRange = lastSelectedCell.column + lastSelectedCell.colspan; | |
| const selectedColumnCells = flatten(map$1(house.all, row => filter$2(row.cells, cell => cell.column >= minColRange && cell.column < maxColRange))); | |
| return findCommonCellType(selectedColumnCells); | |
| }).getOr(''); | |
| }; | |
| const opGetCellsType = (table, target) => { | |
| const house = Warehouse.fromTable(table); | |
| const details = onCells(house, target); | |
| return details.bind(findCommonCellType).getOr(''); | |
| }; | |
| const opGetRowsType = (table, target) => { | |
| const house = Warehouse.fromTable(table); | |
| const details = onCells(house, target); | |
| return details.bind(selectedCells => { | |
| const lastSelectedCell = selectedCells[selectedCells.length - 1]; | |
| const minRowRange = selectedCells[0].row; | |
| const maxRowRange = lastSelectedCell.row + lastSelectedCell.rowspan; | |
| const selectedRows = house.all.slice(minRowRange, maxRowRange); | |
| return findCommonRowType(selectedRows); | |
| }).getOr(''); | |
| }; | |
| const resize = (table, list, details, behaviours) => adjustWidthTo(table, list, details, behaviours.sizing); | |
| const adjustAndRedistributeWidths = (table, list, details, behaviours) => adjustAndRedistributeWidths$1(table, list, details, behaviours.sizing, behaviours.resize); | |
| const firstColumnIsLocked = (_warehouse, details) => exists(details, detail => detail.column === 0 && detail.isLocked); | |
| const lastColumnIsLocked = (warehouse, details) => exists(details, detail => detail.column + detail.colspan >= warehouse.grid.columns && detail.isLocked); | |
| const getColumnsWidth = (warehouse, details) => { | |
| const columns$1 = columns(warehouse); | |
| const uniqueCols = uniqueColumns(details); | |
| return foldl(uniqueCols, (acc, detail) => { | |
| const column = columns$1[detail.column]; | |
| const colWidth = column.map(getOuter$2).getOr(0); | |
| return acc + colWidth; | |
| }, 0); | |
| }; | |
| const insertColumnsExtractor = before => (warehouse, target) => onCells(warehouse, target).filter(details => { | |
| const checkLocked = before ? firstColumnIsLocked : lastColumnIsLocked; | |
| return !checkLocked(warehouse, details); | |
| }).map(details => ({ | |
| details, | |
| pixelDelta: getColumnsWidth(warehouse, details) | |
| })); | |
| const eraseColumnsExtractor = (warehouse, target) => onUnlockedCells(warehouse, target).map(details => ({ | |
| details, | |
| pixelDelta: -getColumnsWidth(warehouse, details) | |
| })); | |
| const pasteColumnsExtractor = before => (warehouse, target) => onPasteByEditor(warehouse, target).filter(details => { | |
| const checkLocked = before ? firstColumnIsLocked : lastColumnIsLocked; | |
| return !checkLocked(warehouse, details.cells); | |
| }); | |
| const headerCellGenerator = Generators.transform('th'); | |
| const bodyCellGenerator = Generators.transform('td'); | |
| const insertRowsBefore = run(opInsertRowsBefore, onCells, noop, noop, Generators.modification); | |
| const insertRowsAfter = run(opInsertRowsAfter, onCells, noop, noop, Generators.modification); | |
| const insertColumnsBefore = run(opInsertColumnsBefore, insertColumnsExtractor(true), adjustAndRedistributeWidths, noop, Generators.modification); | |
| const insertColumnsAfter = run(opInsertColumnsAfter, insertColumnsExtractor(false), adjustAndRedistributeWidths, noop, Generators.modification); | |
| const eraseColumns = run(opEraseColumns, eraseColumnsExtractor, adjustAndRedistributeWidths, prune, Generators.modification); | |
| const eraseRows = run(opEraseRows, onCells, noop, prune, Generators.modification); | |
| const makeColumnsHeader = run(opMakeColumnsHeader, onUnlockedCells, noop, noop, headerCellGenerator); | |
| const unmakeColumnsHeader = run(opUnmakeColumnsHeader, onUnlockedCells, noop, noop, bodyCellGenerator); | |
| const makeRowsHeader = run(opMakeRowsHeader, onUnlockedCells, noop, noop, headerCellGenerator); | |
| const makeRowsBody = run(opMakeRowsBody, onUnlockedCells, noop, noop, bodyCellGenerator); | |
| const makeRowsFooter = run(opMakeRowsFooter, onUnlockedCells, noop, noop, bodyCellGenerator); | |
| const makeCellsHeader = run(opMakeCellsHeader, onUnlockedCells, noop, noop, headerCellGenerator); | |
| const unmakeCellsHeader = run(opUnmakeCellsHeader, onUnlockedCells, noop, noop, bodyCellGenerator); | |
| const mergeCells = run(opMergeCells, onUnlockedMergable, resize, noop, Generators.merging); | |
| const unmergeCells = run(opUnmergeCells, onUnlockedUnmergable, resize, noop, Generators.merging); | |
| const pasteCells = run(opPasteCells, onPaste, resize, noop, Generators.modification); | |
| const pasteColsBefore = run(opPasteColsBefore, pasteColumnsExtractor(true), noop, noop, Generators.modification); | |
| const pasteColsAfter = run(opPasteColsAfter, pasteColumnsExtractor(false), noop, noop, Generators.modification); | |
| const pasteRowsBefore = run(opPasteRowsBefore, onPasteByEditor, noop, noop, Generators.modification); | |
| const pasteRowsAfter = run(opPasteRowsAfter, onPasteByEditor, noop, noop, Generators.modification); | |
| const getColumnsType = opGetColumnsType; | |
| const getCellsType = opGetCellsType; | |
| const getRowsType = opGetRowsType; | |
| const fireNewRow = (editor, row) => editor.dispatch('NewRow', { node: row }); | |
| const fireNewCell = (editor, cell) => editor.dispatch('NewCell', { node: cell }); | |
| const fireTableModified = (editor, table, data) => { | |
| editor.dispatch('TableModified', { | |
| ...data, | |
| table | |
| }); | |
| }; | |
| const fireTableSelectionChange = (editor, cells, start, finish, otherCells) => { | |
| editor.dispatch('TableSelectionChange', { | |
| cells, | |
| start, | |
| finish, | |
| otherCells | |
| }); | |
| }; | |
| const fireTableSelectionClear = editor => { | |
| editor.dispatch('TableSelectionClear'); | |
| }; | |
| const fireObjectResizeStart = (editor, target, width, height, origin) => { | |
| editor.dispatch('ObjectResizeStart', { | |
| target, | |
| width, | |
| height, | |
| origin | |
| }); | |
| }; | |
| const fireObjectResized = (editor, target, width, height, origin) => { | |
| editor.dispatch('ObjectResized', { | |
| target, | |
| width, | |
| height, | |
| origin | |
| }); | |
| }; | |
| const styleModified = { | |
| structure: false, | |
| style: true | |
| }; | |
| const structureModified = { | |
| structure: true, | |
| style: false | |
| }; | |
| const styleAndStructureModified = { | |
| structure: true, | |
| style: true | |
| }; | |
| const option = name => editor => editor.options.get(name); | |
| const defaultWidth = '100%'; | |
| const getPixelForcedWidth = editor => { | |
| var _a; | |
| const dom = editor.dom; | |
| const parentBlock = (_a = dom.getParent(editor.selection.getStart(), dom.isBlock)) !== null && _a !== void 0 ? _a : editor.getBody(); | |
| return getInner(SugarElement.fromDom(parentBlock)) + 'px'; | |
| }; | |
| const determineDefaultTableStyles = (editor, defaultStyles) => { | |
| if (isTableResponsiveForced(editor) || !shouldStyleWithCss(editor)) { | |
| return defaultStyles; | |
| } else if (isTablePixelsForced(editor)) { | |
| return { | |
| ...defaultStyles, | |
| width: getPixelForcedWidth(editor) | |
| }; | |
| } else { | |
| return { | |
| ...defaultStyles, | |
| width: defaultWidth | |
| }; | |
| } | |
| }; | |
| const determineDefaultTableAttributes = (editor, defaultAttributes) => { | |
| if (isTableResponsiveForced(editor) || shouldStyleWithCss(editor)) { | |
| return defaultAttributes; | |
| } else if (isTablePixelsForced(editor)) { | |
| return { | |
| ...defaultAttributes, | |
| width: getPixelForcedWidth(editor) | |
| }; | |
| } else { | |
| return { | |
| ...defaultAttributes, | |
| width: defaultWidth | |
| }; | |
| } | |
| }; | |
| const register = editor => { | |
| const registerOption = editor.options.register; | |
| registerOption('table_clone_elements', { processor: 'string[]' }); | |
| registerOption('table_use_colgroups', { | |
| processor: 'boolean', | |
| default: true | |
| }); | |
| registerOption('table_header_type', { | |
| processor: value => { | |
| const valid = contains$2([ | |
| 'section', | |
| 'cells', | |
| 'sectionCells', | |
| 'auto' | |
| ], value); | |
| return valid ? { | |
| value, | |
| valid | |
| } : { | |
| valid: false, | |
| message: 'Must be one of: section, cells, sectionCells or auto.' | |
| }; | |
| }, | |
| default: 'section' | |
| }); | |
| registerOption('table_sizing_mode', { | |
| processor: 'string', | |
| default: 'auto' | |
| }); | |
| registerOption('table_default_attributes', { | |
| processor: 'object', | |
| default: { border: '1' } | |
| }); | |
| registerOption('table_default_styles', { | |
| processor: 'object', | |
| default: { 'border-collapse': 'collapse' } | |
| }); | |
| registerOption('table_column_resizing', { | |
| processor: value => { | |
| const valid = contains$2([ | |
| 'preservetable', | |
| 'resizetable' | |
| ], value); | |
| return valid ? { | |
| value, | |
| valid | |
| } : { | |
| valid: false, | |
| message: 'Must be preservetable, or resizetable.' | |
| }; | |
| }, | |
| default: 'preservetable' | |
| }); | |
| registerOption('table_resize_bars', { | |
| processor: 'boolean', | |
| default: true | |
| }); | |
| registerOption('table_style_by_css', { | |
| processor: 'boolean', | |
| default: true | |
| }); | |
| }; | |
| const getTableCloneElements = editor => { | |
| return Optional.from(editor.options.get('table_clone_elements')); | |
| }; | |
| const hasTableObjectResizing = editor => { | |
| const objectResizing = editor.options.get('object_resizing'); | |
| return contains$2(objectResizing.split(','), 'table'); | |
| }; | |
| const getTableHeaderType = option('table_header_type'); | |
| const getTableColumnResizingBehaviour = option('table_column_resizing'); | |
| const isPreserveTableColumnResizing = editor => getTableColumnResizingBehaviour(editor) === 'preservetable'; | |
| const isResizeTableColumnResizing = editor => getTableColumnResizingBehaviour(editor) === 'resizetable'; | |
| const getTableSizingMode = option('table_sizing_mode'); | |
| const isTablePercentagesForced = editor => getTableSizingMode(editor) === 'relative'; | |
| const isTablePixelsForced = editor => getTableSizingMode(editor) === 'fixed'; | |
| const isTableResponsiveForced = editor => getTableSizingMode(editor) === 'responsive'; | |
| const hasTableResizeBars = option('table_resize_bars'); | |
| const shouldStyleWithCss = option('table_style_by_css'); | |
| const getTableDefaultAttributes = editor => { | |
| const options = editor.options; | |
| const defaultAttributes = options.get('table_default_attributes'); | |
| return options.isSet('table_default_attributes') ? defaultAttributes : determineDefaultTableAttributes(editor, defaultAttributes); | |
| }; | |
| const getTableDefaultStyles = editor => { | |
| const options = editor.options; | |
| const defaultStyles = options.get('table_default_styles'); | |
| return options.isSet('table_default_styles') ? defaultStyles : determineDefaultTableStyles(editor, defaultStyles); | |
| }; | |
| const tableUseColumnGroup = option('table_use_colgroups'); | |
| const get$5 = (editor, table) => { | |
| if (isTablePercentagesForced(editor)) { | |
| return TableSize.percentageSize(table); | |
| } else if (isTablePixelsForced(editor)) { | |
| return TableSize.pixelSize(table); | |
| } else { | |
| return TableSize.getTableSize(table); | |
| } | |
| }; | |
| const TableActions = (editor, resizeHandler, cellSelectionHandler) => { | |
| const isTableBody = editor => name(getBody(editor)) === 'table'; | |
| const lastRowGuard = table => !isTableBody(editor) || getGridSize(table).rows > 1; | |
| const lastColumnGuard = table => !isTableBody(editor) || getGridSize(table).columns > 1; | |
| const cloneFormats = getTableCloneElements(editor); | |
| const colMutationOp = isResizeTableColumnResizing(editor) ? noop : halve; | |
| const getTableSectionType = table => { | |
| switch (getTableHeaderType(editor)) { | |
| case 'section': | |
| return TableSection.section(); | |
| case 'sectionCells': | |
| return TableSection.sectionCells(); | |
| case 'cells': | |
| return TableSection.cells(); | |
| default: | |
| return TableSection.getTableSectionType(table, 'section'); | |
| } | |
| }; | |
| const setSelectionFromAction = (table, result) => result.cursor.fold(() => { | |
| const cells = cells$1(table); | |
| return head(cells).filter(inBody).map(firstCell => { | |
| cellSelectionHandler.clearSelectedCells(table.dom); | |
| const rng = editor.dom.createRng(); | |
| rng.selectNode(firstCell.dom); | |
| editor.selection.setRng(rng); | |
| set$2(firstCell, 'data-mce-selected', '1'); | |
| return rng; | |
| }); | |
| }, cell => { | |
| const des = freefallRtl(cell); | |
| const rng = editor.dom.createRng(); | |
| rng.setStart(des.element.dom, des.offset); | |
| rng.setEnd(des.element.dom, des.offset); | |
| editor.selection.setRng(rng); | |
| cellSelectionHandler.clearSelectedCells(table.dom); | |
| return Optional.some(rng); | |
| }); | |
| const execute = (operation, guard, mutate, effect) => (table, target, noEvents = false) => { | |
| removeDataStyle(table); | |
| const doc = SugarElement.fromDom(editor.getDoc()); | |
| const generators = cellOperations(mutate, doc, cloneFormats); | |
| const behaviours = { | |
| sizing: get$5(editor, table), | |
| resize: isResizeTableColumnResizing(editor) ? resizeTable() : preserveTable(), | |
| section: getTableSectionType(table) | |
| }; | |
| return guard(table) ? operation(table, target, generators, behaviours).bind(result => { | |
| resizeHandler.refresh(table.dom); | |
| each$2(result.newRows, row => { | |
| fireNewRow(editor, row.dom); | |
| }); | |
| each$2(result.newCells, cell => { | |
| fireNewCell(editor, cell.dom); | |
| }); | |
| const range = setSelectionFromAction(table, result); | |
| if (inBody(table)) { | |
| removeDataStyle(table); | |
| if (!noEvents) { | |
| fireTableModified(editor, table.dom, effect); | |
| } | |
| } | |
| return range.map(rng => ({ | |
| rng, | |
| effect | |
| })); | |
| }) : Optional.none(); | |
| }; | |
| const deleteRow = execute(eraseRows, lastRowGuard, noop, structureModified); | |
| const deleteColumn = execute(eraseColumns, lastColumnGuard, noop, structureModified); | |
| const insertRowsBefore$1 = execute(insertRowsBefore, always, noop, structureModified); | |
| const insertRowsAfter$1 = execute(insertRowsAfter, always, noop, structureModified); | |
| const insertColumnsBefore$1 = execute(insertColumnsBefore, always, colMutationOp, structureModified); | |
| const insertColumnsAfter$1 = execute(insertColumnsAfter, always, colMutationOp, structureModified); | |
| const mergeCells$1 = execute(mergeCells, always, noop, structureModified); | |
| const unmergeCells$1 = execute(unmergeCells, always, noop, structureModified); | |
| const pasteColsBefore$1 = execute(pasteColsBefore, always, noop, structureModified); | |
| const pasteColsAfter$1 = execute(pasteColsAfter, always, noop, structureModified); | |
| const pasteRowsBefore$1 = execute(pasteRowsBefore, always, noop, structureModified); | |
| const pasteRowsAfter$1 = execute(pasteRowsAfter, always, noop, structureModified); | |
| const pasteCells$1 = execute(pasteCells, always, noop, styleAndStructureModified); | |
| const makeCellsHeader$1 = execute(makeCellsHeader, always, noop, structureModified); | |
| const unmakeCellsHeader$1 = execute(unmakeCellsHeader, always, noop, structureModified); | |
| const makeColumnsHeader$1 = execute(makeColumnsHeader, always, noop, structureModified); | |
| const unmakeColumnsHeader$1 = execute(unmakeColumnsHeader, always, noop, structureModified); | |
| const makeRowsHeader$1 = execute(makeRowsHeader, always, noop, structureModified); | |
| const makeRowsBody$1 = execute(makeRowsBody, always, noop, structureModified); | |
| const makeRowsFooter$1 = execute(makeRowsFooter, always, noop, structureModified); | |
| const getTableCellType = getCellsType; | |
| const getTableColType = getColumnsType; | |
| const getTableRowType = getRowsType; | |
| return { | |
| deleteRow, | |
| deleteColumn, | |
| insertRowsBefore: insertRowsBefore$1, | |
| insertRowsAfter: insertRowsAfter$1, | |
| insertColumnsBefore: insertColumnsBefore$1, | |
| insertColumnsAfter: insertColumnsAfter$1, | |
| mergeCells: mergeCells$1, | |
| unmergeCells: unmergeCells$1, | |
| pasteColsBefore: pasteColsBefore$1, | |
| pasteColsAfter: pasteColsAfter$1, | |
| pasteRowsBefore: pasteRowsBefore$1, | |
| pasteRowsAfter: pasteRowsAfter$1, | |
| pasteCells: pasteCells$1, | |
| makeCellsHeader: makeCellsHeader$1, | |
| unmakeCellsHeader: unmakeCellsHeader$1, | |
| makeColumnsHeader: makeColumnsHeader$1, | |
| unmakeColumnsHeader: unmakeColumnsHeader$1, | |
| makeRowsHeader: makeRowsHeader$1, | |
| makeRowsBody: makeRowsBody$1, | |
| makeRowsFooter: makeRowsFooter$1, | |
| getTableRowType, | |
| getTableCellType, | |
| getTableColType | |
| }; | |
| }; | |
| const constrainSpan = (element, property, value) => { | |
| const currentColspan = getAttrValue(element, property, 1); | |
| if (value === 1 || currentColspan <= 1) { | |
| remove$7(element, property); | |
| } else { | |
| set$2(element, property, Math.min(value, currentColspan)); | |
| } | |
| }; | |
| const isColInRange = (minColRange, maxColRange) => cell => { | |
| const endCol = cell.column + cell.colspan - 1; | |
| const startCol = cell.column; | |
| return endCol >= minColRange && startCol < maxColRange; | |
| }; | |
| const generateColGroup = (house, minColRange, maxColRange) => { | |
| if (Warehouse.hasColumns(house)) { | |
| const colsToCopy = filter$2(Warehouse.justColumns(house), isColInRange(minColRange, maxColRange)); | |
| const copiedCols = map$1(colsToCopy, c => { | |
| const clonedCol = deep(c.element); | |
| constrainSpan(clonedCol, 'span', maxColRange - minColRange); | |
| return clonedCol; | |
| }); | |
| const fakeColgroup = SugarElement.fromTag('colgroup'); | |
| append(fakeColgroup, copiedCols); | |
| return [fakeColgroup]; | |
| } else { | |
| return []; | |
| } | |
| }; | |
| const generateRows = (house, minColRange, maxColRange) => map$1(house.all, row => { | |
| const cellsToCopy = filter$2(row.cells, isColInRange(minColRange, maxColRange)); | |
| const copiedCells = map$1(cellsToCopy, cell => { | |
| const clonedCell = deep(cell.element); | |
| constrainSpan(clonedCell, 'colspan', maxColRange - minColRange); | |
| return clonedCell; | |
| }); | |
| const fakeTR = SugarElement.fromTag('tr'); | |
| append(fakeTR, copiedCells); | |
| return fakeTR; | |
| }); | |
| const copyCols = (table, target) => { | |
| const house = Warehouse.fromTable(table); | |
| const details = onUnlockedCells(house, target); | |
| return details.map(selectedCells => { | |
| const lastSelectedCell = selectedCells[selectedCells.length - 1]; | |
| const minColRange = selectedCells[0].column; | |
| const maxColRange = lastSelectedCell.column + lastSelectedCell.colspan; | |
| const fakeColGroups = generateColGroup(house, minColRange, maxColRange); | |
| const fakeRows = generateRows(house, minColRange, maxColRange); | |
| return [ | |
| ...fakeColGroups, | |
| ...fakeRows | |
| ]; | |
| }); | |
| }; | |
| const copyRows = (table, target, generators) => { | |
| const warehouse = Warehouse.fromTable(table); | |
| const details = onCells(warehouse, target); | |
| return details.bind(selectedCells => { | |
| const grid = toGrid(warehouse, generators, false); | |
| const rows = extractGridDetails(grid).rows; | |
| const slicedGrid = rows.slice(selectedCells[0].row, selectedCells[selectedCells.length - 1].row + selectedCells[selectedCells.length - 1].rowspan); | |
| const filteredGrid = bind$2(slicedGrid, row => { | |
| const newCells = filter$2(row.cells, cell => !cell.isLocked); | |
| return newCells.length > 0 ? [{ | |
| ...row, | |
| cells: newCells | |
| }] : []; | |
| }); | |
| const slicedDetails = toDetailList(filteredGrid); | |
| return someIf(slicedDetails.length > 0, slicedDetails); | |
| }).map(slicedDetails => copy(slicedDetails)); | |
| }; | |
| const adt$5 = Adt.generate([ | |
| { invalid: ['raw'] }, | |
| { pixels: ['value'] }, | |
| { percent: ['value'] } | |
| ]); | |
| const validateFor = (suffix, type, value) => { | |
| const rawAmount = value.substring(0, value.length - suffix.length); | |
| const amount = parseFloat(rawAmount); | |
| return rawAmount === amount.toString() ? type(amount) : adt$5.invalid(value); | |
| }; | |
| const from = value => { | |
| if (endsWith(value, '%')) { | |
| return validateFor('%', adt$5.percent, value); | |
| } | |
| if (endsWith(value, 'px')) { | |
| return validateFor('px', adt$5.pixels, value); | |
| } | |
| return adt$5.invalid(value); | |
| }; | |
| const Size = { | |
| ...adt$5, | |
| from | |
| }; | |
| const redistributeToPercent = (widths, totalWidth) => { | |
| return map$1(widths, w => { | |
| const colType = Size.from(w); | |
| return colType.fold(() => { | |
| return w; | |
| }, px => { | |
| const ratio = px / totalWidth * 100; | |
| return ratio + '%'; | |
| }, pc => { | |
| return pc + '%'; | |
| }); | |
| }); | |
| }; | |
| const redistributeToPx = (widths, totalWidth, newTotalWidth) => { | |
| const scale = newTotalWidth / totalWidth; | |
| return map$1(widths, w => { | |
| const colType = Size.from(w); | |
| return colType.fold(() => { | |
| return w; | |
| }, px => { | |
| return px * scale + 'px'; | |
| }, pc => { | |
| return pc / 100 * newTotalWidth + 'px'; | |
| }); | |
| }); | |
| }; | |
| const redistributeEmpty = (newWidthType, columns) => { | |
| const f = newWidthType.fold(() => constant(''), pixels => { | |
| const num = pixels / columns; | |
| return constant(num + 'px'); | |
| }, () => { | |
| const num = 100 / columns; | |
| return constant(num + '%'); | |
| }); | |
| return range$1(columns, f); | |
| }; | |
| const redistributeValues = (newWidthType, widths, totalWidth) => { | |
| return newWidthType.fold(() => { | |
| return widths; | |
| }, px => { | |
| return redistributeToPx(widths, totalWidth, px); | |
| }, _pc => { | |
| return redistributeToPercent(widths, totalWidth); | |
| }); | |
| }; | |
| const redistribute$1 = (widths, totalWidth, newWidth) => { | |
| const newType = Size.from(newWidth); | |
| const floats = forall(widths, s => { | |
| return s === '0px'; | |
| }) ? redistributeEmpty(newType, widths.length) : redistributeValues(newType, widths, totalWidth); | |
| return normalize(floats); | |
| }; | |
| const sum = (values, fallback) => { | |
| if (values.length === 0) { | |
| return fallback; | |
| } | |
| return foldr(values, (rest, v) => { | |
| return Size.from(v).fold(constant(0), identity, identity) + rest; | |
| }, 0); | |
| }; | |
| const roundDown = (num, unit) => { | |
| const floored = Math.floor(num); | |
| return { | |
| value: floored + unit, | |
| remainder: num - floored | |
| }; | |
| }; | |
| const add$3 = (value, amount) => { | |
| return Size.from(value).fold(constant(value), px => { | |
| return px + amount + 'px'; | |
| }, pc => { | |
| return pc + amount + '%'; | |
| }); | |
| }; | |
| const normalize = values => { | |
| if (values.length === 0) { | |
| return values; | |
| } | |
| const scan = foldr(values, (rest, value) => { | |
| const info = Size.from(value).fold(() => ({ | |
| value, | |
| remainder: 0 | |
| }), num => roundDown(num, 'px'), num => ({ | |
| value: num + '%', | |
| remainder: 0 | |
| })); | |
| return { | |
| output: [info.value].concat(rest.output), | |
| remainder: rest.remainder + info.remainder | |
| }; | |
| }, { | |
| output: [], | |
| remainder: 0 | |
| }); | |
| const r = scan.output; | |
| return r.slice(0, r.length - 1).concat([add$3(r[r.length - 1], Math.round(scan.remainder))]); | |
| }; | |
| const validate = Size.from; | |
| const redistributeToW = (newWidths, cells, unit) => { | |
| each$2(cells, cell => { | |
| const widths = newWidths.slice(cell.column, cell.colspan + cell.column); | |
| const w = sum(widths, minWidth()); | |
| set$1(cell.element, 'width', w + unit); | |
| }); | |
| }; | |
| const redistributeToColumns = (newWidths, columns, unit) => { | |
| each$2(columns, (column, index) => { | |
| const width = sum([newWidths[index]], minWidth()); | |
| set$1(column.element, 'width', width + unit); | |
| }); | |
| }; | |
| const redistributeToH = (newHeights, rows, cells, unit) => { | |
| each$2(cells, cell => { | |
| const heights = newHeights.slice(cell.row, cell.rowspan + cell.row); | |
| const h = sum(heights, minHeight()); | |
| set$1(cell.element, 'height', h + unit); | |
| }); | |
| each$2(rows, (row, i) => { | |
| set$1(row.element, 'height', newHeights[i]); | |
| }); | |
| }; | |
| const getUnit = newSize => { | |
| return validate(newSize).fold(constant('px'), constant('px'), constant('%')); | |
| }; | |
| const redistribute = (table, optWidth, optHeight) => { | |
| const warehouse = Warehouse.fromTable(table); | |
| const rows = warehouse.all; | |
| const cells = Warehouse.justCells(warehouse); | |
| const columns = Warehouse.justColumns(warehouse); | |
| optWidth.each(newWidth => { | |
| const widthUnit = getUnit(newWidth); | |
| const totalWidth = get$9(table); | |
| const oldWidths = getRawWidths(warehouse, table); | |
| const nuWidths = redistribute$1(oldWidths, totalWidth, newWidth); | |
| if (Warehouse.hasColumns(warehouse)) { | |
| redistributeToColumns(nuWidths, columns, widthUnit); | |
| } else { | |
| redistributeToW(nuWidths, cells, widthUnit); | |
| } | |
| set$1(table, 'width', newWidth); | |
| }); | |
| optHeight.each(newHeight => { | |
| const hUnit = getUnit(newHeight); | |
| const totalHeight = get$8(table); | |
| const oldHeights = getRawHeights(warehouse, table, height); | |
| const nuHeights = redistribute$1(oldHeights, totalHeight, newHeight); | |
| redistributeToH(nuHeights, rows, cells, hUnit); | |
| set$1(table, 'height', newHeight); | |
| }); | |
| }; | |
| const isPercentSizing = isPercentSizing$1; | |
| const isPixelSizing = isPixelSizing$1; | |
| const isNoneSizing = isNoneSizing$1; | |
| const cleanupLegacyAttributes = element => { | |
| remove$7(element, 'width'); | |
| }; | |
| const convertToPercentSize = table => { | |
| const newWidth = getPercentTableWidth(table); | |
| redistribute(table, Optional.some(newWidth), Optional.none()); | |
| cleanupLegacyAttributes(table); | |
| }; | |
| const convertToPixelSize = table => { | |
| const newWidth = getPixelTableWidth(table); | |
| redistribute(table, Optional.some(newWidth), Optional.none()); | |
| cleanupLegacyAttributes(table); | |
| }; | |
| const convertToNoneSize = table => { | |
| remove$5(table, 'width'); | |
| const columns = columns$1(table); | |
| const rowElements = columns.length > 0 ? columns : cells$1(table); | |
| each$2(rowElements, cell => { | |
| remove$5(cell, 'width'); | |
| cleanupLegacyAttributes(cell); | |
| }); | |
| cleanupLegacyAttributes(table); | |
| }; | |
| const DefaultRenderOptions = { | |
| styles: { | |
| 'border-collapse': 'collapse', | |
| 'width': '100%' | |
| }, | |
| attributes: { border: '1' }, | |
| colGroups: false | |
| }; | |
| const tableHeaderCell = () => SugarElement.fromTag('th'); | |
| const tableCell = () => SugarElement.fromTag('td'); | |
| const tableColumn = () => SugarElement.fromTag('col'); | |
| const createRow = (columns, rowHeaders, columnHeaders, rowIndex) => { | |
| const tr = SugarElement.fromTag('tr'); | |
| for (let j = 0; j < columns; j++) { | |
| const td = rowIndex < rowHeaders || j < columnHeaders ? tableHeaderCell() : tableCell(); | |
| if (j < columnHeaders) { | |
| set$2(td, 'scope', 'row'); | |
| } | |
| if (rowIndex < rowHeaders) { | |
| set$2(td, 'scope', 'col'); | |
| } | |
| append$1(td, SugarElement.fromTag('br')); | |
| append$1(tr, td); | |
| } | |
| return tr; | |
| }; | |
| const createGroupRow = columns => { | |
| const columnGroup = SugarElement.fromTag('colgroup'); | |
| range$1(columns, () => append$1(columnGroup, tableColumn())); | |
| return columnGroup; | |
| }; | |
| const createRows = (rows, columns, rowHeaders, columnHeaders) => range$1(rows, r => createRow(columns, rowHeaders, columnHeaders, r)); | |
| const render = (rows, columns, rowHeaders, columnHeaders, headerType, renderOpts = DefaultRenderOptions) => { | |
| const table = SugarElement.fromTag('table'); | |
| const rowHeadersGoInThead = headerType !== 'cells'; | |
| setAll(table, renderOpts.styles); | |
| setAll$1(table, renderOpts.attributes); | |
| if (renderOpts.colGroups) { | |
| append$1(table, createGroupRow(columns)); | |
| } | |
| const actualRowHeaders = Math.min(rows, rowHeaders); | |
| if (rowHeadersGoInThead && rowHeaders > 0) { | |
| const thead = SugarElement.fromTag('thead'); | |
| append$1(table, thead); | |
| const theadRowHeaders = headerType === 'sectionCells' ? actualRowHeaders : 0; | |
| const theadRows = createRows(rowHeaders, columns, theadRowHeaders, columnHeaders); | |
| append(thead, theadRows); | |
| } | |
| const tbody = SugarElement.fromTag('tbody'); | |
| append$1(table, tbody); | |
| const numRows = rowHeadersGoInThead ? rows - actualRowHeaders : rows; | |
| const numRowHeaders = rowHeadersGoInThead ? 0 : rowHeaders; | |
| const tbodyRows = createRows(numRows, columns, numRowHeaders, columnHeaders); | |
| append(tbody, tbodyRows); | |
| return table; | |
| }; | |
| const get$4 = element => element.dom.innerHTML; | |
| const getOuter = element => { | |
| const container = SugarElement.fromTag('div'); | |
| const clone = SugarElement.fromDom(element.dom.cloneNode(true)); | |
| append$1(container, clone); | |
| return get$4(container); | |
| }; | |
| const placeCaretInCell = (editor, cell) => { | |
| editor.selection.select(cell.dom, true); | |
| editor.selection.collapse(true); | |
| }; | |
| const selectFirstCellInTable = (editor, tableElm) => { | |
| descendant(tableElm, 'td,th').each(curry(placeCaretInCell, editor)); | |
| }; | |
| const fireEvents = (editor, table) => { | |
| each$2(descendants(table, 'tr'), row => { | |
| fireNewRow(editor, row.dom); | |
| each$2(descendants(row, 'th,td'), cell => { | |
| fireNewCell(editor, cell.dom); | |
| }); | |
| }); | |
| }; | |
| const isPercentage = width => isString(width) && width.indexOf('%') !== -1; | |
| const insert = (editor, columns, rows, colHeaders, rowHeaders) => { | |
| const defaultStyles = getTableDefaultStyles(editor); | |
| const options = { | |
| styles: defaultStyles, | |
| attributes: getTableDefaultAttributes(editor), | |
| colGroups: tableUseColumnGroup(editor) | |
| }; | |
| editor.undoManager.ignore(() => { | |
| const table = render(rows, columns, rowHeaders, colHeaders, getTableHeaderType(editor), options); | |
| set$2(table, 'data-mce-id', '__mce'); | |
| const html = getOuter(table); | |
| editor.insertContent(html); | |
| editor.addVisual(); | |
| }); | |
| return descendant(getBody(editor), 'table[data-mce-id="__mce"]').map(table => { | |
| if (isTablePixelsForced(editor)) { | |
| convertToPixelSize(table); | |
| } else if (isTableResponsiveForced(editor)) { | |
| convertToNoneSize(table); | |
| } else if (isTablePercentagesForced(editor) || isPercentage(defaultStyles.width)) { | |
| convertToPercentSize(table); | |
| } | |
| removeDataStyle(table); | |
| remove$7(table, 'data-mce-id'); | |
| fireEvents(editor, table); | |
| selectFirstCellInTable(editor, table); | |
| return table.dom; | |
| }).getOrNull(); | |
| }; | |
| const insertTable = (editor, rows, columns, options = {}) => { | |
| const checkInput = val => isNumber(val) && val > 0; | |
| if (checkInput(rows) && checkInput(columns)) { | |
| const headerRows = options.headerRows || 0; | |
| const headerColumns = options.headerColumns || 0; | |
| return insert(editor, columns, rows, headerColumns, headerRows); | |
| } else { | |
| console.error('Invalid values for mceInsertTable - rows and columns values are required to insert a table.'); | |
| return null; | |
| } | |
| }; | |
| var global = tinymce.util.Tools.resolve('tinymce.FakeClipboard'); | |
| const tableTypeBase = 'x-tinymce/dom-table-'; | |
| const tableTypeRow = tableTypeBase + 'rows'; | |
| const tableTypeColumn = tableTypeBase + 'columns'; | |
| const setData = items => { | |
| const fakeClipboardItem = global.FakeClipboardItem(items); | |
| global.write([fakeClipboardItem]); | |
| }; | |
| const getData = type => { | |
| var _a; | |
| const items = (_a = global.read()) !== null && _a !== void 0 ? _a : []; | |
| return findMap(items, item => Optional.from(item.getType(type))); | |
| }; | |
| const clearData = type => { | |
| if (getData(type).isSome()) { | |
| global.clear(); | |
| } | |
| }; | |
| const setRows = rowsOpt => { | |
| rowsOpt.fold(clearRows, rows => setData({ [tableTypeRow]: rows })); | |
| }; | |
| const getRows = () => getData(tableTypeRow); | |
| const clearRows = () => clearData(tableTypeRow); | |
| const setColumns = columnsOpt => { | |
| columnsOpt.fold(clearColumns, columns => setData({ [tableTypeColumn]: columns })); | |
| }; | |
| const getColumns = () => getData(tableTypeColumn); | |
| const clearColumns = () => clearData(tableTypeColumn); | |
| const getSelectionStartCellOrCaption = editor => getSelectionCellOrCaption(getSelectionStart(editor), getIsRoot(editor)).filter(isInEditableContext$1); | |
| const getSelectionStartCell = editor => getSelectionCell(getSelectionStart(editor), getIsRoot(editor)).filter(isInEditableContext$1); | |
| const registerCommands = (editor, actions) => { | |
| const isRoot = getIsRoot(editor); | |
| const eraseTable = () => getSelectionStartCellOrCaption(editor).each(cellOrCaption => { | |
| table(cellOrCaption, isRoot).filter(not(isRoot)).each(table => { | |
| const cursor = SugarElement.fromText(''); | |
| after$5(table, cursor); | |
| remove$6(table); | |
| if (editor.dom.isEmpty(editor.getBody())) { | |
| editor.setContent(''); | |
| editor.selection.setCursorLocation(); | |
| } else { | |
| const rng = editor.dom.createRng(); | |
| rng.setStart(cursor.dom, 0); | |
| rng.setEnd(cursor.dom, 0); | |
| editor.selection.setRng(rng); | |
| editor.nodeChanged(); | |
| } | |
| }); | |
| }); | |
| const setSizingMode = sizing => getSelectionStartCellOrCaption(editor).each(cellOrCaption => { | |
| const isForcedSizing = isTableResponsiveForced(editor) || isTablePixelsForced(editor) || isTablePercentagesForced(editor); | |
| if (!isForcedSizing) { | |
| table(cellOrCaption, isRoot).each(table => { | |
| if (sizing === 'relative' && !isPercentSizing(table)) { | |
| convertToPercentSize(table); | |
| } else if (sizing === 'fixed' && !isPixelSizing(table)) { | |
| convertToPixelSize(table); | |
| } else if (sizing === 'responsive' && !isNoneSizing(table)) { | |
| convertToNoneSize(table); | |
| } | |
| removeDataStyle(table); | |
| fireTableModified(editor, table.dom, structureModified); | |
| }); | |
| } | |
| }); | |
| const getTableFromCell = cell => table(cell, isRoot); | |
| const performActionOnSelection = action => getSelectionStartCell(editor).bind(cell => getTableFromCell(cell).map(table => action(table, cell))); | |
| const toggleTableClass = (_ui, clazz) => { | |
| performActionOnSelection(table => { | |
| editor.formatter.toggle('tableclass', { value: clazz }, table.dom); | |
| fireTableModified(editor, table.dom, styleModified); | |
| }); | |
| }; | |
| const toggleTableCellClass = (_ui, clazz) => { | |
| performActionOnSelection(table => { | |
| const selectedCells = getCellsFromSelection(editor); | |
| const allHaveClass = forall(selectedCells, cell => editor.formatter.match('tablecellclass', { value: clazz }, cell.dom)); | |
| const formatterAction = allHaveClass ? editor.formatter.remove : editor.formatter.apply; | |
| each$2(selectedCells, cell => formatterAction('tablecellclass', { value: clazz }, cell.dom)); | |
| fireTableModified(editor, table.dom, styleModified); | |
| }); | |
| }; | |
| const toggleCaption = () => { | |
| getSelectionStartCellOrCaption(editor).each(cellOrCaption => { | |
| table(cellOrCaption, isRoot).each(table => { | |
| child(table, 'caption').fold(() => { | |
| const caption = SugarElement.fromTag('caption'); | |
| append$1(caption, SugarElement.fromText('Caption')); | |
| appendAt(table, caption, 0); | |
| editor.selection.setCursorLocation(caption.dom, 0); | |
| }, caption => { | |
| if (isTag('caption')(cellOrCaption)) { | |
| one('td', table).each(td => editor.selection.setCursorLocation(td.dom, 0)); | |
| } | |
| remove$6(caption); | |
| }); | |
| fireTableModified(editor, table.dom, structureModified); | |
| }); | |
| }); | |
| }; | |
| const postExecute = _data => { | |
| editor.focus(); | |
| }; | |
| const actOnSelection = (execute, noEvents = false) => performActionOnSelection((table, startCell) => { | |
| const targets = forMenu(getCellsFromSelection(editor), table, startCell); | |
| execute(table, targets, noEvents).each(postExecute); | |
| }); | |
| const copyRowSelection = () => performActionOnSelection((table, startCell) => { | |
| const targets = forMenu(getCellsFromSelection(editor), table, startCell); | |
| const generators = cellOperations(noop, SugarElement.fromDom(editor.getDoc()), Optional.none()); | |
| return copyRows(table, targets, generators); | |
| }); | |
| const copyColSelection = () => performActionOnSelection((table, startCell) => { | |
| const targets = forMenu(getCellsFromSelection(editor), table, startCell); | |
| return copyCols(table, targets); | |
| }); | |
| const pasteOnSelection = (execute, getRows) => getRows().each(rows => { | |
| const clonedRows = map$1(rows, row => deep(row)); | |
| performActionOnSelection((table, startCell) => { | |
| const generators = paste$1(SugarElement.fromDom(editor.getDoc())); | |
| const targets = pasteRows(getCellsFromSelection(editor), startCell, clonedRows, generators); | |
| execute(table, targets).each(postExecute); | |
| }); | |
| }); | |
| const actOnType = getAction => (_ui, args) => get$c(args, 'type').each(type => { | |
| actOnSelection(getAction(type), args.no_events); | |
| }); | |
| each$1({ | |
| mceTableSplitCells: () => actOnSelection(actions.unmergeCells), | |
| mceTableMergeCells: () => actOnSelection(actions.mergeCells), | |
| mceTableInsertRowBefore: () => actOnSelection(actions.insertRowsBefore), | |
| mceTableInsertRowAfter: () => actOnSelection(actions.insertRowsAfter), | |
| mceTableInsertColBefore: () => actOnSelection(actions.insertColumnsBefore), | |
| mceTableInsertColAfter: () => actOnSelection(actions.insertColumnsAfter), | |
| mceTableDeleteCol: () => actOnSelection(actions.deleteColumn), | |
| mceTableDeleteRow: () => actOnSelection(actions.deleteRow), | |
| mceTableCutCol: () => copyColSelection().each(selection => { | |
| setColumns(selection); | |
| actOnSelection(actions.deleteColumn); | |
| }), | |
| mceTableCutRow: () => copyRowSelection().each(selection => { | |
| setRows(selection); | |
| actOnSelection(actions.deleteRow); | |
| }), | |
| mceTableCopyCol: () => copyColSelection().each(selection => setColumns(selection)), | |
| mceTableCopyRow: () => copyRowSelection().each(selection => setRows(selection)), | |
| mceTablePasteColBefore: () => pasteOnSelection(actions.pasteColsBefore, getColumns), | |
| mceTablePasteColAfter: () => pasteOnSelection(actions.pasteColsAfter, getColumns), | |
| mceTablePasteRowBefore: () => pasteOnSelection(actions.pasteRowsBefore, getRows), | |
| mceTablePasteRowAfter: () => pasteOnSelection(actions.pasteRowsAfter, getRows), | |
| mceTableDelete: eraseTable, | |
| mceTableCellToggleClass: toggleTableCellClass, | |
| mceTableToggleClass: toggleTableClass, | |
| mceTableToggleCaption: toggleCaption, | |
| mceTableSizingMode: (_ui, sizing) => setSizingMode(sizing), | |
| mceTableCellType: actOnType(type => type === 'th' ? actions.makeCellsHeader : actions.unmakeCellsHeader), | |
| mceTableColType: actOnType(type => type === 'th' ? actions.makeColumnsHeader : actions.unmakeColumnsHeader), | |
| mceTableRowType: actOnType(type => { | |
| switch (type) { | |
| case 'header': | |
| return actions.makeRowsHeader; | |
| case 'footer': | |
| return actions.makeRowsFooter; | |
| default: | |
| return actions.makeRowsBody; | |
| } | |
| }) | |
| }, (func, name) => editor.addCommand(name, func)); | |
| editor.addCommand('mceInsertTable', (_ui, args) => { | |
| insertTable(editor, args.rows, args.columns, args.options); | |
| }); | |
| editor.addCommand('mceTableApplyCellStyle', (_ui, args) => { | |
| const getFormatName = style => 'tablecell' + style.toLowerCase().replace('-', ''); | |
| if (!isObject(args)) { | |
| return; | |
| } | |
| const cells = filter$2(getCellsFromSelection(editor), isInEditableContext$1); | |
| if (cells.length === 0) { | |
| return; | |
| } | |
| const validArgs = filter$1(args, (value, style) => editor.formatter.has(getFormatName(style)) && isString(value)); | |
| if (isEmpty(validArgs)) { | |
| return; | |
| } | |
| each$1(validArgs, (value, style) => { | |
| const formatName = getFormatName(style); | |
| each$2(cells, cell => { | |
| if (value === '') { | |
| editor.formatter.remove(formatName, { value: null }, cell.dom, true); | |
| } else { | |
| editor.formatter.apply(formatName, { value }, cell.dom); | |
| } | |
| }); | |
| }); | |
| getTableFromCell(cells[0]).each(table => fireTableModified(editor, table.dom, styleModified)); | |
| }); | |
| }; | |
| const registerQueryCommands = (editor, actions) => { | |
| const isRoot = getIsRoot(editor); | |
| const lookupOnSelection = action => getSelectionCell(getSelectionStart(editor)).bind(cell => table(cell, isRoot).map(table => { | |
| const targets = forMenu(getCellsFromSelection(editor), table, cell); | |
| return action(table, targets); | |
| })).getOr(''); | |
| each$1({ | |
| mceTableRowType: () => lookupOnSelection(actions.getTableRowType), | |
| mceTableCellType: () => lookupOnSelection(actions.getTableCellType), | |
| mceTableColType: () => lookupOnSelection(actions.getTableColType) | |
| }, (func, name) => editor.addQueryValueHandler(name, func)); | |
| }; | |
| const adt$4 = Adt.generate([ | |
| { before: ['element'] }, | |
| { | |
| on: [ | |
| 'element', | |
| 'offset' | |
| ] | |
| }, | |
| { after: ['element'] } | |
| ]); | |
| const cata$1 = (subject, onBefore, onOn, onAfter) => subject.fold(onBefore, onOn, onAfter); | |
| const getStart$1 = situ => situ.fold(identity, identity, identity); | |
| const before$2 = adt$4.before; | |
| const on = adt$4.on; | |
| const after$3 = adt$4.after; | |
| const Situ = { | |
| before: before$2, | |
| on, | |
| after: after$3, | |
| cata: cata$1, | |
| getStart: getStart$1 | |
| }; | |
| const create$4 = (selection, kill) => ({ | |
| selection, | |
| kill | |
| }); | |
| const Response = { create: create$4 }; | |
| const selectNode = (win, element) => { | |
| const rng = win.document.createRange(); | |
| rng.selectNode(element.dom); | |
| return rng; | |
| }; | |
| const selectNodeContents = (win, element) => { | |
| const rng = win.document.createRange(); | |
| selectNodeContentsUsing(rng, element); | |
| return rng; | |
| }; | |
| const selectNodeContentsUsing = (rng, element) => rng.selectNodeContents(element.dom); | |
| const setStart = (rng, situ) => { | |
| situ.fold(e => { | |
| rng.setStartBefore(e.dom); | |
| }, (e, o) => { | |
| rng.setStart(e.dom, o); | |
| }, e => { | |
| rng.setStartAfter(e.dom); | |
| }); | |
| }; | |
| const setFinish = (rng, situ) => { | |
| situ.fold(e => { | |
| rng.setEndBefore(e.dom); | |
| }, (e, o) => { | |
| rng.setEnd(e.dom, o); | |
| }, e => { | |
| rng.setEndAfter(e.dom); | |
| }); | |
| }; | |
| const relativeToNative = (win, startSitu, finishSitu) => { | |
| const range = win.document.createRange(); | |
| setStart(range, startSitu); | |
| setFinish(range, finishSitu); | |
| return range; | |
| }; | |
| const exactToNative = (win, start, soffset, finish, foffset) => { | |
| const rng = win.document.createRange(); | |
| rng.setStart(start.dom, soffset); | |
| rng.setEnd(finish.dom, foffset); | |
| return rng; | |
| }; | |
| const toRect = rect => ({ | |
| left: rect.left, | |
| top: rect.top, | |
| right: rect.right, | |
| bottom: rect.bottom, | |
| width: rect.width, | |
| height: rect.height | |
| }); | |
| const getFirstRect$1 = rng => { | |
| const rects = rng.getClientRects(); | |
| const rect = rects.length > 0 ? rects[0] : rng.getBoundingClientRect(); | |
| return rect.width > 0 || rect.height > 0 ? Optional.some(rect).map(toRect) : Optional.none(); | |
| }; | |
| const adt$3 = Adt.generate([ | |
| { | |
| ltr: [ | |
| 'start', | |
| 'soffset', | |
| 'finish', | |
| 'foffset' | |
| ] | |
| }, | |
| { | |
| rtl: [ | |
| 'start', | |
| 'soffset', | |
| 'finish', | |
| 'foffset' | |
| ] | |
| } | |
| ]); | |
| const fromRange = (win, type, range) => type(SugarElement.fromDom(range.startContainer), range.startOffset, SugarElement.fromDom(range.endContainer), range.endOffset); | |
| const getRanges = (win, selection) => selection.match({ | |
| domRange: rng => { | |
| return { | |
| ltr: constant(rng), | |
| rtl: Optional.none | |
| }; | |
| }, | |
| relative: (startSitu, finishSitu) => { | |
| return { | |
| ltr: cached(() => relativeToNative(win, startSitu, finishSitu)), | |
| rtl: cached(() => Optional.some(relativeToNative(win, finishSitu, startSitu))) | |
| }; | |
| }, | |
| exact: (start, soffset, finish, foffset) => { | |
| return { | |
| ltr: cached(() => exactToNative(win, start, soffset, finish, foffset)), | |
| rtl: cached(() => Optional.some(exactToNative(win, finish, foffset, start, soffset))) | |
| }; | |
| } | |
| }); | |
| const doDiagnose = (win, ranges) => { | |
| const rng = ranges.ltr(); | |
| if (rng.collapsed) { | |
| const reversed = ranges.rtl().filter(rev => rev.collapsed === false); | |
| return reversed.map(rev => adt$3.rtl(SugarElement.fromDom(rev.endContainer), rev.endOffset, SugarElement.fromDom(rev.startContainer), rev.startOffset)).getOrThunk(() => fromRange(win, adt$3.ltr, rng)); | |
| } else { | |
| return fromRange(win, adt$3.ltr, rng); | |
| } | |
| }; | |
| const diagnose = (win, selection) => { | |
| const ranges = getRanges(win, selection); | |
| return doDiagnose(win, ranges); | |
| }; | |
| const asLtrRange = (win, selection) => { | |
| const diagnosis = diagnose(win, selection); | |
| return diagnosis.match({ | |
| ltr: (start, soffset, finish, foffset) => { | |
| const rng = win.document.createRange(); | |
| rng.setStart(start.dom, soffset); | |
| rng.setEnd(finish.dom, foffset); | |
| return rng; | |
| }, | |
| rtl: (start, soffset, finish, foffset) => { | |
| const rng = win.document.createRange(); | |
| rng.setStart(finish.dom, foffset); | |
| rng.setEnd(start.dom, soffset); | |
| return rng; | |
| } | |
| }); | |
| }; | |
| adt$3.ltr; | |
| adt$3.rtl; | |
| const create$3 = (start, soffset, finish, foffset) => ({ | |
| start, | |
| soffset, | |
| finish, | |
| foffset | |
| }); | |
| const SimRange = { create: create$3 }; | |
| const create$2 = (start, soffset, finish, foffset) => { | |
| return { | |
| start: Situ.on(start, soffset), | |
| finish: Situ.on(finish, foffset) | |
| }; | |
| }; | |
| const Situs = { create: create$2 }; | |
| const convertToRange = (win, selection) => { | |
| const rng = asLtrRange(win, selection); | |
| return SimRange.create(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset); | |
| }; | |
| const makeSitus = Situs.create; | |
| const sync = (container, isRoot, start, soffset, finish, foffset, selectRange) => { | |
| if (!(eq$1(start, finish) && soffset === foffset)) { | |
| return closest$1(start, 'td,th', isRoot).bind(s => { | |
| return closest$1(finish, 'td,th', isRoot).bind(f => { | |
| return detect(container, isRoot, s, f, selectRange); | |
| }); | |
| }); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }; | |
| const detect = (container, isRoot, start, finish, selectRange) => { | |
| if (!eq$1(start, finish)) { | |
| return identify(start, finish, isRoot).bind(cellSel => { | |
| const boxes = cellSel.boxes.getOr([]); | |
| if (boxes.length > 1) { | |
| selectRange(container, boxes, cellSel.start, cellSel.finish); | |
| return Optional.some(Response.create(Optional.some(makeSitus(start, 0, start, getEnd(start))), true)); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }; | |
| const update = (rows, columns, container, selected, annotations) => { | |
| const updateSelection = newSels => { | |
| annotations.clearBeforeUpdate(container); | |
| annotations.selectRange(container, newSels.boxes, newSels.start, newSels.finish); | |
| return newSels.boxes; | |
| }; | |
| return shiftSelection(selected, rows, columns, annotations.firstSelectedSelector, annotations.lastSelectedSelector).map(updateSelection); | |
| }; | |
| const traverse = (item, mode) => ({ | |
| item, | |
| mode | |
| }); | |
| const backtrack = (universe, item, _direction, transition = sidestep) => { | |
| return universe.property().parent(item).map(p => { | |
| return traverse(p, transition); | |
| }); | |
| }; | |
| const sidestep = (universe, item, direction, transition = advance) => { | |
| return direction.sibling(universe, item).map(p => { | |
| return traverse(p, transition); | |
| }); | |
| }; | |
| const advance = (universe, item, direction, transition = advance) => { | |
| const children = universe.property().children(item); | |
| const result = direction.first(children); | |
| return result.map(r => { | |
| return traverse(r, transition); | |
| }); | |
| }; | |
| const successors = [ | |
| { | |
| current: backtrack, | |
| next: sidestep, | |
| fallback: Optional.none() | |
| }, | |
| { | |
| current: sidestep, | |
| next: advance, | |
| fallback: Optional.some(backtrack) | |
| }, | |
| { | |
| current: advance, | |
| next: advance, | |
| fallback: Optional.some(sidestep) | |
| } | |
| ]; | |
| const go = (universe, item, mode, direction, rules = successors) => { | |
| const ruleOpt = find$1(rules, succ => { | |
| return succ.current === mode; | |
| }); | |
| return ruleOpt.bind(rule => { | |
| return rule.current(universe, item, direction, rule.next).orThunk(() => { | |
| return rule.fallback.bind(fb => { | |
| return go(universe, item, fb, direction); | |
| }); | |
| }); | |
| }); | |
| }; | |
| const left$1 = () => { | |
| const sibling = (universe, item) => { | |
| return universe.query().prevSibling(item); | |
| }; | |
| const first = children => { | |
| return children.length > 0 ? Optional.some(children[children.length - 1]) : Optional.none(); | |
| }; | |
| return { | |
| sibling, | |
| first | |
| }; | |
| }; | |
| const right$1 = () => { | |
| const sibling = (universe, item) => { | |
| return universe.query().nextSibling(item); | |
| }; | |
| const first = children => { | |
| return children.length > 0 ? Optional.some(children[0]) : Optional.none(); | |
| }; | |
| return { | |
| sibling, | |
| first | |
| }; | |
| }; | |
| const Walkers = { | |
| left: left$1, | |
| right: right$1 | |
| }; | |
| const hone = (universe, item, predicate, mode, direction, isRoot) => { | |
| const next = go(universe, item, mode, direction); | |
| return next.bind(n => { | |
| if (isRoot(n.item)) { | |
| return Optional.none(); | |
| } else { | |
| return predicate(n.item) ? Optional.some(n.item) : hone(universe, n.item, predicate, n.mode, direction, isRoot); | |
| } | |
| }); | |
| }; | |
| const left = (universe, item, predicate, isRoot) => { | |
| return hone(universe, item, predicate, sidestep, Walkers.left(), isRoot); | |
| }; | |
| const right = (universe, item, predicate, isRoot) => { | |
| return hone(universe, item, predicate, sidestep, Walkers.right(), isRoot); | |
| }; | |
| const isLeaf = universe => element => universe.property().children(element).length === 0; | |
| const before$1 = (universe, item, isRoot) => { | |
| return seekLeft$1(universe, item, isLeaf(universe), isRoot); | |
| }; | |
| const after$2 = (universe, item, isRoot) => { | |
| return seekRight$1(universe, item, isLeaf(universe), isRoot); | |
| }; | |
| const seekLeft$1 = left; | |
| const seekRight$1 = right; | |
| const universe = DomUniverse(); | |
| const before = (element, isRoot) => { | |
| return before$1(universe, element, isRoot); | |
| }; | |
| const after$1 = (element, isRoot) => { | |
| return after$2(universe, element, isRoot); | |
| }; | |
| const seekLeft = (element, predicate, isRoot) => { | |
| return seekLeft$1(universe, element, predicate, isRoot); | |
| }; | |
| const seekRight = (element, predicate, isRoot) => { | |
| return seekRight$1(universe, element, predicate, isRoot); | |
| }; | |
| const ancestor = (scope, predicate, isRoot) => ancestor$2(scope, predicate, isRoot).isSome(); | |
| const adt$2 = Adt.generate([ | |
| { none: ['message'] }, | |
| { success: [] }, | |
| { failedUp: ['cell'] }, | |
| { failedDown: ['cell'] } | |
| ]); | |
| const isOverlapping = (bridge, before, after) => { | |
| const beforeBounds = bridge.getRect(before); | |
| const afterBounds = bridge.getRect(after); | |
| return afterBounds.right > beforeBounds.left && afterBounds.left < beforeBounds.right; | |
| }; | |
| const isRow = elem => { | |
| return closest$1(elem, 'tr'); | |
| }; | |
| const verify = (bridge, before, beforeOffset, after, afterOffset, failure, isRoot) => { | |
| return closest$1(after, 'td,th', isRoot).bind(afterCell => { | |
| return closest$1(before, 'td,th', isRoot).map(beforeCell => { | |
| if (!eq$1(afterCell, beforeCell)) { | |
| return sharedOne(isRow, [ | |
| afterCell, | |
| beforeCell | |
| ]).fold(() => { | |
| return isOverlapping(bridge, beforeCell, afterCell) ? adt$2.success() : failure(beforeCell); | |
| }, _sharedRow => { | |
| return failure(beforeCell); | |
| }); | |
| } else { | |
| return eq$1(after, afterCell) && getEnd(afterCell) === afterOffset ? failure(beforeCell) : adt$2.none('in same cell'); | |
| } | |
| }); | |
| }).getOr(adt$2.none('default')); | |
| }; | |
| const cata = (subject, onNone, onSuccess, onFailedUp, onFailedDown) => { | |
| return subject.fold(onNone, onSuccess, onFailedUp, onFailedDown); | |
| }; | |
| const BeforeAfter = { | |
| ...adt$2, | |
| verify, | |
| cata | |
| }; | |
| const inParent = (parent, children, element, index) => ({ | |
| parent, | |
| children, | |
| element, | |
| index | |
| }); | |
| const indexInParent = element => parent(element).bind(parent => { | |
| const children = children$2(parent); | |
| return indexOf(children, element).map(index => inParent(parent, children, element, index)); | |
| }); | |
| const indexOf = (elements, element) => findIndex(elements, curry(eq$1, element)); | |
| const isBr = isTag('br'); | |
| const gatherer = (cand, gather, isRoot) => { | |
| return gather(cand, isRoot).bind(target => { | |
| return isText(target) && get$6(target).trim().length === 0 ? gatherer(target, gather, isRoot) : Optional.some(target); | |
| }); | |
| }; | |
| const handleBr = (isRoot, element, direction) => { | |
| return direction.traverse(element).orThunk(() => { | |
| return gatherer(element, direction.gather, isRoot); | |
| }).map(direction.relative); | |
| }; | |
| const findBr = (element, offset) => { | |
| return child$2(element, offset).filter(isBr).orThunk(() => { | |
| return child$2(element, offset - 1).filter(isBr); | |
| }); | |
| }; | |
| const handleParent = (isRoot, element, offset, direction) => { | |
| return findBr(element, offset).bind(br => { | |
| return direction.traverse(br).fold(() => { | |
| return gatherer(br, direction.gather, isRoot).map(direction.relative); | |
| }, adjacent => { | |
| return indexInParent(adjacent).map(info => { | |
| return Situ.on(info.parent, info.index); | |
| }); | |
| }); | |
| }); | |
| }; | |
| const tryBr = (isRoot, element, offset, direction) => { | |
| const target = isBr(element) ? handleBr(isRoot, element, direction) : handleParent(isRoot, element, offset, direction); | |
| return target.map(tgt => { | |
| return { | |
| start: tgt, | |
| finish: tgt | |
| }; | |
| }); | |
| }; | |
| const process = analysis => { | |
| return BeforeAfter.cata(analysis, _message => { | |
| return Optional.none(); | |
| }, () => { | |
| return Optional.none(); | |
| }, cell => { | |
| return Optional.some(point(cell, 0)); | |
| }, cell => { | |
| return Optional.some(point(cell, getEnd(cell))); | |
| }); | |
| }; | |
| const moveDown = (caret, amount) => { | |
| return { | |
| left: caret.left, | |
| top: caret.top + amount, | |
| right: caret.right, | |
| bottom: caret.bottom + amount | |
| }; | |
| }; | |
| const moveUp = (caret, amount) => { | |
| return { | |
| left: caret.left, | |
| top: caret.top - amount, | |
| right: caret.right, | |
| bottom: caret.bottom - amount | |
| }; | |
| }; | |
| const translate = (caret, xDelta, yDelta) => { | |
| return { | |
| left: caret.left + xDelta, | |
| top: caret.top + yDelta, | |
| right: caret.right + xDelta, | |
| bottom: caret.bottom + yDelta | |
| }; | |
| }; | |
| const getTop = caret => { | |
| return caret.top; | |
| }; | |
| const getBottom = caret => { | |
| return caret.bottom; | |
| }; | |
| const getPartialBox = (bridge, element, offset) => { | |
| if (offset >= 0 && offset < getEnd(element)) { | |
| return bridge.getRangedRect(element, offset, element, offset + 1); | |
| } else if (offset > 0) { | |
| return bridge.getRangedRect(element, offset - 1, element, offset); | |
| } | |
| return Optional.none(); | |
| }; | |
| const toCaret = rect => ({ | |
| left: rect.left, | |
| top: rect.top, | |
| right: rect.right, | |
| bottom: rect.bottom | |
| }); | |
| const getElemBox = (bridge, element) => { | |
| return Optional.some(bridge.getRect(element)); | |
| }; | |
| const getBoxAt = (bridge, element, offset) => { | |
| if (isElement(element)) { | |
| return getElemBox(bridge, element).map(toCaret); | |
| } else if (isText(element)) { | |
| return getPartialBox(bridge, element, offset).map(toCaret); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }; | |
| const getEntireBox = (bridge, element) => { | |
| if (isElement(element)) { | |
| return getElemBox(bridge, element).map(toCaret); | |
| } else if (isText(element)) { | |
| return bridge.getRangedRect(element, 0, element, getEnd(element)).map(toCaret); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }; | |
| const JUMP_SIZE = 5; | |
| const NUM_RETRIES = 100; | |
| const adt$1 = Adt.generate([ | |
| { none: [] }, | |
| { retry: ['caret'] } | |
| ]); | |
| const isOutside = (caret, box) => { | |
| return caret.left < box.left || Math.abs(box.right - caret.left) < 1 || caret.left > box.right; | |
| }; | |
| const inOutsideBlock = (bridge, element, caret) => { | |
| return closest$2(element, isBlock).fold(never, cell => { | |
| return getEntireBox(bridge, cell).exists(box => { | |
| return isOutside(caret, box); | |
| }); | |
| }); | |
| }; | |
| const adjustDown = (bridge, element, guessBox, original, caret) => { | |
| const lowerCaret = moveDown(caret, JUMP_SIZE); | |
| if (Math.abs(guessBox.bottom - original.bottom) < 1) { | |
| return adt$1.retry(lowerCaret); | |
| } else if (guessBox.top > caret.bottom) { | |
| return adt$1.retry(lowerCaret); | |
| } else if (guessBox.top === caret.bottom) { | |
| return adt$1.retry(moveDown(caret, 1)); | |
| } else { | |
| return inOutsideBlock(bridge, element, caret) ? adt$1.retry(translate(lowerCaret, JUMP_SIZE, 0)) : adt$1.none(); | |
| } | |
| }; | |
| const adjustUp = (bridge, element, guessBox, original, caret) => { | |
| const higherCaret = moveUp(caret, JUMP_SIZE); | |
| if (Math.abs(guessBox.top - original.top) < 1) { | |
| return adt$1.retry(higherCaret); | |
| } else if (guessBox.bottom < caret.top) { | |
| return adt$1.retry(higherCaret); | |
| } else if (guessBox.bottom === caret.top) { | |
| return adt$1.retry(moveUp(caret, 1)); | |
| } else { | |
| return inOutsideBlock(bridge, element, caret) ? adt$1.retry(translate(higherCaret, JUMP_SIZE, 0)) : adt$1.none(); | |
| } | |
| }; | |
| const upMovement = { | |
| point: getTop, | |
| adjuster: adjustUp, | |
| move: moveUp, | |
| gather: before | |
| }; | |
| const downMovement = { | |
| point: getBottom, | |
| adjuster: adjustDown, | |
| move: moveDown, | |
| gather: after$1 | |
| }; | |
| const isAtTable = (bridge, x, y) => { | |
| return bridge.elementFromPoint(x, y).filter(elm => { | |
| return name(elm) === 'table'; | |
| }).isSome(); | |
| }; | |
| const adjustForTable = (bridge, movement, original, caret, numRetries) => { | |
| return adjustTil(bridge, movement, original, movement.move(caret, JUMP_SIZE), numRetries); | |
| }; | |
| const adjustTil = (bridge, movement, original, caret, numRetries) => { | |
| if (numRetries === 0) { | |
| return Optional.some(caret); | |
| } | |
| if (isAtTable(bridge, caret.left, movement.point(caret))) { | |
| return adjustForTable(bridge, movement, original, caret, numRetries - 1); | |
| } | |
| return bridge.situsFromPoint(caret.left, movement.point(caret)).bind(guess => { | |
| return guess.start.fold(Optional.none, element => { | |
| return getEntireBox(bridge, element).bind(guessBox => { | |
| return movement.adjuster(bridge, element, guessBox, original, caret).fold(Optional.none, newCaret => { | |
| return adjustTil(bridge, movement, original, newCaret, numRetries - 1); | |
| }); | |
| }).orThunk(() => { | |
| return Optional.some(caret); | |
| }); | |
| }, Optional.none); | |
| }); | |
| }; | |
| const checkScroll = (movement, adjusted, bridge) => { | |
| if (movement.point(adjusted) > bridge.getInnerHeight()) { | |
| return Optional.some(movement.point(adjusted) - bridge.getInnerHeight()); | |
| } else if (movement.point(adjusted) < 0) { | |
| return Optional.some(-movement.point(adjusted)); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }; | |
| const retry = (movement, bridge, caret) => { | |
| const moved = movement.move(caret, JUMP_SIZE); | |
| const adjusted = adjustTil(bridge, movement, caret, moved, NUM_RETRIES).getOr(moved); | |
| return checkScroll(movement, adjusted, bridge).fold(() => { | |
| return bridge.situsFromPoint(adjusted.left, movement.point(adjusted)); | |
| }, delta => { | |
| bridge.scrollBy(0, delta); | |
| return bridge.situsFromPoint(adjusted.left, movement.point(adjusted) - delta); | |
| }); | |
| }; | |
| const Retries = { | |
| tryUp: curry(retry, upMovement), | |
| tryDown: curry(retry, downMovement), | |
| getJumpSize: constant(JUMP_SIZE) | |
| }; | |
| const MAX_RETRIES = 20; | |
| const findSpot = (bridge, isRoot, direction) => { | |
| return bridge.getSelection().bind(sel => { | |
| return tryBr(isRoot, sel.finish, sel.foffset, direction).fold(() => { | |
| return Optional.some(point(sel.finish, sel.foffset)); | |
| }, brNeighbour => { | |
| const range = bridge.fromSitus(brNeighbour); | |
| const analysis = BeforeAfter.verify(bridge, sel.finish, sel.foffset, range.finish, range.foffset, direction.failure, isRoot); | |
| return process(analysis); | |
| }); | |
| }); | |
| }; | |
| const scan = (bridge, isRoot, element, offset, direction, numRetries) => { | |
| if (numRetries === 0) { | |
| return Optional.none(); | |
| } | |
| return tryCursor(bridge, isRoot, element, offset, direction).bind(situs => { | |
| const range = bridge.fromSitus(situs); | |
| const analysis = BeforeAfter.verify(bridge, element, offset, range.finish, range.foffset, direction.failure, isRoot); | |
| return BeforeAfter.cata(analysis, () => { | |
| return Optional.none(); | |
| }, () => { | |
| return Optional.some(situs); | |
| }, cell => { | |
| if (eq$1(element, cell) && offset === 0) { | |
| return tryAgain(bridge, element, offset, moveUp, direction); | |
| } else { | |
| return scan(bridge, isRoot, cell, 0, direction, numRetries - 1); | |
| } | |
| }, cell => { | |
| if (eq$1(element, cell) && offset === getEnd(cell)) { | |
| return tryAgain(bridge, element, offset, moveDown, direction); | |
| } else { | |
| return scan(bridge, isRoot, cell, getEnd(cell), direction, numRetries - 1); | |
| } | |
| }); | |
| }); | |
| }; | |
| const tryAgain = (bridge, element, offset, move, direction) => { | |
| return getBoxAt(bridge, element, offset).bind(box => { | |
| return tryAt(bridge, direction, move(box, Retries.getJumpSize())); | |
| }); | |
| }; | |
| const tryAt = (bridge, direction, box) => { | |
| const browser = detect$2().browser; | |
| if (browser.isChromium() || browser.isSafari() || browser.isFirefox()) { | |
| return direction.retry(bridge, box); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }; | |
| const tryCursor = (bridge, isRoot, element, offset, direction) => { | |
| return getBoxAt(bridge, element, offset).bind(box => { | |
| return tryAt(bridge, direction, box); | |
| }); | |
| }; | |
| const handle$1 = (bridge, isRoot, direction) => { | |
| return findSpot(bridge, isRoot, direction).bind(spot => { | |
| return scan(bridge, isRoot, spot.element, spot.offset, direction, MAX_RETRIES).map(bridge.fromSitus); | |
| }); | |
| }; | |
| const inSameTable = (elem, table) => { | |
| return ancestor(elem, e => { | |
| return parent(e).exists(p => { | |
| return eq$1(p, table); | |
| }); | |
| }); | |
| }; | |
| const simulate = (bridge, isRoot, direction, initial, anchor) => { | |
| return closest$1(initial, 'td,th', isRoot).bind(start => { | |
| return closest$1(start, 'table', isRoot).bind(table => { | |
| if (!inSameTable(anchor, table)) { | |
| return Optional.none(); | |
| } | |
| return handle$1(bridge, isRoot, direction).bind(range => { | |
| return closest$1(range.finish, 'td,th', isRoot).map(finish => { | |
| return { | |
| start, | |
| finish, | |
| range | |
| }; | |
| }); | |
| }); | |
| }); | |
| }); | |
| }; | |
| const navigate = (bridge, isRoot, direction, initial, anchor, precheck) => { | |
| return precheck(initial, isRoot).orThunk(() => { | |
| return simulate(bridge, isRoot, direction, initial, anchor).map(info => { | |
| const range = info.range; | |
| return Response.create(Optional.some(makeSitus(range.start, range.soffset, range.finish, range.foffset)), true); | |
| }); | |
| }); | |
| }; | |
| const firstUpCheck = (initial, isRoot) => { | |
| return closest$1(initial, 'tr', isRoot).bind(startRow => { | |
| return closest$1(startRow, 'table', isRoot).bind(table => { | |
| const rows = descendants(table, 'tr'); | |
| if (eq$1(startRow, rows[0])) { | |
| return seekLeft(table, element => { | |
| return last$1(element).isSome(); | |
| }, isRoot).map(last => { | |
| const lastOffset = getEnd(last); | |
| return Response.create(Optional.some(makeSitus(last, lastOffset, last, lastOffset)), true); | |
| }); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }); | |
| }); | |
| }; | |
| const lastDownCheck = (initial, isRoot) => { | |
| return closest$1(initial, 'tr', isRoot).bind(startRow => { | |
| return closest$1(startRow, 'table', isRoot).bind(table => { | |
| const rows = descendants(table, 'tr'); | |
| if (eq$1(startRow, rows[rows.length - 1])) { | |
| return seekRight(table, element => { | |
| return first(element).isSome(); | |
| }, isRoot).map(first => { | |
| return Response.create(Optional.some(makeSitus(first, 0, first, 0)), true); | |
| }); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }); | |
| }); | |
| }; | |
| const select = (bridge, container, isRoot, direction, initial, anchor, selectRange) => { | |
| return simulate(bridge, isRoot, direction, initial, anchor).bind(info => { | |
| return detect(container, isRoot, info.start, info.finish, selectRange); | |
| }); | |
| }; | |
| const Cell = initial => { | |
| let value = initial; | |
| const get = () => { | |
| return value; | |
| }; | |
| const set = v => { | |
| value = v; | |
| }; | |
| return { | |
| get, | |
| set | |
| }; | |
| }; | |
| const singleton = doRevoke => { | |
| const subject = Cell(Optional.none()); | |
| const revoke = () => subject.get().each(doRevoke); | |
| const clear = () => { | |
| revoke(); | |
| subject.set(Optional.none()); | |
| }; | |
| const isSet = () => subject.get().isSome(); | |
| const get = () => subject.get(); | |
| const set = s => { | |
| revoke(); | |
| subject.set(Optional.some(s)); | |
| }; | |
| return { | |
| clear, | |
| isSet, | |
| get, | |
| set | |
| }; | |
| }; | |
| const value = () => { | |
| const subject = singleton(noop); | |
| const on = f => subject.get().each(f); | |
| return { | |
| ...subject, | |
| on | |
| }; | |
| }; | |
| const findCell = (target, isRoot) => closest$1(target, 'td,th', isRoot); | |
| const isInEditableContext = cell => parentElement(cell).exists(isEditable$1); | |
| const MouseSelection = (bridge, container, isRoot, annotations) => { | |
| const cursor = value(); | |
| const clearstate = cursor.clear; | |
| const applySelection = event => { | |
| cursor.on(start => { | |
| annotations.clearBeforeUpdate(container); | |
| findCell(event.target, isRoot).each(finish => { | |
| identify(start, finish, isRoot).each(cellSel => { | |
| const boxes = cellSel.boxes.getOr([]); | |
| if (boxes.length === 1) { | |
| const singleCell = boxes[0]; | |
| const isNonEditableCell = getRaw(singleCell) === 'false'; | |
| const isCellClosestContentEditable = is(closest(event.target), singleCell, eq$1); | |
| if (isNonEditableCell && isCellClosestContentEditable) { | |
| annotations.selectRange(container, boxes, singleCell, singleCell); | |
| bridge.selectContents(singleCell); | |
| } | |
| } else if (boxes.length > 1) { | |
| annotations.selectRange(container, boxes, cellSel.start, cellSel.finish); | |
| bridge.selectContents(finish); | |
| } | |
| }); | |
| }); | |
| }); | |
| }; | |
| const mousedown = event => { | |
| annotations.clear(container); | |
| findCell(event.target, isRoot).filter(isInEditableContext).each(cursor.set); | |
| }; | |
| const mouseover = event => { | |
| applySelection(event); | |
| }; | |
| const mouseup = event => { | |
| applySelection(event); | |
| clearstate(); | |
| }; | |
| return { | |
| clearstate, | |
| mousedown, | |
| mouseover, | |
| mouseup | |
| }; | |
| }; | |
| const down = { | |
| traverse: nextSibling, | |
| gather: after$1, | |
| relative: Situ.before, | |
| retry: Retries.tryDown, | |
| failure: BeforeAfter.failedDown | |
| }; | |
| const up = { | |
| traverse: prevSibling, | |
| gather: before, | |
| relative: Situ.before, | |
| retry: Retries.tryUp, | |
| failure: BeforeAfter.failedUp | |
| }; | |
| const isKey = key => { | |
| return keycode => { | |
| return keycode === key; | |
| }; | |
| }; | |
| const isUp = isKey(38); | |
| const isDown = isKey(40); | |
| const isNavigation = keycode => { | |
| return keycode >= 37 && keycode <= 40; | |
| }; | |
| const ltr = { | |
| isBackward: isKey(37), | |
| isForward: isKey(39) | |
| }; | |
| const rtl = { | |
| isBackward: isKey(39), | |
| isForward: isKey(37) | |
| }; | |
| const get$3 = _DOC => { | |
| const doc = _DOC !== undefined ? _DOC.dom : document; | |
| const x = doc.body.scrollLeft || doc.documentElement.scrollLeft; | |
| const y = doc.body.scrollTop || doc.documentElement.scrollTop; | |
| return SugarPosition(x, y); | |
| }; | |
| const by = (x, y, _DOC) => { | |
| const doc = _DOC !== undefined ? _DOC.dom : document; | |
| const win = doc.defaultView; | |
| if (win) { | |
| win.scrollBy(x, y); | |
| } | |
| }; | |
| const adt = Adt.generate([ | |
| { domRange: ['rng'] }, | |
| { | |
| relative: [ | |
| 'startSitu', | |
| 'finishSitu' | |
| ] | |
| }, | |
| { | |
| exact: [ | |
| 'start', | |
| 'soffset', | |
| 'finish', | |
| 'foffset' | |
| ] | |
| } | |
| ]); | |
| const exactFromRange = simRange => adt.exact(simRange.start, simRange.soffset, simRange.finish, simRange.foffset); | |
| const getStart = selection => selection.match({ | |
| domRange: rng => SugarElement.fromDom(rng.startContainer), | |
| relative: (startSitu, _finishSitu) => Situ.getStart(startSitu), | |
| exact: (start, _soffset, _finish, _foffset) => start | |
| }); | |
| const domRange = adt.domRange; | |
| const relative = adt.relative; | |
| const exact = adt.exact; | |
| const getWin = selection => { | |
| const start = getStart(selection); | |
| return defaultView(start); | |
| }; | |
| const range = SimRange.create; | |
| const SimSelection = { | |
| domRange, | |
| relative, | |
| exact, | |
| exactFromRange, | |
| getWin, | |
| range | |
| }; | |
| const caretPositionFromPoint = (doc, x, y) => { | |
| var _a, _b; | |
| return Optional.from((_b = (_a = doc.dom).caretPositionFromPoint) === null || _b === void 0 ? void 0 : _b.call(_a, x, y)).bind(pos => { | |
| if (pos.offsetNode === null) { | |
| return Optional.none(); | |
| } | |
| const r = doc.dom.createRange(); | |
| r.setStart(pos.offsetNode, pos.offset); | |
| r.collapse(); | |
| return Optional.some(r); | |
| }); | |
| }; | |
| const caretRangeFromPoint = (doc, x, y) => { | |
| var _a, _b; | |
| return Optional.from((_b = (_a = doc.dom).caretRangeFromPoint) === null || _b === void 0 ? void 0 : _b.call(_a, x, y)); | |
| }; | |
| const availableSearch = (() => { | |
| if (document.caretPositionFromPoint) { | |
| return caretPositionFromPoint; | |
| } else if (document.caretRangeFromPoint) { | |
| return caretRangeFromPoint; | |
| } else { | |
| return Optional.none; | |
| } | |
| })(); | |
| const fromPoint = (win, x, y) => { | |
| const doc = SugarElement.fromDom(win.document); | |
| return availableSearch(doc, x, y).map(rng => SimRange.create(SugarElement.fromDom(rng.startContainer), rng.startOffset, SugarElement.fromDom(rng.endContainer), rng.endOffset)); | |
| }; | |
| const beforeSpecial = (element, offset) => { | |
| const name$1 = name(element); | |
| if ('input' === name$1) { | |
| return Situ.after(element); | |
| } else if (!contains$2([ | |
| 'br', | |
| 'img' | |
| ], name$1)) { | |
| return Situ.on(element, offset); | |
| } else { | |
| return offset === 0 ? Situ.before(element) : Situ.after(element); | |
| } | |
| }; | |
| const preprocessRelative = (startSitu, finishSitu) => { | |
| const start = startSitu.fold(Situ.before, beforeSpecial, Situ.after); | |
| const finish = finishSitu.fold(Situ.before, beforeSpecial, Situ.after); | |
| return SimSelection.relative(start, finish); | |
| }; | |
| const preprocessExact = (start, soffset, finish, foffset) => { | |
| const startSitu = beforeSpecial(start, soffset); | |
| const finishSitu = beforeSpecial(finish, foffset); | |
| return SimSelection.relative(startSitu, finishSitu); | |
| }; | |
| const makeRange = (start, soffset, finish, foffset) => { | |
| const doc = owner(start); | |
| const rng = doc.dom.createRange(); | |
| rng.setStart(start.dom, soffset); | |
| rng.setEnd(finish.dom, foffset); | |
| return rng; | |
| }; | |
| const after = (start, soffset, finish, foffset) => { | |
| const r = makeRange(start, soffset, finish, foffset); | |
| const same = eq$1(start, finish) && soffset === foffset; | |
| return r.collapsed && !same; | |
| }; | |
| const getNativeSelection = win => Optional.from(win.getSelection()); | |
| const doSetNativeRange = (win, rng) => { | |
| getNativeSelection(win).each(selection => { | |
| selection.removeAllRanges(); | |
| selection.addRange(rng); | |
| }); | |
| }; | |
| const doSetRange = (win, start, soffset, finish, foffset) => { | |
| const rng = exactToNative(win, start, soffset, finish, foffset); | |
| doSetNativeRange(win, rng); | |
| }; | |
| const setLegacyRtlRange = (win, selection, start, soffset, finish, foffset) => { | |
| selection.collapse(start.dom, soffset); | |
| selection.extend(finish.dom, foffset); | |
| }; | |
| const setRangeFromRelative = (win, relative) => diagnose(win, relative).match({ | |
| ltr: (start, soffset, finish, foffset) => { | |
| doSetRange(win, start, soffset, finish, foffset); | |
| }, | |
| rtl: (start, soffset, finish, foffset) => { | |
| getNativeSelection(win).each(selection => { | |
| if (selection.setBaseAndExtent) { | |
| selection.setBaseAndExtent(start.dom, soffset, finish.dom, foffset); | |
| } else if (selection.extend) { | |
| try { | |
| setLegacyRtlRange(win, selection, start, soffset, finish, foffset); | |
| } catch (e) { | |
| doSetRange(win, finish, foffset, start, soffset); | |
| } | |
| } else { | |
| doSetRange(win, finish, foffset, start, soffset); | |
| } | |
| }); | |
| } | |
| }); | |
| const setExact = (win, start, soffset, finish, foffset) => { | |
| const relative = preprocessExact(start, soffset, finish, foffset); | |
| setRangeFromRelative(win, relative); | |
| }; | |
| const setRelative = (win, startSitu, finishSitu) => { | |
| const relative = preprocessRelative(startSitu, finishSitu); | |
| setRangeFromRelative(win, relative); | |
| }; | |
| const readRange = selection => { | |
| if (selection.rangeCount > 0) { | |
| const firstRng = selection.getRangeAt(0); | |
| const lastRng = selection.getRangeAt(selection.rangeCount - 1); | |
| return Optional.some(SimRange.create(SugarElement.fromDom(firstRng.startContainer), firstRng.startOffset, SugarElement.fromDom(lastRng.endContainer), lastRng.endOffset)); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }; | |
| const doGetExact = selection => { | |
| if (selection.anchorNode === null || selection.focusNode === null) { | |
| return readRange(selection); | |
| } else { | |
| const anchor = SugarElement.fromDom(selection.anchorNode); | |
| const focus = SugarElement.fromDom(selection.focusNode); | |
| return after(anchor, selection.anchorOffset, focus, selection.focusOffset) ? Optional.some(SimRange.create(anchor, selection.anchorOffset, focus, selection.focusOffset)) : readRange(selection); | |
| } | |
| }; | |
| const setToElement = (win, element, selectNodeContents$1 = true) => { | |
| const rngGetter = selectNodeContents$1 ? selectNodeContents : selectNode; | |
| const rng = rngGetter(win, element); | |
| doSetNativeRange(win, rng); | |
| }; | |
| const getExact = win => getNativeSelection(win).filter(sel => sel.rangeCount > 0).bind(doGetExact); | |
| const get$2 = win => getExact(win).map(range => SimSelection.exact(range.start, range.soffset, range.finish, range.foffset)); | |
| const getFirstRect = (win, selection) => { | |
| const rng = asLtrRange(win, selection); | |
| return getFirstRect$1(rng); | |
| }; | |
| const getAtPoint = (win, x, y) => fromPoint(win, x, y); | |
| const clear = win => { | |
| getNativeSelection(win).each(selection => selection.removeAllRanges()); | |
| }; | |
| const WindowBridge = win => { | |
| const elementFromPoint = (x, y) => { | |
| return SugarElement.fromPoint(SugarElement.fromDom(win.document), x, y); | |
| }; | |
| const getRect = element => { | |
| return element.dom.getBoundingClientRect(); | |
| }; | |
| const getRangedRect = (start, soffset, finish, foffset) => { | |
| const sel = SimSelection.exact(start, soffset, finish, foffset); | |
| return getFirstRect(win, sel); | |
| }; | |
| const getSelection = () => { | |
| return get$2(win).map(exactAdt => { | |
| return convertToRange(win, exactAdt); | |
| }); | |
| }; | |
| const fromSitus = situs => { | |
| const relative = SimSelection.relative(situs.start, situs.finish); | |
| return convertToRange(win, relative); | |
| }; | |
| const situsFromPoint = (x, y) => { | |
| return getAtPoint(win, x, y).map(exact => { | |
| return Situs.create(exact.start, exact.soffset, exact.finish, exact.foffset); | |
| }); | |
| }; | |
| const clearSelection = () => { | |
| clear(win); | |
| }; | |
| const collapseSelection = (toStart = false) => { | |
| get$2(win).each(sel => sel.fold(rng => rng.collapse(toStart), (startSitu, finishSitu) => { | |
| const situ = toStart ? startSitu : finishSitu; | |
| setRelative(win, situ, situ); | |
| }, (start, soffset, finish, foffset) => { | |
| const node = toStart ? start : finish; | |
| const offset = toStart ? soffset : foffset; | |
| setExact(win, node, offset, node, offset); | |
| })); | |
| }; | |
| const selectNode = element => { | |
| setToElement(win, element, false); | |
| }; | |
| const selectContents = element => { | |
| setToElement(win, element); | |
| }; | |
| const setSelection = sel => { | |
| setExact(win, sel.start, sel.soffset, sel.finish, sel.foffset); | |
| }; | |
| const setRelativeSelection = (start, finish) => { | |
| setRelative(win, start, finish); | |
| }; | |
| const getInnerHeight = () => { | |
| return win.innerHeight; | |
| }; | |
| const getScrollY = () => { | |
| const pos = get$3(SugarElement.fromDom(win.document)); | |
| return pos.top; | |
| }; | |
| const scrollBy = (x, y) => { | |
| by(x, y, SugarElement.fromDom(win.document)); | |
| }; | |
| return { | |
| elementFromPoint, | |
| getRect, | |
| getRangedRect, | |
| getSelection, | |
| fromSitus, | |
| situsFromPoint, | |
| clearSelection, | |
| collapseSelection, | |
| setSelection, | |
| setRelativeSelection, | |
| selectNode, | |
| selectContents, | |
| getInnerHeight, | |
| getScrollY, | |
| scrollBy | |
| }; | |
| }; | |
| const rc = (rows, cols) => ({ | |
| rows, | |
| cols | |
| }); | |
| const mouse = (win, container, isRoot, annotations) => { | |
| const bridge = WindowBridge(win); | |
| const handlers = MouseSelection(bridge, container, isRoot, annotations); | |
| return { | |
| clearstate: handlers.clearstate, | |
| mousedown: handlers.mousedown, | |
| mouseover: handlers.mouseover, | |
| mouseup: handlers.mouseup | |
| }; | |
| }; | |
| const isEditableNode = node => closest$2(node, isHTMLElement).exists(isEditable$1); | |
| const isEditableSelection = (start, finish) => isEditableNode(start) || isEditableNode(finish); | |
| const keyboard = (win, container, isRoot, annotations) => { | |
| const bridge = WindowBridge(win); | |
| const clearToNavigate = () => { | |
| annotations.clear(container); | |
| return Optional.none(); | |
| }; | |
| const keydown = (event, start, soffset, finish, foffset, direction) => { | |
| const realEvent = event.raw; | |
| const keycode = realEvent.which; | |
| const shiftKey = realEvent.shiftKey === true; | |
| const handler = retrieve$1(container, annotations.selectedSelector).fold(() => { | |
| if (isNavigation(keycode) && !shiftKey) { | |
| annotations.clearBeforeUpdate(container); | |
| } | |
| if (isNavigation(keycode) && shiftKey && !isEditableSelection(start, finish)) { | |
| return Optional.none; | |
| } else if (isDown(keycode) && shiftKey) { | |
| return curry(select, bridge, container, isRoot, down, finish, start, annotations.selectRange); | |
| } else if (isUp(keycode) && shiftKey) { | |
| return curry(select, bridge, container, isRoot, up, finish, start, annotations.selectRange); | |
| } else if (isDown(keycode)) { | |
| return curry(navigate, bridge, isRoot, down, finish, start, lastDownCheck); | |
| } else if (isUp(keycode)) { | |
| return curry(navigate, bridge, isRoot, up, finish, start, firstUpCheck); | |
| } else { | |
| return Optional.none; | |
| } | |
| }, selected => { | |
| const update$1 = attempts => { | |
| return () => { | |
| const navigation = findMap(attempts, delta => { | |
| return update(delta.rows, delta.cols, container, selected, annotations); | |
| }); | |
| return navigation.fold(() => { | |
| return getEdges(container, annotations.firstSelectedSelector, annotations.lastSelectedSelector).map(edges => { | |
| const relative = isDown(keycode) || direction.isForward(keycode) ? Situ.after : Situ.before; | |
| bridge.setRelativeSelection(Situ.on(edges.first, 0), relative(edges.table)); | |
| annotations.clear(container); | |
| return Response.create(Optional.none(), true); | |
| }); | |
| }, _ => { | |
| return Optional.some(Response.create(Optional.none(), true)); | |
| }); | |
| }; | |
| }; | |
| if (isNavigation(keycode) && shiftKey && !isEditableSelection(start, finish)) { | |
| return Optional.none; | |
| } else if (isDown(keycode) && shiftKey) { | |
| return update$1([rc(+1, 0)]); | |
| } else if (isUp(keycode) && shiftKey) { | |
| return update$1([rc(-1, 0)]); | |
| } else if (direction.isBackward(keycode) && shiftKey) { | |
| return update$1([ | |
| rc(0, -1), | |
| rc(-1, 0) | |
| ]); | |
| } else if (direction.isForward(keycode) && shiftKey) { | |
| return update$1([ | |
| rc(0, +1), | |
| rc(+1, 0) | |
| ]); | |
| } else if (isNavigation(keycode) && !shiftKey) { | |
| return clearToNavigate; | |
| } else { | |
| return Optional.none; | |
| } | |
| }); | |
| return handler(); | |
| }; | |
| const keyup = (event, start, soffset, finish, foffset) => { | |
| return retrieve$1(container, annotations.selectedSelector).fold(() => { | |
| const realEvent = event.raw; | |
| const keycode = realEvent.which; | |
| const shiftKey = realEvent.shiftKey === true; | |
| if (!shiftKey) { | |
| return Optional.none(); | |
| } | |
| if (isNavigation(keycode) && isEditableSelection(start, finish)) { | |
| return sync(container, isRoot, start, soffset, finish, foffset, annotations.selectRange); | |
| } else { | |
| return Optional.none(); | |
| } | |
| }, Optional.none); | |
| }; | |
| return { | |
| keydown, | |
| keyup | |
| }; | |
| }; | |
| const external = (win, container, isRoot, annotations) => { | |
| const bridge = WindowBridge(win); | |
| return (start, finish) => { | |
| annotations.clearBeforeUpdate(container); | |
| identify(start, finish, isRoot).each(cellSel => { | |
| const boxes = cellSel.boxes.getOr([]); | |
| annotations.selectRange(container, boxes, cellSel.start, cellSel.finish); | |
| bridge.selectContents(finish); | |
| bridge.collapseSelection(); | |
| }); | |
| }; | |
| }; | |
| const read = (element, attr) => { | |
| const value = get$b(element, attr); | |
| return value === undefined || value === '' ? [] : value.split(' '); | |
| }; | |
| const add$2 = (element, attr, id) => { | |
| const old = read(element, attr); | |
| const nu = old.concat([id]); | |
| set$2(element, attr, nu.join(' ')); | |
| return true; | |
| }; | |
| const remove$4 = (element, attr, id) => { | |
| const nu = filter$2(read(element, attr), v => v !== id); | |
| if (nu.length > 0) { | |
| set$2(element, attr, nu.join(' ')); | |
| } else { | |
| remove$7(element, attr); | |
| } | |
| return false; | |
| }; | |
| const supports = element => element.dom.classList !== undefined; | |
| const get$1 = element => read(element, 'class'); | |
| const add$1 = (element, clazz) => add$2(element, 'class', clazz); | |
| const remove$3 = (element, clazz) => remove$4(element, 'class', clazz); | |
| const add = (element, clazz) => { | |
| if (supports(element)) { | |
| element.dom.classList.add(clazz); | |
| } else { | |
| add$1(element, clazz); | |
| } | |
| }; | |
| const cleanClass = element => { | |
| const classList = supports(element) ? element.dom.classList : get$1(element); | |
| if (classList.length === 0) { | |
| remove$7(element, 'class'); | |
| } | |
| }; | |
| const remove$2 = (element, clazz) => { | |
| if (supports(element)) { | |
| const classList = element.dom.classList; | |
| classList.remove(clazz); | |
| } else { | |
| remove$3(element, clazz); | |
| } | |
| cleanClass(element); | |
| }; | |
| const has = (element, clazz) => supports(element) && element.dom.classList.contains(clazz); | |
| const remove$1 = (element, classes) => { | |
| each$2(classes, x => { | |
| remove$2(element, x); | |
| }); | |
| }; | |
| const addClass = clazz => element => { | |
| add(element, clazz); | |
| }; | |
| const removeClasses = classes => element => { | |
| remove$1(element, classes); | |
| }; | |
| const byClass = ephemera => { | |
| const addSelectionClass = addClass(ephemera.selected); | |
| const removeSelectionClasses = removeClasses([ | |
| ephemera.selected, | |
| ephemera.lastSelected, | |
| ephemera.firstSelected | |
| ]); | |
| const clear = container => { | |
| const sels = descendants(container, ephemera.selectedSelector); | |
| each$2(sels, removeSelectionClasses); | |
| }; | |
| const selectRange = (container, cells, start, finish) => { | |
| clear(container); | |
| each$2(cells, addSelectionClass); | |
| add(start, ephemera.firstSelected); | |
| add(finish, ephemera.lastSelected); | |
| }; | |
| return { | |
| clearBeforeUpdate: clear, | |
| clear, | |
| selectRange, | |
| selectedSelector: ephemera.selectedSelector, | |
| firstSelectedSelector: ephemera.firstSelectedSelector, | |
| lastSelectedSelector: ephemera.lastSelectedSelector | |
| }; | |
| }; | |
| const byAttr = (ephemera, onSelection, onClear) => { | |
| const removeSelectionAttributes = element => { | |
| remove$7(element, ephemera.selected); | |
| remove$7(element, ephemera.firstSelected); | |
| remove$7(element, ephemera.lastSelected); | |
| }; | |
| const addSelectionAttribute = element => { | |
| set$2(element, ephemera.selected, '1'); | |
| }; | |
| const clear = container => { | |
| clearBeforeUpdate(container); | |
| onClear(); | |
| }; | |
| const clearBeforeUpdate = container => { | |
| const sels = descendants(container, `${ ephemera.selectedSelector },${ ephemera.firstSelectedSelector },${ ephemera.lastSelectedSelector }`); | |
| each$2(sels, removeSelectionAttributes); | |
| }; | |
| const selectRange = (container, cells, start, finish) => { | |
| clear(container); | |
| each$2(cells, addSelectionAttribute); | |
| set$2(start, ephemera.firstSelected, '1'); | |
| set$2(finish, ephemera.lastSelected, '1'); | |
| onSelection(cells, start, finish); | |
| }; | |
| return { | |
| clearBeforeUpdate, | |
| clear, | |
| selectRange, | |
| selectedSelector: ephemera.selectedSelector, | |
| firstSelectedSelector: ephemera.firstSelectedSelector, | |
| lastSelectedSelector: ephemera.lastSelectedSelector | |
| }; | |
| }; | |
| const SelectionAnnotation = { | |
| byClass, | |
| byAttr | |
| }; | |
| const fold = (subject, onNone, onMultiple, onSingle) => { | |
| switch (subject.tag) { | |
| case 'none': | |
| return onNone(); | |
| case 'single': | |
| return onSingle(subject.element); | |
| case 'multiple': | |
| return onMultiple(subject.elements); | |
| } | |
| }; | |
| const none = () => ({ tag: 'none' }); | |
| const multiple = elements => ({ | |
| tag: 'multiple', | |
| elements | |
| }); | |
| const single = element => ({ | |
| tag: 'single', | |
| element | |
| }); | |
| const Selections = (lazyRoot, getStart, selectedSelector) => { | |
| const get = () => retrieve(lazyRoot(), selectedSelector).fold(() => getStart().fold(none, single), multiple); | |
| return { get }; | |
| }; | |
| const getUpOrLeftCells = (grid, selectedCells) => { | |
| const upGrid = grid.slice(0, selectedCells[selectedCells.length - 1].row + 1); | |
| const upDetails = toDetailList(upGrid); | |
| return bind$2(upDetails, detail => { | |
| const slicedCells = detail.cells.slice(0, selectedCells[selectedCells.length - 1].column + 1); | |
| return map$1(slicedCells, cell => cell.element); | |
| }); | |
| }; | |
| const getDownOrRightCells = (grid, selectedCells) => { | |
| const downGrid = grid.slice(selectedCells[0].row + selectedCells[0].rowspan - 1, grid.length); | |
| const downDetails = toDetailList(downGrid); | |
| return bind$2(downDetails, detail => { | |
| const slicedCells = detail.cells.slice(selectedCells[0].column + selectedCells[0].colspan - 1, detail.cells.length); | |
| return map$1(slicedCells, cell => cell.element); | |
| }); | |
| }; | |
| const getOtherCells = (table, target, generators) => { | |
| const warehouse = Warehouse.fromTable(table); | |
| const details = onCells(warehouse, target); | |
| return details.map(selectedCells => { | |
| const grid = toGrid(warehouse, generators, false); | |
| const {rows} = extractGridDetails(grid); | |
| const upOrLeftCells = getUpOrLeftCells(rows, selectedCells); | |
| const downOrRightCells = getDownOrRightCells(rows, selectedCells); | |
| return { | |
| upOrLeftCells, | |
| downOrRightCells | |
| }; | |
| }); | |
| }; | |
| const mkEvent = (target, x, y, stop, prevent, kill, raw) => ({ | |
| target, | |
| x, | |
| y, | |
| stop, | |
| prevent, | |
| kill, | |
| raw | |
| }); | |
| const fromRawEvent$1 = rawEvent => { | |
| const target = SugarElement.fromDom(getOriginalEventTarget(rawEvent).getOr(rawEvent.target)); | |
| const stop = () => rawEvent.stopPropagation(); | |
| const prevent = () => rawEvent.preventDefault(); | |
| const kill = compose(prevent, stop); | |
| return mkEvent(target, rawEvent.clientX, rawEvent.clientY, stop, prevent, kill, rawEvent); | |
| }; | |
| const handle = (filter, handler) => rawEvent => { | |
| if (filter(rawEvent)) { | |
| handler(fromRawEvent$1(rawEvent)); | |
| } | |
| }; | |
| const binder = (element, event, filter, handler, useCapture) => { | |
| const wrapped = handle(filter, handler); | |
| element.dom.addEventListener(event, wrapped, useCapture); | |
| return { unbind: curry(unbind, element, event, wrapped, useCapture) }; | |
| }; | |
| const bind$1 = (element, event, filter, handler) => binder(element, event, filter, handler, false); | |
| const unbind = (element, event, handler, useCapture) => { | |
| element.dom.removeEventListener(event, handler, useCapture); | |
| }; | |
| const filter = always; | |
| const bind = (element, event, handler) => bind$1(element, event, filter, handler); | |
| const fromRawEvent = fromRawEvent$1; | |
| const hasInternalTarget = e => !has(SugarElement.fromDom(e.target), 'ephox-snooker-resizer-bar'); | |
| const TableCellSelectionHandler = (editor, resizeHandler) => { | |
| const cellSelection = Selections(() => SugarElement.fromDom(editor.getBody()), () => getSelectionCell(getSelectionStart(editor), getIsRoot(editor)), ephemera.selectedSelector); | |
| const onSelection = (cells, start, finish) => { | |
| const tableOpt = table(start); | |
| tableOpt.each(table => { | |
| const cloneFormats = getTableCloneElements(editor); | |
| const generators = cellOperations(noop, SugarElement.fromDom(editor.getDoc()), cloneFormats); | |
| const selectedCells = getCellsFromSelection(editor); | |
| const otherCells = getOtherCells(table, { selection: selectedCells }, generators); | |
| fireTableSelectionChange(editor, cells, start, finish, otherCells); | |
| }); | |
| }; | |
| const onClear = () => fireTableSelectionClear(editor); | |
| const annotations = SelectionAnnotation.byAttr(ephemera, onSelection, onClear); | |
| editor.on('init', _e => { | |
| const win = editor.getWin(); | |
| const body = getBody(editor); | |
| const isRoot = getIsRoot(editor); | |
| const syncSelection = () => { | |
| const sel = editor.selection; | |
| const start = SugarElement.fromDom(sel.getStart()); | |
| const end = SugarElement.fromDom(sel.getEnd()); | |
| const shared = sharedOne(table, [ | |
| start, | |
| end | |
| ]); | |
| shared.fold(() => annotations.clear(body), noop); | |
| }; | |
| const mouseHandlers = mouse(win, body, isRoot, annotations); | |
| const keyHandlers = keyboard(win, body, isRoot, annotations); | |
| const external$1 = external(win, body, isRoot, annotations); | |
| const hasShiftKey = event => event.raw.shiftKey === true; | |
| editor.on('TableSelectorChange', e => external$1(e.start, e.finish)); | |
| const handleResponse = (event, response) => { | |
| if (!hasShiftKey(event)) { | |
| return; | |
| } | |
| if (response.kill) { | |
| event.kill(); | |
| } | |
| response.selection.each(ns => { | |
| const relative = SimSelection.relative(ns.start, ns.finish); | |
| const rng = asLtrRange(win, relative); | |
| editor.selection.setRng(rng); | |
| }); | |
| }; | |
| const keyup = event => { | |
| const wrappedEvent = fromRawEvent(event); | |
| if (wrappedEvent.raw.shiftKey && isNavigation(wrappedEvent.raw.which)) { | |
| const rng = editor.selection.getRng(); | |
| const start = SugarElement.fromDom(rng.startContainer); | |
| const end = SugarElement.fromDom(rng.endContainer); | |
| keyHandlers.keyup(wrappedEvent, start, rng.startOffset, end, rng.endOffset).each(response => { | |
| handleResponse(wrappedEvent, response); | |
| }); | |
| } | |
| }; | |
| const keydown = event => { | |
| const wrappedEvent = fromRawEvent(event); | |
| resizeHandler.hide(); | |
| const rng = editor.selection.getRng(); | |
| const start = SugarElement.fromDom(rng.startContainer); | |
| const end = SugarElement.fromDom(rng.endContainer); | |
| const direction = onDirection(ltr, rtl)(SugarElement.fromDom(editor.selection.getStart())); | |
| keyHandlers.keydown(wrappedEvent, start, rng.startOffset, end, rng.endOffset, direction).each(response => { | |
| handleResponse(wrappedEvent, response); | |
| }); | |
| resizeHandler.show(); | |
| }; | |
| const isLeftMouse = raw => raw.button === 0; | |
| const isLeftButtonPressed = raw => { | |
| if (raw.buttons === undefined) { | |
| return true; | |
| } | |
| return (raw.buttons & 1) !== 0; | |
| }; | |
| const dragStart = _e => { | |
| mouseHandlers.clearstate(); | |
| }; | |
| const mouseDown = e => { | |
| if (isLeftMouse(e) && hasInternalTarget(e)) { | |
| mouseHandlers.mousedown(fromRawEvent(e)); | |
| } | |
| }; | |
| const mouseOver = e => { | |
| if (isLeftButtonPressed(e) && hasInternalTarget(e)) { | |
| mouseHandlers.mouseover(fromRawEvent(e)); | |
| } | |
| }; | |
| const mouseUp = e => { | |
| if (isLeftMouse(e) && hasInternalTarget(e)) { | |
| mouseHandlers.mouseup(fromRawEvent(e)); | |
| } | |
| }; | |
| const getDoubleTap = () => { | |
| const lastTarget = Cell(SugarElement.fromDom(body)); | |
| const lastTimeStamp = Cell(0); | |
| const touchEnd = t => { | |
| const target = SugarElement.fromDom(t.target); | |
| if (isTag('td')(target) || isTag('th')(target)) { | |
| const lT = lastTarget.get(); | |
| const lTS = lastTimeStamp.get(); | |
| if (eq$1(lT, target) && t.timeStamp - lTS < 300) { | |
| t.preventDefault(); | |
| external$1(target, target); | |
| } | |
| } | |
| lastTarget.set(target); | |
| lastTimeStamp.set(t.timeStamp); | |
| }; | |
| return { touchEnd }; | |
| }; | |
| const doubleTap = getDoubleTap(); | |
| editor.on('dragstart', dragStart); | |
| editor.on('mousedown', mouseDown); | |
| editor.on('mouseover', mouseOver); | |
| editor.on('mouseup', mouseUp); | |
| editor.on('touchend', doubleTap.touchEnd); | |
| editor.on('keyup', keyup); | |
| editor.on('keydown', keydown); | |
| editor.on('NodeChange', syncSelection); | |
| }); | |
| editor.on('PreInit', () => { | |
| editor.serializer.addTempAttr(ephemera.firstSelected); | |
| editor.serializer.addTempAttr(ephemera.lastSelected); | |
| }); | |
| const clearSelectedCells = container => annotations.clear(SugarElement.fromDom(container)); | |
| const getSelectedCells = () => fold(cellSelection.get(), constant([]), cells => { | |
| return map$1(cells, cell => cell.dom); | |
| }, cell => [cell.dom]); | |
| return { | |
| getSelectedCells, | |
| clearSelectedCells | |
| }; | |
| }; | |
| const Event = fields => { | |
| let handlers = []; | |
| const bind = handler => { | |
| if (handler === undefined) { | |
| throw new Error('Event bind error: undefined handler'); | |
| } | |
| handlers.push(handler); | |
| }; | |
| const unbind = handler => { | |
| handlers = filter$2(handlers, h => { | |
| return h !== handler; | |
| }); | |
| }; | |
| const trigger = (...args) => { | |
| const event = {}; | |
| each$2(fields, (name, i) => { | |
| event[name] = args[i]; | |
| }); | |
| each$2(handlers, handler => { | |
| handler(event); | |
| }); | |
| }; | |
| return { | |
| bind, | |
| unbind, | |
| trigger | |
| }; | |
| }; | |
| const create$1 = typeDefs => { | |
| const registry = map(typeDefs, event => { | |
| return { | |
| bind: event.bind, | |
| unbind: event.unbind | |
| }; | |
| }); | |
| const trigger = map(typeDefs, event => { | |
| return event.trigger; | |
| }); | |
| return { | |
| registry, | |
| trigger | |
| }; | |
| }; | |
| const last = (fn, rate) => { | |
| let timer = null; | |
| const cancel = () => { | |
| if (!isNull(timer)) { | |
| clearTimeout(timer); | |
| timer = null; | |
| } | |
| }; | |
| const throttle = (...args) => { | |
| cancel(); | |
| timer = setTimeout(() => { | |
| timer = null; | |
| fn.apply(null, args); | |
| }, rate); | |
| }; | |
| return { | |
| cancel, | |
| throttle | |
| }; | |
| }; | |
| const sort = arr => { | |
| return arr.slice(0).sort(); | |
| }; | |
| const reqMessage = (required, keys) => { | |
| throw new Error('All required keys (' + sort(required).join(', ') + ') were not specified. Specified keys were: ' + sort(keys).join(', ') + '.'); | |
| }; | |
| const unsuppMessage = unsupported => { | |
| throw new Error('Unsupported keys for object: ' + sort(unsupported).join(', ')); | |
| }; | |
| const validateStrArr = (label, array) => { | |
| if (!isArray(array)) { | |
| throw new Error('The ' + label + ' fields must be an array. Was: ' + array + '.'); | |
| } | |
| each$2(array, a => { | |
| if (!isString(a)) { | |
| throw new Error('The value ' + a + ' in the ' + label + ' fields was not a string.'); | |
| } | |
| }); | |
| }; | |
| const invalidTypeMessage = (incorrect, type) => { | |
| throw new Error('All values need to be of type: ' + type + '. Keys (' + sort(incorrect).join(', ') + ') were not.'); | |
| }; | |
| const checkDupes = everything => { | |
| const sorted = sort(everything); | |
| const dupe = find$1(sorted, (s, i) => { | |
| return i < sorted.length - 1 && s === sorted[i + 1]; | |
| }); | |
| dupe.each(d => { | |
| throw new Error('The field: ' + d + ' occurs more than once in the combined fields: [' + sorted.join(', ') + '].'); | |
| }); | |
| }; | |
| const base = (handleUnsupported, required) => { | |
| return baseWith(handleUnsupported, required, { | |
| validate: isFunction, | |
| label: 'function' | |
| }); | |
| }; | |
| const baseWith = (handleUnsupported, required, pred) => { | |
| if (required.length === 0) { | |
| throw new Error('You must specify at least one required field.'); | |
| } | |
| validateStrArr('required', required); | |
| checkDupes(required); | |
| return obj => { | |
| const keys$1 = keys(obj); | |
| const allReqd = forall(required, req => { | |
| return contains$2(keys$1, req); | |
| }); | |
| if (!allReqd) { | |
| reqMessage(required, keys$1); | |
| } | |
| handleUnsupported(required, keys$1); | |
| const invalidKeys = filter$2(required, key => { | |
| return !pred.validate(obj[key], key); | |
| }); | |
| if (invalidKeys.length > 0) { | |
| invalidTypeMessage(invalidKeys, pred.label); | |
| } | |
| return obj; | |
| }; | |
| }; | |
| const handleExact = (required, keys) => { | |
| const unsupported = filter$2(keys, key => { | |
| return !contains$2(required, key); | |
| }); | |
| if (unsupported.length > 0) { | |
| unsuppMessage(unsupported); | |
| } | |
| }; | |
| const exactly = required => base(handleExact, required); | |
| const DragMode = exactly([ | |
| 'compare', | |
| 'extract', | |
| 'mutate', | |
| 'sink' | |
| ]); | |
| const DragSink = exactly([ | |
| 'element', | |
| 'start', | |
| 'stop', | |
| 'destroy' | |
| ]); | |
| const DragApi = exactly([ | |
| 'forceDrop', | |
| 'drop', | |
| 'move', | |
| 'delayDrop' | |
| ]); | |
| const InDrag = () => { | |
| let previous = Optional.none(); | |
| const reset = () => { | |
| previous = Optional.none(); | |
| }; | |
| const update = (mode, nu) => { | |
| const result = previous.map(old => { | |
| return mode.compare(old, nu); | |
| }); | |
| previous = Optional.some(nu); | |
| return result; | |
| }; | |
| const onEvent = (event, mode) => { | |
| const dataOption = mode.extract(event); | |
| dataOption.each(data => { | |
| const offset = update(mode, data); | |
| offset.each(d => { | |
| events.trigger.move(d); | |
| }); | |
| }); | |
| }; | |
| const events = create$1({ move: Event(['info']) }); | |
| return { | |
| onEvent, | |
| reset, | |
| events: events.registry | |
| }; | |
| }; | |
| const NoDrag = () => { | |
| const events = create$1({ move: Event(['info']) }); | |
| return { | |
| onEvent: noop, | |
| reset: noop, | |
| events: events.registry | |
| }; | |
| }; | |
| const Movement = () => { | |
| const noDragState = NoDrag(); | |
| const inDragState = InDrag(); | |
| let dragState = noDragState; | |
| const on = () => { | |
| dragState.reset(); | |
| dragState = inDragState; | |
| }; | |
| const off = () => { | |
| dragState.reset(); | |
| dragState = noDragState; | |
| }; | |
| const onEvent = (event, mode) => { | |
| dragState.onEvent(event, mode); | |
| }; | |
| const isOn = () => { | |
| return dragState === inDragState; | |
| }; | |
| return { | |
| on, | |
| off, | |
| isOn, | |
| onEvent, | |
| events: inDragState.events | |
| }; | |
| }; | |
| const setup = (mutation, mode, settings) => { | |
| let active = false; | |
| const events = create$1({ | |
| start: Event([]), | |
| stop: Event([]) | |
| }); | |
| const movement = Movement(); | |
| const drop = () => { | |
| sink.stop(); | |
| if (movement.isOn()) { | |
| movement.off(); | |
| events.trigger.stop(); | |
| } | |
| }; | |
| const throttledDrop = last(drop, 200); | |
| const go = parent => { | |
| sink.start(parent); | |
| movement.on(); | |
| events.trigger.start(); | |
| }; | |
| const mousemove = event => { | |
| throttledDrop.cancel(); | |
| movement.onEvent(event, mode); | |
| }; | |
| movement.events.move.bind(event => { | |
| mode.mutate(mutation, event.info); | |
| }); | |
| const on = () => { | |
| active = true; | |
| }; | |
| const off = () => { | |
| active = false; | |
| }; | |
| const isActive = () => active; | |
| const runIfActive = f => { | |
| return (...args) => { | |
| if (active) { | |
| f.apply(null, args); | |
| } | |
| }; | |
| }; | |
| const sink = mode.sink(DragApi({ | |
| forceDrop: drop, | |
| drop: runIfActive(drop), | |
| move: runIfActive(mousemove), | |
| delayDrop: runIfActive(throttledDrop.throttle) | |
| }), settings); | |
| const destroy = () => { | |
| sink.destroy(); | |
| }; | |
| return { | |
| element: sink.element, | |
| go, | |
| on, | |
| off, | |
| isActive, | |
| destroy, | |
| events: events.registry | |
| }; | |
| }; | |
| const css = namespace => { | |
| const dashNamespace = namespace.replace(/\./g, '-'); | |
| const resolve = str => { | |
| return dashNamespace + '-' + str; | |
| }; | |
| return { resolve }; | |
| }; | |
| const styles$1 = css('ephox-dragster'); | |
| const resolve$1 = styles$1.resolve; | |
| const Blocker = options => { | |
| const settings = { | |
| layerClass: resolve$1('blocker'), | |
| ...options | |
| }; | |
| const div = SugarElement.fromTag('div'); | |
| set$2(div, 'role', 'presentation'); | |
| setAll(div, { | |
| position: 'fixed', | |
| left: '0px', | |
| top: '0px', | |
| width: '100%', | |
| height: '100%' | |
| }); | |
| add(div, resolve$1('blocker')); | |
| add(div, settings.layerClass); | |
| const element = constant(div); | |
| const destroy = () => { | |
| remove$6(div); | |
| }; | |
| return { | |
| element, | |
| destroy | |
| }; | |
| }; | |
| const compare = (old, nu) => { | |
| return SugarPosition(nu.left - old.left, nu.top - old.top); | |
| }; | |
| const extract = event => { | |
| return Optional.some(SugarPosition(event.x, event.y)); | |
| }; | |
| const mutate = (mutation, info) => { | |
| mutation.mutate(info.left, info.top); | |
| }; | |
| const sink = (dragApi, settings) => { | |
| const blocker = Blocker(settings); | |
| const mdown = bind(blocker.element(), 'mousedown', dragApi.forceDrop); | |
| const mup = bind(blocker.element(), 'mouseup', dragApi.drop); | |
| const mmove = bind(blocker.element(), 'mousemove', dragApi.move); | |
| const mout = bind(blocker.element(), 'mouseout', dragApi.delayDrop); | |
| const destroy = () => { | |
| blocker.destroy(); | |
| mup.unbind(); | |
| mmove.unbind(); | |
| mout.unbind(); | |
| mdown.unbind(); | |
| }; | |
| const start = parent => { | |
| append$1(parent, blocker.element()); | |
| }; | |
| const stop = () => { | |
| remove$6(blocker.element()); | |
| }; | |
| return DragSink({ | |
| element: blocker.element, | |
| start, | |
| stop, | |
| destroy | |
| }); | |
| }; | |
| var MouseDrag = DragMode({ | |
| compare, | |
| extract, | |
| sink, | |
| mutate | |
| }); | |
| const transform = (mutation, settings = {}) => { | |
| var _a; | |
| const mode = (_a = settings.mode) !== null && _a !== void 0 ? _a : MouseDrag; | |
| return setup(mutation, mode, settings); | |
| }; | |
| const styles = css('ephox-snooker'); | |
| const resolve = styles.resolve; | |
| const Mutation = () => { | |
| const events = create$1({ | |
| drag: Event([ | |
| 'xDelta', | |
| 'yDelta' | |
| ]) | |
| }); | |
| const mutate = (x, y) => { | |
| events.trigger.drag(x, y); | |
| }; | |
| return { | |
| mutate, | |
| events: events.registry | |
| }; | |
| }; | |
| const BarMutation = () => { | |
| const events = create$1({ | |
| drag: Event([ | |
| 'xDelta', | |
| 'yDelta', | |
| 'target' | |
| ]) | |
| }); | |
| let target = Optional.none(); | |
| const delegate = Mutation(); | |
| delegate.events.drag.bind(event => { | |
| target.each(t => { | |
| events.trigger.drag(event.xDelta, event.yDelta, t); | |
| }); | |
| }); | |
| const assign = t => { | |
| target = Optional.some(t); | |
| }; | |
| const get = () => { | |
| return target; | |
| }; | |
| return { | |
| assign, | |
| get, | |
| mutate: delegate.mutate, | |
| events: events.registry | |
| }; | |
| }; | |
| const col = (column, x, y, w, h) => { | |
| const bar = SugarElement.fromTag('div'); | |
| setAll(bar, { | |
| position: 'absolute', | |
| left: x - w / 2 + 'px', | |
| top: y + 'px', | |
| height: h + 'px', | |
| width: w + 'px' | |
| }); | |
| setAll$1(bar, { | |
| 'data-column': column, | |
| 'role': 'presentation' | |
| }); | |
| return bar; | |
| }; | |
| const row = (r, x, y, w, h) => { | |
| const bar = SugarElement.fromTag('div'); | |
| setAll(bar, { | |
| position: 'absolute', | |
| left: x + 'px', | |
| top: y - h / 2 + 'px', | |
| height: h + 'px', | |
| width: w + 'px' | |
| }); | |
| setAll$1(bar, { | |
| 'data-row': r, | |
| 'role': 'presentation' | |
| }); | |
| return bar; | |
| }; | |
| const resizeBar = resolve('resizer-bar'); | |
| const resizeRowBar = resolve('resizer-rows'); | |
| const resizeColBar = resolve('resizer-cols'); | |
| const BAR_THICKNESS = 7; | |
| const resizableRows = (warehouse, isResizable) => bind$2(warehouse.all, (row, i) => isResizable(row.element) ? [i] : []); | |
| const resizableColumns = (warehouse, isResizable) => { | |
| const resizableCols = []; | |
| range$1(warehouse.grid.columns, index => { | |
| const colElmOpt = Warehouse.getColumnAt(warehouse, index).map(col => col.element); | |
| if (colElmOpt.forall(isResizable)) { | |
| resizableCols.push(index); | |
| } | |
| }); | |
| return filter$2(resizableCols, colIndex => { | |
| const columnCells = Warehouse.filterItems(warehouse, cell => cell.column === colIndex); | |
| return forall(columnCells, cell => isResizable(cell.element)); | |
| }); | |
| }; | |
| const destroy = wire => { | |
| const previous = descendants(wire.parent(), '.' + resizeBar); | |
| each$2(previous, remove$6); | |
| }; | |
| const drawBar = (wire, positions, create) => { | |
| const origin = wire.origin(); | |
| each$2(positions, cpOption => { | |
| cpOption.each(cp => { | |
| const bar = create(origin, cp); | |
| add(bar, resizeBar); | |
| append$1(wire.parent(), bar); | |
| }); | |
| }); | |
| }; | |
| const refreshCol = (wire, colPositions, position, tableHeight) => { | |
| drawBar(wire, colPositions, (origin, cp) => { | |
| const colBar = col(cp.col, cp.x - origin.left, position.top - origin.top, BAR_THICKNESS, tableHeight); | |
| add(colBar, resizeColBar); | |
| return colBar; | |
| }); | |
| }; | |
| const refreshRow = (wire, rowPositions, position, tableWidth) => { | |
| drawBar(wire, rowPositions, (origin, cp) => { | |
| const rowBar = row(cp.row, position.left - origin.left, cp.y - origin.top, tableWidth, BAR_THICKNESS); | |
| add(rowBar, resizeRowBar); | |
| return rowBar; | |
| }); | |
| }; | |
| const refreshGrid = (warhouse, wire, table, rows, cols) => { | |
| const position = absolute(table); | |
| const isResizable = wire.isResizable; | |
| const rowPositions = rows.length > 0 ? height.positions(rows, table) : []; | |
| const resizableRowBars = rowPositions.length > 0 ? resizableRows(warhouse, isResizable) : []; | |
| const resizableRowPositions = filter$2(rowPositions, (_pos, i) => exists(resizableRowBars, barIndex => i === barIndex)); | |
| refreshRow(wire, resizableRowPositions, position, getOuter$2(table)); | |
| const colPositions = cols.length > 0 ? width.positions(cols, table) : []; | |
| const resizableColBars = colPositions.length > 0 ? resizableColumns(warhouse, isResizable) : []; | |
| const resizableColPositions = filter$2(colPositions, (_pos, i) => exists(resizableColBars, barIndex => i === barIndex)); | |
| refreshCol(wire, resizableColPositions, position, getOuter$1(table)); | |
| }; | |
| const refresh = (wire, table) => { | |
| destroy(wire); | |
| if (wire.isResizable(table)) { | |
| const warehouse = Warehouse.fromTable(table); | |
| const rows$1 = rows(warehouse); | |
| const cols = columns(warehouse); | |
| refreshGrid(warehouse, wire, table, rows$1, cols); | |
| } | |
| }; | |
| const each = (wire, f) => { | |
| const bars = descendants(wire.parent(), '.' + resizeBar); | |
| each$2(bars, f); | |
| }; | |
| const hide = wire => { | |
| each(wire, bar => { | |
| set$1(bar, 'display', 'none'); | |
| }); | |
| }; | |
| const show = wire => { | |
| each(wire, bar => { | |
| set$1(bar, 'display', 'block'); | |
| }); | |
| }; | |
| const isRowBar = element => { | |
| return has(element, resizeRowBar); | |
| }; | |
| const isColBar = element => { | |
| return has(element, resizeColBar); | |
| }; | |
| const resizeBarDragging = resolve('resizer-bar-dragging'); | |
| const BarManager = wire => { | |
| const mutation = BarMutation(); | |
| const resizing = transform(mutation, {}); | |
| let hoverTable = Optional.none(); | |
| const getResizer = (element, type) => { | |
| return Optional.from(get$b(element, type)); | |
| }; | |
| mutation.events.drag.bind(event => { | |
| getResizer(event.target, 'data-row').each(_dataRow => { | |
| const currentRow = getCssValue(event.target, 'top'); | |
| set$1(event.target, 'top', currentRow + event.yDelta + 'px'); | |
| }); | |
| getResizer(event.target, 'data-column').each(_dataCol => { | |
| const currentCol = getCssValue(event.target, 'left'); | |
| set$1(event.target, 'left', currentCol + event.xDelta + 'px'); | |
| }); | |
| }); | |
| const getDelta = (target, dir) => { | |
| const newX = getCssValue(target, dir); | |
| const oldX = getAttrValue(target, 'data-initial-' + dir, 0); | |
| return newX - oldX; | |
| }; | |
| resizing.events.stop.bind(() => { | |
| mutation.get().each(target => { | |
| hoverTable.each(table => { | |
| getResizer(target, 'data-row').each(row => { | |
| const delta = getDelta(target, 'top'); | |
| remove$7(target, 'data-initial-top'); | |
| events.trigger.adjustHeight(table, delta, parseInt(row, 10)); | |
| }); | |
| getResizer(target, 'data-column').each(column => { | |
| const delta = getDelta(target, 'left'); | |
| remove$7(target, 'data-initial-left'); | |
| events.trigger.adjustWidth(table, delta, parseInt(column, 10)); | |
| }); | |
| refresh(wire, table); | |
| }); | |
| }); | |
| }); | |
| const handler = (target, dir) => { | |
| events.trigger.startAdjust(); | |
| mutation.assign(target); | |
| set$2(target, 'data-initial-' + dir, getCssValue(target, dir)); | |
| add(target, resizeBarDragging); | |
| set$1(target, 'opacity', '0.2'); | |
| resizing.go(wire.parent()); | |
| }; | |
| const mousedown = bind(wire.parent(), 'mousedown', event => { | |
| if (isRowBar(event.target)) { | |
| handler(event.target, 'top'); | |
| } | |
| if (isColBar(event.target)) { | |
| handler(event.target, 'left'); | |
| } | |
| }); | |
| const isRoot = e => { | |
| return eq$1(e, wire.view()); | |
| }; | |
| const findClosestEditableTable = target => closest$1(target, 'table', isRoot).filter(isEditable$1); | |
| const mouseover = bind(wire.view(), 'mouseover', event => { | |
| findClosestEditableTable(event.target).fold(() => { | |
| if (inBody(event.target)) { | |
| destroy(wire); | |
| } | |
| }, table => { | |
| if (resizing.isActive()) { | |
| hoverTable = Optional.some(table); | |
| refresh(wire, table); | |
| } | |
| }); | |
| }); | |
| const destroy$1 = () => { | |
| mousedown.unbind(); | |
| mouseover.unbind(); | |
| resizing.destroy(); | |
| destroy(wire); | |
| }; | |
| const refresh$1 = tbl => { | |
| refresh(wire, tbl); | |
| }; | |
| const events = create$1({ | |
| adjustHeight: Event([ | |
| 'table', | |
| 'delta', | |
| 'row' | |
| ]), | |
| adjustWidth: Event([ | |
| 'table', | |
| 'delta', | |
| 'column' | |
| ]), | |
| startAdjust: Event([]) | |
| }); | |
| return { | |
| destroy: destroy$1, | |
| refresh: refresh$1, | |
| on: resizing.on, | |
| off: resizing.off, | |
| hideBars: curry(hide, wire), | |
| showBars: curry(show, wire), | |
| events: events.registry | |
| }; | |
| }; | |
| const create = (wire, resizing, lazySizing) => { | |
| const hdirection = height; | |
| const vdirection = width; | |
| const manager = BarManager(wire); | |
| const events = create$1({ | |
| beforeResize: Event([ | |
| 'table', | |
| 'type' | |
| ]), | |
| afterResize: Event([ | |
| 'table', | |
| 'type' | |
| ]), | |
| startDrag: Event([]) | |
| }); | |
| manager.events.adjustHeight.bind(event => { | |
| const table = event.table; | |
| events.trigger.beforeResize(table, 'row'); | |
| const delta = hdirection.delta(event.delta, table); | |
| adjustHeight(table, delta, event.row, hdirection); | |
| events.trigger.afterResize(table, 'row'); | |
| }); | |
| manager.events.startAdjust.bind(_event => { | |
| events.trigger.startDrag(); | |
| }); | |
| manager.events.adjustWidth.bind(event => { | |
| const table = event.table; | |
| events.trigger.beforeResize(table, 'col'); | |
| const delta = vdirection.delta(event.delta, table); | |
| const tableSize = lazySizing(table); | |
| adjustWidth(table, delta, event.column, resizing, tableSize); | |
| events.trigger.afterResize(table, 'col'); | |
| }); | |
| return { | |
| on: manager.on, | |
| off: manager.off, | |
| refreshBars: manager.refresh, | |
| hideBars: manager.hideBars, | |
| showBars: manager.showBars, | |
| destroy: manager.destroy, | |
| events: events.registry | |
| }; | |
| }; | |
| const TableResize = { create }; | |
| const only = (element, isResizable) => { | |
| const parent = isDocument(element) ? documentElement(element) : element; | |
| return { | |
| parent: constant(parent), | |
| view: constant(element), | |
| origin: constant(SugarPosition(0, 0)), | |
| isResizable | |
| }; | |
| }; | |
| const detached = (editable, chrome, isResizable) => { | |
| const origin = () => absolute(chrome); | |
| return { | |
| parent: constant(chrome), | |
| view: constant(editable), | |
| origin, | |
| isResizable | |
| }; | |
| }; | |
| const body = (editable, chrome, isResizable) => { | |
| return { | |
| parent: constant(chrome), | |
| view: constant(editable), | |
| origin: constant(SugarPosition(0, 0)), | |
| isResizable | |
| }; | |
| }; | |
| const ResizeWire = { | |
| only, | |
| detached, | |
| body | |
| }; | |
| const createContainer = () => { | |
| const container = SugarElement.fromTag('div'); | |
| setAll(container, { | |
| position: 'static', | |
| height: '0', | |
| width: '0', | |
| padding: '0', | |
| margin: '0', | |
| border: '0' | |
| }); | |
| append$1(body$1(), container); | |
| return container; | |
| }; | |
| const get = (editor, isResizable) => { | |
| return editor.inline ? ResizeWire.body(SugarElement.fromDom(editor.getBody()), createContainer(), isResizable) : ResizeWire.only(SugarElement.fromDom(editor.getDoc()), isResizable); | |
| }; | |
| const remove = (editor, wire) => { | |
| if (editor.inline) { | |
| remove$6(wire.parent()); | |
| } | |
| }; | |
| const isTable = node => isNonNullable(node) && node.nodeName === 'TABLE'; | |
| const barResizerPrefix = 'bar-'; | |
| const isResizable = elm => get$b(elm, 'data-mce-resize') !== 'false'; | |
| const syncPixels = table => { | |
| const warehouse = Warehouse.fromTable(table); | |
| if (!Warehouse.hasColumns(warehouse)) { | |
| each$2(cells$1(table), cell => { | |
| const computedWidth = get$a(cell, 'width'); | |
| set$1(cell, 'width', computedWidth); | |
| remove$7(cell, 'width'); | |
| }); | |
| } | |
| }; | |
| const TableResizeHandler = editor => { | |
| const selectionRng = value(); | |
| const tableResize = value(); | |
| const resizeWire = value(); | |
| let startW; | |
| let startRawW; | |
| const lazySizing = table => get$5(editor, table); | |
| const lazyResizingBehaviour = () => isPreserveTableColumnResizing(editor) ? preserveTable() : resizeTable(); | |
| const getNumColumns = table => getGridSize(table).columns; | |
| const afterCornerResize = (table, origin, width) => { | |
| const isRightEdgeResize = endsWith(origin, 'e'); | |
| if (startRawW === '') { | |
| convertToPercentSize(table); | |
| } | |
| if (width !== startW && startRawW !== '') { | |
| set$1(table, 'width', startRawW); | |
| const resizing = lazyResizingBehaviour(); | |
| const tableSize = lazySizing(table); | |
| const col = isPreserveTableColumnResizing(editor) || isRightEdgeResize ? getNumColumns(table) - 1 : 0; | |
| adjustWidth(table, width - startW, col, resizing, tableSize); | |
| } else if (isPercentage$1(startRawW)) { | |
| const percentW = parseFloat(startRawW.replace('%', '')); | |
| const targetPercentW = width * percentW / startW; | |
| set$1(table, 'width', targetPercentW + '%'); | |
| } | |
| if (isPixel(startRawW)) { | |
| syncPixels(table); | |
| } | |
| }; | |
| const destroy = () => { | |
| tableResize.on(sz => { | |
| sz.destroy(); | |
| }); | |
| resizeWire.on(w => { | |
| remove(editor, w); | |
| }); | |
| }; | |
| editor.on('init', () => { | |
| const rawWire = get(editor, isResizable); | |
| resizeWire.set(rawWire); | |
| if (hasTableObjectResizing(editor) && hasTableResizeBars(editor)) { | |
| const resizing = lazyResizingBehaviour(); | |
| const sz = TableResize.create(rawWire, resizing, lazySizing); | |
| sz.on(); | |
| sz.events.startDrag.bind(_event => { | |
| selectionRng.set(editor.selection.getRng()); | |
| }); | |
| sz.events.beforeResize.bind(event => { | |
| const rawTable = event.table.dom; | |
| fireObjectResizeStart(editor, rawTable, getPixelWidth(rawTable), getPixelHeight(rawTable), barResizerPrefix + event.type); | |
| }); | |
| sz.events.afterResize.bind(event => { | |
| const table = event.table; | |
| const rawTable = table.dom; | |
| removeDataStyle(table); | |
| selectionRng.on(rng => { | |
| editor.selection.setRng(rng); | |
| editor.focus(); | |
| }); | |
| fireObjectResized(editor, rawTable, getPixelWidth(rawTable), getPixelHeight(rawTable), barResizerPrefix + event.type); | |
| editor.undoManager.add(); | |
| }); | |
| tableResize.set(sz); | |
| } | |
| }); | |
| editor.on('ObjectResizeStart', e => { | |
| const targetElm = e.target; | |
| if (isTable(targetElm)) { | |
| const table = SugarElement.fromDom(targetElm); | |
| each$2(editor.dom.select('.mce-clonedresizable'), clone => { | |
| editor.dom.addClass(clone, 'mce-' + getTableColumnResizingBehaviour(editor) + '-columns'); | |
| }); | |
| if (!isPixelSizing(table) && isTablePixelsForced(editor)) { | |
| convertToPixelSize(table); | |
| } else if (!isPercentSizing(table) && isTablePercentagesForced(editor)) { | |
| convertToPercentSize(table); | |
| } | |
| if (isNoneSizing(table) && startsWith(e.origin, barResizerPrefix)) { | |
| convertToPercentSize(table); | |
| } | |
| startW = e.width; | |
| startRawW = isTableResponsiveForced(editor) ? '' : getRawWidth(editor, targetElm).getOr(''); | |
| } | |
| }); | |
| editor.on('ObjectResized', e => { | |
| const targetElm = e.target; | |
| if (isTable(targetElm)) { | |
| const table = SugarElement.fromDom(targetElm); | |
| const origin = e.origin; | |
| if (startsWith(origin, 'corner-')) { | |
| afterCornerResize(table, origin, e.width); | |
| } | |
| removeDataStyle(table); | |
| fireTableModified(editor, table.dom, styleModified); | |
| } | |
| }); | |
| editor.on('SwitchMode', () => { | |
| tableResize.on(resize => { | |
| if (editor.mode.isReadOnly()) { | |
| resize.hideBars(); | |
| } else { | |
| resize.showBars(); | |
| } | |
| }); | |
| }); | |
| editor.on('dragstart dragend', e => { | |
| tableResize.on(resize => { | |
| if (e.type === 'dragstart') { | |
| resize.hideBars(); | |
| resize.off(); | |
| } else { | |
| resize.on(); | |
| resize.showBars(); | |
| } | |
| }); | |
| }); | |
| editor.on('remove', () => { | |
| destroy(); | |
| }); | |
| const refresh = table => { | |
| tableResize.on(resize => resize.refreshBars(SugarElement.fromDom(table))); | |
| }; | |
| const hide = () => { | |
| tableResize.on(resize => resize.hideBars()); | |
| }; | |
| const show = () => { | |
| tableResize.on(resize => resize.showBars()); | |
| }; | |
| return { | |
| refresh, | |
| hide, | |
| show | |
| }; | |
| }; | |
| const setupTable = editor => { | |
| register(editor); | |
| const resizeHandler = TableResizeHandler(editor); | |
| const cellSelectionHandler = TableCellSelectionHandler(editor, resizeHandler); | |
| const actions = TableActions(editor, resizeHandler, cellSelectionHandler); | |
| registerCommands(editor, actions); | |
| registerQueryCommands(editor, actions); | |
| registerEvents(editor, actions); | |
| return { | |
| getSelectedCells: cellSelectionHandler.getSelectedCells, | |
| clearSelectedCells: cellSelectionHandler.clearSelectedCells | |
| }; | |
| }; | |
| const DomModel = editor => { | |
| const table = setupTable(editor); | |
| return { table }; | |
| }; | |
| var Model = () => { | |
| global$1.add('dom', DomModel); | |
| }; | |
| Model(); | |
| })(); | |