Skip to content

Commit

Permalink
minor update
Browse files Browse the repository at this point in the history
  • Loading branch information
hoavmavada committed Oct 2, 2024
1 parent 60ff1ec commit 91dbff7
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 45 deletions.
2 changes: 1 addition & 1 deletion packages/quill/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hoavm-quill",
"version": "2.0.2-alpha1.13",
"version": "2.0.2-alpha1.15",
"description": "Your powerful, rich text editor",
"author": "Jason Chen <jhchen7@gmail.com>",
"homepage": "https://quilljs.com",
Expand Down
9 changes: 8 additions & 1 deletion packages/quill/src/formats/font.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import { ClassAttributor, Scope, StyleAttributor } from 'parchment';

const config = {
scope: Scope.INLINE,
whitelist: ['serif', 'monospace'],
whitelist: [
'arial',
'comic-sans',
'courier-new',
'georgia',
'helvetica',
'lucida',
],
};

const FontClass = new ClassAttributor('font', 'ql-font', config);
Expand Down
48 changes: 39 additions & 9 deletions packages/quill/src/formats/image.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,44 @@
import { EmbedBlot } from 'parchment';
import { sanitize } from './link.js';
// import { sanitize } from './link.js';

const ATTRIBUTES = ['alt', 'height', 'width'];
const ATTRIBUTES = [
'alt',
'height',
'width',
'src',
'srcset',
'sizes',
'crossorigin',
'usemap',
'ismap',
'loading',
'referrerpolicy',
'decoding',
'longdesc',
'title',
'class',
'id',
'style',
'tabindex',
'draggable',
'align',
'border',
'hspace',
'vspace',
'accesskey',
];

class Image extends EmbedBlot {
static blotName = 'image';
static tagName = 'IMG';

static create(value: string) {
const node = super.create(value) as Element;
if (typeof value === 'string') {
node.setAttribute('src', this.sanitize(value));
}
static create(value: any) {
const node = document.createElement('img');
ATTRIBUTES.forEach((attr) => {
if (value[attr]) {
node.setAttribute(attr, value[attr]);
}
});
return node;
}

Expand All @@ -32,11 +59,14 @@ class Image extends EmbedBlot {
}

static sanitize(url: string) {
return sanitize(url, ['http', 'https', 'data']) ? url : '//:0';
return url;
}

static value(domNode: Element) {
return domNode.getAttribute('src');
return ATTRIBUTES.reduce((acc: any, attr) => {
acc[attr] = domNode.getAttribute(attr);
return acc;
}, {});
}

domNode: HTMLImageElement;
Expand Down
54 changes: 45 additions & 9 deletions packages/quill/src/formats/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,65 @@ class Link extends Inline {
static PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel', 'sms'];

static create(value: string) {
const node = super.create(value) as HTMLElement;
node.setAttribute('href', this.sanitize(value));
node.setAttribute('rel', 'noopener noreferrer');
node.setAttribute('target', '_blank');
const node = super.create();
let newValue;
if (isStringified(value)) {
newValue = JSON.parse(value);
} else {
newValue = value;
}

if (typeof newValue !== 'string') {
['href', 'target', 'title'].forEach((attr) => {
if (newValue[attr]) node.setAttribute(attr, newValue[attr]);
});
return node;
}

node.setAttribute('href', newValue);
return node;
}

static formats(domNode: HTMLElement) {
return domNode.getAttribute('href');
return JSON.stringify({
href: domNode.getAttribute('href'),
target: domNode.getAttribute('target'),
title: domNode.getAttribute('title'),
});
}

static sanitize(url: string) {
return sanitize(url, this.PROTOCOL_WHITELIST) ? url : this.SANITIZED_URL;
}

format(name: string, value: unknown) {
format(name: string, value: any) {
if (name !== this.statics.blotName || !value) {
super.format(name, value);
return super.format(name, value);
}
let newValue;
if (isStringified(value)) {
newValue = JSON.parse(value);
} else {
// @ts-expect-error
this.domNode.setAttribute('href', this.constructor.sanitize(value));
newValue = value;
}

if (typeof newValue !== 'string') {
this.domNode.setAttribute('href', newValue.href);
this.domNode.setAttribute('target', newValue.target);
this.domNode.setAttribute('title', newValue.title);
} else {
this.domNode.setAttribute('href', newValue);
}
}
}

function isStringified(value: any) {
try {
JSON.parse(value);
} catch (e) {
return false;
}
return true;
}

function sanitize(url: string, protocols: string[]) {
Expand Down
52 changes: 34 additions & 18 deletions packages/quill/src/formats/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Quill from '../core/quill.js';

class ListContainer extends Container {}
ListContainer.blotName = 'list-container';
ListContainer.tagName = 'OL';
ListContainer.tagName = ['OL', 'UL']; // Support both ordered and unordered lists

class ListItem extends Block {
static create(value: string) {
Expand All @@ -15,7 +15,11 @@ class ListItem extends Block {
}

static formats(domNode: HTMLElement) {
return domNode.getAttribute('data-list') || undefined;
// Handle more formats for list types: ordered, unordered, checked, unchecked
const format = domNode.getAttribute('data-list') || '';
return ['ordered', 'bullet', 'checked', 'unchecked'].includes(format)
? format
: undefined;
}

static register() {
Expand All @@ -24,26 +28,37 @@ class ListItem extends Block {

constructor(scroll: Scroll, domNode: HTMLElement) {
super(scroll, domNode);
const ui = domNode.ownerDocument.createElement('span');
const listEventHandler = (e: Event) => {
if (!scroll.isEnabled()) return;
const format = this.statics.formats(domNode, scroll);
if (format === 'checked') {
this.format('list', 'unchecked');
e.preventDefault();
} else if (format === 'unchecked') {
this.format('list', 'checked');
e.preventDefault();
}
};
ui.addEventListener('mousedown', listEventHandler);
ui.addEventListener('touchstart', listEventHandler);
this.attachUI(ui);
const format = this.statics.formats(domNode);

// Create UI for checkbox if the list format is checked or unchecked
if (['checked', 'unchecked'].includes(format)) {
const ui = domNode.ownerDocument.createElement('span');
ui.classList.add('list-ui'); // Add a CSS class for styling

const listEventHandler = (e: Event) => {
if (!scroll.isEnabled()) return;

if (format === 'checked') {
this.format('list', 'unchecked');
e.preventDefault();
} else if (format === 'unchecked') {
this.format('list', 'checked');
e.preventDefault();
}
};

ui.addEventListener('mousedown', listEventHandler);
ui.addEventListener('touchstart', listEventHandler);
this.attachUI(ui);
}
}

format(name: string, value: string) {
// Extend format handling to include more list types
if (name === this.statics.blotName && value) {
this.domNode.setAttribute('data-list', value);
if (['ordered', 'bullet', 'checked', 'unchecked'].includes(value)) {
this.domNode.setAttribute('data-list', value);
}
} else {
super.format(name, value);
}
Expand All @@ -52,6 +67,7 @@ class ListItem extends Block {
ListItem.blotName = 'list';
ListItem.tagName = 'LI';

// Allow both ordered (OL) and unordered (UL) containers
ListContainer.allowedChildren = [ListItem];
ListItem.requiredContainer = ListContainer;

Expand Down
28 changes: 28 additions & 0 deletions packages/quill/src/formats/scriptTag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Block from '../blots/block.js';

class ScriptTag extends Block {
static blotName = 'script';
static tagName = 'SCRIPT';

static insertAt() {
return;
}

static insertBefore() {
return;
}

static replaceWith() {
return;
}

format() {
return;
}

formatAt() {
return;
}
}

export default ScriptTag;
2 changes: 1 addition & 1 deletion packages/quill/src/formats/size.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ClassAttributor, Scope, StyleAttributor } from 'parchment';

const SizeClass = new ClassAttributor('size', 'ql-size', {
scope: Scope.INLINE,
whitelist: ['small', 'large', 'huge'],
whitelist: ['extra-small', 'small', 'medium', 'large'],
});
const SizeStyle = new StyleAttributor('size', 'font-size', {
scope: Scope.INLINE,
Expand Down
18 changes: 12 additions & 6 deletions packages/quill/src/formats/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@ import { BlockEmbed } from '../blots/block.js';
import Link from './link.js';

const ATTRIBUTES = [
'title',
'sandbox',
'referrerpolicy',
'name',
'src',
'srcdoc',
'name',
'width',
'height',
'frameborder',
'allowfullscreen',
'allow',
'allowfullscreen',
'sandbox',
'referrerpolicy',
'loading',
'allowpaymentrequest',
'longdesc',
'title',
'class',
'id',
'style',
'tabindex',
'draggable',
'scrolling',
];

class Video extends BlockEmbed {
Expand Down
2 changes: 2 additions & 0 deletions packages/quill/src/quill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import Link from './formats/link.js';
import Script from './formats/script.js';
import Strike from './formats/strike.js';
import Underline from './formats/underline.js';
import ScriptTag from './formats/scriptTag.js';

import Formula from './formats/formula.js';
import Image from './formats/image.js';
Expand Down Expand Up @@ -98,6 +99,7 @@ Quill.register(
'formats/formula': Formula,
'formats/image': Image,
'formats/video': Video,
'formats/scriptTag': ScriptTag,

'modules/syntax': Syntax,
'modules/table': Table,
Expand Down

0 comments on commit 91dbff7

Please sign in to comment.