restore composer.json, add mysqli extension
11
public/vendor/editor/src/icons/align-center.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M1005.873,135.999 L17.000,135.999 L17.000,286.464 L1005.873,286.464 L1005.873,135.999 ZM853.738,587.386 L853.738,436.925 L169.135,436.925 L169.135,587.386 L853.738,587.386 ZM17.434,737.787 L17.434,888.000 L1006.000,888.000 L1006.000,737.787 L17.434,737.787 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 575 B |
11
public/vendor/editor/src/icons/align-indent.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M10.989,129.999 L10.989,283.817 L1013.000,283.817 L1013.000,129.999 L10.989,129.999 ZM473.108,588.303 L1012.999,588.303 L1012.999,436.108 L473.108,436.108 L473.108,588.303 L473.108,588.303 ZM473.108,892.792 L1012.999,892.792 L1012.999,740.550 L473.108,740.550 L473.108,892.792 L473.108,892.792 ZM47.458,439.405 C68.401,451.885 288.316,631.710 306.015,642.923 C321.884,652.994 322.166,676.965 306.015,687.013 C281.408,702.313 62.619,881.216 48.187,890.077 C30.201,901.151 11.992,887.071 11.992,868.146 L11.992,461.202 C11.995,440.356 32.119,430.242 47.458,439.405 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 881 B |
11
public/vendor/editor/src/icons/align-justify.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M1005.873,135.999 L17.000,135.999 L17.000,286.464 L1005.873,286.464 L1005.873,135.999 ZM1005.873,436.925 L17.000,436.925 L17.000,587.386 L1005.873,587.386 L1005.873,436.925 ZM17.434,737.787 L17.434,888.000 L1006.000,888.000 L1006.000,737.787 L17.434,737.787 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 576 B |
12
public/vendor/editor/src/icons/align-left.svg
vendored
Executable file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#010202" d="M929.51,135.727H17.704v151.855H929.51V135.727z M777.555,437.932H17.704v150.254h759.847L777.555,437.932
|
||||
L777.555,437.932z M17.704,738.488V888.79h988.593V738.488H17.704z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 703 B |
11
public/vendor/editor/src/icons/align-outdent.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M10.989,129.999 L10.989,283.817 L1013.000,283.817 L1013.000,129.999 L10.989,129.999 ZM473.108,588.303 L1012.999,588.303 L1012.999,436.108 L473.108,436.108 L473.108,588.303 L473.108,588.303 ZM473.108,892.792 L1012.999,892.792 L1012.999,740.550 L473.108,740.550 L473.108,892.792 L473.108,892.792 ZM282.568,439.002 C261.604,451.493 41.464,631.478 23.747,642.701 C7.862,652.780 7.579,676.772 23.747,686.830 C48.380,702.143 267.392,881.205 281.839,890.074 C299.843,901.157 318.071,887.064 318.071,868.123 L318.071,460.818 C318.068,439.954 297.923,429.831 282.568,439.002 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 884 B |
11
public/vendor/editor/src/icons/align-right.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M93.209,136.000 L93.209,287.842 L1007.000,287.842 L1007.000,136.000 L93.209,136.000 ZM245.234,588.421 L1007.000,588.421 L1007.000,438.180 L245.234,438.180 L245.234,588.421 ZM18.001,889.000 L1007.000,889.000 L1007.000,738.711 L18.001,738.711 L18.001,889.000 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 575 B |
8
public/vendor/editor/src/icons/align.svg
vendored
Executable file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<path d="M63.979,588.966V445.989h606.016v142.977H63.979z M64.008,160.015h770.99v143.971H64.008V160.015z M925.011,873.015H64.008
|
||||
V729.98h861.003V873.015z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 640 B |
1
public/vendor/editor/src/icons/arrow-circle-down.svg
vendored
Executable file
@@ -0,0 +1 @@
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1412 897q0-27-18-45l-91-91q-18-18-45-18t-45 18l-189 189v-502q0-26-19-45t-45-19h-128q-26 0-45 19t-19 45v502l-189-189q-19-19-45-19t-45 19l-91 91q-18 18-18 45t18 45l362 362 91 91q18 18 45 18t45-18l91-91 362-362q18-18 18-45zm252-1q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 472 B |
1
public/vendor/editor/src/icons/arrow-circle-left.svg
vendored
Executable file
@@ -0,0 +1 @@
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1408 960v-128q0-26-19-45t-45-19h-502l189-189q19-19 19-45t-19-45l-91-91q-18-18-45-18t-45 18l-362 362-91 91q-18 18-18 45t18 45l91 91 362 362q18 18 45 18t45-18l91-91q18-18 18-45t-18-45l-189-189h502q26 0 45-19t19-45zm256-64q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 465 B |
1
public/vendor/editor/src/icons/arrow-circle-right.svg
vendored
Executable file
@@ -0,0 +1 @@
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1413 896q0-27-18-45l-91-91-362-362q-18-18-45-18t-45 18l-91 91q-18 18-18 45t18 45l189 189h-502q-26 0-45 19t-19 45v128q0 26 19 45t45 19h502l-189 189q-19 19-19 45t19 45l91 91q18 18 45 18t45-18l362-362 91-91q18-18 18-45zm251 0q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 468 B |
1
public/vendor/editor/src/icons/arrow-circle-up.svg
vendored
Executable file
@@ -0,0 +1 @@
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1412 895q0-27-18-45l-362-362-91-91q-18-18-45-18t-45 18l-91 91-362 362q-18 18-18 45t18 45l91 91q18 18 45 18t45-18l189-189v502q0 26 19 45t45 19h128q26 0 45-19t19-45v-502l189 189q19 19 45 19t45-19l91-91q18-18 18-45zm252 1q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 464 B |
11
public/vendor/editor/src/icons/arrows-alt.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #241f20;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M837.145,253.759 L960.985,376.234 L960.962,68.043 L649.512,68.025 L773.407,190.643 L512.001,449.497 L250.597,190.644 L374.284,68.005 L63.042,68.007 L63.043,376.234 L186.856,253.762 L448.259,512.615 L193.137,765.250 L63.009,636.616 L63.047,956.987 L386.773,957.000 L256.875,828.366 L512.000,575.732 L767.129,828.366 L637.230,957.000 L960.948,956.987 L960.926,636.616 L830.863,765.250 L575.742,512.615 L837.145,253.759 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 738 B |
1
public/vendor/editor/src/icons/arrows-h.svg
vendored
Executable file
@@ -0,0 +1 @@
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1792 896q0 26-19 45l-256 256q-19 19-45 19t-45-19-19-45v-128h-1024v128q0 26-19 45t-45 19-45-19l-256-256q-19-19-19-45t19-45l256-256q19-19 45-19t45 19 19 45v128h1024v-128q0-26 19-45t45-19 45 19l256 256q19 19 19 45z"/></svg>
|
||||
|
After Width: | Height: | Size: 321 B |
1
public/vendor/editor/src/icons/arrows-v.svg
vendored
Executable file
@@ -0,0 +1 @@
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1216 320q0 26-19 45t-45 19h-128v1024h128q26 0 45 19t19 45-19 45l-256 256q-19 19-45 19t-45-19l-256-256q-19-19-19-45t19-45 45-19h128v-1024h-128q-26 0-45-19t-19-45 19-45l256-256q19-19 45-19t45 19l256 256q19 19 19 45z"/></svg>
|
||||
|
After Width: | Height: | Size: 323 B |
11
public/vendor/editor/src/icons/bold.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M804.888,401.892 C843.584,366.065 862.937,322.286 862.937,270.577 C862.937,202.087 833.630,147.995 775.010,108.301 C716.386,68.614 606.469,48.762 445.268,48.762 L394.438,48.762 L36.535,58.970 L36.535,134.496 C87.370,134.496 120.343,142.439 135.459,158.309 C150.571,174.195 158.124,203.895 158.124,247.441 L158.124,788.361 C158.124,833.266 150.108,863.321 134.082,878.514 C118.046,893.714 85.535,901.307 36.535,901.307 L36.535,976.833 L524.958,976.833 C660.974,976.833 758.285,955.296 816.914,912.195 C875.529,869.106 904.841,810.134 904.841,735.289 C904.841,668.607 881.941,614.516 836.144,573.013 C790.348,531.511 721.651,502.590 630.063,486.260 C707.913,465.849 766.183,437.732 804.888,401.892 ZM401.996,129.054 C451.453,129.054 488.890,132.688 514.310,139.938 C539.725,147.200 560.219,163.306 575.793,188.245 C591.357,213.200 599.146,247.220 599.146,290.310 C599.146,347.918 586.786,388.401 562.055,411.757 C537.323,435.125 499.769,446.797 449.392,446.797 L401.996,446.797 L401.996,129.054 ZM446.646,529.810 C495.183,529.810 531.832,534.571 556.558,544.101 C581.289,553.623 600.751,572.107 614.951,599.551 C629.143,626.999 636.243,664.985 636.243,713.516 C636.243,774.307 625.359,819.893 603.614,850.274 C581.860,880.672 549.227,895.864 505.728,895.864 C477.784,895.864 456.148,891.442 440.805,882.597 C425.458,873.752 415.154,862.762 409.894,849.597 C404.624,836.448 401.996,817.172 401.996,791.764 L401.996,529.810 L446.646,529.810 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
9
public/vendor/editor/src/icons/caret.svg
vendored
Executable file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<path d="M122.674,347.863c23.249,0,401.35,0,416.049,0c23.247,0,29.402,23.246,23.247,37.945
|
||||
c-8.547,14.704-190.075,256.057-204.776,279.303c-8.545,14.701-37.948,14.701-46.494,0C296,641.865,114.127,409.057,105.924,385.808
|
||||
C93.274,368.714,101.821,347.863,122.674,347.863z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 756 B |
47
public/vendor/editor/src/icons/chain-broken.svg
vendored
Executable file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#010202" d="M362.776-44.529c-15.135-25.854-51.752-32.912-77.83-18.056c-26.095,14.861-39.805,50.604-24.67,76.458
|
||||
l75.941,122.108c15.125,25.838,53.354,31.993,79.43,17.132c26.094-14.858,38.19-49.702,23.064-75.543L362.776-44.529z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#010202" d="M575.588,77.57c-15.126,25.841-3.029,60.685,23.064,75.543c26.075,14.861,64.305,8.707,79.43-17.132
|
||||
l75.941-122.108c15.135-25.854,1.425-61.597-24.67-76.458c-26.078-14.856-62.695-7.798-77.83,18.056L575.588,77.57z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#010202" d="M438.711,943.173c15.126-25.841,3.029-60.685-23.064-75.543c-26.075-14.861-64.305-8.706-79.43,17.132
|
||||
l-75.941,122.108c-15.135,25.854-1.425,61.597,24.67,76.458c26.078,14.855,62.695,7.798,77.83-18.057L438.711,943.173z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#010202" d="M651.524,1065.271c15.135,25.854,51.752,32.912,77.83,18.057c26.095-14.861,39.805-50.604,24.67-76.458
|
||||
l-75.941-122.108c-15.125-25.838-53.354-31.993-79.43-17.132c-26.094,14.858-38.19,49.702-23.064,75.543L651.524,1065.271z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#010202" d="M823.22,222.293l-196.119,0.24c-37.265,0-67.438,29.933-67.556,66.596
|
||||
c0,36.785,30.051,66.477,67.317,66.477l196.117-0.24c87.271-0.119,158.194,69.841,158.075,156.034
|
||||
c-0.117,86.189-71.162,156.393-158.555,156.393l-196.119,0.24c-37.265,0-67.438,29.935-67.438,66.599
|
||||
c0,36.784,30.054,66.475,67.318,66.475l196.115-0.241c161.685-0.239,293.315-130.188,293.555-289.706
|
||||
C1116.293,351.76,984.903,222.053,823.22,222.293z M393.772,668.394l-192.749,0.241c-87.272,0.12-158.198-69.963-158.077-156.033
|
||||
c0.119-86.192,71.284-156.396,158.556-156.515l192.753-0.24c37.265,0,67.437-29.932,67.437-66.596
|
||||
c0-36.784-30.052-66.477-67.316-66.477l-192.752,0.242C39.941,223.255-91.691,353.202-91.931,512.721
|
||||
S39.218,801.949,200.9,801.706l192.753-0.24c37.264,0,67.438-29.813,67.557-66.597
|
||||
C461.209,698.085,431.037,668.394,393.772,668.394z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
10
public/vendor/editor/src/icons/circle.svg
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="cls-1" d="M501.845-55.324c318.546,0,576.775,255.772,576.775,571.282S820.391,1087.24,501.845,1087.24-74.935,831.469-74.935,515.958,183.3-55.324,501.845-55.324Zm0,90.262c268.216,0,485.649,215.36,485.649,481.02s-217.433,481.02-485.649,481.02S16.2,781.618,16.2,515.958,233.629,34.938,501.845,34.938Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 508 B |
16
public/vendor/editor/src/icons/close.svg
vendored
Executable file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#010202" d="M562.27,511.274l305.892-305.888c1.935-1.937,1.935-5.809,0-9.68l-44.532-42.592
|
||||
c-1.936-1.937-3.868-1.937-3.868-1.937c-1.94,0-1.94,0-3.872,1.937L510,459.002l-305.887-306.13
|
||||
c-1.939-1.936-3.873-1.936-3.873-1.936c-1.939,0-1.939,0-3.872,1.936l-44.528,44.77c-1.939,1.935-1.939,5.808,0,9.68
|
||||
L457.728,513.21L151.84,816.92c-1.939,1.936-1.939,5.808,0,9.68l42.592,44.527c1.936,1.939,3.869,1.939,3.869,1.939
|
||||
s1.939,0,3.873-1.939L510,563.545l305.889,305.89c1.936,1.936,3.872,1.936,3.872,1.936s1.935,0,3.868-1.936l42.592-44.532
|
||||
c1.94-1.935,1.94-5.807,0-9.68L562.27,511.274z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
11
public/vendor/editor/src/icons/code.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M799.573,828.389 L799.573,719.346 L1122.342,549.372 L799.573,379.397 L799.573,270.358 L1237.987,510.886 L1237.987,587.857 L799.573,828.389 ZM291.448,927.098 L601.210,127.469 L718.855,127.469 L409.093,927.098 L291.448,927.098 ZM-215.002,510.886 L223.407,270.358 L223.407,379.397 L-99.357,549.372 L223.407,719.346 L223.407,828.389 L-215.002,587.857 L-215.002,510.886 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 683 B |
96
public/vendor/editor/src/icons/col-after.svg
vendored
Executable file
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="3.9521849mm"
|
||||
height="3.9511111mm"
|
||||
viewBox="0 0 14.003805 14"
|
||||
id="svg3446"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="col-after.svg">
|
||||
<defs
|
||||
id="defs3448">
|
||||
<linearGradient
|
||||
id="linearGradient4209"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4211" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4209"
|
||||
id="linearGradient3671"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.0633074,0,0,0.0633074,2746.9779,180.3852)"
|
||||
x1="252.42857"
|
||||
y1="91.944138"
|
||||
x2="379.79977"
|
||||
y2="91.944138" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="-14.589889"
|
||||
inkscape:cy="25.694683"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1854"
|
||||
inkscape:window-height="1057"
|
||||
inkscape:window-x="1424"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata3451">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Camada 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(728.278,112.5011)">
|
||||
<g
|
||||
transform="matrix(0,1,-1,0,-531.8777,-2872.4914)"
|
||||
id="g3610-6">
|
||||
<path
|
||||
style="stroke:url(#linearGradient3671);stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 2771.022,186.4283 q 0,0.264 -0.1915,0.4658 l -0.3882,0.3882 q -0.1966,0.1965 -0.4709,0.1965 -0.2795,0 -0.4658,-0.1965 l -1.5217,-1.5164 0,3.6436 q 0,0.2691 -0.194,0.4372 -0.1941,0.1683 -0.4684,0.1683 l -0.6625,0 q -0.2743,0 -0.4684,-0.1683 -0.1941,-0.1681 -0.1941,-0.4372 l 0,-3.6436 -1.5216,1.5164 q -0.1863,0.1965 -0.4658,0.1965 -0.2795,0 -0.4658,-0.1965 l -0.3882,-0.3882 q -0.1966,-0.1967 -0.1966,-0.4658 0,-0.2742 0.1966,-0.471 l 3.3693,-3.3692 q 0.1812,-0.1916 0.4658,-0.1916 0.2795,0 0.471,0.1916 l 3.3693,3.3692 q 0.1915,0.2019 0.1915,0.471 z"
|
||||
id="path3400-4-4-5-3" />
|
||||
<path
|
||||
id="polygon3366-49-8-3-2"
|
||||
d="m 2773.9903,191.0823 c 0,-0.2901 -0.1688,-0.5229 -0.3789,-0.5229 l -13.2422,0 c -0.21,0 -0.3789,0.2328 -0.3789,0.5229 l 0,4.7928 c 0,0.29 0.1689,0.5252 0.3789,0.5252 l 13.2422,0 c 0.2101,0 0.3789,-0.2352 0.3789,-0.5252 l 0,-4.7928 z m -1.1582,0.687 0,3.4211 -3.2168,0 0,-3.4211 3.2168,0 z m -4.2852,0 0,3.4211 -3.2148,0 0,-3.4211 3.2148,0 z m -4.2832,0 0,3.4211 -3.2148,0 0,-3.4211 3.2148,0 z"
|
||||
style="clip-rule:evenodd;fill:#000000;fill-rule:evenodd;image-rendering:optimizeQuality;shape-rendering:geometricPrecision;text-rendering:geometricPrecision"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
19
public/vendor/editor/src/icons/col-before.svg
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg id="svg3446" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" height="3.9511mm" width="3.9522mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 14.003805 14" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata id="metadata3451">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g id="layer1" transform="translate(785.53 142.75)">
|
||||
<g id="g3610-68" transform="matrix(0 -1 1 0 -967.93 2631.2)">
|
||||
<path id="path3400-4-4-5-65" d="m2771 186.43q0 0.264-0.1915 0.4658l-0.3882 0.3882q-0.1966 0.1965-0.4709 0.1965-0.2795 0-0.4658-0.1965l-1.5217-1.5164v3.6436q0 0.2691-0.194 0.4372-0.1941 0.1683-0.4684 0.1683h-0.6625q-0.2743 0-0.4684-0.1683-0.1941-0.1681-0.1941-0.4372v-3.6436l-1.5216 1.5164q-0.1863 0.1965-0.4658 0.1965t-0.4658-0.1965l-0.3882-0.3882q-0.1966-0.1967-0.1966-0.4658 0-0.2742 0.1966-0.471l3.3693-3.3692q0.1812-0.1916 0.4658-0.1916 0.2795 0 0.471 0.1916l3.3693 3.3692q0.1915 0.2019 0.1915 0.471z" stroke-width="0"/>
|
||||
<path id="polygon3366-49-8-3-1" style="image-rendering:optimizeQuality;shape-rendering:geometricPrecision" d="m2774 191.08c0-0.2901-0.1688-0.5229-0.3789-0.5229h-13.242c-0.21 0-0.3789 0.2328-0.3789 0.5229v4.7928c0 0.29 0.1689 0.5252 0.3789 0.5252h13.242c0.2101 0 0.3789-0.2352 0.3789-0.5252v-4.7928zm-1.1582 0.687v3.4211h-3.2168v-3.4211h3.2168zm-4.2852 0v3.4211h-3.2148v-3.4211h3.2148zm-4.2832 0v3.4211h-3.2148v-3.4211h3.2148z" fill-rule="evenodd" clip-rule="evenodd"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
19
public/vendor/editor/src/icons/col-remove.svg
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg id="svg3446" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" height="3.9511mm" width="3.9496mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" viewBox="0 0 13.99474 14" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata id="metadata3451">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g id="layer1" transform="translate(732.42 107.67)">
|
||||
<g id="g3692-8" transform="matrix(0 1 -1 0 -536.02 -2867.7)">
|
||||
<path id="path5085-1-1" d="m2764.7 190q-0.2558 0-0.4348-0.17906l-0.8698-0.86975q-0.1791-0.17906-0.1791-0.43487t0.1791-0.43487l1.8802-1.8802-1.8802-1.8802q-0.1791-0.17906-0.1791-0.43487t0.1791-0.43487l0.8698-0.86975q0.179-0.17906 0.4348-0.17906t0.4349 0.17906l1.8802 1.8802 1.8802-1.8802q0.179-0.17906 0.4348-0.17906t0.4349 0.17906l0.8698 0.86975q0.179 0.17906 0.179 0.43487t-0.179 0.43487l-1.8802 1.8802 1.8802 1.8802q0.179 0.17906 0.179 0.43487t-0.179 0.43487l-0.8698 0.86975q-0.1791 0.17906-0.4349 0.17906t-0.4348-0.17906l-1.8802-1.8802-1.8802 1.8802q-0.1791 0.17906-0.4349 0.17906z"/>
|
||||
<path id="polygon3366-49-8-3-07" style="image-rendering:optimizeQuality;shape-rendering:geometricPrecision" d="m2774 191.08c0-0.2901-0.1688-0.5229-0.3789-0.5229h-13.242c-0.21 0-0.3789 0.2328-0.3789 0.5229v4.7928c0 0.29 0.1689 0.5252 0.3789 0.5252h13.242c0.2101 0 0.3789-0.2352 0.3789-0.5252v-4.7928zm-1.1582 0.687v3.4211h-3.2168v-3.4211h3.2168zm-4.2852 0v3.4211h-3.2148v-3.4211h3.2148zm-4.2832 0v3.4211h-3.2148v-3.4211h3.2148z" fill-rule="evenodd" clip-rule="evenodd"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
13
public/vendor/editor/src/icons/eraser.svg
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#010202" d="M809.47,113.026H383.481c-67.22,0-121.711,53.92-121.711,120.442L79.209,835.674
|
||||
c0,66.521,54.492,120.442,121.709,120.442h425.986c67.222,0,121.713-53.923,121.713-120.442l182.563-602.206
|
||||
C931.181,166.946,876.687,113.026,809.47,113.026z M652.267,870.801H185.705l60.855-276.013h466.559L652.267,870.801z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 835 B |
12
public/vendor/editor/src/icons/float-left.svg
vendored
Executable file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M9,737.8v153.8h1002V737.8H9z M555.3,586.3H1011V434.1H555.3V586.3L555.3,586.3z M555.3,280.2H1011V128H555.3
|
||||
V280.2L555.3,280.2z M464.1,128L464.1,128H9v456.7h455.1V128"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 638 B |
14
public/vendor/editor/src/icons/float-none.svg
vendored
Executable file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#010202;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M1006.3,135.7H17.7v151.9h988.6V135.7z M17.7,738.5v150.3h988.6V738.5H17.7z"/>
|
||||
</g>
|
||||
<polyline class="st0" points="759.1,372.2 759.1,372.2 264.9,372.2 264.9,651.8 759.1,651.8 759.1,372.2 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 642 B |
12
public/vendor/editor/src/icons/float-right.svg
vendored
Executable file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M1010.7,128L1010.7,128H555.6v456.7h455.1V128 M9,737.8v153.8h1002V737.8H9z M8.7,586.3h455.7V434.1H8.7V586.3
|
||||
L8.7,586.3z M8.7,280.2h455.7V128H8.7V280.2L8.7,280.2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 634 B |
11
public/vendor/editor/src/icons/font.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M573.459,743.967 L601.655,814.844 L611.052,837.039 C619.730,855.657 624.065,870.689 624.065,882.145 C624.065,895.035 618.759,905.661 608.164,914.005 C597.553,922.362 570.318,926.535 526.469,926.535 L526.469,1006.008 L1013.015,1006.008 L1013.015,926.535 C990.358,926.535 966.749,919.016 942.164,903.983 C917.585,888.947 890.112,844.201 859.751,769.742 L552.495,17.984 L467.908,17.984 L166.437,770.455 C124.505,874.518 72.691,926.535 11.003,926.535 L11.003,1006.008 L334.884,1006.008 L334.884,926.535 C298.738,926.535 272.712,921.880 256.806,912.575 C240.900,903.266 232.949,886.921 232.949,863.532 C232.949,849.694 239.691,825.830 253.189,791.937 L271.988,743.967 L573.459,743.967 ZM418.748,371.669 L540.929,661.633 L303.077,661.633 L418.748,371.669 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
10
public/vendor/editor/src/icons/frame.svg
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path id="Rounded_Rectangle_8" data-name="Rounded Rectangle 8" class="cls-1" d="M33.324,124.705H984.236a64,64,0,0,1,64,64V843.319a64,64,0,0,1-64,64H33.324a64,64,0,0,1-64-64V188.705A64,64,0,0,1,33.324,124.705Zm137.33,70.961H840.366c74.689,0,137.484-11.46,137.484,62.519V751.656c0,73.975-62.8,87.175-137.484,87.175H170.654c-74.688,0-130.737-13.2-130.737-87.175V258.185C39.917,184.206,95.966,195.666,170.654,195.666ZM-110.419-17.645H1129.55a64,64,0,0,1,64,64V985.6a64,64,0,0,1-64,64H-110.419a64,64,0,0,1-64-64V46.355A64,64,0,0,1-110.419-17.645ZM93.432,52.331h833.1c98.331,0,193.791-6.99,193.791,90.4V803.587c0,157.53-39.24,176.35-193.791,176.35H93.432c-150.047,0-196.033-27.731-196.033-176.35V142.735C-102.6,45.341-4.9,52.331,93.432,52.331Zm1049.3-27.716a35.4,35.4,0,0,1,0,50.4l-112.18,111.1a36.214,36.214,0,0,1-50.874,0,35.385,35.385,0,0,1,0-50.394l112.164-111.1A36.234,36.234,0,0,1,1142.73,24.616ZM40.88,190.263a35.4,35.4,0,0,0,0-50.4L-71.288,28.766a36.224,36.224,0,0,0-50.882,0,35.4,35.4,0,0,0,0,50.4L-10,190.263A36.226,36.226,0,0,0,40.88,190.263ZM1142.73,1007.65a35.4,35.4,0,0,0,0-50.4l-112.18-111.1a36.214,36.214,0,0,0-50.874,0,35.39,35.39,0,0,0,0,50.4l112.164,111.1A36.228,36.228,0,0,0,1142.73,1007.65ZM40.88,842.005a35.4,35.4,0,0,1,0,50.4L-71.288,1003.5a36.218,36.218,0,0,1-50.882,0,35.392,35.392,0,0,1,0-50.392L-10,842.005A36.226,36.226,0,0,1,40.88,842.005Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
11
public/vendor/editor/src/icons/italic.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M421.903,47.502 L401.526,111.534 C422.134,113.396 440.987,118.386 458.094,126.498 C475.187,134.622 483.740,152.142 483.740,179.046 C483.740,190.652 479.527,211.066 471.091,240.297 L291.199,805.870 C280.890,839.748 268.713,865.849 254.659,884.172 C240.604,902.508 216.713,911.663 182.985,911.663 L162.604,975.699 L558.929,975.699 L577.201,911.663 C550.960,911.663 528.121,906.446 508.688,896.006 C489.241,885.562 479.527,868.511 479.527,844.848 C479.527,830.470 483.740,816.731 492.176,790.283 L670.662,217.327 C679.093,190.423 687.175,169.890 694.903,155.733 C702.635,141.584 712.826,130.794 725.475,123.367 C738.123,115.952 754.041,112.002 773.257,111.534 L793.634,47.502 L421.903,47.502 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1007 B |
21
public/vendor/editor/src/icons/link.svg
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#010202" d="M823.22,222.293l-196.119,0.24c-37.265,0-67.438,29.933-67.556,66.596
|
||||
c0,36.785,30.051,66.477,67.317,66.477l196.117-0.24c87.271-0.119,158.194,69.841,158.075,156.034
|
||||
c-0.117,86.189-71.162,156.393-158.555,156.393l-196.119,0.24c-37.265,0-67.438,29.935-67.438,66.599
|
||||
c0,36.784,30.054,66.475,67.318,66.475l196.115-0.241c161.685-0.239,293.315-130.188,293.555-289.706
|
||||
C1116.293,351.76,984.903,222.053,823.22,222.293z M393.772,668.394l-192.749,0.241c-87.273,0.12-158.198-69.963-158.078-156.033
|
||||
c0.12-86.192,71.284-156.396,158.556-156.515l192.752-0.24c37.265,0,67.437-29.932,67.437-66.596
|
||||
c0-36.784-30.051-66.477-67.316-66.477l-192.751,0.242C39.941,223.255-91.691,353.202-91.931,512.721
|
||||
S39.219,801.949,200.9,801.706l192.753-0.24c37.263,0,67.438-29.813,67.556-66.597
|
||||
C461.209,698.085,431.038,668.394,393.772,668.394z M272.601,509.093c0,36.784,30.052,66.476,67.316,66.476l346.928-0.358
|
||||
c37.266,0,67.439-29.934,67.558-66.598c0-36.784-30.051-66.478-67.316-66.478l-346.928,0.361
|
||||
C302.895,442.496,272.72,472.308,272.601,509.093z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
10
public/vendor/editor/src/icons/magic.svg
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="cls-1" d="M849.684,129.609L904.511,183.9,1050.77,39.032l-54.827-54.3ZM991.24,487.441h219.79V410.647l-219.79.008v76.786ZM849.684,853.314L995.943,998.181l54.827-54.289L904.511,799.022ZM27.557,39.032L173.825,183.9l54.821-54.293L82.378-15.269Zm443.712,4.655h77.524l0.01-217.671H471.278ZM-132.579,944.281L50.885,1126,533.252,648.5l-183.46-181.7ZM404.619,412.5L588.073,594.215,772.047,411.989,588.588,230.279Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 616 B |
8
public/vendor/editor/src/icons/menu-check.svg
vendored
Executable file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<path fill="#010202" d="M402.399,830.4L128.8,556.8V550.4L237.6,448L404,614.4l382.399-420.8l108.8,102.4v6.4L402.399,830.4
|
||||
L402.399,830.4L402.399,830.4z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 638 B |
11
public/vendor/editor/src/icons/minus.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<rect x="108.93" y="432.341" fill="#010202" width="806.141" height="159.071"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 584 B |
12
public/vendor/editor/src/icons/orderedlist.svg
vendored
Executable file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<path d="M83.072,779.246c31.653-22.206,76.371-43.774,76.371-87.973c0-34.427-22.838-55.464-54.488-55.464
|
||||
c-43.655,0-57.678,44.624-57.678,82.661h-70.74c-2.552-82.131,44.609-145.454,131.813-145.454c66.493,0,124.7,42.183,124.7,112.624
|
||||
c0,109.011-116.733,117.828-169.523,191.672h171.647V940H-31.75C-31.75,856.275,20.191,821.854,83.072,779.246z M87.426,192.544
|
||||
H-3.921v-55.037c51.303,1.488,98.146-16.468,106.43-71.504h58.738v359.862H87.426V192.544z M1050.921,90.351H289.056v170.108
|
||||
h761.865V90.351L1050.921,90.351z M289.781,428.395v168.558h761.97V428.395H289.781z M289.056,933.649h761.865V765.3H289.056
|
||||
V933.649z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
8
public/vendor/editor/src/icons/pencil.svg
vendored
Executable file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<path d="M100.403,576.011l-127.892,470.479L452.19,927.81l599.3-599l-352.3-351.3L100.403,576.011z M873.281,328.811
|
||||
L389.143,812.973l-145.086,31.801l-63.754-63.58l34.259-141.311l484.627-484.671L873.281,328.811z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 696 B |
11
public/vendor/editor/src/icons/picture.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M1068.340,975.691 L-26.281,975.691 C-61.627,975.691 -90.281,947.037 -90.281,911.691 L-90.281,113.254 C-90.281,77.908 -61.627,49.254 -26.281,49.254 L1068.340,49.254 C1103.686,49.254 1132.340,77.908 1132.340,113.254 L1132.340,911.691 C1132.340,947.037 1103.686,975.691 1068.340,975.691 ZM1060.475,192.281 C1060.475,113.556 996.043,121.007 916.561,121.007 L125.033,121.007 C45.551,121.007 -18.881,113.556 -18.881,192.281 L-18.881,762.448 C-18.881,841.172 45.551,904.991 125.033,904.991 L916.561,904.991 C996.043,904.991 1060.475,841.172 1060.475,762.448 L1060.475,192.281 ZM820.368,440.468 C767.439,440.468 724.487,400.954 724.487,352.025 C724.487,303.139 767.439,263.554 820.368,263.554 C873.322,263.554 916.222,303.139 916.222,352.025 C916.222,400.954 873.322,440.468 820.368,440.468 ZM873.397,780.358 C918.029,846.441 885.494,832.253 801.185,832.253 L225.930,832.253 C141.522,832.253 101.661,842.771 137.427,772.270 L276.114,497.962 C311.786,427.437 379.266,422.747 426.053,487.463 L562.711,676.687 L600.888,601.377 C636.612,530.876 702.365,527.233 747.024,593.289 L873.397,780.358 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
10
public/vendor/editor/src/icons/question.svg
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path id="Rounded_Rectangle_6" data-name="Rounded Rectangle 6" class="cls-1" d="M418.652,985.119h194.77V798.656H418.652V985.119ZM180.886,334.113H369.145q0-53.547,31.508-103.232T507.57,181.2q76.667,0,105.595,40.326t28.918,89.361q0,42.582-25.973,78.068-14.295,20.655-37.67,38.066L531.017,463.8q-70.137,54.192-87.023,95.81T423.21,710.265H599.743q0.642-51.612,8.446-76.133,12.341-38.713,50.028-67.75L704.332,530.9q70.173-54.2,94.851-89.039,42.234-57.417,42.233-141.3,0-136.773-97.387-206.46t-244.6-69.684q-112.047,0-188.909,49.038Q188.7,150.243,180.886,334.113h0Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 759 B |
10
public/vendor/editor/src/icons/redo.svg
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path id="Shape_29_copy_3" data-name="Shape 29 copy 3" class="cls-1" d="M1181.48,193.372V728.531H616.6L771.612,580.294s21.019-239-341.56-239S-79.66,648.131-79.66,648.131s147.134-482.4,546.5-482.4S1034.35,331.559,1034.35,331.559Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 428 B |
20
public/vendor/editor/src/icons/rollback.svg
vendored
Executable file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M512.4,97.7v112.9c190.3,1.3,344.1,155.9,344.1,346.5c0,191.4-155.1,346.5-346.5,346.5
|
||||
S163.5,748.4,163.5,557.1H50.6c0,253.7,205.7,459.4,459.4,459.4s459.4-205.7,459.4-459.4C969.4,304.2,765,99,512.4,97.7z"/>
|
||||
</g>
|
||||
<path class="st0" d="M486,4.4C469.4,12.7,295.8,132,281.8,139.5c-12.5,6.7-12.8,22.6,0,29.3c19.4,10.2,192.2,128.9,203.6,134.8
|
||||
c14.2,7.4,28.6-2,28.6-14.6V18.8C514,5,498.1-1.7,486,4.4z"/>
|
||||
</g>
|
||||
<polygon class="st0" points="639.5,340.1 512.4,467.2 385.3,340.1 295.4,430 422.5,557.1 295.4,684.2 385.3,774.1 512.4,647
|
||||
639.5,774.1 729.4,684.2 602.3,557.1 729.4,430 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
19
public/vendor/editor/src/icons/row-above.svg
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg id="svg3446" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" height="3.9522mm" width="3.9511mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 14 14.003805" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata id="metadata3451">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g id="layer1" transform="translate(758.78 133.09)">
|
||||
<g id="g3610-8" transform="translate(-3518.8 -315.48)">
|
||||
<path id="path3400-4-4-5-4" d="m2771 186.43q0 0.264-0.1915 0.4658l-0.3882 0.3882q-0.1966 0.1965-0.4709 0.1965-0.2795 0-0.4658-0.1965l-1.5217-1.5164v3.6436q0 0.2691-0.194 0.4372-0.1941 0.1683-0.4684 0.1683h-0.6625q-0.2743 0-0.4684-0.1683-0.1941-0.1681-0.1941-0.4372v-3.6436l-1.5216 1.5164q-0.1863 0.1965-0.4658 0.1965t-0.4658-0.1965l-0.3882-0.3882q-0.1966-0.1967-0.1966-0.4658 0-0.2742 0.1966-0.471l3.3693-3.3692q0.1812-0.1916 0.4658-0.1916 0.2795 0 0.471 0.1916l3.3693 3.3692q0.1915 0.2019 0.1915 0.471z" stroke-width="0"/>
|
||||
<path id="polygon3366-49-8-3-5" style="image-rendering:optimizeQuality;shape-rendering:geometricPrecision" d="m2774 191.08c0-0.2901-0.1688-0.5229-0.3789-0.5229h-13.242c-0.21 0-0.3789 0.2328-0.3789 0.5229v4.7928c0 0.29 0.1689 0.5252 0.3789 0.5252h13.242c0.2101 0 0.3789-0.2352 0.3789-0.5252v-4.7928zm-1.1582 0.687v3.4211h-3.2168v-3.4211h3.2168zm-4.2852 0v3.4211h-3.2148v-3.4211h3.2148zm-4.2832 0v3.4211h-3.2148v-3.4211h3.2148z" fill-rule="evenodd" clip-rule="evenodd"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
19
public/vendor/editor/src/icons/row-below.svg
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg id="svg3446" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" height="3.9522mm" width="3.9511mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 14 14.003805" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata id="metadata3451">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g id="layer1" transform="translate(745.96 126.95)">
|
||||
<g id="g3610-5" transform="matrix(1 0 0 -1 -3506 69.446)">
|
||||
<path id="path3400-4-4-5-6" d="m2771 186.43q0 0.264-0.1915 0.4658l-0.3882 0.3882q-0.1966 0.1965-0.4709 0.1965-0.2795 0-0.4658-0.1965l-1.5217-1.5164v3.6436q0 0.2691-0.194 0.4372-0.1941 0.1683-0.4684 0.1683h-0.6625q-0.2743 0-0.4684-0.1683-0.1941-0.1681-0.1941-0.4372v-3.6436l-1.5216 1.5164q-0.1863 0.1965-0.4658 0.1965t-0.4658-0.1965l-0.3882-0.3882q-0.1966-0.1967-0.1966-0.4658 0-0.2742 0.1966-0.471l3.3693-3.3692q0.1812-0.1916 0.4658-0.1916 0.2795 0 0.471 0.1916l3.3693 3.3692q0.1915 0.2019 0.1915 0.471z" stroke-width="0"/>
|
||||
<path id="polygon3366-49-8-3-0" style="image-rendering:optimizeQuality;shape-rendering:geometricPrecision" d="m2774 191.08c0-0.2901-0.1688-0.5229-0.3789-0.5229h-13.242c-0.21 0-0.3789 0.2328-0.3789 0.5229v4.7928c0 0.29 0.1689 0.5252 0.3789 0.5252h13.242c0.2101 0 0.3789-0.2352 0.3789-0.5252v-4.7928zm-1.1582 0.687v3.4211h-3.2168v-3.4211h3.2168zm-4.2852 0v3.4211h-3.2148v-3.4211h3.2148zm-4.2832 0v3.4211h-3.2148v-3.4211h3.2148z" fill-rule="evenodd" clip-rule="evenodd"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
19
public/vendor/editor/src/icons/row-remove.svg
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg id="svg3446" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" height="3.9496mm" width="3.9511mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" viewBox="0 0 14 13.99474" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata id="metadata3451">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g id="layer1" transform="translate(764.76 127.54)">
|
||||
<g id="g3692-9" transform="translate(-3524.7 -309.95)">
|
||||
<path id="path5085-1-5" d="m2764.7 190q-0.2558 0-0.4348-0.17906l-0.8698-0.86975q-0.1791-0.17906-0.1791-0.43487t0.1791-0.43487l1.8802-1.8802-1.8802-1.8802q-0.1791-0.17906-0.1791-0.43487t0.1791-0.43487l0.8698-0.86975q0.179-0.17906 0.4348-0.17906t0.4349 0.17906l1.8802 1.8802 1.8802-1.8802q0.179-0.17906 0.4348-0.17906t0.4349 0.17906l0.8698 0.86975q0.179 0.17906 0.179 0.43487t-0.179 0.43487l-1.8802 1.8802 1.8802 1.8802q0.179 0.17906 0.179 0.43487t-0.179 0.43487l-0.8698 0.86975q-0.1791 0.17906-0.4349 0.17906t-0.4348-0.17906l-1.8802-1.8802-1.8802 1.8802q-0.1791 0.17906-0.4349 0.17906z"/>
|
||||
<path id="polygon3366-49-8-3-4" style="image-rendering:optimizeQuality;shape-rendering:geometricPrecision" d="m2774 191.08c0-0.2901-0.1688-0.5229-0.3789-0.5229h-13.242c-0.21 0-0.3789 0.2328-0.3789 0.5229v4.7928c0 0.29 0.1689 0.5252 0.3789 0.5252h13.242c0.2101 0 0.3789-0.2352 0.3789-0.5252v-4.7928zm-1.1582 0.687v3.4211h-3.2168v-3.4211h3.2168zm-4.2852 0v3.4211h-3.2148v-3.4211h3.2148zm-4.2832 0v3.4211h-3.2148v-3.4211h3.2148z" fill-rule="evenodd" clip-rule="evenodd"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
10
public/vendor/editor/src/icons/special-character.svg
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path id="_" data-name="※" class="cls-1" d="M807.591,584.975q29.964,27.886,70.339,27.869,46.473,0,75.84-27.869t29.359-72.7q0-44.811-31.194-72.695-31.188-27.864-74-27.869-42.835,0-71.561,29.684-28.76,29.706-28.751,72.093Q777.618,557.11,807.591,584.975Zm-737.635,0q28.732,27.886,74.009,27.869,42.807,0,70.952-27.869,28.124-27.858,28.133-72.7,0-44.811-29.36-72.695-29.353-27.864-69.725-27.869-46.5,0-74.618,29.684Q41.188,471.106,41.21,513.493,41.21,557.11,69.956,584.975ZM422.872,180.3A111.5,111.5,0,0,0,445.5,212.407a103.872,103.872,0,0,0,72.783,29.684q45.243,0,73.4-29.684,28.131-29.677,28.138-72.093,0-43.617-30.582-72.695T518.286,38.541q-46.5,0-75.231,29.684-28.753,29.7-28.746,72.088A92.561,92.561,0,0,0,422.872,180.3ZM444.89,956.94q30.566,27.858,73.4,27.865,44.039,0,72.787-27.865,28.725-27.887,28.747-73.908,0-42.4-30.582-69.667T518.286,786.1q-46.5,0-75.231,28.471-28.753,28.484-28.746,69.667Q414.309,929.058,444.89,956.94ZM919.768,46.429L512.582,449.945,104.533,46.429,43.951,107.145,451.137,509.807,43.951,912.465l60.582,60.715L512.582,569.668,919.768,973.18l59.714-60.715L572.3,509.807,979.482,107.145Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
10
public/vendor/editor/src/icons/square.svg
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path id="Rounded_Rectangle_7" data-name="Rounded Rectangle 7" class="cls-1" d="M26.188-17.645H992.877a128,128,0,0,1,128,128V923.107a128.005,128.005,0,0,1-128,128H26.188a128,128,0,0,1-128-128V110.355A128,128,0,0,1,26.188-17.645ZM97.324,53.5H920.236a128,128,0,0,1,128,128v670.47a128,128,0,0,1-128,128H97.324a128,128,0,0,1-128-128V181.5A128,128,0,0,1,97.324,53.5Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 561 B |
11
public/vendor/editor/src/icons/strikethrough.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M999.602,572.813 L23.826,572.813 C14.990,572.813 7.826,565.650 7.826,556.813 L7.826,446.566 C7.826,437.730 14.990,430.566 23.826,430.566 L222.008,430.566 C219.520,428.463 217.056,426.350 214.637,424.218 C161.037,373.796 134.249,315.121 134.249,248.206 C134.249,212.816 142.487,179.473 158.985,148.198 C175.469,116.922 198.139,90.014 227.007,67.465 C255.857,44.916 290.123,27.349 329.810,14.736 C369.487,2.130 412.255,-4.175 458.127,-4.175 C491.619,-4.175 521.375,-1.993 547.403,2.370 C573.418,6.734 600.862,13.042 629.726,21.281 C658.580,29.531 682.023,35.713 700.069,39.828 C718.101,43.951 732.269,57.144 742.582,57.144 C770.917,57.144 790.242,35.961 800.555,2.504 L870.333,2.504 L870.333,317.302 L803.645,317.302 C782.005,266.876 754.042,222.751 719.780,184.927 C685.500,147.107 647.625,117.903 606.150,97.287 C564.661,76.680 522.016,66.374 478.225,66.374 C431.325,66.374 393.836,77.172 365.756,98.741 C337.661,120.321 323.630,149.052 323.630,184.927 C323.630,217.908 337.793,247.730 366.142,274.390 C382.108,288.460 413.293,307.245 459.670,330.758 C506.051,354.284 550.361,376.340 592.624,396.944 C614.707,407.719 636.721,418.934 658.668,430.566 L999.602,430.566 C1008.439,430.566 1015.602,437.730 1015.602,446.566 L1015.602,556.813 C1015.602,565.650 1008.439,572.813 999.602,572.813 ZM270.294,825.703 C307.395,866.433 348.361,897.594 393.195,919.167 C438.029,940.747 484.660,951.529 533.103,951.529 C568.143,951.529 599.196,945.236 626.248,932.619 C653.301,920.017 674.295,902.800 689.246,880.980 C704.188,859.160 711.661,834.433 711.661,806.793 C711.661,762.675 693.110,723.150 656.009,688.240 C639.510,671.759 615.680,661.588 584.509,644.371 L894.856,644.371 C909.279,679.045 916.502,709.332 916.502,748.605 C916.502,800.496 901.815,847.885 872.442,890.797 C843.068,933.710 802.353,967.293 750.310,991.535 C698.257,1015.773 641.322,1027.902 579.485,1027.902 C537.222,1027.902 499.215,1023.775 465.468,1015.537 C431.712,1007.287 393.450,996.386 350.682,982.804 C307.900,969.238 279.301,962.441 264.883,962.441 C244.265,962.441 246.469,968.383 231.013,980.260 C215.552,992.149 205.503,1007.535 200.866,1026.445 L151.394,1026.445 L151.394,683.876 L200.866,683.876 C223.535,737.697 233.188,784.972 270.294,825.703 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
11
public/vendor/editor/src/icons/subscript.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M1224.354,1117.078 L1006.601,1117.078 L1006.601,1067.562 C1091.466,990.519 1138.701,951.568 1139.220,911.578 C1139.220,887.702 1124.350,874.518 1097.335,874.518 C1072.236,874.518 1052.062,887.167 1032.530,901.592 L1009.016,842.278 C1039.234,818.882 1075.104,808.372 1110.771,808.372 C1176.237,808.372 1218.537,846.614 1218.537,905.801 C1218.537,965.501 1168.603,1011.339 1122.925,1049.581 L1224.354,1049.581 L1224.354,1117.078 ZM486.761,678.568 L296.843,975.803 L46.215,975.803 L46.215,879.988 L100.910,879.988 C134.010,879.988 164.672,863.823 181.618,837.434 L377.259,532.875 L188.346,256.449 C171.116,231.230 141.182,215.947 109.001,215.947 L57.840,215.947 L57.840,120.132 L291.295,120.132 L486.761,404.237 L682.228,120.132 L915.687,120.132 L915.687,215.947 L864.527,215.947 C832.355,215.947 802.411,231.230 785.177,256.449 L596.263,532.875 L791.904,837.434 C808.860,863.823 839.522,879.988 872.622,879.988 L927.312,879.988 L927.312,975.803 L676.684,975.803 L486.761,678.568 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
11
public/vendor/editor/src/icons/summernote.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M1122.033,577.898 L1033.442,577.898 C1036.450,557.776 1038.159,535.279 1038.159,511.748 C1038.159,488.736 1036.511,467.226 1033.634,447.104 L1122.033,447.104 C1157.345,447.104 1186.090,477.189 1186.090,512.501 C1186.090,547.813 1157.345,577.898 1122.033,577.898 ZM929.167,191.898 C901.164,155.489 868.523,122.848 832.113,94.843 L894.837,32.120 C907.751,19.206 924.985,12.093 943.363,12.093 C961.743,12.093 978.976,19.206 991.891,32.120 C1004.807,45.035 1011.919,62.269 1011.919,80.647 C1011.919,99.027 1004.807,116.261 991.891,129.176 L929.167,191.898 ZM390.139,620.385 C386.505,631.404 392.589,637.487 403.607,633.891 L541.794,588.779 L900.676,229.788 C958.318,309.034 992.407,406.516 992.407,512.006 C992.407,777.325 777.324,992.408 512.005,992.408 C246.687,992.408 31.604,777.325 31.604,512.006 C31.604,246.688 246.687,31.605 512.005,31.605 C617.495,31.605 714.974,65.693 794.222,123.335 L435.195,482.236 L390.139,620.385 ZM446.682,-9.540 L446.682,-98.033 C446.682,-133.345 476.766,-162.090 512.078,-162.090 C547.391,-162.090 577.475,-133.345 577.475,-98.033 L577.475,-9.511 C557.353,-12.464 535.275,-14.147 511.965,-14.147 C488.733,-14.147 466.804,-12.474 446.682,-9.540 ZM98.494,187.254 L34.795,124.042 C9.819,99.066 9.262,60.328 34.238,35.352 C59.215,10.391 98.716,10.361 123.721,35.352 L186.790,98.859 C153.982,124.741 124.340,154.417 98.494,187.254 ZM-98.033,577.898 C-133.345,577.898 -162.090,547.813 -162.090,512.501 C-162.090,477.189 -133.345,447.104 -98.033,447.104 L-9.623,447.104 C-12.500,467.226 -14.148,488.736 -14.148,511.748 C-14.148,535.279 -12.439,557.776 -9.430,577.898 L-98.033,577.898 ZM99.092,837.519 C124.835,870.105 154.326,899.562 186.941,925.272 L123.721,988.996 C111.226,1001.477 97.056,1008.066 80.657,1008.066 C64.246,1008.066 47.847,1002.477 35.352,989.996 C10.376,965.020 10.376,925.709 35.352,900.732 L99.092,837.519 ZM577.475,1033.524 L577.475,1122.033 C577.475,1157.345 547.391,1186.090 512.078,1186.090 C476.766,1186.090 446.682,1157.345 446.682,1122.033 L446.682,1033.552 C466.804,1036.486 488.733,1038.160 511.965,1038.160 C535.275,1038.160 557.353,1036.477 577.475,1033.524 ZM925.024,837.387 L988.957,900.732 C1013.933,925.709 1014.225,963.686 989.249,988.663 C976.754,1001.144 960.943,1007.399 944.545,1007.399 C928.133,1007.399 912.907,1001.144 900.413,988.663 L837.302,925.090 C869.869,899.389 899.316,869.949 925.024,837.387 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
11
public/vendor/editor/src/icons/superscript.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M1224.352,265.601 L1122.923,265.601 C1168.606,227.355 1218.535,181.521 1218.535,121.817 C1218.535,62.634 1176.234,24.388 1110.769,24.388 C1075.107,24.388 1039.232,34.898 1009.019,58.298 L1032.528,117.612 C1052.060,103.187 1072.238,90.538 1097.337,90.538 C1124.347,90.538 1139.223,103.722 1139.223,127.594 C1138.704,167.588 1091.468,206.535 1006.603,283.581 L1006.603,333.098 L1224.352,333.098 L1224.352,265.601 ZM888.208,880.169 L888.208,976.165 L636.887,976.165 L446.450,678.379 L256.003,976.165 L4.691,976.165 L4.691,880.169 L59.527,880.169 C92.722,880.169 123.464,863.980 140.462,837.540 L336.646,532.418 L147.213,255.492 C129.927,230.222 99.907,214.911 67.646,214.911 L16.340,214.911 L16.340,118.919 L250.445,118.919 L446.450,403.552 L642.454,118.919 L876.550,118.919 L876.550,214.911 L825.253,214.911 C792.987,214.911 762.963,230.222 745.686,255.492 L556.253,532.422 L752.432,837.544 C769.426,863.980 800.172,880.173 833.363,880.173 L888.208,880.173 L888.208,880.169 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
11
public/vendor/editor/src/icons/table.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M-73.241,975.691 L-73.241,49.254 L285.347,49.254 L358.022,49.254 L645.543,49.254 L718.150,49.254 L1076.738,49.254 L1076.738,975.691 L-73.241,975.691 ZM718.150,904.980 L1005.528,904.980 L1005.528,691.094 L718.150,691.094 L718.150,904.980 ZM1005.528,619.954 L1005.528,406.566 L718.150,406.566 L718.150,619.954 L1005.528,619.954 ZM358.022,904.980 L645.543,904.980 L645.543,691.094 L358.022,691.094 L358.022,904.980 ZM645.543,619.954 L645.543,406.566 L358.022,406.566 L358.022,619.954 L645.543,619.954 ZM-1.871,904.980 L285.347,904.980 L285.347,691.094 L-1.871,691.094 L-1.871,904.980 ZM285.347,619.954 L285.347,406.566 L-1.871,406.566 L-1.871,619.954 L285.347,619.954 ZM-1.871,120.996 L-1.871,335.391 L285.347,335.391 L285.347,120.996 L-1.871,120.996 ZM358.022,120.996 L358.022,335.391 L645.543,335.391 L645.543,120.996 L358.022,120.996 ZM718.150,120.996 L718.150,335.391 L1005.528,335.391 L1005.528,120.996 L718.150,120.996 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
83
public/vendor/editor/src/icons/templates/summernote-icons.css
vendored
Executable file
@@ -0,0 +1,83 @@
|
||||
@font-face {
|
||||
font-family: "{{ fontName }}";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: auto;
|
||||
{% if formats.indexOf('eot')>-1 -%}
|
||||
src: url("{{ fontPath }}{{ fontName }}.eot");
|
||||
{%- endif %}
|
||||
{%- set eotIndex = formats.indexOf('eot') -%}
|
||||
{%- set woff2Index = formats.indexOf('woff2') -%}
|
||||
{%- set woffIndex = formats.indexOf('woff') -%}
|
||||
{%- set ttfIndex = formats.indexOf('ttf') -%}
|
||||
{%- set svgIndex = formats.indexOf('svg') %}
|
||||
src: {% if eotIndex != -1 -%}
|
||||
url("{{ fontPath }}{{ fontName }}.eot?#iefix") format("embedded-opentype")
|
||||
{%- set nothing = formats.splice(eotIndex, 1) -%}
|
||||
{%- if formats.length != 0 -%}, {% else -%}; {% endif -%}
|
||||
{%- endif -%}
|
||||
{%- if woff2Index != -1 -%}
|
||||
url("{{ fontPath }}{{ fontName }}.woff2") format("woff2")
|
||||
{%- set nothing = formats.splice(woff2Index, 1) -%}
|
||||
{%- if formats.length != 0 -%}, {% else -%}; {% endif -%}
|
||||
{%- endif -%}
|
||||
{%- if woffIndex != -1 -%}
|
||||
url("{{ fontPath }}{{ fontName }}.woff") format("woff")
|
||||
{%- set nothing = formats.splice(woffIndex, 1) -%}
|
||||
{%- if formats.length != 0 -%}, {% else -%}; {% endif -%}
|
||||
{%- endif -%}
|
||||
{%- if ttfIndex != -1 -%}
|
||||
url("{{ fontPath }}{{ fontName }}.ttf") format("truetype");
|
||||
{%- set nothing = formats.splice(ttfIndex, 1) -%}
|
||||
{%- endif -%}
|
||||
}
|
||||
|
||||
|
||||
[class^="{{ className }}"]:before,
|
||||
[class*=" {{ className }}"]:before {
|
||||
display: inline-block;
|
||||
font-family: {{ fontName }};
|
||||
font-style: normal;
|
||||
font-size: inherit;
|
||||
text-decoration: inherit;
|
||||
text-rendering: auto;
|
||||
text-transform: none;
|
||||
vertical-align: middle;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
speak: none;
|
||||
}
|
||||
|
||||
|
||||
.{{ className }}-fw {
|
||||
text-align: center;
|
||||
width: 1.25em;
|
||||
}
|
||||
|
||||
.{{ className }}-border {
|
||||
border: solid 0.08em #eee;
|
||||
border-radius: 0.1em;
|
||||
padding: 0.2em 0.25em 0.15em;
|
||||
}
|
||||
|
||||
.{{ className }}-pull-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.{{ className }}-pull-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.{{ className }}.{{ className }}-pull-left {
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
.{{ className }}.{{ className }}-pull-right {
|
||||
margin-left: 0.3em;
|
||||
}
|
||||
|
||||
{% for glyph in glyphs %}
|
||||
.{{ className }}-{{ glyph.name }}::before {
|
||||
content: "\{{ glyph.unicode[0].charCodeAt(0).toString(16) }}";
|
||||
}
|
||||
{% endfor %}
|
||||
11
public/vendor/editor/src/icons/text-height.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M1073.727,843.931 L1211.024,843.931 L1039.529,975.262 L868.030,843.931 L1005.602,843.931 L1005.602,251.428 L876.962,251.428 L1039.529,120.159 L1202.092,251.428 L1073.727,251.428 L1073.727,843.931 ZM649.994,233.729 C624.218,210.392 565.126,198.716 508.696,198.716 L430.715,198.716 L430.715,797.755 C430.715,837.588 437.714,865.323 451.712,880.954 C465.705,896.596 495.137,904.404 540.026,904.404 L563.354,904.404 L563.354,975.720 L76.224,975.720 L76.224,904.404 L97.552,904.404 C142.875,904.404 178.706,896.374 192.265,880.311 C205.816,864.251 212.595,836.734 212.595,797.755 L212.595,198.716 L130.211,198.716 C84.888,198.716 29.423,203.434 8.095,212.849 C-13.237,222.275 -31.128,241.017 -45.565,269.065 C-60.005,297.124 -68.785,330.421 -71.891,368.971 L-145.075,368.971 L-145.075,120.977 L789.565,120.977 L789.565,368.971 L709.981,368.971 C695.755,302.152 675.761,257.081 649.994,233.729 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
17
public/vendor/editor/src/icons/trash.svg
vendored
Executable file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#040507" d="M684.868,95.723c-38.258,0-69.273-30.712-69.273-68.606H389.834c0,37.891-31.018,68.606-69.276,68.606
|
||||
H15.731v69.894h978.333V95.723H684.868z M224.755,996.884h560.281L924.92,234.799H84.871L224.755,996.884z M688.173,355.88
|
||||
c0-22.491,18.415-40.721,41.121-40.721c22.71,0,41.12,18.23,41.12,40.721l-60.545,515.828c0,22.491-18.415,40.719-41.118,40.719
|
||||
c-22.707,0-41.116-18.229-41.116-40.719L688.173,355.88z M464.44,355.88c0-22.491,18.413-40.721,41.123-40.721
|
||||
c22.708,0,41.115,18.23,41.115,40.721v515.828c0,22.491-18.407,40.719-41.115,40.719c-22.712,0-41.123-18.229-41.123-40.719
|
||||
V355.88z M269.712,315.159c22.711,0,41.118,18.23,41.118,40.721l70.229,515.828c0,22.491-18.405,40.719-41.113,40.719
|
||||
c-22.712,0-41.123-18.229-41.123-40.719L228.593,355.88C228.594,333.39,247.004,315.159,269.712,315.159z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
11
public/vendor/editor/src/icons/underline.svg
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #000;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path d="M908.526,146.582 C888.706,156.816 875.010,171.009 867.419,189.175 C859.828,207.341 856.035,232.713 856.035,265.284 L856.035,549.047 C856.035,610.022 852.133,659.822 844.334,698.446 C836.531,737.077 818.089,774.240 788.998,809.946 C759.903,845.651 719.429,874.367 667.570,896.077 C615.711,917.783 558.158,928.648 494.914,928.648 C428.718,928.648 369.376,917.893 316.885,896.388 C264.394,874.886 224.231,847.742 196.405,814.955 C168.579,782.175 149.702,746.473 139.800,707.842 C129.892,669.218 124.938,623.384 124.938,570.343 L124.938,235.218 C124.938,195.125 117.984,167.780 104.071,153.158 C90.153,138.544 59.799,131.235 12.997,131.235 L12.997,61.702 L465.824,61.702 L465.824,131.235 L451.278,131.235 C408.686,131.235 381.077,138.965 368.428,154.410 C355.779,169.867 349.453,196.799 349.453,235.218 L349.453,570.343 C349.453,631.319 354.515,679.338 364.635,714.418 C374.750,749.498 396.886,779.359 431.039,803.995 C465.192,828.639 506.081,840.953 553.732,840.953 C605.591,840.953 649.958,827.796 686.861,801.487 C723.745,775.177 747.670,743.134 758.639,705.333 C769.599,667.544 775.085,614.819 775.085,547.168 L775.085,261.527 C775.085,174.671 732.709,131.235 647.962,131.235 L647.962,61.702 L965.447,61.702 L965.447,131.235 C947.312,131.235 928.341,136.351 908.526,146.582 ZM932.995,1046.691 L70.468,1046.691 L70.468,975.619 L932.995,975.619 L932.995,1046.691 Z" class="cls-1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
10
public/vendor/editor/src/icons/undo.svg
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path id="Shape_29_copy_2" data-name="Shape 29 copy 2" class="cls-1" d="M-84.808,193.372V728.531H480.079L325.064,580.294s-21.019-241.2,341.56-241.2S1176.34,648.131,1176.34,648.131s-147.14-482.4-546.5-482.4c-364.362,0-567.515,165.824-567.515,165.824Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 449 B |
9
public/vendor/editor/src/icons/unorderedlist.svg
vendored
Executable file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<path d="M1032.671,90.351H270.806v170.108h761.865V90.351L1032.671,90.351z M271.531,428.395v168.558h761.97V428.395H271.531z
|
||||
M270.806,933.649h761.865V765.3H270.806V933.649z M160.712,90.351H-9.5v170.108h170.212V90.351L160.712,90.351z M-8.982,596.951
|
||||
h170.314V428.395H-8.982V596.951z M-9.397,933.649h170.212V765.3H-9.397V933.649L-9.397,933.649z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 830 B |
9
public/vendor/editor/src/icons/video.svg
vendored
Executable file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
|
||||
<path d="M-70.443,728.622l284.268-120.036V416.415L-70.443,296.266V728.622z M962.801,200.673H329.249
|
||||
c-39.146,0-71.374,31.489-71.374,69.789v484.301c0,38.076,32.118,69.564,71.374,69.564h633.552
|
||||
c39.256,0,71.263-31.488,71.263-69.564V270.351C1034.064,232.162,1001.946,200.673,962.801,200.673z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 777 B |
241
public/vendor/editor/src/js/base/Context.js
vendored
Executable file
@@ -0,0 +1,241 @@
|
||||
import $ from 'jquery';
|
||||
import func from './core/func';
|
||||
import lists from './core/lists';
|
||||
import dom from './core/dom';
|
||||
|
||||
export default class Context {
|
||||
/**
|
||||
* @param {jQuery} $note
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor($note, options) {
|
||||
this.$note = $note;
|
||||
|
||||
this.memos = {};
|
||||
this.modules = {};
|
||||
this.layoutInfo = {};
|
||||
this.options = $.extend(true, {}, options);
|
||||
|
||||
// init ui with options
|
||||
$.summernote.ui = $.summernote.ui_template(this.options);
|
||||
this.ui = $.summernote.ui;
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* create layout and initialize modules and other resources
|
||||
*/
|
||||
initialize() {
|
||||
this.layoutInfo = this.ui.createLayout(this.$note);
|
||||
this._initialize();
|
||||
this.$note.hide();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy modules and other resources and remove layout
|
||||
*/
|
||||
destroy() {
|
||||
this._destroy();
|
||||
this.$note.removeData('summernote');
|
||||
this.ui.removeLayout(this.$note, this.layoutInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* destory modules and other resources and initialize it again
|
||||
*/
|
||||
reset() {
|
||||
const disabled = this.isDisabled();
|
||||
this.code(dom.emptyPara);
|
||||
this._destroy();
|
||||
this._initialize();
|
||||
|
||||
if (disabled) {
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
||||
_initialize() {
|
||||
// set own id
|
||||
this.options.id = func.uniqueId($.now());
|
||||
// set default container for tooltips, popovers, and dialogs
|
||||
this.options.container = this.options.container || this.layoutInfo.editor;
|
||||
|
||||
// add optional buttons
|
||||
const buttons = $.extend({}, this.options.buttons);
|
||||
Object.keys(buttons).forEach((key) => {
|
||||
this.memo('button.' + key, buttons[key]);
|
||||
});
|
||||
|
||||
const modules = $.extend({}, this.options.modules, $.summernote.plugins || {});
|
||||
|
||||
// add and initialize modules
|
||||
Object.keys(modules).forEach((key) => {
|
||||
this.module(key, modules[key], true);
|
||||
});
|
||||
|
||||
Object.keys(this.modules).forEach((key) => {
|
||||
this.initializeModule(key);
|
||||
});
|
||||
}
|
||||
|
||||
_destroy() {
|
||||
// destroy modules with reversed order
|
||||
Object.keys(this.modules).reverse().forEach((key) => {
|
||||
this.removeModule(key);
|
||||
});
|
||||
|
||||
Object.keys(this.memos).forEach((key) => {
|
||||
this.removeMemo(key);
|
||||
});
|
||||
// trigger custom onDestroy callback
|
||||
this.triggerEvent('destroy', this);
|
||||
}
|
||||
|
||||
code(html) {
|
||||
const isActivated = this.invoke('codeview.isActivated');
|
||||
|
||||
if (html === undefined) {
|
||||
this.invoke('codeview.sync');
|
||||
return isActivated ? this.layoutInfo.codable.val() : this.layoutInfo.editable.html();
|
||||
} else {
|
||||
if (isActivated) {
|
||||
this.layoutInfo.codable.val(html);
|
||||
} else {
|
||||
this.layoutInfo.editable.html(html);
|
||||
}
|
||||
this.$note.val(html);
|
||||
this.triggerEvent('change', html, this.layoutInfo.editable);
|
||||
}
|
||||
}
|
||||
|
||||
isDisabled() {
|
||||
return this.layoutInfo.editable.attr('contenteditable') === 'false';
|
||||
}
|
||||
|
||||
enable() {
|
||||
this.layoutInfo.editable.attr('contenteditable', true);
|
||||
this.invoke('toolbar.activate', true);
|
||||
this.triggerEvent('disable', false);
|
||||
this.options.editing = true;
|
||||
}
|
||||
|
||||
disable() {
|
||||
// close codeview if codeview is opend
|
||||
if (this.invoke('codeview.isActivated')) {
|
||||
this.invoke('codeview.deactivate');
|
||||
}
|
||||
this.layoutInfo.editable.attr('contenteditable', false);
|
||||
this.options.editing = false;
|
||||
this.invoke('toolbar.deactivate', true);
|
||||
|
||||
this.triggerEvent('disable', true);
|
||||
}
|
||||
|
||||
triggerEvent() {
|
||||
const namespace = lists.head(arguments);
|
||||
const args = lists.tail(lists.from(arguments));
|
||||
|
||||
const callback = this.options.callbacks[func.namespaceToCamel(namespace, 'on')];
|
||||
if (callback) {
|
||||
callback.apply(this.$note[0], args);
|
||||
}
|
||||
this.$note.trigger('summernote.' + namespace, args);
|
||||
}
|
||||
|
||||
initializeModule(key) {
|
||||
const module = this.modules[key];
|
||||
module.shouldInitialize = module.shouldInitialize || func.ok;
|
||||
if (!module.shouldInitialize()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// initialize module
|
||||
if (module.initialize) {
|
||||
module.initialize();
|
||||
}
|
||||
|
||||
// attach events
|
||||
if (module.events) {
|
||||
dom.attachEvents(this.$note, module.events);
|
||||
}
|
||||
}
|
||||
|
||||
module(key, ModuleClass, withoutIntialize) {
|
||||
if (arguments.length === 1) {
|
||||
return this.modules[key];
|
||||
}
|
||||
|
||||
this.modules[key] = new ModuleClass(this);
|
||||
|
||||
if (!withoutIntialize) {
|
||||
this.initializeModule(key);
|
||||
}
|
||||
}
|
||||
|
||||
removeModule(key) {
|
||||
const module = this.modules[key];
|
||||
if (module.shouldInitialize()) {
|
||||
if (module.events) {
|
||||
dom.detachEvents(this.$note, module.events);
|
||||
}
|
||||
|
||||
if (module.destroy) {
|
||||
module.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
delete this.modules[key];
|
||||
}
|
||||
|
||||
memo(key, obj) {
|
||||
if (arguments.length === 1) {
|
||||
return this.memos[key];
|
||||
}
|
||||
this.memos[key] = obj;
|
||||
}
|
||||
|
||||
removeMemo(key) {
|
||||
if (this.memos[key] && this.memos[key].destroy) {
|
||||
this.memos[key].destroy();
|
||||
}
|
||||
|
||||
delete this.memos[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Some buttons need to change their visual style immediately once they get pressed
|
||||
*/
|
||||
createInvokeHandlerAndUpdateState(namespace, value) {
|
||||
return (event) => {
|
||||
this.createInvokeHandler(namespace, value)(event);
|
||||
this.invoke('buttons.updateCurrentStyle');
|
||||
};
|
||||
}
|
||||
|
||||
createInvokeHandler(namespace, value) {
|
||||
return (event) => {
|
||||
event.preventDefault();
|
||||
const $target = $(event.target);
|
||||
this.invoke(namespace, value || $target.closest('[data-value]').data('value'), $target);
|
||||
};
|
||||
}
|
||||
|
||||
invoke() {
|
||||
const namespace = lists.head(arguments);
|
||||
const args = lists.tail(lists.from(arguments));
|
||||
|
||||
const splits = namespace.split('.');
|
||||
const hasSeparator = splits.length > 1;
|
||||
const moduleName = hasSeparator && lists.head(splits);
|
||||
const methodName = hasSeparator ? lists.last(splits) : lists.head(splits);
|
||||
|
||||
const module = this.modules[moduleName || 'editor'];
|
||||
if (!moduleName && this[methodName]) {
|
||||
return this[methodName].apply(this, args);
|
||||
} else if (module && module[methodName] && module.shouldInitialize()) {
|
||||
return module[methodName].apply(module, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
public/vendor/editor/src/js/base/core/async.js
vendored
Executable file
@@ -0,0 +1,47 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
/**
|
||||
* @method readFileAsDataURL
|
||||
*
|
||||
* read contents of file as representing URL
|
||||
*
|
||||
* @param {File} file
|
||||
* @return {Promise} - then: dataUrl
|
||||
*/
|
||||
export function readFileAsDataURL(file) {
|
||||
return $.Deferred((deferred) => {
|
||||
$.extend(new FileReader(), {
|
||||
onload: (e) => {
|
||||
const dataURL = e.target.result;
|
||||
deferred.resolve(dataURL);
|
||||
},
|
||||
onerror: (err) => {
|
||||
deferred.reject(err);
|
||||
},
|
||||
}).readAsDataURL(file);
|
||||
}).promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* @method createImage
|
||||
*
|
||||
* create `<image>` from url string
|
||||
*
|
||||
* @param {String} url
|
||||
* @return {Promise} - then: $image
|
||||
*/
|
||||
export function createImage(url) {
|
||||
return $.Deferred((deferred) => {
|
||||
const $img = $('<img>');
|
||||
|
||||
$img.one('load', () => {
|
||||
$img.off('error abort');
|
||||
deferred.resolve($img);
|
||||
}).one('error abort', () => {
|
||||
$img.off('load').detach();
|
||||
deferred.reject($img);
|
||||
}).css({
|
||||
display: 'none',
|
||||
}).appendTo(document.body).attr('src', url);
|
||||
}).promise();
|
||||
}
|
||||
1166
public/vendor/editor/src/js/base/core/dom.js
vendored
Executable file
84
public/vendor/editor/src/js/base/core/env.js
vendored
Executable file
@@ -0,0 +1,84 @@
|
||||
import $ from 'jquery';
|
||||
const isSupportAmd = typeof define === 'function' && define.amd; // eslint-disable-line
|
||||
|
||||
/**
|
||||
* returns whether font is installed or not.
|
||||
*
|
||||
* @param {String} fontName
|
||||
* @return {Boolean}
|
||||
*/
|
||||
const genericFontFamilies = ['sans-serif', 'serif', 'monospace', 'cursive', 'fantasy'];
|
||||
|
||||
function validFontName(fontName) {
|
||||
return ($.inArray(fontName.toLowerCase(), genericFontFamilies) === -1) ? `'${fontName}'` : fontName;
|
||||
}
|
||||
|
||||
function isFontInstalled(fontName) {
|
||||
const testFontName = fontName === 'Comic Sans MS' ? 'Courier New' : 'Comic Sans MS';
|
||||
const testText = 'mmmmmmmmmmwwwww';
|
||||
const testSize = '200px';
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
var context = canvas.getContext('2d');
|
||||
|
||||
context.font = testSize + " '" + testFontName + "'";
|
||||
const originalWidth = context.measureText(testText).width;
|
||||
|
||||
context.font = testSize + ' ' + validFontName(fontName) + ', "' + testFontName + '"';
|
||||
const width = context.measureText(testText).width;
|
||||
|
||||
return originalWidth !== width;
|
||||
}
|
||||
|
||||
const userAgent = navigator.userAgent;
|
||||
const isMSIE = /MSIE|Trident/i.test(userAgent);
|
||||
let browserVersion;
|
||||
if (isMSIE) {
|
||||
let matches = /MSIE (\d+[.]\d+)/.exec(userAgent);
|
||||
if (matches) {
|
||||
browserVersion = parseFloat(matches[1]);
|
||||
}
|
||||
matches = /Trident\/.*rv:([0-9]{1,}[.0-9]{0,})/.exec(userAgent);
|
||||
if (matches) {
|
||||
browserVersion = parseFloat(matches[1]);
|
||||
}
|
||||
}
|
||||
|
||||
const isEdge = /Edge\/\d+/.test(userAgent);
|
||||
|
||||
const isSupportTouch =
|
||||
(('ontouchstart' in window) ||
|
||||
(navigator.MaxTouchPoints > 0) ||
|
||||
(navigator.msMaxTouchPoints > 0));
|
||||
|
||||
// [workaround] IE doesn't have input events for contentEditable
|
||||
// - see: https://goo.gl/4bfIvA
|
||||
const inputEventName = (isMSIE) ? 'DOMCharacterDataModified DOMSubtreeModified DOMNodeInserted' : 'input';
|
||||
|
||||
/**
|
||||
* @class core.env
|
||||
*
|
||||
* Object which check platform and agent
|
||||
*
|
||||
* @singleton
|
||||
* @alternateClassName env
|
||||
*/
|
||||
export default {
|
||||
isMac: navigator.appVersion.indexOf('Mac') > -1,
|
||||
isMSIE,
|
||||
isEdge,
|
||||
isFF: !isEdge && /firefox/i.test(userAgent),
|
||||
isPhantom: /PhantomJS/i.test(userAgent),
|
||||
isWebkit: !isEdge && /webkit/i.test(userAgent),
|
||||
isChrome: !isEdge && /chrome/i.test(userAgent),
|
||||
isSafari: !isEdge && /safari/i.test(userAgent) && (!/chrome/i.test(userAgent)),
|
||||
browserVersion,
|
||||
jqueryVersion: parseFloat($.fn.jquery),
|
||||
isSupportAmd,
|
||||
isSupportTouch,
|
||||
isFontInstalled,
|
||||
isW3CRangeSupport: !!document.createRange,
|
||||
inputEventName,
|
||||
genericFontFamilies,
|
||||
validFontName,
|
||||
};
|
||||
184
public/vendor/editor/src/js/base/core/func.js
vendored
Executable file
@@ -0,0 +1,184 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
/**
|
||||
* @class core.func
|
||||
*
|
||||
* func utils (for high-order func's arg)
|
||||
*
|
||||
* @singleton
|
||||
* @alternateClassName func
|
||||
*/
|
||||
function eq(itemA) {
|
||||
return function(itemB) {
|
||||
return itemA === itemB;
|
||||
};
|
||||
}
|
||||
|
||||
function eq2(itemA, itemB) {
|
||||
return itemA === itemB;
|
||||
}
|
||||
|
||||
function peq2(propName) {
|
||||
return function(itemA, itemB) {
|
||||
return itemA[propName] === itemB[propName];
|
||||
};
|
||||
}
|
||||
|
||||
function ok() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function fail() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function not(f) {
|
||||
return function() {
|
||||
return !f.apply(f, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
function and(fA, fB) {
|
||||
return function(item) {
|
||||
return fA(item) && fB(item);
|
||||
};
|
||||
}
|
||||
|
||||
function self(a) {
|
||||
return a;
|
||||
}
|
||||
|
||||
function invoke(obj, method) {
|
||||
return function() {
|
||||
return obj[method].apply(obj, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
let idCounter = 0;
|
||||
|
||||
/**
|
||||
* reset globally-unique id
|
||||
*
|
||||
*/
|
||||
function resetUniqueId() {
|
||||
idCounter = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate a globally-unique id
|
||||
*
|
||||
* @param {String} [prefix]
|
||||
*/
|
||||
function uniqueId(prefix) {
|
||||
const id = ++idCounter + '';
|
||||
return prefix ? prefix + id : id;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns bnd (bounds) from rect
|
||||
*
|
||||
* - IE Compatibility Issue: http://goo.gl/sRLOAo
|
||||
* - Scroll Issue: http://goo.gl/sNjUc
|
||||
*
|
||||
* @param {Rect} rect
|
||||
* @return {Object} bounds
|
||||
* @return {Number} bounds.top
|
||||
* @return {Number} bounds.left
|
||||
* @return {Number} bounds.width
|
||||
* @return {Number} bounds.height
|
||||
*/
|
||||
function rect2bnd(rect) {
|
||||
const $document = $(document);
|
||||
return {
|
||||
top: rect.top + $document.scrollTop(),
|
||||
left: rect.left + $document.scrollLeft(),
|
||||
width: rect.right - rect.left,
|
||||
height: rect.bottom - rect.top,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a copy of the object where the keys have become the values and the values the keys.
|
||||
* @param {Object} obj
|
||||
* @return {Object}
|
||||
*/
|
||||
function invertObject(obj) {
|
||||
const inverted = {};
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
inverted[obj[key]] = key;
|
||||
}
|
||||
}
|
||||
return inverted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} namespace
|
||||
* @param {String} [prefix]
|
||||
* @return {String}
|
||||
*/
|
||||
function namespaceToCamel(namespace, prefix) {
|
||||
prefix = prefix || '';
|
||||
return prefix + namespace.split('.').map(function(name) {
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function, that, as long as it continues to be invoked, will not
|
||||
* be triggered. The function will be called after it stops being called for
|
||||
* N milliseconds. If `immediate` is passed, trigger the function on the
|
||||
* leading edge, instead of the trailing.
|
||||
* @param {Function} func
|
||||
* @param {Number} wait
|
||||
* @param {Boolean} immediate
|
||||
* @return {Function}
|
||||
*/
|
||||
function debounce(func, wait, immediate) {
|
||||
let timeout;
|
||||
return function() {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
const later = () => {
|
||||
timeout = null;
|
||||
if (!immediate) {
|
||||
func.apply(context, args);
|
||||
}
|
||||
};
|
||||
const callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) {
|
||||
func.apply(context, args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} url
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isValidUrl(url) {
|
||||
const expression = /[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/gi;
|
||||
return expression.test(url);
|
||||
}
|
||||
|
||||
export default {
|
||||
eq,
|
||||
eq2,
|
||||
peq2,
|
||||
ok,
|
||||
fail,
|
||||
self,
|
||||
not,
|
||||
and,
|
||||
invoke,
|
||||
resetUniqueId,
|
||||
uniqueId,
|
||||
rect2bnd,
|
||||
invertObject,
|
||||
namespaceToCamel,
|
||||
debounce,
|
||||
isValidUrl,
|
||||
};
|
||||
113
public/vendor/editor/src/js/base/core/key.js
vendored
Executable file
@@ -0,0 +1,113 @@
|
||||
import lists from './lists';
|
||||
import func from './func';
|
||||
|
||||
const KEY_MAP = {
|
||||
'BACKSPACE': 8,
|
||||
'TAB': 9,
|
||||
'ENTER': 13,
|
||||
'ESCAPE': 27,
|
||||
'SPACE': 32,
|
||||
'DELETE': 46,
|
||||
|
||||
// Arrow
|
||||
'LEFT': 37,
|
||||
'UP': 38,
|
||||
'RIGHT': 39,
|
||||
'DOWN': 40,
|
||||
|
||||
// Number: 0-9
|
||||
'NUM0': 48,
|
||||
'NUM1': 49,
|
||||
'NUM2': 50,
|
||||
'NUM3': 51,
|
||||
'NUM4': 52,
|
||||
'NUM5': 53,
|
||||
'NUM6': 54,
|
||||
'NUM7': 55,
|
||||
'NUM8': 56,
|
||||
|
||||
// Alphabet: a-z
|
||||
'B': 66,
|
||||
'E': 69,
|
||||
'I': 73,
|
||||
'J': 74,
|
||||
'K': 75,
|
||||
'L': 76,
|
||||
'R': 82,
|
||||
'S': 83,
|
||||
'U': 85,
|
||||
'V': 86,
|
||||
'Y': 89,
|
||||
'Z': 90,
|
||||
|
||||
'SLASH': 191,
|
||||
'LEFTBRACKET': 219,
|
||||
'BACKSLASH': 220,
|
||||
'RIGHTBRACKET': 221,
|
||||
|
||||
// Navigation
|
||||
'HOME': 36,
|
||||
'END': 35,
|
||||
'PAGEUP': 33,
|
||||
'PAGEDOWN': 34,
|
||||
};
|
||||
|
||||
/**
|
||||
* @class core.key
|
||||
*
|
||||
* Object for keycodes.
|
||||
*
|
||||
* @singleton
|
||||
* @alternateClassName key
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* @method isEdit
|
||||
*
|
||||
* @param {Number} keyCode
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isEdit: (keyCode) => {
|
||||
return lists.contains([
|
||||
KEY_MAP.BACKSPACE,
|
||||
KEY_MAP.TAB,
|
||||
KEY_MAP.ENTER,
|
||||
KEY_MAP.SPACE,
|
||||
KEY_MAP.DELETE,
|
||||
], keyCode);
|
||||
},
|
||||
/**
|
||||
* @method isMove
|
||||
*
|
||||
* @param {Number} keyCode
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isMove: (keyCode) => {
|
||||
return lists.contains([
|
||||
KEY_MAP.LEFT,
|
||||
KEY_MAP.UP,
|
||||
KEY_MAP.RIGHT,
|
||||
KEY_MAP.DOWN,
|
||||
], keyCode);
|
||||
},
|
||||
/**
|
||||
* @method isNavigation
|
||||
*
|
||||
* @param {Number} keyCode
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isNavigation: (keyCode) => {
|
||||
return lists.contains([
|
||||
KEY_MAP.HOME,
|
||||
KEY_MAP.END,
|
||||
KEY_MAP.PAGEUP,
|
||||
KEY_MAP.PAGEDOWN,
|
||||
], keyCode);
|
||||
},
|
||||
/**
|
||||
* @property {Object} nameFromCode
|
||||
* @property {String} nameFromCode.8 "BACKSPACE"
|
||||
*/
|
||||
nameFromCode: func.invertObject(KEY_MAP),
|
||||
code: KEY_MAP,
|
||||
};
|
||||
212
public/vendor/editor/src/js/base/core/lists.js
vendored
Executable file
@@ -0,0 +1,212 @@
|
||||
import func from './func';
|
||||
|
||||
/**
|
||||
* returns the first item of an array.
|
||||
*
|
||||
* @param {Array} array
|
||||
*/
|
||||
function head(array) {
|
||||
return array[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the last item of an array.
|
||||
*
|
||||
* @param {Array} array
|
||||
*/
|
||||
function last(array) {
|
||||
return array[array.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* returns everything but the last entry of the array.
|
||||
*
|
||||
* @param {Array} array
|
||||
*/
|
||||
function initial(array) {
|
||||
return array.slice(0, array.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the rest of the items in an array.
|
||||
*
|
||||
* @param {Array} array
|
||||
*/
|
||||
function tail(array) {
|
||||
return array.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns item of array
|
||||
*/
|
||||
function find(array, pred) {
|
||||
for (let idx = 0, len = array.length; idx < len; idx++) {
|
||||
const item = array[idx];
|
||||
if (pred(item)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if all of the values in the array pass the predicate truth test.
|
||||
*/
|
||||
function all(array, pred) {
|
||||
for (let idx = 0, len = array.length; idx < len; idx++) {
|
||||
if (!pred(array[idx])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if the value is present in the list.
|
||||
*/
|
||||
function contains(array, item) {
|
||||
if (array && array.length && item) {
|
||||
if (array.indexOf) {
|
||||
return array.indexOf(item) !== -1;
|
||||
} else if (array.contains) {
|
||||
// `DOMTokenList` doesn't implement `.indexOf`, but it implements `.contains`
|
||||
return array.contains(item);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* get sum from a list
|
||||
*
|
||||
* @param {Array} array - array
|
||||
* @param {Function} fn - iterator
|
||||
*/
|
||||
function sum(array, fn) {
|
||||
fn = fn || func.self;
|
||||
return array.reduce(function(memo, v) {
|
||||
return memo + fn(v);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a copy of the collection with array type.
|
||||
* @param {Collection} collection - collection eg) node.childNodes, ...
|
||||
*/
|
||||
function from(collection) {
|
||||
const result = [];
|
||||
const length = collection.length;
|
||||
let idx = -1;
|
||||
while (++idx < length) {
|
||||
result[idx] = collection[idx];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns whether list is empty or not
|
||||
*/
|
||||
function isEmpty(array) {
|
||||
return !array || !array.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* cluster elements by predicate function.
|
||||
*
|
||||
* @param {Array} array - array
|
||||
* @param {Function} fn - predicate function for cluster rule
|
||||
* @param {Array[]}
|
||||
*/
|
||||
function clusterBy(array, fn) {
|
||||
if (!array.length) { return []; }
|
||||
const aTail = tail(array);
|
||||
return aTail.reduce(function(memo, v) {
|
||||
const aLast = last(memo);
|
||||
if (fn(last(aLast), v)) {
|
||||
aLast[aLast.length] = v;
|
||||
} else {
|
||||
memo[memo.length] = [v];
|
||||
}
|
||||
return memo;
|
||||
}, [[head(array)]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a copy of the array with all false values removed
|
||||
*
|
||||
* @param {Array} array - array
|
||||
* @param {Function} fn - predicate function for cluster rule
|
||||
*/
|
||||
function compact(array) {
|
||||
const aResult = [];
|
||||
for (let idx = 0, len = array.length; idx < len; idx++) {
|
||||
if (array[idx]) { aResult.push(array[idx]); }
|
||||
}
|
||||
return aResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* produces a duplicate-free version of the array
|
||||
*
|
||||
* @param {Array} array
|
||||
*/
|
||||
function unique(array) {
|
||||
const results = [];
|
||||
|
||||
for (let idx = 0, len = array.length; idx < len; idx++) {
|
||||
if (!contains(results, array[idx])) {
|
||||
results.push(array[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns next item.
|
||||
* @param {Array} array
|
||||
*/
|
||||
function next(array, item) {
|
||||
if (array && array.length && item) {
|
||||
const idx = array.indexOf(item);
|
||||
return idx === -1 ? null : array[idx + 1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns prev item.
|
||||
* @param {Array} array
|
||||
*/
|
||||
function prev(array, item) {
|
||||
if (array && array.length && item) {
|
||||
const idx = array.indexOf(item);
|
||||
return idx === -1 ? null : array[idx - 1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class core.list
|
||||
*
|
||||
* list utils
|
||||
*
|
||||
* @singleton
|
||||
* @alternateClassName list
|
||||
*/
|
||||
export default {
|
||||
head,
|
||||
last,
|
||||
initial,
|
||||
tail,
|
||||
prev,
|
||||
next,
|
||||
find,
|
||||
contains,
|
||||
all,
|
||||
sum,
|
||||
from,
|
||||
isEmpty,
|
||||
clusterBy,
|
||||
compact,
|
||||
unique,
|
||||
};
|
||||
921
public/vendor/editor/src/js/base/core/range.js
vendored
Executable file
@@ -0,0 +1,921 @@
|
||||
import $ from 'jquery';
|
||||
import env from './env';
|
||||
import func from './func';
|
||||
import lists from './lists';
|
||||
import dom from './dom';
|
||||
|
||||
/**
|
||||
* return boundaryPoint from TextRange, inspired by Andy Na's HuskyRange.js
|
||||
*
|
||||
* @param {TextRange} textRange
|
||||
* @param {Boolean} isStart
|
||||
* @return {BoundaryPoint}
|
||||
*
|
||||
* @see http://msdn.microsoft.com/en-us/library/ie/ms535872(v=vs.85).aspx
|
||||
*/
|
||||
function textRangeToPoint(textRange, isStart) {
|
||||
let container = textRange.parentElement();
|
||||
let offset;
|
||||
|
||||
const tester = document.body.createTextRange();
|
||||
let prevContainer;
|
||||
const childNodes = lists.from(container.childNodes);
|
||||
for (offset = 0; offset < childNodes.length; offset++) {
|
||||
if (dom.isText(childNodes[offset])) {
|
||||
continue;
|
||||
}
|
||||
tester.moveToElementText(childNodes[offset]);
|
||||
if (tester.compareEndPoints('StartToStart', textRange) >= 0) {
|
||||
break;
|
||||
}
|
||||
prevContainer = childNodes[offset];
|
||||
}
|
||||
|
||||
if (offset !== 0 && dom.isText(childNodes[offset - 1])) {
|
||||
const textRangeStart = document.body.createTextRange();
|
||||
let curTextNode = null;
|
||||
textRangeStart.moveToElementText(prevContainer || container);
|
||||
textRangeStart.collapse(!prevContainer);
|
||||
curTextNode = prevContainer ? prevContainer.nextSibling : container.firstChild;
|
||||
|
||||
const pointTester = textRange.duplicate();
|
||||
pointTester.setEndPoint('StartToStart', textRangeStart);
|
||||
let textCount = pointTester.text.replace(/[\r\n]/g, '').length;
|
||||
|
||||
while (textCount > curTextNode.nodeValue.length && curTextNode.nextSibling) {
|
||||
textCount -= curTextNode.nodeValue.length;
|
||||
curTextNode = curTextNode.nextSibling;
|
||||
}
|
||||
|
||||
// [workaround] enforce IE to re-reference curTextNode, hack
|
||||
const dummy = curTextNode.nodeValue; // eslint-disable-line
|
||||
|
||||
if (isStart && curTextNode.nextSibling && dom.isText(curTextNode.nextSibling) &&
|
||||
textCount === curTextNode.nodeValue.length) {
|
||||
textCount -= curTextNode.nodeValue.length;
|
||||
curTextNode = curTextNode.nextSibling;
|
||||
}
|
||||
|
||||
container = curTextNode;
|
||||
offset = textCount;
|
||||
}
|
||||
|
||||
return {
|
||||
cont: container,
|
||||
offset: offset,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* return TextRange from boundary point (inspired by google closure-library)
|
||||
* @param {BoundaryPoint} point
|
||||
* @return {TextRange}
|
||||
*/
|
||||
function pointToTextRange(point) {
|
||||
const textRangeInfo = function(container, offset) {
|
||||
let node, isCollapseToStart;
|
||||
|
||||
if (dom.isText(container)) {
|
||||
const prevTextNodes = dom.listPrev(container, func.not(dom.isText));
|
||||
const prevContainer = lists.last(prevTextNodes).previousSibling;
|
||||
node = prevContainer || container.parentNode;
|
||||
offset += lists.sum(lists.tail(prevTextNodes), dom.nodeLength);
|
||||
isCollapseToStart = !prevContainer;
|
||||
} else {
|
||||
node = container.childNodes[offset] || container;
|
||||
if (dom.isText(node)) {
|
||||
return textRangeInfo(node, 0);
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
isCollapseToStart = false;
|
||||
}
|
||||
|
||||
return {
|
||||
node: node,
|
||||
collapseToStart: isCollapseToStart,
|
||||
offset: offset,
|
||||
};
|
||||
};
|
||||
|
||||
const textRange = document.body.createTextRange();
|
||||
const info = textRangeInfo(point.node, point.offset);
|
||||
|
||||
textRange.moveToElementText(info.node);
|
||||
textRange.collapse(info.collapseToStart);
|
||||
textRange.moveStart('character', info.offset);
|
||||
return textRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapped Range
|
||||
*
|
||||
* @constructor
|
||||
* @param {Node} sc - start container
|
||||
* @param {Number} so - start offset
|
||||
* @param {Node} ec - end container
|
||||
* @param {Number} eo - end offset
|
||||
*/
|
||||
class WrappedRange {
|
||||
constructor(sc, so, ec, eo) {
|
||||
this.sc = sc;
|
||||
this.so = so;
|
||||
this.ec = ec;
|
||||
this.eo = eo;
|
||||
|
||||
// isOnEditable: judge whether range is on editable or not
|
||||
this.isOnEditable = this.makeIsOn(dom.isEditable);
|
||||
// isOnList: judge whether range is on list node or not
|
||||
this.isOnList = this.makeIsOn(dom.isList);
|
||||
// isOnAnchor: judge whether range is on anchor node or not
|
||||
this.isOnAnchor = this.makeIsOn(dom.isAnchor);
|
||||
// isOnCell: judge whether range is on cell node or not
|
||||
this.isOnCell = this.makeIsOn(dom.isCell);
|
||||
// isOnData: judge whether range is on data node or not
|
||||
this.isOnData = this.makeIsOn(dom.isData);
|
||||
}
|
||||
|
||||
// nativeRange: get nativeRange from sc, so, ec, eo
|
||||
nativeRange() {
|
||||
if (env.isW3CRangeSupport) {
|
||||
const w3cRange = document.createRange();
|
||||
w3cRange.setStart(this.sc, this.sc.data && this.so > this.sc.data.length ? 0 : this.so);
|
||||
w3cRange.setEnd(this.ec, this.sc.data ? Math.min(this.eo, this.sc.data.length) : this.eo);
|
||||
|
||||
return w3cRange;
|
||||
} else {
|
||||
const textRange = pointToTextRange({
|
||||
node: this.sc,
|
||||
offset: this.so,
|
||||
});
|
||||
|
||||
textRange.setEndPoint('EndToEnd', pointToTextRange({
|
||||
node: this.ec,
|
||||
offset: this.eo,
|
||||
}));
|
||||
|
||||
return textRange;
|
||||
}
|
||||
}
|
||||
|
||||
getPoints() {
|
||||
return {
|
||||
sc: this.sc,
|
||||
so: this.so,
|
||||
ec: this.ec,
|
||||
eo: this.eo,
|
||||
};
|
||||
}
|
||||
|
||||
getStartPoint() {
|
||||
return {
|
||||
node: this.sc,
|
||||
offset: this.so,
|
||||
};
|
||||
}
|
||||
|
||||
getEndPoint() {
|
||||
return {
|
||||
node: this.ec,
|
||||
offset: this.eo,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* select update visible range
|
||||
*/
|
||||
select() {
|
||||
const nativeRng = this.nativeRange();
|
||||
if (env.isW3CRangeSupport) {
|
||||
const selection = document.getSelection();
|
||||
if (selection.rangeCount > 0) {
|
||||
selection.removeAllRanges();
|
||||
}
|
||||
selection.addRange(nativeRng);
|
||||
} else {
|
||||
nativeRng.select();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the scrollbar to start container(sc) of current range
|
||||
*
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
scrollIntoView(container) {
|
||||
const height = $(container).height();
|
||||
if (container.scrollTop + height < this.sc.offsetTop) {
|
||||
container.scrollTop += Math.abs(container.scrollTop + height - this.sc.offsetTop);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
normalize() {
|
||||
/**
|
||||
* @param {BoundaryPoint} point
|
||||
* @param {Boolean} isLeftToRight - true: prefer to choose right node
|
||||
* - false: prefer to choose left node
|
||||
* @return {BoundaryPoint}
|
||||
*/
|
||||
const getVisiblePoint = function(point, isLeftToRight) {
|
||||
if (!point) {
|
||||
return point;
|
||||
}
|
||||
|
||||
// Just use the given point [XXX:Adhoc]
|
||||
// - case 01. if the point is on the middle of the node
|
||||
// - case 02. if the point is on the right edge and prefer to choose left node
|
||||
// - case 03. if the point is on the left edge and prefer to choose right node
|
||||
// - case 04. if the point is on the right edge and prefer to choose right node but the node is void
|
||||
// - case 05. if the point is on the left edge and prefer to choose left node but the node is void
|
||||
// - case 06. if the point is on the block node and there is no children
|
||||
if (dom.isVisiblePoint(point)) {
|
||||
if (!dom.isEdgePoint(point) ||
|
||||
(dom.isRightEdgePoint(point) && !isLeftToRight) ||
|
||||
(dom.isLeftEdgePoint(point) && isLeftToRight) ||
|
||||
(dom.isRightEdgePoint(point) && isLeftToRight && dom.isVoid(point.node.nextSibling)) ||
|
||||
(dom.isLeftEdgePoint(point) && !isLeftToRight && dom.isVoid(point.node.previousSibling)) ||
|
||||
(dom.isBlock(point.node) && dom.isEmpty(point.node))) {
|
||||
return point;
|
||||
}
|
||||
}
|
||||
|
||||
// point on block's edge
|
||||
const block = dom.ancestor(point.node, dom.isBlock);
|
||||
let hasRightNode = false;
|
||||
|
||||
if (!hasRightNode) {
|
||||
const prevPoint = dom.prevPoint(point) || { node: null };
|
||||
hasRightNode = (dom.isLeftEdgePointOf(point, block) || dom.isVoid(prevPoint.node)) && !isLeftToRight;
|
||||
}
|
||||
|
||||
let hasLeftNode = false;
|
||||
if (!hasLeftNode) {
|
||||
const nextPoint = dom.nextPoint(point) || { node: null };
|
||||
hasLeftNode = (dom.isRightEdgePointOf(point, block) || dom.isVoid(nextPoint.node)) && isLeftToRight;
|
||||
}
|
||||
|
||||
if (hasRightNode || hasLeftNode) {
|
||||
// returns point already on visible point
|
||||
if (dom.isVisiblePoint(point)) {
|
||||
return point;
|
||||
}
|
||||
// reverse direction
|
||||
isLeftToRight = !isLeftToRight;
|
||||
}
|
||||
|
||||
const nextPoint = isLeftToRight ? dom.nextPointUntil(dom.nextPoint(point), dom.isVisiblePoint)
|
||||
: dom.prevPointUntil(dom.prevPoint(point), dom.isVisiblePoint);
|
||||
return nextPoint || point;
|
||||
};
|
||||
|
||||
const endPoint = getVisiblePoint(this.getEndPoint(), false);
|
||||
const startPoint = this.isCollapsed() ? endPoint : getVisiblePoint(this.getStartPoint(), true);
|
||||
|
||||
return new WrappedRange(
|
||||
startPoint.node,
|
||||
startPoint.offset,
|
||||
endPoint.node,
|
||||
endPoint.offset
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns matched nodes on range
|
||||
*
|
||||
* @param {Function} [pred] - predicate function
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [options.includeAncestor]
|
||||
* @param {Boolean} [options.fullyContains]
|
||||
* @return {Node[]}
|
||||
*/
|
||||
nodes(pred, options) {
|
||||
pred = pred || func.ok;
|
||||
|
||||
const includeAncestor = options && options.includeAncestor;
|
||||
const fullyContains = options && options.fullyContains;
|
||||
|
||||
// TODO compare points and sort
|
||||
const startPoint = this.getStartPoint();
|
||||
const endPoint = this.getEndPoint();
|
||||
|
||||
const nodes = [];
|
||||
const leftEdgeNodes = [];
|
||||
|
||||
dom.walkPoint(startPoint, endPoint, function(point) {
|
||||
if (dom.isEditable(point.node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let node;
|
||||
if (fullyContains) {
|
||||
if (dom.isLeftEdgePoint(point)) {
|
||||
leftEdgeNodes.push(point.node);
|
||||
}
|
||||
if (dom.isRightEdgePoint(point) && lists.contains(leftEdgeNodes, point.node)) {
|
||||
node = point.node;
|
||||
}
|
||||
} else if (includeAncestor) {
|
||||
node = dom.ancestor(point.node, pred);
|
||||
} else {
|
||||
node = point.node;
|
||||
}
|
||||
|
||||
if (node && pred(node)) {
|
||||
nodes.push(node);
|
||||
}
|
||||
}, true);
|
||||
|
||||
return lists.unique(nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns commonAncestor of range
|
||||
* @return {Element} - commonAncestor
|
||||
*/
|
||||
commonAncestor() {
|
||||
return dom.commonAncestor(this.sc, this.ec);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns expanded range by pred
|
||||
*
|
||||
* @param {Function} pred - predicate function
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
expand(pred) {
|
||||
const startAncestor = dom.ancestor(this.sc, pred);
|
||||
const endAncestor = dom.ancestor(this.ec, pred);
|
||||
|
||||
if (!startAncestor && !endAncestor) {
|
||||
return new WrappedRange(this.sc, this.so, this.ec, this.eo);
|
||||
}
|
||||
|
||||
const boundaryPoints = this.getPoints();
|
||||
|
||||
if (startAncestor) {
|
||||
boundaryPoints.sc = startAncestor;
|
||||
boundaryPoints.so = 0;
|
||||
}
|
||||
|
||||
if (endAncestor) {
|
||||
boundaryPoints.ec = endAncestor;
|
||||
boundaryPoints.eo = dom.nodeLength(endAncestor);
|
||||
}
|
||||
|
||||
return new WrappedRange(
|
||||
boundaryPoints.sc,
|
||||
boundaryPoints.so,
|
||||
boundaryPoints.ec,
|
||||
boundaryPoints.eo
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Boolean} isCollapseToStart
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
collapse(isCollapseToStart) {
|
||||
if (isCollapseToStart) {
|
||||
return new WrappedRange(this.sc, this.so, this.sc, this.so);
|
||||
} else {
|
||||
return new WrappedRange(this.ec, this.eo, this.ec, this.eo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* splitText on range
|
||||
*/
|
||||
splitText() {
|
||||
const isSameContainer = this.sc === this.ec;
|
||||
const boundaryPoints = this.getPoints();
|
||||
|
||||
if (dom.isText(this.ec) && !dom.isEdgePoint(this.getEndPoint())) {
|
||||
this.ec.splitText(this.eo);
|
||||
}
|
||||
|
||||
if (dom.isText(this.sc) && !dom.isEdgePoint(this.getStartPoint())) {
|
||||
boundaryPoints.sc = this.sc.splitText(this.so);
|
||||
boundaryPoints.so = 0;
|
||||
|
||||
if (isSameContainer) {
|
||||
boundaryPoints.ec = boundaryPoints.sc;
|
||||
boundaryPoints.eo = this.eo - this.so;
|
||||
}
|
||||
}
|
||||
|
||||
return new WrappedRange(
|
||||
boundaryPoints.sc,
|
||||
boundaryPoints.so,
|
||||
boundaryPoints.ec,
|
||||
boundaryPoints.eo
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* delete contents on range
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
deleteContents() {
|
||||
if (this.isCollapsed()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const rng = this.splitText();
|
||||
const nodes = rng.nodes(null, {
|
||||
fullyContains: true,
|
||||
});
|
||||
|
||||
// find new cursor point
|
||||
const point = dom.prevPointUntil(rng.getStartPoint(), function(point) {
|
||||
return !lists.contains(nodes, point.node);
|
||||
});
|
||||
|
||||
const emptyParents = [];
|
||||
$.each(nodes, function(idx, node) {
|
||||
// find empty parents
|
||||
const parent = node.parentNode;
|
||||
if (point.node !== parent && dom.nodeLength(parent) === 1) {
|
||||
emptyParents.push(parent);
|
||||
}
|
||||
dom.remove(node, false);
|
||||
});
|
||||
|
||||
// remove empty parents
|
||||
$.each(emptyParents, function(idx, node) {
|
||||
dom.remove(node, false);
|
||||
});
|
||||
|
||||
return new WrappedRange(
|
||||
point.node,
|
||||
point.offset,
|
||||
point.node,
|
||||
point.offset
|
||||
).normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* makeIsOn: return isOn(pred) function
|
||||
*/
|
||||
makeIsOn(pred) {
|
||||
return function() {
|
||||
const ancestor = dom.ancestor(this.sc, pred);
|
||||
return !!ancestor && (ancestor === dom.ancestor(this.ec, pred));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Function} pred
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isLeftEdgeOf(pred) {
|
||||
if (!dom.isLeftEdgePoint(this.getStartPoint())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const node = dom.ancestor(this.sc, pred);
|
||||
return node && dom.isLeftEdgeOf(this.sc, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns whether range was collapsed or not
|
||||
*/
|
||||
isCollapsed() {
|
||||
return this.sc === this.ec && this.so === this.eo;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrap inline nodes which children of body with paragraph
|
||||
*
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
wrapBodyInlineWithPara() {
|
||||
if (dom.isBodyContainer(this.sc) && dom.isEmpty(this.sc)) {
|
||||
this.sc.innerHTML = dom.emptyPara;
|
||||
return new WrappedRange(this.sc.firstChild, 0, this.sc.firstChild, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* [workaround] firefox often create range on not visible point. so normalize here.
|
||||
* - firefox: |<p>text</p>|
|
||||
* - chrome: <p>|text|</p>
|
||||
*/
|
||||
const rng = this.normalize();
|
||||
if (dom.isParaInline(this.sc) || dom.isPara(this.sc)) {
|
||||
return rng;
|
||||
}
|
||||
|
||||
// find inline top ancestor
|
||||
let topAncestor;
|
||||
if (dom.isInline(rng.sc)) {
|
||||
const ancestors = dom.listAncestor(rng.sc, func.not(dom.isInline));
|
||||
topAncestor = lists.last(ancestors);
|
||||
if (!dom.isInline(topAncestor)) {
|
||||
topAncestor = ancestors[ancestors.length - 2] || rng.sc.childNodes[rng.so];
|
||||
}
|
||||
} else {
|
||||
topAncestor = rng.sc.childNodes[rng.so > 0 ? rng.so - 1 : 0];
|
||||
}
|
||||
|
||||
if (topAncestor) {
|
||||
// siblings not in paragraph
|
||||
let inlineSiblings = dom.listPrev(topAncestor, dom.isParaInline).reverse();
|
||||
inlineSiblings = inlineSiblings.concat(dom.listNext(topAncestor.nextSibling, dom.isParaInline));
|
||||
|
||||
// wrap with paragraph
|
||||
if (inlineSiblings.length) {
|
||||
const para = dom.wrap(lists.head(inlineSiblings), 'p');
|
||||
dom.appendChildNodes(para, lists.tail(inlineSiblings));
|
||||
}
|
||||
}
|
||||
|
||||
return this.normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* insert node at current cursor
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Node}
|
||||
*/
|
||||
insertNode(node) {
|
||||
let rng = this;
|
||||
|
||||
if (dom.isText(node) || dom.isInline(node)) {
|
||||
rng = this.wrapBodyInlineWithPara().deleteContents();
|
||||
}
|
||||
|
||||
const info = dom.splitPoint(rng.getStartPoint(), dom.isInline(node));
|
||||
if (info.rightNode) {
|
||||
info.rightNode.parentNode.insertBefore(node, info.rightNode);
|
||||
} else {
|
||||
info.container.appendChild(node);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert html at current cursor
|
||||
*/
|
||||
pasteHTML(markup) {
|
||||
markup = $.trim(markup);
|
||||
|
||||
const contentsContainer = $('<div></div>').html(markup)[0];
|
||||
let childNodes = lists.from(contentsContainer.childNodes);
|
||||
|
||||
// const rng = this.wrapBodyInlineWithPara().deleteContents();
|
||||
const rng = this;
|
||||
|
||||
if (rng.so >= 0) {
|
||||
childNodes = childNodes.reverse();
|
||||
}
|
||||
childNodes = childNodes.map(function(childNode) {
|
||||
return rng.insertNode(childNode);
|
||||
});
|
||||
if (rng.so > 0) {
|
||||
childNodes = childNodes.reverse();
|
||||
}
|
||||
return childNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns text in range
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
toString() {
|
||||
const nativeRng = this.nativeRange();
|
||||
return env.isW3CRangeSupport ? nativeRng.toString() : nativeRng.text;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns range for word before cursor
|
||||
*
|
||||
* @param {Boolean} [findAfter] - find after cursor, default: false
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
getWordRange(findAfter) {
|
||||
let endPoint = this.getEndPoint();
|
||||
|
||||
if (!dom.isCharPoint(endPoint)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const startPoint = dom.prevPointUntil(endPoint, function(point) {
|
||||
return !dom.isCharPoint(point);
|
||||
});
|
||||
|
||||
if (findAfter) {
|
||||
endPoint = dom.nextPointUntil(endPoint, function(point) {
|
||||
return !dom.isCharPoint(point);
|
||||
});
|
||||
}
|
||||
|
||||
return new WrappedRange(
|
||||
startPoint.node,
|
||||
startPoint.offset,
|
||||
endPoint.node,
|
||||
endPoint.offset
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns range for words before cursor
|
||||
*
|
||||
* @param {Boolean} [findAfter] - find after cursor, default: false
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
getWordsRange(findAfter) {
|
||||
var endPoint = this.getEndPoint();
|
||||
|
||||
var isNotTextPoint = function(point) {
|
||||
return !dom.isCharPoint(point) && !dom.isSpacePoint(point);
|
||||
};
|
||||
|
||||
if (isNotTextPoint(endPoint)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var startPoint = dom.prevPointUntil(endPoint, isNotTextPoint);
|
||||
|
||||
if (findAfter) {
|
||||
endPoint = dom.nextPointUntil(endPoint, isNotTextPoint);
|
||||
}
|
||||
|
||||
return new WrappedRange(
|
||||
startPoint.node,
|
||||
startPoint.offset,
|
||||
endPoint.node,
|
||||
endPoint.offset
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns range for words before cursor that match with a Regex
|
||||
*
|
||||
* example:
|
||||
* range: 'hi @Peter Pan'
|
||||
* regex: '/@[a-z ]+/i'
|
||||
* return range: '@Peter Pan'
|
||||
*
|
||||
* @param {RegExp} [regex]
|
||||
* @return {WrappedRange|null}
|
||||
*/
|
||||
getWordsMatchRange(regex) {
|
||||
var endPoint = this.getEndPoint();
|
||||
|
||||
var startPoint = dom.prevPointUntil(endPoint, function(point) {
|
||||
if (!dom.isCharPoint(point) && !dom.isSpacePoint(point)) {
|
||||
return true;
|
||||
}
|
||||
var rng = new WrappedRange(
|
||||
point.node,
|
||||
point.offset,
|
||||
endPoint.node,
|
||||
endPoint.offset
|
||||
);
|
||||
var result = regex.exec(rng.toString());
|
||||
return result && result.index === 0;
|
||||
});
|
||||
|
||||
var rng = new WrappedRange(
|
||||
startPoint.node,
|
||||
startPoint.offset,
|
||||
endPoint.node,
|
||||
endPoint.offset
|
||||
);
|
||||
|
||||
var text = rng.toString();
|
||||
var result = regex.exec(text);
|
||||
|
||||
if (result && result[0].length === text.length) {
|
||||
return rng;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create offsetPath bookmark
|
||||
*
|
||||
* @param {Node} editable
|
||||
*/
|
||||
bookmark(editable) {
|
||||
return {
|
||||
s: {
|
||||
path: dom.makeOffsetPath(editable, this.sc),
|
||||
offset: this.so,
|
||||
},
|
||||
e: {
|
||||
path: dom.makeOffsetPath(editable, this.ec),
|
||||
offset: this.eo,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* create offsetPath bookmark base on paragraph
|
||||
*
|
||||
* @param {Node[]} paras
|
||||
*/
|
||||
paraBookmark(paras) {
|
||||
return {
|
||||
s: {
|
||||
path: lists.tail(dom.makeOffsetPath(lists.head(paras), this.sc)),
|
||||
offset: this.so,
|
||||
},
|
||||
e: {
|
||||
path: lists.tail(dom.makeOffsetPath(lists.last(paras), this.ec)),
|
||||
offset: this.eo,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* getClientRects
|
||||
* @return {Rect[]}
|
||||
*/
|
||||
getClientRects() {
|
||||
const nativeRng = this.nativeRange();
|
||||
return nativeRng.getClientRects();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data structure
|
||||
* * BoundaryPoint: a point of dom tree
|
||||
* * BoundaryPoints: two boundaryPoints corresponding to the start and the end of the Range
|
||||
*
|
||||
* See to http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Position
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* create Range Object From arguments or Browser Selection
|
||||
*
|
||||
* @param {Node} sc - start container
|
||||
* @param {Number} so - start offset
|
||||
* @param {Node} ec - end container
|
||||
* @param {Number} eo - end offset
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
create: function(sc, so, ec, eo) {
|
||||
if (arguments.length === 4) {
|
||||
return new WrappedRange(sc, so, ec, eo);
|
||||
} else if (arguments.length === 2) { // collapsed
|
||||
ec = sc;
|
||||
eo = so;
|
||||
return new WrappedRange(sc, so, ec, eo);
|
||||
} else {
|
||||
let wrappedRange = this.createFromSelection();
|
||||
|
||||
if (!wrappedRange && arguments.length === 1) {
|
||||
let bodyElement = arguments[0];
|
||||
if (dom.isEditable(bodyElement)) {
|
||||
bodyElement = bodyElement.lastChild;
|
||||
}
|
||||
return this.createFromBodyElement(bodyElement, dom.emptyPara === arguments[0].innerHTML);
|
||||
}
|
||||
return wrappedRange;
|
||||
}
|
||||
},
|
||||
|
||||
createFromBodyElement: function(bodyElement, isCollapseToStart = false) {
|
||||
var wrappedRange = this.createFromNode(bodyElement);
|
||||
return wrappedRange.collapse(isCollapseToStart);
|
||||
},
|
||||
|
||||
createFromSelection: function() {
|
||||
let sc, so, ec, eo;
|
||||
if (env.isW3CRangeSupport) {
|
||||
const selection = document.getSelection();
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
return null;
|
||||
} else if (dom.isBody(selection.anchorNode)) {
|
||||
// Firefox: returns entire body as range on initialization.
|
||||
// We won't never need it.
|
||||
return null;
|
||||
}
|
||||
|
||||
const nativeRng = selection.getRangeAt(0);
|
||||
sc = nativeRng.startContainer;
|
||||
so = nativeRng.startOffset;
|
||||
ec = nativeRng.endContainer;
|
||||
eo = nativeRng.endOffset;
|
||||
} else { // IE8: TextRange
|
||||
const textRange = document.selection.createRange();
|
||||
const textRangeEnd = textRange.duplicate();
|
||||
textRangeEnd.collapse(false);
|
||||
const textRangeStart = textRange;
|
||||
textRangeStart.collapse(true);
|
||||
|
||||
let startPoint = textRangeToPoint(textRangeStart, true);
|
||||
let endPoint = textRangeToPoint(textRangeEnd, false);
|
||||
|
||||
// same visible point case: range was collapsed.
|
||||
if (dom.isText(startPoint.node) && dom.isLeftEdgePoint(startPoint) &&
|
||||
dom.isTextNode(endPoint.node) && dom.isRightEdgePoint(endPoint) &&
|
||||
endPoint.node.nextSibling === startPoint.node) {
|
||||
startPoint = endPoint;
|
||||
}
|
||||
|
||||
sc = startPoint.cont;
|
||||
so = startPoint.offset;
|
||||
ec = endPoint.cont;
|
||||
eo = endPoint.offset;
|
||||
}
|
||||
|
||||
return new WrappedRange(sc, so, ec, eo);
|
||||
},
|
||||
|
||||
/**
|
||||
* @method
|
||||
*
|
||||
* create WrappedRange from node
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
createFromNode: function(node) {
|
||||
let sc = node;
|
||||
let so = 0;
|
||||
let ec = node;
|
||||
let eo = dom.nodeLength(ec);
|
||||
|
||||
// browsers can't target a picture or void node
|
||||
if (dom.isVoid(sc)) {
|
||||
so = dom.listPrev(sc).length - 1;
|
||||
sc = sc.parentNode;
|
||||
}
|
||||
if (dom.isBR(ec)) {
|
||||
eo = dom.listPrev(ec).length - 1;
|
||||
ec = ec.parentNode;
|
||||
} else if (dom.isVoid(ec)) {
|
||||
eo = dom.listPrev(ec).length;
|
||||
ec = ec.parentNode;
|
||||
}
|
||||
|
||||
return this.create(sc, so, ec, eo);
|
||||
},
|
||||
|
||||
/**
|
||||
* create WrappedRange from node after position
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
createFromNodeBefore: function(node) {
|
||||
return this.createFromNode(node).collapse(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* create WrappedRange from node after position
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
createFromNodeAfter: function(node) {
|
||||
return this.createFromNode(node).collapse();
|
||||
},
|
||||
|
||||
/**
|
||||
* @method
|
||||
*
|
||||
* create WrappedRange from bookmark
|
||||
*
|
||||
* @param {Node} editable
|
||||
* @param {Object} bookmark
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
createFromBookmark: function(editable, bookmark) {
|
||||
const sc = dom.fromOffsetPath(editable, bookmark.s.path);
|
||||
const so = bookmark.s.offset;
|
||||
const ec = dom.fromOffsetPath(editable, bookmark.e.path);
|
||||
const eo = bookmark.e.offset;
|
||||
return new WrappedRange(sc, so, ec, eo);
|
||||
},
|
||||
|
||||
/**
|
||||
* @method
|
||||
*
|
||||
* create WrappedRange from paraBookmark
|
||||
*
|
||||
* @param {Object} bookmark
|
||||
* @param {Node[]} paras
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
createFromParaBookmark: function(bookmark, paras) {
|
||||
const so = bookmark.s.offset;
|
||||
const eo = bookmark.e.offset;
|
||||
const sc = dom.fromOffsetPath(lists.head(paras), bookmark.s.path);
|
||||
const ec = dom.fromOffsetPath(lists.last(paras), bookmark.e.path);
|
||||
|
||||
return new WrappedRange(sc, so, ec, eo);
|
||||
},
|
||||
};
|
||||
286
public/vendor/editor/src/js/base/editing/Bullet.js
vendored
Executable file
@@ -0,0 +1,286 @@
|
||||
import $ from 'jquery';
|
||||
import lists from '../core/lists';
|
||||
import func from '../core/func';
|
||||
import dom from '../core/dom';
|
||||
import range from '../core/range';
|
||||
|
||||
export default class Bullet {
|
||||
/**
|
||||
* toggle ordered list
|
||||
*/
|
||||
insertOrderedList(editable) {
|
||||
this.toggleList('OL', editable);
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle unordered list
|
||||
*/
|
||||
insertUnorderedList(editable) {
|
||||
this.toggleList('UL', editable);
|
||||
}
|
||||
|
||||
/**
|
||||
* indent
|
||||
*/
|
||||
indent(editable) {
|
||||
const rng = range.create(editable).wrapBodyInlineWithPara();
|
||||
|
||||
const paras = rng.nodes(dom.isPara, { includeAncestor: true });
|
||||
const clustereds = lists.clusterBy(paras, func.peq2('parentNode'));
|
||||
|
||||
$.each(clustereds, (idx, paras) => {
|
||||
const head = lists.head(paras);
|
||||
if (dom.isLi(head)) {
|
||||
const previousList = this.findList(head.previousSibling);
|
||||
if (previousList) {
|
||||
paras
|
||||
.map(para => previousList.appendChild(para));
|
||||
} else {
|
||||
this.wrapList(paras, head.parentNode.nodeName);
|
||||
paras
|
||||
.map((para) => para.parentNode)
|
||||
.map((para) => this.appendToPrevious(para));
|
||||
}
|
||||
} else {
|
||||
$.each(paras, (idx, para) => {
|
||||
$(para).css('marginLeft', (idx, val) => {
|
||||
return (parseInt(val, 10) || 0) + 25;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
rng.select();
|
||||
}
|
||||
|
||||
/**
|
||||
* outdent
|
||||
*/
|
||||
outdent(editable) {
|
||||
const rng = range.create(editable).wrapBodyInlineWithPara();
|
||||
|
||||
const paras = rng.nodes(dom.isPara, { includeAncestor: true });
|
||||
const clustereds = lists.clusterBy(paras, func.peq2('parentNode'));
|
||||
|
||||
$.each(clustereds, (idx, paras) => {
|
||||
const head = lists.head(paras);
|
||||
if (dom.isLi(head)) {
|
||||
this.releaseList([paras]);
|
||||
} else {
|
||||
$.each(paras, (idx, para) => {
|
||||
$(para).css('marginLeft', (idx, val) => {
|
||||
val = (parseInt(val, 10) || 0);
|
||||
return val > 25 ? val - 25 : '';
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
rng.select();
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle list
|
||||
*
|
||||
* @param {String} listName - OL or UL
|
||||
*/
|
||||
toggleList(listName, editable) {
|
||||
const rng = range.create(editable).wrapBodyInlineWithPara();
|
||||
|
||||
let paras = rng.nodes(dom.isPara, { includeAncestor: true });
|
||||
const bookmark = rng.paraBookmark(paras);
|
||||
const clustereds = lists.clusterBy(paras, func.peq2('parentNode'));
|
||||
|
||||
// paragraph to list
|
||||
if (lists.find(paras, dom.isPurePara)) {
|
||||
let wrappedParas = [];
|
||||
$.each(clustereds, (idx, paras) => {
|
||||
wrappedParas = wrappedParas.concat(this.wrapList(paras, listName));
|
||||
});
|
||||
paras = wrappedParas;
|
||||
// list to paragraph or change list style
|
||||
} else {
|
||||
const diffLists = rng.nodes(dom.isList, {
|
||||
includeAncestor: true,
|
||||
}).filter((listNode) => {
|
||||
return !$.nodeName(listNode, listName);
|
||||
});
|
||||
|
||||
if (diffLists.length) {
|
||||
$.each(diffLists, (idx, listNode) => {
|
||||
dom.replace(listNode, listName);
|
||||
});
|
||||
} else {
|
||||
paras = this.releaseList(clustereds, true);
|
||||
}
|
||||
}
|
||||
|
||||
range.createFromParaBookmark(bookmark, paras).select();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Node[]} paras
|
||||
* @param {String} listName
|
||||
* @return {Node[]}
|
||||
*/
|
||||
wrapList(paras, listName) {
|
||||
const head = lists.head(paras);
|
||||
const last = lists.last(paras);
|
||||
|
||||
const prevList = dom.isList(head.previousSibling) && head.previousSibling;
|
||||
const nextList = dom.isList(last.nextSibling) && last.nextSibling;
|
||||
|
||||
const listNode = prevList || dom.insertAfter(dom.create(listName || 'UL'), last);
|
||||
|
||||
// P to LI
|
||||
paras = paras.map((para) => {
|
||||
return dom.isPurePara(para) ? dom.replace(para, 'LI') : para;
|
||||
});
|
||||
|
||||
// append to list(<ul>, <ol>)
|
||||
dom.appendChildNodes(listNode, paras);
|
||||
|
||||
if (nextList) {
|
||||
dom.appendChildNodes(listNode, lists.from(nextList.childNodes));
|
||||
dom.remove(nextList);
|
||||
}
|
||||
|
||||
return paras;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method releaseList
|
||||
*
|
||||
* @param {Array[]} clustereds
|
||||
* @param {Boolean} isEscapseToBody
|
||||
* @return {Node[]}
|
||||
*/
|
||||
releaseList(clustereds, isEscapseToBody) {
|
||||
let releasedParas = [];
|
||||
|
||||
$.each(clustereds, (idx, paras) => {
|
||||
const head = lists.head(paras);
|
||||
const last = lists.last(paras);
|
||||
|
||||
const headList = isEscapseToBody ? dom.lastAncestor(head, dom.isList) : head.parentNode;
|
||||
const parentItem = headList.parentNode;
|
||||
|
||||
if (headList.parentNode.nodeName === 'LI') {
|
||||
paras.map(para => {
|
||||
const newList = this.findNextSiblings(para);
|
||||
|
||||
if (parentItem.nextSibling) {
|
||||
parentItem.parentNode.insertBefore(
|
||||
para,
|
||||
parentItem.nextSibling
|
||||
);
|
||||
} else {
|
||||
parentItem.parentNode.appendChild(para);
|
||||
}
|
||||
|
||||
if (newList.length) {
|
||||
this.wrapList(newList, headList.nodeName);
|
||||
para.appendChild(newList[0].parentNode);
|
||||
}
|
||||
});
|
||||
|
||||
if (headList.children.length === 0) {
|
||||
parentItem.removeChild(headList);
|
||||
}
|
||||
|
||||
if (parentItem.childNodes.length === 0) {
|
||||
parentItem.parentNode.removeChild(parentItem);
|
||||
}
|
||||
} else {
|
||||
const lastList = headList.childNodes.length > 1 ? dom.splitTree(headList, {
|
||||
node: last.parentNode,
|
||||
offset: dom.position(last) + 1,
|
||||
}, {
|
||||
isSkipPaddingBlankHTML: true,
|
||||
}) : null;
|
||||
|
||||
const middleList = dom.splitTree(headList, {
|
||||
node: head.parentNode,
|
||||
offset: dom.position(head),
|
||||
}, {
|
||||
isSkipPaddingBlankHTML: true,
|
||||
});
|
||||
|
||||
paras = isEscapseToBody ? dom.listDescendant(middleList, dom.isLi)
|
||||
: lists.from(middleList.childNodes).filter(dom.isLi);
|
||||
|
||||
// LI to P
|
||||
if (isEscapseToBody || !dom.isList(headList.parentNode)) {
|
||||
paras = paras.map((para) => {
|
||||
return dom.replace(para, 'P');
|
||||
});
|
||||
}
|
||||
|
||||
$.each(lists.from(paras).reverse(), (idx, para) => {
|
||||
dom.insertAfter(para, headList);
|
||||
});
|
||||
|
||||
// remove empty lists
|
||||
const rootLists = lists.compact([headList, middleList, lastList]);
|
||||
$.each(rootLists, (idx, rootList) => {
|
||||
const listNodes = [rootList].concat(dom.listDescendant(rootList, dom.isList));
|
||||
$.each(listNodes.reverse(), (idx, listNode) => {
|
||||
if (!dom.nodeLength(listNode)) {
|
||||
dom.remove(listNode, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
releasedParas = releasedParas.concat(paras);
|
||||
});
|
||||
|
||||
return releasedParas;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method appendToPrevious
|
||||
*
|
||||
* Appends list to previous list item, if
|
||||
* none exist it wraps the list in a new list item.
|
||||
*
|
||||
* @param {HTMLNode} ListItem
|
||||
* @return {HTMLNode}
|
||||
*/
|
||||
appendToPrevious(node) {
|
||||
return node.previousSibling
|
||||
? dom.appendChildNodes(node.previousSibling, [node])
|
||||
: this.wrapList([node], 'LI');
|
||||
}
|
||||
|
||||
/**
|
||||
* @method findList
|
||||
*
|
||||
* Finds an existing list in list item
|
||||
*
|
||||
* @param {HTMLNode} ListItem
|
||||
* @return {Array[]}
|
||||
*/
|
||||
findList(node) {
|
||||
return node
|
||||
? lists.find(node.children, child => ['OL', 'UL'].indexOf(child.nodeName) > -1)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method findNextSiblings
|
||||
*
|
||||
* Finds all list item siblings that follow it
|
||||
*
|
||||
* @param {HTMLNode} ListItem
|
||||
* @return {HTMLNode}
|
||||
*/
|
||||
findNextSiblings(node) {
|
||||
const siblings = [];
|
||||
while (node.nextSibling) {
|
||||
siblings.push(node.nextSibling);
|
||||
node = node.nextSibling;
|
||||
}
|
||||
return siblings;
|
||||
}
|
||||
}
|
||||
127
public/vendor/editor/src/js/base/editing/History.js
vendored
Executable file
@@ -0,0 +1,127 @@
|
||||
import range from '../core/range';
|
||||
|
||||
export default class History {
|
||||
constructor(context) {
|
||||
this.stack = [];
|
||||
this.stackOffset = -1;
|
||||
this.context = context;
|
||||
this.$editable = context.layoutInfo.editable;
|
||||
this.editable = this.$editable[0];
|
||||
}
|
||||
|
||||
makeSnapshot() {
|
||||
const rng = range.create(this.editable);
|
||||
const emptyBookmark = { s: { path: [], offset: 0 }, e: { path: [], offset: 0 } };
|
||||
|
||||
return {
|
||||
contents: this.$editable.html(),
|
||||
bookmark: ((rng && rng.isOnEditable()) ? rng.bookmark(this.editable) : emptyBookmark),
|
||||
};
|
||||
}
|
||||
|
||||
applySnapshot(snapshot) {
|
||||
if (snapshot.contents !== null) {
|
||||
this.$editable.html(snapshot.contents);
|
||||
}
|
||||
if (snapshot.bookmark !== null) {
|
||||
range.createFromBookmark(this.editable, snapshot.bookmark).select();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @method rewind
|
||||
* Rewinds the history stack back to the first snapshot taken.
|
||||
* Leaves the stack intact, so that "Redo" can still be used.
|
||||
*/
|
||||
rewind() {
|
||||
// Create snap shot if not yet recorded
|
||||
if (this.$editable.html() !== this.stack[this.stackOffset].contents) {
|
||||
this.recordUndo();
|
||||
}
|
||||
|
||||
// Return to the first available snapshot.
|
||||
this.stackOffset = 0;
|
||||
|
||||
// Apply that snapshot.
|
||||
this.applySnapshot(this.stack[this.stackOffset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @method commit
|
||||
* Resets history stack, but keeps current editor's content.
|
||||
*/
|
||||
commit() {
|
||||
// Clear the stack.
|
||||
this.stack = [];
|
||||
|
||||
// Restore stackOffset to its original value.
|
||||
this.stackOffset = -1;
|
||||
|
||||
// Record our first snapshot (of nothing).
|
||||
this.recordUndo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @method reset
|
||||
* Resets the history stack completely; reverting to an empty editor.
|
||||
*/
|
||||
reset() {
|
||||
// Clear the stack.
|
||||
this.stack = [];
|
||||
|
||||
// Restore stackOffset to its original value.
|
||||
this.stackOffset = -1;
|
||||
|
||||
// Clear the editable area.
|
||||
this.$editable.html('');
|
||||
|
||||
// Record our first snapshot (of nothing).
|
||||
this.recordUndo();
|
||||
}
|
||||
|
||||
/**
|
||||
* undo
|
||||
*/
|
||||
undo() {
|
||||
// Create snap shot if not yet recorded
|
||||
if (this.$editable.html() !== this.stack[this.stackOffset].contents) {
|
||||
this.recordUndo();
|
||||
}
|
||||
|
||||
if (this.stackOffset > 0) {
|
||||
this.stackOffset--;
|
||||
this.applySnapshot(this.stack[this.stackOffset]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* redo
|
||||
*/
|
||||
redo() {
|
||||
if (this.stack.length - 1 > this.stackOffset) {
|
||||
this.stackOffset++;
|
||||
this.applySnapshot(this.stack[this.stackOffset]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* recorded undo
|
||||
*/
|
||||
recordUndo() {
|
||||
this.stackOffset++;
|
||||
|
||||
// Wash out stack after stackOffset
|
||||
if (this.stack.length > this.stackOffset) {
|
||||
this.stack = this.stack.slice(0, this.stackOffset);
|
||||
}
|
||||
|
||||
// Create new snapshot and push it to the end
|
||||
this.stack.push(this.makeSnapshot());
|
||||
|
||||
// If the stack size reachs to the limit, then slice it
|
||||
if (this.stack.length > this.context.options.historyLimit) {
|
||||
this.stack.shift();
|
||||
this.stackOffset -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
165
public/vendor/editor/src/js/base/editing/Style.js
vendored
Executable file
@@ -0,0 +1,165 @@
|
||||
import $ from 'jquery';
|
||||
import env from '../core/env';
|
||||
import func from '../core/func';
|
||||
import lists from '../core/lists';
|
||||
import dom from '../core/dom';
|
||||
|
||||
export default class Style {
|
||||
/**
|
||||
* @method jQueryCSS
|
||||
*
|
||||
* [workaround] for old jQuery
|
||||
* passing an array of style properties to .css()
|
||||
* will result in an object of property-value pairs.
|
||||
* (compability with version < 1.9)
|
||||
*
|
||||
* @private
|
||||
* @param {jQuery} $obj
|
||||
* @param {Array} propertyNames - An array of one or more CSS properties.
|
||||
* @return {Object}
|
||||
*/
|
||||
jQueryCSS($obj, propertyNames) {
|
||||
if (env.jqueryVersion < 1.9) {
|
||||
const result = {};
|
||||
$.each(propertyNames, (idx, propertyName) => {
|
||||
result[propertyName] = $obj.css(propertyName);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
return $obj.css(propertyNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns style object from node
|
||||
*
|
||||
* @param {jQuery} $node
|
||||
* @return {Object}
|
||||
*/
|
||||
fromNode($node) {
|
||||
const properties = ['font-family', 'font-size', 'text-align', 'list-style-type', 'line-height'];
|
||||
const styleInfo = this.jQueryCSS($node, properties) || {};
|
||||
|
||||
const fontSize = $node[0].style.fontSize || styleInfo['font-size'];
|
||||
|
||||
styleInfo['font-size'] = parseInt(fontSize, 10);
|
||||
styleInfo['font-size-unit'] = fontSize.match(/[a-z%]+$/);
|
||||
|
||||
return styleInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* paragraph level style
|
||||
*
|
||||
* @param {WrappedRange} rng
|
||||
* @param {Object} styleInfo
|
||||
*/
|
||||
stylePara(rng, styleInfo) {
|
||||
$.each(rng.nodes(dom.isPara, {
|
||||
includeAncestor: true,
|
||||
}), (idx, para) => {
|
||||
$(para).css(styleInfo);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* insert and returns styleNodes on range.
|
||||
*
|
||||
* @param {WrappedRange} rng
|
||||
* @param {Object} [options] - options for styleNodes
|
||||
* @param {String} [options.nodeName] - default: `SPAN`
|
||||
* @param {Boolean} [options.expandClosestSibling] - default: `false`
|
||||
* @param {Boolean} [options.onlyPartialContains] - default: `false`
|
||||
* @return {Node[]}
|
||||
*/
|
||||
styleNodes(rng, options) {
|
||||
rng = rng.splitText();
|
||||
|
||||
const nodeName = (options && options.nodeName) || 'SPAN';
|
||||
const expandClosestSibling = !!(options && options.expandClosestSibling);
|
||||
const onlyPartialContains = !!(options && options.onlyPartialContains);
|
||||
|
||||
if (rng.isCollapsed()) {
|
||||
return [rng.insertNode(dom.create(nodeName))];
|
||||
}
|
||||
|
||||
let pred = dom.makePredByNodeName(nodeName);
|
||||
const nodes = rng.nodes(dom.isText, {
|
||||
fullyContains: true,
|
||||
}).map((text) => {
|
||||
return dom.singleChildAncestor(text, pred) || dom.wrap(text, nodeName);
|
||||
});
|
||||
|
||||
if (expandClosestSibling) {
|
||||
if (onlyPartialContains) {
|
||||
const nodesInRange = rng.nodes();
|
||||
// compose with partial contains predication
|
||||
pred = func.and(pred, (node) => {
|
||||
return lists.contains(nodesInRange, node);
|
||||
});
|
||||
}
|
||||
|
||||
return nodes.map((node) => {
|
||||
const siblings = dom.withClosestSiblings(node, pred);
|
||||
const head = lists.head(siblings);
|
||||
const tails = lists.tail(siblings);
|
||||
$.each(tails, (idx, elem) => {
|
||||
dom.appendChildNodes(head, elem.childNodes);
|
||||
dom.remove(elem);
|
||||
});
|
||||
return lists.head(siblings);
|
||||
});
|
||||
} else {
|
||||
return nodes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get current style on cursor
|
||||
*
|
||||
* @param {WrappedRange} rng
|
||||
* @return {Object} - object contains style properties.
|
||||
*/
|
||||
current(rng) {
|
||||
const $cont = $(!dom.isElement(rng.sc) ? rng.sc.parentNode : rng.sc);
|
||||
let styleInfo = this.fromNode($cont);
|
||||
|
||||
// document.queryCommandState for toggle state
|
||||
// [workaround] prevent Firefox nsresult: "0x80004005 (NS_ERROR_FAILURE)"
|
||||
try {
|
||||
styleInfo = $.extend(styleInfo, {
|
||||
'font-bold': document.queryCommandState('bold') ? 'bold' : 'normal',
|
||||
'font-italic': document.queryCommandState('italic') ? 'italic' : 'normal',
|
||||
'font-underline': document.queryCommandState('underline') ? 'underline' : 'normal',
|
||||
'font-subscript': document.queryCommandState('subscript') ? 'subscript' : 'normal',
|
||||
'font-superscript': document.queryCommandState('superscript') ? 'superscript' : 'normal',
|
||||
'font-strikethrough': document.queryCommandState('strikethrough') ? 'strikethrough' : 'normal',
|
||||
'font-family': document.queryCommandValue('fontname') || styleInfo['font-family'],
|
||||
});
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line
|
||||
}
|
||||
|
||||
// list-style-type to list-style(unordered, ordered)
|
||||
if (!rng.isOnList()) {
|
||||
styleInfo['list-style'] = 'none';
|
||||
} else {
|
||||
const orderedTypes = ['circle', 'disc', 'disc-leading-zero', 'square'];
|
||||
const isUnordered = orderedTypes.indexOf(styleInfo['list-style-type']) > -1;
|
||||
styleInfo['list-style'] = isUnordered ? 'unordered' : 'ordered';
|
||||
}
|
||||
|
||||
const para = dom.ancestor(rng.sc, dom.isPara);
|
||||
if (para && para.style['line-height']) {
|
||||
styleInfo['line-height'] = para.style.lineHeight;
|
||||
} else {
|
||||
const lineHeight = parseInt(styleInfo['line-height'], 10) / parseInt(styleInfo['font-size'], 10);
|
||||
styleInfo['line-height'] = lineHeight.toFixed(1);
|
||||
}
|
||||
|
||||
styleInfo.anchor = rng.isOnAnchor() && dom.ancestor(rng.sc, dom.isAnchor);
|
||||
styleInfo.ancestors = dom.listAncestor(rng.sc, dom.isEditable);
|
||||
styleInfo.range = rng;
|
||||
|
||||
return styleInfo;
|
||||
}
|
||||
}
|
||||
580
public/vendor/editor/src/js/base/editing/Table.js
vendored
Executable file
@@ -0,0 +1,580 @@
|
||||
import $ from 'jquery';
|
||||
import dom from '../core/dom';
|
||||
import range from '../core/range';
|
||||
import lists from '../core/lists';
|
||||
|
||||
/**
|
||||
* @class Create a virtual table to create what actions to do in change.
|
||||
* @param {object} startPoint Cell selected to apply change.
|
||||
* @param {enum} where Where change will be applied Row or Col. Use enum: TableResultAction.where
|
||||
* @param {enum} action Action to be applied. Use enum: TableResultAction.requestAction
|
||||
* @param {object} domTable Dom element of table to make changes.
|
||||
*/
|
||||
const TableResultAction = function(startPoint, where, action, domTable) {
|
||||
const _startPoint = { 'colPos': 0, 'rowPos': 0 };
|
||||
const _virtualTable = [];
|
||||
const _actionCellList = [];
|
||||
|
||||
/// ///////////////////////////////////////////
|
||||
// Private functions
|
||||
/// ///////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Set the startPoint of action.
|
||||
*/
|
||||
function setStartPoint() {
|
||||
if (!startPoint || !startPoint.tagName || (startPoint.tagName.toLowerCase() !== 'td' && startPoint.tagName.toLowerCase() !== 'th')) {
|
||||
// Impossible to identify start Cell point
|
||||
return;
|
||||
}
|
||||
_startPoint.colPos = startPoint.cellIndex;
|
||||
if (!startPoint.parentElement || !startPoint.parentElement.tagName || startPoint.parentElement.tagName.toLowerCase() !== 'tr') {
|
||||
// Impossible to identify start Row point
|
||||
return;
|
||||
}
|
||||
_startPoint.rowPos = startPoint.parentElement.rowIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define virtual table position info object.
|
||||
*
|
||||
* @param {int} rowIndex Index position in line of virtual table.
|
||||
* @param {int} cellIndex Index position in column of virtual table.
|
||||
* @param {object} baseRow Row affected by this position.
|
||||
* @param {object} baseCell Cell affected by this position.
|
||||
* @param {bool} isSpan Inform if it is an span cell/row.
|
||||
*/
|
||||
function setVirtualTablePosition(rowIndex, cellIndex, baseRow, baseCell, isRowSpan, isColSpan, isVirtualCell) {
|
||||
const objPosition = {
|
||||
'baseRow': baseRow,
|
||||
'baseCell': baseCell,
|
||||
'isRowSpan': isRowSpan,
|
||||
'isColSpan': isColSpan,
|
||||
'isVirtual': isVirtualCell,
|
||||
};
|
||||
if (!_virtualTable[rowIndex]) {
|
||||
_virtualTable[rowIndex] = [];
|
||||
}
|
||||
_virtualTable[rowIndex][cellIndex] = objPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create action cell object.
|
||||
*
|
||||
* @param {object} virtualTableCellObj Object of specific position on virtual table.
|
||||
* @param {enum} resultAction Action to be applied in that item.
|
||||
*/
|
||||
function getActionCell(virtualTableCellObj, resultAction, virtualRowPosition, virtualColPosition) {
|
||||
return {
|
||||
'baseCell': virtualTableCellObj.baseCell,
|
||||
'action': resultAction,
|
||||
'virtualTable': {
|
||||
'rowIndex': virtualRowPosition,
|
||||
'cellIndex': virtualColPosition,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Recover free index of row to append Cell.
|
||||
*
|
||||
* @param {int} rowIndex Index of row to find free space.
|
||||
* @param {int} cellIndex Index of cell to find free space in table.
|
||||
*/
|
||||
function recoverCellIndex(rowIndex, cellIndex) {
|
||||
if (!_virtualTable[rowIndex]) {
|
||||
return cellIndex;
|
||||
}
|
||||
if (!_virtualTable[rowIndex][cellIndex]) {
|
||||
return cellIndex;
|
||||
}
|
||||
|
||||
let newCellIndex = cellIndex;
|
||||
while (_virtualTable[rowIndex][newCellIndex]) {
|
||||
newCellIndex++;
|
||||
if (!_virtualTable[rowIndex][newCellIndex]) {
|
||||
return newCellIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recover info about row and cell and add information to virtual table.
|
||||
*
|
||||
* @param {object} row Row to recover information.
|
||||
* @param {object} cell Cell to recover information.
|
||||
*/
|
||||
function addCellInfoToVirtual(row, cell) {
|
||||
const cellIndex = recoverCellIndex(row.rowIndex, cell.cellIndex);
|
||||
const cellHasColspan = (cell.colSpan > 1);
|
||||
const cellHasRowspan = (cell.rowSpan > 1);
|
||||
const isThisSelectedCell = (row.rowIndex === _startPoint.rowPos && cell.cellIndex === _startPoint.colPos);
|
||||
setVirtualTablePosition(row.rowIndex, cellIndex, row, cell, cellHasRowspan, cellHasColspan, false);
|
||||
|
||||
// Add span rows to virtual Table.
|
||||
const rowspanNumber = cell.attributes.rowSpan ? parseInt(cell.attributes.rowSpan.value, 10) : 0;
|
||||
if (rowspanNumber > 1) {
|
||||
for (let rp = 1; rp < rowspanNumber; rp++) {
|
||||
const rowspanIndex = row.rowIndex + rp;
|
||||
adjustStartPoint(rowspanIndex, cellIndex, cell, isThisSelectedCell);
|
||||
setVirtualTablePosition(rowspanIndex, cellIndex, row, cell, true, cellHasColspan, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Add span cols to virtual table.
|
||||
const colspanNumber = cell.attributes.colSpan ? parseInt(cell.attributes.colSpan.value, 10) : 0;
|
||||
if (colspanNumber > 1) {
|
||||
for (let cp = 1; cp < colspanNumber; cp++) {
|
||||
const cellspanIndex = recoverCellIndex(row.rowIndex, (cellIndex + cp));
|
||||
adjustStartPoint(row.rowIndex, cellspanIndex, cell, isThisSelectedCell);
|
||||
setVirtualTablePosition(row.rowIndex, cellspanIndex, row, cell, cellHasRowspan, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process validation and adjust of start point if needed
|
||||
*
|
||||
* @param {int} rowIndex
|
||||
* @param {int} cellIndex
|
||||
* @param {object} cell
|
||||
* @param {bool} isSelectedCell
|
||||
*/
|
||||
function adjustStartPoint(rowIndex, cellIndex, cell, isSelectedCell) {
|
||||
if (rowIndex === _startPoint.rowPos && _startPoint.colPos >= cell.cellIndex && cell.cellIndex <= cellIndex && !isSelectedCell) {
|
||||
_startPoint.colPos++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create virtual table of cells with all cells, including span cells.
|
||||
*/
|
||||
function createVirtualTable() {
|
||||
const rows = domTable.rows;
|
||||
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
||||
const cells = rows[rowIndex].cells;
|
||||
for (let cellIndex = 0; cellIndex < cells.length; cellIndex++) {
|
||||
addCellInfoToVirtual(rows[rowIndex], cells[cellIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get action to be applied on the cell.
|
||||
*
|
||||
* @param {object} cell virtual table cell to apply action
|
||||
*/
|
||||
function getDeleteResultActionToCell(cell) {
|
||||
switch (where) {
|
||||
case TableResultAction.where.Column:
|
||||
if (cell.isColSpan) {
|
||||
return TableResultAction.resultAction.SubtractSpanCount;
|
||||
}
|
||||
break;
|
||||
case TableResultAction.where.Row:
|
||||
if (!cell.isVirtual && cell.isRowSpan) {
|
||||
return TableResultAction.resultAction.AddCell;
|
||||
} else if (cell.isRowSpan) {
|
||||
return TableResultAction.resultAction.SubtractSpanCount;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return TableResultAction.resultAction.RemoveCell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get action to be applied on the cell.
|
||||
*
|
||||
* @param {object} cell virtual table cell to apply action
|
||||
*/
|
||||
function getAddResultActionToCell(cell) {
|
||||
switch (where) {
|
||||
case TableResultAction.where.Column:
|
||||
if (cell.isColSpan) {
|
||||
return TableResultAction.resultAction.SumSpanCount;
|
||||
} else if (cell.isRowSpan && cell.isVirtual) {
|
||||
return TableResultAction.resultAction.Ignore;
|
||||
}
|
||||
break;
|
||||
case TableResultAction.where.Row:
|
||||
if (cell.isRowSpan) {
|
||||
return TableResultAction.resultAction.SumSpanCount;
|
||||
} else if (cell.isColSpan && cell.isVirtual) {
|
||||
return TableResultAction.resultAction.Ignore;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return TableResultAction.resultAction.AddCell;
|
||||
}
|
||||
|
||||
function init() {
|
||||
setStartPoint();
|
||||
createVirtualTable();
|
||||
}
|
||||
|
||||
/// ///////////////////////////////////////////
|
||||
// Public functions
|
||||
/// ///////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Recover array os what to do in table.
|
||||
*/
|
||||
this.getActionList = function() {
|
||||
const fixedRow = (where === TableResultAction.where.Row) ? _startPoint.rowPos : -1;
|
||||
const fixedCol = (where === TableResultAction.where.Column) ? _startPoint.colPos : -1;
|
||||
|
||||
let actualPosition = 0;
|
||||
let canContinue = true;
|
||||
while (canContinue) {
|
||||
const rowPosition = (fixedRow >= 0) ? fixedRow : actualPosition;
|
||||
const colPosition = (fixedCol >= 0) ? fixedCol : actualPosition;
|
||||
const row = _virtualTable[rowPosition];
|
||||
if (!row) {
|
||||
canContinue = false;
|
||||
return _actionCellList;
|
||||
}
|
||||
const cell = row[colPosition];
|
||||
if (!cell) {
|
||||
canContinue = false;
|
||||
return _actionCellList;
|
||||
}
|
||||
|
||||
// Define action to be applied in this cell
|
||||
let resultAction = TableResultAction.resultAction.Ignore;
|
||||
switch (action) {
|
||||
case TableResultAction.requestAction.Add:
|
||||
resultAction = getAddResultActionToCell(cell);
|
||||
break;
|
||||
case TableResultAction.requestAction.Delete:
|
||||
resultAction = getDeleteResultActionToCell(cell);
|
||||
break;
|
||||
}
|
||||
_actionCellList.push(getActionCell(cell, resultAction, rowPosition, colPosition));
|
||||
actualPosition++;
|
||||
}
|
||||
|
||||
return _actionCellList;
|
||||
};
|
||||
|
||||
init();
|
||||
};
|
||||
/**
|
||||
*
|
||||
* Where action occours enum.
|
||||
*/
|
||||
TableResultAction.where = { 'Row': 0, 'Column': 1 };
|
||||
/**
|
||||
*
|
||||
* Requested action to apply enum.
|
||||
*/
|
||||
TableResultAction.requestAction = { 'Add': 0, 'Delete': 1 };
|
||||
/**
|
||||
*
|
||||
* Result action to be executed enum.
|
||||
*/
|
||||
TableResultAction.resultAction = { 'Ignore': 0, 'SubtractSpanCount': 1, 'RemoveCell': 2, 'AddCell': 3, 'SumSpanCount': 4 };
|
||||
|
||||
/**
|
||||
*
|
||||
* @class editing.Table
|
||||
*
|
||||
* Table
|
||||
*
|
||||
*/
|
||||
export default class Table {
|
||||
/**
|
||||
* handle tab key
|
||||
*
|
||||
* @param {WrappedRange} rng
|
||||
* @param {Boolean} isShift
|
||||
*/
|
||||
tab(rng, isShift) {
|
||||
const cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
|
||||
const table = dom.ancestor(cell, dom.isTable);
|
||||
const cells = dom.listDescendant(table, dom.isCell);
|
||||
|
||||
const nextCell = lists[isShift ? 'prev' : 'next'](cells, cell);
|
||||
if (nextCell) {
|
||||
range.create(nextCell, 0).select();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new row
|
||||
*
|
||||
* @param {WrappedRange} rng
|
||||
* @param {String} position (top/bottom)
|
||||
* @return {Node}
|
||||
*/
|
||||
addRow(rng, position) {
|
||||
const cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
|
||||
|
||||
const currentTr = $(cell).closest('tr');
|
||||
const trAttributes = this.recoverAttributes(currentTr);
|
||||
const html = $('<tr' + trAttributes + '></tr>');
|
||||
|
||||
const vTable = new TableResultAction(cell, TableResultAction.where.Row,
|
||||
TableResultAction.requestAction.Add, $(currentTr).closest('table')[0]);
|
||||
const actions = vTable.getActionList();
|
||||
|
||||
for (let idCell = 0; idCell < actions.length; idCell++) {
|
||||
const currentCell = actions[idCell];
|
||||
const tdAttributes = this.recoverAttributes(currentCell.baseCell);
|
||||
switch (currentCell.action) {
|
||||
case TableResultAction.resultAction.AddCell:
|
||||
html.append('<td' + tdAttributes + '>' + dom.blank + '</td>');
|
||||
break;
|
||||
case TableResultAction.resultAction.SumSpanCount:
|
||||
{
|
||||
if (position === 'top') {
|
||||
const baseCellTr = currentCell.baseCell.parent;
|
||||
const isTopFromRowSpan = (!baseCellTr ? 0 : currentCell.baseCell.closest('tr').rowIndex) <= currentTr[0].rowIndex;
|
||||
if (isTopFromRowSpan) {
|
||||
const newTd = $('<div></div>').append($('<td' + tdAttributes + '>' + dom.blank + '</td>').removeAttr('rowspan')).html();
|
||||
html.append(newTd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let rowspanNumber = parseInt(currentCell.baseCell.rowSpan, 10);
|
||||
rowspanNumber++;
|
||||
currentCell.baseCell.setAttribute('rowSpan', rowspanNumber);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (position === 'top') {
|
||||
currentTr.before(html);
|
||||
} else {
|
||||
const cellHasRowspan = (cell.rowSpan > 1);
|
||||
if (cellHasRowspan) {
|
||||
const lastTrIndex = currentTr[0].rowIndex + (cell.rowSpan - 2);
|
||||
$($(currentTr).parent().find('tr')[lastTrIndex]).after($(html));
|
||||
return;
|
||||
}
|
||||
currentTr.after(html);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new col
|
||||
*
|
||||
* @param {WrappedRange} rng
|
||||
* @param {String} position (left/right)
|
||||
* @return {Node}
|
||||
*/
|
||||
addCol(rng, position) {
|
||||
const cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
|
||||
const row = $(cell).closest('tr');
|
||||
const rowsGroup = $(row).siblings();
|
||||
rowsGroup.push(row);
|
||||
|
||||
const vTable = new TableResultAction(cell, TableResultAction.where.Column,
|
||||
TableResultAction.requestAction.Add, $(row).closest('table')[0]);
|
||||
const actions = vTable.getActionList();
|
||||
|
||||
for (let actionIndex = 0; actionIndex < actions.length; actionIndex++) {
|
||||
const currentCell = actions[actionIndex];
|
||||
const tdAttributes = this.recoverAttributes(currentCell.baseCell);
|
||||
switch (currentCell.action) {
|
||||
case TableResultAction.resultAction.AddCell:
|
||||
if (position === 'right') {
|
||||
$(currentCell.baseCell).after('<td' + tdAttributes + '>' + dom.blank + '</td>');
|
||||
} else {
|
||||
$(currentCell.baseCell).before('<td' + tdAttributes + '>' + dom.blank + '</td>');
|
||||
}
|
||||
break;
|
||||
case TableResultAction.resultAction.SumSpanCount:
|
||||
if (position === 'right') {
|
||||
let colspanNumber = parseInt(currentCell.baseCell.colSpan, 10);
|
||||
colspanNumber++;
|
||||
currentCell.baseCell.setAttribute('colSpan', colspanNumber);
|
||||
} else {
|
||||
$(currentCell.baseCell).before('<td' + tdAttributes + '>' + dom.blank + '</td>');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy attributes from element.
|
||||
*
|
||||
* @param {object} Element to recover attributes.
|
||||
* @return {string} Copied string elements.
|
||||
*/
|
||||
recoverAttributes(el) {
|
||||
let resultStr = '';
|
||||
|
||||
if (!el) {
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
const attrList = el.attributes || [];
|
||||
|
||||
for (let i = 0; i < attrList.length; i++) {
|
||||
if (attrList[i].name.toLowerCase() === 'id') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attrList[i].specified) {
|
||||
resultStr += ' ' + attrList[i].name + '=\'' + attrList[i].value + '\'';
|
||||
}
|
||||
}
|
||||
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete current row
|
||||
*
|
||||
* @param {WrappedRange} rng
|
||||
* @return {Node}
|
||||
*/
|
||||
deleteRow(rng) {
|
||||
const cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
|
||||
const row = $(cell).closest('tr');
|
||||
const cellPos = row.children('td, th').index($(cell));
|
||||
const rowPos = row[0].rowIndex;
|
||||
|
||||
const vTable = new TableResultAction(cell, TableResultAction.where.Row,
|
||||
TableResultAction.requestAction.Delete, $(row).closest('table')[0]);
|
||||
const actions = vTable.getActionList();
|
||||
|
||||
for (let actionIndex = 0; actionIndex < actions.length; actionIndex++) {
|
||||
if (!actions[actionIndex]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const baseCell = actions[actionIndex].baseCell;
|
||||
const virtualPosition = actions[actionIndex].virtualTable;
|
||||
const hasRowspan = (baseCell.rowSpan && baseCell.rowSpan > 1);
|
||||
let rowspanNumber = (hasRowspan) ? parseInt(baseCell.rowSpan, 10) : 0;
|
||||
switch (actions[actionIndex].action) {
|
||||
case TableResultAction.resultAction.Ignore:
|
||||
continue;
|
||||
case TableResultAction.resultAction.AddCell:
|
||||
{
|
||||
const nextRow = row.next('tr')[0];
|
||||
if (!nextRow) { continue; }
|
||||
const cloneRow = row[0].cells[cellPos];
|
||||
if (hasRowspan) {
|
||||
if (rowspanNumber > 2) {
|
||||
rowspanNumber--;
|
||||
nextRow.insertBefore(cloneRow, nextRow.cells[cellPos]);
|
||||
nextRow.cells[cellPos].setAttribute('rowSpan', rowspanNumber);
|
||||
nextRow.cells[cellPos].innerHTML = '';
|
||||
} else if (rowspanNumber === 2) {
|
||||
nextRow.insertBefore(cloneRow, nextRow.cells[cellPos]);
|
||||
nextRow.cells[cellPos].removeAttribute('rowSpan');
|
||||
nextRow.cells[cellPos].innerHTML = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
case TableResultAction.resultAction.SubtractSpanCount:
|
||||
if (hasRowspan) {
|
||||
if (rowspanNumber > 2) {
|
||||
rowspanNumber--;
|
||||
baseCell.setAttribute('rowSpan', rowspanNumber);
|
||||
if (virtualPosition.rowIndex !== rowPos && baseCell.cellIndex === cellPos) { baseCell.innerHTML = ''; }
|
||||
} else if (rowspanNumber === 2) {
|
||||
baseCell.removeAttribute('rowSpan');
|
||||
if (virtualPosition.rowIndex !== rowPos && baseCell.cellIndex === cellPos) { baseCell.innerHTML = ''; }
|
||||
}
|
||||
}
|
||||
continue;
|
||||
case TableResultAction.resultAction.RemoveCell:
|
||||
// Do not need remove cell because row will be deleted.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
row.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete current col
|
||||
*
|
||||
* @param {WrappedRange} rng
|
||||
* @return {Node}
|
||||
*/
|
||||
deleteCol(rng) {
|
||||
const cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
|
||||
const row = $(cell).closest('tr');
|
||||
const cellPos = row.children('td, th').index($(cell));
|
||||
|
||||
const vTable = new TableResultAction(cell, TableResultAction.where.Column,
|
||||
TableResultAction.requestAction.Delete, $(row).closest('table')[0]);
|
||||
const actions = vTable.getActionList();
|
||||
|
||||
for (let actionIndex = 0; actionIndex < actions.length; actionIndex++) {
|
||||
if (!actions[actionIndex]) {
|
||||
continue;
|
||||
}
|
||||
switch (actions[actionIndex].action) {
|
||||
case TableResultAction.resultAction.Ignore:
|
||||
continue;
|
||||
case TableResultAction.resultAction.SubtractSpanCount:
|
||||
{
|
||||
const baseCell = actions[actionIndex].baseCell;
|
||||
const hasColspan = (baseCell.colSpan && baseCell.colSpan > 1);
|
||||
if (hasColspan) {
|
||||
let colspanNumber = (baseCell.colSpan) ? parseInt(baseCell.colSpan, 10) : 0;
|
||||
if (colspanNumber > 2) {
|
||||
colspanNumber--;
|
||||
baseCell.setAttribute('colSpan', colspanNumber);
|
||||
if (baseCell.cellIndex === cellPos) { baseCell.innerHTML = ''; }
|
||||
} else if (colspanNumber === 2) {
|
||||
baseCell.removeAttribute('colSpan');
|
||||
if (baseCell.cellIndex === cellPos) { baseCell.innerHTML = ''; }
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
case TableResultAction.resultAction.RemoveCell:
|
||||
dom.remove(actions[actionIndex].baseCell, true);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create empty table element
|
||||
*
|
||||
* @param {Number} rowCount
|
||||
* @param {Number} colCount
|
||||
* @return {Node}
|
||||
*/
|
||||
createTable(colCount, rowCount, options) {
|
||||
const tds = [];
|
||||
let tdHTML;
|
||||
for (let idxCol = 0; idxCol < colCount; idxCol++) {
|
||||
tds.push('<td>' + dom.blank + '</td>');
|
||||
}
|
||||
tdHTML = tds.join('');
|
||||
|
||||
const trs = [];
|
||||
let trHTML;
|
||||
for (let idxRow = 0; idxRow < rowCount; idxRow++) {
|
||||
trs.push('<tr>' + tdHTML + '</tr>');
|
||||
}
|
||||
trHTML = trs.join('');
|
||||
const $table = $('<table>' + trHTML + '</table>');
|
||||
if (options && options.tableClassName) {
|
||||
$table.addClass(options.tableClassName);
|
||||
}
|
||||
|
||||
return $table[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete current table
|
||||
*
|
||||
* @param {WrappedRange} rng
|
||||
* @return {Node}
|
||||
*/
|
||||
deleteTable(rng) {
|
||||
const cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
|
||||
$(cell).closest('table').remove();
|
||||
}
|
||||
}
|
||||
117
public/vendor/editor/src/js/base/editing/Typing.js
vendored
Executable file
@@ -0,0 +1,117 @@
|
||||
import $ from 'jquery';
|
||||
import dom from '../core/dom';
|
||||
import range from '../core/range';
|
||||
import Bullet from '../editing/Bullet';
|
||||
|
||||
/**
|
||||
* @class editing.Typing
|
||||
*
|
||||
* Typing
|
||||
*
|
||||
*/
|
||||
export default class Typing {
|
||||
constructor(context) {
|
||||
// a Bullet instance to toggle lists off
|
||||
this.bullet = new Bullet();
|
||||
this.options = context.options;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert tab
|
||||
*
|
||||
* @param {WrappedRange} rng
|
||||
* @param {Number} tabsize
|
||||
*/
|
||||
insertTab(rng, tabsize) {
|
||||
const tab = dom.createText(new Array(tabsize + 1).join(dom.NBSP_CHAR));
|
||||
rng = rng.deleteContents();
|
||||
rng.insertNode(tab, true);
|
||||
|
||||
rng = range.create(tab, tabsize);
|
||||
rng.select();
|
||||
}
|
||||
|
||||
/**
|
||||
* insert paragraph
|
||||
*
|
||||
* @param {jQuery} $editable
|
||||
* @param {WrappedRange} rng Can be used in unit tests to "mock" the range
|
||||
*
|
||||
* blockquoteBreakingLevel
|
||||
* 0 - No break, the new paragraph remains inside the quote
|
||||
* 1 - Break the first blockquote in the ancestors list
|
||||
* 2 - Break all blockquotes, so that the new paragraph is not quoted (this is the default)
|
||||
*/
|
||||
insertParagraph(editable, rng) {
|
||||
rng = rng || range.create(editable);
|
||||
|
||||
// deleteContents on range.
|
||||
rng = rng.deleteContents();
|
||||
|
||||
// Wrap range if it needs to be wrapped by paragraph
|
||||
rng = rng.wrapBodyInlineWithPara();
|
||||
|
||||
// finding paragraph
|
||||
const splitRoot = dom.ancestor(rng.sc, dom.isPara);
|
||||
|
||||
let nextPara;
|
||||
// on paragraph: split paragraph
|
||||
if (splitRoot) {
|
||||
// if it is an empty line with li
|
||||
if (dom.isLi(splitRoot) && (dom.isEmpty(splitRoot) || dom.deepestChildIsEmpty(splitRoot))) {
|
||||
// toggle UL/OL and escape
|
||||
this.bullet.toggleList(splitRoot.parentNode.nodeName);
|
||||
return;
|
||||
} else {
|
||||
let blockquote = null;
|
||||
if (this.options.blockquoteBreakingLevel === 1) {
|
||||
blockquote = dom.ancestor(splitRoot, dom.isBlockquote);
|
||||
} else if (this.options.blockquoteBreakingLevel === 2) {
|
||||
blockquote = dom.lastAncestor(splitRoot, dom.isBlockquote);
|
||||
}
|
||||
|
||||
if (blockquote) {
|
||||
// We're inside a blockquote and options ask us to break it
|
||||
nextPara = $(dom.emptyPara)[0];
|
||||
// If the split is right before a <br>, remove it so that there's no "empty line"
|
||||
// after the split in the new blockquote created
|
||||
if (dom.isRightEdgePoint(rng.getStartPoint()) && dom.isBR(rng.sc.nextSibling)) {
|
||||
$(rng.sc.nextSibling).remove();
|
||||
}
|
||||
const split = dom.splitTree(blockquote, rng.getStartPoint(), { isDiscardEmptySplits: true });
|
||||
if (split) {
|
||||
split.parentNode.insertBefore(nextPara, split);
|
||||
} else {
|
||||
dom.insertAfter(nextPara, blockquote); // There's no split if we were at the end of the blockquote
|
||||
}
|
||||
} else {
|
||||
nextPara = dom.splitTree(splitRoot, rng.getStartPoint());
|
||||
|
||||
// not a blockquote, just insert the paragraph
|
||||
let emptyAnchors = dom.listDescendant(splitRoot, dom.isEmptyAnchor);
|
||||
emptyAnchors = emptyAnchors.concat(dom.listDescendant(nextPara, dom.isEmptyAnchor));
|
||||
|
||||
$.each(emptyAnchors, (idx, anchor) => {
|
||||
dom.remove(anchor);
|
||||
});
|
||||
|
||||
// replace empty heading, pre or custom-made styleTag with P tag
|
||||
if ((dom.isHeading(nextPara) || dom.isPre(nextPara) || dom.isCustomStyleTag(nextPara)) && dom.isEmpty(nextPara)) {
|
||||
nextPara = dom.replace(nextPara, 'p');
|
||||
}
|
||||
}
|
||||
}
|
||||
// no paragraph: insert empty paragraph
|
||||
} else {
|
||||
const next = rng.sc.childNodes[rng.so];
|
||||
nextPara = $(dom.emptyPara)[0];
|
||||
if (next) {
|
||||
rng.sc.insertBefore(nextPara, next);
|
||||
} else {
|
||||
rng.sc.appendChild(nextPara);
|
||||
}
|
||||
}
|
||||
|
||||
range.create(nextPara, 0).normalize().select().scrollIntoView(editable);
|
||||
}
|
||||
}
|
||||
107
public/vendor/editor/src/js/base/module/AirPopover.js
vendored
Executable file
@@ -0,0 +1,107 @@
|
||||
import $ from 'jquery';
|
||||
import lists from '../core/lists';
|
||||
|
||||
const AIRMODE_POPOVER_X_OFFSET = -5;
|
||||
const AIRMODE_POPOVER_Y_OFFSET = 5;
|
||||
|
||||
export default class AirPopover {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
this.ui = $.summernote.ui;
|
||||
this.options = context.options;
|
||||
|
||||
this.hidable = true;
|
||||
this.onContextmenu = false;
|
||||
this.pageX = null;
|
||||
this.pageY = null;
|
||||
|
||||
this.events = {
|
||||
'summernote.contextmenu': (e) => {
|
||||
if (this.options.editing) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.onContextmenu = true;
|
||||
this.update(true);
|
||||
}
|
||||
},
|
||||
'summernote.mousedown': (we, e) => {
|
||||
this.pageX = e.pageX;
|
||||
this.pageY = e.pageY;
|
||||
},
|
||||
'summernote.keyup summernote.mouseup summernote.scroll': (we, e) => {
|
||||
if (this.options.editing && !this.onContextmenu) {
|
||||
this.pageX = e.pageX;
|
||||
this.pageY = e.pageY;
|
||||
this.update();
|
||||
}
|
||||
this.onContextmenu = false;
|
||||
},
|
||||
'summernote.disable summernote.change summernote.dialog.shown summernote.blur': () => {
|
||||
this.hide();
|
||||
},
|
||||
'summernote.focusout': () => {
|
||||
if (!this.$popover.is(':active,:focus')) {
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
shouldInitialize() {
|
||||
return this.options.airMode && !lists.isEmpty(this.options.popover.air);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.$popover = this.ui.popover({
|
||||
className: 'note-air-popover',
|
||||
}).render().appendTo(this.options.container);
|
||||
const $content = this.$popover.find('.popover-content');
|
||||
|
||||
this.context.invoke('buttons.build', $content, this.options.popover.air);
|
||||
|
||||
// disable hiding this popover preemptively by 'summernote.blur' event.
|
||||
this.$popover.on('mousedown', () => { this.hidable = false; });
|
||||
// (re-)enable hiding after 'summernote.blur' has been handled (aka. ignored).
|
||||
this.$popover.on('mouseup', () => { this.hidable = true; });
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.$popover.remove();
|
||||
}
|
||||
|
||||
update(forcelyOpen) {
|
||||
const styleInfo = this.context.invoke('editor.currentStyle');
|
||||
if (styleInfo.range && (!styleInfo.range.isCollapsed() || forcelyOpen)) {
|
||||
let rect = {
|
||||
left: this.pageX,
|
||||
top: this.pageY,
|
||||
};
|
||||
|
||||
const containerOffset = $(this.options.container).offset();
|
||||
rect.top -= containerOffset.top;
|
||||
rect.left -= containerOffset.left;
|
||||
|
||||
this.$popover.css({
|
||||
display: 'block',
|
||||
left: Math.max(rect.left, 0) + AIRMODE_POPOVER_X_OFFSET,
|
||||
top: rect.top + AIRMODE_POPOVER_Y_OFFSET,
|
||||
});
|
||||
this.context.invoke('buttons.updateCurrentStyle', this.$popover);
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
updateCodeview(isCodeview) {
|
||||
this.ui.toggleBtnActive(this.$popover.find('.btn-codeview'), isCodeview);
|
||||
if (isCodeview) {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (this.hidable) {
|
||||
this.$popover.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
68
public/vendor/editor/src/js/base/module/AutoLink.js
vendored
Executable file
@@ -0,0 +1,68 @@
|
||||
import $ from 'jquery';
|
||||
import lists from '../core/lists';
|
||||
import key from '../core/key';
|
||||
|
||||
const defaultScheme = 'http://';
|
||||
const linkPattern = /^([A-Za-z][A-Za-z0-9+-.]*\:[\/]{2}|tel:|mailto:[A-Z0-9._%+-]+@)?(www\.)?(.+)$/i;
|
||||
|
||||
export default class AutoLink {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
this.options = context.options;
|
||||
this.events = {
|
||||
'summernote.keyup': (we, e) => {
|
||||
if (!e.isDefaultPrevented()) {
|
||||
this.handleKeyup(e);
|
||||
}
|
||||
},
|
||||
'summernote.keydown': (we, e) => {
|
||||
this.handleKeydown(e);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.lastWordRange = null;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.lastWordRange = null;
|
||||
}
|
||||
|
||||
replace() {
|
||||
if (!this.lastWordRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
const keyword = this.lastWordRange.toString();
|
||||
const match = keyword.match(linkPattern);
|
||||
|
||||
if (match && (match[1] || match[2])) {
|
||||
const link = match[1] ? keyword : defaultScheme + keyword;
|
||||
const urlText = this.options.showDomainOnlyForAutolink ?
|
||||
keyword.replace(/^(?:https?:\/\/)?(?:tel?:?)?(?:mailto?:?)?(?:www\.)?/i, '').split('/')[0]
|
||||
: keyword;
|
||||
const node = $('<a />').html(urlText).attr('href', link)[0];
|
||||
if (this.context.options.linkTargetBlank) {
|
||||
$(node).attr('target', '_blank');
|
||||
}
|
||||
|
||||
this.lastWordRange.insertNode(node);
|
||||
this.lastWordRange = null;
|
||||
this.context.invoke('editor.focus');
|
||||
}
|
||||
}
|
||||
|
||||
handleKeydown(e) {
|
||||
if (lists.contains([key.code.ENTER, key.code.SPACE], e.keyCode)) {
|
||||
const wordRange = this.context.invoke('editor.createRange').getWordRange();
|
||||
this.lastWordRange = wordRange;
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyup(e) {
|
||||
if (lists.contains([key.code.ENTER, key.code.SPACE], e.keyCode)) {
|
||||
this.replace();
|
||||
}
|
||||
}
|
||||
}
|
||||
84
public/vendor/editor/src/js/base/module/AutoReplace.js
vendored
Executable file
@@ -0,0 +1,84 @@
|
||||
import lists from '../core/lists';
|
||||
import dom from '../core/dom';
|
||||
import key from '../core/key';
|
||||
|
||||
export default class AutoReplace {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
this.options = context.options.replace || {};
|
||||
|
||||
this.keys = [key.code.ENTER, key.code.SPACE, key.code.PERIOD, key.code.COMMA, key.code.SEMICOLON, key.code.SLASH];
|
||||
this.previousKeydownCode = null;
|
||||
|
||||
this.events = {
|
||||
'summernote.keyup': (we, e) => {
|
||||
if (!e.isDefaultPrevented()) {
|
||||
this.handleKeyup(e);
|
||||
}
|
||||
},
|
||||
'summernote.keydown': (we, e) => {
|
||||
this.handleKeydown(e);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
shouldInitialize() {
|
||||
return !!this.options.match;
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.lastWord = null;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.lastWord = null;
|
||||
}
|
||||
|
||||
replace() {
|
||||
if (!this.lastWord) {
|
||||
return;
|
||||
}
|
||||
|
||||
const self = this;
|
||||
const keyword = this.lastWord.toString();
|
||||
this.options.match(keyword, function(match) {
|
||||
if (match) {
|
||||
let node = '';
|
||||
|
||||
if (typeof match === 'string') {
|
||||
node = dom.createText(match);
|
||||
} else if (match instanceof jQuery) {
|
||||
node = match[0];
|
||||
} else if (match instanceof Node) {
|
||||
node = match;
|
||||
}
|
||||
|
||||
if (!node) return;
|
||||
self.lastWord.insertNode(node);
|
||||
self.lastWord = null;
|
||||
self.context.invoke('editor.focus');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleKeydown(e) {
|
||||
// this forces it to remember the last whole word, even if multiple termination keys are pressed
|
||||
// before the previous key is let go.
|
||||
if (this.previousKeydownCode && lists.contains(this.keys, this.previousKeydownCode)) {
|
||||
this.previousKeydownCode = e.keyCode;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lists.contains(this.keys, e.keyCode)) {
|
||||
const wordRange = this.context.invoke('editor.createRange').getWordRange();
|
||||
this.lastWord = wordRange;
|
||||
}
|
||||
this.previousKeydownCode = e.keyCode;
|
||||
}
|
||||
|
||||
handleKeyup(e) {
|
||||
if (lists.contains(this.keys, e.keyCode)) {
|
||||
this.replace();
|
||||
}
|
||||
}
|
||||
}
|
||||
19
public/vendor/editor/src/js/base/module/AutoSync.js
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
import dom from '../core/dom';
|
||||
|
||||
/**
|
||||
* textarea auto sync.
|
||||
*/
|
||||
export default class AutoSync {
|
||||
constructor(context) {
|
||||
this.$note = context.layoutInfo.note;
|
||||
this.events = {
|
||||
'summernote.change': () => {
|
||||
this.$note.val(context.invoke('code'));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
shouldInitialize() {
|
||||
return dom.isTextarea(this.$note[0]);
|
||||
}
|
||||
}
|
||||
942
public/vendor/editor/src/js/base/module/Buttons.js
vendored
Executable file
@@ -0,0 +1,942 @@
|
||||
import $ from 'jquery';
|
||||
import func from '../core/func';
|
||||
import lists from '../core/lists';
|
||||
import env from '../core/env';
|
||||
|
||||
export default class Buttons {
|
||||
constructor(context) {
|
||||
this.ui = $.summernote.ui;
|
||||
this.context = context;
|
||||
this.$toolbar = context.layoutInfo.toolbar;
|
||||
this.options = context.options;
|
||||
this.lang = this.options.langInfo;
|
||||
this.invertedKeyMap = func.invertObject(
|
||||
this.options.keyMap[env.isMac ? 'mac' : 'pc']
|
||||
);
|
||||
}
|
||||
|
||||
representShortcut(editorMethod) {
|
||||
let shortcut = this.invertedKeyMap[editorMethod];
|
||||
if (!this.options.shortcuts || !shortcut) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (env.isMac) {
|
||||
shortcut = shortcut.replace('CMD', '⌘').replace('SHIFT', '⇧');
|
||||
}
|
||||
|
||||
shortcut = shortcut.replace('BACKSLASH', '\\')
|
||||
.replace('SLASH', '/')
|
||||
.replace('LEFTBRACKET', '[')
|
||||
.replace('RIGHTBRACKET', ']');
|
||||
|
||||
return ' (' + shortcut + ')';
|
||||
}
|
||||
|
||||
button(o) {
|
||||
if (!this.options.tooltip && o.tooltip) {
|
||||
delete o.tooltip;
|
||||
}
|
||||
o.container = this.options.container;
|
||||
return this.ui.button(o);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.addToolbarButtons();
|
||||
this.addImagePopoverButtons();
|
||||
this.addLinkPopoverButtons();
|
||||
this.addTablePopoverButtons();
|
||||
this.fontInstalledMap = {};
|
||||
}
|
||||
|
||||
destroy() {
|
||||
delete this.fontInstalledMap;
|
||||
}
|
||||
|
||||
isFontInstalled(name) {
|
||||
if (!Object.prototype.hasOwnProperty.call(this.fontInstalledMap, name)) {
|
||||
this.fontInstalledMap[name] = env.isFontInstalled(name) ||
|
||||
lists.contains(this.options.fontNamesIgnoreCheck, name);
|
||||
}
|
||||
return this.fontInstalledMap[name];
|
||||
}
|
||||
|
||||
isFontDeservedToAdd(name) {
|
||||
name = name.toLowerCase();
|
||||
return (name !== '' && this.isFontInstalled(name) && env.genericFontFamilies.indexOf(name) === -1);
|
||||
}
|
||||
|
||||
colorPalette(className, tooltip, backColor, foreColor) {
|
||||
return this.ui.buttonGroup({
|
||||
className: 'note-color ' + className,
|
||||
children: [
|
||||
this.button({
|
||||
className: 'note-current-color-button',
|
||||
contents: this.ui.icon(this.options.icons.font + ' note-recent-color'),
|
||||
tooltip: tooltip,
|
||||
click: (e) => {
|
||||
const $button = $(e.currentTarget);
|
||||
if (backColor && foreColor) {
|
||||
this.context.invoke('editor.color', {
|
||||
backColor: $button.attr('data-backColor'),
|
||||
foreColor: $button.attr('data-foreColor'),
|
||||
});
|
||||
} else if (backColor) {
|
||||
this.context.invoke('editor.color', {
|
||||
backColor: $button.attr('data-backColor'),
|
||||
});
|
||||
} else if (foreColor) {
|
||||
this.context.invoke('editor.color', {
|
||||
foreColor: $button.attr('data-foreColor'),
|
||||
});
|
||||
}
|
||||
},
|
||||
callback: ($button) => {
|
||||
const $recentColor = $button.find('.note-recent-color');
|
||||
if (backColor) {
|
||||
$recentColor.css('background-color', this.options.colorButton.backColor);
|
||||
$button.attr('data-backColor', this.options.colorButton.backColor);
|
||||
}
|
||||
if (foreColor) {
|
||||
$recentColor.css('color', this.options.colorButton.foreColor);
|
||||
$button.attr('data-foreColor', this.options.colorButton.foreColor);
|
||||
} else {
|
||||
$recentColor.css('color', 'transparent');
|
||||
}
|
||||
},
|
||||
}),
|
||||
this.button({
|
||||
className: 'dropdown-toggle',
|
||||
contents: this.ui.dropdownButtonContents('', this.options),
|
||||
tooltip: this.lang.color.more,
|
||||
data: {
|
||||
toggle: 'dropdown',
|
||||
},
|
||||
}),
|
||||
this.ui.dropdown({
|
||||
items: (backColor ? [
|
||||
'<div class="note-palette">',
|
||||
'<div class="note-palette-title">' + this.lang.color.background + '</div>',
|
||||
'<div>',
|
||||
'<button type="button" class="note-color-reset btn btn-light" data-event="backColor" data-value="transparent">',
|
||||
this.lang.color.transparent,
|
||||
'</button>',
|
||||
'</div>',
|
||||
'<div class="note-holder" data-event="backColor"><!-- back colors --></div>',
|
||||
'<div>',
|
||||
'<button type="button" class="note-color-select btn btn-light" data-event="openPalette" data-value="backColorPicker">',
|
||||
this.lang.color.cpSelect,
|
||||
'</button>',
|
||||
'<input type="color" id="backColorPicker" class="note-btn note-color-select-btn" value="' + this.options.colorButton.backColor + '" data-event="backColorPalette">',
|
||||
'</div>',
|
||||
'<div class="note-holder-custom" id="backColorPalette" data-event="backColor"/>',
|
||||
'</div>',
|
||||
].join('') : '') +
|
||||
(foreColor ? [
|
||||
'<div class="note-palette">',
|
||||
'<div class="note-palette-title">' + this.lang.color.foreground + '</div>',
|
||||
'<div>',
|
||||
'<button type="button" class="note-color-reset btn btn-light" data-event="removeFormat" data-value="foreColor">',
|
||||
this.lang.color.resetToDefault,
|
||||
'</button>',
|
||||
'</div>',
|
||||
'<div class="note-holder" data-event="foreColor"><!-- fore colors --></div>',
|
||||
'<div>',
|
||||
'<button type="button" class="note-color-select btn btn-light" data-event="openPalette" data-value="foreColorPicker">',
|
||||
this.lang.color.cpSelect,
|
||||
'</button>',
|
||||
'<input type="color" id="foreColorPicker" class="note-btn note-color-select-btn" value="' + this.options.colorButton.foreColor + '" data-event="foreColorPalette">',
|
||||
'</div>', // Fix missing Div, Commented to find easily if it's wrong
|
||||
'<div class="note-holder-custom" id="foreColorPalette" data-event="foreColor"/>',
|
||||
'</div>',
|
||||
].join('') : ''),
|
||||
callback: ($dropdown) => {
|
||||
$dropdown.find('.note-holder').each((idx, item) => {
|
||||
const $holder = $(item);
|
||||
$holder.append(this.ui.palette({
|
||||
colors: this.options.colors,
|
||||
colorsName: this.options.colorsName,
|
||||
eventName: $holder.data('event'),
|
||||
container: this.options.container,
|
||||
tooltip: this.options.tooltip,
|
||||
}).render());
|
||||
});
|
||||
/* TODO: do we have to record recent custom colors within cookies? */
|
||||
var customColors = [
|
||||
['#FFFFFF', '#FFFFFF', '#FFFFFF', '#FFFFFF', '#FFFFFF', '#FFFFFF', '#FFFFFF', '#FFFFFF'],
|
||||
];
|
||||
$dropdown.find('.note-holder-custom').each((idx, item) => {
|
||||
const $holder = $(item);
|
||||
$holder.append(this.ui.palette({
|
||||
colors: customColors,
|
||||
colorsName: customColors,
|
||||
eventName: $holder.data('event'),
|
||||
container: this.options.container,
|
||||
tooltip: this.options.tooltip,
|
||||
}).render());
|
||||
});
|
||||
$dropdown.find('input[type=color]').each((idx, item) => {
|
||||
$(item).change(function() {
|
||||
const $chip = $dropdown.find('#' + $(this).data('event')).find('.note-color-btn').first();
|
||||
const color = this.value.toUpperCase();
|
||||
$chip.css('background-color', color)
|
||||
.attr('aria-label', color)
|
||||
.attr('data-value', color)
|
||||
.attr('data-original-title', color);
|
||||
$chip.click();
|
||||
});
|
||||
});
|
||||
},
|
||||
click: (event) => {
|
||||
event.stopPropagation();
|
||||
|
||||
const $parent = $('.' + className).find('.note-dropdown-menu');
|
||||
const $button = $(event.target);
|
||||
const eventName = $button.data('event');
|
||||
const value = $button.attr('data-value');
|
||||
|
||||
if (eventName === 'openPalette') {
|
||||
const $picker = $parent.find('#' + value);
|
||||
const $palette = $($parent.find('#' + $picker.data('event')).find('.note-color-row')[0]);
|
||||
|
||||
// Shift palette chips
|
||||
const $chip = $palette.find('.note-color-btn').last().detach();
|
||||
|
||||
// Set chip attributes
|
||||
const color = $picker.val();
|
||||
$chip.css('background-color', color)
|
||||
.attr('aria-label', color)
|
||||
.attr('data-value', color)
|
||||
.attr('data-original-title', color);
|
||||
$palette.prepend($chip);
|
||||
$picker.click();
|
||||
} else {
|
||||
if (lists.contains(['backColor', 'foreColor'], eventName)) {
|
||||
const key = eventName === 'backColor' ? 'background-color' : 'color';
|
||||
const $color = $button.closest('.note-color').find('.note-recent-color');
|
||||
const $currentButton = $button.closest('.note-color').find('.note-current-color-button');
|
||||
|
||||
$color.css(key, value);
|
||||
$currentButton.attr('data-' + eventName, value);
|
||||
}
|
||||
this.context.invoke('editor.' + eventName, value);
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
}).render();
|
||||
}
|
||||
|
||||
addToolbarButtons() {
|
||||
this.context.memo('button.style', () => {
|
||||
return this.ui.buttonGroup([
|
||||
this.button({
|
||||
className: 'dropdown-toggle',
|
||||
contents: this.ui.dropdownButtonContents(
|
||||
this.ui.icon(this.options.icons.magic), this.options
|
||||
),
|
||||
tooltip: this.lang.style.style,
|
||||
data: {
|
||||
toggle: 'dropdown',
|
||||
},
|
||||
}),
|
||||
this.ui.dropdown({
|
||||
className: 'dropdown-style',
|
||||
items: this.options.styleTags,
|
||||
title: this.lang.style.style,
|
||||
template: (item) => {
|
||||
// TBD: need to be simplified
|
||||
if (typeof item === 'string') {
|
||||
item = {
|
||||
tag: item,
|
||||
title: (Object.prototype.hasOwnProperty.call(this.lang.style, item) ? this.lang.style[item] : item),
|
||||
};
|
||||
}
|
||||
|
||||
const tag = item.tag;
|
||||
const title = item.title;
|
||||
const style = item.style ? ' style="' + item.style + '" ' : '';
|
||||
const className = item.className ? ' class="' + item.className + '"' : '';
|
||||
|
||||
return '<' + tag + style + className + '>' + title + '</' + tag + '>';
|
||||
},
|
||||
click: this.context.createInvokeHandler('editor.formatBlock'),
|
||||
}),
|
||||
]).render();
|
||||
});
|
||||
|
||||
for (let styleIdx = 0, styleLen = this.options.styleTags.length; styleIdx < styleLen; styleIdx++) {
|
||||
const item = this.options.styleTags[styleIdx];
|
||||
|
||||
this.context.memo('button.style.' + item, () => {
|
||||
return this.button({
|
||||
className: 'note-btn-style-' + item,
|
||||
contents: '<div data-value="' + item + '">' + item.toUpperCase() + '</div>',
|
||||
tooltip: this.lang.style[item],
|
||||
click: this.context.createInvokeHandler('editor.formatBlock'),
|
||||
}).render();
|
||||
});
|
||||
}
|
||||
|
||||
this.context.memo('button.bold', () => {
|
||||
return this.button({
|
||||
className: 'note-btn-bold',
|
||||
contents: this.ui.icon(this.options.icons.bold),
|
||||
tooltip: this.lang.font.bold + this.representShortcut('bold'),
|
||||
click: this.context.createInvokeHandlerAndUpdateState('editor.bold'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.italic', () => {
|
||||
return this.button({
|
||||
className: 'note-btn-italic',
|
||||
contents: this.ui.icon(this.options.icons.italic),
|
||||
tooltip: this.lang.font.italic + this.representShortcut('italic'),
|
||||
click: this.context.createInvokeHandlerAndUpdateState('editor.italic'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.underline', () => {
|
||||
return this.button({
|
||||
className: 'note-btn-underline',
|
||||
contents: this.ui.icon(this.options.icons.underline),
|
||||
tooltip: this.lang.font.underline + this.representShortcut('underline'),
|
||||
click: this.context.createInvokeHandlerAndUpdateState('editor.underline'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.clear', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.eraser),
|
||||
tooltip: this.lang.font.clear + this.representShortcut('removeFormat'),
|
||||
click: this.context.createInvokeHandler('editor.removeFormat'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.strikethrough', () => {
|
||||
return this.button({
|
||||
className: 'note-btn-strikethrough',
|
||||
contents: this.ui.icon(this.options.icons.strikethrough),
|
||||
tooltip: this.lang.font.strikethrough + this.representShortcut('strikethrough'),
|
||||
click: this.context.createInvokeHandlerAndUpdateState('editor.strikethrough'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.superscript', () => {
|
||||
return this.button({
|
||||
className: 'note-btn-superscript',
|
||||
contents: this.ui.icon(this.options.icons.superscript),
|
||||
tooltip: this.lang.font.superscript,
|
||||
click: this.context.createInvokeHandlerAndUpdateState('editor.superscript'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.subscript', () => {
|
||||
return this.button({
|
||||
className: 'note-btn-subscript',
|
||||
contents: this.ui.icon(this.options.icons.subscript),
|
||||
tooltip: this.lang.font.subscript,
|
||||
click: this.context.createInvokeHandlerAndUpdateState('editor.subscript'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.fontname', () => {
|
||||
const styleInfo = this.context.invoke('editor.currentStyle');
|
||||
|
||||
if (this.options.addDefaultFonts) {
|
||||
// Add 'default' fonts into the fontnames array if not exist
|
||||
$.each(styleInfo['font-family'].split(','), (idx, fontname) => {
|
||||
fontname = fontname.trim().replace(/['"]+/g, '');
|
||||
if (this.isFontDeservedToAdd(fontname)) {
|
||||
if (this.options.fontNames.indexOf(fontname) === -1) {
|
||||
this.options.fontNames.push(fontname);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this.ui.buttonGroup([
|
||||
this.button({
|
||||
className: 'dropdown-toggle',
|
||||
contents: this.ui.dropdownButtonContents(
|
||||
'<span class="note-current-fontname"/>', this.options
|
||||
),
|
||||
tooltip: this.lang.font.name,
|
||||
data: {
|
||||
toggle: 'dropdown',
|
||||
},
|
||||
}),
|
||||
this.ui.dropdownCheck({
|
||||
className: 'dropdown-fontname',
|
||||
checkClassName: this.options.icons.menuCheck,
|
||||
items: this.options.fontNames.filter(this.isFontInstalled.bind(this)),
|
||||
title: this.lang.font.name,
|
||||
template: (item) => {
|
||||
return '<span style="font-family: ' + env.validFontName(item) + '">' + item + '</span>';
|
||||
},
|
||||
click: this.context.createInvokeHandlerAndUpdateState('editor.fontName'),
|
||||
}),
|
||||
]).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.fontsize', () => {
|
||||
return this.ui.buttonGroup([
|
||||
this.button({
|
||||
className: 'dropdown-toggle',
|
||||
contents: this.ui.dropdownButtonContents('<span class="note-current-fontsize"/>', this.options),
|
||||
tooltip: this.lang.font.size,
|
||||
data: {
|
||||
toggle: 'dropdown',
|
||||
},
|
||||
}),
|
||||
this.ui.dropdownCheck({
|
||||
className: 'dropdown-fontsize',
|
||||
checkClassName: this.options.icons.menuCheck,
|
||||
items: this.options.fontSizes,
|
||||
title: this.lang.font.size,
|
||||
click: this.context.createInvokeHandlerAndUpdateState('editor.fontSize'),
|
||||
}),
|
||||
]).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.fontsizeunit', () => {
|
||||
return this.ui.buttonGroup([
|
||||
this.button({
|
||||
className: 'dropdown-toggle',
|
||||
contents: this.ui.dropdownButtonContents('<span class="note-current-fontsizeunit"/>', this.options),
|
||||
tooltip: this.lang.font.sizeunit,
|
||||
data: {
|
||||
toggle: 'dropdown',
|
||||
},
|
||||
}),
|
||||
this.ui.dropdownCheck({
|
||||
className: 'dropdown-fontsizeunit',
|
||||
checkClassName: this.options.icons.menuCheck,
|
||||
items: this.options.fontSizeUnits,
|
||||
title: this.lang.font.sizeunit,
|
||||
click: this.context.createInvokeHandlerAndUpdateState('editor.fontSizeUnit'),
|
||||
}),
|
||||
]).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.color', () => {
|
||||
return this.colorPalette('note-color-all', this.lang.color.recent, true, true);
|
||||
});
|
||||
|
||||
this.context.memo('button.forecolor', () => {
|
||||
return this.colorPalette('note-color-fore', this.lang.color.foreground, false, true);
|
||||
});
|
||||
|
||||
this.context.memo('button.backcolor', () => {
|
||||
return this.colorPalette('note-color-back', this.lang.color.background, true, false);
|
||||
});
|
||||
|
||||
this.context.memo('button.ul', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.unorderedlist),
|
||||
tooltip: this.lang.lists.unordered + this.representShortcut('insertUnorderedList'),
|
||||
click: this.context.createInvokeHandler('editor.insertUnorderedList'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.ol', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.orderedlist),
|
||||
tooltip: this.lang.lists.ordered + this.representShortcut('insertOrderedList'),
|
||||
click: this.context.createInvokeHandler('editor.insertOrderedList'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
const justifyLeft = this.button({
|
||||
contents: this.ui.icon(this.options.icons.alignLeft),
|
||||
tooltip: this.lang.paragraph.left + this.representShortcut('justifyLeft'),
|
||||
click: this.context.createInvokeHandler('editor.justifyLeft'),
|
||||
});
|
||||
|
||||
const justifyCenter = this.button({
|
||||
contents: this.ui.icon(this.options.icons.alignCenter),
|
||||
tooltip: this.lang.paragraph.center + this.representShortcut('justifyCenter'),
|
||||
click: this.context.createInvokeHandler('editor.justifyCenter'),
|
||||
});
|
||||
|
||||
const justifyRight = this.button({
|
||||
contents: this.ui.icon(this.options.icons.alignRight),
|
||||
tooltip: this.lang.paragraph.right + this.representShortcut('justifyRight'),
|
||||
click: this.context.createInvokeHandler('editor.justifyRight'),
|
||||
});
|
||||
|
||||
const justifyFull = this.button({
|
||||
contents: this.ui.icon(this.options.icons.alignJustify),
|
||||
tooltip: this.lang.paragraph.justify + this.representShortcut('justifyFull'),
|
||||
click: this.context.createInvokeHandler('editor.justifyFull'),
|
||||
});
|
||||
|
||||
const outdent = this.button({
|
||||
contents: this.ui.icon(this.options.icons.outdent),
|
||||
tooltip: this.lang.paragraph.outdent + this.representShortcut('outdent'),
|
||||
click: this.context.createInvokeHandler('editor.outdent'),
|
||||
});
|
||||
|
||||
const indent = this.button({
|
||||
contents: this.ui.icon(this.options.icons.indent),
|
||||
tooltip: this.lang.paragraph.indent + this.representShortcut('indent'),
|
||||
click: this.context.createInvokeHandler('editor.indent'),
|
||||
});
|
||||
|
||||
this.context.memo('button.justifyLeft', func.invoke(justifyLeft, 'render'));
|
||||
this.context.memo('button.justifyCenter', func.invoke(justifyCenter, 'render'));
|
||||
this.context.memo('button.justifyRight', func.invoke(justifyRight, 'render'));
|
||||
this.context.memo('button.justifyFull', func.invoke(justifyFull, 'render'));
|
||||
this.context.memo('button.outdent', func.invoke(outdent, 'render'));
|
||||
this.context.memo('button.indent', func.invoke(indent, 'render'));
|
||||
|
||||
this.context.memo('button.paragraph', () => {
|
||||
return this.ui.buttonGroup([
|
||||
this.button({
|
||||
className: 'dropdown-toggle',
|
||||
contents: this.ui.dropdownButtonContents(this.ui.icon(this.options.icons.alignLeft), this.options),
|
||||
tooltip: this.lang.paragraph.paragraph,
|
||||
data: {
|
||||
toggle: 'dropdown',
|
||||
},
|
||||
}),
|
||||
this.ui.dropdown([
|
||||
this.ui.buttonGroup({
|
||||
className: 'note-align',
|
||||
children: [justifyLeft, justifyCenter, justifyRight, justifyFull],
|
||||
}),
|
||||
this.ui.buttonGroup({
|
||||
className: 'note-list',
|
||||
children: [outdent, indent],
|
||||
}),
|
||||
]),
|
||||
]).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.height', () => {
|
||||
return this.ui.buttonGroup([
|
||||
this.button({
|
||||
className: 'dropdown-toggle',
|
||||
contents: this.ui.dropdownButtonContents(this.ui.icon(this.options.icons.textHeight), this.options),
|
||||
tooltip: this.lang.font.height,
|
||||
data: {
|
||||
toggle: 'dropdown',
|
||||
},
|
||||
}),
|
||||
this.ui.dropdownCheck({
|
||||
items: this.options.lineHeights,
|
||||
checkClassName: this.options.icons.menuCheck,
|
||||
className: 'dropdown-line-height',
|
||||
title: this.lang.font.height,
|
||||
click: this.context.createInvokeHandler('editor.lineHeight'),
|
||||
}),
|
||||
]).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.table', () => {
|
||||
return this.ui.buttonGroup([
|
||||
this.button({
|
||||
className: 'dropdown-toggle',
|
||||
contents: this.ui.dropdownButtonContents(this.ui.icon(this.options.icons.table), this.options),
|
||||
tooltip: this.lang.table.table,
|
||||
data: {
|
||||
toggle: 'dropdown',
|
||||
},
|
||||
}),
|
||||
this.ui.dropdown({
|
||||
title: this.lang.table.table,
|
||||
className: 'note-table',
|
||||
items: [
|
||||
'<div class="note-dimension-picker">',
|
||||
'<div class="note-dimension-picker-mousecatcher" data-event="insertTable" data-value="1x1"/>',
|
||||
'<div class="note-dimension-picker-highlighted"/>',
|
||||
'<div class="note-dimension-picker-unhighlighted"/>',
|
||||
'</div>',
|
||||
'<div class="note-dimension-display">1 x 1</div>',
|
||||
].join(''),
|
||||
}),
|
||||
], {
|
||||
callback: ($node) => {
|
||||
const $catcher = $node.find('.note-dimension-picker-mousecatcher');
|
||||
$catcher.css({
|
||||
width: this.options.insertTableMaxSize.col + 'em',
|
||||
height: this.options.insertTableMaxSize.row + 'em',
|
||||
}).mousedown(this.context.createInvokeHandler('editor.insertTable'))
|
||||
.on('mousemove', this.tableMoveHandler.bind(this));
|
||||
},
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.link', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.link),
|
||||
tooltip: this.lang.link.link + this.representShortcut('linkDialog.show'),
|
||||
click: this.context.createInvokeHandler('linkDialog.show'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.picture', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.picture),
|
||||
tooltip: this.lang.image.image,
|
||||
click: this.context.createInvokeHandler('imageDialog.show'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.video', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.video),
|
||||
tooltip: this.lang.video.video,
|
||||
click: this.context.createInvokeHandler('videoDialog.show'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.hr', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.minus),
|
||||
tooltip: this.lang.hr.insert + this.representShortcut('insertHorizontalRule'),
|
||||
click: this.context.createInvokeHandler('editor.insertHorizontalRule'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.fullscreen', () => {
|
||||
return this.button({
|
||||
className: 'btn-fullscreen',
|
||||
contents: this.ui.icon(this.options.icons.arrowsAlt),
|
||||
tooltip: this.lang.options.fullscreen,
|
||||
click: this.context.createInvokeHandler('fullscreen.toggle'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.codeview', () => {
|
||||
return this.button({
|
||||
className: 'btn-codeview',
|
||||
contents: this.ui.icon(this.options.icons.code),
|
||||
tooltip: this.lang.options.codeview,
|
||||
click: this.context.createInvokeHandler('codeview.toggle'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.redo', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.redo),
|
||||
tooltip: this.lang.history.redo + this.representShortcut('redo'),
|
||||
click: this.context.createInvokeHandler('editor.redo'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.undo', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.undo),
|
||||
tooltip: this.lang.history.undo + this.representShortcut('undo'),
|
||||
click: this.context.createInvokeHandler('editor.undo'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.help', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.question),
|
||||
tooltip: this.lang.options.help,
|
||||
click: this.context.createInvokeHandler('helpDialog.show'),
|
||||
}).render();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* image: [
|
||||
* ['imageResize', ['resizeFull', 'resizeHalf', 'resizeQuarter', 'resizeNone']],
|
||||
* ['float', ['floatLeft', 'floatRight', 'floatNone']],
|
||||
* ['remove', ['removeMedia']],
|
||||
* ],
|
||||
*/
|
||||
addImagePopoverButtons() {
|
||||
// Image Size Buttons
|
||||
this.context.memo('button.resizeFull', () => {
|
||||
return this.button({
|
||||
contents: '<span class="note-fontsize-10">100%</span>',
|
||||
tooltip: this.lang.image.resizeFull,
|
||||
click: this.context.createInvokeHandler('editor.resize', '1'),
|
||||
}).render();
|
||||
});
|
||||
this.context.memo('button.resizeHalf', () => {
|
||||
return this.button({
|
||||
contents: '<span class="note-fontsize-10">50%</span>',
|
||||
tooltip: this.lang.image.resizeHalf,
|
||||
click: this.context.createInvokeHandler('editor.resize', '0.5'),
|
||||
}).render();
|
||||
});
|
||||
this.context.memo('button.resizeQuarter', () => {
|
||||
return this.button({
|
||||
contents: '<span class="note-fontsize-10">25%</span>',
|
||||
tooltip: this.lang.image.resizeQuarter,
|
||||
click: this.context.createInvokeHandler('editor.resize', '0.25'),
|
||||
}).render();
|
||||
});
|
||||
this.context.memo('button.resizeNone', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.rollback),
|
||||
tooltip: this.lang.image.resizeNone,
|
||||
click: this.context.createInvokeHandler('editor.resize', '0'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
// Float Buttons
|
||||
this.context.memo('button.floatLeft', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.floatLeft),
|
||||
tooltip: this.lang.image.floatLeft,
|
||||
click: this.context.createInvokeHandler('editor.floatMe', 'left'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.floatRight', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.floatRight),
|
||||
tooltip: this.lang.image.floatRight,
|
||||
click: this.context.createInvokeHandler('editor.floatMe', 'right'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.floatNone', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.rollback),
|
||||
tooltip: this.lang.image.floatNone,
|
||||
click: this.context.createInvokeHandler('editor.floatMe', 'none'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
// Remove Buttons
|
||||
this.context.memo('button.removeMedia', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.trash),
|
||||
tooltip: this.lang.image.remove,
|
||||
click: this.context.createInvokeHandler('editor.removeMedia'),
|
||||
}).render();
|
||||
});
|
||||
}
|
||||
|
||||
addLinkPopoverButtons() {
|
||||
this.context.memo('button.linkDialogShow', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.link),
|
||||
tooltip: this.lang.link.edit,
|
||||
click: this.context.createInvokeHandler('linkDialog.show'),
|
||||
}).render();
|
||||
});
|
||||
|
||||
this.context.memo('button.unlink', () => {
|
||||
return this.button({
|
||||
contents: this.ui.icon(this.options.icons.unlink),
|
||||
tooltip: this.lang.link.unlink,
|
||||
click: this.context.createInvokeHandler('editor.unlink'),
|
||||
}).render();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* table : [
|
||||
* ['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
|
||||
* ['delete', ['deleteRow', 'deleteCol', 'deleteTable']]
|
||||
* ],
|
||||
*/
|
||||
addTablePopoverButtons() {
|
||||
this.context.memo('button.addRowUp', () => {
|
||||
return this.button({
|
||||
className: 'btn-md',
|
||||
contents: this.ui.icon(this.options.icons.rowAbove),
|
||||
tooltip: this.lang.table.addRowAbove,
|
||||
click: this.context.createInvokeHandler('editor.addRow', 'top'),
|
||||
}).render();
|
||||
});
|
||||
this.context.memo('button.addRowDown', () => {
|
||||
return this.button({
|
||||
className: 'btn-md',
|
||||
contents: this.ui.icon(this.options.icons.rowBelow),
|
||||
tooltip: this.lang.table.addRowBelow,
|
||||
click: this.context.createInvokeHandler('editor.addRow', 'bottom'),
|
||||
}).render();
|
||||
});
|
||||
this.context.memo('button.addColLeft', () => {
|
||||
return this.button({
|
||||
className: 'btn-md',
|
||||
contents: this.ui.icon(this.options.icons.colBefore),
|
||||
tooltip: this.lang.table.addColLeft,
|
||||
click: this.context.createInvokeHandler('editor.addCol', 'left'),
|
||||
}).render();
|
||||
});
|
||||
this.context.memo('button.addColRight', () => {
|
||||
return this.button({
|
||||
className: 'btn-md',
|
||||
contents: this.ui.icon(this.options.icons.colAfter),
|
||||
tooltip: this.lang.table.addColRight,
|
||||
click: this.context.createInvokeHandler('editor.addCol', 'right'),
|
||||
}).render();
|
||||
});
|
||||
this.context.memo('button.deleteRow', () => {
|
||||
return this.button({
|
||||
className: 'btn-md',
|
||||
contents: this.ui.icon(this.options.icons.rowRemove),
|
||||
tooltip: this.lang.table.delRow,
|
||||
click: this.context.createInvokeHandler('editor.deleteRow'),
|
||||
}).render();
|
||||
});
|
||||
this.context.memo('button.deleteCol', () => {
|
||||
return this.button({
|
||||
className: 'btn-md',
|
||||
contents: this.ui.icon(this.options.icons.colRemove),
|
||||
tooltip: this.lang.table.delCol,
|
||||
click: this.context.createInvokeHandler('editor.deleteCol'),
|
||||
}).render();
|
||||
});
|
||||
this.context.memo('button.deleteTable', () => {
|
||||
return this.button({
|
||||
className: 'btn-md',
|
||||
contents: this.ui.icon(this.options.icons.trash),
|
||||
tooltip: this.lang.table.delTable,
|
||||
click: this.context.createInvokeHandler('editor.deleteTable'),
|
||||
}).render();
|
||||
});
|
||||
}
|
||||
|
||||
build($container, groups) {
|
||||
for (let groupIdx = 0, groupLen = groups.length; groupIdx < groupLen; groupIdx++) {
|
||||
const group = groups[groupIdx];
|
||||
const groupName = Array.isArray(group) ? group[0] : group;
|
||||
const buttons = Array.isArray(group) ? ((group.length === 1) ? [group[0]] : group[1]) : [group];
|
||||
|
||||
const $group = this.ui.buttonGroup({
|
||||
className: 'note-' + groupName,
|
||||
}).render();
|
||||
|
||||
for (let idx = 0, len = buttons.length; idx < len; idx++) {
|
||||
const btn = this.context.memo('button.' + buttons[idx]);
|
||||
if (btn) {
|
||||
$group.append(typeof btn === 'function' ? btn(this.context) : btn);
|
||||
}
|
||||
}
|
||||
$group.appendTo($container);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {jQuery} [$container]
|
||||
*/
|
||||
updateCurrentStyle($container) {
|
||||
const $cont = $container || this.$toolbar;
|
||||
|
||||
const styleInfo = this.context.invoke('editor.currentStyle');
|
||||
this.updateBtnStates($cont, {
|
||||
'.note-btn-bold': () => {
|
||||
return styleInfo['font-bold'] === 'bold';
|
||||
},
|
||||
'.note-btn-italic': () => {
|
||||
return styleInfo['font-italic'] === 'italic';
|
||||
},
|
||||
'.note-btn-underline': () => {
|
||||
return styleInfo['font-underline'] === 'underline';
|
||||
},
|
||||
'.note-btn-subscript': () => {
|
||||
return styleInfo['font-subscript'] === 'subscript';
|
||||
},
|
||||
'.note-btn-superscript': () => {
|
||||
return styleInfo['font-superscript'] === 'superscript';
|
||||
},
|
||||
'.note-btn-strikethrough': () => {
|
||||
return styleInfo['font-strikethrough'] === 'strikethrough';
|
||||
},
|
||||
});
|
||||
|
||||
if (styleInfo['font-family']) {
|
||||
const fontNames = styleInfo['font-family'].split(',').map((name) => {
|
||||
return name.replace(/[\'\"]/g, '')
|
||||
.replace(/\s+$/, '')
|
||||
.replace(/^\s+/, '');
|
||||
});
|
||||
const fontName = lists.find(fontNames, this.isFontInstalled.bind(this));
|
||||
|
||||
$cont.find('.dropdown-fontname a').each((idx, item) => {
|
||||
const $item = $(item);
|
||||
// always compare string to avoid creating another func.
|
||||
const isChecked = ($item.data('value') + '') === (fontName + '');
|
||||
$item.toggleClass('checked', isChecked);
|
||||
});
|
||||
$cont.find('.note-current-fontname').text(fontName).css('font-family', fontName);
|
||||
}
|
||||
|
||||
if (styleInfo['font-size']) {
|
||||
const fontSize = styleInfo['font-size'];
|
||||
$cont.find('.dropdown-fontsize a').each((idx, item) => {
|
||||
const $item = $(item);
|
||||
// always compare with string to avoid creating another func.
|
||||
const isChecked = ($item.data('value') + '') === (fontSize + '');
|
||||
$item.toggleClass('checked', isChecked);
|
||||
});
|
||||
$cont.find('.note-current-fontsize').text(fontSize);
|
||||
|
||||
const fontSizeUnit = styleInfo['font-size-unit'];
|
||||
$cont.find('.dropdown-fontsizeunit a').each((idx, item) => {
|
||||
const $item = $(item);
|
||||
const isChecked = ($item.data('value') + '') === (fontSizeUnit + '');
|
||||
$item.toggleClass('checked', isChecked);
|
||||
});
|
||||
$cont.find('.note-current-fontsizeunit').text(fontSizeUnit);
|
||||
}
|
||||
|
||||
if (styleInfo['line-height']) {
|
||||
const lineHeight = styleInfo['line-height'];
|
||||
$cont.find('.dropdown-line-height li a').each((idx, item) => {
|
||||
// always compare with string to avoid creating another func.
|
||||
const isChecked = ($(item).data('value') + '') === (lineHeight + '');
|
||||
this.className = isChecked ? 'checked' : '';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateBtnStates($container, infos) {
|
||||
$.each(infos, (selector, pred) => {
|
||||
this.ui.toggleBtnActive($container.find(selector), pred());
|
||||
});
|
||||
}
|
||||
|
||||
tableMoveHandler(event) {
|
||||
const PX_PER_EM = 18;
|
||||
const $picker = $(event.target.parentNode); // target is mousecatcher
|
||||
const $dimensionDisplay = $picker.next();
|
||||
const $catcher = $picker.find('.note-dimension-picker-mousecatcher');
|
||||
const $highlighted = $picker.find('.note-dimension-picker-highlighted');
|
||||
const $unhighlighted = $picker.find('.note-dimension-picker-unhighlighted');
|
||||
|
||||
let posOffset;
|
||||
// HTML5 with jQuery - e.offsetX is undefined in Firefox
|
||||
if (event.offsetX === undefined) {
|
||||
const posCatcher = $(event.target).offset();
|
||||
posOffset = {
|
||||
x: event.pageX - posCatcher.left,
|
||||
y: event.pageY - posCatcher.top,
|
||||
};
|
||||
} else {
|
||||
posOffset = {
|
||||
x: event.offsetX,
|
||||
y: event.offsetY,
|
||||
};
|
||||
}
|
||||
|
||||
const dim = {
|
||||
c: Math.ceil(posOffset.x / PX_PER_EM) || 1,
|
||||
r: Math.ceil(posOffset.y / PX_PER_EM) || 1,
|
||||
};
|
||||
|
||||
$highlighted.css({ width: dim.c + 'em', height: dim.r + 'em' });
|
||||
$catcher.data('value', dim.c + 'x' + dim.r);
|
||||
|
||||
if (dim.c > 3 && dim.c < this.options.insertTableMaxSize.col) {
|
||||
$unhighlighted.css({ width: dim.c + 1 + 'em' });
|
||||
}
|
||||
|
||||
if (dim.r > 3 && dim.r < this.options.insertTableMaxSize.row) {
|
||||
$unhighlighted.css({ height: dim.r + 1 + 'em' });
|
||||
}
|
||||
|
||||
$dimensionDisplay.html(dim.c + ' x ' + dim.r);
|
||||
}
|
||||
}
|
||||
45
public/vendor/editor/src/js/base/module/Clipboard.js
vendored
Executable file
@@ -0,0 +1,45 @@
|
||||
import lists from '../core/lists';
|
||||
|
||||
export default class Clipboard {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
this.$editable = context.layoutInfo.editable;
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.$editable.on('paste', this.pasteByEvent.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* paste by clipboard event
|
||||
*
|
||||
* @param {Event} event
|
||||
*/
|
||||
pasteByEvent(event) {
|
||||
const clipboardData = event.originalEvent.clipboardData;
|
||||
|
||||
if (clipboardData && clipboardData.items && clipboardData.items.length) {
|
||||
const item = clipboardData.items.length > 1 ? clipboardData.items[1] : lists.head(clipboardData.items);
|
||||
if (item.kind === 'file' && item.type.indexOf('image/') !== -1) {
|
||||
// paste img file
|
||||
this.context.invoke('editor.insertImagesOrCallback', [item.getAsFile()]);
|
||||
event.preventDefault();
|
||||
} else if (item.kind === 'string') {
|
||||
// paste text with maxTextLength check
|
||||
if (this.context.invoke('editor.isLimited', clipboardData.getData('Text').length)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
} else if (window.clipboardData) {
|
||||
// for IE
|
||||
let text = window.clipboardData.getData('text');
|
||||
if (this.context.invoke('editor.isLimited', text.length)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
// Call editor.afterCommand after proceeding default event handler
|
||||
setTimeout(() => {
|
||||
this.context.invoke('editor.afterCommand');
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
167
public/vendor/editor/src/js/base/module/Codeview.js
vendored
Executable file
@@ -0,0 +1,167 @@
|
||||
import dom from '../core/dom';
|
||||
import key from '../core/key';
|
||||
|
||||
/**
|
||||
* @class Codeview
|
||||
*/
|
||||
export default class CodeView {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
this.$editor = context.layoutInfo.editor;
|
||||
this.$editable = context.layoutInfo.editable;
|
||||
this.$codable = context.layoutInfo.codable;
|
||||
this.options = context.options;
|
||||
this.CodeMirrorConstructor = window.CodeMirror;
|
||||
|
||||
if (this.options.codemirror.CodeMirrorConstructor) {
|
||||
this.CodeMirrorConstructor = this.options.codemirror.CodeMirrorConstructor;
|
||||
}
|
||||
}
|
||||
|
||||
sync() {
|
||||
const isCodeview = this.isActivated();
|
||||
const CodeMirror = this.CodeMirrorConstructor;
|
||||
if (isCodeview && CodeMirror) {
|
||||
this.$codable.data('cmEditor').save();
|
||||
}
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.$codable.on('keyup', (event) => {
|
||||
if (event.keyCode === key.code.ESCAPE) {
|
||||
this.deactivate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isActivated() {
|
||||
return this.$editor.hasClass('codeview');
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle codeview
|
||||
*/
|
||||
toggle() {
|
||||
if (this.isActivated()) {
|
||||
this.deactivate();
|
||||
} else {
|
||||
this.activate();
|
||||
}
|
||||
this.context.triggerEvent('codeview.toggled');
|
||||
}
|
||||
|
||||
/**
|
||||
* purify input value
|
||||
* @param value
|
||||
* @returns {*}
|
||||
*/
|
||||
purify(value) {
|
||||
if (this.options.codeviewFilter) {
|
||||
// filter code view regex
|
||||
value = value.replace(this.options.codeviewFilterRegex, '');
|
||||
// allow specific iframe tag
|
||||
if (this.options.codeviewIframeFilter) {
|
||||
const whitelist = this.options.codeviewIframeWhitelistSrc.concat(this.options.codeviewIframeWhitelistSrcBase);
|
||||
value = value.replace(/(<iframe.*?>.*?(?:<\/iframe>)?)/gi, function(tag) {
|
||||
// remove if src attribute is duplicated
|
||||
if (/<.+src(?==?('|"|\s)?)[\s\S]+src(?=('|"|\s)?)[^>]*?>/i.test(tag)) {
|
||||
return '';
|
||||
}
|
||||
for (const src of whitelist) {
|
||||
// pass if src is trusted
|
||||
if ((new RegExp('src="(https?:)?\/\/' + src.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + '\/(.+)"')).test(tag)) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
});
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* activate code view
|
||||
*/
|
||||
activate() {
|
||||
const CodeMirror = this.CodeMirrorConstructor;
|
||||
this.$codable.val(dom.html(this.$editable, this.options.prettifyHtml));
|
||||
this.$codable.height(this.$editable.height());
|
||||
|
||||
this.context.invoke('toolbar.updateCodeview', true);
|
||||
this.context.invoke('airPopover.updateCodeview', true);
|
||||
|
||||
this.$editor.addClass('codeview');
|
||||
this.$codable.focus();
|
||||
|
||||
// activate CodeMirror as codable
|
||||
if (CodeMirror) {
|
||||
const cmEditor = CodeMirror.fromTextArea(this.$codable[0], this.options.codemirror);
|
||||
|
||||
// CodeMirror TernServer
|
||||
if (this.options.codemirror.tern) {
|
||||
const server = new CodeMirror.TernServer(this.options.codemirror.tern);
|
||||
cmEditor.ternServer = server;
|
||||
cmEditor.on('cursorActivity', (cm) => {
|
||||
server.updateArgHints(cm);
|
||||
});
|
||||
}
|
||||
|
||||
cmEditor.on('blur', (event) => {
|
||||
this.context.triggerEvent('blur.codeview', cmEditor.getValue(), event);
|
||||
});
|
||||
cmEditor.on('change', () => {
|
||||
this.context.triggerEvent('change.codeview', cmEditor.getValue(), cmEditor);
|
||||
});
|
||||
|
||||
// CodeMirror hasn't Padding.
|
||||
cmEditor.setSize(null, this.$editable.outerHeight());
|
||||
this.$codable.data('cmEditor', cmEditor);
|
||||
} else {
|
||||
this.$codable.on('blur', (event) => {
|
||||
this.context.triggerEvent('blur.codeview', this.$codable.val(), event);
|
||||
});
|
||||
this.$codable.on('input', () => {
|
||||
this.context.triggerEvent('change.codeview', this.$codable.val(), this.$codable);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* deactivate code view
|
||||
*/
|
||||
deactivate() {
|
||||
const CodeMirror = this.CodeMirrorConstructor;
|
||||
// deactivate CodeMirror as codable
|
||||
if (CodeMirror) {
|
||||
const cmEditor = this.$codable.data('cmEditor');
|
||||
this.$codable.val(cmEditor.getValue());
|
||||
cmEditor.toTextArea();
|
||||
}
|
||||
|
||||
const value = this.purify(dom.value(this.$codable, this.options.prettifyHtml) || dom.emptyPara);
|
||||
const isChange = this.$editable.html() !== value;
|
||||
|
||||
this.$editable.html(value);
|
||||
this.$editable.height(this.options.height ? this.$codable.height() : 'auto');
|
||||
this.$editor.removeClass('codeview');
|
||||
|
||||
if (isChange) {
|
||||
this.context.triggerEvent('change', this.$editable.html(), this.$editable);
|
||||
}
|
||||
|
||||
this.$editable.focus();
|
||||
|
||||
this.context.invoke('toolbar.updateCodeview', false);
|
||||
this.context.invoke('airPopover.updateCodeview', false);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.isActivated()) {
|
||||
this.deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
122
public/vendor/editor/src/js/base/module/Dropzone.js
vendored
Executable file
@@ -0,0 +1,122 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
export default class Dropzone {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
this.$eventListener = $(document);
|
||||
this.$editor = context.layoutInfo.editor;
|
||||
this.$editable = context.layoutInfo.editable;
|
||||
this.options = context.options;
|
||||
this.lang = this.options.langInfo;
|
||||
this.documentEventHandlers = {};
|
||||
|
||||
this.$dropzone = $([
|
||||
'<div class="note-dropzone">',
|
||||
'<div class="note-dropzone-message"/>',
|
||||
'</div>',
|
||||
].join('')).prependTo(this.$editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* attach Drag and Drop Events
|
||||
*/
|
||||
initialize() {
|
||||
if (this.options.disableDragAndDrop) {
|
||||
// prevent default drop event
|
||||
this.documentEventHandlers.onDrop = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
// do not consider outside of dropzone
|
||||
this.$eventListener = this.$dropzone;
|
||||
this.$eventListener.on('drop', this.documentEventHandlers.onDrop);
|
||||
} else {
|
||||
this.attachDragAndDropEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* attach Drag and Drop Events
|
||||
*/
|
||||
attachDragAndDropEvent() {
|
||||
let collection = $();
|
||||
const $dropzoneMessage = this.$dropzone.find('.note-dropzone-message');
|
||||
|
||||
this.documentEventHandlers.onDragenter = (e) => {
|
||||
const isCodeview = this.context.invoke('codeview.isActivated');
|
||||
const hasEditorSize = this.$editor.width() > 0 && this.$editor.height() > 0;
|
||||
if (!isCodeview && !collection.length && hasEditorSize) {
|
||||
this.$editor.addClass('dragover');
|
||||
this.$dropzone.width(this.$editor.width());
|
||||
this.$dropzone.height(this.$editor.height());
|
||||
$dropzoneMessage.text(this.lang.image.dragImageHere);
|
||||
}
|
||||
collection = collection.add(e.target);
|
||||
};
|
||||
|
||||
this.documentEventHandlers.onDragleave = (e) => {
|
||||
collection = collection.not(e.target);
|
||||
|
||||
// If nodeName is BODY, then just make it over (fix for IE)
|
||||
if (!collection.length || e.target.nodeName === 'BODY') {
|
||||
collection = $();
|
||||
this.$editor.removeClass('dragover');
|
||||
}
|
||||
};
|
||||
|
||||
this.documentEventHandlers.onDrop = () => {
|
||||
collection = $();
|
||||
this.$editor.removeClass('dragover');
|
||||
};
|
||||
|
||||
// show dropzone on dragenter when dragging a object to document
|
||||
// -but only if the editor is visible, i.e. has a positive width and height
|
||||
this.$eventListener.on('dragenter', this.documentEventHandlers.onDragenter)
|
||||
.on('dragleave', this.documentEventHandlers.onDragleave)
|
||||
.on('drop', this.documentEventHandlers.onDrop);
|
||||
|
||||
// change dropzone's message on hover.
|
||||
this.$dropzone.on('dragenter', () => {
|
||||
this.$dropzone.addClass('hover');
|
||||
$dropzoneMessage.text(this.lang.image.dropImage);
|
||||
}).on('dragleave', () => {
|
||||
this.$dropzone.removeClass('hover');
|
||||
$dropzoneMessage.text(this.lang.image.dragImageHere);
|
||||
});
|
||||
|
||||
// attach dropImage
|
||||
this.$dropzone.on('drop', (event) => {
|
||||
const dataTransfer = event.originalEvent.dataTransfer;
|
||||
|
||||
// stop the browser from opening the dropped content
|
||||
event.preventDefault();
|
||||
|
||||
if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
|
||||
this.$editable.focus();
|
||||
this.context.invoke('editor.insertImagesOrCallback', dataTransfer.files);
|
||||
} else {
|
||||
$.each(dataTransfer.types, (idx, type) => {
|
||||
// skip moz-specific types
|
||||
if (type.toLowerCase().indexOf('_moz_') > -1) {
|
||||
return;
|
||||
}
|
||||
const content = dataTransfer.getData(type);
|
||||
|
||||
if (type.toLowerCase().indexOf('text') > -1) {
|
||||
this.context.invoke('editor.pasteHTML', content);
|
||||
} else {
|
||||
$(content).each((idx, item) => {
|
||||
this.context.invoke('editor.insertNode', item);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}).on('dragover', false); // prevent default dragover event
|
||||
}
|
||||
|
||||
destroy() {
|
||||
Object.keys(this.documentEventHandlers).forEach((key) => {
|
||||
this.$eventListener.off(key.substr(2).toLowerCase(), this.documentEventHandlers[key]);
|
||||
});
|
||||
this.documentEventHandlers = {};
|
||||
}
|
||||
}
|
||||
973
public/vendor/editor/src/js/base/module/Editor.js
vendored
Executable file
@@ -0,0 +1,973 @@
|
||||
import $ from 'jquery';
|
||||
import env from '../core/env';
|
||||
import key from '../core/key';
|
||||
import func from '../core/func';
|
||||
import lists from '../core/lists';
|
||||
import dom from '../core/dom';
|
||||
import range from '../core/range';
|
||||
import { readFileAsDataURL, createImage } from '../core/async';
|
||||
import History from '../editing/History';
|
||||
import Style from '../editing/Style';
|
||||
import Typing from '../editing/Typing';
|
||||
import Table from '../editing/Table';
|
||||
import Bullet from '../editing/Bullet';
|
||||
|
||||
const KEY_BOGUS = 'bogus';
|
||||
|
||||
/**
|
||||
* @class Editor
|
||||
*/
|
||||
export default class Editor {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
|
||||
this.$note = context.layoutInfo.note;
|
||||
this.$editor = context.layoutInfo.editor;
|
||||
this.$editable = context.layoutInfo.editable;
|
||||
this.options = context.options;
|
||||
this.lang = this.options.langInfo;
|
||||
|
||||
this.editable = this.$editable[0];
|
||||
this.lastRange = null;
|
||||
this.snapshot = null;
|
||||
|
||||
this.style = new Style();
|
||||
this.table = new Table();
|
||||
this.typing = new Typing(context);
|
||||
this.bullet = new Bullet();
|
||||
this.history = new History(context);
|
||||
|
||||
this.context.memo('help.escape', this.lang.help.escape);
|
||||
this.context.memo('help.undo', this.lang.help.undo);
|
||||
this.context.memo('help.redo', this.lang.help.redo);
|
||||
this.context.memo('help.tab', this.lang.help.tab);
|
||||
this.context.memo('help.untab', this.lang.help.untab);
|
||||
this.context.memo('help.insertParagraph', this.lang.help.insertParagraph);
|
||||
this.context.memo('help.insertOrderedList', this.lang.help.insertOrderedList);
|
||||
this.context.memo('help.insertUnorderedList', this.lang.help.insertUnorderedList);
|
||||
this.context.memo('help.indent', this.lang.help.indent);
|
||||
this.context.memo('help.outdent', this.lang.help.outdent);
|
||||
this.context.memo('help.formatPara', this.lang.help.formatPara);
|
||||
this.context.memo('help.insertHorizontalRule', this.lang.help.insertHorizontalRule);
|
||||
this.context.memo('help.fontName', this.lang.help.fontName);
|
||||
|
||||
// native commands(with execCommand), generate function for execCommand
|
||||
const commands = [
|
||||
'bold', 'italic', 'underline', 'strikethrough', 'superscript', 'subscript',
|
||||
'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull',
|
||||
'formatBlock', 'removeFormat', 'backColor',
|
||||
];
|
||||
|
||||
for (let idx = 0, len = commands.length; idx < len; idx++) {
|
||||
this[commands[idx]] = ((sCmd) => {
|
||||
return (value) => {
|
||||
this.beforeCommand();
|
||||
document.execCommand(sCmd, false, value);
|
||||
this.afterCommand(true);
|
||||
};
|
||||
})(commands[idx]);
|
||||
this.context.memo('help.' + commands[idx], this.lang.help[commands[idx]]);
|
||||
}
|
||||
|
||||
this.fontName = this.wrapCommand((value) => {
|
||||
return this.fontStyling('font-family', env.validFontName(value));
|
||||
});
|
||||
|
||||
this.fontSize = this.wrapCommand((value) => {
|
||||
const unit = this.currentStyle()['font-size-unit'];
|
||||
return this.fontStyling('font-size', value + unit);
|
||||
});
|
||||
|
||||
this.fontSizeUnit = this.wrapCommand((value) => {
|
||||
const size = this.currentStyle()['font-size'];
|
||||
return this.fontStyling('font-size', size + value);
|
||||
});
|
||||
|
||||
for (let idx = 1; idx <= 6; idx++) {
|
||||
this['formatH' + idx] = ((idx) => {
|
||||
return () => {
|
||||
this.formatBlock('H' + idx);
|
||||
};
|
||||
})(idx);
|
||||
this.context.memo('help.formatH' + idx, this.lang.help['formatH' + idx]);
|
||||
}
|
||||
|
||||
this.insertParagraph = this.wrapCommand(() => {
|
||||
this.typing.insertParagraph(this.editable);
|
||||
});
|
||||
|
||||
this.insertOrderedList = this.wrapCommand(() => {
|
||||
this.bullet.insertOrderedList(this.editable);
|
||||
});
|
||||
|
||||
this.insertUnorderedList = this.wrapCommand(() => {
|
||||
this.bullet.insertUnorderedList(this.editable);
|
||||
});
|
||||
|
||||
this.indent = this.wrapCommand(() => {
|
||||
this.bullet.indent(this.editable);
|
||||
});
|
||||
|
||||
this.outdent = this.wrapCommand(() => {
|
||||
this.bullet.outdent(this.editable);
|
||||
});
|
||||
|
||||
/**
|
||||
* insertNode
|
||||
* insert node
|
||||
* @param {Node} node
|
||||
*/
|
||||
this.insertNode = this.wrapCommand((node) => {
|
||||
if (this.isLimited($(node).text().length)) {
|
||||
return;
|
||||
}
|
||||
const rng = this.getLastRange();
|
||||
rng.insertNode(node);
|
||||
this.setLastRange(range.createFromNodeAfter(node).select());
|
||||
});
|
||||
|
||||
/**
|
||||
* insert text
|
||||
* @param {String} text
|
||||
*/
|
||||
this.insertText = this.wrapCommand((text) => {
|
||||
if (this.isLimited(text.length)) {
|
||||
return;
|
||||
}
|
||||
const rng = this.getLastRange();
|
||||
const textNode = rng.insertNode(dom.createText(text));
|
||||
this.setLastRange(range.create(textNode, dom.nodeLength(textNode)).select());
|
||||
});
|
||||
|
||||
/**
|
||||
* paste HTML
|
||||
* @param {String} markup
|
||||
*/
|
||||
this.pasteHTML = this.wrapCommand((markup) => {
|
||||
if (this.isLimited(markup.length)) {
|
||||
return;
|
||||
}
|
||||
markup = this.context.invoke('codeview.purify', markup);
|
||||
const contents = this.getLastRange().pasteHTML(markup);
|
||||
this.setLastRange(range.createFromNodeAfter(lists.last(contents)).select());
|
||||
});
|
||||
|
||||
/**
|
||||
* formatBlock
|
||||
*
|
||||
* @param {String} tagName
|
||||
*/
|
||||
this.formatBlock = this.wrapCommand((tagName, $target) => {
|
||||
const onApplyCustomStyle = this.options.callbacks.onApplyCustomStyle;
|
||||
if (onApplyCustomStyle) {
|
||||
onApplyCustomStyle.call(this, $target, this.context, this.onFormatBlock);
|
||||
} else {
|
||||
this.onFormatBlock(tagName, $target);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* insert horizontal rule
|
||||
*/
|
||||
this.insertHorizontalRule = this.wrapCommand(() => {
|
||||
const hrNode = this.getLastRange().insertNode(dom.create('HR'));
|
||||
if (hrNode.nextSibling) {
|
||||
this.setLastRange(range.create(hrNode.nextSibling, 0).normalize().select());
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* lineHeight
|
||||
* @param {String} value
|
||||
*/
|
||||
this.lineHeight = this.wrapCommand((value) => {
|
||||
this.style.stylePara(this.getLastRange(), {
|
||||
lineHeight: value,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* create link (command)
|
||||
*
|
||||
* @param {Object} linkInfo
|
||||
*/
|
||||
this.createLink = this.wrapCommand((linkInfo) => {
|
||||
let linkUrl = linkInfo.url;
|
||||
const linkText = linkInfo.text;
|
||||
const isNewWindow = linkInfo.isNewWindow;
|
||||
const checkProtocol = linkInfo.checkProtocol;
|
||||
let rng = linkInfo.range || this.getLastRange();
|
||||
const additionalTextLength = linkText.length - rng.toString().length;
|
||||
if (additionalTextLength > 0 && this.isLimited(additionalTextLength)) {
|
||||
return;
|
||||
}
|
||||
const isTextChanged = rng.toString() !== linkText;
|
||||
|
||||
// handle spaced urls from input
|
||||
if (typeof linkUrl === 'string') {
|
||||
linkUrl = linkUrl.trim();
|
||||
}
|
||||
|
||||
if (this.options.onCreateLink) {
|
||||
linkUrl = this.options.onCreateLink(linkUrl);
|
||||
} else if (checkProtocol) {
|
||||
// if url doesn't have any protocol and not even a relative or a label, use http:// as default
|
||||
linkUrl = /^([A-Za-z][A-Za-z0-9+-.]*\:|#|\/)/.test(linkUrl)
|
||||
? linkUrl : this.options.defaultProtocol + linkUrl;
|
||||
}
|
||||
|
||||
let anchors = [];
|
||||
if (isTextChanged) {
|
||||
rng = rng.deleteContents();
|
||||
const anchor = rng.insertNode($('<A>' + linkText + '</A>')[0]);
|
||||
anchors.push(anchor);
|
||||
} else {
|
||||
anchors = this.style.styleNodes(rng, {
|
||||
nodeName: 'A',
|
||||
expandClosestSibling: true,
|
||||
onlyPartialContains: true,
|
||||
});
|
||||
}
|
||||
|
||||
$.each(anchors, (idx, anchor) => {
|
||||
$(anchor).attr('href', linkUrl);
|
||||
if (isNewWindow) {
|
||||
$(anchor).attr('target', '_blank');
|
||||
} else {
|
||||
$(anchor).removeAttr('target');
|
||||
}
|
||||
});
|
||||
|
||||
const startRange = range.createFromNodeBefore(lists.head(anchors));
|
||||
const startPoint = startRange.getStartPoint();
|
||||
const endRange = range.createFromNodeAfter(lists.last(anchors));
|
||||
const endPoint = endRange.getEndPoint();
|
||||
|
||||
this.setLastRange(
|
||||
range.create(
|
||||
startPoint.node,
|
||||
startPoint.offset,
|
||||
endPoint.node,
|
||||
endPoint.offset
|
||||
).select()
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* setting color
|
||||
*
|
||||
* @param {Object} sObjColor color code
|
||||
* @param {String} sObjColor.foreColor foreground color
|
||||
* @param {String} sObjColor.backColor background color
|
||||
*/
|
||||
this.color = this.wrapCommand((colorInfo) => {
|
||||
const foreColor = colorInfo.foreColor;
|
||||
const backColor = colorInfo.backColor;
|
||||
|
||||
if (foreColor) { document.execCommand('foreColor', false, foreColor); }
|
||||
if (backColor) { document.execCommand('backColor', false, backColor); }
|
||||
});
|
||||
|
||||
/**
|
||||
* Set foreground color
|
||||
*
|
||||
* @param {String} colorCode foreground color code
|
||||
*/
|
||||
this.foreColor = this.wrapCommand((colorInfo) => {
|
||||
document.execCommand('foreColor', false, colorInfo);
|
||||
});
|
||||
|
||||
/**
|
||||
* insert Table
|
||||
*
|
||||
* @param {String} dimension of table (ex : "5x5")
|
||||
*/
|
||||
this.insertTable = this.wrapCommand((dim) => {
|
||||
const dimension = dim.split('x');
|
||||
|
||||
const rng = this.getLastRange().deleteContents();
|
||||
rng.insertNode(this.table.createTable(dimension[0], dimension[1], this.options));
|
||||
});
|
||||
|
||||
/**
|
||||
* remove media object and Figure Elements if media object is img with Figure.
|
||||
*/
|
||||
this.removeMedia = this.wrapCommand(() => {
|
||||
let $target = $(this.restoreTarget()).parent();
|
||||
if ($target.closest('figure').length) {
|
||||
$target.closest('figure').remove();
|
||||
} else {
|
||||
$target = $(this.restoreTarget()).detach();
|
||||
}
|
||||
this.context.triggerEvent('media.delete', $target, this.$editable);
|
||||
});
|
||||
|
||||
/**
|
||||
* float me
|
||||
*
|
||||
* @param {String} value
|
||||
*/
|
||||
this.floatMe = this.wrapCommand((value) => {
|
||||
const $target = $(this.restoreTarget());
|
||||
$target.toggleClass('note-float-left', value === 'left');
|
||||
$target.toggleClass('note-float-right', value === 'right');
|
||||
$target.css('float', (value === 'none' ? '' : value));
|
||||
});
|
||||
|
||||
/**
|
||||
* resize overlay element
|
||||
* @param {String} value
|
||||
*/
|
||||
this.resize = this.wrapCommand((value) => {
|
||||
const $target = $(this.restoreTarget());
|
||||
value = parseFloat(value);
|
||||
if (value === 0) {
|
||||
$target.css('width', '');
|
||||
} else {
|
||||
$target.css({
|
||||
width: value * 100 + '%',
|
||||
height: '',
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initialize() {
|
||||
// bind custom events
|
||||
this.$editable.on('keydown', (event) => {
|
||||
if (event.keyCode === key.code.ENTER) {
|
||||
this.context.triggerEvent('enter', event);
|
||||
}
|
||||
this.context.triggerEvent('keydown', event);
|
||||
|
||||
// keep a snapshot to limit text on input event
|
||||
this.snapshot = this.history.makeSnapshot();
|
||||
this.hasKeyShortCut = false;
|
||||
if (!event.isDefaultPrevented()) {
|
||||
if (this.options.shortcuts) {
|
||||
this.hasKeyShortCut = this.handleKeyMap(event);
|
||||
} else {
|
||||
this.preventDefaultEditableShortCuts(event);
|
||||
}
|
||||
}
|
||||
if (this.isLimited(1, event)) {
|
||||
const lastRange = this.getLastRange();
|
||||
if (lastRange.eo - lastRange.so === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this.setLastRange();
|
||||
|
||||
// record undo in the key event except keyMap.
|
||||
if (this.options.recordEveryKeystroke) {
|
||||
if (this.hasKeyShortCut === false) {
|
||||
this.history.recordUndo();
|
||||
}
|
||||
}
|
||||
}).on('keyup', (event) => {
|
||||
this.setLastRange();
|
||||
this.context.triggerEvent('keyup', event);
|
||||
}).on('focus', (event) => {
|
||||
this.setLastRange();
|
||||
this.context.triggerEvent('focus', event);
|
||||
}).on('blur', (event) => {
|
||||
this.context.triggerEvent('blur', event);
|
||||
}).on('mousedown', (event) => {
|
||||
this.context.triggerEvent('mousedown', event);
|
||||
}).on('mouseup', (event) => {
|
||||
this.setLastRange();
|
||||
this.history.recordUndo();
|
||||
this.context.triggerEvent('mouseup', event);
|
||||
}).on('scroll', (event) => {
|
||||
this.context.triggerEvent('scroll', event);
|
||||
}).on('paste', (event) => {
|
||||
this.setLastRange();
|
||||
this.context.triggerEvent('paste', event);
|
||||
}).on('input', () => {
|
||||
// To limit composition characters (e.g. Korean)
|
||||
if (this.isLimited(0) && this.snapshot) {
|
||||
this.history.applySnapshot(this.snapshot);
|
||||
}
|
||||
});
|
||||
|
||||
this.$editable.attr('spellcheck', this.options.spellCheck);
|
||||
|
||||
this.$editable.attr('autocorrect', this.options.spellCheck);
|
||||
|
||||
if (this.options.disableGrammar) {
|
||||
this.$editable.attr('data-gramm', false);
|
||||
}
|
||||
|
||||
// init content before set event
|
||||
this.$editable.html(dom.html(this.$note) || dom.emptyPara);
|
||||
|
||||
this.$editable.on(env.inputEventName, func.debounce(() => {
|
||||
this.context.triggerEvent('change', this.$editable.html(), this.$editable);
|
||||
}, 10));
|
||||
|
||||
this.$editable.on('focusin', (event) => {
|
||||
this.context.triggerEvent('focusin', event);
|
||||
}).on('focusout', (event) => {
|
||||
this.context.triggerEvent('focusout', event);
|
||||
});
|
||||
|
||||
if (this.options.airMode) {
|
||||
if (this.options.overrideContextMenu) {
|
||||
this.$editor.on('contextmenu', (event) => {
|
||||
this.context.triggerEvent('contextmenu', event);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (this.options.width) {
|
||||
this.$editor.outerWidth(this.options.width);
|
||||
}
|
||||
if (this.options.height) {
|
||||
this.$editable.outerHeight(this.options.height);
|
||||
}
|
||||
if (this.options.maxHeight) {
|
||||
this.$editable.css('max-height', this.options.maxHeight);
|
||||
}
|
||||
if (this.options.minHeight) {
|
||||
this.$editable.css('min-height', this.options.minHeight);
|
||||
}
|
||||
}
|
||||
|
||||
this.history.recordUndo();
|
||||
this.setLastRange();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.$editable.off();
|
||||
}
|
||||
|
||||
handleKeyMap(event) {
|
||||
const keyMap = this.options.keyMap[env.isMac ? 'mac' : 'pc'];
|
||||
const keys = [];
|
||||
|
||||
if (event.metaKey) { keys.push('CMD'); }
|
||||
if (event.ctrlKey && !event.altKey) { keys.push('CTRL'); }
|
||||
if (event.shiftKey) { keys.push('SHIFT'); }
|
||||
|
||||
const keyName = key.nameFromCode[event.keyCode];
|
||||
if (keyName) {
|
||||
keys.push(keyName);
|
||||
}
|
||||
|
||||
const eventName = keyMap[keys.join('+')];
|
||||
|
||||
if (keyName === 'TAB' && !this.options.tabDisable) {
|
||||
this.afterCommand();
|
||||
} else if (eventName) {
|
||||
if (this.context.invoke(eventName) !== false) {
|
||||
event.preventDefault();
|
||||
// if keyMap action was invoked
|
||||
return true;
|
||||
}
|
||||
} else if (key.isEdit(event.keyCode)) {
|
||||
this.afterCommand();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
preventDefaultEditableShortCuts(event) {
|
||||
// B(Bold, 66) / I(Italic, 73) / U(Underline, 85)
|
||||
if ((event.ctrlKey || event.metaKey) &&
|
||||
lists.contains([66, 73, 85], event.keyCode)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
isLimited(pad, event) {
|
||||
pad = pad || 0;
|
||||
|
||||
if (typeof event !== 'undefined') {
|
||||
if (key.isMove(event.keyCode) ||
|
||||
key.isNavigation(event.keyCode) ||
|
||||
(event.ctrlKey || event.metaKey) ||
|
||||
lists.contains([key.code.BACKSPACE, key.code.DELETE], event.keyCode)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.maxTextLength > 0) {
|
||||
if ((this.$editable.text().length + pad) > this.options.maxTextLength) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* create range
|
||||
* @return {WrappedRange}
|
||||
*/
|
||||
createRange() {
|
||||
this.focus();
|
||||
this.setLastRange();
|
||||
return this.getLastRange();
|
||||
}
|
||||
|
||||
setLastRange(rng) {
|
||||
if (rng) {
|
||||
this.lastRange = rng;
|
||||
} else {
|
||||
this.lastRange = range.create(this.editable);
|
||||
|
||||
if ($(this.lastRange.sc).closest('.note-editable').length === 0) {
|
||||
this.lastRange = range.createFromBodyElement(this.editable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getLastRange() {
|
||||
if (!this.lastRange) {
|
||||
this.setLastRange();
|
||||
}
|
||||
return this.lastRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* saveRange
|
||||
*
|
||||
* save current range
|
||||
*
|
||||
* @param {Boolean} [thenCollapse=false]
|
||||
*/
|
||||
saveRange(thenCollapse) {
|
||||
if (thenCollapse) {
|
||||
this.getLastRange().collapse().select();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* restoreRange
|
||||
*
|
||||
* restore lately range
|
||||
*/
|
||||
restoreRange() {
|
||||
if (this.lastRange) {
|
||||
this.lastRange.select();
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
|
||||
saveTarget(node) {
|
||||
this.$editable.data('target', node);
|
||||
}
|
||||
|
||||
clearTarget() {
|
||||
this.$editable.removeData('target');
|
||||
}
|
||||
|
||||
restoreTarget() {
|
||||
return this.$editable.data('target');
|
||||
}
|
||||
|
||||
/**
|
||||
* currentStyle
|
||||
*
|
||||
* current style
|
||||
* @return {Object|Boolean} unfocus
|
||||
*/
|
||||
currentStyle() {
|
||||
let rng = range.create();
|
||||
if (rng) {
|
||||
rng = rng.normalize();
|
||||
}
|
||||
return rng ? this.style.current(rng) : this.style.fromNode(this.$editable);
|
||||
}
|
||||
|
||||
/**
|
||||
* style from node
|
||||
*
|
||||
* @param {jQuery} $node
|
||||
* @return {Object}
|
||||
*/
|
||||
styleFromNode($node) {
|
||||
return this.style.fromNode($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* undo
|
||||
*/
|
||||
undo() {
|
||||
this.context.triggerEvent('before.command', this.$editable.html());
|
||||
this.history.undo();
|
||||
this.context.triggerEvent('change', this.$editable.html(), this.$editable);
|
||||
}
|
||||
|
||||
/*
|
||||
* commit
|
||||
*/
|
||||
commit() {
|
||||
this.context.triggerEvent('before.command', this.$editable.html());
|
||||
this.history.commit();
|
||||
this.context.triggerEvent('change', this.$editable.html(), this.$editable);
|
||||
}
|
||||
|
||||
/**
|
||||
* redo
|
||||
*/
|
||||
redo() {
|
||||
this.context.triggerEvent('before.command', this.$editable.html());
|
||||
this.history.redo();
|
||||
this.context.triggerEvent('change', this.$editable.html(), this.$editable);
|
||||
}
|
||||
|
||||
/**
|
||||
* before command
|
||||
*/
|
||||
beforeCommand() {
|
||||
this.context.triggerEvent('before.command', this.$editable.html());
|
||||
|
||||
// Set styleWithCSS before run a command
|
||||
document.execCommand('styleWithCSS', false, this.options.styleWithCSS);
|
||||
|
||||
// keep focus on editable before command execution
|
||||
this.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* after command
|
||||
* @param {Boolean} isPreventTrigger
|
||||
*/
|
||||
afterCommand(isPreventTrigger) {
|
||||
this.normalizeContent();
|
||||
this.history.recordUndo();
|
||||
if (!isPreventTrigger) {
|
||||
this.context.triggerEvent('change', this.$editable.html(), this.$editable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handle tab key
|
||||
*/
|
||||
tab() {
|
||||
const rng = this.getLastRange();
|
||||
if (rng.isCollapsed() && rng.isOnCell()) {
|
||||
this.table.tab(rng);
|
||||
} else {
|
||||
if (this.options.tabSize === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.isLimited(this.options.tabSize)) {
|
||||
this.beforeCommand();
|
||||
this.typing.insertTab(rng, this.options.tabSize);
|
||||
this.afterCommand();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handle shift+tab key
|
||||
*/
|
||||
untab() {
|
||||
const rng = this.getLastRange();
|
||||
if (rng.isCollapsed() && rng.isOnCell()) {
|
||||
this.table.tab(rng, true);
|
||||
} else {
|
||||
if (this.options.tabSize === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* run given function between beforeCommand and afterCommand
|
||||
*/
|
||||
wrapCommand(fn) {
|
||||
return function() {
|
||||
this.beforeCommand();
|
||||
fn.apply(this, arguments);
|
||||
this.afterCommand();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* insert image
|
||||
*
|
||||
* @param {String} src
|
||||
* @param {String|Function} param
|
||||
* @return {Promise}
|
||||
*/
|
||||
insertImage(src, param) {
|
||||
return createImage(src, param).then(($image) => {
|
||||
this.beforeCommand();
|
||||
|
||||
if (typeof param === 'function') {
|
||||
param($image);
|
||||
} else {
|
||||
if (typeof param === 'string') {
|
||||
$image.attr('data-filename', param);
|
||||
}
|
||||
$image.css('width', Math.min(this.$editable.width(), $image.width()));
|
||||
}
|
||||
|
||||
$image.show();
|
||||
this.getLastRange().insertNode($image[0]);
|
||||
this.setLastRange(range.createFromNodeAfter($image[0]).select());
|
||||
this.afterCommand();
|
||||
}).fail((e) => {
|
||||
this.context.triggerEvent('image.upload.error', e);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* insertImages
|
||||
* @param {File[]} files
|
||||
*/
|
||||
insertImagesAsDataURL(files) {
|
||||
$.each(files, (idx, file) => {
|
||||
const filename = file.name;
|
||||
if (this.options.maximumImageFileSize && this.options.maximumImageFileSize < file.size) {
|
||||
this.context.triggerEvent('image.upload.error', this.lang.image.maximumFileSizeError);
|
||||
} else {
|
||||
readFileAsDataURL(file).then((dataURL) => {
|
||||
return this.insertImage(dataURL, filename);
|
||||
}).fail(() => {
|
||||
this.context.triggerEvent('image.upload.error');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* insertImagesOrCallback
|
||||
* @param {File[]} files
|
||||
*/
|
||||
insertImagesOrCallback(files) {
|
||||
const callbacks = this.options.callbacks;
|
||||
// If onImageUpload set,
|
||||
if (callbacks.onImageUpload) {
|
||||
this.context.triggerEvent('image.upload', files);
|
||||
// else insert Image as dataURL
|
||||
} else {
|
||||
this.insertImagesAsDataURL(files);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return selected plain text
|
||||
* @return {String} text
|
||||
*/
|
||||
getSelectedText() {
|
||||
let rng = this.getLastRange();
|
||||
|
||||
// if range on anchor, expand range with anchor
|
||||
if (rng.isOnAnchor()) {
|
||||
rng = range.createFromNode(dom.ancestor(rng.sc, dom.isAnchor));
|
||||
}
|
||||
|
||||
return rng.toString();
|
||||
}
|
||||
|
||||
onFormatBlock(tagName, $target) {
|
||||
// [workaround] for MSIE, IE need `<`
|
||||
document.execCommand('FormatBlock', false, env.isMSIE ? '<' + tagName + '>' : tagName);
|
||||
|
||||
// support custom class
|
||||
if ($target && $target.length) {
|
||||
// find the exact element has given tagName
|
||||
if ($target[0].tagName.toUpperCase() !== tagName.toUpperCase()) {
|
||||
$target = $target.find(tagName);
|
||||
}
|
||||
|
||||
if ($target && $target.length) {
|
||||
const className = $target[0].className || '';
|
||||
if (className) {
|
||||
const currentRange = this.createRange();
|
||||
|
||||
const $parent = $([currentRange.sc, currentRange.ec]).closest(tagName);
|
||||
$parent.addClass(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formatPara() {
|
||||
this.formatBlock('P');
|
||||
}
|
||||
|
||||
fontStyling(target, value) {
|
||||
const rng = this.getLastRange();
|
||||
|
||||
if (rng !== '') {
|
||||
const spans = this.style.styleNodes(rng);
|
||||
this.$editor.find('.note-status-output').html('');
|
||||
$(spans).css(target, value);
|
||||
|
||||
// [workaround] added styled bogus span for style
|
||||
// - also bogus character needed for cursor position
|
||||
if (rng.isCollapsed()) {
|
||||
const firstSpan = lists.head(spans);
|
||||
if (firstSpan && !dom.nodeLength(firstSpan)) {
|
||||
firstSpan.innerHTML = dom.ZERO_WIDTH_NBSP_CHAR;
|
||||
range.createFromNodeAfter(firstSpan.firstChild).select();
|
||||
this.setLastRange();
|
||||
this.$editable.data(KEY_BOGUS, firstSpan);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const noteStatusOutput = $.now();
|
||||
this.$editor.find('.note-status-output').html('<div id="note-status-output-' + noteStatusOutput + '" class="alert alert-info">' + this.lang.output.noSelection + '</div>');
|
||||
setTimeout(function() { $('#note-status-output-' + noteStatusOutput).remove(); }, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* unlink
|
||||
*
|
||||
* @type command
|
||||
*/
|
||||
unlink() {
|
||||
let rng = this.getLastRange();
|
||||
if (rng.isOnAnchor()) {
|
||||
const anchor = dom.ancestor(rng.sc, dom.isAnchor);
|
||||
rng = range.createFromNode(anchor);
|
||||
rng.select();
|
||||
this.setLastRange();
|
||||
|
||||
this.beforeCommand();
|
||||
document.execCommand('unlink');
|
||||
this.afterCommand();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns link info
|
||||
*
|
||||
* @return {Object}
|
||||
* @return {WrappedRange} return.range
|
||||
* @return {String} return.text
|
||||
* @return {Boolean} [return.isNewWindow=true]
|
||||
* @return {String} [return.url=""]
|
||||
*/
|
||||
getLinkInfo() {
|
||||
const rng = this.getLastRange().expand(dom.isAnchor);
|
||||
// Get the first anchor on range(for edit).
|
||||
const $anchor = $(lists.head(rng.nodes(dom.isAnchor)));
|
||||
const linkInfo = {
|
||||
range: rng,
|
||||
text: rng.toString(),
|
||||
url: $anchor.length ? $anchor.attr('href') : '',
|
||||
};
|
||||
|
||||
// When anchor exists,
|
||||
if ($anchor.length) {
|
||||
// Set isNewWindow by checking its target.
|
||||
linkInfo.isNewWindow = $anchor.attr('target') === '_blank';
|
||||
}
|
||||
|
||||
return linkInfo;
|
||||
}
|
||||
|
||||
addRow(position) {
|
||||
const rng = this.getLastRange(this.$editable);
|
||||
if (rng.isCollapsed() && rng.isOnCell()) {
|
||||
this.beforeCommand();
|
||||
this.table.addRow(rng, position);
|
||||
this.afterCommand();
|
||||
}
|
||||
}
|
||||
|
||||
addCol(position) {
|
||||
const rng = this.getLastRange(this.$editable);
|
||||
if (rng.isCollapsed() && rng.isOnCell()) {
|
||||
this.beforeCommand();
|
||||
this.table.addCol(rng, position);
|
||||
this.afterCommand();
|
||||
}
|
||||
}
|
||||
|
||||
deleteRow() {
|
||||
const rng = this.getLastRange(this.$editable);
|
||||
if (rng.isCollapsed() && rng.isOnCell()) {
|
||||
this.beforeCommand();
|
||||
this.table.deleteRow(rng);
|
||||
this.afterCommand();
|
||||
}
|
||||
}
|
||||
|
||||
deleteCol() {
|
||||
const rng = this.getLastRange(this.$editable);
|
||||
if (rng.isCollapsed() && rng.isOnCell()) {
|
||||
this.beforeCommand();
|
||||
this.table.deleteCol(rng);
|
||||
this.afterCommand();
|
||||
}
|
||||
}
|
||||
|
||||
deleteTable() {
|
||||
const rng = this.getLastRange(this.$editable);
|
||||
if (rng.isCollapsed() && rng.isOnCell()) {
|
||||
this.beforeCommand();
|
||||
this.table.deleteTable(rng);
|
||||
this.afterCommand();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Position} pos
|
||||
* @param {jQuery} $target - target element
|
||||
* @param {Boolean} [bKeepRatio] - keep ratio
|
||||
*/
|
||||
resizeTo(pos, $target, bKeepRatio) {
|
||||
let imageSize;
|
||||
if (bKeepRatio) {
|
||||
const newRatio = pos.y / pos.x;
|
||||
const ratio = $target.data('ratio');
|
||||
imageSize = {
|
||||
width: ratio > newRatio ? pos.x : pos.y / ratio,
|
||||
height: ratio > newRatio ? pos.x * ratio : pos.y,
|
||||
};
|
||||
} else {
|
||||
imageSize = {
|
||||
width: pos.x,
|
||||
height: pos.y,
|
||||
};
|
||||
}
|
||||
|
||||
$target.css(imageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns whether editable area has focus or not.
|
||||
*/
|
||||
hasFocus() {
|
||||
return this.$editable.is(':focus');
|
||||
}
|
||||
|
||||
/**
|
||||
* set focus
|
||||
*/
|
||||
focus() {
|
||||
// [workaround] Screen will move when page is scolled in IE.
|
||||
// - do focus when not focused
|
||||
if (!this.hasFocus()) {
|
||||
this.$editable.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns whether contents is empty or not.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isEmpty() {
|
||||
return dom.isEmpty(this.$editable[0]) || dom.emptyPara === this.$editable.html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all contents and restores the editable instance to an _emptyPara_.
|
||||
*/
|
||||
empty() {
|
||||
this.context.invoke('code', dom.emptyPara);
|
||||
}
|
||||
|
||||
/**
|
||||
* normalize content
|
||||
*/
|
||||
normalizeContent() {
|
||||
this.$editable[0].normalize();
|
||||
}
|
||||
}
|
||||
54
public/vendor/editor/src/js/base/module/Fullscreen.js
vendored
Executable file
@@ -0,0 +1,54 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
export default class Fullscreen {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
|
||||
this.$editor = context.layoutInfo.editor;
|
||||
this.$toolbar = context.layoutInfo.toolbar;
|
||||
this.$editable = context.layoutInfo.editable;
|
||||
this.$codable = context.layoutInfo.codable;
|
||||
|
||||
this.$window = $(window);
|
||||
this.$scrollbar = $('html, body');
|
||||
|
||||
this.onResize = () => {
|
||||
this.resizeTo({
|
||||
h: this.$window.height() - this.$toolbar.outerHeight(),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
resizeTo(size) {
|
||||
this.$editable.css('height', size.h);
|
||||
this.$codable.css('height', size.h);
|
||||
if (this.$codable.data('cmeditor')) {
|
||||
this.$codable.data('cmeditor').setsize(null, size.h);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle fullscreen
|
||||
*/
|
||||
toggle() {
|
||||
this.$editor.toggleClass('fullscreen');
|
||||
if (this.isFullscreen()) {
|
||||
this.$editable.data('orgHeight', this.$editable.css('height'));
|
||||
this.$editable.data('orgMaxHeight', this.$editable.css('maxHeight'));
|
||||
this.$editable.css('maxHeight', '');
|
||||
this.$window.on('resize', this.onResize).trigger('resize');
|
||||
this.$scrollbar.css('overflow', 'hidden');
|
||||
} else {
|
||||
this.$window.off('resize', this.onResize);
|
||||
this.resizeTo({ h: this.$editable.data('orgHeight') });
|
||||
this.$editable.css('maxHeight', this.$editable.css('orgMaxHeight'));
|
||||
this.$scrollbar.css('overflow', 'visible');
|
||||
}
|
||||
|
||||
this.context.invoke('toolbar.updateFullscreen', this.isFullscreen());
|
||||
}
|
||||
|
||||
isFullscreen() {
|
||||
return this.$editor.hasClass('fullscreen');
|
||||
}
|
||||
}
|
||||
143
public/vendor/editor/src/js/base/module/Handle.js
vendored
Executable file
@@ -0,0 +1,143 @@
|
||||
import $ from 'jquery';
|
||||
import dom from '../core/dom';
|
||||
|
||||
export default class Handle {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
this.$document = $(document);
|
||||
this.$editingArea = context.layoutInfo.editingArea;
|
||||
this.options = context.options;
|
||||
this.lang = this.options.langInfo;
|
||||
|
||||
this.events = {
|
||||
'summernote.mousedown': (we, e) => {
|
||||
if (this.update(e.target, e)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
'summernote.keyup summernote.scroll summernote.change summernote.dialog.shown': () => {
|
||||
this.update();
|
||||
},
|
||||
'summernote.disable summernote.blur': () => {
|
||||
this.hide();
|
||||
},
|
||||
'summernote.codeview.toggled': () => {
|
||||
this.update();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.$handle = $([
|
||||
'<div class="note-handle">',
|
||||
'<div class="note-control-selection">',
|
||||
'<div class="note-control-selection-bg"></div>',
|
||||
'<div class="note-control-holder note-control-nw"></div>',
|
||||
'<div class="note-control-holder note-control-ne"></div>',
|
||||
'<div class="note-control-holder note-control-sw"></div>',
|
||||
'<div class="',
|
||||
(this.options.disableResizeImage ? 'note-control-holder' : 'note-control-sizing'),
|
||||
' note-control-se"></div>',
|
||||
(this.options.disableResizeImage ? '' : '<div class="note-control-selection-info"></div>'),
|
||||
'</div>',
|
||||
'</div>',
|
||||
].join('')).prependTo(this.$editingArea);
|
||||
|
||||
this.$handle.on('mousedown', (event) => {
|
||||
if (dom.isControlSizing(event.target)) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const $target = this.$handle.find('.note-control-selection').data('target');
|
||||
const posStart = $target.offset();
|
||||
const scrollTop = this.$document.scrollTop();
|
||||
|
||||
const onMouseMove = (event) => {
|
||||
this.context.invoke('editor.resizeTo', {
|
||||
x: event.clientX - posStart.left,
|
||||
y: event.clientY - (posStart.top - scrollTop),
|
||||
}, $target, !event.shiftKey);
|
||||
|
||||
this.update($target[0], event);
|
||||
};
|
||||
|
||||
this.$document
|
||||
.on('mousemove', onMouseMove)
|
||||
.one('mouseup', (e) => {
|
||||
e.preventDefault();
|
||||
this.$document.off('mousemove', onMouseMove);
|
||||
this.context.invoke('editor.afterCommand');
|
||||
});
|
||||
|
||||
if (!$target.data('ratio')) { // original ratio.
|
||||
$target.data('ratio', $target.height() / $target.width());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for scrolling on the handle overlay.
|
||||
this.$handle.on('wheel', (e) => {
|
||||
e.preventDefault();
|
||||
this.update();
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.$handle.remove();
|
||||
}
|
||||
|
||||
update(target, event) {
|
||||
if (this.context.isDisabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isImage = dom.isImg(target);
|
||||
const $selection = this.$handle.find('.note-control-selection');
|
||||
|
||||
this.context.invoke('imagePopover.update', target, event);
|
||||
|
||||
if (isImage) {
|
||||
const $image = $(target);
|
||||
const position = $image.position();
|
||||
const pos = {
|
||||
left: position.left + parseInt($image.css('marginLeft'), 10),
|
||||
top: position.top + parseInt($image.css('marginTop'), 10),
|
||||
};
|
||||
|
||||
// exclude margin
|
||||
const imageSize = {
|
||||
w: $image.outerWidth(false),
|
||||
h: $image.outerHeight(false),
|
||||
};
|
||||
|
||||
$selection.css({
|
||||
display: 'block',
|
||||
left: pos.left,
|
||||
top: pos.top,
|
||||
width: imageSize.w,
|
||||
height: imageSize.h,
|
||||
}).data('target', $image); // save current image element.
|
||||
|
||||
const origImageObj = new Image();
|
||||
origImageObj.src = $image.attr('src');
|
||||
|
||||
const sizingText = imageSize.w + 'x' + imageSize.h + ' (' + this.lang.image.original + ': ' + origImageObj.width + 'x' + origImageObj.height + ')';
|
||||
$selection.find('.note-control-selection-info').text(sizingText);
|
||||
this.context.invoke('editor.saveTarget', target);
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
return isImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* hide
|
||||
*
|
||||
* @param {jQuery} $handle
|
||||
*/
|
||||
hide() {
|
||||
this.context.invoke('editor.clearTarget');
|
||||
this.$handle.children().hide();
|
||||
}
|
||||
}
|
||||
78
public/vendor/editor/src/js/base/module/HelpDialog.js
vendored
Executable file
@@ -0,0 +1,78 @@
|
||||
import $ from 'jquery';
|
||||
import env from '../core/env';
|
||||
|
||||
export default class HelpDialog {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
|
||||
this.ui = $.summernote.ui;
|
||||
this.$body = $(document.body);
|
||||
this.$editor = context.layoutInfo.editor;
|
||||
this.options = context.options;
|
||||
this.lang = this.options.langInfo;
|
||||
}
|
||||
|
||||
initialize() {
|
||||
const $container = this.options.dialogsInBody ? this.$body : this.options.container;
|
||||
const body = [
|
||||
'<p class="text-center">',
|
||||
'<a href="http://summernote.org/" target="_blank">Summernote @@VERSION@@</a> · ',
|
||||
'<a href="https://github.com/summernote/summernote" target="_blank">Project</a> · ',
|
||||
'<a href="https://github.com/summernote/summernote/issues" target="_blank">Issues</a>',
|
||||
'</p>',
|
||||
].join('');
|
||||
|
||||
this.$dialog = this.ui.dialog({
|
||||
title: this.lang.options.help,
|
||||
fade: this.options.dialogsFade,
|
||||
body: this.createShortcutList(),
|
||||
footer: body,
|
||||
callback: ($node) => {
|
||||
$node.find('.modal-body,.note-modal-body').css({
|
||||
'max-height': 300,
|
||||
'overflow': 'scroll',
|
||||
});
|
||||
},
|
||||
}).render().appendTo($container);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.ui.hideDialog(this.$dialog);
|
||||
this.$dialog.remove();
|
||||
}
|
||||
|
||||
createShortcutList() {
|
||||
const keyMap = this.options.keyMap[env.isMac ? 'mac' : 'pc'];
|
||||
return Object.keys(keyMap).map((key) => {
|
||||
const command = keyMap[key];
|
||||
const $row = $('<div><div class="help-list-item"/></div>');
|
||||
$row.append($('<label><kbd>' + key + '</kdb></label>').css({
|
||||
'width': 180,
|
||||
'margin-right': 10,
|
||||
})).append($('<span/>').html(this.context.memo('help.' + command) || command));
|
||||
return $row.html();
|
||||
}).join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* show help dialog
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
showHelpDialog() {
|
||||
return $.Deferred((deferred) => {
|
||||
this.ui.onDialogShown(this.$dialog, () => {
|
||||
this.context.triggerEvent('dialog.shown');
|
||||
deferred.resolve();
|
||||
});
|
||||
this.ui.showDialog(this.$dialog);
|
||||
}).promise();
|
||||
}
|
||||
|
||||
show() {
|
||||
this.context.invoke('editor.saveRange');
|
||||
this.showHelpDialog().then(() => {
|
||||
this.context.invoke('editor.restoreRange');
|
||||
});
|
||||
}
|
||||
}
|
||||
271
public/vendor/editor/src/js/base/module/HintPopover.js
vendored
Executable file
@@ -0,0 +1,271 @@
|
||||
import $ from 'jquery';
|
||||
import func from '../core/func';
|
||||
import lists from '../core/lists';
|
||||
import dom from '../core/dom';
|
||||
import range from '../core/range';
|
||||
import key from '../core/key';
|
||||
|
||||
const POPOVER_DIST = 5;
|
||||
|
||||
export default class HintPopover {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
|
||||
this.ui = $.summernote.ui;
|
||||
this.$editable = context.layoutInfo.editable;
|
||||
this.options = context.options;
|
||||
this.hint = this.options.hint || [];
|
||||
this.direction = this.options.hintDirection || 'bottom';
|
||||
this.hints = Array.isArray(this.hint) ? this.hint : [this.hint];
|
||||
|
||||
this.events = {
|
||||
'summernote.keyup': (we, e) => {
|
||||
if (!e.isDefaultPrevented()) {
|
||||
this.handleKeyup(e);
|
||||
}
|
||||
},
|
||||
'summernote.keydown': (we, e) => {
|
||||
this.handleKeydown(e);
|
||||
},
|
||||
'summernote.disable summernote.dialog.shown summernote.blur': () => {
|
||||
this.hide();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
shouldInitialize() {
|
||||
return this.hints.length > 0;
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.lastWordRange = null;
|
||||
this.matchingWord = null;
|
||||
this.$popover = this.ui.popover({
|
||||
className: 'note-hint-popover',
|
||||
hideArrow: true,
|
||||
direction: '',
|
||||
}).render().appendTo(this.options.container);
|
||||
|
||||
this.$popover.hide();
|
||||
this.$content = this.$popover.find('.popover-content,.note-popover-content');
|
||||
this.$content.on('click', '.note-hint-item', (e) => {
|
||||
this.$content.find('.active').removeClass('active');
|
||||
$(e.currentTarget).addClass('active');
|
||||
this.replace();
|
||||
});
|
||||
|
||||
this.$popover.on('mousedown', (e) => { e.preventDefault(); });
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.$popover.remove();
|
||||
}
|
||||
|
||||
selectItem($item) {
|
||||
this.$content.find('.active').removeClass('active');
|
||||
$item.addClass('active');
|
||||
|
||||
this.$content[0].scrollTop = $item[0].offsetTop - (this.$content.innerHeight() / 2);
|
||||
}
|
||||
|
||||
moveDown() {
|
||||
const $current = this.$content.find('.note-hint-item.active');
|
||||
const $next = $current.next();
|
||||
|
||||
if ($next.length) {
|
||||
this.selectItem($next);
|
||||
} else {
|
||||
let $nextGroup = $current.parent().next();
|
||||
|
||||
if (!$nextGroup.length) {
|
||||
$nextGroup = this.$content.find('.note-hint-group').first();
|
||||
}
|
||||
|
||||
this.selectItem($nextGroup.find('.note-hint-item').first());
|
||||
}
|
||||
}
|
||||
|
||||
moveUp() {
|
||||
const $current = this.$content.find('.note-hint-item.active');
|
||||
const $prev = $current.prev();
|
||||
|
||||
if ($prev.length) {
|
||||
this.selectItem($prev);
|
||||
} else {
|
||||
let $prevGroup = $current.parent().prev();
|
||||
|
||||
if (!$prevGroup.length) {
|
||||
$prevGroup = this.$content.find('.note-hint-group').last();
|
||||
}
|
||||
|
||||
this.selectItem($prevGroup.find('.note-hint-item').last());
|
||||
}
|
||||
}
|
||||
|
||||
replace() {
|
||||
const $item = this.$content.find('.note-hint-item.active');
|
||||
|
||||
if ($item.length) {
|
||||
var node = this.nodeFromItem($item);
|
||||
// If matchingWord length = 0 -> capture OK / open hint / but as mention capture "" (\w*)
|
||||
if (this.matchingWord !== null && this.matchingWord.length === 0) {
|
||||
this.lastWordRange.so = this.lastWordRange.eo;
|
||||
// Else si > 0 and normal case -> adjust range "before" for correct position of insertion
|
||||
} else if (this.matchingWord !== null && this.matchingWord.length > 0 && !this.lastWordRange.isCollapsed()) {
|
||||
let rangeCompute = this.lastWordRange.eo - this.lastWordRange.so - this.matchingWord.length;
|
||||
if (rangeCompute > 0) {
|
||||
this.lastWordRange.so += rangeCompute;
|
||||
}
|
||||
}
|
||||
this.lastWordRange.insertNode(node);
|
||||
|
||||
if (this.options.hintSelect === 'next') {
|
||||
var blank = document.createTextNode('');
|
||||
$(node).after(blank);
|
||||
range.createFromNodeBefore(blank).select();
|
||||
} else {
|
||||
range.createFromNodeAfter(node).select();
|
||||
}
|
||||
|
||||
this.lastWordRange = null;
|
||||
this.hide();
|
||||
this.context.invoke('editor.focus');
|
||||
}
|
||||
}
|
||||
|
||||
nodeFromItem($item) {
|
||||
const hint = this.hints[$item.data('index')];
|
||||
const item = $item.data('item');
|
||||
let node = hint.content ? hint.content(item) : item;
|
||||
if (typeof node === 'string') {
|
||||
node = dom.createText(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
createItemTemplates(hintIdx, items) {
|
||||
const hint = this.hints[hintIdx];
|
||||
return items.map((item /*, idx */) => {
|
||||
const $item = $('<div class="note-hint-item"/>');
|
||||
$item.append(hint.template ? hint.template(item) : item + '');
|
||||
$item.data({
|
||||
'index': hintIdx,
|
||||
'item': item,
|
||||
});
|
||||
return $item;
|
||||
});
|
||||
}
|
||||
|
||||
handleKeydown(e) {
|
||||
if (!this.$popover.is(':visible')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.keyCode === key.code.ENTER) {
|
||||
e.preventDefault();
|
||||
this.replace();
|
||||
} else if (e.keyCode === key.code.UP) {
|
||||
e.preventDefault();
|
||||
this.moveUp();
|
||||
} else if (e.keyCode === key.code.DOWN) {
|
||||
e.preventDefault();
|
||||
this.moveDown();
|
||||
}
|
||||
}
|
||||
|
||||
searchKeyword(index, keyword, callback) {
|
||||
const hint = this.hints[index];
|
||||
if (hint && hint.match.test(keyword) && hint.search) {
|
||||
const matches = hint.match.exec(keyword);
|
||||
this.matchingWord = matches[0];
|
||||
hint.search(matches[1], callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
createGroup(idx, keyword) {
|
||||
const $group = $('<div class="note-hint-group note-hint-group-' + idx + '"/>');
|
||||
this.searchKeyword(idx, keyword, (items) => {
|
||||
items = items || [];
|
||||
if (items.length) {
|
||||
$group.html(this.createItemTemplates(idx, items));
|
||||
this.show();
|
||||
}
|
||||
});
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
handleKeyup(e) {
|
||||
if (!lists.contains([key.code.ENTER, key.code.UP, key.code.DOWN], e.keyCode)) {
|
||||
let range = this.context.invoke('editor.getLastRange');
|
||||
let wordRange, keyword;
|
||||
if (this.options.hintMode === 'words') {
|
||||
wordRange = range.getWordsRange(range);
|
||||
keyword = wordRange.toString();
|
||||
|
||||
this.hints.forEach((hint) => {
|
||||
if (hint.match.test(keyword)) {
|
||||
wordRange = range.getWordsMatchRange(hint.match);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (!wordRange) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
keyword = wordRange.toString();
|
||||
} else {
|
||||
wordRange = range.getWordRange();
|
||||
keyword = wordRange.toString();
|
||||
}
|
||||
|
||||
if (this.hints.length && keyword) {
|
||||
this.$content.empty();
|
||||
|
||||
const bnd = func.rect2bnd(lists.last(wordRange.getClientRects()));
|
||||
const containerOffset = $(this.options.container).offset();
|
||||
if (bnd) {
|
||||
bnd.top -= containerOffset.top;
|
||||
bnd.left -= containerOffset.left;
|
||||
|
||||
this.$popover.hide();
|
||||
this.lastWordRange = wordRange;
|
||||
this.hints.forEach((hint, idx) => {
|
||||
if (hint.match.test(keyword)) {
|
||||
this.createGroup(idx, keyword).appendTo(this.$content);
|
||||
}
|
||||
});
|
||||
// select first .note-hint-item
|
||||
this.$content.find('.note-hint-item:first').addClass('active');
|
||||
|
||||
// set position for popover after group is created
|
||||
if (this.direction === 'top') {
|
||||
this.$popover.css({
|
||||
left: bnd.left,
|
||||
top: bnd.top - this.$popover.outerHeight() - POPOVER_DIST,
|
||||
});
|
||||
} else {
|
||||
this.$popover.css({
|
||||
left: bnd.left,
|
||||
top: bnd.top + bnd.height + POPOVER_DIST,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
show() {
|
||||
this.$popover.show();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.$popover.hide();
|
||||
}
|
||||
}
|
||||
133
public/vendor/editor/src/js/base/module/ImageDialog.js
vendored
Executable file
@@ -0,0 +1,133 @@
|
||||
import $ from 'jquery';
|
||||
import env from '../core/env';
|
||||
import key from '../core/key';
|
||||
|
||||
export default class ImageDialog {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
this.ui = $.summernote.ui;
|
||||
this.$body = $(document.body);
|
||||
this.$editor = context.layoutInfo.editor;
|
||||
this.options = context.options;
|
||||
this.lang = this.options.langInfo;
|
||||
}
|
||||
|
||||
initialize() {
|
||||
let imageLimitation = '';
|
||||
if (this.options.maximumImageFileSize) {
|
||||
const unit = Math.floor(Math.log(this.options.maximumImageFileSize) / Math.log(1024));
|
||||
const readableSize = (this.options.maximumImageFileSize / Math.pow(1024, unit)).toFixed(2) * 1 +
|
||||
' ' + ' KMGTP'[unit] + 'B';
|
||||
imageLimitation = `<small>${this.lang.image.maximumFileSize + ' : ' + readableSize}</small>`;
|
||||
}
|
||||
|
||||
const $container = this.options.dialogsInBody ? this.$body : this.options.container;
|
||||
const body = [
|
||||
'<div class="form-group note-form-group note-group-select-from-files">',
|
||||
'<label for="note-dialog-image-file-' + this.options.id + '" class="note-form-label">' + this.lang.image.selectFromFiles + '</label>',
|
||||
'<input id="note-dialog-image-file-' + this.options.id + '" class="note-image-input form-control-file note-form-control note-input" ',
|
||||
' type="file" name="files" accept="image/*" multiple="multiple"/>',
|
||||
imageLimitation,
|
||||
'</div>',
|
||||
'<div class="form-group note-group-image-url">',
|
||||
'<label for="note-dialog-image-url-' + this.options.id + '" class="note-form-label">' + this.lang.image.url + '</label>',
|
||||
'<input id="note-dialog-image-url-' + this.options.id + '" class="note-image-url form-control note-form-control note-input" type="text"/>',
|
||||
'</div>',
|
||||
].join('');
|
||||
const buttonClass = 'btn btn-primary note-btn note-btn-primary note-image-btn';
|
||||
const footer = `<input type="button" href="#" class="${buttonClass}" value="${this.lang.image.insert}" disabled>`;
|
||||
|
||||
this.$dialog = this.ui.dialog({
|
||||
title: this.lang.image.insert,
|
||||
fade: this.options.dialogsFade,
|
||||
body: body,
|
||||
footer: footer,
|
||||
}).render().appendTo($container);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.ui.hideDialog(this.$dialog);
|
||||
this.$dialog.remove();
|
||||
}
|
||||
|
||||
bindEnterKey($input, $btn) {
|
||||
$input.on('keypress', (event) => {
|
||||
if (event.keyCode === key.code.ENTER) {
|
||||
event.preventDefault();
|
||||
$btn.trigger('click');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
show() {
|
||||
this.context.invoke('editor.saveRange');
|
||||
this.showImageDialog().then((data) => {
|
||||
// [workaround] hide dialog before restore range for IE range focus
|
||||
this.ui.hideDialog(this.$dialog);
|
||||
this.context.invoke('editor.restoreRange');
|
||||
|
||||
if (typeof data === 'string') { // image url
|
||||
// If onImageLinkInsert set,
|
||||
if (this.options.callbacks.onImageLinkInsert) {
|
||||
this.context.triggerEvent('image.link.insert', data);
|
||||
} else {
|
||||
this.context.invoke('editor.insertImage', data);
|
||||
}
|
||||
} else { // array of files
|
||||
this.context.invoke('editor.insertImagesOrCallback', data);
|
||||
}
|
||||
}).fail(() => {
|
||||
this.context.invoke('editor.restoreRange');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* show image dialog
|
||||
*
|
||||
* @param {jQuery} $dialog
|
||||
* @return {Promise}
|
||||
*/
|
||||
showImageDialog() {
|
||||
return $.Deferred((deferred) => {
|
||||
const $imageInput = this.$dialog.find('.note-image-input');
|
||||
const $imageUrl = this.$dialog.find('.note-image-url');
|
||||
const $imageBtn = this.$dialog.find('.note-image-btn');
|
||||
|
||||
this.ui.onDialogShown(this.$dialog, () => {
|
||||
this.context.triggerEvent('dialog.shown');
|
||||
|
||||
// Cloning imageInput to clear element.
|
||||
$imageInput.replaceWith($imageInput.clone().on('change', (event) => {
|
||||
deferred.resolve(event.target.files || event.target.value);
|
||||
}).val(''));
|
||||
|
||||
$imageUrl.on('input paste propertychange', () => {
|
||||
this.ui.toggleBtn($imageBtn, $imageUrl.val());
|
||||
}).val('');
|
||||
|
||||
if (!env.isSupportTouch) {
|
||||
$imageUrl.trigger('focus');
|
||||
}
|
||||
|
||||
$imageBtn.click((event) => {
|
||||
event.preventDefault();
|
||||
deferred.resolve($imageUrl.val());
|
||||
});
|
||||
|
||||
this.bindEnterKey($imageUrl, $imageBtn);
|
||||
});
|
||||
|
||||
this.ui.onDialogHidden(this.$dialog, () => {
|
||||
$imageInput.off();
|
||||
$imageUrl.off();
|
||||
$imageBtn.off();
|
||||
|
||||
if (deferred.state() === 'pending') {
|
||||
deferred.reject();
|
||||
}
|
||||
});
|
||||
|
||||
this.ui.showDialog(this.$dialog);
|
||||
});
|
||||
}
|
||||
}
|
||||
70
public/vendor/editor/src/js/base/module/ImagePopover.js
vendored
Executable file
@@ -0,0 +1,70 @@
|
||||
import $ from 'jquery';
|
||||
import lists from '../core/lists';
|
||||
import dom from '../core/dom';
|
||||
|
||||
/**
|
||||
* Image popover module
|
||||
* mouse events that show/hide popover will be handled by Handle.js.
|
||||
* Handle.js will receive the events and invoke 'imagePopover.update'.
|
||||
*/
|
||||
export default class ImagePopover {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
this.ui = $.summernote.ui;
|
||||
|
||||
this.editable = context.layoutInfo.editable[0];
|
||||
this.options = context.options;
|
||||
|
||||
this.events = {
|
||||
'summernote.disable summernote.blur': () => {
|
||||
this.hide();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
shouldInitialize() {
|
||||
return !lists.isEmpty(this.options.popover.image);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.$popover = this.ui.popover({
|
||||
className: 'note-image-popover',
|
||||
}).render().appendTo(this.options.container);
|
||||
const $content = this.$popover.find('.popover-content,.note-popover-content');
|
||||
this.context.invoke('buttons.build', $content, this.options.popover.image);
|
||||
|
||||
this.$popover.on('mousedown', (e) => { e.preventDefault(); });
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.$popover.remove();
|
||||
}
|
||||
|
||||
update(target, event) {
|
||||
if (dom.isImg(target)) {
|
||||
const position = $(target).offset();
|
||||
const containerOffset = $(this.options.container).offset();
|
||||
let pos = {};
|
||||
if (this.options.popatmouse) {
|
||||
pos.left = event.pageX - 20;
|
||||
pos.top = event.pageY;
|
||||
} else {
|
||||
pos = position;
|
||||
}
|
||||
pos.top -= containerOffset.top;
|
||||
pos.left -= containerOffset.left;
|
||||
|
||||
this.$popover.css({
|
||||
display: 'block',
|
||||
left: pos.left,
|
||||
top: pos.top,
|
||||
});
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.$popover.hide();
|
||||
}
|
||||
}
|
||||
178
public/vendor/editor/src/js/base/module/LinkDialog.js
vendored
Executable file
@@ -0,0 +1,178 @@
|
||||
import $ from 'jquery';
|
||||
import env from '../core/env';
|
||||
import key from '../core/key';
|
||||
import func from '../core/func';
|
||||
|
||||
export default class LinkDialog {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
|
||||
this.ui = $.summernote.ui;
|
||||
this.$body = $(document.body);
|
||||
this.$editor = context.layoutInfo.editor;
|
||||
this.options = context.options;
|
||||
this.lang = this.options.langInfo;
|
||||
|
||||
context.memo('help.linkDialog.show', this.options.langInfo.help['linkDialog.show']);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
const $container = this.options.dialogsInBody ? this.$body : this.options.container;
|
||||
const body = [
|
||||
'<div class="form-group note-form-group">',
|
||||
`<label for="note-dialog-link-txt-${this.options.id}" class="note-form-label">${this.lang.link.textToDisplay}</label>`,
|
||||
`<input id="note-dialog-link-txt-${this.options.id}" class="note-link-text form-control note-form-control note-input" type="text"/>`,
|
||||
'</div>',
|
||||
'<div class="form-group note-form-group">',
|
||||
`<label for="note-dialog-link-url-${this.options.id}" class="note-form-label">${this.lang.link.url}</label>`,
|
||||
`<input id="note-dialog-link-url-${this.options.id}" class="note-link-url form-control note-form-control note-input" type="text" value="http://"/>`,
|
||||
'</div>',
|
||||
!this.options.disableLinkTarget
|
||||
? $('<div/>').append(this.ui.checkbox({
|
||||
className: 'sn-checkbox-open-in-new-window',
|
||||
text: this.lang.link.openInNewWindow,
|
||||
checked: true,
|
||||
}).render()).html()
|
||||
: '',
|
||||
$('<div/>').append(this.ui.checkbox({
|
||||
className: 'sn-checkbox-use-protocol',
|
||||
text: this.lang.link.useProtocol,
|
||||
checked: true,
|
||||
}).render()).html(),
|
||||
].join('');
|
||||
|
||||
const buttonClass = 'btn btn-primary note-btn note-btn-primary note-link-btn';
|
||||
const footer = `<input type="button" href="#" class="${buttonClass}" value="${this.lang.link.insert}" disabled>`;
|
||||
|
||||
this.$dialog = this.ui.dialog({
|
||||
className: 'link-dialog',
|
||||
title: this.lang.link.insert,
|
||||
fade: this.options.dialogsFade,
|
||||
body: body,
|
||||
footer: footer,
|
||||
}).render().appendTo($container);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.ui.hideDialog(this.$dialog);
|
||||
this.$dialog.remove();
|
||||
}
|
||||
|
||||
bindEnterKey($input, $btn) {
|
||||
$input.on('keypress', (event) => {
|
||||
if (event.keyCode === key.code.ENTER) {
|
||||
event.preventDefault();
|
||||
$btn.trigger('click');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle update button
|
||||
*/
|
||||
toggleLinkBtn($linkBtn, $linkText, $linkUrl) {
|
||||
this.ui.toggleBtn($linkBtn, $linkText.val() && $linkUrl.val());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show link dialog and set event handlers on dialog controls.
|
||||
*
|
||||
* @param {Object} linkInfo
|
||||
* @return {Promise}
|
||||
*/
|
||||
showLinkDialog(linkInfo) {
|
||||
return $.Deferred((deferred) => {
|
||||
const $linkText = this.$dialog.find('.note-link-text');
|
||||
const $linkUrl = this.$dialog.find('.note-link-url');
|
||||
const $linkBtn = this.$dialog.find('.note-link-btn');
|
||||
const $openInNewWindow = this.$dialog
|
||||
.find('.sn-checkbox-open-in-new-window input[type=checkbox]');
|
||||
const $useProtocol = this.$dialog
|
||||
.find('.sn-checkbox-use-protocol input[type=checkbox]');
|
||||
|
||||
this.ui.onDialogShown(this.$dialog, () => {
|
||||
this.context.triggerEvent('dialog.shown');
|
||||
|
||||
// If no url was given and given text is valid URL then copy that into URL Field
|
||||
if (!linkInfo.url && func.isValidUrl(linkInfo.text)) {
|
||||
linkInfo.url = linkInfo.text;
|
||||
}
|
||||
|
||||
$linkText.on('input paste propertychange', () => {
|
||||
// If linktext was modified by input events,
|
||||
// cloning text from linkUrl will be stopped.
|
||||
linkInfo.text = $linkText.val();
|
||||
this.toggleLinkBtn($linkBtn, $linkText, $linkUrl);
|
||||
}).val(linkInfo.text);
|
||||
|
||||
$linkUrl.on('input paste propertychange', () => {
|
||||
// Display same text on `Text to display` as default
|
||||
// when linktext has no text
|
||||
if (!linkInfo.text) {
|
||||
$linkText.val($linkUrl.val());
|
||||
}
|
||||
this.toggleLinkBtn($linkBtn, $linkText, $linkUrl);
|
||||
}).val(linkInfo.url);
|
||||
|
||||
if (!env.isSupportTouch) {
|
||||
$linkUrl.trigger('focus');
|
||||
}
|
||||
|
||||
this.toggleLinkBtn($linkBtn, $linkText, $linkUrl);
|
||||
this.bindEnterKey($linkUrl, $linkBtn);
|
||||
this.bindEnterKey($linkText, $linkBtn);
|
||||
|
||||
const isNewWindowChecked = linkInfo.isNewWindow !== undefined
|
||||
? linkInfo.isNewWindow : this.context.options.linkTargetBlank;
|
||||
|
||||
$openInNewWindow.prop('checked', isNewWindowChecked);
|
||||
|
||||
const useProtocolChecked = linkInfo.url
|
||||
? false : this.context.options.useProtocol;
|
||||
|
||||
$useProtocol.prop('checked', useProtocolChecked);
|
||||
|
||||
$linkBtn.one('click', (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
deferred.resolve({
|
||||
range: linkInfo.range,
|
||||
url: $linkUrl.val(),
|
||||
text: $linkText.val(),
|
||||
isNewWindow: $openInNewWindow.is(':checked'),
|
||||
checkProtocol: $useProtocol.is(':checked'),
|
||||
});
|
||||
this.ui.hideDialog(this.$dialog);
|
||||
});
|
||||
});
|
||||
|
||||
this.ui.onDialogHidden(this.$dialog, () => {
|
||||
// detach events
|
||||
$linkText.off();
|
||||
$linkUrl.off();
|
||||
$linkBtn.off();
|
||||
|
||||
if (deferred.state() === 'pending') {
|
||||
deferred.reject();
|
||||
}
|
||||
});
|
||||
|
||||
this.ui.showDialog(this.$dialog);
|
||||
}).promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} layoutInfo
|
||||
*/
|
||||
show() {
|
||||
const linkInfo = this.context.invoke('editor.getLinkInfo');
|
||||
|
||||
this.context.invoke('editor.saveRange');
|
||||
this.showLinkDialog(linkInfo).then((linkInfo) => {
|
||||
this.context.invoke('editor.restoreRange');
|
||||
this.context.invoke('editor.createLink', linkInfo);
|
||||
}).fail(() => {
|
||||
this.context.invoke('editor.restoreRange');
|
||||
});
|
||||
}
|
||||
}
|
||||
75
public/vendor/editor/src/js/base/module/LinkPopover.js
vendored
Executable file
@@ -0,0 +1,75 @@
|
||||
import $ from 'jquery';
|
||||
import lists from '../core/lists';
|
||||
import dom from '../core/dom';
|
||||
|
||||
export default class LinkPopover {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
|
||||
this.ui = $.summernote.ui;
|
||||
this.options = context.options;
|
||||
this.events = {
|
||||
'summernote.keyup summernote.mouseup summernote.change summernote.scroll': () => {
|
||||
this.update();
|
||||
},
|
||||
'summernote.disable summernote.dialog.shown summernote.blur': () => {
|
||||
this.hide();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
shouldInitialize() {
|
||||
return !lists.isEmpty(this.options.popover.link);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.$popover = this.ui.popover({
|
||||
className: 'note-link-popover',
|
||||
callback: ($node) => {
|
||||
const $content = $node.find('.popover-content,.note-popover-content');
|
||||
$content.prepend('<span><a target="_blank"></a> </span>');
|
||||
},
|
||||
}).render().appendTo(this.options.container);
|
||||
const $content = this.$popover.find('.popover-content,.note-popover-content');
|
||||
|
||||
this.context.invoke('buttons.build', $content, this.options.popover.link);
|
||||
|
||||
this.$popover.on('mousedown', (e) => { e.preventDefault(); });
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.$popover.remove();
|
||||
}
|
||||
|
||||
update() {
|
||||
// Prevent focusing on editable when invoke('code') is executed
|
||||
if (!this.context.invoke('editor.hasFocus')) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const rng = this.context.invoke('editor.getLastRange');
|
||||
if (rng.isCollapsed() && rng.isOnAnchor()) {
|
||||
const anchor = dom.ancestor(rng.sc, dom.isAnchor);
|
||||
const href = $(anchor).attr('href');
|
||||
this.$popover.find('a').attr('href', href).text(href);
|
||||
|
||||
const pos = dom.posFromPlaceholder(anchor);
|
||||
const containerOffset = $(this.options.container).offset();
|
||||
pos.top -= containerOffset.top;
|
||||
pos.left -= containerOffset.left;
|
||||
|
||||
this.$popover.css({
|
||||
display: 'block',
|
||||
left: pos.left,
|
||||
top: pos.top,
|
||||
});
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.$popover.hide();
|
||||
}
|
||||
}
|
||||
45
public/vendor/editor/src/js/base/module/Placeholder.js
vendored
Executable file
@@ -0,0 +1,45 @@
|
||||
import $ from 'jquery';
|
||||
export default class Placeholder {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
|
||||
this.$editingArea = context.layoutInfo.editingArea;
|
||||
this.options = context.options;
|
||||
|
||||
if (this.options.inheritPlaceholder === true) {
|
||||
// get placeholder value from the original element
|
||||
this.options.placeholder = this.context.$note.attr('placeholder') || this.options.placeholder;
|
||||
}
|
||||
|
||||
this.events = {
|
||||
'summernote.init summernote.change': () => {
|
||||
this.update();
|
||||
},
|
||||
'summernote.codeview.toggled': () => {
|
||||
this.update();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
shouldInitialize() {
|
||||
return !!this.options.placeholder;
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.$placeholder = $('<div class="note-placeholder">');
|
||||
this.$placeholder.on('click', () => {
|
||||
this.context.invoke('focus');
|
||||
}).html(this.options.placeholder).prependTo(this.$editingArea);
|
||||
|
||||
this.update();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.$placeholder.remove();
|
||||
}
|
||||
|
||||
update() {
|
||||
const isShow = !this.context.invoke('codeview.isActivated') && this.context.invoke('editor.isEmpty');
|
||||
this.$placeholder.toggle(isShow);
|
||||
}
|
||||
}
|
||||
42
public/vendor/editor/src/js/base/module/Statusbar.js
vendored
Executable file
@@ -0,0 +1,42 @@
|
||||
import $ from 'jquery';
|
||||
const EDITABLE_PADDING = 24;
|
||||
|
||||
export default class Statusbar {
|
||||
constructor(context) {
|
||||
this.$document = $(document);
|
||||
this.$statusbar = context.layoutInfo.statusbar;
|
||||
this.$editable = context.layoutInfo.editable;
|
||||
this.options = context.options;
|
||||
}
|
||||
|
||||
initialize() {
|
||||
if (this.options.airMode || this.options.disableResizeEditor) {
|
||||
this.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
this.$statusbar.on('mousedown', (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const editableTop = this.$editable.offset().top - this.$document.scrollTop();
|
||||
const onMouseMove = (event) => {
|
||||
let height = event.clientY - (editableTop + EDITABLE_PADDING);
|
||||
|
||||
height = (this.options.minheight > 0) ? Math.max(height, this.options.minheight) : height;
|
||||
height = (this.options.maxHeight > 0) ? Math.min(height, this.options.maxHeight) : height;
|
||||
|
||||
this.$editable.height(height);
|
||||
};
|
||||
|
||||
this.$document.on('mousemove', onMouseMove).one('mouseup', () => {
|
||||
this.$document.off('mousemove', onMouseMove);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.$statusbar.off();
|
||||
this.$statusbar.addClass('locked');
|
||||
}
|
||||
}
|
||||
77
public/vendor/editor/src/js/base/module/TablePopover.js
vendored
Executable file
@@ -0,0 +1,77 @@
|
||||
import $ from 'jquery';
|
||||
import env from '../core/env';
|
||||
import lists from '../core/lists';
|
||||
import dom from '../core/dom';
|
||||
|
||||
export default class TablePopover {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
|
||||
this.ui = $.summernote.ui;
|
||||
this.options = context.options;
|
||||
this.events = {
|
||||
'summernote.mousedown': (we, e) => {
|
||||
this.update(e.target);
|
||||
},
|
||||
'summernote.keyup summernote.scroll summernote.change': () => {
|
||||
this.update();
|
||||
},
|
||||
'summernote.disable summernote.blur': () => {
|
||||
this.hide();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
shouldInitialize() {
|
||||
return !lists.isEmpty(this.options.popover.table);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.$popover = this.ui.popover({
|
||||
className: 'note-table-popover',
|
||||
}).render().appendTo(this.options.container);
|
||||
const $content = this.$popover.find('.popover-content,.note-popover-content');
|
||||
|
||||
this.context.invoke('buttons.build', $content, this.options.popover.table);
|
||||
|
||||
// [workaround] Disable Firefox's default table editor
|
||||
if (env.isFF) {
|
||||
document.execCommand('enableInlineTableEditing', false, false);
|
||||
}
|
||||
|
||||
this.$popover.on('mousedown', (e) => { e.preventDefault(); });
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.$popover.remove();
|
||||
}
|
||||
|
||||
update(target) {
|
||||
if (this.context.isDisabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isCell = dom.isCell(target);
|
||||
|
||||
if (isCell) {
|
||||
const pos = dom.posFromPlaceholder(target);
|
||||
const containerOffset = $(this.options.container).offset();
|
||||
pos.top -= containerOffset.top;
|
||||
pos.left -= containerOffset.left;
|
||||
|
||||
this.$popover.css({
|
||||
display: 'block',
|
||||
left: pos.left,
|
||||
top: pos.top,
|
||||
});
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
return isCell;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.$popover.hide();
|
||||
}
|
||||
}
|
||||
150
public/vendor/editor/src/js/base/module/Toolbar.js
vendored
Executable file
@@ -0,0 +1,150 @@
|
||||
import $ from 'jquery';
|
||||
export default class Toolbar {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
|
||||
this.$window = $(window);
|
||||
this.$document = $(document);
|
||||
|
||||
this.ui = $.summernote.ui;
|
||||
this.$note = context.layoutInfo.note;
|
||||
this.$editor = context.layoutInfo.editor;
|
||||
this.$toolbar = context.layoutInfo.toolbar;
|
||||
this.$editable = context.layoutInfo.editable;
|
||||
this.$statusbar = context.layoutInfo.statusbar;
|
||||
this.options = context.options;
|
||||
|
||||
this.isFollowing = false;
|
||||
this.followScroll = this.followScroll.bind(this);
|
||||
}
|
||||
|
||||
shouldInitialize() {
|
||||
return !this.options.airMode;
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.options.toolbar = this.options.toolbar || [];
|
||||
|
||||
if (!this.options.toolbar.length) {
|
||||
this.$toolbar.hide();
|
||||
} else {
|
||||
this.context.invoke('buttons.build', this.$toolbar, this.options.toolbar);
|
||||
}
|
||||
|
||||
if (this.options.toolbarContainer) {
|
||||
this.$toolbar.appendTo(this.options.toolbarContainer);
|
||||
}
|
||||
|
||||
this.changeContainer(false);
|
||||
|
||||
this.$note.on('summernote.keyup summernote.mouseup summernote.change', () => {
|
||||
this.context.invoke('buttons.updateCurrentStyle');
|
||||
});
|
||||
|
||||
this.context.invoke('buttons.updateCurrentStyle');
|
||||
if (this.options.followingToolbar) {
|
||||
this.$window.on('scroll resize', this.followScroll);
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.$toolbar.children().remove();
|
||||
|
||||
if (this.options.followingToolbar) {
|
||||
this.$window.off('scroll resize', this.followScroll);
|
||||
}
|
||||
}
|
||||
|
||||
followScroll() {
|
||||
if (this.$editor.hasClass('fullscreen')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const editorHeight = this.$editor.outerHeight();
|
||||
const editorWidth = this.$editor.width();
|
||||
const toolbarHeight = this.$toolbar.height();
|
||||
const statusbarHeight = this.$statusbar.height();
|
||||
|
||||
// check if the web app is currently using another static bar
|
||||
let otherBarHeight = 0;
|
||||
if (this.options.otherStaticBar) {
|
||||
otherBarHeight = $(this.options.otherStaticBar).outerHeight();
|
||||
}
|
||||
|
||||
const currentOffset = this.$document.scrollTop();
|
||||
const editorOffsetTop = this.$editor.offset().top;
|
||||
const editorOffsetBottom = editorOffsetTop + editorHeight;
|
||||
const activateOffset = editorOffsetTop - otherBarHeight;
|
||||
const deactivateOffsetBottom = editorOffsetBottom - otherBarHeight - toolbarHeight - statusbarHeight;
|
||||
|
||||
if (!this.isFollowing &&
|
||||
(currentOffset > activateOffset) && (currentOffset < deactivateOffsetBottom - toolbarHeight)) {
|
||||
this.isFollowing = true;
|
||||
this.$editable.css({
|
||||
marginTop: this.$toolbar.outerHeight(),
|
||||
});
|
||||
this.$toolbar.css({
|
||||
position: 'fixed',
|
||||
top: otherBarHeight,
|
||||
width: editorWidth,
|
||||
zIndex: 1000,
|
||||
});
|
||||
} else if (this.isFollowing &&
|
||||
((currentOffset < activateOffset) || (currentOffset > deactivateOffsetBottom))) {
|
||||
this.isFollowing = false;
|
||||
this.$toolbar.css({
|
||||
position: 'relative',
|
||||
top: 0,
|
||||
width: '100%',
|
||||
zIndex: 'auto',
|
||||
});
|
||||
this.$editable.css({
|
||||
marginTop: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
changeContainer(isFullscreen) {
|
||||
if (isFullscreen) {
|
||||
this.$toolbar.prependTo(this.$editor);
|
||||
} else {
|
||||
if (this.options.toolbarContainer) {
|
||||
this.$toolbar.appendTo(this.options.toolbarContainer);
|
||||
}
|
||||
}
|
||||
if (this.options.followingToolbar) {
|
||||
this.followScroll();
|
||||
}
|
||||
}
|
||||
|
||||
updateFullscreen(isFullscreen) {
|
||||
this.ui.toggleBtnActive(this.$toolbar.find('.btn-fullscreen'), isFullscreen);
|
||||
|
||||
this.changeContainer(isFullscreen);
|
||||
}
|
||||
|
||||
updateCodeview(isCodeview) {
|
||||
this.ui.toggleBtnActive(this.$toolbar.find('.btn-codeview'), isCodeview);
|
||||
if (isCodeview) {
|
||||
this.deactivate();
|
||||
} else {
|
||||
this.activate();
|
||||
}
|
||||
}
|
||||
|
||||
activate(isIncludeCodeview) {
|
||||
let $btn = this.$toolbar.find('button');
|
||||
if (!isIncludeCodeview) {
|
||||
$btn = $btn.not('.btn-codeview').not('.btn-fullscreen');
|
||||
}
|
||||
this.ui.toggleBtn($btn, true);
|
||||
}
|
||||
|
||||
deactivate(isIncludeCodeview) {
|
||||
let $btn = this.$toolbar.find('button');
|
||||
if (!isIncludeCodeview) {
|
||||
$btn = $btn.not('.btn-codeview').not('.btn-fullscreen');
|
||||
}
|
||||
this.ui.toggleBtn($btn, false);
|
||||
}
|
||||
}
|
||||
223
public/vendor/editor/src/js/base/module/VideoDialog.js
vendored
Executable file
@@ -0,0 +1,223 @@
|
||||
import $ from 'jquery';
|
||||
import env from '../core/env';
|
||||
import key from '../core/key';
|
||||
|
||||
export default class VideoDialog {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
|
||||
this.ui = $.summernote.ui;
|
||||
this.$body = $(document.body);
|
||||
this.$editor = context.layoutInfo.editor;
|
||||
this.options = context.options;
|
||||
this.lang = this.options.langInfo;
|
||||
}
|
||||
|
||||
initialize() {
|
||||
const $container = this.options.dialogsInBody ? this.$body : this.options.container;
|
||||
const body = [
|
||||
'<div class="form-group note-form-group row-fluid">',
|
||||
`<label for="note-dialog-video-url-${this.options.id}" class="note-form-label">${this.lang.video.url} <small class="text-muted">${this.lang.video.providers}</small></label>`,
|
||||
`<input id="note-dialog-video-url-${this.options.id}" class="note-video-url form-control note-form-control note-input" type="text"/>`,
|
||||
'</div>',
|
||||
].join('');
|
||||
const buttonClass = 'btn btn-primary note-btn note-btn-primary note-video-btn';
|
||||
const footer = `<input type="button" href="#" class="${buttonClass}" value="${this.lang.video.insert}" disabled>`;
|
||||
|
||||
this.$dialog = this.ui.dialog({
|
||||
title: this.lang.video.insert,
|
||||
fade: this.options.dialogsFade,
|
||||
body: body,
|
||||
footer: footer,
|
||||
}).render().appendTo($container);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.ui.hideDialog(this.$dialog);
|
||||
this.$dialog.remove();
|
||||
}
|
||||
|
||||
bindEnterKey($input, $btn) {
|
||||
$input.on('keypress', (event) => {
|
||||
if (event.keyCode === key.code.ENTER) {
|
||||
event.preventDefault();
|
||||
$btn.trigger('click');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createVideoNode(url) {
|
||||
// video url patterns(youtube, instagram, vimeo, dailymotion, youku, mp4, ogg, webm)
|
||||
const ytRegExp = /\/\/(?:(?:www|m)\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([\w|-]{11})(?:(?:[\?&]t=)(\S+))?$/;
|
||||
const ytRegExpForStart = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/;
|
||||
const ytMatch = url.match(ytRegExp);
|
||||
|
||||
const igRegExp = /(?:www\.|\/\/)instagram\.com\/p\/(.[a-zA-Z0-9_-]*)/;
|
||||
const igMatch = url.match(igRegExp);
|
||||
|
||||
const vRegExp = /\/\/vine\.co\/v\/([a-zA-Z0-9]+)/;
|
||||
const vMatch = url.match(vRegExp);
|
||||
|
||||
const vimRegExp = /\/\/(player\.)?vimeo\.com\/([a-z]*\/)*(\d+)[?]?.*/;
|
||||
const vimMatch = url.match(vimRegExp);
|
||||
|
||||
const dmRegExp = /.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/;
|
||||
const dmMatch = url.match(dmRegExp);
|
||||
|
||||
const youkuRegExp = /\/\/v\.youku\.com\/v_show\/id_(\w+)=*\.html/;
|
||||
const youkuMatch = url.match(youkuRegExp);
|
||||
|
||||
const qqRegExp = /\/\/v\.qq\.com.*?vid=(.+)/;
|
||||
const qqMatch = url.match(qqRegExp);
|
||||
|
||||
const qqRegExp2 = /\/\/v\.qq\.com\/x?\/?(page|cover).*?\/([^\/]+)\.html\??.*/;
|
||||
const qqMatch2 = url.match(qqRegExp2);
|
||||
|
||||
const mp4RegExp = /^.+.(mp4|m4v)$/;
|
||||
const mp4Match = url.match(mp4RegExp);
|
||||
|
||||
const oggRegExp = /^.+.(ogg|ogv)$/;
|
||||
const oggMatch = url.match(oggRegExp);
|
||||
|
||||
const webmRegExp = /^.+.(webm)$/;
|
||||
const webmMatch = url.match(webmRegExp);
|
||||
|
||||
const fbRegExp = /(?:www\.|\/\/)facebook\.com\/([^\/]+)\/videos\/([0-9]+)/;
|
||||
const fbMatch = url.match(fbRegExp);
|
||||
|
||||
let $video;
|
||||
if (ytMatch && ytMatch[1].length === 11) {
|
||||
const youtubeId = ytMatch[1];
|
||||
var start = 0;
|
||||
if (typeof ytMatch[2] !== 'undefined') {
|
||||
const ytMatchForStart = ytMatch[2].match(ytRegExpForStart);
|
||||
if (ytMatchForStart) {
|
||||
for (var n = [3600, 60, 1], i = 0, r = n.length; i < r; i++) {
|
||||
start += (typeof ytMatchForStart[i + 1] !== 'undefined' ? n[i] * parseInt(ytMatchForStart[i + 1], 10) : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
$video = $('<iframe>')
|
||||
.attr('frameborder', 0)
|
||||
.attr('src', '//www.youtube.com/embed/' + youtubeId + (start > 0 ? '?start=' + start : ''))
|
||||
.attr('width', '640').attr('height', '360');
|
||||
} else if (igMatch && igMatch[0].length) {
|
||||
$video = $('<iframe>')
|
||||
.attr('frameborder', 0)
|
||||
.attr('src', 'https://instagram.com/p/' + igMatch[1] + '/embed/')
|
||||
.attr('width', '612').attr('height', '710')
|
||||
.attr('scrolling', 'no')
|
||||
.attr('allowtransparency', 'true');
|
||||
} else if (vMatch && vMatch[0].length) {
|
||||
$video = $('<iframe>')
|
||||
.attr('frameborder', 0)
|
||||
.attr('src', vMatch[0] + '/embed/simple')
|
||||
.attr('width', '600').attr('height', '600')
|
||||
.attr('class', 'vine-embed');
|
||||
} else if (vimMatch && vimMatch[3].length) {
|
||||
$video = $('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
|
||||
.attr('frameborder', 0)
|
||||
.attr('src', '//player.vimeo.com/video/' + vimMatch[3])
|
||||
.attr('width', '640').attr('height', '360');
|
||||
} else if (dmMatch && dmMatch[2].length) {
|
||||
$video = $('<iframe>')
|
||||
.attr('frameborder', 0)
|
||||
.attr('src', '//www.dailymotion.com/embed/video/' + dmMatch[2])
|
||||
.attr('width', '640').attr('height', '360');
|
||||
} else if (youkuMatch && youkuMatch[1].length) {
|
||||
$video = $('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
|
||||
.attr('frameborder', 0)
|
||||
.attr('height', '498')
|
||||
.attr('width', '510')
|
||||
.attr('src', '//player.youku.com/embed/' + youkuMatch[1]);
|
||||
} else if ((qqMatch && qqMatch[1].length) || (qqMatch2 && qqMatch2[2].length)) {
|
||||
const vid = ((qqMatch && qqMatch[1].length) ? qqMatch[1] : qqMatch2[2]);
|
||||
$video = $('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
|
||||
.attr('frameborder', 0)
|
||||
.attr('height', '310')
|
||||
.attr('width', '500')
|
||||
.attr('src', 'https://v.qq.com/iframe/player.html?vid=' + vid + '&auto=0');
|
||||
} else if (mp4Match || oggMatch || webmMatch) {
|
||||
$video = $('<video controls>')
|
||||
.attr('src', url)
|
||||
.attr('width', '640').attr('height', '360');
|
||||
} else if (fbMatch && fbMatch[0].length) {
|
||||
$video = $('<iframe>')
|
||||
.attr('frameborder', 0)
|
||||
.attr('src', 'https://www.facebook.com/plugins/video.php?href=' + encodeURIComponent(fbMatch[0]) + '&show_text=0&width=560')
|
||||
.attr('width', '560').attr('height', '301')
|
||||
.attr('scrolling', 'no')
|
||||
.attr('allowtransparency', 'true');
|
||||
} else {
|
||||
// this is not a known video link. Now what, Cat? Now what?
|
||||
return false;
|
||||
}
|
||||
|
||||
$video.addClass('note-video-clip');
|
||||
|
||||
return $video[0];
|
||||
}
|
||||
|
||||
show() {
|
||||
const text = this.context.invoke('editor.getSelectedText');
|
||||
this.context.invoke('editor.saveRange');
|
||||
this.showVideoDialog(text).then((url) => {
|
||||
// [workaround] hide dialog before restore range for IE range focus
|
||||
this.ui.hideDialog(this.$dialog);
|
||||
this.context.invoke('editor.restoreRange');
|
||||
|
||||
// build node
|
||||
const $node = this.createVideoNode(url);
|
||||
|
||||
if ($node) {
|
||||
// insert video node
|
||||
this.context.invoke('editor.insertNode', $node);
|
||||
}
|
||||
}).fail(() => {
|
||||
this.context.invoke('editor.restoreRange');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* show video dialog
|
||||
*
|
||||
* @param {jQuery} $dialog
|
||||
* @return {Promise}
|
||||
*/
|
||||
showVideoDialog(/* text */) {
|
||||
return $.Deferred((deferred) => {
|
||||
const $videoUrl = this.$dialog.find('.note-video-url');
|
||||
const $videoBtn = this.$dialog.find('.note-video-btn');
|
||||
|
||||
this.ui.onDialogShown(this.$dialog, () => {
|
||||
this.context.triggerEvent('dialog.shown');
|
||||
|
||||
$videoUrl.on('input paste propertychange', () => {
|
||||
this.ui.toggleBtn($videoBtn, $videoUrl.val());
|
||||
});
|
||||
|
||||
if (!env.isSupportTouch) {
|
||||
$videoUrl.trigger('focus');
|
||||
}
|
||||
|
||||
$videoBtn.click((event) => {
|
||||
event.preventDefault();
|
||||
deferred.resolve($videoUrl.val());
|
||||
});
|
||||
|
||||
this.bindEnterKey($videoUrl, $videoBtn);
|
||||
});
|
||||
|
||||
this.ui.onDialogHidden(this.$dialog, () => {
|
||||
$videoUrl.off();
|
||||
$videoBtn.off();
|
||||
|
||||
if (deferred.state() === 'pending') {
|
||||
deferred.reject();
|
||||
}
|
||||
});
|
||||
|
||||
this.ui.showDialog(this.$dialog);
|
||||
});
|
||||
}
|
||||
}
|
||||
66
public/vendor/editor/src/js/base/renderer.js
vendored
Executable file
@@ -0,0 +1,66 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
class Renderer {
|
||||
constructor(markup, children, options, callback) {
|
||||
this.markup = markup;
|
||||
this.children = children;
|
||||
this.options = options;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
render($parent) {
|
||||
const $node = $(this.markup);
|
||||
|
||||
if (this.options && this.options.contents) {
|
||||
$node.html(this.options.contents);
|
||||
}
|
||||
|
||||
if (this.options && this.options.className) {
|
||||
$node.addClass(this.options.className);
|
||||
}
|
||||
|
||||
if (this.options && this.options.data) {
|
||||
$.each(this.options.data, (k, v) => {
|
||||
$node.attr('data-' + k, v);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.options && this.options.click) {
|
||||
$node.on('click', this.options.click);
|
||||
}
|
||||
|
||||
if (this.children) {
|
||||
const $container = $node.find('.note-children-container');
|
||||
this.children.forEach((child) => {
|
||||
child.render($container.length ? $container : $node);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.callback) {
|
||||
this.callback($node, this.options);
|
||||
}
|
||||
|
||||
if (this.options && this.options.callback) {
|
||||
this.options.callback($node);
|
||||
}
|
||||
|
||||
if ($parent) {
|
||||
$parent.append($node);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
create: (markup, callback) => {
|
||||
return function() {
|
||||
const options = typeof arguments[1] === 'object' ? arguments[1] : arguments[0];
|
||||
let children = Array.isArray(arguments[0]) ? arguments[0] : [];
|
||||
if (options && options.children) {
|
||||
children = options.children;
|
||||
}
|
||||
return new Renderer(markup, children, options, callback);
|
||||
};
|
||||
},
|
||||
};
|
||||
363
public/vendor/editor/src/js/base/settings.js
vendored
Executable file
@@ -0,0 +1,363 @@
|
||||
import $ from 'jquery';
|
||||
import './summernote-en-US';
|
||||
import '../summernote';
|
||||
import dom from './core/dom';
|
||||
import range from './core/range';
|
||||
import lists from './core/lists';
|
||||
import Editor from './module/Editor';
|
||||
import Clipboard from './module/Clipboard';
|
||||
import Dropzone from './module/Dropzone';
|
||||
import Codeview from './module/Codeview';
|
||||
import Statusbar from './module/Statusbar';
|
||||
import Fullscreen from './module/Fullscreen';
|
||||
import Handle from './module/Handle';
|
||||
import AutoLink from './module/AutoLink';
|
||||
import AutoSync from './module/AutoSync';
|
||||
import AutoReplace from './module/AutoReplace';
|
||||
import Placeholder from './module/Placeholder';
|
||||
import Buttons from './module/Buttons';
|
||||
import Toolbar from './module/Toolbar';
|
||||
import LinkDialog from './module/LinkDialog';
|
||||
import LinkPopover from './module/LinkPopover';
|
||||
import ImageDialog from './module/ImageDialog';
|
||||
import ImagePopover from './module/ImagePopover';
|
||||
import TablePopover from './module/TablePopover';
|
||||
import VideoDialog from './module/VideoDialog';
|
||||
import HelpDialog from './module/HelpDialog';
|
||||
import AirPopover from './module/AirPopover';
|
||||
import HintPopover from './module/HintPopover';
|
||||
|
||||
$.summernote = $.extend($.summernote, {
|
||||
version: '@@VERSION@@',
|
||||
plugins: {},
|
||||
|
||||
dom: dom,
|
||||
range: range,
|
||||
lists: lists,
|
||||
|
||||
options: {
|
||||
langInfo: $.summernote.lang['en-US'],
|
||||
editing: true,
|
||||
modules: {
|
||||
'editor': Editor,
|
||||
'clipboard': Clipboard,
|
||||
'dropzone': Dropzone,
|
||||
'codeview': Codeview,
|
||||
'statusbar': Statusbar,
|
||||
'fullscreen': Fullscreen,
|
||||
'handle': Handle,
|
||||
// FIXME: HintPopover must be front of autolink
|
||||
// - Script error about range when Enter key is pressed on hint popover
|
||||
'hintPopover': HintPopover,
|
||||
'autoLink': AutoLink,
|
||||
'autoSync': AutoSync,
|
||||
'autoReplace': AutoReplace,
|
||||
'placeholder': Placeholder,
|
||||
'buttons': Buttons,
|
||||
'toolbar': Toolbar,
|
||||
'linkDialog': LinkDialog,
|
||||
'linkPopover': LinkPopover,
|
||||
'imageDialog': ImageDialog,
|
||||
'imagePopover': ImagePopover,
|
||||
'tablePopover': TablePopover,
|
||||
'videoDialog': VideoDialog,
|
||||
'helpDialog': HelpDialog,
|
||||
'airPopover': AirPopover,
|
||||
},
|
||||
|
||||
buttons: {},
|
||||
|
||||
lang: 'en-US',
|
||||
|
||||
followingToolbar: false,
|
||||
toolbarPosition: 'top',
|
||||
otherStaticBar: '',
|
||||
|
||||
// toolbar
|
||||
toolbar: [
|
||||
['style', ['style']],
|
||||
['font', ['bold', 'underline', 'clear']],
|
||||
['fontname', ['fontname']],
|
||||
['color', ['color']],
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['table', ['table']],
|
||||
['insert', ['link', 'picture', 'video']],
|
||||
['view', ['fullscreen', 'codeview', 'help']],
|
||||
],
|
||||
|
||||
// popover
|
||||
popatmouse: true,
|
||||
popover: {
|
||||
image: [
|
||||
['resize', ['resizeFull', 'resizeHalf', 'resizeQuarter', 'resizeNone']],
|
||||
['float', ['floatLeft', 'floatRight', 'floatNone']],
|
||||
['remove', ['removeMedia']],
|
||||
],
|
||||
link: [
|
||||
['link', ['linkDialogShow', 'unlink']],
|
||||
],
|
||||
table: [
|
||||
['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
|
||||
['delete', ['deleteRow', 'deleteCol', 'deleteTable']],
|
||||
],
|
||||
air: [
|
||||
['color', ['color']],
|
||||
['font', ['bold', 'underline', 'clear']],
|
||||
['para', ['ul', 'paragraph']],
|
||||
['table', ['table']],
|
||||
['insert', ['link', 'picture']],
|
||||
['view', ['fullscreen', 'codeview']],
|
||||
],
|
||||
},
|
||||
|
||||
// air mode: inline editor
|
||||
airMode: false,
|
||||
overrideContextMenu: false, // TBD
|
||||
|
||||
width: null,
|
||||
height: null,
|
||||
linkTargetBlank: true,
|
||||
useProtocol: true,
|
||||
defaultProtocol: 'http://',
|
||||
|
||||
focus: false,
|
||||
tabDisabled: false,
|
||||
tabSize: 4,
|
||||
styleWithCSS: false,
|
||||
shortcuts: true,
|
||||
textareaAutoSync: true,
|
||||
tooltip: 'auto',
|
||||
container: null,
|
||||
maxTextLength: 0,
|
||||
blockquoteBreakingLevel: 2,
|
||||
spellCheck: true,
|
||||
disableGrammar: false,
|
||||
placeholder: null,
|
||||
inheritPlaceholder: false,
|
||||
// TODO: need to be documented
|
||||
recordEveryKeystroke: false,
|
||||
historyLimit: 200,
|
||||
|
||||
// TODO: need to be documented
|
||||
showDomainOnlyForAutolink: false,
|
||||
|
||||
// TODO: need to be documented
|
||||
hintMode: 'word',
|
||||
hintSelect: 'after',
|
||||
hintDirection: 'bottom',
|
||||
|
||||
styleTags: ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
||||
|
||||
fontNames: [
|
||||
'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New',
|
||||
'Helvetica Neue', 'Helvetica', 'Impact', 'Lucida Grande',
|
||||
'Tahoma', 'Times New Roman', 'Verdana',
|
||||
],
|
||||
fontNamesIgnoreCheck: [],
|
||||
addDefaultFonts: true,
|
||||
|
||||
fontSizes: ['8', '9', '10', '11', '12', '14', '18', '24', '36'],
|
||||
|
||||
fontSizeUnits: ['px', 'pt'],
|
||||
|
||||
// pallete colors(n x n)
|
||||
colors: [
|
||||
['#000000', '#424242', '#636363', '#9C9C94', '#CEC6CE', '#EFEFEF', '#F7F7F7', '#FFFFFF'],
|
||||
['#FF0000', '#FF9C00', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF', '#9C00FF', '#FF00FF'],
|
||||
['#F7C6CE', '#FFE7CE', '#FFEFC6', '#D6EFD6', '#CEDEE7', '#CEE7F7', '#D6D6E7', '#E7D6DE'],
|
||||
['#E79C9C', '#FFC69C', '#FFE79C', '#B5D6A5', '#A5C6CE', '#9CC6EF', '#B5A5D6', '#D6A5BD'],
|
||||
['#E76363', '#F7AD6B', '#FFD663', '#94BD7B', '#73A5AD', '#6BADDE', '#8C7BC6', '#C67BA5'],
|
||||
['#CE0000', '#E79439', '#EFC631', '#6BA54A', '#4A7B8C', '#3984C6', '#634AA5', '#A54A7B'],
|
||||
['#9C0000', '#B56308', '#BD9400', '#397B21', '#104A5A', '#085294', '#311873', '#731842'],
|
||||
['#630000', '#7B3900', '#846300', '#295218', '#083139', '#003163', '#21104A', '#4A1031'],
|
||||
],
|
||||
|
||||
// http://chir.ag/projects/name-that-color/
|
||||
colorsName: [
|
||||
['Black', 'Tundora', 'Dove Gray', 'Star Dust', 'Pale Slate', 'Gallery', 'Alabaster', 'White'],
|
||||
['Red', 'Orange Peel', 'Yellow', 'Green', 'Cyan', 'Blue', 'Electric Violet', 'Magenta'],
|
||||
['Azalea', 'Karry', 'Egg White', 'Zanah', 'Botticelli', 'Tropical Blue', 'Mischka', 'Twilight'],
|
||||
['Tonys Pink', 'Peach Orange', 'Cream Brulee', 'Sprout', 'Casper', 'Perano', 'Cold Purple', 'Careys Pink'],
|
||||
['Mandy', 'Rajah', 'Dandelion', 'Olivine', 'Gulf Stream', 'Viking', 'Blue Marguerite', 'Puce'],
|
||||
['Guardsman Red', 'Fire Bush', 'Golden Dream', 'Chelsea Cucumber', 'Smalt Blue', 'Boston Blue', 'Butterfly Bush', 'Cadillac'],
|
||||
['Sangria', 'Mai Tai', 'Buddha Gold', 'Forest Green', 'Eden', 'Venice Blue', 'Meteorite', 'Claret'],
|
||||
['Rosewood', 'Cinnamon', 'Olive', 'Parsley', 'Tiber', 'Midnight Blue', 'Valentino', 'Loulou'],
|
||||
],
|
||||
|
||||
colorButton: {
|
||||
foreColor: '#000000',
|
||||
backColor: '#FFFF00',
|
||||
},
|
||||
|
||||
lineHeights: ['1.0', '1.2', '1.4', '1.5', '1.6', '1.8', '2.0', '3.0'],
|
||||
|
||||
tableClassName: 'table table-bordered',
|
||||
|
||||
insertTableMaxSize: {
|
||||
col: 10,
|
||||
row: 10,
|
||||
},
|
||||
|
||||
// By default, dialogs are attached in container.
|
||||
dialogsInBody: false,
|
||||
dialogsFade: false,
|
||||
|
||||
maximumImageFileSize: null,
|
||||
|
||||
callbacks: {
|
||||
onBeforeCommand: null,
|
||||
onBlur: null,
|
||||
onBlurCodeview: null,
|
||||
onChange: null,
|
||||
onChangeCodeview: null,
|
||||
onDialogShown: null,
|
||||
onEnter: null,
|
||||
onFocus: null,
|
||||
onImageLinkInsert: null,
|
||||
onImageUpload: null,
|
||||
onImageUploadError: null,
|
||||
onInit: null,
|
||||
onKeydown: null,
|
||||
onKeyup: null,
|
||||
onMousedown: null,
|
||||
onMouseup: null,
|
||||
onPaste: null,
|
||||
onScroll: null,
|
||||
},
|
||||
|
||||
codemirror: {
|
||||
mode: 'text/html',
|
||||
htmlMode: true,
|
||||
lineNumbers: true,
|
||||
},
|
||||
|
||||
codeviewFilter: false,
|
||||
codeviewFilterRegex: /<\/*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|ilayer|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|t(?:itle|extarea)|xml)[^>]*?>/gi,
|
||||
codeviewIframeFilter: true,
|
||||
codeviewIframeWhitelistSrc: [],
|
||||
codeviewIframeWhitelistSrcBase: [
|
||||
'www.youtube.com',
|
||||
'www.youtube-nocookie.com',
|
||||
'www.facebook.com',
|
||||
'vine.co',
|
||||
'instagram.com',
|
||||
'player.vimeo.com',
|
||||
'www.dailymotion.com',
|
||||
'player.youku.com',
|
||||
'v.qq.com',
|
||||
],
|
||||
|
||||
keyMap: {
|
||||
pc: {
|
||||
'ESC': 'escape',
|
||||
'ENTER': 'insertParagraph',
|
||||
'CTRL+Z': 'undo',
|
||||
'CTRL+Y': 'redo',
|
||||
'TAB': 'tab',
|
||||
'SHIFT+TAB': 'untab',
|
||||
'CTRL+B': 'bold',
|
||||
'CTRL+I': 'italic',
|
||||
'CTRL+U': 'underline',
|
||||
'CTRL+SHIFT+S': 'strikethrough',
|
||||
'CTRL+BACKSLASH': 'removeFormat',
|
||||
'CTRL+SHIFT+L': 'justifyLeft',
|
||||
'CTRL+SHIFT+E': 'justifyCenter',
|
||||
'CTRL+SHIFT+R': 'justifyRight',
|
||||
'CTRL+SHIFT+J': 'justifyFull',
|
||||
'CTRL+SHIFT+NUM7': 'insertUnorderedList',
|
||||
'CTRL+SHIFT+NUM8': 'insertOrderedList',
|
||||
'CTRL+LEFTBRACKET': 'outdent',
|
||||
'CTRL+RIGHTBRACKET': 'indent',
|
||||
'CTRL+NUM0': 'formatPara',
|
||||
'CTRL+NUM1': 'formatH1',
|
||||
'CTRL+NUM2': 'formatH2',
|
||||
'CTRL+NUM3': 'formatH3',
|
||||
'CTRL+NUM4': 'formatH4',
|
||||
'CTRL+NUM5': 'formatH5',
|
||||
'CTRL+NUM6': 'formatH6',
|
||||
'CTRL+ENTER': 'insertHorizontalRule',
|
||||
'CTRL+K': 'linkDialog.show',
|
||||
},
|
||||
|
||||
mac: {
|
||||
'ESC': 'escape',
|
||||
'ENTER': 'insertParagraph',
|
||||
'CMD+Z': 'undo',
|
||||
'CMD+SHIFT+Z': 'redo',
|
||||
'TAB': 'tab',
|
||||
'SHIFT+TAB': 'untab',
|
||||
'CMD+B': 'bold',
|
||||
'CMD+I': 'italic',
|
||||
'CMD+U': 'underline',
|
||||
'CMD+SHIFT+S': 'strikethrough',
|
||||
'CMD+BACKSLASH': 'removeFormat',
|
||||
'CMD+SHIFT+L': 'justifyLeft',
|
||||
'CMD+SHIFT+E': 'justifyCenter',
|
||||
'CMD+SHIFT+R': 'justifyRight',
|
||||
'CMD+SHIFT+J': 'justifyFull',
|
||||
'CMD+SHIFT+NUM7': 'insertUnorderedList',
|
||||
'CMD+SHIFT+NUM8': 'insertOrderedList',
|
||||
'CMD+LEFTBRACKET': 'outdent',
|
||||
'CMD+RIGHTBRACKET': 'indent',
|
||||
'CMD+NUM0': 'formatPara',
|
||||
'CMD+NUM1': 'formatH1',
|
||||
'CMD+NUM2': 'formatH2',
|
||||
'CMD+NUM3': 'formatH3',
|
||||
'CMD+NUM4': 'formatH4',
|
||||
'CMD+NUM5': 'formatH5',
|
||||
'CMD+NUM6': 'formatH6',
|
||||
'CMD+ENTER': 'insertHorizontalRule',
|
||||
'CMD+K': 'linkDialog.show',
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
'align': 'note-icon-align',
|
||||
'alignCenter': 'note-icon-align-center',
|
||||
'alignJustify': 'note-icon-align-justify',
|
||||
'alignLeft': 'note-icon-align-left',
|
||||
'alignRight': 'note-icon-align-right',
|
||||
'rowBelow': 'note-icon-row-below',
|
||||
'colBefore': 'note-icon-col-before',
|
||||
'colAfter': 'note-icon-col-after',
|
||||
'rowAbove': 'note-icon-row-above',
|
||||
'rowRemove': 'note-icon-row-remove',
|
||||
'colRemove': 'note-icon-col-remove',
|
||||
'indent': 'note-icon-align-indent',
|
||||
'outdent': 'note-icon-align-outdent',
|
||||
'arrowsAlt': 'note-icon-arrows-alt',
|
||||
'bold': 'note-icon-bold',
|
||||
'caret': 'note-icon-caret',
|
||||
'circle': 'note-icon-circle',
|
||||
'close': 'note-icon-close',
|
||||
'code': 'note-icon-code',
|
||||
'eraser': 'note-icon-eraser',
|
||||
'floatLeft': 'note-icon-float-left',
|
||||
'floatRight': 'note-icon-float-right',
|
||||
'font': 'note-icon-font',
|
||||
'frame': 'note-icon-frame',
|
||||
'italic': 'note-icon-italic',
|
||||
'link': 'note-icon-link',
|
||||
'unlink': 'note-icon-chain-broken',
|
||||
'magic': 'note-icon-magic',
|
||||
'menuCheck': 'note-icon-menu-check',
|
||||
'minus': 'note-icon-minus',
|
||||
'orderedlist': 'note-icon-orderedlist',
|
||||
'pencil': 'note-icon-pencil',
|
||||
'picture': 'note-icon-picture',
|
||||
'question': 'note-icon-question',
|
||||
'redo': 'note-icon-redo',
|
||||
'rollback': 'note-icon-rollback',
|
||||
'square': 'note-icon-square',
|
||||
'strikethrough': 'note-icon-strikethrough',
|
||||
'subscript': 'note-icon-subscript',
|
||||
'superscript': 'note-icon-superscript',
|
||||
'table': 'note-icon-table',
|
||||
'textHeight': 'note-icon-text-height',
|
||||
'trash': 'note-icon-trash',
|
||||
'underline': 'note-icon-underline',
|
||||
'undo': 'note-icon-undo',
|
||||
'unorderedlist': 'note-icon-unorderedlist',
|
||||
'video': 'note-icon-video',
|
||||
},
|
||||
},
|
||||
});
|
||||
167
public/vendor/editor/src/js/base/summernote-en-US.js
vendored
Executable file
@@ -0,0 +1,167 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
$.summernote = $.summernote || {
|
||||
lang: {},
|
||||
};
|
||||
|
||||
$.extend($.summernote.lang, {
|
||||
'en-US': {
|
||||
font: {
|
||||
bold: 'Bold',
|
||||
italic: 'Italic',
|
||||
underline: 'Underline',
|
||||
clear: 'Remove Font Style',
|
||||
height: 'Line Height',
|
||||
name: 'Font Family',
|
||||
strikethrough: 'Strikethrough',
|
||||
subscript: 'Subscript',
|
||||
superscript: 'Superscript',
|
||||
size: 'Font Size',
|
||||
sizeunit: 'Font Size Unit',
|
||||
},
|
||||
image: {
|
||||
image: 'Picture',
|
||||
insert: 'Insert Image',
|
||||
resizeFull: 'Resize full',
|
||||
resizeHalf: 'Resize half',
|
||||
resizeQuarter: 'Resize quarter',
|
||||
resizeNone: 'Original size',
|
||||
floatLeft: 'Float Left',
|
||||
floatRight: 'Float Right',
|
||||
floatNone: 'Remove float',
|
||||
shapeRounded: 'Shape: Rounded',
|
||||
shapeCircle: 'Shape: Circle',
|
||||
shapeThumbnail: 'Shape: Thumbnail',
|
||||
shapeNone: 'Shape: None',
|
||||
dragImageHere: 'Drag image or text here',
|
||||
dropImage: 'Drop image or Text',
|
||||
selectFromFiles: 'Select from files',
|
||||
maximumFileSize: 'Maximum file size',
|
||||
maximumFileSizeError: 'Maximum file size exceeded.',
|
||||
url: 'Image URL',
|
||||
remove: 'Remove Image',
|
||||
original: 'Original',
|
||||
},
|
||||
video: {
|
||||
video: 'Video',
|
||||
videoLink: 'Video Link',
|
||||
insert: 'Insert Video',
|
||||
url: 'Video URL',
|
||||
providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion or Youku)',
|
||||
},
|
||||
link: {
|
||||
link: 'Link',
|
||||
insert: 'Insert Link',
|
||||
unlink: 'Unlink',
|
||||
edit: 'Edit',
|
||||
textToDisplay: 'Text to display',
|
||||
url: 'To what URL should this link go?',
|
||||
openInNewWindow: 'Open in new window',
|
||||
useProtocol: 'Use default protocol',
|
||||
},
|
||||
table: {
|
||||
table: 'Table',
|
||||
addRowAbove: 'Add row above',
|
||||
addRowBelow: 'Add row below',
|
||||
addColLeft: 'Add column left',
|
||||
addColRight: 'Add column right',
|
||||
delRow: 'Delete row',
|
||||
delCol: 'Delete column',
|
||||
delTable: 'Delete table',
|
||||
},
|
||||
hr: {
|
||||
insert: 'Insert Horizontal Rule',
|
||||
},
|
||||
style: {
|
||||
style: 'Style',
|
||||
p: 'Normal',
|
||||
blockquote: 'Quote',
|
||||
pre: 'Code',
|
||||
h1: 'Header 1',
|
||||
h2: 'Header 2',
|
||||
h3: 'Header 3',
|
||||
h4: 'Header 4',
|
||||
h5: 'Header 5',
|
||||
h6: 'Header 6',
|
||||
},
|
||||
lists: {
|
||||
unordered: 'Unordered list',
|
||||
ordered: 'Ordered list',
|
||||
},
|
||||
options: {
|
||||
help: 'Help',
|
||||
fullscreen: 'Full Screen',
|
||||
codeview: 'Code View',
|
||||
},
|
||||
paragraph: {
|
||||
paragraph: 'Paragraph',
|
||||
outdent: 'Outdent',
|
||||
indent: 'Indent',
|
||||
left: 'Align left',
|
||||
center: 'Align center',
|
||||
right: 'Align right',
|
||||
justify: 'Justify full',
|
||||
},
|
||||
color: {
|
||||
recent: 'Recent Color',
|
||||
more: 'More Color',
|
||||
background: 'Background Color',
|
||||
foreground: 'Text Color',
|
||||
transparent: 'Transparent',
|
||||
setTransparent: 'Set transparent',
|
||||
reset: 'Reset',
|
||||
resetToDefault: 'Reset to default',
|
||||
cpSelect: 'Select',
|
||||
},
|
||||
shortcut: {
|
||||
shortcuts: 'Keyboard shortcuts',
|
||||
close: 'Close',
|
||||
textFormatting: 'Text formatting',
|
||||
action: 'Action',
|
||||
paragraphFormatting: 'Paragraph formatting',
|
||||
documentStyle: 'Document Style',
|
||||
extraKeys: 'Extra keys',
|
||||
},
|
||||
help: {
|
||||
'escape': 'Escape',
|
||||
'insertParagraph': 'Insert Paragraph',
|
||||
'undo': 'Undoes the last command',
|
||||
'redo': 'Redoes the last command',
|
||||
'tab': 'Tab',
|
||||
'untab': 'Untab',
|
||||
'bold': 'Set a bold style',
|
||||
'italic': 'Set a italic style',
|
||||
'underline': 'Set a underline style',
|
||||
'strikethrough': 'Set a strikethrough style',
|
||||
'removeFormat': 'Clean a style',
|
||||
'justifyLeft': 'Set left align',
|
||||
'justifyCenter': 'Set center align',
|
||||
'justifyRight': 'Set right align',
|
||||
'justifyFull': 'Set full align',
|
||||
'insertUnorderedList': 'Toggle unordered list',
|
||||
'insertOrderedList': 'Toggle ordered list',
|
||||
'outdent': 'Outdent on current paragraph',
|
||||
'indent': 'Indent on current paragraph',
|
||||
'formatPara': 'Change current block\'s format as a paragraph(P tag)',
|
||||
'formatH1': 'Change current block\'s format as H1',
|
||||
'formatH2': 'Change current block\'s format as H2',
|
||||
'formatH3': 'Change current block\'s format as H3',
|
||||
'formatH4': 'Change current block\'s format as H4',
|
||||
'formatH5': 'Change current block\'s format as H5',
|
||||
'formatH6': 'Change current block\'s format as H6',
|
||||
'insertHorizontalRule': 'Insert horizontal rule',
|
||||
'linkDialog.show': 'Show Link Dialog',
|
||||
},
|
||||
history: {
|
||||
undo: 'Undo',
|
||||
redo: 'Redo',
|
||||
},
|
||||
specialChar: {
|
||||
specialChar: 'SPECIAL CHARACTERS',
|
||||
select: 'Select Special characters',
|
||||
},
|
||||
output: {
|
||||
noSelection: 'No Selection Made!',
|
||||
},
|
||||
},
|
||||
});
|
||||
10
public/vendor/editor/src/js/bs3/settings.js
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
import $ from 'jquery';
|
||||
import ui from './ui';
|
||||
import '../base/settings.js';
|
||||
|
||||
import '../../styles/summernote-bs3.scss';
|
||||
|
||||
$.summernote = $.extend($.summernote, {
|
||||
ui_template: ui,
|
||||
interface: 'bs3',
|
||||
});
|
||||
250
public/vendor/editor/src/js/bs3/ui.js
vendored
Executable file
@@ -0,0 +1,250 @@
|
||||
import $ from 'jquery';
|
||||
import renderer from '../base/renderer';
|
||||
|
||||
const editor = renderer.create('<div class="note-editor note-frame panel panel-default"/>');
|
||||
const toolbar = renderer.create('<div class="note-toolbar panel-heading" role="toolbar"></div></div>');
|
||||
const editingArea = renderer.create('<div class="note-editing-area"/>');
|
||||
const codable = renderer.create('<textarea class="note-codable" aria-multiline="true"/>');
|
||||
const editable = renderer.create('<div class="note-editable" contentEditable="true" role="textbox" aria-multiline="true"/>');
|
||||
const statusbar = renderer.create([
|
||||
'<output class="note-status-output" role="status" aria-live="polite"/>',
|
||||
'<div class="note-statusbar" role="status">',
|
||||
'<div class="note-resizebar" aria-label="Resize">',
|
||||
'<div class="note-icon-bar"/>',
|
||||
'<div class="note-icon-bar"/>',
|
||||
'<div class="note-icon-bar"/>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
].join(''));
|
||||
|
||||
const airEditor = renderer.create('<div class="note-editor note-airframe"/>');
|
||||
const airEditable = renderer.create([
|
||||
'<div class="note-editable" contentEditable="true" role="textbox" aria-multiline="true"/>',
|
||||
'<output class="note-status-output" role="status" aria-live="polite"/>',
|
||||
].join(''));
|
||||
|
||||
const buttonGroup = renderer.create('<div class="note-btn-group btn-group">');
|
||||
|
||||
const dropdown = renderer.create('<ul class="note-dropdown-menu dropdown-menu">', function($node, options) {
|
||||
const markup = Array.isArray(options.items) ? options.items.map(function(item) {
|
||||
const value = (typeof item === 'string') ? item : (item.value || '');
|
||||
const content = options.template ? options.template(item) : item;
|
||||
const option = (typeof item === 'object') ? item.option : undefined;
|
||||
|
||||
const dataValue = 'data-value="' + value + '"';
|
||||
const dataOption = (option !== undefined) ? ' data-option="' + option + '"' : '';
|
||||
return '<li aria-label="' + value + '"><a href="#" ' + (dataValue + dataOption) + '>' + content + '</a></li>';
|
||||
}).join('') : options.items;
|
||||
|
||||
$node.html(markup).attr({ 'aria-label': options.title });
|
||||
});
|
||||
|
||||
const dropdownButtonContents = function(contents, options) {
|
||||
return contents + ' ' + icon(options.icons.caret, 'span');
|
||||
};
|
||||
|
||||
const dropdownCheck = renderer.create('<ul class="note-dropdown-menu dropdown-menu note-check">', function($node, options) {
|
||||
const markup = Array.isArray(options.items) ? options.items.map(function(item) {
|
||||
const value = (typeof item === 'string') ? item : (item.value || '');
|
||||
const content = options.template ? options.template(item) : item;
|
||||
return '<li aria-label="' + item + '"><a href="#" data-value="' + value + '">' + icon(options.checkClassName) + ' ' + content + '</a></li>';
|
||||
}).join('') : options.items;
|
||||
$node.html(markup).attr({ 'aria-label': options.title });
|
||||
});
|
||||
|
||||
const dialog = renderer.create('<div class="modal note-modal" aria-hidden="false" tabindex="-1" role="dialog"/>', function($node, options) {
|
||||
if (options.fade) {
|
||||
$node.addClass('fade');
|
||||
}
|
||||
$node.attr({
|
||||
'aria-label': options.title,
|
||||
});
|
||||
$node.html([
|
||||
'<div class="modal-dialog">',
|
||||
'<div class="modal-content">',
|
||||
(options.title ? '<div class="modal-header">' +
|
||||
'<button type="button" class="close" data-dismiss="modal" aria-label="Close" aria-hidden="true">×</button>' +
|
||||
'<h4 class="modal-title">' + options.title + '</h4>' +
|
||||
'</div>' : ''),
|
||||
'<div class="modal-body">' + options.body + '</div>',
|
||||
(options.footer ? '<div class="modal-footer">' + options.footer + '</div>' : ''),
|
||||
'</div>',
|
||||
'</div>',
|
||||
].join(''));
|
||||
});
|
||||
|
||||
const popover = renderer.create([
|
||||
'<div class="note-popover popover in">',
|
||||
'<div class="arrow"/>',
|
||||
'<div class="popover-content note-children-container"/>',
|
||||
'</div>',
|
||||
].join(''), function($node, options) {
|
||||
const direction = typeof options.direction !== 'undefined' ? options.direction : 'bottom';
|
||||
|
||||
$node.addClass(direction);
|
||||
|
||||
if (options.hideArrow) {
|
||||
$node.find('.arrow').hide();
|
||||
}
|
||||
});
|
||||
|
||||
const checkbox = renderer.create('<div class="checkbox"></div>', function($node, options) {
|
||||
$node.html([
|
||||
'<label' + (options.id ? ' for="note-' + options.id + '"' : '') + '>',
|
||||
'<input type="checkbox"' + (options.id ? ' id="note-' + options.id + '"' : ''),
|
||||
(options.checked ? ' checked' : ''),
|
||||
' aria-checked="' + (options.checked ? 'true' : 'false') + '"/>',
|
||||
(options.text ? options.text : ''),
|
||||
'</label>',
|
||||
].join(''));
|
||||
});
|
||||
|
||||
const icon = function(iconClassName, tagName) {
|
||||
tagName = tagName || 'i';
|
||||
return '<' + tagName + ' class="' + iconClassName + '"/>';
|
||||
};
|
||||
|
||||
const ui = function(editorOptions) {
|
||||
return {
|
||||
editor: editor,
|
||||
toolbar: toolbar,
|
||||
editingArea: editingArea,
|
||||
codable: codable,
|
||||
editable: editable,
|
||||
statusbar: statusbar,
|
||||
airEditor: airEditor,
|
||||
airEditable: airEditable,
|
||||
buttonGroup: buttonGroup,
|
||||
dropdown: dropdown,
|
||||
dropdownButtonContents: dropdownButtonContents,
|
||||
dropdownCheck: dropdownCheck,
|
||||
dialog: dialog,
|
||||
popover: popover,
|
||||
checkbox: checkbox,
|
||||
icon: icon,
|
||||
options: editorOptions,
|
||||
|
||||
palette: function($node, options) {
|
||||
return renderer.create('<div class="note-color-palette"/>', function($node, options) {
|
||||
const contents = [];
|
||||
for (let row = 0, rowSize = options.colors.length; row < rowSize; row++) {
|
||||
const eventName = options.eventName;
|
||||
const colors = options.colors[row];
|
||||
const colorsName = options.colorsName[row];
|
||||
const buttons = [];
|
||||
for (let col = 0, colSize = colors.length; col < colSize; col++) {
|
||||
const color = colors[col];
|
||||
const colorName = colorsName[col];
|
||||
buttons.push([
|
||||
'<button type="button" class="note-color-btn"',
|
||||
'style="background-color:', color, '" ',
|
||||
'data-event="', eventName, '" ',
|
||||
'data-value="', color, '" ',
|
||||
'title="', colorName, '" ',
|
||||
'aria-label="', colorName, '" ',
|
||||
'data-toggle="button" tabindex="-1"></button>',
|
||||
].join(''));
|
||||
}
|
||||
contents.push('<div class="note-color-row">' + buttons.join('') + '</div>');
|
||||
}
|
||||
$node.html(contents.join(''));
|
||||
|
||||
if (options.tooltip) {
|
||||
$node.find('.note-color-btn').tooltip({
|
||||
container: options.container || editorOptions.container,
|
||||
trigger: 'hover',
|
||||
placement: 'bottom',
|
||||
});
|
||||
}
|
||||
})($node, options);
|
||||
},
|
||||
|
||||
button: function($node, options) {
|
||||
return renderer.create('<button type="button" class="note-btn btn btn-default btn-sm" tabindex="-1">', function($node, options) {
|
||||
if (options && options.tooltip) {
|
||||
$node.attr({
|
||||
title: options.tooltip,
|
||||
'aria-label': options.tooltip,
|
||||
}).tooltip({
|
||||
container: options.container || editorOptions.container,
|
||||
trigger: 'hover',
|
||||
placement: 'bottom',
|
||||
}).on('click', (e) => {
|
||||
$(e.currentTarget).tooltip('hide');
|
||||
});
|
||||
}
|
||||
})($node, options);
|
||||
},
|
||||
|
||||
toggleBtn: function($btn, isEnable) {
|
||||
$btn.toggleClass('disabled', !isEnable);
|
||||
$btn.attr('disabled', !isEnable);
|
||||
},
|
||||
|
||||
toggleBtnActive: function($btn, isActive) {
|
||||
$btn.toggleClass('active', isActive);
|
||||
},
|
||||
|
||||
onDialogShown: function($dialog, handler) {
|
||||
$dialog.one('shown.bs.modal', handler);
|
||||
},
|
||||
|
||||
onDialogHidden: function($dialog, handler) {
|
||||
$dialog.one('hidden.bs.modal', handler);
|
||||
},
|
||||
|
||||
showDialog: function($dialog) {
|
||||
$dialog.modal('show');
|
||||
},
|
||||
|
||||
hideDialog: function($dialog) {
|
||||
$dialog.modal('hide');
|
||||
},
|
||||
|
||||
createLayout: function($note) {
|
||||
const $editor = (editorOptions.airMode ? airEditor([
|
||||
editingArea([
|
||||
codable(),
|
||||
airEditable(),
|
||||
]),
|
||||
]) : (editorOptions.toolbarPosition === 'bottom'
|
||||
? editor([
|
||||
editingArea([
|
||||
codable(),
|
||||
editable(),
|
||||
]),
|
||||
toolbar(),
|
||||
statusbar(),
|
||||
])
|
||||
: editor([
|
||||
toolbar(),
|
||||
editingArea([
|
||||
codable(),
|
||||
editable(),
|
||||
]),
|
||||
statusbar(),
|
||||
])
|
||||
)).render();
|
||||
|
||||
$editor.insertAfter($note);
|
||||
|
||||
return {
|
||||
note: $note,
|
||||
editor: $editor,
|
||||
toolbar: $editor.find('.note-toolbar'),
|
||||
editingArea: $editor.find('.note-editing-area'),
|
||||
editable: $editor.find('.note-editable'),
|
||||
codable: $editor.find('.note-codable'),
|
||||
statusbar: $editor.find('.note-statusbar'),
|
||||
};
|
||||
},
|
||||
|
||||
removeLayout: function($note, layoutInfo) {
|
||||
$note.html(layoutInfo.editable.html());
|
||||
layoutInfo.editor.remove();
|
||||
$note.show();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default ui;
|
||||
16
public/vendor/editor/src/js/bs4/settings.js
vendored
Executable file
@@ -0,0 +1,16 @@
|
||||
import $ from 'jquery';
|
||||
import ui from './ui';
|
||||
import '../base/settings.js';
|
||||
|
||||
import '../../styles/summernote-bs4.scss';
|
||||
|
||||
$.summernote = $.extend($.summernote, {
|
||||
ui_template: ui,
|
||||
interface: 'bs4',
|
||||
});
|
||||
|
||||
$.summernote.options.styleTags = [
|
||||
'p',
|
||||
{ title: 'Blockquote', tag: 'blockquote', className: 'blockquote', value: 'blockquote' },
|
||||
'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||
];
|
||||
251
public/vendor/editor/src/js/bs4/ui.js
vendored
Executable file
@@ -0,0 +1,251 @@
|
||||
import $ from 'jquery';
|
||||
import renderer from '../base/renderer';
|
||||
|
||||
const editor = renderer.create('<div class="note-editor note-frame card"/>');
|
||||
const toolbar = renderer.create('<div class="note-toolbar card-header" role="toolbar"></div>');
|
||||
const editingArea = renderer.create('<div class="note-editing-area"/>');
|
||||
const codable = renderer.create('<textarea class="note-codable" aria-multiline="true"/>');
|
||||
const editable = renderer.create('<div class="note-editable card-block" contentEditable="true" role="textbox" aria-multiline="true"/>');
|
||||
const statusbar = renderer.create([
|
||||
'<output class="note-status-output" role="status" aria-live="polite"/>',
|
||||
'<div class="note-statusbar" role="status">',
|
||||
'<div class="note-resizebar" aria-label="Resize">',
|
||||
'<div class="note-icon-bar"/>',
|
||||
'<div class="note-icon-bar"/>',
|
||||
'<div class="note-icon-bar"/>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
].join(''));
|
||||
|
||||
const airEditor = renderer.create('<div class="note-editor note-airframe"/>');
|
||||
const airEditable = renderer.create([
|
||||
'<div class="note-editable" contentEditable="true" role="textbox" aria-multiline="true"/>',
|
||||
'<output class="note-status-output" role="status" aria-live="polite"/>',
|
||||
].join(''));
|
||||
|
||||
const buttonGroup = renderer.create('<div class="note-btn-group btn-group">');
|
||||
|
||||
const dropdown = renderer.create('<div class="note-dropdown-menu dropdown-menu" role="list">', function($node, options) {
|
||||
const markup = Array.isArray(options.items) ? options.items.map(function(item) {
|
||||
const value = (typeof item === 'string') ? item : (item.value || '');
|
||||
const content = options.template ? options.template(item) : item;
|
||||
const option = (typeof item === 'object') ? item.option : undefined;
|
||||
|
||||
const dataValue = 'data-value="' + value + '"';
|
||||
const dataOption = (option !== undefined) ? ' data-option="' + option + '"' : '';
|
||||
return '<a class="dropdown-item" href="#" ' + (dataValue + dataOption) + ' role="listitem" aria-label="' + value + '">' + content + '</a>';
|
||||
}).join('') : options.items;
|
||||
|
||||
$node.html(markup).attr({ 'aria-label': options.title });
|
||||
});
|
||||
|
||||
const dropdownButtonContents = function(contents) {
|
||||
return contents;
|
||||
};
|
||||
|
||||
const dropdownCheck = renderer.create('<div class="note-dropdown-menu dropdown-menu note-check" role="list">', function($node, options) {
|
||||
const markup = Array.isArray(options.items) ? options.items.map(function(item) {
|
||||
const value = (typeof item === 'string') ? item : (item.value || '');
|
||||
const content = options.template ? options.template(item) : item;
|
||||
return '<a class="dropdown-item" href="#" data-value="' + value + '" role="listitem" aria-label="' + item + '">' + icon(options.checkClassName) + ' ' + content + '</a>';
|
||||
}).join('') : options.items;
|
||||
$node.html(markup).attr({ 'aria-label': options.title });
|
||||
});
|
||||
|
||||
const dialog = renderer.create('<div class="modal note-modal" aria-hidden="false" tabindex="-1" role="dialog"/>', function($node, options) {
|
||||
if (options.fade) {
|
||||
$node.addClass('fade');
|
||||
}
|
||||
$node.attr({
|
||||
'aria-label': options.title,
|
||||
});
|
||||
$node.html([
|
||||
'<div class="modal-dialog">',
|
||||
'<div class="modal-content">',
|
||||
(options.title ? '<div class="modal-header">' +
|
||||
'<h4 class="modal-title">' + options.title + '</h4>' +
|
||||
'<button type="button" class="close" data-dismiss="modal" aria-label="Close" aria-hidden="true">×</button>' +
|
||||
'</div>' : ''),
|
||||
'<div class="modal-body">' + options.body + '</div>',
|
||||
(options.footer ? '<div class="modal-footer">' + options.footer + '</div>' : ''),
|
||||
'</div>',
|
||||
'</div>',
|
||||
].join(''));
|
||||
});
|
||||
|
||||
const popover = renderer.create([
|
||||
'<div class="note-popover popover in">',
|
||||
'<div class="arrow"/>',
|
||||
'<div class="popover-content note-children-container"/>',
|
||||
'</div>',
|
||||
].join(''), function($node, options) {
|
||||
const direction = typeof options.direction !== 'undefined' ? options.direction : 'bottom';
|
||||
|
||||
$node.addClass(direction);
|
||||
|
||||
if (options.hideArrow) {
|
||||
$node.find('.arrow').hide();
|
||||
}
|
||||
});
|
||||
|
||||
const checkbox = renderer.create('<div class="form-check"></div>', function($node, options) {
|
||||
$node.html([
|
||||
'<label class="form-check-label"' + (options.id ? ' for="note-' + options.id + '"' : '') + '>',
|
||||
'<input type="checkbox" class="form-check-input"' + (options.id ? ' id="note-' + options.id + '"' : ''),
|
||||
(options.checked ? ' checked' : ''),
|
||||
' aria-label="' + (options.text ? options.text : '') + '"',
|
||||
' aria-checked="' + (options.checked ? 'true' : 'false') + '"/>',
|
||||
' ' + (options.text ? options.text : '') +
|
||||
'</label>',
|
||||
].join(''));
|
||||
});
|
||||
|
||||
const icon = function(iconClassName, tagName) {
|
||||
tagName = tagName || 'i';
|
||||
return '<' + tagName + ' class="' + iconClassName + '"/>';
|
||||
};
|
||||
|
||||
const ui = function(editorOptions) {
|
||||
return {
|
||||
editor: editor,
|
||||
toolbar: toolbar,
|
||||
editingArea: editingArea,
|
||||
codable: codable,
|
||||
editable: editable,
|
||||
statusbar: statusbar,
|
||||
airEditor: airEditor,
|
||||
airEditable: airEditable,
|
||||
buttonGroup: buttonGroup,
|
||||
dropdown: dropdown,
|
||||
dropdownButtonContents: dropdownButtonContents,
|
||||
dropdownCheck: dropdownCheck,
|
||||
dialog: dialog,
|
||||
popover: popover,
|
||||
icon: icon,
|
||||
checkbox: checkbox,
|
||||
options: editorOptions,
|
||||
|
||||
palette: function($node, options) {
|
||||
return renderer.create('<div class="note-color-palette"/>', function($node, options) {
|
||||
const contents = [];
|
||||
for (let row = 0, rowSize = options.colors.length; row < rowSize; row++) {
|
||||
const eventName = options.eventName;
|
||||
const colors = options.colors[row];
|
||||
const colorsName = options.colorsName[row];
|
||||
const buttons = [];
|
||||
for (let col = 0, colSize = colors.length; col < colSize; col++) {
|
||||
const color = colors[col];
|
||||
const colorName = colorsName[col];
|
||||
buttons.push([
|
||||
'<button type="button" class="note-color-btn"',
|
||||
'style="background-color:', color, '" ',
|
||||
'data-event="', eventName, '" ',
|
||||
'data-value="', color, '" ',
|
||||
'title="', colorName, '" ',
|
||||
'aria-label="', colorName, '" ',
|
||||
'data-toggle="button" tabindex="-1"></button>',
|
||||
].join(''));
|
||||
}
|
||||
contents.push('<div class="note-color-row">' + buttons.join('') + '</div>');
|
||||
}
|
||||
$node.html(contents.join(''));
|
||||
|
||||
if (options.tooltip) {
|
||||
$node.find('.note-color-btn').tooltip({
|
||||
container: options.container || editorOptions.container,
|
||||
trigger: 'hover',
|
||||
placement: 'bottom',
|
||||
});
|
||||
}
|
||||
})($node, options);
|
||||
},
|
||||
|
||||
button: function($node, options) {
|
||||
return renderer.create('<button type="button" class="note-btn btn btn-light btn-sm" tabindex="-1">', function($node, options) {
|
||||
if (options && options.tooltip) {
|
||||
$node.attr({
|
||||
title: options.tooltip,
|
||||
'aria-label': options.tooltip,
|
||||
}).tooltip({
|
||||
container: options.container || editorOptions.container,
|
||||
trigger: 'hover',
|
||||
placement: 'bottom',
|
||||
}).on('click', (e) => {
|
||||
$(e.currentTarget).tooltip('hide');
|
||||
});
|
||||
}
|
||||
})($node, options);
|
||||
},
|
||||
|
||||
toggleBtn: function($btn, isEnable) {
|
||||
$btn.toggleClass('disabled', !isEnable);
|
||||
$btn.attr('disabled', !isEnable);
|
||||
},
|
||||
|
||||
toggleBtnActive: function($btn, isActive) {
|
||||
$btn.toggleClass('active', isActive);
|
||||
},
|
||||
|
||||
onDialogShown: function($dialog, handler) {
|
||||
$dialog.one('shown.bs.modal', handler);
|
||||
},
|
||||
|
||||
onDialogHidden: function($dialog, handler) {
|
||||
$dialog.one('hidden.bs.modal', handler);
|
||||
},
|
||||
|
||||
showDialog: function($dialog) {
|
||||
$dialog.modal('show');
|
||||
},
|
||||
|
||||
hideDialog: function($dialog) {
|
||||
$dialog.modal('hide');
|
||||
},
|
||||
|
||||
createLayout: function($note) {
|
||||
const $editor = (editorOptions.airMode ? airEditor([
|
||||
editingArea([
|
||||
codable(),
|
||||
airEditable(),
|
||||
]),
|
||||
]) : (editorOptions.toolbarPosition === 'bottom'
|
||||
? editor([
|
||||
editingArea([
|
||||
codable(),
|
||||
editable(),
|
||||
]),
|
||||
toolbar(),
|
||||
statusbar(),
|
||||
])
|
||||
: editor([
|
||||
toolbar(),
|
||||
editingArea([
|
||||
codable(),
|
||||
editable(),
|
||||
]),
|
||||
statusbar(),
|
||||
])
|
||||
)).render();
|
||||
|
||||
$editor.insertAfter($note);
|
||||
|
||||
return {
|
||||
note: $note,
|
||||
editor: $editor,
|
||||
toolbar: $editor.find('.note-toolbar'),
|
||||
editingArea: $editor.find('.note-editing-area'),
|
||||
editable: $editor.find('.note-editable'),
|
||||
codable: $editor.find('.note-codable'),
|
||||
statusbar: $editor.find('.note-statusbar'),
|
||||
};
|
||||
},
|
||||
|
||||
removeLayout: function($note, layoutInfo) {
|
||||
$note.html(layoutInfo.editable.html());
|
||||
layoutInfo.editor.remove();
|
||||
$note.show();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default ui;
|
||||
10
public/vendor/editor/src/js/lite/settings.js
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
import $ from 'jquery';
|
||||
import ui from './ui';
|
||||
import '../base/settings.js';
|
||||
|
||||
import '../../styles/summernote-lite.scss';
|
||||
|
||||
$.summernote = $.extend($.summernote, {
|
||||
ui_template: ui,
|
||||
interface: 'lite',
|
||||
});
|
||||