1+ ( function ( global , factory ) {
2+ typeof exports === 'object' && typeof module !== 'undefined' ? module . exports = factory ( ) :
3+ typeof define === 'function' && define . amd ? define ( factory ) :
4+ ( global . Pell = factory ( ) ) ;
5+ } ( this , ( function ( ) { 'use strict' ;
6+
7+ const defaultParagraphSeparatorString = 'defaultParagraphSeparator'
8+ const formatBlock = 'formatBlock'
9+ const addEventListener = ( parent , type , listener ) => parent . addEventListener ( type , listener )
10+ const appendChild = ( parent , child ) => parent . appendChild ( child )
11+ const createElement = tag => document . createElement ( tag )
12+ const queryCommandState = command => document . queryCommandState ( command )
13+ const queryCommandValue = command => document . queryCommandValue ( command )
14+ const exec = ( command , value = null ) => document . execCommand ( command , false , value )
15+
16+ const defaultActions = {
17+ bold : {
18+ icon : '<b>B</b>' ,
19+ title : 'Bold' ,
20+ state : ( ) => queryCommandState ( 'bold' ) ,
21+ result : ( ) => exec ( 'bold' )
22+ } ,
23+ italic : {
24+ icon : '<i>I</i>' ,
25+ title : 'Italic' ,
26+ state : ( ) => queryCommandState ( 'italic' ) ,
27+ result : ( ) => exec ( 'italic' )
28+ } ,
29+ underline : {
30+ icon : '<u>U</u>' ,
31+ title : 'Underline' ,
32+ state : ( ) => queryCommandState ( 'underline' ) ,
33+ result : ( ) => exec ( 'underline' )
34+ } ,
35+ strikethrough : {
36+ icon : '<strike>S</strike>' ,
37+ title : 'Strike-through' ,
38+ state : ( ) => queryCommandState ( 'strikeThrough' ) ,
39+ result : ( ) => exec ( 'strikeThrough' )
40+ } ,
41+ heading1 : {
42+ icon : '<b>H<sub>1</sub></b>' ,
43+ title : 'Heading 1' ,
44+ result : ( ) => exec ( formatBlock , '<h1>' )
45+ } ,
46+ heading2 : {
47+ icon : '<b>H<sub>2</sub></b>' ,
48+ title : 'Heading 2' ,
49+ result : ( ) => exec ( formatBlock , '<h2>' )
50+ } ,
51+ paragraph : {
52+ icon : '¶' ,
53+ title : 'Paragraph' ,
54+ result : ( ) => exec ( formatBlock , '<p>' )
55+ } ,
56+ quote : {
57+ icon : '“ ”' ,
58+ title : 'Quote' ,
59+ result : ( ) => exec ( formatBlock , '<blockquote>' )
60+ } ,
61+ olist : {
62+ icon : '#' ,
63+ title : 'Ordered List' ,
64+ result : ( ) => exec ( 'insertOrderedList' )
65+ } ,
66+ ulist : {
67+ icon : '•' ,
68+ title : 'Unordered List' ,
69+ result : ( ) => exec ( 'insertUnorderedList' )
70+ } ,
71+ code : {
72+ icon : '</>' ,
73+ title : 'Code' ,
74+ result : ( ) => exec ( formatBlock , '<pre>' )
75+ } ,
76+ line : {
77+ icon : '―' ,
78+ title : 'Horizontal Line' ,
79+ result : ( ) => exec ( 'insertHorizontalRule' )
80+ } ,
81+ link : {
82+ icon : '🔗' ,
83+ title : 'Link' ,
84+ result : ( ) => navigator . clipboard . readText ( ) . then ( url => exec ( 'createLink' , url ) )
85+ } ,
86+ image : {
87+ icon : '📷' ,
88+ title : 'Image' ,
89+ result : ( ) => {
90+ navigator . clipboard . readText ( ) . then ( url => exec ( 'insertImage' , url ) )
91+ exec ( 'enableObjectResizing' )
92+ }
93+ }
94+ }
95+
96+ const defaultClasses = {
97+ actionbar : 'pell-actionbar' ,
98+ button : 'pell-button' ,
99+ content : 'pell-content' ,
100+ selected : 'pell-button-selected'
101+ }
102+
103+ const init = settings => {
104+ const actions = settings . actions
105+ ? (
106+ settings . actions . map ( action => {
107+ if ( typeof action === 'string' ) return defaultActions [ action ]
108+ else if ( defaultActions [ action . name ] ) return { ...defaultActions [ action . name ] , ...action }
109+ return action
110+ } )
111+ )
112+ : Object . keys ( defaultActions ) . map ( action => defaultActions [ action ] )
113+
114+ const classes = { ...defaultClasses , ...settings . classes }
115+
116+ const defaultParagraphSeparator = settings [ defaultParagraphSeparatorString ] || 'div'
117+
118+ const actionbar = createElement ( 'div' )
119+ actionbar . className = classes . actionbar
120+ appendChild ( settings . element , actionbar )
121+
122+ const content = settings . element . content = createElement ( 'div' )
123+ content . contentEditable = true
124+ content . className = classes . content
125+ content . oninput = ( { target : { firstChild } } ) => {
126+ if ( firstChild && firstChild . nodeType === 3 ) exec ( formatBlock , `<${ defaultParagraphSeparator } >` )
127+ else if ( content . innerHTML === '<br>' ) content . innerHTML = ''
128+ settings . onChange ( content . innerHTML )
129+ }
130+ content . onkeydown = event => {
131+ if ( event . key === 'Enter' && queryCommandValue ( formatBlock ) === 'blockquote' ) {
132+ setTimeout ( ( ) => exec ( formatBlock , `<${ defaultParagraphSeparator } >` ) , 0 )
133+ }
134+ }
135+ appendChild ( settings . element , content )
136+
137+ actions . forEach ( action => {
138+ const button = createElement ( 'button' )
139+ button . className = classes . button
140+ button . innerHTML = action . icon
141+ button . title = action . title
142+ button . setAttribute ( 'type' , 'button' )
143+ button . onclick = ( ) => action . result ( ) && content . focus ( )
144+
145+ if ( action . state ) {
146+ const handler = ( ) => button . classList [ action . state ( ) ? 'add' : 'remove' ] ( classes . selected )
147+ addEventListener ( content , 'keyup' , handler )
148+ addEventListener ( content , 'mouseup' , handler )
149+ addEventListener ( button , 'click' , handler )
150+ }
151+
152+ appendChild ( actionbar , button )
153+ } )
154+
155+ if ( settings . styleWithCSS ) exec ( 'styleWithCSS' )
156+ exec ( defaultParagraphSeparatorString , defaultParagraphSeparator )
157+
158+ return settings . element
159+ }
160+
161+ return { exec, init}
162+
163+ } ) ) ) ;
0 commit comments