Initial commit as of 2018-10-16
0
assets/js/all-blog-tags.js
Normal file
304
assets/js/assets/base.styl
Normal file
@@ -0,0 +1,304 @@
|
||||
// Styles shared between snow and bubble
|
||||
|
||||
controlHeight = 24px
|
||||
inputPaddingWidth = 5px
|
||||
inputPaddingHeight = 3px
|
||||
|
||||
colorItemMargin = 2px
|
||||
colorItemSize = 16px
|
||||
colorItemsPerRow = 7
|
||||
|
||||
|
||||
.ql-{themeName}.ql-toolbar, .ql-{themeName} .ql-toolbar
|
||||
&:after
|
||||
clear: both
|
||||
content: ''
|
||||
display: table
|
||||
|
||||
button
|
||||
background: none
|
||||
border: none
|
||||
cursor: pointer
|
||||
display: inline-block
|
||||
float: left
|
||||
height: controlHeight
|
||||
padding: inputPaddingHeight inputPaddingWidth
|
||||
width: controlHeight + (inputPaddingWidth - inputPaddingHeight)*2
|
||||
|
||||
svg
|
||||
float: left
|
||||
height: 100%
|
||||
|
||||
&:active:hover
|
||||
outline: none
|
||||
|
||||
input.ql-image[type=file]
|
||||
display: none
|
||||
|
||||
button:hover, button:focus, button.ql-active,
|
||||
.ql-picker-label:hover, .ql-picker-label.ql-active,
|
||||
.ql-picker-item:hover, .ql-picker-item.ql-selected
|
||||
color: activeColor
|
||||
.ql-fill, .ql-stroke.ql-fill
|
||||
fill: activeColor
|
||||
.ql-stroke, .ql-stroke-miter
|
||||
stroke: activeColor
|
||||
|
||||
// Fix for iOS not losing hover on touch
|
||||
@media (pointer: coarse)
|
||||
.ql-{themeName}.ql-toolbar, .ql-{themeName} .ql-toolbar
|
||||
button:hover:not(.ql-active)
|
||||
color: inactiveColor
|
||||
.ql-fill, .ql-stroke.ql-fill
|
||||
fill: inactiveColor
|
||||
.ql-stroke, .ql-stroke-miter
|
||||
stroke: inactiveColor
|
||||
|
||||
.ql-{themeName}
|
||||
box-sizing: border-box
|
||||
*
|
||||
box-sizing: border-box
|
||||
|
||||
.ql-hidden
|
||||
display: none
|
||||
.ql-out-bottom, .ql-out-top
|
||||
visibility: hidden
|
||||
|
||||
.ql-tooltip
|
||||
position: absolute
|
||||
transform: translateY(10px)
|
||||
a
|
||||
cursor: pointer
|
||||
text-decoration: none
|
||||
.ql-tooltip.ql-flip
|
||||
transform: translateY(-10px)
|
||||
|
||||
.ql-formats
|
||||
&:after
|
||||
clear: both
|
||||
content: ''
|
||||
display: table
|
||||
display: inline-block
|
||||
vertical-align: middle
|
||||
|
||||
.ql-stroke
|
||||
fill: none
|
||||
stroke: inactiveColor
|
||||
stroke-linecap: round
|
||||
stroke-linejoin: round
|
||||
stroke-width: 2
|
||||
.ql-stroke-miter
|
||||
fill: none
|
||||
stroke: inactiveColor
|
||||
stroke-miterlimit: 10
|
||||
stroke-width: 2
|
||||
|
||||
.ql-fill, .ql-stroke.ql-fill
|
||||
fill: inactiveColor
|
||||
|
||||
.ql-empty
|
||||
fill: none
|
||||
.ql-even
|
||||
fill-rule: evenodd
|
||||
.ql-thin, .ql-stroke.ql-thin
|
||||
stroke-width: 1
|
||||
.ql-transparent
|
||||
opacity: 0.4
|
||||
|
||||
.ql-direction
|
||||
svg:last-child
|
||||
display: none
|
||||
.ql-direction.ql-active
|
||||
svg:last-child
|
||||
display: inline
|
||||
svg:first-child
|
||||
display: none
|
||||
|
||||
.ql-editor
|
||||
h1
|
||||
font-size: 2em
|
||||
h2
|
||||
font-size: 1.5em
|
||||
h3
|
||||
font-size: 1.17em
|
||||
h4
|
||||
font-size: 1em
|
||||
h5
|
||||
font-size: 0.83em
|
||||
h6
|
||||
font-size: 0.67em
|
||||
a
|
||||
text-decoration: underline
|
||||
blockquote
|
||||
border-left: 4px solid #ccc
|
||||
margin-bottom: 5px
|
||||
margin-top: 5px
|
||||
padding-left: 16px
|
||||
code, pre
|
||||
background-color: #f0f0f0
|
||||
border-radius: 3px
|
||||
pre
|
||||
white-space: pre-wrap
|
||||
margin-bottom: 5px
|
||||
margin-top: 5px
|
||||
padding: 5px 10px
|
||||
code
|
||||
font-size: 85%
|
||||
padding: 2px 4px
|
||||
pre.ql-syntax
|
||||
background-color: #23241f
|
||||
color: #f8f8f2;
|
||||
overflow: visible
|
||||
img
|
||||
max-width: 100%
|
||||
|
||||
.ql-picker
|
||||
color: inactiveColor
|
||||
display: inline-block
|
||||
float: left
|
||||
font-size: 14px
|
||||
font-weight: 500
|
||||
height: controlHeight
|
||||
position: relative
|
||||
vertical-align: middle
|
||||
.ql-picker-label
|
||||
cursor: pointer
|
||||
display: inline-block
|
||||
height: 100%
|
||||
padding-left: 8px
|
||||
padding-right: 2px
|
||||
position: relative
|
||||
width: 100%
|
||||
&::before
|
||||
display: inline-block
|
||||
line-height: 22px
|
||||
.ql-picker-options
|
||||
background-color: backgroundColor
|
||||
display: none
|
||||
min-width: 100%
|
||||
padding: 4px 8px
|
||||
position: absolute
|
||||
white-space: nowrap
|
||||
.ql-picker-item
|
||||
cursor: pointer
|
||||
display: block
|
||||
padding-bottom: 5px
|
||||
padding-top: 5px
|
||||
.ql-picker.ql-expanded
|
||||
.ql-picker-label
|
||||
color: borderColor
|
||||
z-index: 2
|
||||
.ql-fill
|
||||
fill: borderColor
|
||||
.ql-stroke
|
||||
stroke: borderColor
|
||||
.ql-picker-options
|
||||
display: block
|
||||
margin-top: -1px
|
||||
top: 100%
|
||||
z-index: 1
|
||||
|
||||
.ql-color-picker, .ql-icon-picker
|
||||
width: controlHeight + 4
|
||||
.ql-picker-label
|
||||
padding: 2px 4px
|
||||
svg
|
||||
right: 4px
|
||||
.ql-icon-picker
|
||||
.ql-picker-options
|
||||
padding: 4px 0px
|
||||
.ql-picker-item
|
||||
height: controlHeight
|
||||
width: controlHeight
|
||||
padding: 2px 4px
|
||||
.ql-color-picker
|
||||
.ql-picker-options
|
||||
padding: inputPaddingHeight inputPaddingWidth
|
||||
width: (colorItemSize + 2*colorItemMargin) * colorItemsPerRow + 2*inputPaddingWidth + 2 // +2 for the border
|
||||
.ql-picker-item
|
||||
border: 1px solid transparent
|
||||
float: left
|
||||
height: colorItemSize
|
||||
margin: colorItemMargin
|
||||
padding: 0px
|
||||
width: colorItemSize
|
||||
|
||||
.ql-picker:not(.ql-color-picker):not(.ql-icon-picker)
|
||||
svg
|
||||
position: absolute
|
||||
margin-top: -9px
|
||||
right: 0
|
||||
top: 50%
|
||||
width: 18px
|
||||
|
||||
.ql-picker.ql-header, .ql-picker.ql-font, .ql-picker.ql-size
|
||||
.ql-picker-label[data-label]:not([data-label='']),
|
||||
.ql-picker-item[data-label]:not([data-label=''])
|
||||
&::before
|
||||
content: attr(data-label)
|
||||
|
||||
.ql-picker.ql-header
|
||||
width: 98px
|
||||
.ql-picker-label::before,
|
||||
.ql-picker-item::before
|
||||
content: 'Normal'
|
||||
for num in (1..6)
|
||||
.ql-picker-label[data-value=\"{num}\"]::before,
|
||||
.ql-picker-item[data-value=\"{num}\"]::before
|
||||
content: 'Heading ' + num
|
||||
.ql-picker-item[data-value="1"]::before
|
||||
font-size: 2em
|
||||
.ql-picker-item[data-value="2"]::before
|
||||
font-size: 1.5em
|
||||
.ql-picker-item[data-value="3"]::before
|
||||
font-size: 1.17em
|
||||
.ql-picker-item[data-value="4"]::before
|
||||
font-size: 1em
|
||||
.ql-picker-item[data-value="5"]::before
|
||||
font-size: 0.83em
|
||||
.ql-picker-item[data-value="6"]::before
|
||||
font-size: 0.67em
|
||||
|
||||
.ql-picker.ql-font
|
||||
width: 108px
|
||||
.ql-picker-label::before,
|
||||
.ql-picker-item::before
|
||||
content: 'Sans Serif'
|
||||
.ql-picker-label[data-value=serif]::before,
|
||||
.ql-picker-item[data-value=serif]::before
|
||||
content: 'Serif'
|
||||
.ql-picker-label[data-value=monospace]::before,
|
||||
.ql-picker-item[data-value=monospace]::before
|
||||
content: 'Monospace'
|
||||
.ql-picker-item[data-value=serif]::before
|
||||
font-family: Georgia, Times New Roman, serif;
|
||||
.ql-picker-item[data-value=monospace]::before
|
||||
font-family: Monaco, Courier New, monospace;
|
||||
|
||||
.ql-picker.ql-size
|
||||
width: 98px
|
||||
.ql-picker-label::before,
|
||||
.ql-picker-item::before
|
||||
content: 'Normal'
|
||||
.ql-picker-label[data-value=small]::before,
|
||||
.ql-picker-item[data-value=small]::before
|
||||
content: 'Small'
|
||||
.ql-picker-label[data-value=large]::before,
|
||||
.ql-picker-item[data-value=large]::before
|
||||
content: 'Large'
|
||||
.ql-picker-label[data-value=huge]::before,
|
||||
.ql-picker-item[data-value=huge]::before
|
||||
content: 'Huge'
|
||||
.ql-picker-item[data-value=small]::before
|
||||
font-size: 10px
|
||||
.ql-picker-item[data-value=large]::before
|
||||
font-size: 18px
|
||||
.ql-picker-item[data-value=huge]::before
|
||||
font-size: 32px
|
||||
|
||||
.ql-color-picker.ql-background
|
||||
.ql-picker-item
|
||||
background-color: #fff;
|
||||
.ql-color-picker.ql-color
|
||||
.ql-picker-item
|
||||
background-color: #000;
|
45
assets/js/assets/bubble.styl
Normal file
@@ -0,0 +1,45 @@
|
||||
themeName = 'bubble'
|
||||
activeColor = #fff
|
||||
borderColor = #777
|
||||
backgroundColor = #444
|
||||
inactiveColor = #ccc
|
||||
shadowColor = #ddd
|
||||
textColor = #fff
|
||||
|
||||
@import './core'
|
||||
@import './base'
|
||||
@import './bubble/*'
|
||||
|
||||
.ql-container.ql-bubble:not(.ql-disabled)
|
||||
a
|
||||
position: relative
|
||||
white-space: nowrap
|
||||
a::before
|
||||
background-color: #444
|
||||
border-radius: 15px
|
||||
top: -5px
|
||||
font-size: 12px
|
||||
color: #fff
|
||||
content: attr(href)
|
||||
font-weight: normal
|
||||
overflow: hidden
|
||||
padding: 5px 15px
|
||||
text-decoration: none
|
||||
z-index: 1
|
||||
a::after
|
||||
border-top: 6px solid #444
|
||||
border-left: 6px solid transparent
|
||||
border-right: 6px solid transparent
|
||||
top: 0
|
||||
content: " "
|
||||
height: 0
|
||||
width: 0
|
||||
a::before, a::after
|
||||
left: 0
|
||||
margin-left: 50%
|
||||
position: absolute
|
||||
transform: translate(-50%, -100%)
|
||||
transition: visibility 0s ease 200ms
|
||||
visibility: hidden
|
||||
a:hover::before, a:hover::after
|
||||
visibility: visible
|
14
assets/js/assets/bubble/toolbar.styl
Normal file
@@ -0,0 +1,14 @@
|
||||
arrowWidth = 6px
|
||||
|
||||
.ql-bubble
|
||||
.ql-toolbar
|
||||
.ql-formats
|
||||
margin: 8px 12px 8px 0px
|
||||
.ql-formats:first-child
|
||||
margin-left: 12px
|
||||
|
||||
.ql-color-picker
|
||||
svg
|
||||
margin: 1px
|
||||
.ql-picker-item.ql-selected, .ql-picker-item:hover
|
||||
border-color: activeColor
|
49
assets/js/assets/bubble/tooltip.styl
Normal file
@@ -0,0 +1,49 @@
|
||||
arrowWidth = 6px
|
||||
|
||||
.ql-bubble
|
||||
.ql-tooltip
|
||||
background-color: backgroundColor
|
||||
border-radius: 25px
|
||||
color: textColor
|
||||
.ql-tooltip-arrow
|
||||
border-left: arrowWidth solid transparent
|
||||
border-right: arrowWidth solid transparent
|
||||
content: " "
|
||||
display: block
|
||||
left: 50%
|
||||
margin-left: -1 * arrowWidth
|
||||
position: absolute
|
||||
.ql-tooltip:not(.ql-flip) .ql-tooltip-arrow
|
||||
border-bottom: arrowWidth solid backgroundColor
|
||||
top: -1 * arrowWidth
|
||||
.ql-tooltip.ql-flip .ql-tooltip-arrow
|
||||
border-top: arrowWidth solid backgroundColor
|
||||
bottom: -1 * arrowWidth
|
||||
|
||||
.ql-tooltip.ql-editing
|
||||
.ql-tooltip-editor
|
||||
display: block
|
||||
.ql-formats
|
||||
visibility: hidden
|
||||
|
||||
.ql-tooltip-editor
|
||||
display: none
|
||||
input[type=text]
|
||||
background: transparent
|
||||
border: none
|
||||
color: textColor
|
||||
font-size: 13px
|
||||
height: 100%
|
||||
outline: none
|
||||
padding: 10px 20px
|
||||
position: absolute
|
||||
width: 100%
|
||||
a
|
||||
&:before
|
||||
color: inactiveColor
|
||||
content: "\00D7"
|
||||
font-size: 16px
|
||||
font-weight: bold
|
||||
top: 10px
|
||||
position: absolute
|
||||
right: 20px
|
183
assets/js/assets/core.styl
Normal file
@@ -0,0 +1,183 @@
|
||||
// Styles necessary for Quill
|
||||
|
||||
LIST_STYLE = decimal lower-alpha lower-roman
|
||||
LIST_STYLE_WIDTH = 1.2em
|
||||
LIST_STYLE_MARGIN = 0.3em
|
||||
LIST_STYLE_OUTER_WIDTH = LIST_STYLE_MARGIN + LIST_STYLE_WIDTH
|
||||
MAX_INDENT = 9
|
||||
|
||||
resets(arr)
|
||||
unquote('list-' + join(' list-', arr))
|
||||
|
||||
.ql-container
|
||||
box-sizing: border-box
|
||||
font-family: Helvetica, Arial, sans-serif
|
||||
font-size: 13px
|
||||
height: 100%
|
||||
margin: 0px
|
||||
position: relative
|
||||
|
||||
.ql-container.ql-disabled
|
||||
.ql-tooltip
|
||||
visibility: hidden
|
||||
.ql-editor
|
||||
ul[data-checked] > li::before
|
||||
pointer-events: none
|
||||
|
||||
.ql-clipboard
|
||||
left: -100000px
|
||||
height: 1px
|
||||
overflow-y: hidden
|
||||
position: absolute
|
||||
top: 50%
|
||||
p
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
.ql-editor
|
||||
box-sizing: border-box
|
||||
line-height: 1.42
|
||||
height: 100%
|
||||
outline: none
|
||||
overflow-y: auto
|
||||
padding: 12px 15px
|
||||
tab-size: 4
|
||||
-moz-tab-size: 4
|
||||
text-align: left
|
||||
white-space: pre-wrap
|
||||
word-wrap: break-word
|
||||
> *
|
||||
cursor: text
|
||||
|
||||
p, ol, ul, pre, blockquote, h1, h2, h3, h4, h5, h6
|
||||
margin: 0
|
||||
padding: 0
|
||||
counter-reset: resets(1..MAX_INDENT)
|
||||
ol, ul
|
||||
padding-left: 1.5em
|
||||
ol > li, ul > li
|
||||
list-style-type: none
|
||||
ul > li::before
|
||||
content: '\2022'
|
||||
ul[data-checked=true],
|
||||
ul[data-checked=false]
|
||||
pointer-events: none
|
||||
> li *
|
||||
pointer-events: all
|
||||
> li::before
|
||||
color: #777
|
||||
cursor: pointer
|
||||
pointer-events: all
|
||||
ul[data-checked=true] > li::before
|
||||
content: '\2611'
|
||||
ul[data-checked=false] > li::before
|
||||
content: '\2610'
|
||||
li::before
|
||||
display: inline-block
|
||||
white-space: nowrap
|
||||
width: LIST_STYLE_WIDTH
|
||||
li:not(.ql-direction-rtl)::before
|
||||
margin-left: -1*LIST_STYLE_OUTER_WIDTH
|
||||
margin-right: LIST_STYLE_MARGIN
|
||||
text-align: right
|
||||
li.ql-direction-rtl::before
|
||||
margin-left: LIST_STYLE_MARGIN
|
||||
margin-right: -1*LIST_STYLE_OUTER_WIDTH
|
||||
ol, ul
|
||||
li:not(.ql-direction-rtl)
|
||||
padding-left: LIST_STYLE_OUTER_WIDTH
|
||||
li.ql-direction-rtl
|
||||
padding-right: LIST_STYLE_OUTER_WIDTH
|
||||
ol
|
||||
li
|
||||
counter-reset: resets(1..MAX_INDENT)
|
||||
counter-increment: unquote('list-0')
|
||||
&:before
|
||||
content: unquote('counter(list-0, ' + LIST_STYLE[0] + ')') '. '
|
||||
for num in (1..MAX_INDENT)
|
||||
li.ql-indent-{num}
|
||||
counter-increment: unquote('list-' + num)
|
||||
&:before
|
||||
content: unquote('counter(list-' + num + ', ' + LIST_STYLE[num%3] + ')') '. '
|
||||
if (num < MAX_INDENT)
|
||||
li.ql-indent-{num}
|
||||
counter-reset: resets((num+1)..MAX_INDENT)
|
||||
for num in (1..MAX_INDENT)
|
||||
.ql-indent-{num}:not(.ql-direction-rtl)
|
||||
padding-left: (3*num)em
|
||||
li.ql-indent-{num}:not(.ql-direction-rtl)
|
||||
padding-left: (3*num + LIST_STYLE_OUTER_WIDTH)em
|
||||
.ql-indent-{num}.ql-direction-rtl.ql-align-right
|
||||
padding-right: (3*num)em
|
||||
li.ql-indent-{num}.ql-direction-rtl.ql-align-right
|
||||
padding-right: (3*num + LIST_STYLE_OUTER_WIDTH)em
|
||||
|
||||
.ql-video
|
||||
display: block
|
||||
max-width: 100%
|
||||
.ql-video.ql-align-center
|
||||
margin: 0 auto
|
||||
.ql-video.ql-align-right
|
||||
margin: 0 0 0 auto
|
||||
|
||||
.ql-bg-black
|
||||
background-color: rgb(0,0,0)
|
||||
.ql-bg-red
|
||||
background-color: rgb(230,0,0)
|
||||
.ql-bg-orange
|
||||
background-color: rgb(255,153,0)
|
||||
.ql-bg-yellow
|
||||
background-color: rgb(255,255,0)
|
||||
.ql-bg-green
|
||||
background-color: rgb(0,138,0)
|
||||
.ql-bg-blue
|
||||
background-color: rgb(0,102,204)
|
||||
.ql-bg-purple
|
||||
background-color: rgb(153,51,255)
|
||||
|
||||
.ql-color-white
|
||||
color: rgb(255,255,255)
|
||||
.ql-color-red
|
||||
color: rgb(230,0,0)
|
||||
.ql-color-orange
|
||||
color: rgb(255,153,0)
|
||||
.ql-color-yellow
|
||||
color: rgb(255,255,0)
|
||||
.ql-color-green
|
||||
color: rgb(0,138,0)
|
||||
.ql-color-blue
|
||||
color: rgb(0,102,204)
|
||||
.ql-color-purple
|
||||
color: rgb(153,51,255)
|
||||
|
||||
.ql-font-serif
|
||||
font-family: Georgia, Times New Roman, serif
|
||||
.ql-font-monospace
|
||||
font-family: Monaco, Courier New, monospace
|
||||
|
||||
.ql-size-small
|
||||
font-size: 0.75em
|
||||
.ql-size-large
|
||||
font-size: 1.5em
|
||||
.ql-size-huge
|
||||
font-size: 2.5em
|
||||
|
||||
.ql-direction-rtl
|
||||
direction: rtl
|
||||
text-align: inherit
|
||||
|
||||
.ql-align-center
|
||||
text-align: center
|
||||
.ql-align-justify
|
||||
text-align: justify
|
||||
.ql-align-right
|
||||
text-align: right
|
||||
|
||||
.ql-editor.ql-blank::before
|
||||
color: rgba(0,0,0,0.6)
|
||||
content: attr(data-placeholder)
|
||||
font-style: italic
|
||||
left: 15px
|
||||
pointer-events: none
|
||||
position: absolute
|
||||
right: 15px
|
BIN
assets/js/assets/favicon.png
Normal file
After Width: | Height: | Size: 696 B |
5
assets/js/assets/icons/align-center.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="15" x2="3" y1="9" y2="9"></line>
|
||||
<line class="ql-stroke" x1="14" x2="4" y1="14" y2="14"></line>
|
||||
<line class="ql-stroke" x1="12" x2="6" y1="4" y2="4"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 223 B |
5
assets/js/assets/icons/align-justify.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="15" x2="3" y1="9" y2="9"></line>
|
||||
<line class="ql-stroke" x1="15" x2="3" y1="14" y2="14"></line>
|
||||
<line class="ql-stroke" x1="15" x2="3" y1="4" y2="4"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 223 B |
5
assets/js/assets/icons/align-left.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="3" x2="15" y1="9" y2="9"></line>
|
||||
<line class="ql-stroke" x1="3" x2="13" y1="14" y2="14"></line>
|
||||
<line class="ql-stroke" x1="3" x2="9" y1="4" y2="4"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 222 B |
5
assets/js/assets/icons/align-right.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="15" x2="3" y1="9" y2="9"></line>
|
||||
<line class="ql-stroke" x1="15" x2="5" y1="14" y2="14"></line>
|
||||
<line class="ql-stroke" x1="15" x2="9" y1="4" y2="4"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 223 B |
3
assets/js/assets/icons/attachment.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-stroke" d="M6.6,11.4L9,9a1.456,1.456,0,0,1,2.059,2.059L7.971,14.147a2.912,2.912,0,0,1-4.118-4.118l6.177-6.177a2.912,2.912,0,0,1,4.118,4.118"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 199 B |
5
assets/js/assets/icons/audio.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<ellipse class="ql-fill" cx="10.5" cy="14" rx="2.5" ry="2"></ellipse>
|
||||
<path class="ql-stroke" d="M12,14V3c0,1.5,3,2.021,3,5"></path>
|
||||
<path class="ql-fill" d="M7,4A5,5,0,0,0,7,14a3.191,3.191,0,0,1,3-2.957V5.023A4.955,4.955,0,0,0,7,4ZM4.06,8.412a0.5,0.5,0,0,1-.49.4,0.485,0.485,0,0,1-.1-0.01,0.5,0.5,0,0,1-.393-0.588A3.98,3.98,0,0,1,6.216,5.079a0.5,0.5,0,0,1,.2.98A2.985,2.985,0,0,0,4.06,8.412ZM7,10A1,1,0,1,1,8,9,1,1,0,0,1,7,10Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 475 B |
5
assets/js/assets/icons/authorship.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="3" x2="15" y1="15" y2="15"></line>
|
||||
<path class="ql-fill ql-stroke" d="M9,8H9a3,3,0,0,1,3,3v0a0,0,0,0,1,0,0H6a0,0,0,0,1,0,0v0A3,3,0,0,1,9,8Z"></path>
|
||||
<path class="ql-even ql-fill" d="M11,5.01C11,6.021,10,9,9,9S7,6.021,7,5.01c0-1.651.292-2.99,2-2.99S11,3.359,11,5.01Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 341 B |
52
assets/js/assets/icons/background.svg
Normal file
@@ -0,0 +1,52 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<g class="ql-fill ql-color-label">
|
||||
<polygon points="6 6.868 6 6 5 6 5 7 5.942 7 6 6.868"></polygon>
|
||||
<rect height="1" width="1" x="4" y="4"></rect>
|
||||
<polygon points="6.817 5 6 5 6 6 6.38 6 6.817 5"></polygon>
|
||||
<rect height="1" width="1" x="2" y="6"></rect>
|
||||
<rect height="1" width="1" x="3" y="5"></rect>
|
||||
<rect height="1" width="1" x="4" y="7"></rect>
|
||||
<polygon points="4 11.439 4 11 3 11 3 12 3.755 12 4 11.439"></polygon>
|
||||
<rect height="1" width="1" x="2" y="12"></rect>
|
||||
<rect height="1" width="1" x="2" y="9"></rect>
|
||||
<rect height="1" width="1" x="2" y="15"></rect>
|
||||
<polygon points="4.63 10 4 10 4 11 4.192 11 4.63 10"></polygon>
|
||||
<rect height="1" width="1" x="3" y="8"></rect>
|
||||
<path d="M10.832,4.2L11,4.582V4H10.708A1.948,1.948,0,0,1,10.832,4.2Z"></path>
|
||||
<path d="M7,4.582L7.168,4.2A1.929,1.929,0,0,1,7.292,4H7V4.582Z"></path>
|
||||
<path d="M8,13H7.683l-0.351.8a1.933,1.933,0,0,1-.124.2H8V13Z"></path>
|
||||
<rect height="1" width="1" x="12" y="2"></rect>
|
||||
<rect height="1" width="1" x="11" y="3"></rect>
|
||||
<path d="M9,3H8V3.282A1.985,1.985,0,0,1,9,3Z"></path>
|
||||
<rect height="1" width="1" x="2" y="3"></rect>
|
||||
<rect height="1" width="1" x="6" y="2"></rect>
|
||||
<rect height="1" width="1" x="3" y="2"></rect>
|
||||
<rect height="1" width="1" x="5" y="3"></rect>
|
||||
<rect height="1" width="1" x="9" y="2"></rect>
|
||||
<rect height="1" width="1" x="15" y="14"></rect>
|
||||
<polygon points="13.447 10.174 13.469 10.225 13.472 10.232 13.808 11 14 11 14 10 13.37 10 13.447 10.174"></polygon>
|
||||
<rect height="1" width="1" x="13" y="7"></rect>
|
||||
<rect height="1" width="1" x="15" y="5"></rect>
|
||||
<rect height="1" width="1" x="14" y="6"></rect>
|
||||
<rect height="1" width="1" x="15" y="8"></rect>
|
||||
<rect height="1" width="1" x="14" y="9"></rect>
|
||||
<path d="M3.775,14H3v1H4V14.314A1.97,1.97,0,0,1,3.775,14Z"></path>
|
||||
<rect height="1" width="1" x="14" y="3"></rect>
|
||||
<polygon points="12 6.868 12 6 11.62 6 12 6.868"></polygon>
|
||||
<rect height="1" width="1" x="15" y="2"></rect>
|
||||
<rect height="1" width="1" x="12" y="5"></rect>
|
||||
<rect height="1" width="1" x="13" y="4"></rect>
|
||||
<polygon points="12.933 9 13 9 13 8 12.495 8 12.933 9"></polygon>
|
||||
<rect height="1" width="1" x="9" y="14"></rect>
|
||||
<rect height="1" width="1" x="8" y="15"></rect>
|
||||
<path d="M6,14.926V15H7V14.316A1.993,1.993,0,0,1,6,14.926Z"></path>
|
||||
<rect height="1" width="1" x="5" y="15"></rect>
|
||||
<path d="M10.668,13.8L10.317,13H10v1h0.792A1.947,1.947,0,0,1,10.668,13.8Z"></path>
|
||||
<rect height="1" width="1" x="11" y="15"></rect>
|
||||
<path d="M14.332,12.2a1.99,1.99,0,0,1,.166.8H15V12H14.245Z"></path>
|
||||
<rect height="1" width="1" x="14" y="15"></rect>
|
||||
<rect height="1" width="1" x="15" y="11"></rect>
|
||||
</g>
|
||||
<polyline class="ql-stroke" points="5.5 13 9 5 12.5 13"></polyline>
|
||||
<line class="ql-stroke" x1="11.63" x2="6.38" y1="11" y2="11"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
6
assets/js/assets/icons/blockquote.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<rect class="ql-fill ql-stroke" height="3" width="3" x="4" y="5"></rect>
|
||||
<rect class="ql-fill ql-stroke" height="3" width="3" x="11" y="5"></rect>
|
||||
<path class="ql-even ql-fill ql-stroke" d="M7,8c0,4.031-3,5-3,5"></path>
|
||||
<path class="ql-even ql-fill ql-stroke" d="M14,8c0,4.031-3,5-3,5"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 334 B |
4
assets/js/assets/icons/bold.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-stroke" d="M5,4H9.5A2.5,2.5,0,0,1,12,6.5v0A2.5,2.5,0,0,1,9.5,9H5A0,0,0,0,1,5,9V4A0,0,0,0,1,5,4Z"></path>
|
||||
<path class="ql-stroke" d="M5,9h5.5A2.5,2.5,0,0,1,13,11.5v0A2.5,2.5,0,0,1,10.5,14H5a0,0,0,0,1,0,0V9A0,0,0,0,1,5,9Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 281 B |
7
assets/js/assets/icons/clean.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg class="" viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="5" x2="13" y1="3" y2="3"></line>
|
||||
<line class="ql-stroke" x1="6" x2="9.35" y1="12" y2="3"></line>
|
||||
<line class="ql-stroke" x1="11" x2="15" y1="11" y2="15"></line>
|
||||
<line class="ql-stroke" x1="15" x2="11" y1="11" y2="15"></line>
|
||||
<rect class="ql-fill" height="1" rx="0.5" ry="0.5" width="7" x="2" y="14"></rect>
|
||||
</svg>
|
After Width: | Height: | Size: 386 B |
5
assets/js/assets/icons/code.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<polyline class="ql-even ql-stroke" points="5 7 3 9 5 11"></polyline>
|
||||
<polyline class="ql-even ql-stroke" points="13 7 15 9 13 11"></polyline>
|
||||
<line class="ql-stroke" x1="10" x2="8" y1="5" y2="13"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 243 B |
5
assets/js/assets/icons/color.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-color-label ql-stroke ql-transparent" x1="3" x2="15" y1="15" y2="15"></line>
|
||||
<polyline class="ql-stroke" points="5.5 11 9 3 12.5 11"></polyline>
|
||||
<line class="ql-stroke" x1="11.63" x2="6.38" y1="9" y2="9"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 266 B |
3
assets/js/assets/icons/comment.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-stroke" d="M9,3C5.686,3,3,5.239,3,8a4.669,4.669,0,0,0,2,3.719V15l3.094-2.063A7.186,7.186,0,0,0,9,13c3.314,0,6-2.239,6-5S12.314,3,9,3Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 193 B |
7
assets/js/assets/icons/direction-ltr.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<polygon class="ql-stroke ql-fill" points="3 11 5 9 3 7 3 11"></polygon>
|
||||
<line class="ql-stroke ql-fill" x1="15" x2="11" y1="4" y2="4"></line>
|
||||
<path class="ql-fill" d="M11,3a3,3,0,0,0,0,6h1V3H11Z"></path>
|
||||
<rect class="ql-fill" height="11" width="1" x="11" y="4"></rect>
|
||||
<rect class="ql-fill" height="11" width="1" x="13" y="4"></rect>
|
||||
</svg>
|
After Width: | Height: | Size: 377 B |
7
assets/js/assets/icons/direction-rtl.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<polygon class="ql-stroke ql-fill" points="15 12 13 10 15 8 15 12"></polygon>
|
||||
<line class="ql-stroke ql-fill" x1="9" x2="5" y1="4" y2="4"></line>
|
||||
<path class="ql-fill" d="M5,3A3,3,0,0,0,5,9H6V3H5Z"></path>
|
||||
<rect class="ql-fill" height="11" width="1" x="5" y="4"></rect>
|
||||
<rect class="ql-fill" height="11" width="1" x="7" y="4"></rect>
|
||||
</svg>
|
After Width: | Height: | Size: 376 B |
4
assets/js/assets/icons/dropdown.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<polygon class="ql-stroke" points="7 11 9 13 11 11 7 11"></polygon>
|
||||
<polygon class="ql-stroke" points="7 7 9 5 11 7 7 7"></polygon>
|
||||
</svg>
|
After Width: | Height: | Size: 168 B |
3
assets/js/assets/icons/embed.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 18 18">
|
||||
<path class="ql-fill" d="M14.60284,8.5036A2.48315,2.48315,0,0,0,14.71429,7.8a2.34494,2.34494,0,0,0-2.28571-2.4,2.19651,2.19651,0,0,0-1.3631.48358A4.01147,4.01147,0,0,0,7.28571,3a4.1037,4.1037,0,0,0-4,4.2,4.40682,4.40682,0,0,0,.07642.79553A3.57444,3.57444,0,0,0,1,11.4,3.51743,3.51743,0,0,0,4.42859,15h9.42853A3.22436,3.22436,0,0,0,17,11.7,3.26609,3.26609,0,0,0,14.60284,8.5036ZM6.85352,10.64648a.49995.49995,0,1,1-.707.707l-1-1a.49982.49982,0,0,1,0-.707l1-1a.49995.49995,0,0,1,.707.707L6.207,10ZM9.48535,8.12109l-1,4A.5.5,0,0,1,8,12.5a.51952.51952,0,0,1-.12109-.01465.50066.50066,0,0,1-.36426-.60645l1-4a.50023.50023,0,0,1,.9707.24219Zm2.36816,2.23242-1,1a.49995.49995,0,0,1-.707-.707L10.793,10l-.64648-.64648a.49995.49995,0,0,1,.707-.707l1,1A.49982.49982,0,0,1,11.85352,10.35352Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 818 B |
6
assets/js/assets/icons/emoji.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<circle class="ql-fill" cx="7" cy="7" r="1"></circle>
|
||||
<circle class="ql-fill" cx="11" cy="7" r="1"></circle>
|
||||
<path class="ql-stroke" d="M7,10a2,2,0,0,0,4,0H7Z"></path>
|
||||
<circle class="ql-stroke" cx="9" cy="9" r="6"></circle>
|
||||
</svg>
|
After Width: | Height: | Size: 264 B |
5
assets/js/assets/icons/float-center.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-fill" d="M14,16H4a1,1,0,0,1,0-2H14A1,1,0,0,1,14,16Z"/>
|
||||
<path class="ql-fill" d="M14,4H4A1,1,0,0,1,4,2H14A1,1,0,0,1,14,4Z"/>
|
||||
<rect class="ql-fill" x="3" y="6" width="12" height="6" rx="1" ry="1"/>
|
||||
</svg>
|
After Width: | Height: | Size: 250 B |
5
assets/js/assets/icons/float-full.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-fill" d="M13,16H5a1,1,0,0,1,0-2h8A1,1,0,0,1,13,16Z"/>
|
||||
<path class="ql-fill" d="M13,4H5A1,1,0,0,1,5,2h8A1,1,0,0,1,13,4Z"/>
|
||||
<rect class="ql-fill" x="2" y="6" width="14" height="6" rx="1" ry="1"/>
|
||||
</svg>
|
After Width: | Height: | Size: 248 B |
7
assets/js/assets/icons/float-left.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-fill" d="M15,8H13a1,1,0,0,1,0-2h2A1,1,0,0,1,15,8Z"/>
|
||||
<path class="ql-fill" d="M15,12H13a1,1,0,0,1,0-2h2A1,1,0,0,1,15,12Z"/>
|
||||
<path class="ql-fill" d="M15,16H5a1,1,0,0,1,0-2H15A1,1,0,0,1,15,16Z"/>
|
||||
<path class="ql-fill" d="M15,4H5A1,1,0,0,1,5,2H15A1,1,0,0,1,15,4Z"/>
|
||||
<rect class="ql-fill" x="2" y="6" width="8" height="6" rx="1" ry="1"/>
|
||||
</svg>
|
After Width: | Height: | Size: 393 B |
7
assets/js/assets/icons/float-right.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-fill" d="M5,8H3A1,1,0,0,1,3,6H5A1,1,0,0,1,5,8Z"/>
|
||||
<path class="ql-fill" d="M5,12H3a1,1,0,0,1,0-2H5A1,1,0,0,1,5,12Z"/>
|
||||
<path class="ql-fill" d="M13,16H3a1,1,0,0,1,0-2H13A1,1,0,0,1,13,16Z"/>
|
||||
<path class="ql-fill" d="M13,4H3A1,1,0,0,1,3,2H13A1,1,0,0,1,13,4Z"/>
|
||||
<rect class="ql-fill" x="8" y="6" width="8" height="6" rx="1" ry="1" transform="translate(24 18) rotate(-180)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 429 B |
5
assets/js/assets/icons/font.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<polyline class="ql-stroke" points="3.5 14 7 4 10.5 14"></polyline>
|
||||
<line class="ql-stroke" x1="9.45" x2="4.55" y1="11" y2="11"></line>
|
||||
<path class="ql-fill" d="M13.636,5.013a4.016,4.016,0,0,0-1.863.472,0.42,0.42,0,0,0-.179.629l0.112,0.214a0.418,0.418,0,0,0,.625.191,2.557,2.557,0,0,1,1.183-.326A0.933,0.933,0,0,1,14.573,7.2V7.338H14.339c-1.272,0-3.325.281-3.325,1.954A1.75,1.75,0,0,0,12.9,11.011a2.072,2.072,0,0,0,1.785-1.078h0.022a1.132,1.132,0,0,0-.022.247V10.4a0.412,0.412,0,0,0,.457.472h0.379A0.416,0.416,0,0,0,15.99,10.4V7.293A2.121,2.121,0,0,0,13.636,5.013Zm0.948,3.4a1.452,1.452,0,0,1-1.305,1.505,0.775,0.775,0,0,1-.859-0.753c0-.854,1.216-0.966,1.93-0.966h0.234V8.416Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 724 B |
5
assets/js/assets/icons/formula.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-fill" d="M11.759,2.482a2.561,2.561,0,0,0-3.53.607A7.656,7.656,0,0,0,6.8,6.2C6.109,9.188,5.275,14.677,4.15,14.927a1.545,1.545,0,0,0-1.3-.933A0.922,0.922,0,0,0,2,15.036S1.954,16,4.119,16s3.091-2.691,3.7-5.553c0.177-.826.36-1.726,0.554-2.6L8.775,6.2c0.381-1.421.807-2.521,1.306-2.676a1.014,1.014,0,0,0,1.02.56A0.966,0.966,0,0,0,11.759,2.482Z"></path>
|
||||
<rect class="ql-fill" height="1.6" rx="0.8" ry="0.8" width="5" x="5.15" y="6.2"></rect>
|
||||
<path class="ql-fill" d="M13.663,12.027a1.662,1.662,0,0,1,.266-0.276q0.193,0.069.456,0.138a2.1,2.1,0,0,0,.535.069,1.075,1.075,0,0,0,.767-0.3,1.044,1.044,0,0,0,.314-0.8,0.84,0.84,0,0,0-.238-0.619,0.8,0.8,0,0,0-.594-0.239,1.154,1.154,0,0,0-.781.3,4.607,4.607,0,0,0-.781,1q-0.091.15-.218,0.346l-0.246.38c-0.068-.288-0.137-0.582-0.212-0.885-0.459-1.847-2.494-.984-2.941-0.8-0.482.2-.353,0.647-0.094,0.529a0.869,0.869,0,0,1,1.281.585c0.217,0.751.377,1.436,0.527,2.038a5.688,5.688,0,0,1-.362.467,2.69,2.69,0,0,1-.264.271q-0.221-.08-0.471-0.147a2.029,2.029,0,0,0-.522-0.066,1.079,1.079,0,0,0-.768.3A1.058,1.058,0,0,0,9,15.131a0.82,0.82,0,0,0,.832.852,1.134,1.134,0,0,0,.787-0.3,5.11,5.11,0,0,0,.776-0.993q0.141-.219.215-0.34c0.046-.076.122-0.194,0.223-0.346a2.786,2.786,0,0,0,.918,1.726,2.582,2.582,0,0,0,2.376-.185c0.317-.181.212-0.565,0-0.494A0.807,0.807,0,0,1,14.176,15a5.159,5.159,0,0,1-.913-2.446l0,0Q13.487,12.24,13.663,12.027Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
6
assets/js/assets/icons/hashtag.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="7" x2="6" y1="3" y2="15"></line>
|
||||
<line class="ql-stroke" x1="12" x2="11" y1="3" y2="15"></line>
|
||||
<line class="ql-stroke" x1="3.75" x2="14.75" y1="7" y2="7"></line>
|
||||
<line class="ql-stroke" x1="3.25" x2="14.25" y1="11" y2="11"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 300 B |
3
assets/js/assets/icons/header-2.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 18 18">
|
||||
<path class="ql-fill" d="M16.73975,13.81445v.43945a.54085.54085,0,0,1-.605.60547H11.855a.58392.58392,0,0,1-.64893-.60547V14.0127c0-2.90527,3.39941-3.42187,3.39941-4.55469a.77675.77675,0,0,0-.84717-.78125,1.17684,1.17684,0,0,0-.83594.38477c-.2749.26367-.561.374-.85791.13184l-.4292-.34082c-.30811-.24219-.38525-.51758-.1543-.81445a2.97155,2.97155,0,0,1,2.45361-1.17676,2.45393,2.45393,0,0,1,2.68408,2.40918c0,2.45312-3.1792,2.92676-3.27832,3.93848h2.79443A.54085.54085,0,0,1,16.73975,13.81445ZM9,3A.99974.99974,0,0,0,8,4V8H3V4A1,1,0,0,0,1,4V14a1,1,0,0,0,2,0V10H8v4a1,1,0,0,0,2,0V4A.99974.99974,0,0,0,9,3Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 641 B |
3
assets/js/assets/icons/header-3.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 18 18">
|
||||
<path class="ql-fill" d="M16.65186,12.30664a2.6742,2.6742,0,0,1-2.915,2.68457,3.96592,3.96592,0,0,1-2.25537-.6709.56007.56007,0,0,1-.13232-.83594L11.64648,13c.209-.34082.48389-.36328.82471-.1543a2.32654,2.32654,0,0,0,1.12256.33008c.71484,0,1.12207-.35156,1.12207-.78125,0-.61523-.61621-.86816-1.46338-.86816H13.2085a.65159.65159,0,0,1-.68213-.41895l-.05518-.10937a.67114.67114,0,0,1,.14307-.78125l.71533-.86914a8.55289,8.55289,0,0,1,.68213-.7373V8.58887a3.93913,3.93913,0,0,1-.748.05469H11.9873a.54085.54085,0,0,1-.605-.60547V7.59863a.54085.54085,0,0,1,.605-.60547h3.75146a.53773.53773,0,0,1,.60547.59375v.17676a1.03723,1.03723,0,0,1-.27539.748L14.74854,10.0293A2.31132,2.31132,0,0,1,16.65186,12.30664ZM9,3A.99974.99974,0,0,0,8,4V8H3V4A1,1,0,0,0,1,4V14a1,1,0,0,0,2,0V10H8v4a1,1,0,0,0,2,0V4A.99974.99974,0,0,0,9,3Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 851 B |
3
assets/js/assets/icons/header-4.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 18 18">
|
||||
<path class="ql-fill" d="M10,4V14a1,1,0,0,1-2,0V10H3v4a1,1,0,0,1-2,0V4A1,1,0,0,1,3,4V8H8V4a1,1,0,0,1,2,0Zm7.05371,7.96582v.38477c0,.39648-.165.60547-.46191.60547h-.47314v1.29785a.54085.54085,0,0,1-.605.60547h-.69336a.54085.54085,0,0,1-.605-.60547V12.95605H11.333a.5412.5412,0,0,1-.60547-.60547v-.15332a1.199,1.199,0,0,1,.22021-.748l2.56348-4.05957a.7819.7819,0,0,1,.72607-.39648h1.27637a.54085.54085,0,0,1,.605.60547v3.7627h.33008A.54055.54055,0,0,1,17.05371,11.96582ZM14.28125,8.7207h-.022a4.18969,4.18969,0,0,1-.38525.81348l-1.188,1.80469v.02246h1.5293V9.60059A7.04058,7.04058,0,0,1,14.28125,8.7207Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 639 B |
3
assets/js/assets/icons/header-5.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 18 18">
|
||||
<path class="ql-fill" d="M16.74023,12.18555a2.75131,2.75131,0,0,1-2.91553,2.80566,3.908,3.908,0,0,1-2.25537-.68164.54809.54809,0,0,1-.13184-.8252L11.73438,13c.209-.34082.48389-.36328.8252-.1543a2.23757,2.23757,0,0,0,1.1001.33008,1.01827,1.01827,0,0,0,1.1001-.96777c0-.61621-.53906-.97949-1.25439-.97949a2.15554,2.15554,0,0,0-.64893.09961,1.15209,1.15209,0,0,1-.814.01074l-.12109-.04395a.64116.64116,0,0,1-.45117-.71484l.231-3.00391a.56666.56666,0,0,1,.62744-.583H15.541a.54085.54085,0,0,1,.605.60547v.43945a.54085.54085,0,0,1-.605.60547H13.41748l-.04395.72559a1.29306,1.29306,0,0,1-.04395.30859h.022a2.39776,2.39776,0,0,1,.57227-.07715A2.53266,2.53266,0,0,1,16.74023,12.18555ZM9,3A.99974.99974,0,0,0,8,4V8H3V4A1,1,0,0,0,1,4V14a1,1,0,0,0,2,0V10H8v4a1,1,0,0,0,2,0V4A.99974.99974,0,0,0,9,3Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 825 B |
3
assets/js/assets/icons/header-6.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 18 18">
|
||||
<path class="ql-fill" d="M14.51758,9.64453a1.85627,1.85627,0,0,0-1.24316.38477H13.252a1.73532,1.73532,0,0,1,1.72754-1.4082,2.66491,2.66491,0,0,1,.5498.06641c.35254.05469.57227.01074.70508-.40723l.16406-.5166a.53393.53393,0,0,0-.373-.75977,4.83723,4.83723,0,0,0-1.17773-.14258c-2.43164,0-3.7627,2.17773-3.7627,4.43359,0,2.47559,1.60645,3.69629,3.19043,3.69629A2.70585,2.70585,0,0,0,16.96,12.19727,2.43861,2.43861,0,0,0,14.51758,9.64453Zm-.23047,3.58691c-.67187,0-1.22168-.81445-1.22168-1.45215,0-.47363.30762-.583.72559-.583.96875,0,1.27734.59375,1.27734,1.12207A.82182.82182,0,0,1,14.28711,13.23145ZM10,4V14a1,1,0,0,1-2,0V10H3v4a1,1,0,0,1-2,0V4A1,1,0,0,1,3,4V8H8V4a1,1,0,0,1,2,0Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 717 B |
3
assets/js/assets/icons/header.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 18 18">
|
||||
<path class="ql-fill" d="M10,4V14a1,1,0,0,1-2,0V10H3v4a1,1,0,0,1-2,0V4A1,1,0,0,1,3,4V8H8V4a1,1,0,0,1,2,0Zm6.06787,9.209H14.98975V7.59863a.54085.54085,0,0,0-.605-.60547h-.62744a1.01119,1.01119,0,0,0-.748.29688L11.645,8.56641a.5435.5435,0,0,0-.022.8584l.28613.30762a.53861.53861,0,0,0,.84717.0332l.09912-.08789a1.2137,1.2137,0,0,0,.2417-.35254h.02246s-.01123.30859-.01123.60547V13.209H12.041a.54085.54085,0,0,0-.605.60547v.43945a.54085.54085,0,0,0,.605.60547h4.02686a.54085.54085,0,0,0,.605-.60547v-.43945A.54085.54085,0,0,0,16.06787,13.209Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 577 B |
4
assets/js/assets/icons/horizontal-rule.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewBox="0 0 18 18">
|
||||
<path class="ql-fill" d="M15,12v2a.99942.99942,0,0,1-1,1H4a.99942.99942,0,0,1-1-1V12a1,1,0,0,1,2,0v1h8V12a1,1,0,0,1,2,0ZM14,3H4A.99942.99942,0,0,0,3,4V6A1,1,0,0,0,5,6V5h8V6a1,1,0,0,0,2,0V4A.99942.99942,0,0,0,14,3Z"/>
|
||||
<path class="ql-fill" d="M15,10H3A1,1,0,0,1,3,8H15a1,1,0,0,1,0,2Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 322 B |
5
assets/js/assets/icons/image.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<rect class="ql-stroke" height="10" width="12" x="3" y="4"></rect>
|
||||
<circle class="ql-fill" cx="6" cy="7" r="1"></circle>
|
||||
<polyline class="ql-even ql-fill" points="5 12 5 11 7 9 8 10 11 7 13 9 13 12 5 12"></polyline>
|
||||
</svg>
|
After Width: | Height: | Size: 254 B |
6
assets/js/assets/icons/indent.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="3" x2="15" y1="14" y2="14"></line>
|
||||
<line class="ql-stroke" x1="3" x2="15" y1="4" y2="4"></line>
|
||||
<line class="ql-stroke" x1="9" x2="15" y1="9" y2="9"></line>
|
||||
<polyline class="ql-fill ql-stroke" points="3 7 3 11 5 9 3 7"></polyline>
|
||||
</svg>
|
After Width: | Height: | Size: 299 B |
5
assets/js/assets/icons/italic.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="7" x2="13" y1="4" y2="4"></line>
|
||||
<line class="ql-stroke" x1="5" x2="11" y1="14" y2="14"></line>
|
||||
<line class="ql-stroke" x1="8" x2="10" y1="14" y2="4"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 224 B |
5
assets/js/assets/icons/link.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="7" x2="11" y1="7" y2="11"></line>
|
||||
<path class="ql-even ql-stroke" d="M8.9,4.577a3.476,3.476,0,0,1,.36,4.679A3.476,3.476,0,0,1,4.577,8.9C3.185,7.5,2.035,6.4,4.217,4.217S7.5,3.185,8.9,4.577Z"></path>
|
||||
<path class="ql-even ql-stroke" d="M13.423,9.1a3.476,3.476,0,0,0-4.679-.36,3.476,3.476,0,0,0,.36,4.679c1.392,1.392,2.5,2.542,4.679.36S14.815,10.5,13.423,9.1Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
8
assets/js/assets/icons/list-bullet.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="6" x2="15" y1="4" y2="4"></line>
|
||||
<line class="ql-stroke" x1="6" x2="15" y1="9" y2="9"></line>
|
||||
<line class="ql-stroke" x1="6" x2="15" y1="14" y2="14"></line>
|
||||
<line class="ql-stroke" x1="3" x2="3" y1="4" y2="4"></line>
|
||||
<line class="ql-stroke" x1="3" x2="3" y1="9" y2="9"></line>
|
||||
<line class="ql-stroke" x1="3" x2="3" y1="14" y2="14"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 411 B |
8
assets/js/assets/icons/list-check.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg class="" viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="9" x2="15" y1="4" y2="4"></line>
|
||||
<polyline class="ql-stroke" points="3 4 4 5 6 3"></polyline>
|
||||
<line class="ql-stroke" x1="9" x2="15" y1="14" y2="14"></line>
|
||||
<polyline class="ql-stroke" points="3 14 4 15 6 13"></polyline>
|
||||
<line class="ql-stroke" x1="9" x2="15" y1="9" y2="9"></line>
|
||||
<polyline class="ql-stroke" points="3 9 4 10 6 8"></polyline>
|
||||
</svg>
|
After Width: | Height: | Size: 425 B |
9
assets/js/assets/icons/list-ordered.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="7" x2="15" y1="4" y2="4"></line>
|
||||
<line class="ql-stroke" x1="7" x2="15" y1="9" y2="9"></line>
|
||||
<line class="ql-stroke" x1="7" x2="15" y1="14" y2="14"></line>
|
||||
<line class="ql-stroke ql-thin" x1="2.5" x2="4.5" y1="5.5" y2="5.5"></line>
|
||||
<path class="ql-fill" d="M3.5,6A0.5,0.5,0,0,1,3,5.5V3.085l-0.276.138A0.5,0.5,0,0,1,2.053,3c-0.124-.247-0.023-0.324.224-0.447l1-.5A0.5,0.5,0,0,1,4,2.5v3A0.5,0.5,0,0,1,3.5,6Z"></path>
|
||||
<path class="ql-stroke ql-thin" d="M4.5,10.5h-2c0-.234,1.85-1.076,1.85-2.234A0.959,0.959,0,0,0,2.5,8.156"></path>
|
||||
<path class="ql-stroke ql-thin" d="M2.5,14.846a0.959,0.959,0,0,0,1.85-.109A0.7,0.7,0,0,0,3.75,14a0.688,0.688,0,0,0,.6-0.736,0.959,0.959,0,0,0-1.85-.109"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 764 B |
5
assets/js/assets/icons/map.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-stroke" d="M12,5c0,1.1-2,4-2,4S8,6.1,8,5A2,2,0,0,1,12,5Z"></path>
|
||||
<path class="ql-fill" d="M15.472,2.118a1,1,0,0,0-1.026.05l-1.067.712A3.944,3.944,0,0,1,14,4.869h0v7.6L12,13.8l-2.445-1.63a1,1,0,0,0-1.109,0L6,13.8,4,12.465v-7.6L5.445,5.832a0.992,0.992,0,0,0,.717.144A3.742,3.742,0,0,1,6,5a3.956,3.956,0,0,1,.258-1.374L6,3.8,3.555,2.168A1,1,0,0,0,2,3V13a1,1,0,0,0,.445.832l3,2a1,1,0,0,0,1.109,0L9,14.2l2.445,1.63a1,1,0,0,0,1.109,0l3-2A1,1,0,0,0,16,13V3A1,1,0,0,0,15.472,2.118Z"></path>
|
||||
<path class="ql-fill" d="M12.092,14.938a4.2,4.2,0,0,0-1.936-3.032c-1.125-.656-2.425.738-2.75-0.687A2.036,2.036,0,0,0,5.688,9.656,2.878,2.878,0,0,0,3,8.653V13l3,2,3-2,3,2Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 718 B |
4
assets/js/assets/icons/mention.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<circle class="ql-stroke" cx="9" cy="9" r="2"></circle>
|
||||
<path class="ql-stroke" d="M11,14.651A6,6,0,1,1,15,9a2,2,0,0,1-4,0V7"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 170 B |
3
assets/js/assets/icons/more.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 18 18">
|
||||
<path class="ql-fill" d="M6,9.5A1.5,1.5,0,1,1,4.5,8,1.5,1.5,0,0,1,6,9.5ZM9.5,8A1.5,1.5,0,1,0,11,9.5,1.5,1.5,0,0,0,9.5,8Zm5,0A1.5,1.5,0,1,0,16,9.5,1.5,1.5,0,0,0,14.5,8Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 205 B |
6
assets/js/assets/icons/outdent.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="3" x2="15" y1="14" y2="14"></line>
|
||||
<line class="ql-stroke" x1="3" x2="15" y1="4" y2="4"></line>
|
||||
<line class="ql-stroke" x1="9" x2="15" y1="9" y2="9"></line>
|
||||
<polyline class="ql-stroke" points="5 7 5 11 3 9 5 7"></polyline>
|
||||
</svg>
|
After Width: | Height: | Size: 291 B |
4
assets/js/assets/icons/redo.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<polygon class="ql-fill ql-stroke" points="12 10 14 12 16 10 12 10"></polygon>
|
||||
<path class="ql-stroke" d="M9.91,13.91A4.6,4.6,0,0,1,9,14a5,5,0,1,1,5-5"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 196 B |
5
assets/js/assets/icons/size-decrease.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<polyline class="ql-stroke" points="3.5 14 7 4 10.5 14"></polyline>
|
||||
<line class="ql-stroke" x1="9.45" x2="4.55" y1="11" y2="11"></line>
|
||||
<rect class="ql-fill" height="5" rx="0.5" ry="0.5" transform="translate(20 -7) rotate(90)" width="1" x="13" y="4"></rect>
|
||||
</svg>
|
After Width: | Height: | Size: 296 B |
6
assets/js/assets/icons/size-increase.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<polyline class="ql-stroke" points="3.5 14 7 4 10.5 14"></polyline>
|
||||
<line class="ql-stroke" x1="9.45" x2="4.55" y1="11" y2="11"></line>
|
||||
<rect class="ql-fill" height="5" rx="0.5" ry="0.5" width="1" x="13" y="4"></rect>
|
||||
<rect class="ql-fill" height="5" rx="0.5" ry="0.5" transform="translate(20 -7) rotate(90)" width="1" x="13" y="4"></rect>
|
||||
</svg>
|
After Width: | Height: | Size: 380 B |
6
assets/js/assets/icons/size.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<polyline class="ql-stroke" points="3.5 14 7 4 10.5 14"></polyline>
|
||||
<line class="ql-stroke" x1="9.45" x2="4.55" y1="11" y2="11"></line>
|
||||
<path class="ql-fill" d="M12.09,7.55l1.7-1.473a0.337,0.337,0,0,1,.429,0l1.7,1.473A0.261,0.261,0,0,1,15.7,8H12.3A0.261,0.261,0,0,1,12.09,7.55Z"></path>
|
||||
<path class="ql-fill" d="M12.09,10.45l1.7,1.473a0.337,0.337,0,0,0,.429,0l1.7-1.473A0.261,0.261,0,0,0,15.7,10H12.3A0.261,0.261,0,0,0,12.09,10.45Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 481 B |
9
assets/js/assets/icons/spacing.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke" x1="10" x2="15" y1="4" y2="4"></line>
|
||||
<line class="ql-stroke" x1="10" x2="15" y1="9" y2="9"></line>
|
||||
<line class="ql-stroke" x1="10" x2="15" y1="14" y2="14"></line>
|
||||
<polygon class="ql-fill ql-stroke" points="3 5 5 3 7 5 3 5"></polygon>
|
||||
<line class="ql-stroke" x1="5" x2="5" y1="7" y2="5"></line>
|
||||
<polygon class="ql-fill ql-stroke" points="3 13 5 15 7 13 3 13"></polygon>
|
||||
<line class="ql-stroke" x1="5" x2="5" y1="11" y2="13"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 502 B |
6
assets/js/assets/icons/speech.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-stroke" d="M5,8a4,4,0,0,0,8,0"></path>
|
||||
<line class="ql-stroke" x1="6" x2="12" y1="15" y2="15"></line>
|
||||
<line class="ql-stroke" x1="9" x2="9" y1="12" y2="15"></line>
|
||||
<rect class="ql-fill" height="8" rx="2" ry="2" width="4" x="7" y="2"></rect>
|
||||
</svg>
|
After Width: | Height: | Size: 297 B |
5
assets/js/assets/icons/strike.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<line class="ql-stroke ql-thin" x1="15.5" x2="2.5" y1="8.5" y2="9.5"></line>
|
||||
<path class="ql-fill" d="M9.007,8C6.542,7.791,6,7.519,6,6.5,6,5.792,7.283,5,9,5c1.571,0,2.765.679,2.969,1.309a1,1,0,0,0,1.9-.617C13.356,4.106,11.354,3,9,3,6.2,3,4,4.538,4,6.5a3.2,3.2,0,0,0,.5,1.843Z"></path>
|
||||
<path class="ql-fill" d="M8.984,10C11.457,10.208,12,10.479,12,11.5c0,0.708-1.283,1.5-3,1.5-1.571,0-2.765-.679-2.969-1.309a1,1,0,1,0-1.9.617C4.644,13.894,6.646,15,9,15c2.8,0,5-1.538,5-3.5a3.2,3.2,0,0,0-.5-1.843Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 543 B |
4
assets/js/assets/icons/subscript.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-fill" d="M15.5,15H13.861a3.858,3.858,0,0,0,1.914-2.975,1.8,1.8,0,0,0-1.6-1.751A1.921,1.921,0,0,0,12.021,11.7a0.50013,0.50013,0,1,0,.957.291h0a0.914,0.914,0,0,1,1.053-.725,0.81,0.81,0,0,1,.744.762c0,1.076-1.16971,1.86982-1.93971,2.43082A1.45639,1.45639,0,0,0,12,15.5a0.5,0.5,0,0,0,.5.5h3A0.5,0.5,0,0,0,15.5,15Z"/>
|
||||
<path class="ql-fill" d="M9.65,5.241a1,1,0,0,0-1.409.108L6,7.964,3.759,5.349A1,1,0,0,0,2.192,6.59178Q2.21541,6.6213,2.241,6.649L4.684,9.5,2.241,12.35A1,1,0,0,0,3.71,13.70722q0.02557-.02768.049-0.05722L6,11.036,8.241,13.65a1,1,0,1,0,1.567-1.24277Q9.78459,12.3777,9.759,12.35L7.316,9.5,9.759,6.651A1,1,0,0,0,9.65,5.241Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 686 B |
4
assets/js/assets/icons/superscript.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-fill" d="M15.5,7H13.861a4.015,4.015,0,0,0,1.914-2.975,1.8,1.8,0,0,0-1.6-1.751A1.922,1.922,0,0,0,12.021,3.7a0.5,0.5,0,1,0,.957.291,0.917,0.917,0,0,1,1.053-.725,0.81,0.81,0,0,1,.744.762c0,1.077-1.164,1.925-1.934,2.486A1.423,1.423,0,0,0,12,7.5a0.5,0.5,0,0,0,.5.5h3A0.5,0.5,0,0,0,15.5,7Z"/>
|
||||
<path class="ql-fill" d="M9.651,5.241a1,1,0,0,0-1.41.108L6,7.964,3.759,5.349a1,1,0,1,0-1.519,1.3L4.683,9.5,2.241,12.35a1,1,0,1,0,1.519,1.3L6,11.036,8.241,13.65a1,1,0,0,0,1.519-1.3L7.317,9.5,9.759,6.651A1,1,0,0,0,9.651,5.241Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 567 B |
5
assets/js/assets/icons/table-border-all.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<rect class="ql-stroke-miter" height="12" width="12" x="3" y="3"></rect>
|
||||
<line class="ql-stroke-miter" x1="9" x2="9" y1="3" y2="15"></line>
|
||||
<line class="ql-stroke-miter" x1="15" x2="3" y1="9" y2="9"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 246 B |
28
assets/js/assets/icons/table-border-bottom.svg
Normal file
@@ -0,0 +1,28 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="2" transform="translate(18 -12) rotate(90)" width="2" x="14" y="2"></rect>
|
||||
<rect height="2" transform="translate(21 -9) rotate(90)" width="2" x="14" y="5"></rect>
|
||||
<rect height="2" transform="translate(24 -6) rotate(90)" width="2" x="14" y="8"></rect>
|
||||
<rect height="2" transform="translate(30 0) rotate(90)" width="2" x="14" y="14"></rect>
|
||||
<rect height="2" transform="translate(27 -3) rotate(90)" width="2" x="14" y="11"></rect>
|
||||
<rect height="2" transform="translate(6 0) rotate(90)" width="2" x="2" y="2"></rect>
|
||||
<rect height="2" transform="translate(9 3) rotate(90)" width="2" x="2" y="5"></rect>
|
||||
<rect height="2" transform="translate(12 6) rotate(90)" width="2" x="2" y="8"></rect>
|
||||
<rect height="2" transform="translate(18 12) rotate(90)" width="2" x="2" y="14"></rect>
|
||||
<rect height="2" transform="translate(15 9) rotate(90)" width="2" x="2" y="11"></rect>
|
||||
</g>
|
||||
<line class="ql-stroke-miter" x1="2" x2="16" y1="15" y2="15"></line>
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="2" width="2" x="5" y="2"></rect>
|
||||
<rect height="2" width="2" x="8" y="2"></rect>
|
||||
<rect height="2" width="2" x="11" y="2"></rect>
|
||||
<rect height="2" width="2" x="5" y="14"></rect>
|
||||
<rect height="2" width="2" x="8" y="14"></rect>
|
||||
<rect height="2" width="2" x="8" y="11"></rect>
|
||||
<rect height="2" width="2" x="8" y="8"></rect>
|
||||
<rect height="2" width="2" x="8" y="5"></rect>
|
||||
<rect height="2" transform="translate(15 3) rotate(90)" width="2" x="5" y="8"></rect>
|
||||
<rect height="2" transform="translate(21 -3) rotate(90)" width="2" x="11" y="8"></rect>
|
||||
<rect height="2" width="2" x="11" y="14"></rect>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
28
assets/js/assets/icons/table-border-left.svg
Normal file
@@ -0,0 +1,28 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="2" transform="translate(30 6) rotate(180)" width="2" x="14" y="2"></rect>
|
||||
<rect height="2" transform="translate(24 6) rotate(180)" width="2" x="11" y="2"></rect>
|
||||
<rect height="2" transform="translate(18 6) rotate(180)" width="2" x="8" y="2"></rect>
|
||||
<rect height="2" transform="translate(6 6) rotate(180)" width="2" x="2" y="2"></rect>
|
||||
<rect height="2" transform="translate(12 6) rotate(180)" width="2" x="5" y="2"></rect>
|
||||
<rect height="2" transform="translate(30 30) rotate(180)" width="2" x="14" y="14"></rect>
|
||||
<rect height="2" transform="translate(24 30) rotate(180)" width="2" x="11" y="14"></rect>
|
||||
<rect height="2" transform="translate(18 30) rotate(180)" width="2" x="8" y="14"></rect>
|
||||
<rect height="2" transform="translate(6 30) rotate(180)" width="2" x="2" y="14"></rect>
|
||||
<rect height="2" transform="translate(12 30) rotate(180)" width="2" x="5" y="14"></rect>
|
||||
</g>
|
||||
<line class="ql-stroke-miter" x1="3" x2="3" y1="16" y2="2"></line>
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="2" transform="translate(3 27) rotate(-90)" width="2" x="14" y="11"></rect>
|
||||
<rect height="2" transform="translate(6 24) rotate(-90)" width="2" x="14" y="8"></rect>
|
||||
<rect height="2" transform="translate(9 21) rotate(-90)" width="2" x="14" y="5"></rect>
|
||||
<rect height="2" transform="translate(-9 15) rotate(-90)" width="2" x="2" y="11"></rect>
|
||||
<rect height="2" transform="translate(-6 12) rotate(-90)" width="2" x="2" y="8"></rect>
|
||||
<rect height="2" transform="translate(-3 15) rotate(-90)" width="2" x="5" y="8"></rect>
|
||||
<rect height="2" transform="translate(0 18) rotate(-90)" width="2" x="8" y="8"></rect>
|
||||
<rect height="2" transform="translate(3 21) rotate(-90)" width="2" x="11" y="8"></rect>
|
||||
<rect height="2" transform="translate(18 24) rotate(180)" width="2" x="8" y="11"></rect>
|
||||
<rect height="2" transform="translate(18 12) rotate(180)" width="2" x="8" y="5"></rect>
|
||||
<rect height="2" transform="translate(-3 9) rotate(-90)" width="2" x="2" y="5"></rect>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
25
assets/js/assets/icons/table-border-none.svg
Normal file
@@ -0,0 +1,25 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="2" width="2" x="2" y="2"></rect>
|
||||
<rect height="2" width="2" x="5" y="2"></rect>
|
||||
<rect height="2" width="2" x="8" y="2"></rect>
|
||||
<rect height="2" width="2" x="14" y="2"></rect>
|
||||
<rect height="2" width="2" x="11" y="2"></rect>
|
||||
<rect height="2" width="2" x="2" y="14"></rect>
|
||||
<rect height="2" width="2" x="5" y="14"></rect>
|
||||
<rect height="2" width="2" x="8" y="14"></rect>
|
||||
<rect height="2" width="2" x="14" y="14"></rect>
|
||||
<rect height="2" width="2" x="11" y="14"></rect>
|
||||
<rect height="2" transform="translate(-9 15) rotate(-90)" width="2" x="2" y="11"></rect>
|
||||
<rect height="2" transform="translate(-6 12) rotate(-90)" width="2" x="2" y="8"></rect>
|
||||
<rect height="2" transform="translate(-3 9) rotate(-90)" width="2" x="2" y="5"></rect>
|
||||
<rect height="2" transform="translate(3 27) rotate(-90)" width="2" x="14" y="11"></rect>
|
||||
<rect height="2" transform="translate(6 24) rotate(-90)" width="2" x="14" y="8"></rect>
|
||||
<rect height="2" transform="translate(3 21) rotate(-90)" width="2" x="11" y="8"></rect>
|
||||
<rect height="2" transform="translate(0 18) rotate(-90)" width="2" x="8" y="8"></rect>
|
||||
<rect height="2" transform="translate(-3 15) rotate(-90)" width="2" x="5" y="8"></rect>
|
||||
<rect height="2" width="2" x="8" y="11"></rect>
|
||||
<rect height="2" width="2" x="8" y="5"></rect>
|
||||
<rect height="2" transform="translate(9 21) rotate(-90)" width="2" x="14" y="5"></rect>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
35
assets/js/assets/icons/table-border-outside.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1{opacity:0.4;}
|
||||
.cls-2{fill:#444;}
|
||||
.cls-3{fill:none;stroke:#444;stroke-miterlimit:10;stroke-width:2px;}
|
||||
</style>
|
||||
</defs>
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="2" width="2" x="2" y="2"></rect>
|
||||
<rect height="2" width="2" x="5" y="2"></rect>
|
||||
<rect height="2" width="2" x="8" y="2"></rect>
|
||||
<rect height="2" width="2" x="14" y="2"></rect>
|
||||
<rect height="2" width="2" x="11" y="2"></rect>
|
||||
<rect height="2" width="2" x="2" y="14"></rect>
|
||||
<rect height="2" width="2" x="5" y="14"></rect>
|
||||
<rect height="2" width="2" x="8" y="14"></rect>
|
||||
</g>
|
||||
<rect class="ql-stroke-miter" height="12" width="12" x="3" y="3"></rect>
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="2" width="2" x="14" y="14"></rect>
|
||||
<rect height="2" width="2" x="11" y="14"></rect>
|
||||
<rect height="2" transform="translate(-9 15) rotate(-90)" width="2" x="2" y="11"></rect>
|
||||
<rect height="2" transform="translate(-6 12) rotate(-90)" width="2" x="2" y="8"></rect>
|
||||
<rect height="2" transform="translate(-3 9) rotate(-90)" width="2" x="2" y="5"></rect>
|
||||
<rect height="2" transform="translate(3 27) rotate(-90)" width="2" x="14" y="11"></rect>
|
||||
<rect height="2" transform="translate(6 24) rotate(-90)" width="2" x="14" y="8"></rect>
|
||||
<rect height="2" transform="translate(3 21) rotate(-90)" width="2" x="11" y="8"></rect>
|
||||
<rect height="2" transform="translate(0 18) rotate(-90)" width="2" x="8" y="8"></rect>
|
||||
<rect height="2" transform="translate(-3 15) rotate(-90)" width="2" x="5" y="8"></rect>
|
||||
<rect height="2" width="2" x="8" y="11"></rect>
|
||||
<rect height="2" width="2" x="8" y="5"></rect>
|
||||
<rect height="2" transform="translate(9 21) rotate(-90)" width="2" x="14" y="5"></rect>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
28
assets/js/assets/icons/table-border-right.svg
Normal file
@@ -0,0 +1,28 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="2" width="2" x="2" y="2"></rect>
|
||||
<rect height="2" width="2" x="5" y="2"></rect>
|
||||
<rect height="2" width="2" x="8" y="2"></rect>
|
||||
<rect height="2" width="2" x="14" y="2"></rect>
|
||||
<rect height="2" width="2" x="11" y="2"></rect>
|
||||
<rect height="2" width="2" x="2" y="14"></rect>
|
||||
<rect height="2" width="2" x="5" y="14"></rect>
|
||||
<rect height="2" width="2" x="8" y="14"></rect>
|
||||
<rect height="2" width="2" x="14" y="14"></rect>
|
||||
<rect height="2" width="2" x="11" y="14"></rect>
|
||||
</g>
|
||||
<line class="ql-stroke-miter" x1="15" x2="15" y1="16" y2="2"></line>
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="2" transform="translate(-9 15) rotate(-90)" width="2" x="2" y="11"></rect>
|
||||
<rect height="2" transform="translate(-6 12) rotate(-90)" width="2" x="2" y="8"></rect>
|
||||
<rect height="2" transform="translate(-3 9) rotate(-90)" width="2" x="2" y="5"></rect>
|
||||
<rect height="2" transform="translate(3 27) rotate(-90)" width="2" x="14" y="11"></rect>
|
||||
<rect height="2" transform="translate(6 24) rotate(-90)" width="2" x="14" y="8"></rect>
|
||||
<rect height="2" transform="translate(3 21) rotate(-90)" width="2" x="11" y="8"></rect>
|
||||
<rect height="2" transform="translate(0 18) rotate(-90)" width="2" x="8" y="8"></rect>
|
||||
<rect height="2" transform="translate(-3 15) rotate(-90)" width="2" x="5" y="8"></rect>
|
||||
<rect height="2" width="2" x="8" y="11"></rect>
|
||||
<rect height="2" width="2" x="8" y="5"></rect>
|
||||
<rect height="2" transform="translate(9 21) rotate(-90)" width="2" x="14" y="5"></rect>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
28
assets/js/assets/icons/table-border-top.svg
Normal file
@@ -0,0 +1,28 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="2" transform="translate(-12 18) rotate(-90)" width="2" x="2" y="14"></rect>
|
||||
<rect height="2" transform="translate(-9 15) rotate(-90)" width="2" x="2" y="11"></rect>
|
||||
<rect height="2" transform="translate(-6 12) rotate(-90)" width="2" x="2" y="8"></rect>
|
||||
<rect height="2" transform="translate(0 6) rotate(-90)" width="2" x="2" y="2"></rect>
|
||||
<rect height="2" transform="translate(-3 9) rotate(-90)" width="2" x="2" y="5"></rect>
|
||||
<rect height="2" transform="translate(0 30) rotate(-90)" width="2" x="14" y="14"></rect>
|
||||
<rect height="2" transform="translate(3 27) rotate(-90)" width="2" x="14" y="11"></rect>
|
||||
<rect height="2" transform="translate(6 24) rotate(-90)" width="2" x="14" y="8"></rect>
|
||||
<rect height="2" transform="translate(12 18) rotate(-90)" width="2" x="14" y="2"></rect>
|
||||
<rect height="2" transform="translate(9 21) rotate(-90)" width="2" x="14" y="5"></rect>
|
||||
</g>
|
||||
<line class="ql-stroke-miter" x1="16" x2="2" y1="3" y2="3"></line>
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="2" transform="translate(24 30) rotate(-180)" width="2" x="11" y="14"></rect>
|
||||
<rect height="2" transform="translate(18 30) rotate(-180)" width="2" x="8" y="14"></rect>
|
||||
<rect height="2" transform="translate(12 30) rotate(-180)" width="2" x="5" y="14"></rect>
|
||||
<rect height="2" transform="translate(24 6) rotate(-180)" width="2" x="11" y="2"></rect>
|
||||
<rect height="2" transform="translate(18 6) rotate(-180)" width="2" x="8" y="2"></rect>
|
||||
<rect height="2" transform="translate(18 12) rotate(-180)" width="2" x="8" y="5"></rect>
|
||||
<rect height="2" transform="translate(18 18) rotate(-180)" width="2" x="8" y="8"></rect>
|
||||
<rect height="2" transform="translate(18 24) rotate(-180)" width="2" x="8" y="11"></rect>
|
||||
<rect height="2" transform="translate(3 21) rotate(-90)" width="2" x="11" y="8"></rect>
|
||||
<rect height="2" transform="translate(-3 15) rotate(-90)" width="2" x="5" y="8"></rect>
|
||||
<rect height="2" transform="translate(12 6) rotate(-180)" width="2" x="5" y="2"></rect>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
13
assets/js/assets/icons/table-delete-cells.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-fill" d="M15.707,7l1.146-1.146a0.5,0.5,0,1,0-.707-0.707L15,6.293,13.854,5.146a0.5,0.5,0,0,0-.707.707L14.293,7,13.146,8.146a0.5,0.5,0,1,0,.707.707L15,7.707l1.146,1.146a0.5,0.5,0,1,0,.707-0.707Z"></path>
|
||||
<path class="ql-fill" d="M6,5H3A1,1,0,0,0,2,6V8A1,1,0,0,0,3,9H6V5Z"></path>
|
||||
<path class="ql-fill" d="M10,5H7V9h3a1,1,0,0,0,1-1V6A1,1,0,0,0,10,5Z"></path>
|
||||
<g class="ql-fill ql-transparent">
|
||||
<path d="M8,11h4V9a1,1,0,0,0-1-1H8v3Z"></path>
|
||||
<path d="M7,11V8H4A1,1,0,0,0,3,9v2H7Z"></path>
|
||||
<path d="M7,12H3v2a1,1,0,0,0,1,1H7V12Z"></path>
|
||||
<path d="M8,12v3h3a1,1,0,0,0,1-1V12H8Z"></path>
|
||||
<path d="M8,6h3a1,1,0,0,0,1-1V3a1,1,0,0,0-1-1H8V6Z"></path>
|
||||
<path d="M4,6H7V2H4A1,1,0,0,0,3,3V5A1,1,0,0,0,4,6Z"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 788 B |
8
assets/js/assets/icons/table-delete-columns.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="10" rx="1" ry="1" width="4" x="2" y="6"></rect>
|
||||
<rect height="10" rx="1" ry="1" width="4" x="12" y="6"></rect>
|
||||
</g>
|
||||
<rect class="ql-fill" height="8" rx="1" ry="1" width="4" x="7" y="2"></rect>
|
||||
<path class="ql-fill" d="M9.707,13l1.146-1.146a0.5,0.5,0,0,0-.707-0.707L9,12.293,7.854,11.146a0.5,0.5,0,0,0-.707.707L8.293,13,7.146,14.146a0.5,0.5,0,1,0,.707.707L9,13.707l1.146,1.146a0.5,0.5,0,0,0,.707-0.707Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 508 B |
9
assets/js/assets/icons/table-delete-rows.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<g class="ql-fill ql-stroke ql-thin ql-transparent">
|
||||
<rect height="3" rx="0.5" ry="0.5" width="7" x="4.5" y="2.5"></rect>
|
||||
<rect height="3" rx="0.5" ry="0.5" width="7" x="4.5" y="12.5"></rect>
|
||||
</g>
|
||||
<rect class="ql-fill ql-stroke ql-thin" height="3" rx="0.5" ry="0.5" width="7" x="8.5" y="7.5"></rect>
|
||||
<line class="ql-stroke ql-thin" x1="6.5" x2="3.5" y1="7.5" y2="10.5"></line>
|
||||
<line class="ql-stroke ql-thin" x1="3.5" x2="6.5" y1="7.5" y2="10.5"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 504 B |
13
assets/js/assets/icons/table-insert-cells.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<g class="ql-fill ql-transparent">
|
||||
<path d="M11,11h4V9a1,1,0,0,0-1-1H11v3Z"></path>
|
||||
<path d="M10,11V8H7A1,1,0,0,0,6,9v2h4Z"></path>
|
||||
<path d="M10,12H6v2a1,1,0,0,0,1,1h3V12Z"></path>
|
||||
<path d="M11,12v3h3a1,1,0,0,0,1-1V12H11Z"></path>
|
||||
<path d="M11,6h3a1,1,0,0,0,1-1V3a1,1,0,0,0-1-1H11V6Z"></path>
|
||||
<path d="M7,6h3V2H7A1,1,0,0,0,6,3V5A1,1,0,0,0,7,6Z"></path>
|
||||
</g>
|
||||
<path class="ql-fill" d="M5,6H4V5a0.5,0.5,0,0,0-.854-0.354l-2,2a0.5,0.5,0,0,0,0,.707l2,2A0.5,0.5,0,0,0,3.5,9.5a0.494,0.494,0,0,0,.191-0.038A0.5,0.5,0,0,0,4,9V8H5A1,1,0,0,0,5,6Z"></path>
|
||||
<path class="ql-fill" d="M15,5H12V9h3a1,1,0,0,0,1-1V6A1,1,0,0,0,15,5Z"></path>
|
||||
<path class="ql-fill" d="M11,5H8A1,1,0,0,0,7,6V8A1,1,0,0,0,8,9h3V5Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 766 B |
8
assets/js/assets/icons/table-insert-columns.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="10" rx="1" ry="1" width="4" x="12" y="2"></rect>
|
||||
<rect height="10" rx="1" ry="1" width="4" x="2" y="2"></rect>
|
||||
</g>
|
||||
<path class="ql-fill" d="M11.354,4.146l-2-2a0.5,0.5,0,0,0-.707,0l-2,2A0.5,0.5,0,0,0,7,5H8V6a1,1,0,0,0,2,0V5h1A0.5,0.5,0,0,0,11.354,4.146Z"></path>
|
||||
<rect class="ql-fill" height="8" rx="1" ry="1" width="4" x="7" y="8"></rect>
|
||||
</svg>
|
After Width: | Height: | Size: 437 B |
9
assets/js/assets/icons/table-insert-rows.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<g class="ql-fill ql-stroke ql-thin ql-transparent">
|
||||
<rect height="3" rx="0.5" ry="0.5" width="7" x="4.5" y="2.5"></rect>
|
||||
<rect height="3" rx="0.5" ry="0.5" width="7" x="4.5" y="12.5"></rect>
|
||||
</g>
|
||||
<rect class="ql-fill ql-stroke ql-thin" height="3" rx="0.5" ry="0.5" width="7" x="8.5" y="7.5"></rect>
|
||||
<polygon class="ql-fill ql-stroke ql-thin" points="4.5 11 2.5 9 4.5 7 4.5 11"></polygon>
|
||||
<line class="ql-stroke" x1="6" x2="4" y1="9" y2="9"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 499 B |
4
assets/js/assets/icons/table-merge-cells.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<rect class="ql-stroke" height="4" width="12" x="3" y="7"></rect>
|
||||
<path class="ql-fill ql-transparent" d="M2,2V16H16V2H2ZM14,14H10V11H8v3H4V4H8V7h2V4h4V14Z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 201 B |
7
assets/js/assets/icons/table-unmerge-cells.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<rect class="ql-stroke" height="4" width="12" x="3" y="7"></rect>
|
||||
<path class="ql-fill ql-transparent" d="M2,2V16H16V2H2ZM14,14H10V11H8v3H4V4H8V7h2V4h4V14Z"></path>
|
||||
<line class="ql-stroke" x1="12" x2="12" y1="11" y2="7"></line>
|
||||
<line class="ql-stroke" x1="9" x2="9" y1="11" y2="7"></line>
|
||||
<line class="ql-stroke" x1="6" x2="6" y1="11" y2="7"></line>
|
||||
</svg>
|
After Width: | Height: | Size: 392 B |
11
assets/js/assets/icons/table.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<rect class="ql-stroke" height="12" width="12" x="3" y="3"></rect>
|
||||
<rect class="ql-fill" height="2" width="3" x="5" y="5"></rect>
|
||||
<rect class="ql-fill" height="2" width="4" x="9" y="5"></rect>
|
||||
<g class="ql-fill ql-transparent">
|
||||
<rect height="2" width="3" x="5" y="8"></rect>
|
||||
<rect height="2" width="4" x="9" y="8"></rect>
|
||||
<rect height="2" width="3" x="5" y="11"></rect>
|
||||
<rect height="2" width="4" x="9" y="11"></rect>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 481 B |
4
assets/js/assets/icons/underline.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<path class="ql-stroke" d="M5,3V9a4.012,4.012,0,0,0,4,4H9a4.012,4.012,0,0,0,4-4V3"></path>
|
||||
<rect class="ql-fill" height="1" rx="0.5" ry="0.5" width="12" x="3" y="15"></rect>
|
||||
</svg>
|
After Width: | Height: | Size: 210 B |
4
assets/js/assets/icons/undo.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<polygon class="ql-fill ql-stroke" points="6 10 4 12 2 10 6 10"></polygon>
|
||||
<path class="ql-stroke" d="M8.09,13.91A4.6,4.6,0,0,0,9,14,5,5,0,1,0,4,9"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 192 B |
14
assets/js/assets/icons/video.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<svg viewbox="0 0 18 18">
|
||||
<rect class="ql-stroke" height="12" width="12" x="3" y="3"></rect>
|
||||
<rect class="ql-fill" height="12" width="1" x="5" y="3"></rect>
|
||||
<rect class="ql-fill" height="12" width="1" x="12" y="3"></rect>
|
||||
<rect class="ql-fill" height="2" width="8" x="5" y="8"></rect>
|
||||
<rect class="ql-fill" height="1" width="3" x="3" y="5"></rect>
|
||||
<rect class="ql-fill" height="1" width="3" x="3" y="7"></rect>
|
||||
<rect class="ql-fill" height="1" width="3" x="3" y="10"></rect>
|
||||
<rect class="ql-fill" height="1" width="3" x="3" y="12"></rect>
|
||||
<rect class="ql-fill" height="1" width="3" x="12" y="5"></rect>
|
||||
<rect class="ql-fill" height="1" width="3" x="12" y="7"></rect>
|
||||
<rect class="ql-fill" height="1" width="3" x="12" y="10"></rect>
|
||||
<rect class="ql-fill" height="1" width="3" x="12" y="12"></rect>
|
||||
</svg>
|
After Width: | Height: | Size: 827 B |
18
assets/js/assets/snow.styl
Normal file
@@ -0,0 +1,18 @@
|
||||
themeName = 'snow'
|
||||
activeColor = #06c
|
||||
borderColor = #ccc
|
||||
backgroundColor = #fff
|
||||
inactiveColor = #444
|
||||
shadowColor = #ddd
|
||||
textColor = #444
|
||||
|
||||
@import './core'
|
||||
@import './base'
|
||||
@import './snow/*'
|
||||
|
||||
.ql-snow
|
||||
a
|
||||
color: activeColor
|
||||
|
||||
.ql-container.ql-snow
|
||||
border: 1px solid borderColor
|
26
assets/js/assets/snow/toolbar.styl
Normal file
@@ -0,0 +1,26 @@
|
||||
.ql-toolbar.ql-snow
|
||||
border: 1px solid borderColor
|
||||
box-sizing: border-box
|
||||
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif
|
||||
padding: 8px
|
||||
|
||||
.ql-formats
|
||||
margin-right: 15px
|
||||
|
||||
.ql-picker-label
|
||||
border: 1px solid transparent
|
||||
.ql-picker-options
|
||||
border: 1px solid transparent
|
||||
box-shadow: rgba(0,0,0,0.2) 0 2px 8px
|
||||
.ql-picker.ql-expanded
|
||||
.ql-picker-label
|
||||
border-color: borderColor
|
||||
.ql-picker-options
|
||||
border-color: borderColor
|
||||
|
||||
.ql-color-picker
|
||||
.ql-picker-item.ql-selected, .ql-picker-item:hover
|
||||
border-color: #000
|
||||
|
||||
.ql-toolbar.ql-snow + .ql-container.ql-snow
|
||||
border-top: 0px;
|
53
assets/js/assets/snow/tooltip.styl
Normal file
@@ -0,0 +1,53 @@
|
||||
tooltipMargin = 8px
|
||||
|
||||
.ql-snow
|
||||
.ql-tooltip
|
||||
background-color: #fff
|
||||
border: 1px solid borderColor
|
||||
box-shadow: 0px 0px 5px shadowColor
|
||||
color: textColor
|
||||
padding: 5px 12px
|
||||
white-space: nowrap
|
||||
&::before
|
||||
content: "Visit URL:"
|
||||
line-height: 26px
|
||||
margin-right: tooltipMargin
|
||||
input[type=text]
|
||||
display: none
|
||||
border: 1px solid borderColor
|
||||
font-size: 13px
|
||||
height: 26px
|
||||
margin: 0px
|
||||
padding: 3px 5px
|
||||
width: 170px
|
||||
a.ql-preview
|
||||
display: inline-block
|
||||
max-width: 200px
|
||||
overflow-x: hidden
|
||||
text-overflow: ellipsis
|
||||
vertical-align: top
|
||||
a.ql-action::after
|
||||
border-right: 1px solid borderColor
|
||||
content: 'Edit'
|
||||
margin-left: tooltipMargin*2
|
||||
padding-right: tooltipMargin
|
||||
a.ql-remove::before
|
||||
content: 'Remove'
|
||||
margin-left: tooltipMargin
|
||||
a
|
||||
line-height: 26px
|
||||
.ql-tooltip.ql-editing
|
||||
a.ql-preview, a.ql-remove
|
||||
display: none
|
||||
input[type=text]
|
||||
display: inline-block
|
||||
a.ql-action::after
|
||||
border-right: 0px
|
||||
content: 'Save'
|
||||
padding-right: 0px
|
||||
.ql-tooltip[data-mode=link]::before
|
||||
content: "Enter link:"
|
||||
.ql-tooltip[data-mode=formula]::before
|
||||
content: "Enter formula:"
|
||||
.ql-tooltip[data-mode=video]::before
|
||||
content: "Enter video:"
|
515
assets/js/blog-edit.js
Normal file
@@ -0,0 +1,515 @@
|
||||
const toolbarOptions = [
|
||||
[{'header': [2, 3, 4, 5, 6, false]}],
|
||||
|
||||
['bold', 'italic', 'underline', 'strike'], // toggled buttons
|
||||
['image', 'video'],
|
||||
['link', 'code-block', 'blockquote'],
|
||||
|
||||
[{'list': 'ordered'}, {'list': 'bullet'}],
|
||||
[{'script': 'sub'}, {'script': 'super'}], // superscript/subscript
|
||||
[{'indent': '-1'}, {'indent': '+1'}], // outdent/indent
|
||||
|
||||
[{'color': []}, {'background': []}], // dropdown with defaults from theme
|
||||
[{'align': []}]
|
||||
|
||||
['clean']
|
||||
];
|
||||
|
||||
const quill = new Quill('#postContent', {
|
||||
theme: 'snow',
|
||||
bounds: '#postContent .ql-editor',
|
||||
modules: {
|
||||
syntax: true,
|
||||
toolbar: toolbarOptions
|
||||
}
|
||||
});
|
||||
|
||||
let changeUrl = true;
|
||||
|
||||
// var edits = ["#title", "#description", "#content"];
|
||||
|
||||
// Prevent the backspace key from navigating back.
|
||||
// $(document).on("keydown", function (e) {
|
||||
// if (e.which === 8 && !$(e.target||e.srcElement).is("span, [contenteditable=true]")) {
|
||||
// e.preventDefault();
|
||||
// return false;
|
||||
// }
|
||||
// });
|
||||
|
||||
// var autolist = new AutoList();
|
||||
// var editor = new MediumEditor('#content', {
|
||||
// extensions: {
|
||||
// 'autolist': autolist
|
||||
// }
|
||||
// });
|
||||
// $('#content').mediumInsert({
|
||||
// editor: editor
|
||||
// });
|
||||
|
||||
// TODO
|
||||
// $(function () {
|
||||
// $('#content').mediumInsert({
|
||||
// editor: editor
|
||||
// });
|
||||
// });
|
||||
|
||||
$('#postTitle').bind('click', function () {
|
||||
$(this).attr('contentEditable', true);
|
||||
}).blur(
|
||||
function () {
|
||||
$(this).attr('contentEditable', false);
|
||||
});
|
||||
|
||||
$('#postTitle').on('keyup', function () {
|
||||
if (changeUrl) {
|
||||
var title = prepareString($('#postTitle').text());
|
||||
$('#postUrl').val(encodeURI(title));
|
||||
}
|
||||
});
|
||||
|
||||
$('#postDescription').bind('click', function () {
|
||||
$(this).attr('contentEditable', true);
|
||||
}).blur(
|
||||
function () {
|
||||
$(this).attr('contentEditable', false);
|
||||
});
|
||||
|
||||
$('#postCategory').bind('change', function () {
|
||||
switchCategory();
|
||||
});
|
||||
|
||||
function switchCategory() {
|
||||
if ($('#postCategory').val() === 'new-category') {
|
||||
$('#new-category').fadeIn(250);
|
||||
} else {
|
||||
$('#new-category').fadeOut(250);
|
||||
}
|
||||
}
|
||||
|
||||
$('#cat-dname').bind('keyup', function () {
|
||||
$('#cat-name').val(prepareString($(this).val()));
|
||||
});
|
||||
|
||||
function prepareString(input) {
|
||||
input = input.toLowerCase()
|
||||
.replace(/ /g, '-') // Remove spaces
|
||||
.replace(/ä/g, 'ae') // Remove umlauts
|
||||
.replace(/ö/g, 'oe')
|
||||
.replace(/ü/g, 'ue')
|
||||
.replace(/[^a-z1-9-]/g, '') // Remove non alphanumerical chars
|
||||
.replace(/--+/g, '-'); // Prevent multiple dashes
|
||||
return input;
|
||||
}
|
||||
|
||||
/*$('#content').bind('click', function () {
|
||||
$(this).attr('contentEditable', true);
|
||||
}).blur(
|
||||
function () {
|
||||
$(this).attr('contentEditable', false);
|
||||
});*/
|
||||
|
||||
var shift = false;
|
||||
|
||||
$('span').keydown(function (e) {
|
||||
var key = e.keyCode;
|
||||
console.log(key);
|
||||
|
||||
// Shift Control
|
||||
|
||||
if (key === 16) {
|
||||
shift = true;
|
||||
}
|
||||
|
||||
// Ctrl + Return Control
|
||||
if (key === 13 && (e.ctrlKey || e.metaKey)) {
|
||||
console.log('test');
|
||||
e.preventDefault();
|
||||
$(this).attr('contentEditable', false);
|
||||
}
|
||||
|
||||
// Tab Control
|
||||
if (key === 9) {
|
||||
e.preventDefault();
|
||||
$(this).attr('contentEditable', false);
|
||||
var pos = edits.indexOf("#" + $(this).attr('id'));
|
||||
console.log(pos);
|
||||
if (shift) {
|
||||
$(edits[pos - 1]).attr('contentEditable', true);
|
||||
} else {
|
||||
$(edits[pos + 1]).attr('contentEditable', true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
$('.noEnter').keydown(function (e) {
|
||||
var key = e.keyCode;
|
||||
|
||||
// Return Control
|
||||
if (key === 13 && $(this).hasClass('noEnter')) {
|
||||
e.preventDefault();
|
||||
$(this).attr('contentEditable', false);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$('span').keyup(function (e) {
|
||||
var key = e.keyCode;
|
||||
|
||||
// Shift Control
|
||||
if (key === 16) {
|
||||
shift = false;
|
||||
}
|
||||
});
|
||||
|
||||
let tagsList = [];
|
||||
$(function () {
|
||||
$.ajax({
|
||||
url: 'http://192.168.178.39/admin/blog/tagsList',
|
||||
method: 'GET',
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
for (let tag of data) {
|
||||
tagsList.push(tag.display_name);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#postTags').tagsinput();
|
||||
|
||||
$('#postTags').tagsinput({
|
||||
typeaheadjs: {
|
||||
name: 'blogposttags',
|
||||
source: substringMatcher(tagsList)
|
||||
}
|
||||
});
|
||||
|
||||
$('#postTags').keydown(function (e) {
|
||||
if (e.keyCode === 9 || e.keyCode === 13) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
if ($('#postID').val() !== '-1') {
|
||||
getPostData();
|
||||
getPostTags();
|
||||
// getTranslations();
|
||||
|
||||
if ($('#contentID').val() !== '-1') {
|
||||
getContentData();
|
||||
}
|
||||
|
||||
if ($('#translationID').val() !== '-1') {
|
||||
getTranslationData();
|
||||
}
|
||||
}
|
||||
|
||||
$('#postPublishDate').datetimepicker({
|
||||
format: 'DD.MM.YYYY HH:mm',
|
||||
stepping: '10',
|
||||
sideBySide: true,
|
||||
showTodayButton: true,
|
||||
icons: {
|
||||
time: "fa fa-clock-o",
|
||||
date: "fa fa-calendar",
|
||||
up: "fa fa-arrow-up",
|
||||
down: "fa fa-arrow-down"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function getPostData() {
|
||||
const postID = $('#postID').val();
|
||||
$.ajax({
|
||||
url: 'http://192.168.178.39/admin/blog/getPost',
|
||||
method: 'post',
|
||||
data: {
|
||||
postID
|
||||
},
|
||||
success: (result) => {
|
||||
console.log(result);
|
||||
|
||||
if (result.status === 'success') {
|
||||
$('#postUrl').val(result.postData.postUrl);
|
||||
$('#postCategory').val(result.postData.postCategoryID);
|
||||
switchCategory();
|
||||
$('#postPublishDate').val(result.postData.postPublishDate);
|
||||
$('#uploadedImage').val(result.postData.postImage);
|
||||
$('.img-container').css('background-image', 'url(' + result.postData.postImage + ')');
|
||||
|
||||
if (result.postData.postState === "1") {
|
||||
$('#postUrl').attr('disabled', '');
|
||||
changeUrl = false;
|
||||
$('#postUrl').keydown((e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getContentData() {
|
||||
const postID = $('#postID').val();
|
||||
const contentID = $('#contentID').val();
|
||||
const lang = $('#postLanguage').val();
|
||||
$.ajax({
|
||||
url: 'http://192.168.178.39/admin/blog/getContent',
|
||||
method: 'post',
|
||||
data: {
|
||||
postID, contentID, lang
|
||||
},
|
||||
success: (result) => {
|
||||
console.log(result);
|
||||
if (result.status === 'success') {
|
||||
quill.clipboard.dangerouslyPasteHTML(result.contentData.content);
|
||||
// $('#contentID').val('-1');
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getTranslationData() {
|
||||
const postID = $('#postID').val();
|
||||
const translationID = $('#translationID').val();
|
||||
const lang = $('#postLanguage').val();
|
||||
$.ajax({
|
||||
url: 'http://192.168.178.39/admin/blog/getTranslationData',
|
||||
method: 'post',
|
||||
data: {
|
||||
postID, translationID, lang
|
||||
},
|
||||
success: (result) => {
|
||||
console.log(result);
|
||||
|
||||
if (result.status === 'success') {
|
||||
$('#postTitle').text(result.translationData.postTitle);
|
||||
$('#postDescription').text(result.translationData.postDesc);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getPostTags() {
|
||||
const postID = $('#postID').val();
|
||||
$.ajax({
|
||||
url: 'http://192.168.178.39/admin/blog/getPostTags',
|
||||
method: 'POST',
|
||||
data: {
|
||||
postID
|
||||
},
|
||||
success: (result) => {
|
||||
console.log(result);
|
||||
|
||||
if (result.success) {
|
||||
for (let tag of result.tags) {
|
||||
$('#postTags').tagsinput('add', tag.display_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$('#switchLanguages > li').click(function () {
|
||||
const currentPostTitle = $('#postTitle').text();
|
||||
const currentPostDescription = $('#postDescription').text();
|
||||
const currentPostContent = quill.getContents();
|
||||
const currentContentID = $('#contentID').val();
|
||||
const currentTranslationID = $('#translationID').val();
|
||||
|
||||
$('#switchLanguages > li.active').data('title', currentPostTitle)
|
||||
.data('description', currentPostDescription)
|
||||
.data('content', currentPostContent)
|
||||
.data('contentid', currentContentID)
|
||||
.data('translationid', currentTranslationID)
|
||||
.removeClass('active');
|
||||
|
||||
console.log($(this));
|
||||
|
||||
if ($(this).data('title')) {
|
||||
const postTitle = $(this).data('title');
|
||||
const postDescription = $(this).data('description');
|
||||
const postContent = $(this).data('content');
|
||||
|
||||
$('#postTitle').text(postTitle);
|
||||
$('#postDescription').text(postDescription);
|
||||
quill.setContents(postContent);
|
||||
}
|
||||
|
||||
const contentID = $(this).data('contentid');
|
||||
const translationID = $(this).data('translationid');
|
||||
|
||||
$('#contentID').val(contentID);
|
||||
$('#translationID').val(translationID);
|
||||
$('#postLanguage').val($(this).data('lang'));
|
||||
|
||||
getTranslationData();
|
||||
getContentData();
|
||||
|
||||
$(this).addClass('active');
|
||||
});
|
||||
});
|
||||
|
||||
function sendPost(executionFinished) {
|
||||
const postID = $('#postID').val(),
|
||||
contentID = $('#contentID').val(),
|
||||
translationID = $('#translationID').val(),
|
||||
postImage = $('#uploadedImage').val(),
|
||||
postTitle = $('#postTitle').text(),
|
||||
postDescription = $('#postDescription').text(),
|
||||
postContent = $('.ql-editor').html(),
|
||||
postPublishDate = $('#postPublishDate').val(),
|
||||
postUrl = $('#postUrl').val(),
|
||||
postCategory = $('#postCategory').val(),
|
||||
postLanguage = $('#postLanguage').val(),
|
||||
postTags = $('#postTags').tagsinput('items');
|
||||
|
||||
$.ajax({
|
||||
url: 'http://192.168.178.39/admin/blog/sendEdit',
|
||||
method: 'post',
|
||||
data: {
|
||||
postID,
|
||||
contentID,
|
||||
translationID,
|
||||
postImage,
|
||||
postTitle,
|
||||
postDescription,
|
||||
postContent,
|
||||
postPublishDate,
|
||||
postUrl,
|
||||
postCategory,
|
||||
postLanguage,
|
||||
postTags
|
||||
},
|
||||
success: (result) => {
|
||||
console.log(result);
|
||||
|
||||
if (result.success) {
|
||||
$('#postID').val(result.postID);
|
||||
$('#contentID').val(result.contentID);
|
||||
$('#switchLanguages > li.active').data('contentid', result.contentID);
|
||||
$('#translationID').val(result.translationID);
|
||||
$('#switchLanguages > li.active').data('translationid', result.translationID);
|
||||
|
||||
$('.snackbar-container').append(`<div class="alert alert-success snackbar" role="alert">${result.message}</div>`);
|
||||
} else {
|
||||
$('.snackbar-container').append(`<div class="alert alert-danger snackbar" role="alert">${result.message}</div>`);
|
||||
}
|
||||
},
|
||||
error: console.log
|
||||
}).done(() => {
|
||||
if (executionFinished)
|
||||
executionFinished()
|
||||
});
|
||||
}
|
||||
|
||||
function publishPost() {
|
||||
const postID = $('#postID').val(),
|
||||
contentID = $('#contentID').val(),
|
||||
contentDE = $('#switchLanguages').find('> li[data-lang=de]').data('contentid'),
|
||||
contentEN = $('#switchLanguages').find('> li[data-lang=en]').data('contentid'),
|
||||
contentFR = $('#switchLanguages').find('> li[data-lang=fr]').data('contentid');
|
||||
|
||||
$.ajax({
|
||||
url: 'http://192.168.178.39/admin/blog/publishPost',
|
||||
method: 'post',
|
||||
data: {
|
||||
postID, contentID,
|
||||
contentIDs: {'de': contentDE, 'en': contentEN, 'fr': contentFR}
|
||||
},
|
||||
success: (result) => {
|
||||
console.log(result);
|
||||
$('#blogSubmit').button('reset');
|
||||
if (result.success) {
|
||||
$('#postUrl').attr('disabled', '');
|
||||
changeUrl = false;
|
||||
$('.snackbar-container').append(`<div class="alert alert-success snackbar" role="alert">${result.message}</div>`);
|
||||
} else {
|
||||
$('.snackbar-container').append(`<div class="alert alert-danger snackbar" role="alert">${result.message}</div>`);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$('#blogPostSave').on('click', () => {
|
||||
$(this).button('loading');
|
||||
sendPost();
|
||||
});
|
||||
|
||||
$('#blogSubmit').on('click', function () {
|
||||
$(this).button('loading');
|
||||
sendPost(publishPost);
|
||||
});
|
||||
|
||||
// Upload Header Image
|
||||
$('.upload-btn').on('change', '.upload-image', function () {
|
||||
$('.upload-btn').fadeOut();
|
||||
$('.img-container').append('<i class="fa fa-cog fa-spin fa-4x"></i>');
|
||||
|
||||
const file = this.files[0],
|
||||
imageFile = file.type,
|
||||
match = ["image/jpeg", "image/jpg", "image/png"];
|
||||
if (match.indexOf(imageFile) === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', () => {
|
||||
$('.img-container').css('background-image', 'url(' + reader.result + ')');
|
||||
|
||||
$.ajax({
|
||||
url: 'http://192.168.178.39/admin/files/uploadImage',
|
||||
type: 'POST',
|
||||
data: {
|
||||
image: reader.result,
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
},
|
||||
success: (data) => {
|
||||
if (data.success) {
|
||||
$('.img-container > .fa').removeClass('fa-spin').removeClass('fa-cog').addClass('fa-check').addClass('text-success')
|
||||
.delay(2000).fadeOut();
|
||||
$('#uploadedImage').val(data.url);
|
||||
$('.snackbar-container').append(`<div class="alert alert-success snackbar" role="alert">${data.message}</div>`);
|
||||
} else {
|
||||
$('.img-container > .fa').removeClass('fa-spin').removeClass('fa-cog').addClass('fa-cross').addClass('text-danger')
|
||||
.delay(2000).fadeOut();
|
||||
$('.img-container').delay(2000).css('background-image', '');
|
||||
$('.snackbar-container').append(`<div class="alert alert-danger snackbar" role="alert">${data.message}</div>`);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
$('.upload-btn').fadeIn();
|
||||
}, 2500);
|
||||
}
|
||||
});
|
||||
});
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
||||
var substringMatcher = function (strs) {
|
||||
return function findMatches(q, cb) {
|
||||
var matches, substringRegex;
|
||||
|
||||
// an array that will be populated with substring matches
|
||||
matches = [];
|
||||
|
||||
// regex used to determine if a string contains the substring `q`
|
||||
substrRegex = new RegExp(q, 'i');
|
||||
|
||||
// iterate through the pool of strings and for any string that
|
||||
// contains the substring `q`, add it to the `matches` array
|
||||
$.each(strs, function (i, str) {
|
||||
if (substrRegex.test(str)) {
|
||||
matches.push(str);
|
||||
}
|
||||
});
|
||||
|
||||
cb(matches);
|
||||
};
|
||||
};
|
||||
|
||||
|
135
assets/js/blog.js
Normal file
@@ -0,0 +1,135 @@
|
||||
$(document).ready(function () {
|
||||
$.ajax({
|
||||
url: "http://192.168.178.39/blog/getComments",
|
||||
data: {
|
||||
url: window.location.pathname
|
||||
},
|
||||
beforeSend: function () {
|
||||
$("#comment-list").html("<h2><i class='fa fa-cog fa-spin'></i> Loading Comments</h2>");
|
||||
},
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
$("#comment-list").html(data);
|
||||
},
|
||||
error: function () {
|
||||
$("#comment-list").html("<div class='alert alert-danger' role='alert'><b>Error!</b> Couldn't load the comments for this posts caused by an unkown error! Please try again later or contact the admin for help.</div>");
|
||||
}
|
||||
});
|
||||
|
||||
$('.blog .info-box').each((el) => {
|
||||
console.log(el);
|
||||
});
|
||||
});
|
||||
|
||||
$('#commentForm').submit(function (event) {
|
||||
event.preventDefault();
|
||||
addComment();
|
||||
});
|
||||
|
||||
var addComment = function () {
|
||||
if ($('#commentField').val() !== '') {
|
||||
var comment = $('#commentField').val();
|
||||
comment.replace("\n", "<br>");
|
||||
|
||||
var item;
|
||||
$.ajax({
|
||||
url: "http://192.168.178.39/blog/comment",
|
||||
method: 'POST',
|
||||
data: {
|
||||
url: window.location.pathname,
|
||||
comment: comment
|
||||
},
|
||||
beforeSend: function () {
|
||||
var date = new Date();
|
||||
date = date.getDate() + '.' + date.getMonth() + '.' + date.getYear() + ' ' + date.getHours() + ':' + date.getMinutes() + ' Uhr';
|
||||
item = $(`
|
||||
<li>
|
||||
<div class="well comment-well">
|
||||
<div class="d-inline-block mr-2">
|
||||
<div class="image-placeholder">
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-inline-block mr-2">
|
||||
<h3>
|
||||
<small>
|
||||
von Dir / ${date}
|
||||
</small>
|
||||
</h3>
|
||||
<p class="comment">${comment}</p>
|
||||
</div>
|
||||
<div class="loading-container d-inline-block">
|
||||
<i class="fa fa-cog fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
`);
|
||||
|
||||
$('#comment-list').prepend(item);
|
||||
$('#commentField').attr('disabled', '');
|
||||
$('#addComment').attr('disabled', '');
|
||||
},
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
|
||||
if (data.type === 'success') {
|
||||
$('#commentField').val('');
|
||||
$('#notice-container').html('<div class="alert alert-success" role="alert">Dein Kommentar wurde <b>erfolgreich</b> gesendet!</div>');
|
||||
$('.loading-container', item).css('background-color', '#28b62c');
|
||||
$('.loading-container i', item)
|
||||
.removeClass('fa-cog').removeClass('fa-spin')
|
||||
.addClass('fa-check').css('color', '#fff');
|
||||
$('.content h3 small', item).hide()
|
||||
.html(`von <a href="/user/${data.content.username}" target="_blank">${data.content.displayname}</a> / ${data.content.date}`).fadeIn();
|
||||
$('.loading-container', item).delay(2000).fadeOut(2000);
|
||||
$('.comment-well', item).prepend(`<img src="${data.content.profilePic}" style="display:none">`);
|
||||
$('.comment-well img', item).fadeIn(2000);
|
||||
$('.image-placeholder', item).remove();
|
||||
wait(8000);
|
||||
$('.content', item).removeClass('small');
|
||||
$('.comment-count').text(parseInt($('.comment-count').text()) + 1);
|
||||
} else {
|
||||
|
||||
}
|
||||
},
|
||||
error: function (data) {
|
||||
console.log(data);
|
||||
$('#notice-container').html('<div class="alert alert-danger" role="alert"><b>Error:</b> Comment couldn\'t be sent caused by an unkown error! Please try again later or contact the admin to get help!</div>')
|
||||
$('div.loading-container', item).css('background-color', '#ff4136');
|
||||
$('div.loading-container i', item).removeClass('fa-cog').removeClass('fa-spin').addClass('fa-exclamation').css("color", '#fff');
|
||||
$(item).delay(3000).fadeOut(2000);
|
||||
},
|
||||
complete: function () {
|
||||
$('#commentField').removeAttr('disabled');
|
||||
$('#addComment').html('Kommentar senden').removeAttr('disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function wait(ms) {
|
||||
var start = new Date().getTime();
|
||||
var end = start;
|
||||
while (end < start + ms) {
|
||||
end = new Date().getTime();
|
||||
}
|
||||
}
|
||||
|
||||
function likeDislike(postID) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "http://192.168.178.39/blog/like",
|
||||
data: {
|
||||
postID: postID
|
||||
},
|
||||
success: function (data) {
|
||||
if (data == "no-user") {
|
||||
$('#loginModal').modal('show');
|
||||
} else {
|
||||
data = data.split(":");
|
||||
|
||||
$('.like-toggle-icon').toggleClass('-checked');
|
||||
$('.like-count').text(data[1]);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
174
assets/js/blots/block.js
Normal file
@@ -0,0 +1,174 @@
|
||||
import extend from 'extend';
|
||||
import Delta from 'quill-delta';
|
||||
import Parchment from 'parchment';
|
||||
import Break from './break';
|
||||
import Inline from './inline';
|
||||
import TextBlot from './text';
|
||||
|
||||
|
||||
const NEWLINE_LENGTH = 1;
|
||||
|
||||
|
||||
class BlockEmbed extends Parchment.Embed {
|
||||
attach() {
|
||||
super.attach();
|
||||
this.attributes = new Parchment.Attributor.Store(this.domNode);
|
||||
}
|
||||
|
||||
delta() {
|
||||
return new Delta().insert(this.value(), extend(this.formats(), this.attributes.values()));
|
||||
}
|
||||
|
||||
format(name, value) {
|
||||
let attribute = Parchment.query(name, Parchment.Scope.BLOCK_ATTRIBUTE);
|
||||
if (attribute != null) {
|
||||
this.attributes.attribute(attribute, value);
|
||||
}
|
||||
}
|
||||
|
||||
formatAt(index, length, name, value) {
|
||||
this.format(name, value);
|
||||
}
|
||||
|
||||
insertAt(index, value, def) {
|
||||
if (typeof value === 'string' && value.endsWith('\n')) {
|
||||
let block = Parchment.create(Block.blotName);
|
||||
this.parent.insertBefore(block, index === 0 ? this : this.next);
|
||||
block.insertAt(0, value.slice(0, -1));
|
||||
} else {
|
||||
super.insertAt(index, value, def);
|
||||
}
|
||||
}
|
||||
}
|
||||
BlockEmbed.scope = Parchment.Scope.BLOCK_BLOT;
|
||||
// It is important for cursor behavior BlockEmbeds use tags that are block level elements
|
||||
|
||||
|
||||
class Block extends Parchment.Block {
|
||||
constructor(domNode) {
|
||||
super(domNode);
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
delta() {
|
||||
if (this.cache.delta == null) {
|
||||
this.cache.delta = this.descendants(Parchment.Leaf).reduce((delta, leaf) => {
|
||||
if (leaf.length() === 0) {
|
||||
return delta;
|
||||
} else {
|
||||
return delta.insert(leaf.value(), bubbleFormats(leaf));
|
||||
}
|
||||
}, new Delta()).insert('\n', bubbleFormats(this));
|
||||
}
|
||||
return this.cache.delta;
|
||||
}
|
||||
|
||||
deleteAt(index, length) {
|
||||
super.deleteAt(index, length);
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
formatAt(index, length, name, value) {
|
||||
if (length <= 0) return;
|
||||
if (Parchment.query(name, Parchment.Scope.BLOCK)) {
|
||||
if (index + length === this.length()) {
|
||||
this.format(name, value);
|
||||
}
|
||||
} else {
|
||||
super.formatAt(index, Math.min(length, this.length() - index - 1), name, value);
|
||||
}
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
insertAt(index, value, def) {
|
||||
if (def != null) return super.insertAt(index, value, def);
|
||||
if (value.length === 0) return;
|
||||
let lines = value.split('\n');
|
||||
let text = lines.shift();
|
||||
if (text.length > 0) {
|
||||
if (index < this.length() - 1 || this.children.tail == null) {
|
||||
super.insertAt(Math.min(index, this.length() - 1), text);
|
||||
} else {
|
||||
this.children.tail.insertAt(this.children.tail.length(), text);
|
||||
}
|
||||
this.cache = {};
|
||||
}
|
||||
let block = this;
|
||||
lines.reduce(function(index, line) {
|
||||
block = block.split(index, true);
|
||||
block.insertAt(0, line);
|
||||
return line.length;
|
||||
}, index + text.length);
|
||||
}
|
||||
|
||||
insertBefore(blot, ref) {
|
||||
let head = this.children.head;
|
||||
super.insertBefore(blot, ref);
|
||||
if (head instanceof Break) {
|
||||
head.remove();
|
||||
}
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
length() {
|
||||
if (this.cache.length == null) {
|
||||
this.cache.length = super.length() + NEWLINE_LENGTH;
|
||||
}
|
||||
return this.cache.length;
|
||||
}
|
||||
|
||||
moveChildren(target, ref) {
|
||||
super.moveChildren(target, ref);
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
optimize(context) {
|
||||
super.optimize(context);
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
path(index) {
|
||||
return super.path(index, true);
|
||||
}
|
||||
|
||||
removeChild(child) {
|
||||
super.removeChild(child);
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
split(index, force = false) {
|
||||
if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) {
|
||||
let clone = this.clone();
|
||||
if (index === 0) {
|
||||
this.parent.insertBefore(clone, this);
|
||||
return this;
|
||||
} else {
|
||||
this.parent.insertBefore(clone, this.next);
|
||||
return clone;
|
||||
}
|
||||
} else {
|
||||
let next = super.split(index, force);
|
||||
this.cache = {};
|
||||
return next;
|
||||
}
|
||||
}
|
||||
}
|
||||
Block.blotName = 'block';
|
||||
Block.tagName = 'P';
|
||||
Block.defaultChild = 'break';
|
||||
Block.allowedChildren = [Inline, Parchment.Embed, TextBlot];
|
||||
|
||||
|
||||
function bubbleFormats(blot, formats = {}) {
|
||||
if (blot == null) return formats;
|
||||
if (typeof blot.formats === 'function') {
|
||||
formats = extend(formats, blot.formats());
|
||||
}
|
||||
if (blot.parent == null || blot.parent.blotName == 'scroll' || blot.parent.statics.scope !== blot.statics.scope) {
|
||||
return formats;
|
||||
}
|
||||
return bubbleFormats(blot.parent, formats);
|
||||
}
|
||||
|
||||
|
||||
export { bubbleFormats, BlockEmbed, Block as default };
|
29
assets/js/blots/break.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import Parchment from 'parchment';
|
||||
|
||||
|
||||
class Break extends Parchment.Embed {
|
||||
static value() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
insertInto(parent, ref) {
|
||||
if (parent.children.length === 0) {
|
||||
super.insertInto(parent, ref);
|
||||
} else {
|
||||
this.remove();
|
||||
}
|
||||
}
|
||||
|
||||
length() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
value() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
Break.blotName = 'break';
|
||||
Break.tagName = 'BR';
|
||||
|
||||
|
||||
export default Break;
|
9
assets/js/blots/container.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import Parchment from 'parchment';
|
||||
import Block, { BlockEmbed } from './block';
|
||||
|
||||
|
||||
class Container extends Parchment.Container { }
|
||||
Container.allowedChildren = [Block, BlockEmbed, Container];
|
||||
|
||||
|
||||
export default Container;
|
116
assets/js/blots/cursor.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import Parchment from 'parchment';
|
||||
import TextBlot from './text';
|
||||
|
||||
|
||||
class Cursor extends Parchment.Embed {
|
||||
static value() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
constructor(domNode, selection) {
|
||||
super(domNode);
|
||||
this.selection = selection;
|
||||
this.textNode = document.createTextNode(Cursor.CONTENTS);
|
||||
this.domNode.appendChild(this.textNode);
|
||||
this._length = 0;
|
||||
}
|
||||
|
||||
detach() {
|
||||
// super.detach() will also clear domNode.__blot
|
||||
if (this.parent != null) this.parent.removeChild(this);
|
||||
}
|
||||
|
||||
format(name, value) {
|
||||
if (this._length !== 0) {
|
||||
return super.format(name, value);
|
||||
}
|
||||
let target = this, index = 0;
|
||||
while (target != null && target.statics.scope !== Parchment.Scope.BLOCK_BLOT) {
|
||||
index += target.offset(target.parent);
|
||||
target = target.parent;
|
||||
}
|
||||
if (target != null) {
|
||||
this._length = Cursor.CONTENTS.length;
|
||||
target.optimize();
|
||||
target.formatAt(index, Cursor.CONTENTS.length, name, value);
|
||||
this._length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
index(node, offset) {
|
||||
if (node === this.textNode) return 0;
|
||||
return super.index(node, offset);
|
||||
}
|
||||
|
||||
length() {
|
||||
return this._length;
|
||||
}
|
||||
|
||||
position() {
|
||||
return [this.textNode, this.textNode.data.length];
|
||||
}
|
||||
|
||||
remove() {
|
||||
super.remove();
|
||||
this.parent = null;
|
||||
}
|
||||
|
||||
restore() {
|
||||
if (this.selection.composing || this.parent == null) return;
|
||||
let textNode = this.textNode;
|
||||
let range = this.selection.getNativeRange();
|
||||
let restoreText, start, end;
|
||||
if (range != null && range.start.node === textNode && range.end.node === textNode) {
|
||||
[restoreText, start, end] = [textNode, range.start.offset, range.end.offset];
|
||||
}
|
||||
// Link format will insert text outside of anchor tag
|
||||
while (this.domNode.lastChild != null && this.domNode.lastChild !== this.textNode) {
|
||||
this.domNode.parentNode.insertBefore(this.domNode.lastChild, this.domNode);
|
||||
}
|
||||
if (this.textNode.data !== Cursor.CONTENTS) {
|
||||
let text = this.textNode.data.split(Cursor.CONTENTS).join('');
|
||||
if (this.next instanceof TextBlot) {
|
||||
restoreText = this.next.domNode;
|
||||
this.next.insertAt(0, text);
|
||||
this.textNode.data = Cursor.CONTENTS;
|
||||
} else {
|
||||
this.textNode.data = text;
|
||||
this.parent.insertBefore(Parchment.create(this.textNode), this);
|
||||
this.textNode = document.createTextNode(Cursor.CONTENTS);
|
||||
this.domNode.appendChild(this.textNode);
|
||||
}
|
||||
}
|
||||
this.remove();
|
||||
if (start != null) {
|
||||
[start, end] = [start, end].map(function(offset) {
|
||||
return Math.max(0, Math.min(restoreText.data.length, offset - 1));
|
||||
});
|
||||
return {
|
||||
startNode: restoreText,
|
||||
startOffset: start,
|
||||
endNode: restoreText,
|
||||
endOffset: end
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
update(mutations, context) {
|
||||
if (mutations.some((mutation) => {
|
||||
return mutation.type === 'characterData' && mutation.target === this.textNode;
|
||||
})) {
|
||||
let range = this.restore();
|
||||
if (range) context.range = range;
|
||||
}
|
||||
}
|
||||
|
||||
value() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
Cursor.blotName = 'cursor';
|
||||
Cursor.className = 'ql-cursor';
|
||||
Cursor.tagName = 'span';
|
||||
Cursor.CONTENTS = "\uFEFF"; // Zero width no break space
|
||||
|
||||
|
||||
export default Cursor;
|
79
assets/js/blots/embed.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import Parchment from 'parchment';
|
||||
import TextBlot from './text';
|
||||
|
||||
const GUARD_TEXT = "\uFEFF";
|
||||
|
||||
|
||||
class Embed extends Parchment.Embed {
|
||||
constructor(node) {
|
||||
super(node);
|
||||
this.contentNode = document.createElement('span');
|
||||
this.contentNode.setAttribute('contenteditable', false);
|
||||
[].slice.call(this.domNode.childNodes).forEach((childNode) => {
|
||||
this.contentNode.appendChild(childNode);
|
||||
});
|
||||
this.leftGuard = document.createTextNode(GUARD_TEXT);
|
||||
this.rightGuard = document.createTextNode(GUARD_TEXT);
|
||||
this.domNode.appendChild(this.leftGuard);
|
||||
this.domNode.appendChild(this.contentNode);
|
||||
this.domNode.appendChild(this.rightGuard);
|
||||
}
|
||||
|
||||
index(node, offset) {
|
||||
if (node === this.leftGuard) return 0;
|
||||
if (node === this.rightGuard) return 1;
|
||||
return super.index(node, offset);
|
||||
}
|
||||
|
||||
restore(node) {
|
||||
let range, textNode;
|
||||
let text = node.data.split(GUARD_TEXT).join('');
|
||||
if (node === this.leftGuard) {
|
||||
if (this.prev instanceof TextBlot) {
|
||||
let prevLength = this.prev.length();
|
||||
this.prev.insertAt(prevLength, text);
|
||||
range = {
|
||||
startNode: this.prev.domNode,
|
||||
startOffset: prevLength + text.length
|
||||
};
|
||||
} else {
|
||||
textNode = document.createTextNode(text);
|
||||
this.parent.insertBefore(Parchment.create(textNode), this);
|
||||
range = {
|
||||
startNode: textNode,
|
||||
startOffset: text.length
|
||||
};
|
||||
}
|
||||
} else if (node === this.rightGuard) {
|
||||
if (this.next instanceof TextBlot) {
|
||||
this.next.insertAt(0, text);
|
||||
range = {
|
||||
startNode: this.next.domNode,
|
||||
startOffset: text.length
|
||||
}
|
||||
} else {
|
||||
textNode = document.createTextNode(text);
|
||||
this.parent.insertBefore(Parchment.create(textNode), this.next);
|
||||
range = {
|
||||
startNode: textNode,
|
||||
startOffset: text.length
|
||||
};
|
||||
}
|
||||
}
|
||||
node.data = GUARD_TEXT;
|
||||
return range;
|
||||
}
|
||||
|
||||
update(mutations, context) {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.type === 'characterData' &&
|
||||
(mutation.target === this.leftGuard || mutation.target === this.rightGuard)) {
|
||||
let range = this.restore(mutation.target);
|
||||
if (range) context.range = range;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default Embed;
|
50
assets/js/blots/inline.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import Text from './text';
|
||||
import Parchment from 'parchment';
|
||||
|
||||
|
||||
class Inline extends Parchment.Inline {
|
||||
static compare(self, other) {
|
||||
let selfIndex = Inline.order.indexOf(self);
|
||||
let otherIndex = Inline.order.indexOf(other);
|
||||
if (selfIndex >= 0 || otherIndex >= 0) {
|
||||
return selfIndex - otherIndex;
|
||||
} else if (self === other) {
|
||||
return 0;
|
||||
} else if (self < other) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
formatAt(index, length, name, value) {
|
||||
if (Inline.compare(this.statics.blotName, name) < 0 && Parchment.query(name, Parchment.Scope.BLOT)) {
|
||||
let blot = this.isolate(index, length);
|
||||
if (value) {
|
||||
blot.wrap(name, value);
|
||||
}
|
||||
} else {
|
||||
super.formatAt(index, length, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
optimize(context) {
|
||||
super.optimize(context);
|
||||
if (this.parent instanceof Inline &&
|
||||
Inline.compare(this.statics.blotName, this.parent.statics.blotName) > 0) {
|
||||
let parent = this.parent.isolate(this.offset(), this.length());
|
||||
this.moveChildren(parent);
|
||||
parent.wrap(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
Inline.allowedChildren = [Inline, Parchment.Embed, Text];
|
||||
// Lower index means deeper in the DOM tree, since not found (-1) is for embeds
|
||||
Inline.order = [
|
||||
'cursor', 'inline', // Must be lower
|
||||
'underline', 'strike', 'italic', 'bold', 'script',
|
||||
'link', 'code' // Must be higher
|
||||
];
|
||||
|
||||
|
||||
export default Inline;
|
173
assets/js/blots/scroll.js
Normal file
@@ -0,0 +1,173 @@
|
||||
import Parchment from 'parchment';
|
||||
import Emitter from '../core/emitter';
|
||||
import Block, { BlockEmbed } from './block';
|
||||
import Break from './break';
|
||||
import CodeBlock from '../formats/code';
|
||||
import Container from './container';
|
||||
|
||||
|
||||
function isLine(blot) {
|
||||
return (blot instanceof Block || blot instanceof BlockEmbed);
|
||||
}
|
||||
|
||||
|
||||
class Scroll extends Parchment.Scroll {
|
||||
constructor(domNode, config) {
|
||||
super(domNode);
|
||||
this.emitter = config.emitter;
|
||||
if (Array.isArray(config.whitelist)) {
|
||||
this.whitelist = config.whitelist.reduce(function(whitelist, format) {
|
||||
whitelist[format] = true;
|
||||
return whitelist;
|
||||
}, {});
|
||||
}
|
||||
// Some reason fixes composition issues with character languages in Windows/Chrome, Safari
|
||||
this.domNode.addEventListener('DOMNodeInserted', function() {});
|
||||
this.optimize();
|
||||
this.enable();
|
||||
}
|
||||
|
||||
batchStart() {
|
||||
this.batch = true;
|
||||
}
|
||||
|
||||
batchEnd() {
|
||||
this.batch = false;
|
||||
this.optimize();
|
||||
}
|
||||
|
||||
deleteAt(index, length) {
|
||||
let [first, offset] = this.line(index);
|
||||
let [last, ] = this.line(index + length);
|
||||
super.deleteAt(index, length);
|
||||
if (last != null && first !== last && offset > 0) {
|
||||
if (first instanceof BlockEmbed || last instanceof BlockEmbed) {
|
||||
this.optimize();
|
||||
return;
|
||||
}
|
||||
if (first instanceof CodeBlock) {
|
||||
let newlineIndex = first.newlineIndex(first.length(), true);
|
||||
if (newlineIndex > -1) {
|
||||
first = first.split(newlineIndex + 1);
|
||||
if (first === last) {
|
||||
this.optimize();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (last instanceof CodeBlock) {
|
||||
let newlineIndex = last.newlineIndex(0);
|
||||
if (newlineIndex > -1) {
|
||||
last.split(newlineIndex + 1);
|
||||
}
|
||||
}
|
||||
let ref = last.children.head instanceof Break ? null : last.children.head;
|
||||
first.moveChildren(last, ref);
|
||||
first.remove();
|
||||
}
|
||||
this.optimize();
|
||||
}
|
||||
|
||||
enable(enabled = true) {
|
||||
this.domNode.setAttribute('contenteditable', enabled);
|
||||
}
|
||||
|
||||
formatAt(index, length, format, value) {
|
||||
if (this.whitelist != null && !this.whitelist[format]) return;
|
||||
super.formatAt(index, length, format, value);
|
||||
this.optimize();
|
||||
}
|
||||
|
||||
insertAt(index, value, def) {
|
||||
if (def != null && this.whitelist != null && !this.whitelist[value]) return;
|
||||
if (index >= this.length()) {
|
||||
if (def == null || Parchment.query(value, Parchment.Scope.BLOCK) == null) {
|
||||
let blot = Parchment.create(this.statics.defaultChild);
|
||||
this.appendChild(blot);
|
||||
if (def == null && value.endsWith('\n')) {
|
||||
value = value.slice(0, -1);
|
||||
}
|
||||
blot.insertAt(0, value, def);
|
||||
} else {
|
||||
let embed = Parchment.create(value, def);
|
||||
this.appendChild(embed);
|
||||
}
|
||||
} else {
|
||||
super.insertAt(index, value, def);
|
||||
}
|
||||
this.optimize();
|
||||
}
|
||||
|
||||
insertBefore(blot, ref) {
|
||||
if (blot.statics.scope === Parchment.Scope.INLINE_BLOT) {
|
||||
let wrapper = Parchment.create(this.statics.defaultChild);
|
||||
wrapper.appendChild(blot);
|
||||
blot = wrapper;
|
||||
}
|
||||
super.insertBefore(blot, ref);
|
||||
}
|
||||
|
||||
leaf(index) {
|
||||
return this.path(index).pop() || [null, -1];
|
||||
}
|
||||
|
||||
line(index) {
|
||||
if (index === this.length()) {
|
||||
return this.line(index - 1);
|
||||
}
|
||||
return this.descendant(isLine, index);
|
||||
}
|
||||
|
||||
lines(index = 0, length = Number.MAX_VALUE) {
|
||||
let getLines = (blot, index, length) => {
|
||||
let lines = [], lengthLeft = length;
|
||||
blot.children.forEachAt(index, length, function(child, index, length) {
|
||||
if (isLine(child)) {
|
||||
lines.push(child);
|
||||
} else if (child instanceof Parchment.Container) {
|
||||
lines = lines.concat(getLines(child, index, lengthLeft));
|
||||
}
|
||||
lengthLeft -= length;
|
||||
});
|
||||
return lines;
|
||||
};
|
||||
return getLines(this, index, length);
|
||||
}
|
||||
|
||||
optimize(mutations = [], context = {}) {
|
||||
if (this.batch === true) return;
|
||||
super.optimize(mutations, context);
|
||||
if (mutations.length > 0) {
|
||||
this.emitter.emit(Emitter.events.SCROLL_OPTIMIZE, mutations, context);
|
||||
}
|
||||
}
|
||||
|
||||
path(index) {
|
||||
return super.path(index).slice(1); // Exclude self
|
||||
}
|
||||
|
||||
update(mutations) {
|
||||
if (this.batch === true) return;
|
||||
let source = Emitter.sources.USER;
|
||||
if (typeof mutations === 'string') {
|
||||
source = mutations;
|
||||
}
|
||||
if (!Array.isArray(mutations)) {
|
||||
mutations = this.observer.takeRecords();
|
||||
}
|
||||
if (mutations.length > 0) {
|
||||
this.emitter.emit(Emitter.events.SCROLL_BEFORE_UPDATE, source, mutations);
|
||||
}
|
||||
super.update(mutations.concat([])); // pass copy
|
||||
if (mutations.length > 0) {
|
||||
this.emitter.emit(Emitter.events.SCROLL_UPDATE, source, mutations);
|
||||
}
|
||||
}
|
||||
}
|
||||
Scroll.blotName = 'scroll';
|
||||
Scroll.className = 'ql-editor';
|
||||
Scroll.tagName = 'DIV';
|
||||
Scroll.defaultChild = 'block';
|
||||
Scroll.allowedChildren = [Block, BlockEmbed, Container];
|
||||
|
||||
|
||||
export default Scroll;
|
5
assets/js/blots/text.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import Parchment from 'parchment';
|
||||
|
||||
class TextBlot extends Parchment.Text { }
|
||||
|
||||
export default TextBlot;
|
267
assets/js/core/editor.js
Normal file
@@ -0,0 +1,267 @@
|
||||
import Delta from 'quill-delta';
|
||||
import DeltaOp from 'quill-delta/lib/op';
|
||||
import Parchment from 'parchment';
|
||||
import CodeBlock from '../formats/code';
|
||||
import CursorBlot from '../blots/cursor';
|
||||
import Block, { bubbleFormats } from '../blots/block';
|
||||
import Break from '../blots/break';
|
||||
import clone from 'clone';
|
||||
import equal from 'deep-equal';
|
||||
import extend from 'extend';
|
||||
|
||||
|
||||
const ASCII = /^[ -~]*$/;
|
||||
|
||||
|
||||
class Editor {
|
||||
constructor(scroll) {
|
||||
this.scroll = scroll;
|
||||
this.delta = this.getDelta();
|
||||
}
|
||||
|
||||
applyDelta(delta) {
|
||||
let consumeNextNewline = false;
|
||||
this.scroll.update();
|
||||
let scrollLength = this.scroll.length();
|
||||
this.scroll.batchStart();
|
||||
delta = normalizeDelta(delta);
|
||||
delta.reduce((index, op) => {
|
||||
let length = op.retain || op.delete || op.insert.length || 1;
|
||||
let attributes = op.attributes || {};
|
||||
if (op.insert != null) {
|
||||
if (typeof op.insert === 'string') {
|
||||
let text = op.insert;
|
||||
if (text.endsWith('\n') && consumeNextNewline) {
|
||||
consumeNextNewline = false;
|
||||
text = text.slice(0, -1);
|
||||
}
|
||||
if (index >= scrollLength && !text.endsWith('\n')) {
|
||||
consumeNextNewline = true;
|
||||
}
|
||||
this.scroll.insertAt(index, text);
|
||||
let [line, offset] = this.scroll.line(index);
|
||||
let formats = extend({}, bubbleFormats(line));
|
||||
if (line instanceof Block) {
|
||||
let [leaf, ] = line.descendant(Parchment.Leaf, offset);
|
||||
formats = extend(formats, bubbleFormats(leaf));
|
||||
}
|
||||
attributes = DeltaOp.attributes.diff(formats, attributes) || {};
|
||||
} else if (typeof op.insert === 'object') {
|
||||
let key = Object.keys(op.insert)[0]; // There should only be one key
|
||||
if (key == null) return index;
|
||||
this.scroll.insertAt(index, key, op.insert[key]);
|
||||
}
|
||||
scrollLength += length;
|
||||
}
|
||||
Object.keys(attributes).forEach((name) => {
|
||||
this.scroll.formatAt(index, length, name, attributes[name]);
|
||||
});
|
||||
return index + length;
|
||||
}, 0);
|
||||
delta.reduce((index, op) => {
|
||||
if (typeof op.delete === 'number') {
|
||||
this.scroll.deleteAt(index, op.delete);
|
||||
return index;
|
||||
}
|
||||
return index + (op.retain || op.insert.length || 1);
|
||||
}, 0);
|
||||
this.scroll.batchEnd();
|
||||
return this.update(delta);
|
||||
}
|
||||
|
||||
deleteText(index, length) {
|
||||
this.scroll.deleteAt(index, length);
|
||||
return this.update(new Delta().retain(index).delete(length));
|
||||
}
|
||||
|
||||
formatLine(index, length, formats = {}) {
|
||||
this.scroll.update();
|
||||
Object.keys(formats).forEach((format) => {
|
||||
if (this.scroll.whitelist != null && !this.scroll.whitelist[format]) return;
|
||||
let lines = this.scroll.lines(index, Math.max(length, 1));
|
||||
let lengthRemaining = length;
|
||||
lines.forEach((line) => {
|
||||
let lineLength = line.length();
|
||||
if (!(line instanceof CodeBlock)) {
|
||||
line.format(format, formats[format]);
|
||||
} else {
|
||||
let codeIndex = index - line.offset(this.scroll);
|
||||
let codeLength = line.newlineIndex(codeIndex + lengthRemaining) - codeIndex + 1;
|
||||
line.formatAt(codeIndex, codeLength, format, formats[format]);
|
||||
}
|
||||
lengthRemaining -= lineLength;
|
||||
});
|
||||
});
|
||||
this.scroll.optimize();
|
||||
return this.update(new Delta().retain(index).retain(length, clone(formats)));
|
||||
}
|
||||
|
||||
formatText(index, length, formats = {}) {
|
||||
Object.keys(formats).forEach((format) => {
|
||||
this.scroll.formatAt(index, length, format, formats[format]);
|
||||
});
|
||||
return this.update(new Delta().retain(index).retain(length, clone(formats)));
|
||||
}
|
||||
|
||||
getContents(index, length) {
|
||||
return this.delta.slice(index, index + length);
|
||||
}
|
||||
|
||||
getDelta() {
|
||||
return this.scroll.lines().reduce((delta, line) => {
|
||||
return delta.concat(line.delta());
|
||||
}, new Delta());
|
||||
}
|
||||
|
||||
getFormat(index, length = 0) {
|
||||
let lines = [], leaves = [];
|
||||
if (length === 0) {
|
||||
this.scroll.path(index).forEach(function(path) {
|
||||
let [blot, ] = path;
|
||||
if (blot instanceof Block) {
|
||||
lines.push(blot);
|
||||
} else if (blot instanceof Parchment.Leaf) {
|
||||
leaves.push(blot);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
lines = this.scroll.lines(index, length);
|
||||
leaves = this.scroll.descendants(Parchment.Leaf, index, length);
|
||||
}
|
||||
let formatsArr = [lines, leaves].map(function(blots) {
|
||||
if (blots.length === 0) return {};
|
||||
let formats = bubbleFormats(blots.shift());
|
||||
while (Object.keys(formats).length > 0) {
|
||||
let blot = blots.shift();
|
||||
if (blot == null) return formats;
|
||||
formats = combineFormats(bubbleFormats(blot), formats);
|
||||
}
|
||||
return formats;
|
||||
});
|
||||
return extend.apply(extend, formatsArr);
|
||||
}
|
||||
|
||||
getText(index, length) {
|
||||
return this.getContents(index, length).filter(function(op) {
|
||||
return typeof op.insert === 'string';
|
||||
}).map(function(op) {
|
||||
return op.insert;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
insertEmbed(index, embed, value) {
|
||||
this.scroll.insertAt(index, embed, value);
|
||||
return this.update(new Delta().retain(index).insert({ [embed]: value }));
|
||||
}
|
||||
|
||||
insertText(index, text, formats = {}) {
|
||||
text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
||||
this.scroll.insertAt(index, text);
|
||||
Object.keys(formats).forEach((format) => {
|
||||
this.scroll.formatAt(index, text.length, format, formats[format]);
|
||||
});
|
||||
return this.update(new Delta().retain(index).insert(text, clone(formats)));
|
||||
}
|
||||
|
||||
isBlank() {
|
||||
if (this.scroll.children.length == 0) return true;
|
||||
if (this.scroll.children.length > 1) return false;
|
||||
let block = this.scroll.children.head;
|
||||
if (block.statics.blotName !== Block.blotName) return false;
|
||||
if (block.children.length > 1) return false;
|
||||
return block.children.head instanceof Break;
|
||||
}
|
||||
|
||||
removeFormat(index, length) {
|
||||
let text = this.getText(index, length);
|
||||
let [line, offset] = this.scroll.line(index + length);
|
||||
let suffixLength = 0, suffix = new Delta();
|
||||
if (line != null) {
|
||||
if (!(line instanceof CodeBlock)) {
|
||||
suffixLength = line.length() - offset;
|
||||
} else {
|
||||
suffixLength = line.newlineIndex(offset) - offset + 1;
|
||||
}
|
||||
suffix = line.delta().slice(offset, offset + suffixLength - 1).insert('\n');
|
||||
}
|
||||
let contents = this.getContents(index, length + suffixLength);
|
||||
let diff = contents.diff(new Delta().insert(text).concat(suffix));
|
||||
let delta = new Delta().retain(index).concat(diff);
|
||||
return this.applyDelta(delta);
|
||||
}
|
||||
|
||||
update(change, mutations = [], cursorIndex = undefined) {
|
||||
let oldDelta = this.delta;
|
||||
if (mutations.length === 1 &&
|
||||
mutations[0].type === 'characterData' &&
|
||||
mutations[0].target.data.match(ASCII) &&
|
||||
Parchment.find(mutations[0].target)) {
|
||||
// Optimization for character changes
|
||||
let textBlot = Parchment.find(mutations[0].target);
|
||||
let formats = bubbleFormats(textBlot);
|
||||
let index = textBlot.offset(this.scroll);
|
||||
let oldValue = mutations[0].oldValue.replace(CursorBlot.CONTENTS, '');
|
||||
let oldText = new Delta().insert(oldValue);
|
||||
let newText = new Delta().insert(textBlot.value());
|
||||
let diffDelta = new Delta().retain(index).concat(oldText.diff(newText, cursorIndex));
|
||||
change = diffDelta.reduce(function(delta, op) {
|
||||
if (op.insert) {
|
||||
return delta.insert(op.insert, formats);
|
||||
} else {
|
||||
return delta.push(op);
|
||||
}
|
||||
}, new Delta());
|
||||
this.delta = oldDelta.compose(change);
|
||||
} else {
|
||||
this.delta = this.getDelta();
|
||||
if (!change || !equal(oldDelta.compose(change), this.delta)) {
|
||||
change = oldDelta.diff(this.delta, cursorIndex);
|
||||
}
|
||||
}
|
||||
return change;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function combineFormats(formats, combined) {
|
||||
return Object.keys(combined).reduce(function(merged, name) {
|
||||
if (formats[name] == null) return merged;
|
||||
if (combined[name] === formats[name]) {
|
||||
merged[name] = combined[name];
|
||||
} else if (Array.isArray(combined[name])) {
|
||||
if (combined[name].indexOf(formats[name]) < 0) {
|
||||
merged[name] = combined[name].concat([formats[name]]);
|
||||
}
|
||||
} else {
|
||||
merged[name] = [combined[name], formats[name]];
|
||||
}
|
||||
return merged;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function normalizeDelta(delta) {
|
||||
return delta.reduce(function(delta, op) {
|
||||
if (op.insert === 1) {
|
||||
let attributes = clone(op.attributes);
|
||||
delete attributes['image'];
|
||||
return delta.insert({ image: op.attributes.image }, attributes);
|
||||
}
|
||||
if (op.attributes != null && (op.attributes.list === true || op.attributes.bullet === true)) {
|
||||
op = clone(op);
|
||||
if (op.attributes.list) {
|
||||
op.attributes.list = 'ordered';
|
||||
} else {
|
||||
op.attributes.list = 'bullet';
|
||||
delete op.attributes.bullet;
|
||||
}
|
||||
}
|
||||
if (typeof op.insert === 'string') {
|
||||
let text = op.insert.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
||||
return delta.insert(text, op.attributes);
|
||||
}
|
||||
return delta.push(op);
|
||||
}, new Delta());
|
||||
}
|
||||
|
||||
|
||||
export default Editor;
|
63
assets/js/core/emitter.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import logger from './logger';
|
||||
|
||||
let debug = logger('quill:events');
|
||||
|
||||
const EVENTS = ['selectionchange', 'mousedown', 'mouseup', 'click'];
|
||||
|
||||
EVENTS.forEach(function(eventName) {
|
||||
document.addEventListener(eventName, (...args) => {
|
||||
[].slice.call(document.querySelectorAll('.ql-container')).forEach((node) => {
|
||||
// TODO use WeakMap
|
||||
if (node.__quill && node.__quill.emitter) {
|
||||
node.__quill.emitter.handleDOM(...args);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
class Emitter extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
this.listeners = {};
|
||||
this.on('error', debug.error);
|
||||
}
|
||||
|
||||
emit() {
|
||||
debug.log.apply(debug, arguments);
|
||||
super.emit.apply(this, arguments);
|
||||
}
|
||||
|
||||
handleDOM(event, ...args) {
|
||||
(this.listeners[event.type] || []).forEach(function({ node, handler }) {
|
||||
if (event.target === node || node.contains(event.target)) {
|
||||
handler(event, ...args);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
listenDOM(eventName, node, handler) {
|
||||
if (!this.listeners[eventName]) {
|
||||
this.listeners[eventName] = [];
|
||||
}
|
||||
this.listeners[eventName].push({ node, handler })
|
||||
}
|
||||
}
|
||||
|
||||
Emitter.events = {
|
||||
EDITOR_CHANGE : 'editor-change',
|
||||
SCROLL_BEFORE_UPDATE : 'scroll-before-update',
|
||||
SCROLL_OPTIMIZE : 'scroll-optimize',
|
||||
SCROLL_UPDATE : 'scroll-update',
|
||||
SELECTION_CHANGE : 'selection-change',
|
||||
TEXT_CHANGE : 'text-change'
|
||||
};
|
||||
Emitter.sources = {
|
||||
API : 'api',
|
||||
SILENT : 'silent',
|
||||
USER : 'user'
|
||||
};
|
||||
|
||||
|
||||
export default Emitter;
|
22
assets/js/core/logger.js
Normal file
@@ -0,0 +1,22 @@
|
||||
let levels = ['error', 'warn', 'log', 'info'];
|
||||
let level = 'warn';
|
||||
|
||||
function debug(method, ...args) {
|
||||
if (levels.indexOf(method) <= levels.indexOf(level)) {
|
||||
console[method](...args); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
function namespace(ns) {
|
||||
return levels.reduce(function(logger, method) {
|
||||
logger[method] = debug.bind(console, method, ns);
|
||||
return logger;
|
||||
}, {});
|
||||
}
|
||||
|
||||
debug.level = namespace.level = function(newLevel) {
|
||||
level = newLevel;
|
||||
};
|
||||
|
||||
|
||||
export default namespace;
|
10
assets/js/core/module.js
Normal file
@@ -0,0 +1,10 @@
|
||||
class Module {
|
||||
constructor(quill, options = {}) {
|
||||
this.quill = quill;
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
Module.DEFAULTS = {};
|
||||
|
||||
|
||||
export default Module;
|
63
assets/js/core/polyfill.js
Normal file
@@ -0,0 +1,63 @@
|
||||
let elem = document.createElement('div');
|
||||
elem.classList.toggle('test-class', false);
|
||||
if (elem.classList.contains('test-class')) {
|
||||
let _toggle = DOMTokenList.prototype.toggle;
|
||||
DOMTokenList.prototype.toggle = function(token, force) {
|
||||
if (arguments.length > 1 && !this.contains(token) === !force) {
|
||||
return force;
|
||||
} else {
|
||||
return _toggle.call(this, token);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.startsWith) {
|
||||
String.prototype.startsWith = function(searchString, position){
|
||||
position = position || 0;
|
||||
return this.substr(position, searchString.length) === searchString;
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.endsWith) {
|
||||
String.prototype.endsWith = function(searchString, position) {
|
||||
var subjectString = this.toString();
|
||||
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
|
||||
position = subjectString.length;
|
||||
}
|
||||
position -= searchString.length;
|
||||
var lastIndex = subjectString.indexOf(searchString, position);
|
||||
return lastIndex !== -1 && lastIndex === position;
|
||||
};
|
||||
}
|
||||
|
||||
if (!Array.prototype.find) {
|
||||
Object.defineProperty(Array.prototype, "find", {
|
||||
value: function(predicate) {
|
||||
if (this === null) {
|
||||
throw new TypeError('Array.prototype.find called on null or undefined');
|
||||
}
|
||||
if (typeof predicate !== 'function') {
|
||||
throw new TypeError('predicate must be a function');
|
||||
}
|
||||
var list = Object(this);
|
||||
var length = list.length >>> 0;
|
||||
var thisArg = arguments[1];
|
||||
var value;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
value = list[i];
|
||||
if (predicate.call(thisArg, value, i, list)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Disable resizing in Firefox
|
||||
document.execCommand("enableObjectResizing", false, false);
|
||||
// Disable automatic linkifying in IE11
|
||||
document.execCommand("autoUrlDetect", false, false);
|
||||
});
|
506
assets/js/core/quill.js
Normal file
@@ -0,0 +1,506 @@
|
||||
import './polyfill';
|
||||
import Delta from 'quill-delta';
|
||||
import Editor from './editor';
|
||||
import Emitter from './emitter';
|
||||
import Module from './module';
|
||||
import Parchment from 'parchment';
|
||||
import Selection, { Range } from './selection';
|
||||
import extend from 'extend';
|
||||
import logger from './logger';
|
||||
import Theme from './theme';
|
||||
|
||||
let debug = logger('quill');
|
||||
|
||||
|
||||
class Quill {
|
||||
static debug(limit) {
|
||||
if (limit === true) {
|
||||
limit = 'log';
|
||||
}
|
||||
logger.level(limit);
|
||||
}
|
||||
|
||||
static find(node) {
|
||||
return node.__quill || Parchment.find(node);
|
||||
}
|
||||
|
||||
static import(name) {
|
||||
if (this.imports[name] == null) {
|
||||
debug.error(`Cannot import ${name}. Are you sure it was registered?`);
|
||||
}
|
||||
return this.imports[name];
|
||||
}
|
||||
|
||||
static register(path, target, overwrite = false) {
|
||||
if (typeof path !== 'string') {
|
||||
let name = path.attrName || path.blotName;
|
||||
if (typeof name === 'string') {
|
||||
// register(Blot | Attributor, overwrite)
|
||||
this.register('formats/' + name, path, target);
|
||||
} else {
|
||||
Object.keys(path).forEach((key) => {
|
||||
this.register(key, path[key], target);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (this.imports[path] != null && !overwrite) {
|
||||
debug.warn(`Overwriting ${path} with`, target);
|
||||
}
|
||||
this.imports[path] = target;
|
||||
if ((path.startsWith('blots/') || path.startsWith('formats/')) &&
|
||||
target.blotName !== 'abstract') {
|
||||
Parchment.register(target);
|
||||
} else if (path.startsWith('modules') && typeof target.register === 'function') {
|
||||
target.register();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(container, options = {}) {
|
||||
this.options = expandConfig(container, options);
|
||||
this.container = this.options.container;
|
||||
if (this.container == null) {
|
||||
return debug.error('Invalid Quill container', container);
|
||||
}
|
||||
if (this.options.debug) {
|
||||
Quill.debug(this.options.debug);
|
||||
}
|
||||
let html = this.container.innerHTML.trim();
|
||||
this.container.classList.add('ql-container');
|
||||
this.container.innerHTML = '';
|
||||
this.container.__quill = this;
|
||||
this.root = this.addContainer('ql-editor');
|
||||
this.root.classList.add('ql-blank');
|
||||
this.root.setAttribute('data-gramm', false);
|
||||
this.scrollingContainer = this.options.scrollingContainer || this.root;
|
||||
this.emitter = new Emitter();
|
||||
this.scroll = Parchment.create(this.root, {
|
||||
emitter: this.emitter,
|
||||
whitelist: this.options.formats
|
||||
});
|
||||
this.editor = new Editor(this.scroll);
|
||||
this.selection = new Selection(this.scroll, this.emitter);
|
||||
this.theme = new this.options.theme(this, this.options);
|
||||
this.keyboard = this.theme.addModule('keyboard');
|
||||
this.clipboard = this.theme.addModule('clipboard');
|
||||
this.history = this.theme.addModule('history');
|
||||
this.theme.init();
|
||||
this.emitter.on(Emitter.events.EDITOR_CHANGE, (type) => {
|
||||
if (type === Emitter.events.TEXT_CHANGE) {
|
||||
this.root.classList.toggle('ql-blank', this.editor.isBlank());
|
||||
}
|
||||
});
|
||||
this.emitter.on(Emitter.events.SCROLL_UPDATE, (source, mutations) => {
|
||||
let range = this.selection.lastRange;
|
||||
let index = range && range.length === 0 ? range.index : undefined;
|
||||
modify.call(this, () => {
|
||||
return this.editor.update(null, mutations, index);
|
||||
}, source);
|
||||
});
|
||||
let contents = this.clipboard.convert(`<div class='ql-editor' style="white-space: normal;">${html}<p><br></p></div>`);
|
||||
this.setContents(contents);
|
||||
this.history.clear();
|
||||
if (this.options.placeholder) {
|
||||
this.root.setAttribute('data-placeholder', this.options.placeholder);
|
||||
}
|
||||
if (this.options.readOnly) {
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
||||
addContainer(container, refNode = null) {
|
||||
if (typeof container === 'string') {
|
||||
let className = container;
|
||||
container = document.createElement('div');
|
||||
container.classList.add(className);
|
||||
}
|
||||
this.container.insertBefore(container, refNode);
|
||||
return container;
|
||||
}
|
||||
|
||||
blur() {
|
||||
this.selection.setRange(null);
|
||||
}
|
||||
|
||||
deleteText(index, length, source) {
|
||||
[index, length, , source] = overload(index, length, source);
|
||||
return modify.call(this, () => {
|
||||
return this.editor.deleteText(index, length);
|
||||
}, source, index, -1*length);
|
||||
}
|
||||
|
||||
disable() {
|
||||
this.enable(false);
|
||||
}
|
||||
|
||||
enable(enabled = true) {
|
||||
this.scroll.enable(enabled);
|
||||
this.container.classList.toggle('ql-disabled', !enabled);
|
||||
}
|
||||
|
||||
focus() {
|
||||
let scrollTop = this.scrollingContainer.scrollTop;
|
||||
this.selection.focus();
|
||||
this.scrollingContainer.scrollTop = scrollTop;
|
||||
this.scrollIntoView();
|
||||
}
|
||||
|
||||
format(name, value, source = Emitter.sources.API) {
|
||||
return modify.call(this, () => {
|
||||
let range = this.getSelection(true);
|
||||
let change = new Delta();
|
||||
if (range == null) {
|
||||
return change;
|
||||
} else if (Parchment.query(name, Parchment.Scope.BLOCK)) {
|
||||
change = this.editor.formatLine(range.index, range.length, { [name]: value });
|
||||
} else if (range.length === 0) {
|
||||
this.selection.format(name, value);
|
||||
return change;
|
||||
} else {
|
||||
change = this.editor.formatText(range.index, range.length, { [name]: value });
|
||||
}
|
||||
this.setSelection(range, Emitter.sources.SILENT);
|
||||
return change;
|
||||
}, source);
|
||||
}
|
||||
|
||||
formatLine(index, length, name, value, source) {
|
||||
let formats;
|
||||
[index, length, formats, source] = overload(index, length, name, value, source);
|
||||
return modify.call(this, () => {
|
||||
return this.editor.formatLine(index, length, formats);
|
||||
}, source, index, 0);
|
||||
}
|
||||
|
||||
formatText(index, length, name, value, source) {
|
||||
let formats;
|
||||
[index, length, formats, source] = overload(index, length, name, value, source);
|
||||
return modify.call(this, () => {
|
||||
return this.editor.formatText(index, length, formats);
|
||||
}, source, index, 0);
|
||||
}
|
||||
|
||||
getBounds(index, length = 0) {
|
||||
let bounds;
|
||||
if (typeof index === 'number') {
|
||||
bounds = this.selection.getBounds(index, length);
|
||||
} else {
|
||||
bounds = this.selection.getBounds(index.index, index.length);
|
||||
}
|
||||
let containerBounds = this.container.getBoundingClientRect();
|
||||
return {
|
||||
bottom: bounds.bottom - containerBounds.top,
|
||||
height: bounds.height,
|
||||
left: bounds.left - containerBounds.left,
|
||||
right: bounds.right - containerBounds.left,
|
||||
top: bounds.top - containerBounds.top,
|
||||
width: bounds.width
|
||||
};
|
||||
}
|
||||
|
||||
getContents(index = 0, length = this.getLength() - index) {
|
||||
[index, length] = overload(index, length);
|
||||
return this.editor.getContents(index, length);
|
||||
}
|
||||
|
||||
getFormat(index = this.getSelection(true), length = 0) {
|
||||
if (typeof index === 'number') {
|
||||
return this.editor.getFormat(index, length);
|
||||
} else {
|
||||
return this.editor.getFormat(index.index, index.length);
|
||||
}
|
||||
}
|
||||
|
||||
getIndex(blot) {
|
||||
return blot.offset(this.scroll);
|
||||
}
|
||||
|
||||
getLength() {
|
||||
return this.scroll.length();
|
||||
}
|
||||
|
||||
getLeaf(index) {
|
||||
return this.scroll.leaf(index);
|
||||
}
|
||||
|
||||
getLine(index) {
|
||||
return this.scroll.line(index);
|
||||
}
|
||||
|
||||
getLines(index = 0, length = Number.MAX_VALUE) {
|
||||
if (typeof index !== 'number') {
|
||||
return this.scroll.lines(index.index, index.length);
|
||||
} else {
|
||||
return this.scroll.lines(index, length);
|
||||
}
|
||||
}
|
||||
|
||||
getModule(name) {
|
||||
return this.theme.modules[name];
|
||||
}
|
||||
|
||||
getSelection(focus = false) {
|
||||
if (focus) this.focus();
|
||||
this.update(); // Make sure we access getRange with editor in consistent state
|
||||
return this.selection.getRange()[0];
|
||||
}
|
||||
|
||||
getText(index = 0, length = this.getLength() - index) {
|
||||
[index, length] = overload(index, length);
|
||||
return this.editor.getText(index, length);
|
||||
}
|
||||
|
||||
hasFocus() {
|
||||
return this.selection.hasFocus();
|
||||
}
|
||||
|
||||
insertEmbed(index, embed, value, source = Quill.sources.API) {
|
||||
return modify.call(this, () => {
|
||||
return this.editor.insertEmbed(index, embed, value);
|
||||
}, source, index);
|
||||
}
|
||||
|
||||
insertText(index, text, name, value, source) {
|
||||
let formats;
|
||||
[index, , formats, source] = overload(index, 0, name, value, source);
|
||||
return modify.call(this, () => {
|
||||
return this.editor.insertText(index, text, formats);
|
||||
}, source, index, text.length);
|
||||
}
|
||||
|
||||
isEnabled() {
|
||||
return !this.container.classList.contains('ql-disabled');
|
||||
}
|
||||
|
||||
off() {
|
||||
return this.emitter.off.apply(this.emitter, arguments);
|
||||
}
|
||||
|
||||
on() {
|
||||
return this.emitter.on.apply(this.emitter, arguments);
|
||||
}
|
||||
|
||||
once() {
|
||||
return this.emitter.once.apply(this.emitter, arguments);
|
||||
}
|
||||
|
||||
pasteHTML(index, html, source) {
|
||||
this.clipboard.dangerouslyPasteHTML(index, html, source);
|
||||
}
|
||||
|
||||
removeFormat(index, length, source) {
|
||||
[index, length, , source] = overload(index, length, source);
|
||||
return modify.call(this, () => {
|
||||
return this.editor.removeFormat(index, length);
|
||||
}, source, index);
|
||||
}
|
||||
|
||||
scrollIntoView() {
|
||||
this.selection.scrollIntoView(this.scrollingContainer);
|
||||
}
|
||||
|
||||
setContents(delta, source = Emitter.sources.API) {
|
||||
return modify.call(this, () => {
|
||||
delta = new Delta(delta);
|
||||
let length = this.getLength();
|
||||
let deleted = this.editor.deleteText(0, length);
|
||||
let applied = this.editor.applyDelta(delta);
|
||||
let lastOp = applied.ops[applied.ops.length - 1];
|
||||
if (lastOp != null && typeof(lastOp.insert) === 'string' && lastOp.insert[lastOp.insert.length-1] === '\n') {
|
||||
this.editor.deleteText(this.getLength() - 1, 1);
|
||||
applied.delete(1);
|
||||
}
|
||||
let ret = deleted.compose(applied);
|
||||
return ret;
|
||||
}, source);
|
||||
}
|
||||
|
||||
setSelection(index, length, source) {
|
||||
if (index == null) {
|
||||
this.selection.setRange(null, length || Quill.sources.API);
|
||||
} else {
|
||||
[index, length, , source] = overload(index, length, source);
|
||||
this.selection.setRange(new Range(index, length), source);
|
||||
if (source !== Emitter.sources.SILENT) {
|
||||
this.selection.scrollIntoView(this.scrollingContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setText(text, source = Emitter.sources.API) {
|
||||
let delta = new Delta().insert(text);
|
||||
return this.setContents(delta, source);
|
||||
}
|
||||
|
||||
update(source = Emitter.sources.USER) {
|
||||
let change = this.scroll.update(source); // Will update selection before selection.update() does if text changes
|
||||
this.selection.update(source);
|
||||
return change;
|
||||
}
|
||||
|
||||
updateContents(delta, source = Emitter.sources.API) {
|
||||
return modify.call(this, () => {
|
||||
delta = new Delta(delta);
|
||||
return this.editor.applyDelta(delta, source);
|
||||
}, source, true);
|
||||
}
|
||||
}
|
||||
Quill.DEFAULTS = {
|
||||
bounds: null,
|
||||
formats: null,
|
||||
modules: {},
|
||||
placeholder: '',
|
||||
readOnly: false,
|
||||
scrollingContainer: null,
|
||||
strict: true,
|
||||
theme: 'default'
|
||||
};
|
||||
Quill.events = Emitter.events;
|
||||
Quill.sources = Emitter.sources;
|
||||
// eslint-disable-next-line no-undef
|
||||
Quill.version = typeof(QUILL_VERSION) === 'undefined' ? 'dev' : QUILL_VERSION;
|
||||
|
||||
Quill.imports = {
|
||||
'delta' : Delta,
|
||||
'parchment' : Parchment,
|
||||
'core/module' : Module,
|
||||
'core/theme' : Theme
|
||||
};
|
||||
|
||||
|
||||
function expandConfig(container, userConfig) {
|
||||
userConfig = extend(true, {
|
||||
container: container,
|
||||
modules: {
|
||||
clipboard: true,
|
||||
keyboard: true,
|
||||
history: true
|
||||
}
|
||||
}, userConfig);
|
||||
if (!userConfig.theme || userConfig.theme === Quill.DEFAULTS.theme) {
|
||||
userConfig.theme = Theme;
|
||||
} else {
|
||||
userConfig.theme = Quill.import(`themes/${userConfig.theme}`);
|
||||
if (userConfig.theme == null) {
|
||||
throw new Error(`Invalid theme ${userConfig.theme}. Did you register it?`);
|
||||
}
|
||||
}
|
||||
let themeConfig = extend(true, {}, userConfig.theme.DEFAULTS);
|
||||
[themeConfig, userConfig].forEach(function(config) {
|
||||
config.modules = config.modules || {};
|
||||
Object.keys(config.modules).forEach(function(module) {
|
||||
if (config.modules[module] === true) {
|
||||
config.modules[module] = {};
|
||||
}
|
||||
});
|
||||
});
|
||||
let moduleNames = Object.keys(themeConfig.modules).concat(Object.keys(userConfig.modules));
|
||||
let moduleConfig = moduleNames.reduce(function(config, name) {
|
||||
let moduleClass = Quill.import(`modules/${name}`);
|
||||
if (moduleClass == null) {
|
||||
debug.error(`Cannot load ${name} module. Are you sure you registered it?`);
|
||||
} else {
|
||||
config[name] = moduleClass.DEFAULTS || {};
|
||||
}
|
||||
return config;
|
||||
}, {});
|
||||
// Special case toolbar shorthand
|
||||
if (userConfig.modules != null && userConfig.modules.toolbar &&
|
||||
userConfig.modules.toolbar.constructor !== Object) {
|
||||
userConfig.modules.toolbar = {
|
||||
container: userConfig.modules.toolbar
|
||||
};
|
||||
}
|
||||
userConfig = extend(true, {}, Quill.DEFAULTS, { modules: moduleConfig }, themeConfig, userConfig);
|
||||
['bounds', 'container', 'scrollingContainer'].forEach(function(key) {
|
||||
if (typeof userConfig[key] === 'string') {
|
||||
userConfig[key] = document.querySelector(userConfig[key]);
|
||||
}
|
||||
});
|
||||
userConfig.modules = Object.keys(userConfig.modules).reduce(function(config, name) {
|
||||
if (userConfig.modules[name]) {
|
||||
config[name] = userConfig.modules[name];
|
||||
}
|
||||
return config;
|
||||
}, {});
|
||||
return userConfig;
|
||||
}
|
||||
|
||||
// Handle selection preservation and TEXT_CHANGE emission
|
||||
// common to modification APIs
|
||||
function modify(modifier, source, index, shift) {
|
||||
if (this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
|
||||
return new Delta();
|
||||
}
|
||||
let range = index == null ? null : this.getSelection();
|
||||
let oldDelta = this.editor.delta;
|
||||
let change = modifier();
|
||||
if (range != null) {
|
||||
if (index === true) index = range.index;
|
||||
if (shift == null) {
|
||||
range = shiftRange(range, change, source);
|
||||
} else if (shift !== 0) {
|
||||
range = shiftRange(range, index, shift, source);
|
||||
}
|
||||
this.setSelection(range, Emitter.sources.SILENT);
|
||||
}
|
||||
if (change.length() > 0) {
|
||||
let args = [Emitter.events.TEXT_CHANGE, change, oldDelta, source];
|
||||
this.emitter.emit(Emitter.events.EDITOR_CHANGE, ...args);
|
||||
if (source !== Emitter.sources.SILENT) {
|
||||
this.emitter.emit(...args);
|
||||
}
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
function overload(index, length, name, value, source) {
|
||||
let formats = {};
|
||||
if (typeof index.index === 'number' && typeof index.length === 'number') {
|
||||
// Allow for throwaway end (used by insertText/insertEmbed)
|
||||
if (typeof length !== 'number') {
|
||||
source = value, value = name, name = length, length = index.length, index = index.index;
|
||||
} else {
|
||||
length = index.length, index = index.index;
|
||||
}
|
||||
} else if (typeof length !== 'number') {
|
||||
source = value, value = name, name = length, length = 0;
|
||||
}
|
||||
// Handle format being object, two format name/value strings or excluded
|
||||
if (typeof name === 'object') {
|
||||
formats = name;
|
||||
source = value;
|
||||
} else if (typeof name === 'string') {
|
||||
if (value != null) {
|
||||
formats[name] = value;
|
||||
} else {
|
||||
source = name;
|
||||
}
|
||||
}
|
||||
// Handle optional source
|
||||
source = source || Emitter.sources.API;
|
||||
return [index, length, formats, source];
|
||||
}
|
||||
|
||||
function shiftRange(range, index, length, source) {
|
||||
if (range == null) return null;
|
||||
let start, end;
|
||||
if (index instanceof Delta) {
|
||||
[start, end] = [range.index, range.index + range.length].map(function(pos) {
|
||||
return index.transformPosition(pos, source !== Emitter.sources.USER);
|
||||
});
|
||||
} else {
|
||||
[start, end] = [range.index, range.index + range.length].map(function(pos) {
|
||||
if (pos < index || (pos === index && source === Emitter.sources.USER)) return pos;
|
||||
if (length >= 0) {
|
||||
return pos + length;
|
||||
} else {
|
||||
return Math.max(index, pos + length);
|
||||
}
|
||||
});
|
||||
}
|
||||
return new Range(start, end - start);
|
||||
}
|
||||
|
||||
|
||||
export { expandConfig, overload, Quill as default };
|
355
assets/js/core/selection.js
Normal file
@@ -0,0 +1,355 @@
|
||||
import Parchment from 'parchment';
|
||||
import clone from 'clone';
|
||||
import equal from 'deep-equal';
|
||||
import Emitter from './emitter';
|
||||
import logger from './logger';
|
||||
|
||||
let debug = logger('quill:selection');
|
||||
|
||||
|
||||
class Range {
|
||||
constructor(index, length = 0) {
|
||||
this.index = index;
|
||||
this.length = length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Selection {
|
||||
constructor(scroll, emitter) {
|
||||
this.emitter = emitter;
|
||||
this.scroll = scroll;
|
||||
this.composing = false;
|
||||
this.mouseDown = false;
|
||||
this.root = this.scroll.domNode;
|
||||
this.cursor = Parchment.create('cursor', this);
|
||||
// savedRange is last non-null range
|
||||
this.lastRange = this.savedRange = new Range(0, 0);
|
||||
this.handleComposition();
|
||||
this.handleDragging();
|
||||
this.emitter.listenDOM('selectionchange', document, () => {
|
||||
if (!this.mouseDown) {
|
||||
setTimeout(this.update.bind(this, Emitter.sources.USER), 1);
|
||||
}
|
||||
});
|
||||
this.emitter.on(Emitter.events.EDITOR_CHANGE, (type, delta) => {
|
||||
if (type === Emitter.events.TEXT_CHANGE && delta.length() > 0) {
|
||||
this.update(Emitter.sources.SILENT);
|
||||
}
|
||||
});
|
||||
this.emitter.on(Emitter.events.SCROLL_BEFORE_UPDATE, () => {
|
||||
if (!this.hasFocus()) return;
|
||||
let native = this.getNativeRange();
|
||||
if (native == null) return;
|
||||
if (native.start.node === this.cursor.textNode) return; // cursor.restore() will handle
|
||||
// TODO unclear if this has negative side effects
|
||||
this.emitter.once(Emitter.events.SCROLL_UPDATE, () => {
|
||||
try {
|
||||
this.setNativeRange(native.start.node, native.start.offset, native.end.node, native.end.offset);
|
||||
} catch (ignored) {}
|
||||
});
|
||||
});
|
||||
this.emitter.on(Emitter.events.SCROLL_OPTIMIZE, (mutations, context) => {
|
||||
if (context.range) {
|
||||
const { startNode, startOffset, endNode, endOffset } = context.range;
|
||||
this.setNativeRange(startNode, startOffset, endNode, endOffset);
|
||||
}
|
||||
});
|
||||
this.update(Emitter.sources.SILENT);
|
||||
}
|
||||
|
||||
handleComposition() {
|
||||
this.root.addEventListener('compositionstart', () => {
|
||||
this.composing = true;
|
||||
});
|
||||
this.root.addEventListener('compositionend', () => {
|
||||
this.composing = false;
|
||||
if (this.cursor.parent) {
|
||||
const range = this.cursor.restore();
|
||||
if (!range) return;
|
||||
setTimeout(() => {
|
||||
this.setNativeRange(range.startNode, range.startOffset, range.endNode, range.endOffset);
|
||||
}, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleDragging() {
|
||||
this.emitter.listenDOM('mousedown', document.body, () => {
|
||||
this.mouseDown = true;
|
||||
});
|
||||
this.emitter.listenDOM('mouseup', document.body, () => {
|
||||
this.mouseDown = false;
|
||||
this.update(Emitter.sources.USER);
|
||||
});
|
||||
}
|
||||
|
||||
focus() {
|
||||
if (this.hasFocus()) return;
|
||||
this.root.focus();
|
||||
this.setRange(this.savedRange);
|
||||
}
|
||||
|
||||
format(format, value) {
|
||||
if (this.scroll.whitelist != null && !this.scroll.whitelist[format]) return;
|
||||
this.scroll.update();
|
||||
let nativeRange = this.getNativeRange();
|
||||
if (nativeRange == null || !nativeRange.native.collapsed || Parchment.query(format, Parchment.Scope.BLOCK)) return;
|
||||
if (nativeRange.start.node !== this.cursor.textNode) {
|
||||
let blot = Parchment.find(nativeRange.start.node, false);
|
||||
if (blot == null) return;
|
||||
// TODO Give blot ability to not split
|
||||
if (blot instanceof Parchment.Leaf) {
|
||||
let after = blot.split(nativeRange.start.offset);
|
||||
blot.parent.insertBefore(this.cursor, after);
|
||||
} else {
|
||||
blot.insertBefore(this.cursor, nativeRange.start.node); // Should never happen
|
||||
}
|
||||
this.cursor.attach();
|
||||
}
|
||||
this.cursor.format(format, value);
|
||||
this.scroll.optimize();
|
||||
this.setNativeRange(this.cursor.textNode, this.cursor.textNode.data.length);
|
||||
this.update();
|
||||
}
|
||||
|
||||
getBounds(index, length = 0) {
|
||||
let scrollLength = this.scroll.length();
|
||||
index = Math.min(index, scrollLength - 1);
|
||||
length = Math.min(index + length, scrollLength - 1) - index;
|
||||
let node, [leaf, offset] = this.scroll.leaf(index);
|
||||
if (leaf == null) return null;
|
||||
[node, offset] = leaf.position(offset, true);
|
||||
let range = document.createRange();
|
||||
if (length > 0) {
|
||||
range.setStart(node, offset);
|
||||
[leaf, offset] = this.scroll.leaf(index + length);
|
||||
if (leaf == null) return null;
|
||||
[node, offset] = leaf.position(offset, true);
|
||||
range.setEnd(node, offset);
|
||||
return range.getBoundingClientRect();
|
||||
} else {
|
||||
let side = 'left';
|
||||
let rect;
|
||||
if (node instanceof Text) {
|
||||
if (offset < node.data.length) {
|
||||
range.setStart(node, offset);
|
||||
range.setEnd(node, offset + 1);
|
||||
} else {
|
||||
range.setStart(node, offset - 1);
|
||||
range.setEnd(node, offset);
|
||||
side = 'right';
|
||||
}
|
||||
rect = range.getBoundingClientRect();
|
||||
} else {
|
||||
rect = leaf.domNode.getBoundingClientRect();
|
||||
if (offset > 0) side = 'right';
|
||||
}
|
||||
return {
|
||||
bottom: rect.top + rect.height,
|
||||
height: rect.height,
|
||||
left: rect[side],
|
||||
right: rect[side],
|
||||
top: rect.top,
|
||||
width: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
getNativeRange() {
|
||||
let selection = document.getSelection();
|
||||
if (selection == null || selection.rangeCount <= 0) return null;
|
||||
let nativeRange = selection.getRangeAt(0);
|
||||
if (nativeRange == null) return null;
|
||||
let range = this.normalizeNative(nativeRange);
|
||||
debug.info('getNativeRange', range);
|
||||
return range;
|
||||
}
|
||||
|
||||
getRange() {
|
||||
let normalized = this.getNativeRange();
|
||||
if (normalized == null) return [null, null];
|
||||
let range = this.normalizedToRange(normalized);
|
||||
return [range, normalized];
|
||||
}
|
||||
|
||||
hasFocus() {
|
||||
return document.activeElement === this.root;
|
||||
}
|
||||
|
||||
normalizedToRange(range) {
|
||||
let positions = [[range.start.node, range.start.offset]];
|
||||
if (!range.native.collapsed) {
|
||||
positions.push([range.end.node, range.end.offset]);
|
||||
}
|
||||
let indexes = positions.map((position) => {
|
||||
let [node, offset] = position;
|
||||
let blot = Parchment.find(node, true);
|
||||
let index = blot.offset(this.scroll);
|
||||
if (offset === 0) {
|
||||
return index;
|
||||
} else if (blot instanceof Parchment.Container) {
|
||||
return index + blot.length();
|
||||
} else {
|
||||
return index + blot.index(node, offset);
|
||||
}
|
||||
});
|
||||
let end = Math.min(Math.max(...indexes), this.scroll.length() - 1);
|
||||
let start = Math.min(end, ...indexes);
|
||||
return new Range(start, end-start);
|
||||
}
|
||||
|
||||
normalizeNative(nativeRange) {
|
||||
if (!contains(this.root, nativeRange.startContainer) ||
|
||||
(!nativeRange.collapsed && !contains(this.root, nativeRange.endContainer))) {
|
||||
return null;
|
||||
}
|
||||
let range = {
|
||||
start: { node: nativeRange.startContainer, offset: nativeRange.startOffset },
|
||||
end: { node: nativeRange.endContainer, offset: nativeRange.endOffset },
|
||||
native: nativeRange
|
||||
};
|
||||
[range.start, range.end].forEach(function(position) {
|
||||
let node = position.node, offset = position.offset;
|
||||
while (!(node instanceof Text) && node.childNodes.length > 0) {
|
||||
if (node.childNodes.length > offset) {
|
||||
node = node.childNodes[offset];
|
||||
offset = 0;
|
||||
} else if (node.childNodes.length === offset) {
|
||||
node = node.lastChild;
|
||||
offset = node instanceof Text ? node.data.length : node.childNodes.length + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
position.node = node, position.offset = offset;
|
||||
});
|
||||
return range;
|
||||
}
|
||||
|
||||
rangeToNative(range) {
|
||||
let indexes = range.collapsed ? [range.index] : [range.index, range.index + range.length];
|
||||
let args = [];
|
||||
let scrollLength = this.scroll.length();
|
||||
indexes.forEach((index, i) => {
|
||||
index = Math.min(scrollLength - 1, index);
|
||||
let node, [leaf, offset] = this.scroll.leaf(index);
|
||||
[node, offset] = leaf.position(offset, i !== 0);
|
||||
args.push(node, offset);
|
||||
});
|
||||
if (args.length < 2) {
|
||||
args = args.concat(args);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
scrollIntoView(scrollingContainer) {
|
||||
let range = this.lastRange;
|
||||
if (range == null) return;
|
||||
let bounds = this.getBounds(range.index, range.length);
|
||||
if (bounds == null) return;
|
||||
let limit = this.scroll.length()-1;
|
||||
let [first, ] = this.scroll.line(Math.min(range.index, limit));
|
||||
let last = first;
|
||||
if (range.length > 0) {
|
||||
[last, ] = this.scroll.line(Math.min(range.index + range.length, limit));
|
||||
}
|
||||
if (first == null || last == null) return;
|
||||
let scrollBounds = scrollingContainer.getBoundingClientRect();
|
||||
if (bounds.top < scrollBounds.top) {
|
||||
scrollingContainer.scrollTop -= (scrollBounds.top - bounds.top);
|
||||
} else if (bounds.bottom > scrollBounds.bottom) {
|
||||
scrollingContainer.scrollTop += (bounds.bottom - scrollBounds.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
setNativeRange(startNode, startOffset, endNode = startNode, endOffset = startOffset, force = false) {
|
||||
debug.info('setNativeRange', startNode, startOffset, endNode, endOffset);
|
||||
if (startNode != null && (this.root.parentNode == null || startNode.parentNode == null || endNode.parentNode == null)) {
|
||||
return;
|
||||
}
|
||||
let selection = document.getSelection();
|
||||
if (selection == null) return;
|
||||
if (startNode != null) {
|
||||
if (!this.hasFocus()) this.root.focus();
|
||||
let native = (this.getNativeRange() || {}).native;
|
||||
if (native == null || force ||
|
||||
startNode !== native.startContainer ||
|
||||
startOffset !== native.startOffset ||
|
||||
endNode !== native.endContainer ||
|
||||
endOffset !== native.endOffset) {
|
||||
|
||||
if (startNode.tagName == "BR") {
|
||||
startOffset = [].indexOf.call(startNode.parentNode.childNodes, startNode);
|
||||
startNode = startNode.parentNode;
|
||||
}
|
||||
if (endNode.tagName == "BR") {
|
||||
endOffset = [].indexOf.call(endNode.parentNode.childNodes, endNode);
|
||||
endNode = endNode.parentNode;
|
||||
}
|
||||
let range = document.createRange();
|
||||
range.setStart(startNode, startOffset);
|
||||
range.setEnd(endNode, endOffset);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
} else {
|
||||
selection.removeAllRanges();
|
||||
this.root.blur();
|
||||
document.body.focus(); // root.blur() not enough on IE11+Travis+SauceLabs (but not local VMs)
|
||||
}
|
||||
}
|
||||
|
||||
setRange(range, force = false, source = Emitter.sources.API) {
|
||||
if (typeof force === 'string') {
|
||||
source = force;
|
||||
force = false;
|
||||
}
|
||||
debug.info('setRange', range);
|
||||
if (range != null) {
|
||||
let args = this.rangeToNative(range);
|
||||
this.setNativeRange(...args, force);
|
||||
} else {
|
||||
this.setNativeRange(null);
|
||||
}
|
||||
this.update(source);
|
||||
}
|
||||
|
||||
update(source = Emitter.sources.USER) {
|
||||
let oldRange = this.lastRange;
|
||||
let [lastRange, nativeRange] = this.getRange();
|
||||
this.lastRange = lastRange;
|
||||
if (this.lastRange != null) {
|
||||
this.savedRange = this.lastRange;
|
||||
}
|
||||
if (!equal(oldRange, this.lastRange)) {
|
||||
if (!this.composing && nativeRange != null && nativeRange.native.collapsed && nativeRange.start.node !== this.cursor.textNode) {
|
||||
this.cursor.restore();
|
||||
}
|
||||
let args = [Emitter.events.SELECTION_CHANGE, clone(this.lastRange), clone(oldRange), source];
|
||||
this.emitter.emit(Emitter.events.EDITOR_CHANGE, ...args);
|
||||
if (source !== Emitter.sources.SILENT) {
|
||||
this.emitter.emit(...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function contains(parent, descendant) {
|
||||
try {
|
||||
// Firefox inserts inaccessible nodes around video elements
|
||||
descendant.parentNode;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
// IE11 has bug with Text nodes
|
||||
// https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect
|
||||
if (descendant instanceof Text) {
|
||||
descendant = descendant.parentNode;
|
||||
}
|
||||
return parent.contains(descendant);
|
||||
}
|
||||
|
||||
|
||||
export { Range, Selection as default };
|
30
assets/js/core/theme.js
Normal file
@@ -0,0 +1,30 @@
|
||||
class Theme {
|
||||
constructor(quill, options) {
|
||||
this.quill = quill;
|
||||
this.options = options;
|
||||
this.modules = {};
|
||||
}
|
||||
|
||||
init() {
|
||||
Object.keys(this.options.modules).forEach((name) => {
|
||||
if (this.modules[name] == null) {
|
||||
this.addModule(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addModule(name) {
|
||||
let moduleClass = this.quill.constructor.import(`modules/${name}`);
|
||||
this.modules[name] = new moduleClass(this.quill, this.options.modules[name] || {});
|
||||
return this.modules[name];
|
||||
}
|
||||
}
|
||||
Theme.DEFAULTS = {
|
||||
modules: {}
|
||||
};
|
||||
Theme.themes = {
|
||||
'default': Theme
|
||||
};
|
||||
|
||||
|
||||
export default Theme;
|