Archived
1
0

Initial commit as of 2018-10-16

This commit is contained in:
Marcel
2018-10-16 18:28:42 +02:00
commit 29d7c2ffdc
3601 changed files with 358427 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
import Parchment from 'parchment';
let config = {
scope: Parchment.Scope.BLOCK,
whitelist: ['right', 'center', 'justify']
};
let AlignAttribute = new Parchment.Attributor.Attribute('align', 'align', config);
let AlignClass = new Parchment.Attributor.Class('align', 'ql-align', config);
let AlignStyle = new Parchment.Attributor.Style('align', 'text-align', config);
export { AlignAttribute, AlignClass, AlignStyle };

View File

@@ -0,0 +1,11 @@
import Parchment from 'parchment';
import { ColorAttributor } from './color';
let BackgroundClass = new Parchment.Attributor.Class('background', 'ql-bg', {
scope: Parchment.Scope.INLINE
});
let BackgroundStyle = new ColorAttributor('background', 'background-color', {
scope: Parchment.Scope.INLINE
});
export { BackgroundClass, BackgroundStyle };

View File

@@ -0,0 +1,9 @@
import Block from '../blots/block';
class Blockquote extends Block {}
Blockquote.blotName = 'blockquote';
Blockquote.tagName = 'blockquote';
export default Blockquote;

22
assets/js/formats/bold.js Normal file
View File

@@ -0,0 +1,22 @@
import Inline from '../blots/inline';
class Bold extends Inline {
static create() {
return super.create();
}
static formats() {
return true;
}
optimize(context) {
super.optimize(context);
if (this.domNode.tagName !== this.statics.tagName[0]) {
this.replaceWith(this.statics.blotName);
}
}
}
Bold.blotName = 'bold';
Bold.tagName = ['STRONG', 'B'];
export default Bold;

118
assets/js/formats/code.js Normal file
View File

@@ -0,0 +1,118 @@
import Delta from 'quill-delta';
import Parchment from 'parchment';
import Block from '../blots/block';
import Inline from '../blots/inline';
import TextBlot from '../blots/text';
class Code extends Inline {}
Code.blotName = 'code';
Code.tagName = 'CODE';
class CodeBlock extends Block {
static create(value) {
let domNode = super.create(value);
domNode.setAttribute('spellcheck', false);
return domNode;
}
static formats() {
return true;
}
delta() {
let text = this.domNode.textContent;
if (text.endsWith('\n')) { // Should always be true
text = text.slice(0, -1);
}
return text.split('\n').reduce((delta, frag) => {
return delta.insert(frag).insert('\n', this.formats());
}, new Delta());
}
format(name, value) {
if (name === this.statics.blotName && value) return;
let [text, ] = this.descendant(TextBlot, this.length() - 1);
if (text != null) {
text.deleteAt(text.length() - 1, 1);
}
super.format(name, value);
}
formatAt(index, length, name, value) {
if (length === 0) return;
if (Parchment.query(name, Parchment.Scope.BLOCK) == null ||
(name === this.statics.blotName && value === this.statics.formats(this.domNode))) {
return;
}
let nextNewline = this.newlineIndex(index);
if (nextNewline < 0 || nextNewline >= index + length) return;
let prevNewline = this.newlineIndex(index, true) + 1;
let isolateLength = nextNewline - prevNewline + 1;
let blot = this.isolate(prevNewline, isolateLength);
let next = blot.next;
blot.format(name, value);
if (next instanceof CodeBlock) {
next.formatAt(0, index - prevNewline + length - isolateLength, name, value);
}
}
insertAt(index, value, def) {
if (def != null) return;
let [text, offset] = this.descendant(TextBlot, index);
text.insertAt(offset, value);
}
length() {
let length = this.domNode.textContent.length;
if (!this.domNode.textContent.endsWith('\n')) {
return length + 1;
}
return length;
}
newlineIndex(searchIndex, reverse = false) {
if (!reverse) {
let offset = this.domNode.textContent.slice(searchIndex).indexOf('\n');
return offset > -1 ? searchIndex + offset : -1;
} else {
return this.domNode.textContent.slice(0, searchIndex).lastIndexOf('\n');
}
}
optimize(context) {
if (!this.domNode.textContent.endsWith('\n')) {
this.appendChild(Parchment.create('text', '\n'));
}
super.optimize(context);
let next = this.next;
if (next != null && next.prev === this &&
next.statics.blotName === this.statics.blotName &&
this.statics.formats(this.domNode) === next.statics.formats(next.domNode)) {
next.optimize(context);
next.moveChildren(this);
next.remove();
}
}
replace(target) {
super.replace(target);
[].slice.call(this.domNode.querySelectorAll('*')).forEach(function(node) {
let blot = Parchment.find(node);
if (blot == null) {
node.parentNode.removeChild(node);
} else if (blot instanceof Parchment.Embed) {
blot.remove();
} else {
blot.unwrap();
}
});
}
}
CodeBlock.blotName = 'code-block';
CodeBlock.tagName = 'PRE';
CodeBlock.TAB = ' ';
export { Code, CodeBlock as default };

View File

@@ -0,0 +1,21 @@
import Parchment from 'parchment';
class ColorAttributor extends Parchment.Attributor.Style {
value(domNode) {
let value = super.value(domNode);
if (!value.startsWith('rgb(')) return value;
value = value.replace(/^[^\d]+/, '').replace(/[^\d]+$/, '');
return '#' + value.split(',').map(function(component) {
return ('00' + parseInt(component).toString(16)).slice(-2);
}).join('');
}
}
let ColorClass = new Parchment.Attributor.Class('color', 'ql-color', {
scope: Parchment.Scope.INLINE
});
let ColorStyle = new ColorAttributor('color', 'color', {
scope: Parchment.Scope.INLINE
});
export { ColorAttributor, ColorClass, ColorStyle };

View File

@@ -0,0 +1,12 @@
import Parchment from 'parchment';
let config = {
scope: Parchment.Scope.BLOCK,
whitelist: ['rtl']
};
let DirectionAttribute = new Parchment.Attributor.Attribute('direction', 'dir', config);
let DirectionClass = new Parchment.Attributor.Class('direction', 'ql-direction', config);
let DirectionStyle = new Parchment.Attributor.Style('direction', 'direction', config);
export { DirectionAttribute, DirectionClass, DirectionStyle };

18
assets/js/formats/font.js Normal file
View File

@@ -0,0 +1,18 @@
import Parchment from 'parchment';
let config = {
scope: Parchment.Scope.INLINE,
whitelist: ['serif', 'monospace']
};
let FontClass = new Parchment.Attributor.Class('font', 'ql-font', config);
class FontStyleAttributor extends Parchment.Attributor.Style {
value(node) {
return super.value(node).replace(/["']/g, '');
}
}
let FontStyle = new FontStyleAttributor('font', 'font-family', config);
export { FontStyle, FontClass };

View File

@@ -0,0 +1,13 @@
import Block from '../blots/block';
class Header extends Block {
static formats(domNode) {
return this.tagName.indexOf(domNode.tagName) + 1;
}
}
Header.blotName = 'header';
Header.tagName = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'];
export default Header;

View File

@@ -0,0 +1,57 @@
import Parchment from 'parchment';
import { sanitize } from '../formats/link';
const ATTRIBUTES = [
'alt',
'height',
'width'
];
class Image extends Parchment.Embed {
static create(value) {
let node = super.create(value);
if (typeof value === 'string') {
node.setAttribute('src', this.sanitize(value));
}
return node;
}
static formats(domNode) {
return ATTRIBUTES.reduce(function(formats, attribute) {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute);
}
return formats;
}, {});
}
static match(url) {
return /\.(jpe?g|gif|png)$/.test(url) || /^data:image\/.+;base64/.test(url);
}
static sanitize(url) {
return sanitize(url, ['http', 'https', 'data']) ? url : '//:0';
}
static value(domNode) {
return domNode.getAttribute('src');
}
format(name, value) {
if (ATTRIBUTES.indexOf(name) > -1) {
if (value) {
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
super.format(name, value);
}
}
}
Image.blotName = 'image';
Image.tagName = 'IMG';
export default Image;

View File

@@ -0,0 +1,31 @@
import Parchment from 'parchment';
class IdentAttributor extends Parchment.Attributor.Class {
add(node, value) {
if (value === '+1' || value === '-1') {
let indent = this.value(node) || 0;
value = (value === '+1' ? (indent + 1) : (indent - 1));
}
if (value === 0) {
this.remove(node);
return true;
} else {
return super.add(node, value);
}
}
canAdd(node, value) {
return super.canAdd(node, value) || super.canAdd(node, parseInt(value));
}
value(node) {
return parseInt(super.value(node)) || undefined; // Don't return NaN
}
}
let IndentClass = new IdentAttributor('indent', 'ql-indent', {
scope: Parchment.Scope.BLOCK,
whitelist: [1, 2, 3, 4, 5, 6, 7, 8]
});
export { IndentClass };

View File

@@ -0,0 +1,7 @@
import Bold from './bold';
class Italic extends Bold { }
Italic.blotName = 'italic';
Italic.tagName = ['EM', 'I'];
export default Italic;

41
assets/js/formats/link.js Normal file
View File

@@ -0,0 +1,41 @@
import Inline from '../blots/inline';
class Link extends Inline {
static create(value) {
let node = super.create(value);
value = this.sanitize(value);
node.setAttribute('href', value);
node.setAttribute('target', '_blank');
return node;
}
static formats(domNode) {
return domNode.getAttribute('href');
}
static sanitize(url) {
return sanitize(url, this.PROTOCOL_WHITELIST) ? url : this.SANITIZED_URL;
}
format(name, value) {
if (name !== this.statics.blotName || !value) return super.format(name, value);
value = this.constructor.sanitize(value);
this.domNode.setAttribute('href', value);
}
}
Link.blotName = 'link';
Link.tagName = 'A';
Link.SANITIZED_URL = 'about:blank';
Link.PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel'];
function sanitize(url, protocols) {
let anchor = document.createElement('a');
anchor.href = url;
let protocol = anchor.href.slice(0, anchor.href.indexOf(':'));
return protocols.indexOf(protocol) > -1;
}
export { Link as default, sanitize };

130
assets/js/formats/list.js Normal file
View File

@@ -0,0 +1,130 @@
import Parchment from 'parchment';
import Block from '../blots/block';
import Container from '../blots/container';
class ListItem extends Block {
static formats(domNode) {
return domNode.tagName === this.tagName ? undefined : super.formats(domNode);
}
format(name, value) {
if (name === List.blotName && !value) {
this.replaceWith(Parchment.create(this.statics.scope));
} else {
super.format(name, value);
}
}
remove() {
if (this.prev == null && this.next == null) {
this.parent.remove();
} else {
super.remove();
}
}
replaceWith(name, value) {
this.parent.isolate(this.offset(this.parent), this.length());
if (name === this.parent.statics.blotName) {
this.parent.replaceWith(name, value);
return this;
} else {
this.parent.unwrap();
return super.replaceWith(name, value);
}
}
}
ListItem.blotName = 'list-item';
ListItem.tagName = 'LI';
class List extends Container {
static create(value) {
let tagName = value === 'ordered' ? 'OL' : 'UL';
let node = super.create(tagName);
if (value === 'checked' || value === 'unchecked') {
node.setAttribute('data-checked', value === 'checked');
}
return node;
}
static formats(domNode) {
if (domNode.tagName === 'OL') return 'ordered';
if (domNode.tagName === 'UL') {
if (domNode.hasAttribute('data-checked')) {
return domNode.getAttribute('data-checked') === 'true' ? 'checked' : 'unchecked';
} else {
return 'bullet';
}
}
return undefined;
}
constructor(domNode) {
super(domNode);
const listEventHandler = (e) => {
if (e.target.parentNode !== domNode) return;
let format = this.statics.formats(domNode);
let blot = Parchment.find(e.target);
if (format === 'checked') {
blot.format('list', 'unchecked');
} else if(format === 'unchecked') {
blot.format('list', 'checked');
}
}
domNode.addEventListener('touchstart', listEventHandler);
domNode.addEventListener('mousedown', listEventHandler);
}
format(name, value) {
if (this.children.length > 0) {
this.children.tail.format(name, value);
}
}
formats() {
// We don't inherit from FormatBlot
return { [this.statics.blotName]: this.statics.formats(this.domNode) };
}
insertBefore(blot, ref) {
if (blot instanceof ListItem) {
super.insertBefore(blot, ref);
} else {
let index = ref == null ? this.length() : ref.offset(this);
let after = this.split(index);
after.parent.insertBefore(blot, after);
}
}
optimize(context) {
super.optimize(context);
let next = this.next;
if (next != null && next.prev === this &&
next.statics.blotName === this.statics.blotName &&
next.domNode.tagName === this.domNode.tagName &&
next.domNode.getAttribute('data-checked') === this.domNode.getAttribute('data-checked')) {
next.moveChildren(this);
next.remove();
}
}
replace(target) {
if (target.statics.blotName !== this.statics.blotName) {
let item = Parchment.create(this.statics.defaultChild);
target.moveChildren(item);
this.appendChild(item);
}
super.replace(target);
}
}
List.blotName = 'list';
List.scope = Parchment.Scope.BLOCK_BLOT;
List.tagName = ['OL', 'UL'];
List.defaultChild = 'list-item';
List.allowedChildren = [ListItem];
export { ListItem, List as default };

View File

@@ -0,0 +1,23 @@
import Inline from '../blots/inline';
class Script extends Inline {
static create(value) {
if (value === 'super') {
return document.createElement('sup');
} else if (value === 'sub') {
return document.createElement('sub');
} else {
return super.create(value);
}
}
static formats(domNode) {
if (domNode.tagName === 'SUB') return 'sub';
if (domNode.tagName === 'SUP') return 'super';
return undefined;
}
}
Script.blotName = 'script';
Script.tagName = ['SUB', 'SUP'];
export default Script;

12
assets/js/formats/size.js Normal file
View File

@@ -0,0 +1,12 @@
import Parchment from 'parchment';
let SizeClass = new Parchment.Attributor.Class('size', 'ql-size', {
scope: Parchment.Scope.INLINE,
whitelist: ['small', 'large', 'huge']
});
let SizeStyle = new Parchment.Attributor.Style('size', 'font-size', {
scope: Parchment.Scope.INLINE,
whitelist: ['10px', '18px', '32px']
});
export { SizeClass, SizeStyle };

View File

@@ -0,0 +1,7 @@
import Inline from '../blots/inline';
class Strike extends Inline { }
Strike.blotName = 'strike';
Strike.tagName = 'S';
export default Strike;

View File

@@ -0,0 +1,7 @@
import Inline from '../blots/inline';
class Underline extends Inline { }
Underline.blotName = 'underline';
Underline.tagName = 'U';
export default Underline;

View File

@@ -0,0 +1,53 @@
import { BlockEmbed } from '../blots/block';
import Link from '../formats/link';
const ATTRIBUTES = [
'height',
'width'
];
class Video extends BlockEmbed {
static create(value) {
let node = super.create(value);
node.setAttribute('frameborder', '0');
node.setAttribute('allowfullscreen', true);
node.setAttribute('src', this.sanitize(value));
return node;
}
static formats(domNode) {
return ATTRIBUTES.reduce(function(formats, attribute) {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute);
}
return formats;
}, {});
}
static sanitize(url) {
return Link.sanitize(url);
}
static value(domNode) {
return domNode.getAttribute('src');
}
format(name, value) {
if (ATTRIBUTES.indexOf(name) > -1) {
if (value) {
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
super.format(name, value);
}
}
}
Video.blotName = 'video';
Video.className = 'ql-video';
Video.tagName = 'IFRAME';
export default Video;