scratch – Diff between revs 58 and 125
?pathlinks?
Rev 58 | Rev 125 | |||
---|---|---|---|---|
1 | /** |
1 | /** |
|
2 | * Trumbowyg v2.5.1 - A lightweight WYSIWYG editor |
2 | * Trumbowyg v2.6.0 - A lightweight WYSIWYG editor |
|
3 | * Trumbowyg core file |
3 | * Trumbowyg core file |
|
4 | * ------------------------ |
4 | * ------------------------ |
|
5 | * @link http://alex-d.github.io/Trumbowyg |
5 | * @link http://alex-d.github.io/Trumbowyg |
|
6 | * @license MIT |
6 | * @license MIT |
|
7 | * @author Alexandre Demode (Alex-D) |
7 | * @author Alexandre Demode (Alex-D) |
|
8 | * Twitter : @AlexandreDemode |
8 | * Twitter : @AlexandreDemode |
|
9 | * Website : alex-d.fr |
9 | * Website : alex-d.fr |
|
10 | */ |
10 | */ |
|
11 | |
11 | |
|
12 | jQuery.trumbowyg = { |
12 | jQuery.trumbowyg = { |
|
13 | langs: { |
13 | langs: { |
|
14 | en: { |
14 | en: { |
|
15 | viewHTML: 'View HTML', |
15 | viewHTML: 'View HTML', |
|
16 | |
16 | |
|
17 | undo: 'Undo', |
17 | undo: 'Undo', |
|
18 | redo: 'Redo', |
18 | redo: 'Redo', |
|
19 | |
19 | |
|
20 | formatting: 'Formatting', |
20 | formatting: 'Formatting', |
|
21 | p: 'Paragraph', |
21 | p: 'Paragraph', |
|
22 | blockquote: 'Quote', |
22 | blockquote: 'Quote', |
|
23 | code: 'Code', |
23 | code: 'Code', |
|
24 | header: 'Header', |
24 | header: 'Header', |
|
25 | |
25 | |
|
26 | bold: 'Bold', |
26 | bold: 'Bold', |
|
27 | italic: 'Italic', |
27 | italic: 'Italic', |
|
28 | strikethrough: 'Stroke', |
28 | strikethrough: 'Stroke', |
|
29 | underline: 'Underline', |
29 | underline: 'Underline', |
|
30 | |
30 | |
|
31 | strong: 'Strong', |
31 | strong: 'Strong', |
|
32 | em: 'Emphasis', |
32 | em: 'Emphasis', |
|
33 | del: 'Deleted', |
33 | del: 'Deleted', |
|
34 | |
34 | |
|
35 | superscript: 'Superscript', |
35 | superscript: 'Superscript', |
|
36 | subscript: 'Subscript', |
36 | subscript: 'Subscript', |
|
37 | |
37 | |
|
38 | unorderedList: 'Unordered list', |
38 | unorderedList: 'Unordered list', |
|
39 | orderedList: 'Ordered list', |
39 | orderedList: 'Ordered list', |
|
40 | |
40 | |
|
41 | insertImage: 'Insert Image', |
41 | insertImage: 'Insert Image', |
|
42 | link: 'Link', |
42 | link: 'Link', |
|
43 | createLink: 'Insert link', |
43 | createLink: 'Insert link', |
|
44 | unlink: 'Remove link', |
44 | unlink: 'Remove link', |
|
45 | |
45 | |
|
46 | justifyLeft: 'Align Left', |
46 | justifyLeft: 'Align Left', |
|
47 | justifyCenter: 'Align Center', |
47 | justifyCenter: 'Align Center', |
|
48 | justifyRight: 'Align Right', |
48 | justifyRight: 'Align Right', |
|
49 | justifyFull: 'Align Justify', |
49 | justifyFull: 'Align Justify', |
|
50 | |
50 | |
|
51 | horizontalRule: 'Insert horizontal rule', |
51 | horizontalRule: 'Insert horizontal rule', |
|
52 | removeformat: 'Remove format', |
52 | removeformat: 'Remove format', |
|
53 | |
53 | |
|
54 | fullscreen: 'Fullscreen', |
54 | fullscreen: 'Fullscreen', |
|
55 | |
55 | |
|
56 | close: 'Close', |
56 | close: 'Close', |
|
57 | |
57 | |
|
58 | submit: 'Confirm', |
58 | submit: 'Confirm', |
|
59 | reset: 'Cancel', |
59 | reset: 'Cancel', |
|
60 | |
60 | |
|
61 | required: 'Required', |
61 | required: 'Required', |
|
62 | description: 'Description', |
62 | description: 'Description', |
|
63 | title: 'Title', |
63 | title: 'Title', |
|
64 | text: 'Text', |
64 | text: 'Text', |
|
65 | target: 'Target' |
65 | target: 'Target' |
|
66 | } |
66 | } |
|
67 | }, |
67 | }, |
|
68 | |
68 | |
|
69 | // Plugins |
69 | // Plugins |
|
70 | plugins: {}, |
70 | plugins: {}, |
|
71 | |
71 | |
|
72 | // SVG Path globally |
72 | // SVG Path globally |
|
73 | svgPath: null, |
73 | svgPath: null, |
|
74 | |
74 | |
|
75 | hideButtonTexts: null |
75 | hideButtonTexts: null |
|
76 | }; |
76 | }; |
|
77 | |
77 | |
|
78 | |
78 | |
|
79 | (function (navigator, window, document, $) { |
79 | (function (navigator, window, document, $) { |
|
80 | 'use strict'; |
80 | 'use strict'; |
|
81 | |
81 | |
|
82 | $.fn.trumbowyg = function (options, params) { |
82 | $.fn.trumbowyg = function (options, params) { |
|
83 | var trumbowygDataName = 'trumbowyg'; |
83 | var trumbowygDataName = 'trumbowyg'; |
|
84 | if (options === Object(options) || !options) { |
84 | if (options === Object(options) || !options) { |
|
85 | return this.each(function () { |
85 | return this.each(function () { |
|
86 | if (!$(this).data(trumbowygDataName)) { |
86 | if (!$(this).data(trumbowygDataName)) { |
|
87 | $(this).data(trumbowygDataName, new Trumbowyg(this, options)); |
87 | $(this).data(trumbowygDataName, new Trumbowyg(this, options)); |
|
88 | } |
88 | } |
|
89 | }); |
89 | }); |
|
90 | } |
90 | } |
|
91 | if (this.length === 1) { |
91 | if (this.length === 1) { |
|
92 | try { |
92 | try { |
|
93 | var t = $(this).data(trumbowygDataName); |
93 | var t = $(this).data(trumbowygDataName); |
|
94 | switch (options) { |
94 | switch (options) { |
|
95 | // Exec command |
95 | // Exec command |
|
96 | case 'execCmd': |
96 | case 'execCmd': |
|
97 | return t.execCmd(params.cmd, params.param, params.forceCss); |
97 | return t.execCmd(params.cmd, params.param, params.forceCss); |
|
98 | |
98 | |
|
99 | // Modal box |
99 | // Modal box |
|
100 | case 'openModal': |
100 | case 'openModal': |
|
101 | return t.openModal(params.title, params.content); |
101 | return t.openModal(params.title, params.content); |
|
102 | case 'closeModal': |
102 | case 'closeModal': |
|
103 | return t.closeModal(); |
103 | return t.closeModal(); |
|
104 | case 'openModalInsert': |
104 | case 'openModalInsert': |
|
105 | return t.openModalInsert(params.title, params.fields, params.callback); |
105 | return t.openModalInsert(params.title, params.fields, params.callback); |
|
106 | |
106 | |
|
107 | // Range |
107 | // Range |
|
108 | case 'saveRange': |
108 | case 'saveRange': |
|
109 | return t.saveRange(); |
109 | return t.saveRange(); |
|
110 | case 'getRange': |
110 | case 'getRange': |
|
111 | return t.range; |
111 | return t.range; |
|
112 | case 'getRangeText': |
112 | case 'getRangeText': |
|
113 | return t.getRangeText(); |
113 | return t.getRangeText(); |
|
114 | case 'restoreRange': |
114 | case 'restoreRange': |
|
115 | return t.restoreRange(); |
115 | return t.restoreRange(); |
|
116 | |
116 | |
|
117 | // Enable/disable |
117 | // Enable/disable |
|
118 | case 'enable': |
118 | case 'enable': |
|
119 | return t.toggleDisable(false); |
119 | return t.toggleDisable(false); |
|
120 | case 'disable': |
120 | case 'disable': |
|
121 | return t.toggleDisable(true); |
121 | return t.toggleDisable(true); |
|
122 | |
122 | |
|
123 | // Destroy |
123 | // Destroy |
|
124 | case 'destroy': |
124 | case 'destroy': |
|
125 | return t.destroy(); |
125 | return t.destroy(); |
|
126 | |
126 | |
|
127 | // Empty |
127 | // Empty |
|
128 | case 'empty': |
128 | case 'empty': |
|
129 | return t.empty(); |
129 | return t.empty(); |
|
130 | |
130 | |
|
131 | // HTML |
131 | // HTML |
|
132 | case 'html': |
132 | case 'html': |
|
133 | return t.html(params); |
133 | return t.html(params); |
|
134 | } |
134 | } |
|
135 | } catch (c) { |
135 | } catch (c) { |
|
136 | } |
136 | } |
|
137 | } |
137 | } |
|
138 | |
138 | |
|
139 | return false; |
139 | return false; |
|
140 | }; |
140 | }; |
|
141 | |
141 | |
|
142 | // @param: editorElem is the DOM element |
142 | // @param: editorElem is the DOM element |
|
143 | var Trumbowyg = function (editorElem, options) { |
143 | var Trumbowyg = function (editorElem, options) { |
|
144 | var t = this, |
144 | var t = this, |
|
145 | trumbowygIconsId = 'trumbowyg-icons'; |
145 | trumbowygIconsId = 'trumbowyg-icons'; |
|
146 | |
146 | |
|
147 | // Get the document of the element. It use to makes the plugin |
147 | // Get the document of the element. It use to makes the plugin |
|
148 | // compatible on iframes. |
148 | // compatible on iframes. |
|
149 | t.doc = editorElem.ownerDocument || document; |
149 | t.doc = editorElem.ownerDocument || document; |
|
150 | |
150 | |
|
151 | // jQuery object of the editor |
151 | // jQuery object of the editor |
|
152 | t.$ta = $(editorElem); // $ta : Textarea |
152 | t.$ta = $(editorElem); // $ta : Textarea |
|
153 | t.$c = $(editorElem); // $c : creator |
153 | t.$c = $(editorElem); // $c : creator |
|
154 | |
154 | |
|
155 | options = options || {}; |
155 | options = options || {}; |
|
156 | |
156 | |
|
157 | // Localization management |
157 | // Localization management |
|
158 | if (options.lang != null || $.trumbowyg.langs[options.lang] != null) { |
158 | if (options.lang != null || $.trumbowyg.langs[options.lang] != null) { |
|
159 | t.lang = $.extend(true, {}, $.trumbowyg.langs.en, $.trumbowyg.langs[options.lang]); |
159 | t.lang = $.extend(true, {}, $.trumbowyg.langs.en, $.trumbowyg.langs[options.lang]); |
|
160 | } else { |
160 | } else { |
|
161 | t.lang = $.trumbowyg.langs.en; |
161 | t.lang = $.trumbowyg.langs.en; |
|
162 | } |
162 | } |
|
163 | |
163 | |
|
164 | t.hideButtonTexts = $.trumbowyg.hideButtonTexts != null ? $.trumbowyg.hideButtonTexts : options.hideButtonTexts; |
164 | t.hideButtonTexts = $.trumbowyg.hideButtonTexts != null ? $.trumbowyg.hideButtonTexts : options.hideButtonTexts; |
|
165 | |
165 | |
|
166 | // SVG path |
166 | // SVG path |
|
167 | var svgPathOption = $.trumbowyg.svgPath != null ? $.trumbowyg.svgPath : options.svgPath; |
167 | var svgPathOption = $.trumbowyg.svgPath != null ? $.trumbowyg.svgPath : options.svgPath; |
|
168 | t.hasSvg = svgPathOption !== false; |
168 | t.hasSvg = svgPathOption !== false; |
|
169 | t.svgPath = !!t.doc.querySelector('base') ? window.location.href.split('#')[0] : ''; |
169 | t.svgPath = !!t.doc.querySelector('base') ? window.location.href.split('#')[0] : ''; |
|
170 | if ($('#' + trumbowygIconsId, t.doc).length === 0 && svgPathOption !== false) { |
170 | if ($('#' + trumbowygIconsId, t.doc).length === 0 && svgPathOption !== false) { |
|
171 | if (svgPathOption == null) { |
171 | if (svgPathOption == null) { |
|
172 | try { |
172 | try { |
|
173 | throw new Error(); |
173 | throw new Error(); |
|
174 | } catch (e) { |
174 | } catch (e) { |
|
175 | var stackLines = e.stack.split('\n'); |
175 | var stackLines = e.stack.split('\n'); |
|
176 | |
176 | |
|
177 | for (var i in stackLines) { |
177 | for (var i in stackLines) { |
|
178 | if (!stackLines[i].match(/http[s]?:\/\//)) { |
178 | if (!stackLines[i].match(/http[s]?:\/\//)) { |
|
179 | continue; |
179 | continue; |
|
180 | } |
180 | } |
|
181 | svgPathOption = stackLines[Number(i)].match(/((http[s]?:\/\/.+\/)([^\/]+\.js))(\?.*)?:/)[1].split('/'); |
181 | svgPathOption = stackLines[Number(i)].match(/((http[s]?:\/\/.+\/)([^\/]+\.js))(\?.*)?:/)[1].split('/'); |
|
182 | svgPathOption.pop(); |
182 | svgPathOption.pop(); |
|
183 | svgPathOption = svgPathOption.join('/') + '/ui/icons.svg'; |
183 | svgPathOption = svgPathOption.join('/') + '/ui/icons.svg'; |
|
184 | break; |
184 | break; |
|
185 | } |
185 | } |
|
186 | } |
186 | } |
|
187 | } |
187 | } |
|
188 | |
188 | |
|
189 | var div = t.doc.createElement('div'); |
189 | var div = t.doc.createElement('div'); |
|
190 | div.id = trumbowygIconsId; |
190 | div.id = trumbowygIconsId; |
|
191 | t.doc.body.insertBefore(div, t.doc.body.childNodes[0]); |
191 | t.doc.body.insertBefore(div, t.doc.body.childNodes[0]); |
|
192 | $.ajax({ |
192 | $.ajax({ |
|
193 | async: true, |
193 | async: true, |
|
194 | type: 'GET', |
194 | type: 'GET', |
|
195 | contentType: 'application/x-www-form-urlencoded; charset=UTF-8', |
195 | contentType: 'application/x-www-form-urlencoded; charset=UTF-8', |
|
196 | dataType: 'xml', |
196 | dataType: 'xml', |
|
- | 197 | crossDomain : true, |
||
197 | url: svgPathOption, |
198 | url: svgPathOption, |
|
198 | data: null, |
199 | data: null, |
|
199 | beforeSend: null, |
200 | beforeSend: null, |
|
200 | complete: null, |
201 | complete: null, |
|
201 | success: function (data) { |
202 | success: function (data) { |
|
202 | div.innerHTML = new XMLSerializer().serializeToString(data.documentElement); |
203 | div.innerHTML = new XMLSerializer().serializeToString(data.documentElement); |
|
203 | } |
204 | } |
|
204 | }); |
205 | }); |
|
205 | } |
206 | } |
|
206 | |
207 | |
|
207 | |
208 | |
|
208 | /** |
209 | /** |
|
209 | * When the button is associated to a empty object |
210 | * When the button is associated to a empty object |
|
210 | * fn and title attributs are defined from the button key value |
211 | * fn and title attributs are defined from the button key value |
|
211 | * |
212 | * |
|
212 | * For example |
213 | * For example |
|
213 | * foo: {} |
214 | * foo: {} |
|
214 | * is equivalent to : |
215 | * is equivalent to : |
|
215 | * foo: { |
216 | * foo: { |
|
216 | * fn: 'foo', |
217 | * fn: 'foo', |
|
217 | * title: this.lang.foo |
218 | * title: this.lang.foo |
|
218 | * } |
219 | * } |
|
219 | */ |
220 | */ |
|
220 | var h = t.lang.header, // Header translation |
221 | var h = t.lang.header, // Header translation |
|
221 | isBlinkFunction = function () { |
222 | isBlinkFunction = function () { |
|
222 | return (window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window; |
223 | return (window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window; |
|
223 | }; |
224 | }; |
|
224 | t.btnsDef = { |
225 | t.btnsDef = { |
|
225 | viewHTML: { |
226 | viewHTML: { |
|
226 | fn: 'toggle' |
227 | fn: 'toggle' |
|
227 | }, |
228 | }, |
|
228 | |
229 | |
|
229 | undo: { |
230 | undo: { |
|
230 | isSupported: isBlinkFunction, |
231 | isSupported: isBlinkFunction, |
|
231 | key: 'Z' |
232 | key: 'Z' |
|
232 | }, |
233 | }, |
|
233 | redo: { |
234 | redo: { |
|
234 | isSupported: isBlinkFunction, |
235 | isSupported: isBlinkFunction, |
|
235 | key: 'Y' |
236 | key: 'Y' |
|
236 | }, |
237 | }, |
|
237 | |
238 | |
|
238 | p: { |
239 | p: { |
|
239 | fn: 'formatBlock' |
240 | fn: 'formatBlock' |
|
240 | }, |
241 | }, |
|
241 | blockquote: { |
242 | blockquote: { |
|
242 | fn: 'formatBlock' |
243 | fn: 'formatBlock' |
|
243 | }, |
244 | }, |
|
244 | h1: { |
245 | h1: { |
|
245 | fn: 'formatBlock', |
246 | fn: 'formatBlock', |
|
246 | title: h + ' 1' |
247 | title: h + ' 1' |
|
247 | }, |
248 | }, |
|
248 | h2: { |
249 | h2: { |
|
249 | fn: 'formatBlock', |
250 | fn: 'formatBlock', |
|
250 | title: h + ' 2' |
251 | title: h + ' 2' |
|
251 | }, |
252 | }, |
|
252 | h3: { |
253 | h3: { |
|
253 | fn: 'formatBlock', |
254 | fn: 'formatBlock', |
|
254 | title: h + ' 3' |
255 | title: h + ' 3' |
|
255 | }, |
256 | }, |
|
256 | h4: { |
257 | h4: { |
|
257 | fn: 'formatBlock', |
258 | fn: 'formatBlock', |
|
258 | title: h + ' 4' |
259 | title: h + ' 4' |
|
259 | }, |
260 | }, |
|
260 | subscript: { |
261 | subscript: { |
|
261 | tag: 'sub' |
262 | tag: 'sub' |
|
262 | }, |
263 | }, |
|
263 | superscript: { |
264 | superscript: { |
|
264 | tag: 'sup' |
265 | tag: 'sup' |
|
265 | }, |
266 | }, |
|
266 | |
267 | |
|
267 | bold: { |
268 | bold: { |
|
268 | key: 'B', |
269 | key: 'B', |
|
269 | tag: 'b' |
270 | tag: 'b' |
|
270 | }, |
271 | }, |
|
271 | italic: { |
272 | italic: { |
|
272 | key: 'I', |
273 | key: 'I', |
|
273 | tag: 'i' |
274 | tag: 'i' |
|
274 | }, |
275 | }, |
|
275 | underline: { |
276 | underline: { |
|
276 | tag: 'u' |
277 | tag: 'u' |
|
277 | }, |
278 | }, |
|
278 | strikethrough: { |
279 | strikethrough: { |
|
279 | tag: 'strike' |
280 | tag: 'strike' |
|
280 | }, |
281 | }, |
|
281 | |
282 | |
|
282 | strong: { |
283 | strong: { |
|
283 | fn: 'bold', |
284 | fn: 'bold', |
|
284 | key: 'B' |
285 | key: 'B' |
|
285 | }, |
286 | }, |
|
286 | em: { |
287 | em: { |
|
287 | fn: 'italic', |
288 | fn: 'italic', |
|
288 | key: 'I' |
289 | key: 'I' |
|
289 | }, |
290 | }, |
|
290 | del: { |
291 | del: { |
|
291 | fn: 'strikethrough' |
292 | fn: 'strikethrough' |
|
292 | }, |
293 | }, |
|
293 | |
294 | |
|
294 | createLink: { |
295 | createLink: { |
|
295 | key: 'K', |
296 | key: 'K', |
|
296 | tag: 'a' |
297 | tag: 'a' |
|
297 | }, |
298 | }, |
|
298 | unlink: {}, |
299 | unlink: {}, |
|
299 | |
300 | |
|
300 | insertImage: {}, |
301 | insertImage: {}, |
|
301 | |
302 | |
|
302 | justifyLeft: { |
303 | justifyLeft: { |
|
303 | tag: 'left', |
304 | tag: 'left', |
|
304 | forceCss: true |
305 | forceCss: true |
|
305 | }, |
306 | }, |
|
306 | justifyCenter: { |
307 | justifyCenter: { |
|
307 | tag: 'center', |
308 | tag: 'center', |
|
308 | forceCss: true |
309 | forceCss: true |
|
309 | }, |
310 | }, |
|
310 | justifyRight: { |
311 | justifyRight: { |
|
311 | tag: 'right', |
312 | tag: 'right', |
|
312 | forceCss: true |
313 | forceCss: true |
|
313 | }, |
314 | }, |
|
314 | justifyFull: { |
315 | justifyFull: { |
|
315 | tag: 'justify', |
316 | tag: 'justify', |
|
316 | forceCss: true |
317 | forceCss: true |
|
317 | }, |
318 | }, |
|
318 | |
319 | |
|
319 | unorderedList: { |
320 | unorderedList: { |
|
320 | fn: 'insertUnorderedList', |
321 | fn: 'insertUnorderedList', |
|
321 | tag: 'ul' |
322 | tag: 'ul' |
|
322 | }, |
323 | }, |
|
323 | orderedList: { |
324 | orderedList: { |
|
324 | fn: 'insertOrderedList', |
325 | fn: 'insertOrderedList', |
|
325 | tag: 'ol' |
326 | tag: 'ol' |
|
326 | }, |
327 | }, |
|
327 | |
328 | |
|
328 | horizontalRule: { |
329 | horizontalRule: { |
|
329 | fn: 'insertHorizontalRule' |
330 | fn: 'insertHorizontalRule' |
|
330 | }, |
331 | }, |
|
331 | |
332 | |
|
332 | removeformat: {}, |
333 | removeformat: {}, |
|
333 | |
334 | |
|
334 | fullscreen: { |
335 | fullscreen: { |
|
335 | class: 'trumbowyg-not-disable' |
336 | class: 'trumbowyg-not-disable' |
|
336 | }, |
337 | }, |
|
337 | close: { |
338 | close: { |
|
338 | fn: 'destroy', |
339 | fn: 'destroy', |
|
339 | class: 'trumbowyg-not-disable' |
340 | class: 'trumbowyg-not-disable' |
|
340 | }, |
341 | }, |
|
341 | |
342 | |
|
342 | // Dropdowns |
343 | // Dropdowns |
|
343 | formatting: { |
344 | formatting: { |
|
344 | dropdown: ['p', 'blockquote', 'h1', 'h2', 'h3', 'h4'], |
345 | dropdown: ['p', 'blockquote', 'h1', 'h2', 'h3', 'h4'], |
|
345 | ico: 'p' |
346 | ico: 'p' |
|
346 | }, |
347 | }, |
|
347 | link: { |
348 | link: { |
|
348 | dropdown: ['createLink', 'unlink'] |
349 | dropdown: ['createLink', 'unlink'] |
|
349 | } |
350 | } |
|
350 | }; |
351 | }; |
|
351 | |
352 | |
|
352 | // Defaults Options |
353 | // Defaults Options |
|
353 | t.o = $.extend(true, {}, { |
354 | t.o = $.extend(true, {}, { |
|
354 | lang: 'en', |
355 | lang: 'en', |
|
355 | |
356 | |
|
356 | fixedBtnPane: false, |
357 | fixedBtnPane: false, |
|
357 | fixedFullWidth: false, |
358 | fixedFullWidth: false, |
|
358 | autogrow: false, |
359 | autogrow: false, |
|
359 | |
360 | |
|
360 | prefix: 'trumbowyg-', |
361 | prefix: 'trumbowyg-', |
|
361 | |
362 | |
|
362 | semantic: true, |
363 | semantic: true, |
|
363 | resetCss: false, |
364 | resetCss: false, |
|
364 | removeformatPasted: false, |
365 | removeformatPasted: false, |
|
365 | tagsToRemove: [], |
366 | tagsToRemove: [], |
|
366 | |
367 | |
|
367 | btnsGrps: { |
368 | btnsGrps: { |
|
368 | design: ['bold', 'italic', 'underline', 'strikethrough'], |
369 | design: ['bold', 'italic', 'underline', 'strikethrough'], |
|
369 | semantic: ['strong', 'em', 'del'], |
370 | semantic: ['strong', 'em', 'del'], |
|
370 | justify: ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'], |
371 | justify: ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'], |
|
371 | lists: ['unorderedList', 'orderedList'] |
372 | lists: ['unorderedList', 'orderedList'] |
|
372 | }, |
373 | }, |
|
373 | btns: [ |
374 | btns: [ |
|
374 | ['viewHTML'], |
375 | ['viewHTML'], |
|
375 | ['undo', 'redo'], |
376 | ['undo', 'redo'], |
|
376 | ['formatting'], |
377 | ['formatting'], |
|
377 | 'btnGrp-semantic', |
378 | 'btnGrp-semantic', |
|
378 | ['superscript', 'subscript'], |
379 | ['superscript', 'subscript'], |
|
379 | ['link'], |
380 | ['link'], |
|
380 | ['insertImage'], |
381 | ['insertImage'], |
|
381 | 'btnGrp-justify', |
382 | 'btnGrp-justify', |
|
382 | 'btnGrp-lists', |
383 | 'btnGrp-lists', |
|
383 | ['horizontalRule'], |
384 | ['horizontalRule'], |
|
384 | ['removeformat'], |
385 | ['removeformat'], |
|
385 | ['fullscreen'] |
386 | ['fullscreen'] |
|
386 | ], |
387 | ], |
|
387 | // For custom button definitions |
388 | // For custom button definitions |
|
388 | btnsDef: {}, |
389 | btnsDef: {}, |
|
389 | |
390 | |
|
390 | inlineElementsSelector: 'a,abbr,acronym,b,caption,cite,code,col,dfn,dir,dt,dd,em,font,hr,i,kbd,li,q,span,strikeout,strong,sub,sup,u', |
391 | inlineElementsSelector: 'a,abbr,acronym,b,caption,cite,code,col,dfn,dir,dt,dd,em,font,hr,i,kbd,li,q,span,strikeout,strong,sub,sup,u', |
|
391 | |
392 | |
|
392 | pasteHandlers: [], |
393 | pasteHandlers: [], |
|
393 | |
394 | |
|
394 | imgDblClickHandler: function () { |
395 | imgDblClickHandler: function () { |
|
395 | var $img = $(this), |
396 | var $img = $(this), |
|
396 | src = $img.attr('src'), |
397 | src = $img.attr('src'), |
|
397 | base64 = '(Base64)'; |
398 | base64 = '(Base64)'; |
|
398 | |
399 | |
|
399 | if (src.indexOf('data:image') === 0) { |
400 | if (src.indexOf('data:image') === 0) { |
|
400 | src = base64; |
401 | src = base64; |
|
401 | } |
402 | } |
|
402 | |
403 | |
|
403 | t.openModalInsert(t.lang.insertImage, { |
404 | t.openModalInsert(t.lang.insertImage, { |
|
404 | url: { |
405 | url: { |
|
405 | label: 'URL', |
406 | label: 'URL', |
|
406 | value: src, |
407 | value: src, |
|
407 | required: true |
408 | required: true |
|
408 | }, |
409 | }, |
|
409 | alt: { |
410 | alt: { |
|
410 | label: t.lang.description, |
411 | label: t.lang.description, |
|
411 | value: $img.attr('alt') |
412 | value: $img.attr('alt') |
|
412 | } |
413 | } |
|
413 | }, function (v) { |
414 | }, function (v) { |
|
414 | if (v.src !== base64) { |
415 | if (v.src !== base64) { |
|
415 | $img.attr({ |
416 | $img.attr({ |
|
416 | src: v.src |
417 | src: v.src |
|
417 | }); |
418 | }); |
|
418 | } |
419 | } |
|
419 | $img.attr({ |
420 | $img.attr({ |
|
420 | alt: v.alt |
421 | alt: v.alt |
|
421 | }); |
422 | }); |
|
422 | return true; |
423 | return true; |
|
423 | }); |
424 | }); |
|
424 | return false; |
425 | return false; |
|
425 | }, |
426 | }, |
|
426 | |
427 | |
|
427 | plugins: {} |
428 | plugins: {} |
|
428 | }, options); |
429 | }, options); |
|
429 | |
430 | |
|
430 | t.disabled = t.o.disabled || (editorElem.nodeName === 'TEXTAREA' && editorElem.disabled); |
431 | t.disabled = t.o.disabled || (editorElem.nodeName === 'TEXTAREA' && editorElem.disabled); |
|
431 | |
432 | |
|
432 | if (options.btns) { |
433 | if (options.btns) { |
|
433 | t.o.btns = options.btns; |
434 | t.o.btns = options.btns; |
|
434 | } else if (!t.o.semantic) { |
435 | } else if (!t.o.semantic) { |
|
435 | t.o.btns[4] = 'btnGrp-design'; |
436 | t.o.btns[4] = 'btnGrp-design'; |
|
436 | } |
437 | } |
|
437 | |
438 | |
|
438 | $.each(t.o.btnsDef, function (btnName, btnDef) { |
439 | $.each(t.o.btnsDef, function (btnName, btnDef) { |
|
439 | t.addBtnDef(btnName, btnDef); |
440 | t.addBtnDef(btnName, btnDef); |
|
440 | }); |
441 | }); |
|
441 | |
442 | |
|
442 | // put this here in the event it would be merged in with options |
443 | // put this here in the event it would be merged in with options |
|
443 | t.eventNamespace = 'trumbowyg-event'; |
444 | t.eventNamespace = 'trumbowyg-event'; |
|
444 | |
445 | |
|
445 | // Keyboard shortcuts are load in this array |
446 | // Keyboard shortcuts are load in this array |
|
446 | t.keys = []; |
447 | t.keys = []; |
|
447 | |
448 | |
|
448 | // Tag to button dynamically hydrated |
449 | // Tag to button dynamically hydrated |
|
449 | t.tagToButton = {}; |
450 | t.tagToButton = {}; |
|
450 | t.tagHandlers = []; |
451 | t.tagHandlers = []; |
|
451 | |
452 | |
|
452 | // Admit multiple paste handlers |
453 | // Admit multiple paste handlers |
|
453 | t.pasteHandlers = [].concat(t.o.pasteHandlers); |
454 | t.pasteHandlers = [].concat(t.o.pasteHandlers); |
|
454 | |
455 | |
|
455 | // Check if browser is IE |
456 | // Check if browser is IE |
|
456 | t.isIE = (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') !== -1); |
457 | t.isIE = (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') !== -1); |
|
457 | |
458 | |
|
458 | t.init(); |
459 | t.init(); |
|
459 | }; |
460 | }; |
|
460 | |
461 | |
|
461 | Trumbowyg.prototype = { |
462 | Trumbowyg.prototype = { |
|
462 | init: function () { |
463 | init: function () { |
|
463 | var t = this; |
464 | var t = this; |
|
464 | t.height = t.$ta.height(); |
465 | t.height = t.$ta.height(); |
|
465 | |
466 | |
|
466 | t.initPlugins(); |
467 | t.initPlugins(); |
|
467 | |
468 | |
|
468 | try { |
469 | try { |
|
469 | // Disable image resize, try-catch for old IE |
470 | // Disable image resize, try-catch for old IE |
|
470 | t.doc.execCommand('enableObjectResizing', false, false); |
471 | t.doc.execCommand('enableObjectResizing', false, false); |
|
471 | t.doc.execCommand('defaultParagraphSeparator', false, 'p'); |
472 | t.doc.execCommand('defaultParagraphSeparator', false, 'p'); |
|
472 | } catch (e) { |
473 | } catch (e) { |
|
473 | } |
474 | } |
|
474 | |
475 | |
|
475 | t.buildEditor(); |
476 | t.buildEditor(); |
|
476 | t.buildBtnPane(); |
477 | t.buildBtnPane(); |
|
477 | |
478 | |
|
478 | t.fixedBtnPaneEvents(); |
479 | t.fixedBtnPaneEvents(); |
|
479 | |
480 | |
|
480 | t.buildOverlay(); |
481 | t.buildOverlay(); |
|
481 | |
482 | |
|
482 | setTimeout(function () { |
483 | setTimeout(function () { |
|
483 | if (t.disabled) { |
484 | if (t.disabled) { |
|
484 | t.toggleDisable(true); |
485 | t.toggleDisable(true); |
|
485 | } |
486 | } |
|
486 | t.$c.trigger('tbwinit'); |
487 | t.$c.trigger('tbwinit'); |
|
487 | }); |
488 | }); |
|
488 | }, |
489 | }, |
|
489 | |
490 | |
|
490 | addBtnDef: function (btnName, btnDef) { |
491 | addBtnDef: function (btnName, btnDef) { |
|
491 | this.btnsDef[btnName] = btnDef; |
492 | this.btnsDef[btnName] = btnDef; |
|
492 | }, |
493 | }, |
|
493 | |
494 | |
|
494 | buildEditor: function () { |
495 | buildEditor: function () { |
|
495 | var t = this, |
496 | var t = this, |
|
496 | prefix = t.o.prefix, |
497 | prefix = t.o.prefix, |
|
497 | html = ''; |
498 | html = ''; |
|
498 | |
499 | |
|
499 | t.$box = $('<div/>', { |
500 | t.$box = $('<div/>', { |
|
500 | class: prefix + 'box ' + prefix + 'editor-visible ' + prefix + t.o.lang + ' trumbowyg' |
501 | class: prefix + 'box ' + prefix + 'editor-visible ' + prefix + t.o.lang + ' trumbowyg' |
|
501 | }); |
502 | }); |
|
502 | |
503 | |
|
503 | // $ta = Textarea |
504 | // $ta = Textarea |
|
504 | // $ed = Editor |
505 | // $ed = Editor |
|
505 | t.isTextarea = t.$ta.is('textarea'); |
506 | t.isTextarea = t.$ta.is('textarea'); |
|
506 | if (t.isTextarea) { |
507 | if (t.isTextarea) { |
|
507 | html = t.$ta.val(); |
508 | html = t.$ta.val(); |
|
508 | t.$ed = $('<div/>'); |
509 | t.$ed = $('<div/>'); |
|
509 | t.$box |
510 | t.$box |
|
510 | .insertAfter(t.$ta) |
511 | .insertAfter(t.$ta) |
|
511 | .append(t.$ed, t.$ta); |
512 | .append(t.$ed, t.$ta); |
|
512 | } else { |
513 | } else { |
|
513 | t.$ed = t.$ta; |
514 | t.$ed = t.$ta; |
|
514 | html = t.$ed.html(); |
515 | html = t.$ed.html(); |
|
515 | |
516 | |
|
516 | t.$ta = $('<textarea/>', { |
517 | t.$ta = $('<textarea/>', { |
|
517 | name: t.$ta.attr('id'), |
518 | name: t.$ta.attr('id'), |
|
518 | height: t.height |
519 | height: t.height |
|
519 | }).val(html); |
520 | }).val(html); |
|
520 | |
521 | |
|
521 | t.$box |
522 | t.$box |
|
522 | .insertAfter(t.$ed) |
523 | .insertAfter(t.$ed) |
|
523 | .append(t.$ta, t.$ed); |
524 | .append(t.$ta, t.$ed); |
|
524 | t.syncCode(); |
525 | t.syncCode(); |
|
525 | } |
526 | } |
|
526 | |
527 | |
|
527 | t.$ta |
528 | t.$ta |
|
528 | .addClass(prefix + 'textarea') |
529 | .addClass(prefix + 'textarea') |
|
529 | .attr('tabindex', -1) |
530 | .attr('tabindex', -1) |
|
530 | ; |
531 | ; |
|
531 | |
532 | |
|
532 | t.$ed |
533 | t.$ed |
|
533 | .addClass(prefix + 'editor') |
534 | .addClass(prefix + 'editor') |
|
534 | .attr({ |
535 | .attr({ |
|
535 | contenteditable: true, |
536 | contenteditable: true, |
|
536 | dir: t.lang._dir || 'ltr' |
537 | dir: t.lang._dir || 'ltr' |
|
537 | }) |
538 | }) |
|
538 | .html(html) |
539 | .html(html) |
|
539 | ; |
540 | ; |
|
540 | |
541 | |
|
541 | if (t.o.tabindex) { |
542 | if (t.o.tabindex) { |
|
542 | t.$ed.attr('tabindex', t.o.tabindex); |
543 | t.$ed.attr('tabindex', t.o.tabindex); |
|
543 | } |
544 | } |
|
544 | |
545 | |
|
545 | if (t.$c.is('[placeholder]')) { |
546 | if (t.$c.is('[placeholder]')) { |
|
546 | t.$ed.attr('placeholder', t.$c.attr('placeholder')); |
547 | t.$ed.attr('placeholder', t.$c.attr('placeholder')); |
|
547 | } |
548 | } |
|
548 | |
549 | |
|
549 | if (t.o.resetCss) { |
550 | if (t.o.resetCss) { |
|
550 | t.$ed.addClass(prefix + 'reset-css'); |
551 | t.$ed.addClass(prefix + 'reset-css'); |
|
551 | } |
552 | } |
|
552 | |
553 | |
|
553 | if (!t.o.autogrow) { |
554 | if (!t.o.autogrow) { |
|
554 | t.$ta.add(t.$ed).css({ |
555 | t.$ta.add(t.$ed).css({ |
|
555 | height: t.height |
556 | height: t.height |
|
556 | }); |
557 | }); |
|
557 | } |
558 | } |
|
558 | |
559 | |
|
559 | t.semanticCode(); |
560 | t.semanticCode(); |
|
560 | |
561 | |
|
561 | |
562 | |
|
562 | var ctrl = false, |
563 | var ctrl = false, |
|
563 | composition = false, |
564 | composition = false, |
|
564 | debounceButtonPaneStatus, |
565 | debounceButtonPaneStatus, |
|
565 | updateEventName = t.isIE ? 'keyup' : 'input'; |
566 | updateEventName = t.isIE ? 'keyup' : 'input'; |
|
566 | |
567 | |
|
567 | t.$ed |
568 | t.$ed |
|
568 | .on('dblclick', 'img', t.o.imgDblClickHandler) |
569 | .on('dblclick', 'img', t.o.imgDblClickHandler) |
|
569 | .on('keydown', function (e) { |
570 | .on('keydown', function (e) { |
|
570 | if (e.ctrlKey) { |
571 | if (e.ctrlKey) { |
|
571 | ctrl = true; |
572 | ctrl = true; |
|
572 | var key = t.keys[String.fromCharCode(e.which).toUpperCase()]; |
573 | var key = t.keys[String.fromCharCode(e.which).toUpperCase()]; |
|
573 | |
574 | |
|
574 | try { |
575 | try { |
|
575 | t.execCmd(key.fn, key.param); |
576 | t.execCmd(key.fn, key.param); |
|
576 | return false; |
577 | return false; |
|
577 | } catch (c) { |
578 | } catch (c) { |
|
578 | } |
579 | } |
|
579 | } |
580 | } |
|
580 | }) |
581 | }) |
|
581 | .on('compositionstart compositionupdate', function () { |
582 | .on('compositionstart compositionupdate', function () { |
|
582 | composition = true; |
583 | composition = true; |
|
583 | }) |
584 | }) |
|
584 | .on(updateEventName + ' compositionend', function (e) { |
585 | .on(updateEventName + ' compositionend', function (e) { |
|
585 | if (e.type === 'compositionend') { |
586 | if (e.type === 'compositionend') { |
|
586 | composition = false; |
587 | composition = false; |
|
587 | } else if(composition) { |
588 | } else if(composition) { |
|
588 | return; |
589 | return; |
|
589 | } |
590 | } |
|
590 | |
591 | |
|
591 | var keyCode = e.which; |
592 | var keyCode = e.which; |
|
592 | |
593 | |
|
593 | if (keyCode >= 37 && keyCode <= 40) { |
594 | if (keyCode >= 37 && keyCode <= 40) { |
|
594 | return; |
595 | return; |
|
595 | } |
596 | } |
|
596 | |
597 | |
|
597 | if (e.ctrlKey && (keyCode === 89 || keyCode === 90)) { |
598 | if (e.ctrlKey && (keyCode === 89 || keyCode === 90)) { |
|
598 | t.$c.trigger('tbwchange'); |
599 | t.$c.trigger('tbwchange'); |
|
599 | } else if (!ctrl && keyCode !== 17) { |
600 | } else if (!ctrl && keyCode !== 17) { |
|
600 | t.semanticCode(false, keyCode === 13); |
601 | t.semanticCode(false, keyCode === 13); |
|
601 | t.$c.trigger('tbwchange'); |
602 | t.$c.trigger('tbwchange'); |
|
602 | } else if (typeof e.which === 'undefined') { |
603 | } else if (typeof e.which === 'undefined') { |
|
603 | t.semanticCode(false, false, true); |
604 | t.semanticCode(false, false, true); |
|
604 | } |
605 | } |
|
605 | |
606 | |
|
606 | setTimeout(function () { |
607 | setTimeout(function () { |
|
607 | ctrl = false; |
608 | ctrl = false; |
|
608 | }, 200); |
609 | }, 200); |
|
609 | }) |
610 | }) |
|
610 | .on('mouseup keydown keyup', function () { |
611 | .on('mouseup keydown keyup', function () { |
|
611 | clearTimeout(debounceButtonPaneStatus); |
612 | clearTimeout(debounceButtonPaneStatus); |
|
612 | debounceButtonPaneStatus = setTimeout(function () { |
613 | debounceButtonPaneStatus = setTimeout(function () { |
|
613 | t.updateButtonPaneStatus(); |
614 | t.updateButtonPaneStatus(); |
|
614 | }, 50); |
615 | }, 50); |
|
615 | }) |
616 | }) |
|
616 | .on('focus blur', function (e) { |
617 | .on('focus blur', function (e) { |
|
617 | t.$c.trigger('tbw' + e.type); |
618 | t.$c.trigger('tbw' + e.type); |
|
618 | if (e.type === 'blur') { |
619 | if (e.type === 'blur') { |
|
619 | $('.' + prefix + 'active-button', t.$btnPane).removeClass(prefix + 'active-button ' + prefix + 'active'); |
620 | $('.' + prefix + 'active-button', t.$btnPane).removeClass(prefix + 'active-button ' + prefix + 'active'); |
|
620 | } |
621 | } |
|
621 | }) |
622 | }) |
|
622 | .on('cut', function () { |
623 | .on('cut', function () { |
|
623 | setTimeout(function () { |
624 | setTimeout(function () { |
|
624 | t.semanticCode(false, true); |
625 | t.semanticCode(false, true); |
|
625 | t.$c.trigger('tbwchange'); |
626 | t.$c.trigger('tbwchange'); |
|
626 | }, 0); |
627 | }, 0); |
|
627 | }) |
628 | }) |
|
628 | .on('paste', function (e) { |
629 | .on('paste', function (e) { |
|
629 | if (t.o.removeformatPasted) { |
630 | if (t.o.removeformatPasted) { |
|
630 | e.preventDefault(); |
631 | e.preventDefault(); |
|
631 | |
632 | |
|
632 | try { |
633 | try { |
|
633 | // IE |
634 | // IE |
|
634 | var text = window.clipboardData.getData('Text'); |
635 | var text = window.clipboardData.getData('Text'); |
|
635 | |
636 | |
|
636 | try { |
637 | try { |
|
637 | // <= IE10 |
638 | // <= IE10 |
|
638 | t.doc.selection.createRange().pasteHTML(text); |
639 | t.doc.selection.createRange().pasteHTML(text); |
|
639 | } catch (c) { |
640 | } catch (c) { |
|
640 | // IE 11 |
641 | // IE 11 |
|
641 | t.doc.getSelection().getRangeAt(0).insertNode(t.doc.createTextNode(text)); |
642 | t.doc.getSelection().getRangeAt(0).insertNode(t.doc.createTextNode(text)); |
|
642 | } |
643 | } |
|
643 | } catch (d) { |
644 | } catch (d) { |
|
644 | // Not IE |
645 | // Not IE |
|
645 | t.execCmd('insertText', (e.originalEvent || e).clipboardData.getData('text/plain')); |
646 | t.execCmd('insertText', (e.originalEvent || e).clipboardData.getData('text/plain')); |
|
646 | } |
647 | } |
|
647 | } |
648 | } |
|
648 | |
649 | |
|
649 | // Call pasteHandlers |
650 | // Call pasteHandlers |
|
650 | $.each(t.pasteHandlers, function (i, pasteHandler) { |
651 | $.each(t.pasteHandlers, function (i, pasteHandler) { |
|
651 | pasteHandler(e); |
652 | pasteHandler(e); |
|
652 | }); |
653 | }); |
|
653 | |
654 | |
|
654 | setTimeout(function () { |
655 | setTimeout(function () { |
|
655 | t.semanticCode(false, true); |
656 | t.semanticCode(false, true); |
|
656 | t.$c.trigger('tbwpaste', e); |
657 | t.$c.trigger('tbwpaste', e); |
|
657 | }, 0); |
658 | }, 0); |
|
658 | }); |
659 | }); |
|
659 | t.$ta.on('keyup paste', function () { |
660 | t.$ta.on('keyup paste', function () { |
|
660 | t.$c.trigger('tbwchange'); |
661 | t.$c.trigger('tbwchange'); |
|
661 | }); |
662 | }); |
|
662 | |
663 | |
|
663 | t.$box.on('keydown', function (e) { |
664 | t.$box.on('keydown', function (e) { |
|
664 | if (e.which === 27 && $('.' + prefix + 'modal-box', t.$box).length === 1) { |
665 | if (e.which === 27 && $('.' + prefix + 'modal-box', t.$box).length === 1) { |
|
665 | t.closeModal(); |
666 | t.closeModal(); |
|
666 | return false; |
667 | return false; |
|
667 | } |
668 | } |
|
668 | }); |
669 | }); |
|
669 | }, |
670 | }, |
|
670 | |
671 | |
|
671 | |
672 | |
|
672 | // Build button pane, use o.btns option |
673 | // Build button pane, use o.btns option |
|
673 | buildBtnPane: function () { |
674 | buildBtnPane: function () { |
|
674 | var t = this, |
675 | var t = this, |
|
675 | prefix = t.o.prefix; |
676 | prefix = t.o.prefix; |
|
676 | |
677 | |
|
677 | var $btnPane = t.$btnPane = $('<div/>', { |
678 | var $btnPane = t.$btnPane = $('<div/>', { |
|
678 | class: prefix + 'button-pane' |
679 | class: prefix + 'button-pane' |
|
679 | }); |
680 | }); |
|
680 | |
681 | |
|
681 | $.each(t.o.btns, function (i, btnGrps) { |
682 | $.each(t.o.btns, function (i, btnGrps) { |
|
682 | // Managment of group of buttons |
683 | // Managment of group of buttons |
|
683 | try { |
684 | try { |
|
684 | var b = btnGrps.split('btnGrp-'); |
685 | var b = btnGrps.split('btnGrp-'); |
|
685 | if (b[1] != null) { |
686 | if (b[1] != null) { |
|
686 | btnGrps = t.o.btnsGrps[b[1]]; |
687 | btnGrps = t.o.btnsGrps[b[1]]; |
|
687 | } |
688 | } |
|
688 | } catch (c) { |
689 | } catch (c) { |
|
689 | } |
690 | } |
|
690 | |
691 | |
|
691 | if (!$.isArray(btnGrps)) { |
692 | if (!$.isArray(btnGrps)) { |
|
692 | btnGrps = [btnGrps]; |
693 | btnGrps = [btnGrps]; |
|
693 | } |
694 | } |
|
694 | |
695 | |
|
695 | var $btnGroup = $('<div/>', { |
696 | var $btnGroup = $('<div/>', { |
|
696 | class: prefix + 'button-group ' + ((btnGrps.indexOf('fullscreen') >= 0) ? prefix + 'right' : '') |
697 | class: prefix + 'button-group ' + ((btnGrps.indexOf('fullscreen') >= 0) ? prefix + 'right' : '') |
|
697 | }); |
698 | }); |
|
698 | $.each(btnGrps, function (i, btn) { |
699 | $.each(btnGrps, function (i, btn) { |
|
699 | try { // Prevent buildBtn error |
700 | try { // Prevent buildBtn error |
|
700 | var $item; |
701 | var $item; |
|
701 | |
702 | |
|
702 | if (t.isSupportedBtn(btn)) { // It's a supported button |
703 | if (t.isSupportedBtn(btn)) { // It's a supported button |
|
703 | $item = t.buildBtn(btn); |
704 | $item = t.buildBtn(btn); |
|
704 | } |
705 | } |
|
705 | |
706 | |
|
706 | $btnGroup.append($item); |
707 | $btnGroup.append($item); |
|
707 | } catch (c) { |
708 | } catch (c) { |
|
708 | } |
709 | } |
|
709 | }); |
710 | }); |
|
710 | $btnPane.append($btnGroup); |
711 | $btnPane.append($btnGroup); |
|
711 | }); |
712 | }); |
|
712 | |
713 | |
|
713 | t.$box.prepend($btnPane); |
714 | t.$box.prepend($btnPane); |
|
714 | }, |
715 | }, |
|
715 | |
716 | |
|
716 | |
717 | |
|
717 | // Build a button and his action |
718 | // Build a button and his action |
|
718 | buildBtn: function (btnName) { // btnName is name of the button |
719 | buildBtn: function (btnName) { // btnName is name of the button |
|
719 | var t = this, |
720 | var t = this, |
|
720 | prefix = t.o.prefix, |
721 | prefix = t.o.prefix, |
|
721 | btn = t.btnsDef[btnName], |
722 | btn = t.btnsDef[btnName], |
|
722 | isDropdown = btn.dropdown, |
723 | isDropdown = btn.dropdown, |
|
723 | hasIcon = btn.hasIcon != null ? btn.hasIcon : true, |
724 | hasIcon = btn.hasIcon != null ? btn.hasIcon : true, |
|
724 | textDef = t.lang[btnName] || btnName, |
725 | textDef = t.lang[btnName] || btnName, |
|
725 | |
726 | |
|
726 | $btn = $('<button/>', { |
727 | $btn = $('<button/>', { |
|
727 | type: 'button', |
728 | type: 'button', |
|
728 | class: prefix + btnName + '-button ' + (btn.class || '') + (!hasIcon ? ' ' + prefix + 'textual-button' : ''), |
729 | class: prefix + btnName + '-button ' + (btn.class || '') + (!hasIcon ? ' ' + prefix + 'textual-button' : ''), |
|
729 | html: t.hasSvg && hasIcon ? |
730 | html: t.hasSvg && hasIcon ? |
|
730 | '<svg><use xlink:href="' + t.svgPath + '#' + prefix + (btn.ico || btnName).replace(/([A-Z]+)/g, '-$1').toLowerCase() + '"/></svg>' : |
731 | '<svg><use xlink:href="' + t.svgPath + '#' + prefix + (btn.ico || btnName).replace(/([A-Z]+)/g, '-$1').toLowerCase() + '"/></svg>' : |
|
731 | t.hideButtonTexts ? '' : (btn.text || btn.title || t.lang[btnName] || btnName), |
732 | t.hideButtonTexts ? '' : (btn.text || btn.title || t.lang[btnName] || btnName), |
|
732 | title: (btn.title || btn.text || textDef) + ((btn.key) ? ' (Ctrl + ' + btn.key + ')' : ''), |
733 | title: (btn.title || btn.text || textDef) + ((btn.key) ? ' (Ctrl + ' + btn.key + ')' : ''), |
|
733 | tabindex: -1, |
734 | tabindex: -1, |
|
734 | mousedown: function () { |
735 | mousedown: function () { |
|
735 | if (!isDropdown || $('.' + btnName + '-' + prefix + 'dropdown', t.$box).is(':hidden')) { |
736 | if (!isDropdown || $('.' + btnName + '-' + prefix + 'dropdown', t.$box).is(':hidden')) { |
|
736 | $('body', t.doc).trigger('mousedown'); |
737 | $('body', t.doc).trigger('mousedown'); |
|
737 | } |
738 | } |
|
738 | |
739 | |
|
739 | if (t.$btnPane.hasClass(prefix + 'disable') && !$(this).hasClass(prefix + 'active') && !$(this).hasClass(prefix + 'not-disable')) { |
740 | if (t.$btnPane.hasClass(prefix + 'disable') && !$(this).hasClass(prefix + 'active') && !$(this).hasClass(prefix + 'not-disable')) { |
|
740 | return false; |
741 | return false; |
|
741 | } |
742 | } |
|
742 | |
743 | |
|
743 | t.execCmd((isDropdown ? 'dropdown' : false) || btn.fn || btnName, btn.param || btnName, btn.forceCss || false); |
744 | t.execCmd((isDropdown ? 'dropdown' : false) || btn.fn || btnName, btn.param || btnName, btn.forceCss || false); |
|
744 | |
745 | |
|
745 | return false; |
746 | return false; |
|
746 | } |
747 | } |
|
747 | }); |
748 | }); |
|
748 | |
749 | |
|
749 | if (isDropdown) { |
750 | if (isDropdown) { |
|
750 | $btn.addClass(prefix + 'open-dropdown'); |
751 | $btn.addClass(prefix + 'open-dropdown'); |
|
751 | var dropdownPrefix = prefix + 'dropdown', |
752 | var dropdownPrefix = prefix + 'dropdown', |
|
752 | $dropdown = $('<div/>', { // the dropdown |
753 | $dropdown = $('<div/>', { // the dropdown |
|
753 | class: dropdownPrefix + '-' + btnName + ' ' + dropdownPrefix + ' ' + prefix + 'fixed-top', |
754 | class: dropdownPrefix + '-' + btnName + ' ' + dropdownPrefix + ' ' + prefix + 'fixed-top', |
|
754 | 'data-dropdown': btnName |
755 | 'data-dropdown': btnName |
|
755 | }); |
756 | }); |
|
756 | $.each(isDropdown, function (i, def) { |
757 | $.each(isDropdown, function (i, def) { |
|
757 | if (t.btnsDef[def] && t.isSupportedBtn(def)) { |
758 | if (t.btnsDef[def] && t.isSupportedBtn(def)) { |
|
758 | $dropdown.append(t.buildSubBtn(def)); |
759 | $dropdown.append(t.buildSubBtn(def)); |
|
759 | } |
760 | } |
|
760 | }); |
761 | }); |
|
761 | t.$box.append($dropdown.hide()); |
762 | t.$box.append($dropdown.hide()); |
|
762 | } else if (btn.key) { |
763 | } else if (btn.key) { |
|
763 | t.keys[btn.key] = { |
764 | t.keys[btn.key] = { |
|
764 | fn: btn.fn || btnName, |
765 | fn: btn.fn || btnName, |
|
765 | param: btn.param || btnName |
766 | param: btn.param || btnName |
|
766 | }; |
767 | }; |
|
767 | } |
768 | } |
|
768 | |
769 | |
|
769 | if (!isDropdown) { |
770 | if (!isDropdown) { |
|
770 | t.tagToButton[(btn.tag || btnName).toLowerCase()] = btnName; |
771 | t.tagToButton[(btn.tag || btnName).toLowerCase()] = btnName; |
|
771 | } |
772 | } |
|
772 | |
773 | |
|
773 | return $btn; |
774 | return $btn; |
|
774 | }, |
775 | }, |
|
775 | // Build a button for dropdown menu |
776 | // Build a button for dropdown menu |
|
776 | // @param n : name of the subbutton |
777 | // @param n : name of the subbutton |
|
777 | buildSubBtn: function (btnName) { |
778 | buildSubBtn: function (btnName) { |
|
778 | var t = this, |
779 | var t = this, |
|
779 | prefix = t.o.prefix, |
780 | prefix = t.o.prefix, |
|
780 | btn = t.btnsDef[btnName], |
781 | btn = t.btnsDef[btnName], |
|
781 | hasIcon = btn.hasIcon != null ? btn.hasIcon : true; |
782 | hasIcon = btn.hasIcon != null ? btn.hasIcon : true; |
|
782 | |
783 | |
|
783 | if (btn.key) { |
784 | if (btn.key) { |
|
784 | t.keys[btn.key] = { |
785 | t.keys[btn.key] = { |
|
785 | fn: btn.fn || btnName, |
786 | fn: btn.fn || btnName, |
|
786 | param: btn.param || btnName |
787 | param: btn.param || btnName |
|
787 | }; |
788 | }; |
|
788 | } |
789 | } |
|
789 | |
790 | |
|
790 | t.tagToButton[(btn.tag || btnName).toLowerCase()] = btnName; |
791 | t.tagToButton[(btn.tag || btnName).toLowerCase()] = btnName; |
|
791 | |
792 | |
|
792 | return $('<button/>', { |
793 | return $('<button/>', { |
|
793 | type: 'button', |
794 | type: 'button', |
|
794 | class: prefix + btnName + '-dropdown-button' + (btn.ico ? ' ' + prefix + btn.ico + '-button' : ''), |
795 | class: prefix + btnName + '-dropdown-button' + (btn.ico ? ' ' + prefix + btn.ico + '-button' : ''), |
|
795 | html: t.hasSvg && hasIcon ? '<svg><use xlink:href="' + t.svgPath + '#' + prefix + (btn.ico || btnName).replace(/([A-Z]+)/g, '-$1').toLowerCase() + '"/></svg>' + (btn.text || btn.title || t.lang[btnName] || btnName) : (btn.text || btn.title || t.lang[btnName] || btnName), |
796 | html: t.hasSvg && hasIcon ? '<svg><use xlink:href="' + t.svgPath + '#' + prefix + (btn.ico || btnName).replace(/([A-Z]+)/g, '-$1').toLowerCase() + '"/></svg>' + (btn.text || btn.title || t.lang[btnName] || btnName) : (btn.text || btn.title || t.lang[btnName] || btnName), |
|
796 | title: ((btn.key) ? ' (Ctrl + ' + btn.key + ')' : null), |
797 | title: ((btn.key) ? ' (Ctrl + ' + btn.key + ')' : null), |
|
797 | style: btn.style || null, |
798 | style: btn.style || null, |
|
798 | mousedown: function () { |
799 | mousedown: function () { |
|
799 | $('body', t.doc).trigger('mousedown'); |
800 | $('body', t.doc).trigger('mousedown'); |
|
800 | |
801 | |
|
801 | t.execCmd(btn.fn || btnName, btn.param || btnName, btn.forceCss || false); |
802 | t.execCmd(btn.fn || btnName, btn.param || btnName, btn.forceCss || false); |
|
802 | |
803 | |
|
803 | return false; |
804 | return false; |
|
804 | } |
805 | } |
|
805 | }); |
806 | }); |
|
806 | }, |
807 | }, |
|
807 | // Check if button is supported |
808 | // Check if button is supported |
|
808 | isSupportedBtn: function (b) { |
809 | isSupportedBtn: function (b) { |
|
809 | try { |
810 | try { |
|
810 | return this.btnsDef[b].isSupported(); |
811 | return this.btnsDef[b].isSupported(); |
|
811 | } catch (c) { |
812 | } catch (c) { |
|
812 | } |
813 | } |
|
813 | return true; |
814 | return true; |
|
814 | }, |
815 | }, |
|
815 | |
816 | |
|
816 | // Build overlay for modal box |
817 | // Build overlay for modal box |
|
817 | buildOverlay: function () { |
818 | buildOverlay: function () { |
|
818 | var t = this; |
819 | var t = this; |
|
819 | t.$overlay = $('<div/>', { |
820 | t.$overlay = $('<div/>', { |
|
820 | class: t.o.prefix + 'overlay' |
821 | class: t.o.prefix + 'overlay' |
|
821 | }).css({ |
822 | }).css({ |
|
822 | top: t.$btnPane.outerHeight(), |
823 | top: t.$btnPane.outerHeight(), |
|
823 | height: (t.$ed.outerHeight() + 1) + 'px' |
824 | height: (t.$ed.outerHeight() + 1) + 'px' |
|
824 | }).appendTo(t.$box); |
825 | }).appendTo(t.$box); |
|
825 | return t.$overlay; |
826 | return t.$overlay; |
|
826 | }, |
827 | }, |
|
827 | showOverlay: function () { |
828 | showOverlay: function () { |
|
828 | var t = this; |
829 | var t = this; |
|
829 | $(window).trigger('scroll'); |
830 | $(window).trigger('scroll'); |
|
830 | t.$overlay.fadeIn(200); |
831 | t.$overlay.fadeIn(200); |
|
831 | t.$box.addClass(t.o.prefix + 'box-blur'); |
832 | t.$box.addClass(t.o.prefix + 'box-blur'); |
|
832 | }, |
833 | }, |
|
833 | hideOverlay: function () { |
834 | hideOverlay: function () { |
|
834 | var t = this; |
835 | var t = this; |
|
835 | t.$overlay.fadeOut(50); |
836 | t.$overlay.fadeOut(50); |
|
836 | t.$box.removeClass(t.o.prefix + 'box-blur'); |
837 | t.$box.removeClass(t.o.prefix + 'box-blur'); |
|
837 | }, |
838 | }, |
|
838 | |
839 | |
|
839 | // Management of fixed button pane |
840 | // Management of fixed button pane |
|
840 | fixedBtnPaneEvents: function () { |
841 | fixedBtnPaneEvents: function () { |
|
841 | var t = this, |
842 | var t = this, |
|
842 | fixedFullWidth = t.o.fixedFullWidth, |
843 | fixedFullWidth = t.o.fixedFullWidth, |
|
843 | $box = t.$box; |
844 | $box = t.$box; |
|
844 | |
845 | |
|
845 | if (!t.o.fixedBtnPane) { |
846 | if (!t.o.fixedBtnPane) { |
|
846 | return; |
847 | return; |
|
847 | } |
848 | } |
|
848 | |
849 | |
|
849 | t.isFixed = false; |
850 | t.isFixed = false; |
|
850 | |
851 | |
|
851 | $(window) |
852 | $(window) |
|
852 | .on('scroll.'+t.eventNamespace+' resize.'+t.eventNamespace, function () { |
853 | .on('scroll.'+t.eventNamespace+' resize.'+t.eventNamespace, function () { |
|
853 | if (!$box) { |
854 | if (!$box) { |
|
854 | return; |
855 | return; |
|
855 | } |
856 | } |
|
856 | |
857 | |
|
857 | t.syncCode(); |
858 | t.syncCode(); |
|
858 | |
859 | |
|
859 | var scrollTop = $(window).scrollTop(), |
860 | var scrollTop = $(window).scrollTop(), |
|
860 | offset = $box.offset().top + 1, |
861 | offset = $box.offset().top + 1, |
|
861 | bp = t.$btnPane, |
862 | bp = t.$btnPane, |
|
862 | oh = bp.outerHeight() - 2; |
863 | oh = bp.outerHeight() - 2; |
|
863 | |
864 | |
|
864 | if ((scrollTop - offset > 0) && ((scrollTop - offset - t.height) < 0)) { |
865 | if ((scrollTop - offset > 0) && ((scrollTop - offset - t.height) < 0)) { |
|
865 | if (!t.isFixed) { |
866 | if (!t.isFixed) { |
|
866 | t.isFixed = true; |
867 | t.isFixed = true; |
|
867 | bp.css({ |
868 | bp.css({ |
|
868 | position: 'fixed', |
869 | position: 'fixed', |
|
869 | top: 0, |
870 | top: 0, |
|
870 | left: fixedFullWidth ? '0' : 'auto', |
871 | left: fixedFullWidth ? '0' : 'auto', |
|
871 | zIndex: 7 |
872 | zIndex: 7 |
|
872 | }); |
873 | }); |
|
873 | $([t.$ta, t.$ed]).css({marginTop: bp.height()}); |
874 | $([t.$ta, t.$ed]).css({marginTop: bp.height()}); |
|
874 | } |
875 | } |
|
875 | bp.css({ |
876 | bp.css({ |
|
876 | width: fixedFullWidth ? '100%' : (($box.width() - 1) + 'px') |
877 | width: fixedFullWidth ? '100%' : (($box.width() - 1) + 'px') |
|
877 | }); |
878 | }); |
|
878 | |
879 | |
|
879 | $('.' + t.o.prefix + 'fixed-top', $box).css({ |
880 | $('.' + t.o.prefix + 'fixed-top', $box).css({ |
|
880 | position: fixedFullWidth ? 'fixed' : 'absolute', |
881 | position: fixedFullWidth ? 'fixed' : 'absolute', |
|
881 | top: fixedFullWidth ? oh : oh + (scrollTop - offset) + 'px', |
882 | top: fixedFullWidth ? oh : oh + (scrollTop - offset) + 'px', |
|
882 | zIndex: 15 |
883 | zIndex: 15 |
|
883 | }); |
884 | }); |
|
884 | } else if (t.isFixed) { |
885 | } else if (t.isFixed) { |
|
885 | t.isFixed = false; |
886 | t.isFixed = false; |
|
886 | bp.removeAttr('style'); |
887 | bp.removeAttr('style'); |
|
887 | $([t.$ta, t.$ed]).css({marginTop: 0}); |
888 | $([t.$ta, t.$ed]).css({marginTop: 0}); |
|
888 | $('.' + t.o.prefix + 'fixed-top', $box).css({ |
889 | $('.' + t.o.prefix + 'fixed-top', $box).css({ |
|
889 | position: 'absolute', |
890 | position: 'absolute', |
|
890 | top: oh |
891 | top: oh |
|
891 | }); |
892 | }); |
|
892 | } |
893 | } |
|
893 | }); |
894 | }); |
|
894 | }, |
895 | }, |
|
895 | |
896 | |
|
896 | // Disable editor |
897 | // Disable editor |
|
897 | toggleDisable: function (disable) { |
898 | toggleDisable: function (disable) { |
|
898 | var t = this, |
899 | var t = this, |
|
899 | prefix = t.o.prefix; |
900 | prefix = t.o.prefix; |
|
900 | |
901 | |
|
901 | t.disabled = disable; |
902 | t.disabled = disable; |
|
902 | |
903 | |
|
903 | if (disable) { |
904 | if (disable) { |
|
904 | t.$ta.attr('disabled', true); |
905 | t.$ta.attr('disabled', true); |
|
905 | } else { |
906 | } else { |
|
906 | t.$ta.removeAttr('disabled'); |
907 | t.$ta.removeAttr('disabled'); |
|
907 | } |
908 | } |
|
908 | t.$box.toggleClass(prefix + 'disabled', disable); |
909 | t.$box.toggleClass(prefix + 'disabled', disable); |
|
909 | t.$ed.attr('contenteditable', !disable); |
910 | t.$ed.attr('contenteditable', !disable); |
|
910 | }, |
911 | }, |
|
911 | |
912 | |
|
912 | // Destroy the editor |
913 | // Destroy the editor |
|
913 | destroy: function () { |
914 | destroy: function () { |
|
914 | var t = this, |
915 | var t = this, |
|
915 | prefix = t.o.prefix, |
916 | prefix = t.o.prefix, |
|
916 | height = t.height; |
917 | height = t.height; |
|
917 | |
918 | |
|
918 | if (t.isTextarea) { |
919 | if (t.isTextarea) { |
|
919 | t.$box.after( |
920 | t.$box.after( |
|
920 | t.$ta |
921 | t.$ta |
|
921 | .css({height: height}) |
922 | .css({height: height}) |
|
922 | .val(t.html()) |
923 | .val(t.html()) |
|
923 | .removeClass(prefix + 'textarea') |
924 | .removeClass(prefix + 'textarea') |
|
924 | .show() |
925 | .show() |
|
925 | ); |
926 | ); |
|
926 | } else { |
927 | } else { |
|
927 | t.$box.after( |
928 | t.$box.after( |
|
928 | t.$ed |
929 | t.$ed |
|
929 | .css({height: height}) |
930 | .css({height: height}) |
|
930 | .removeClass(prefix + 'editor') |
931 | .removeClass(prefix + 'editor') |
|
931 | .removeAttr('contenteditable') |
932 | .removeAttr('contenteditable') |
|
932 | .html(t.html()) |
933 | .html(t.html()) |
|
933 | .show() |
934 | .show() |
|
934 | ); |
935 | ); |
|
935 | } |
936 | } |
|
936 | |
937 | |
|
937 | t.$ed.off('dblclick', 'img'); |
938 | t.$ed.off('dblclick', 'img'); |
|
938 | |
939 | |
|
939 | t.destroyPlugins(); |
940 | t.destroyPlugins(); |
|
940 | |
941 | |
|
941 | t.$box.remove(); |
942 | t.$box.remove(); |
|
942 | t.$c.removeData('trumbowyg'); |
943 | t.$c.removeData('trumbowyg'); |
|
943 | $('body').removeClass(prefix + 'body-fullscreen'); |
944 | $('body').removeClass(prefix + 'body-fullscreen'); |
|
944 | t.$c.trigger('tbwclose'); |
945 | t.$c.trigger('tbwclose'); |
|
945 | $(window).off('scroll.'+t.eventNamespace+' resize.'+t.eventNamespace); |
946 | $(window).off('scroll.'+t.eventNamespace+' resize.'+t.eventNamespace); |
|
946 | }, |
947 | }, |
|
947 | |
948 | |
|
948 | |
949 | |
|
949 | // Empty the editor |
950 | // Empty the editor |
|
950 | empty: function () { |
951 | empty: function () { |
|
951 | this.$ta.val(''); |
952 | this.$ta.val(''); |
|
952 | this.syncCode(true); |
953 | this.syncCode(true); |
|
953 | }, |
954 | }, |
|
954 | |
955 | |
|
955 | |
956 | |
|
956 | // Function call when click on viewHTML button |
957 | // Function call when click on viewHTML button |
|
957 | toggle: function () { |
958 | toggle: function () { |
|
958 | var t = this, |
959 | var t = this, |
|
959 | prefix = t.o.prefix; |
960 | prefix = t.o.prefix; |
|
960 | t.semanticCode(false, true); |
961 | t.semanticCode(false, true); |
|
961 | setTimeout(function () { |
962 | setTimeout(function () { |
|
962 | t.doc.activeElement.blur(); |
963 | t.doc.activeElement.blur(); |
|
963 | t.$box.toggleClass(prefix + 'editor-hidden ' + prefix + 'editor-visible'); |
964 | t.$box.toggleClass(prefix + 'editor-hidden ' + prefix + 'editor-visible'); |
|
964 | t.$btnPane.toggleClass(prefix + 'disable'); |
965 | t.$btnPane.toggleClass(prefix + 'disable'); |
|
965 | $('.' + prefix + 'viewHTML-button', t.$btnPane).toggleClass(prefix + 'active'); |
966 | $('.' + prefix + 'viewHTML-button', t.$btnPane).toggleClass(prefix + 'active'); |
|
966 | if (t.$box.hasClass(prefix + 'editor-visible')) { |
967 | if (t.$box.hasClass(prefix + 'editor-visible')) { |
|
967 | t.$ta.attr('tabindex', -1); |
968 | t.$ta.attr('tabindex', -1); |
|
968 | } else { |
969 | } else { |
|
969 | t.$ta.removeAttr('tabindex'); |
970 | t.$ta.removeAttr('tabindex'); |
|
970 | } |
971 | } |
|
971 | }, 0); |
972 | }, 0); |
|
972 | }, |
973 | }, |
|
973 | |
974 | |
|
974 | // Open dropdown when click on a button which open that |
975 | // Open dropdown when click on a button which open that |
|
975 | dropdown: function (name) { |
976 | dropdown: function (name) { |
|
976 | var t = this, |
977 | var t = this, |
|
977 | d = t.doc, |
978 | d = t.doc, |
|
978 | prefix = t.o.prefix, |
979 | prefix = t.o.prefix, |
|
979 | $dropdown = $('[data-dropdown=' + name + ']', t.$box), |
980 | $dropdown = $('[data-dropdown=' + name + ']', t.$box), |
|
980 | $btn = $('.' + prefix + name + '-button', t.$btnPane), |
981 | $btn = $('.' + prefix + name + '-button', t.$btnPane), |
|
981 | show = $dropdown.is(':hidden'); |
982 | show = $dropdown.is(':hidden'); |
|
982 | |
983 | |
|
983 | $('body', d).trigger('mousedown'); |
984 | $('body', d).trigger('mousedown'); |
|
984 | |
985 | |
|
985 | if (show) { |
986 | if (show) { |
|
986 | var o = $btn.offset().left; |
987 | var o = $btn.offset().left; |
|
987 | $btn.addClass(prefix + 'active'); |
988 | $btn.addClass(prefix + 'active'); |
|
988 | |
989 | |
|
989 | $dropdown.css({ |
990 | $dropdown.css({ |
|
990 | position: 'absolute', |
991 | position: 'absolute', |
|
991 | top: $btn.offset().top - t.$btnPane.offset().top + $btn.outerHeight(), |
992 | top: $btn.offset().top - t.$btnPane.offset().top + $btn.outerHeight(), |
|
992 | left: (t.o.fixedFullWidth && t.isFixed) ? o + 'px' : (o - t.$btnPane.offset().left) + 'px' |
993 | left: (t.o.fixedFullWidth && t.isFixed) ? o + 'px' : (o - t.$btnPane.offset().left) + 'px' |
|
993 | }).show(); |
994 | }).show(); |
|
994 | |
995 | |
|
995 | $(window).trigger('scroll'); |
996 | $(window).trigger('scroll'); |
|
996 | |
997 | |
|
997 | $('body', d).on('mousedown.'+t.eventNamespace, function (e) { |
998 | $('body', d).on('mousedown.'+t.eventNamespace, function (e) { |
|
998 | if (!$dropdown.is(e.target)) { |
999 | if (!$dropdown.is(e.target)) { |
|
999 | $('.' + prefix + 'dropdown', d).hide(); |
1000 | $('.' + prefix + 'dropdown', d).hide(); |
|
1000 | $('.' + prefix + 'active', d).removeClass(prefix + 'active'); |
1001 | $('.' + prefix + 'active', d).removeClass(prefix + 'active'); |
|
1001 | $('body', d).off('mousedown.'+t.eventNamespace); |
1002 | $('body', d).off('mousedown.'+t.eventNamespace); |
|
1002 | } |
1003 | } |
|
1003 | }); |
1004 | }); |
|
1004 | } |
1005 | } |
|
1005 | }, |
1006 | }, |
|
1006 | |
1007 | |
|
1007 | |
1008 | |
|
1008 | // HTML Code management |
1009 | // HTML Code management |
|
1009 | html: function (html) { |
1010 | html: function (html) { |
|
1010 | var t = this; |
1011 | var t = this; |
|
1011 | if (html != null) { |
1012 | if (html != null) { |
|
1012 | t.$ta.val(html); |
1013 | t.$ta.val(html); |
|
1013 | t.syncCode(true); |
1014 | t.syncCode(true); |
|
1014 | return t; |
1015 | return t; |
|
1015 | } |
1016 | } |
|
1016 | return t.$ta.val(); |
1017 | return t.$ta.val(); |
|
1017 | }, |
1018 | }, |
|
1018 | syncTextarea: function () { |
1019 | syncTextarea: function () { |
|
1019 | var t = this; |
1020 | var t = this; |
|
1020 | t.$ta.val(t.$ed.text().trim().length > 0 || t.$ed.find('hr,img,embed,iframe,input').length > 0 ? t.$ed.html() : ''); |
1021 | t.$ta.val(t.$ed.text().trim().length > 0 || t.$ed.find('hr,img,embed,iframe,input').length > 0 ? t.$ed.html() : ''); |
|
1021 | }, |
1022 | }, |
|
1022 | syncCode: function (force) { |
1023 | syncCode: function (force) { |
|
1023 | var t = this; |
1024 | var t = this; |
|
1024 | if (!force && t.$ed.is(':visible')) { |
1025 | if (!force && t.$ed.is(':visible')) { |
|
1025 | t.syncTextarea(); |
1026 | t.syncTextarea(); |
|
1026 | } else { |
1027 | } else { |
|
- | 1028 | // wrap the content in a div it's easier to get the innerhtml |
||
- | 1029 | var html = '<div>' + t.$ta.val() + '</div>'; |
||
- | 1030 | //scrub the html before loading into the doc |
||
- | 1031 | html = $(t.o.tagsToRemove.join(','), html).remove().end().html(); |
||
1027 | t.$ed.html(t.$ta.val()); |
1032 | t.$ed.html(html); |
|
1028 | } |
1033 | } |
|
1029 | |
1034 | |
|
1030 | if (t.o.autogrow) { |
1035 | if (t.o.autogrow) { |
|
1031 | t.height = t.$ed.height(); |
1036 | t.height = t.$ed.height(); |
|
1032 | if (t.height !== t.$ta.css('height')) { |
1037 | if (t.height !== t.$ta.css('height')) { |
|
1033 | t.$ta.css({height: t.height}); |
1038 | t.$ta.css({height: t.height}); |
|
1034 | t.$c.trigger('tbwresize'); |
1039 | t.$c.trigger('tbwresize'); |
|
1035 | } |
1040 | } |
|
1036 | } |
1041 | } |
|
1037 | }, |
1042 | }, |
|
1038 | |
1043 | |
|
1039 | // Analyse and update to semantic code |
1044 | // Analyse and update to semantic code |
|
1040 | // @param force : force to sync code from textarea |
1045 | // @param force : force to sync code from textarea |
|
1041 | // @param full : wrap text nodes in <p> |
1046 | // @param full : wrap text nodes in <p> |
|
1042 | // @param keepRange : leave selection range as it is |
1047 | // @param keepRange : leave selection range as it is |
|
1043 | semanticCode: function (force, full, keepRange) { |
1048 | semanticCode: function (force, full, keepRange) { |
|
1044 | var t = this; |
1049 | var t = this; |
|
1045 | t.saveRange(); |
1050 | t.saveRange(); |
|
1046 | t.syncCode(force); |
1051 | t.syncCode(force); |
|
1047 | |
- | ||
1048 | $(t.o.tagsToRemove.join(','), t.$ed).remove(); |
- | ||
1049 | |
1052 | |
|
1050 | if (t.o.semantic) { |
1053 | if (t.o.semantic) { |
|
1051 | t.semanticTag('b', 'strong'); |
1054 | t.semanticTag('b', 'strong'); |
|
1052 | t.semanticTag('i', 'em'); |
1055 | t.semanticTag('i', 'em'); |
|
1053 | |
1056 | |
|
1054 | if (full) { |
1057 | if (full) { |
|
1055 | var inlineElementsSelector = t.o.inlineElementsSelector, |
1058 | var inlineElementsSelector = t.o.inlineElementsSelector, |
|
1056 | blockElementsSelector = ':not(' + inlineElementsSelector + ')'; |
1059 | blockElementsSelector = ':not(' + inlineElementsSelector + ')'; |
|
1057 | |
1060 | |
|
1058 | // Wrap text nodes in span for easier processing |
1061 | // Wrap text nodes in span for easier processing |
|
1059 | t.$ed.contents().filter(function () { |
1062 | t.$ed.contents().filter(function () { |
|
1060 | return this.nodeType === 3 && this.nodeValue.trim().length > 0; |
1063 | return this.nodeType === 3 && this.nodeValue.trim().length > 0; |
|
1061 | }).wrap('<span data-tbw/>'); |
1064 | }).wrap('<span data-tbw/>'); |
|
1062 | |
1065 | |
|
1063 | // Wrap groups of inline elements in paragraphs (recursive) |
1066 | // Wrap groups of inline elements in paragraphs (recursive) |
|
1064 | var wrapInlinesInParagraphsFrom = function ($from) { |
1067 | var wrapInlinesInParagraphsFrom = function ($from) { |
|
1065 | if ($from.length !== 0) { |
1068 | if ($from.length !== 0) { |
|
1066 | var $finalParagraph = $from.nextUntil(blockElementsSelector).addBack().wrapAll('<p/>').parent(), |
1069 | var $finalParagraph = $from.nextUntil(blockElementsSelector).addBack().wrapAll('<p/>').parent(), |
|
1067 | $nextElement = $finalParagraph.nextAll(inlineElementsSelector).first(); |
1070 | $nextElement = $finalParagraph.nextAll(inlineElementsSelector).first(); |
|
1068 | $finalParagraph.next('br').remove(); |
1071 | $finalParagraph.next('br').remove(); |
|
1069 | wrapInlinesInParagraphsFrom($nextElement); |
1072 | wrapInlinesInParagraphsFrom($nextElement); |
|
1070 | } |
1073 | } |
|
1071 | }; |
1074 | }; |
|
1072 | wrapInlinesInParagraphsFrom(t.$ed.children(inlineElementsSelector).first()); |
1075 | wrapInlinesInParagraphsFrom(t.$ed.children(inlineElementsSelector).first()); |
|
1073 | |
1076 | |
|
1074 | t.semanticTag('div', 'p', true); |
1077 | t.semanticTag('div', 'p', true); |
|
1075 | |
1078 | |
|
1076 | // Unwrap paragraphs content, containing nothing usefull |
1079 | // Unwrap paragraphs content, containing nothing usefull |
|
1077 | t.$ed.find('p').filter(function () { |
1080 | t.$ed.find('p').filter(function () { |
|
1078 | // Don't remove currently being edited element |
1081 | // Don't remove currently being edited element |
|
1079 | if (t.range && this === t.range.startContainer) { |
1082 | if (t.range && this === t.range.startContainer) { |
|
1080 | return false; |
1083 | return false; |
|
1081 | } |
1084 | } |
|
1082 | return $(this).text().trim().length === 0 && $(this).children().not('br,span').length === 0; |
1085 | return $(this).text().trim().length === 0 && $(this).children().not('br,span').length === 0; |
|
1083 | }).contents().unwrap(); |
1086 | }).contents().unwrap(); |
|
1084 | |
1087 | |
|
1085 | // Get rid of temporial span's |
1088 | // Get rid of temporial span's |
|
1086 | $('[data-tbw]', t.$ed).contents().unwrap(); |
1089 | $('[data-tbw]', t.$ed).contents().unwrap(); |
|
1087 | |
1090 | |
|
1088 | // Remove empty <p> |
1091 | // Remove empty <p> |
|
1089 | t.$ed.find('p:empty').remove(); |
1092 | t.$ed.find('p:empty').remove(); |
|
1090 | } |
1093 | } |
|
1091 | |
1094 | |
|
1092 | if (!keepRange) { |
1095 | if (!keepRange) { |
|
1093 | t.restoreRange(); |
1096 | t.restoreRange(); |
|
1094 | } |
1097 | } |
|
1095 | |
1098 | |
|
1096 | t.syncTextarea(); |
1099 | t.syncTextarea(); |
|
1097 | } |
1100 | } |
|
1098 | }, |
1101 | }, |
|
1099 | |
1102 | |
|
1100 | semanticTag: function (oldTag, newTag, copyAttributes) { |
1103 | semanticTag: function (oldTag, newTag, copyAttributes) { |
|
1101 | $(oldTag, this.$ed).each(function () { |
1104 | $(oldTag, this.$ed).each(function () { |
|
1102 | var $oldTag = $(this); |
1105 | var $oldTag = $(this); |
|
1103 | $oldTag.wrap('<' + newTag + '/>'); |
1106 | $oldTag.wrap('<' + newTag + '/>'); |
|
1104 | if (copyAttributes) { |
1107 | if (copyAttributes) { |
|
1105 | $.each($oldTag.prop('attributes'), function () { |
1108 | $.each($oldTag.prop('attributes'), function () { |
|
1106 | $oldTag.parent().attr(this.name, this.value); |
1109 | $oldTag.parent().attr(this.name, this.value); |
|
1107 | }); |
1110 | }); |
|
1108 | } |
1111 | } |
|
1109 | $oldTag.contents().unwrap(); |
1112 | $oldTag.contents().unwrap(); |
|
1110 | }); |
1113 | }); |
|
1111 | }, |
1114 | }, |
|
1112 | |
1115 | |
|
1113 | // Function call when user click on "Insert Link" |
1116 | // Function call when user click on "Insert Link" |
|
1114 | createLink: function () { |
1117 | createLink: function () { |
|
1115 | var t = this, |
1118 | var t = this, |
|
1116 | documentSelection = t.doc.getSelection(), |
1119 | documentSelection = t.doc.getSelection(), |
|
1117 | node = documentSelection.focusNode, |
1120 | node = documentSelection.focusNode, |
|
1118 | url, |
1121 | url, |
|
1119 | title, |
1122 | title, |
|
1120 | target; |
1123 | target; |
|
1121 | |
1124 | |
|
1122 | while (['A', 'DIV'].indexOf(node.nodeName) < 0) { |
1125 | while (['A', 'DIV'].indexOf(node.nodeName) < 0) { |
|
1123 | node = node.parentNode; |
1126 | node = node.parentNode; |
|
1124 | } |
1127 | } |
|
1125 | |
1128 | |
|
1126 | if (node && node.nodeName === 'A') { |
1129 | if (node && node.nodeName === 'A') { |
|
1127 | var $a = $(node); |
1130 | var $a = $(node); |
|
1128 | url = $a.attr('href'); |
1131 | url = $a.attr('href'); |
|
1129 | title = $a.attr('title'); |
1132 | title = $a.attr('title'); |
|
1130 | target = $a.attr('target'); |
1133 | target = $a.attr('target'); |
|
1131 | var range = t.doc.createRange(); |
1134 | var range = t.doc.createRange(); |
|
1132 | range.selectNode(node); |
1135 | range.selectNode(node); |
|
- | 1136 | documentSelection.removeAllRanges(); |
||
1133 | documentSelection.addRange(range); |
1137 | documentSelection.addRange(range); |
|
1134 | } |
1138 | } |
|
1135 | |
1139 | |
|
1136 | t.saveRange(); |
1140 | t.saveRange(); |
|
1137 | |
1141 | |
|
1138 | t.openModalInsert(t.lang.createLink, { |
1142 | t.openModalInsert(t.lang.createLink, { |
|
1139 | url: { |
1143 | url: { |
|
1140 | label: 'URL', |
1144 | label: 'URL', |
|
1141 | required: true, |
1145 | required: true, |
|
1142 | value: url |
1146 | value: url |
|
1143 | }, |
1147 | }, |
|
1144 | title: { |
1148 | title: { |
|
1145 | label: t.lang.title, |
1149 | label: t.lang.title, |
|
1146 | value: title |
1150 | value: title |
|
1147 | }, |
1151 | }, |
|
1148 | text: { |
1152 | text: { |
|
1149 | label: t.lang.text, |
1153 | label: t.lang.text, |
|
1150 | value: t.getRangeText() |
1154 | value: t.getRangeText() |
|
1151 | }, |
1155 | }, |
|
1152 | target: { |
1156 | target: { |
|
1153 | label: t.lang.target, |
1157 | label: t.lang.target, |
|
1154 | value: target |
1158 | value: target |
|
1155 | } |
1159 | } |
|
1156 | }, function (v) { // v is value |
1160 | }, function (v) { // v is value |
|
1157 | var link = $(['<a href="', v.url, '">', v.text, '</a>'].join('')); |
1161 | var link = $(['<a href="', v.url, '">', v.text, '</a>'].join('')); |
|
1158 | if (v.title.length > 0) { |
1162 | if (v.title.length > 0) { |
|
1159 | link.attr('title', v.title); |
1163 | link.attr('title', v.title); |
|
1160 | } |
1164 | } |
|
1161 | if (v.target.length > 0) { |
1165 | if (v.target.length > 0) { |
|
1162 | link.attr('target', v.target); |
1166 | link.attr('target', v.target); |
|
1163 | } |
1167 | } |
|
1164 | t.range.deleteContents(); |
1168 | t.range.deleteContents(); |
|
1165 | t.range.insertNode(link[0]); |
1169 | t.range.insertNode(link[0]); |
|
1166 | return true; |
1170 | return true; |
|
1167 | }); |
1171 | }); |
|
1168 | }, |
1172 | }, |
|
1169 | unlink: function () { |
1173 | unlink: function () { |
|
1170 | var t = this, |
1174 | var t = this, |
|
1171 | documentSelection = t.doc.getSelection(), |
1175 | documentSelection = t.doc.getSelection(), |
|
1172 | node = documentSelection.focusNode; |
1176 | node = documentSelection.focusNode; |
|
1173 | |
1177 | |
|
1174 | if (documentSelection.isCollapsed) { |
1178 | if (documentSelection.isCollapsed) { |
|
1175 | while (['A', 'DIV'].indexOf(node.nodeName) < 0) { |
1179 | while (['A', 'DIV'].indexOf(node.nodeName) < 0) { |
|
1176 | node = node.parentNode; |
1180 | node = node.parentNode; |
|
1177 | } |
1181 | } |
|
1178 | |
1182 | |
|
1179 | if (node && node.nodeName === 'A') { |
1183 | if (node && node.nodeName === 'A') { |
|
1180 | var range = t.doc.createRange(); |
1184 | var range = t.doc.createRange(); |
|
1181 | range.selectNode(node); |
1185 | range.selectNode(node); |
|
- | 1186 | documentSelection.removeAllRanges(); |
||
1182 | documentSelection.addRange(range); |
1187 | documentSelection.addRange(range); |
|
1183 | } |
1188 | } |
|
1184 | } |
1189 | } |
|
1185 | t.execCmd('unlink', undefined, undefined, true); |
1190 | t.execCmd('unlink', undefined, undefined, true); |
|
1186 | }, |
1191 | }, |
|
1187 | insertImage: function () { |
1192 | insertImage: function () { |
|
1188 | var t = this; |
1193 | var t = this; |
|
1189 | t.saveRange(); |
1194 | t.saveRange(); |
|
1190 | t.openModalInsert(t.lang.insertImage, { |
1195 | t.openModalInsert(t.lang.insertImage, { |
|
1191 | url: { |
1196 | url: { |
|
1192 | label: 'URL', |
1197 | label: 'URL', |
|
1193 | required: true |
1198 | required: true |
|
1194 | }, |
1199 | }, |
|
1195 | alt: { |
1200 | alt: { |
|
1196 | label: t.lang.description, |
1201 | label: t.lang.description, |
|
1197 | value: t.getRangeText() |
1202 | value: t.getRangeText() |
|
1198 | } |
1203 | } |
|
1199 | }, function (v) { // v are values |
1204 | }, function (v) { // v are values |
|
1200 | t.execCmd('insertImage', v.url); |
1205 | t.execCmd('insertImage', v.url); |
|
1201 | $('img[src="' + v.url + '"]:not([alt])', t.$box).attr('alt', v.alt); |
1206 | $('img[src="' + v.url + '"]:not([alt])', t.$box).attr('alt', v.alt); |
|
1202 | return true; |
1207 | return true; |
|
1203 | }); |
1208 | }); |
|
1204 | }, |
1209 | }, |
|
1205 | fullscreen: function () { |
1210 | fullscreen: function () { |
|
1206 | var t = this, |
1211 | var t = this, |
|
1207 | prefix = t.o.prefix, |
1212 | prefix = t.o.prefix, |
|
1208 | fullscreenCssClass = prefix + 'fullscreen', |
1213 | fullscreenCssClass = prefix + 'fullscreen', |
|
1209 | isFullscreen; |
1214 | isFullscreen; |
|
1210 | |
1215 | |
|
1211 | t.$box.toggleClass(fullscreenCssClass); |
1216 | t.$box.toggleClass(fullscreenCssClass); |
|
1212 | isFullscreen = t.$box.hasClass(fullscreenCssClass); |
1217 | isFullscreen = t.$box.hasClass(fullscreenCssClass); |
|
1213 | $('body').toggleClass(prefix + 'body-fullscreen', isFullscreen); |
1218 | $('body').toggleClass(prefix + 'body-fullscreen', isFullscreen); |
|
1214 | $(window).trigger('scroll'); |
1219 | $(window).trigger('scroll'); |
|
1215 | t.$c.trigger('tbw' + (isFullscreen ? 'open' : 'close') + 'fullscreen'); |
1220 | t.$c.trigger('tbw' + (isFullscreen ? 'open' : 'close') + 'fullscreen'); |
|
1216 | }, |
1221 | }, |
|
1217 | |
1222 | |
|
1218 | |
1223 | |
|
1219 | /* |
1224 | /* |
|
1220 | * Call method of trumbowyg if exist |
1225 | * Call method of trumbowyg if exist |
|
1221 | * else try to call anonymous function |
1226 | * else try to call anonymous function |
|
1222 | * and finaly native execCommand |
1227 | * and finaly native execCommand |
|
1223 | */ |
1228 | */ |
|
1224 | execCmd: function (cmd, param, forceCss, skipTrumbowyg) { |
1229 | execCmd: function (cmd, param, forceCss, skipTrumbowyg) { |
|
1225 | var t = this; |
1230 | var t = this; |
|
1226 | skipTrumbowyg = !!skipTrumbowyg || ''; |
1231 | skipTrumbowyg = !!skipTrumbowyg || ''; |
|
1227 | |
1232 | |
|
1228 | if (cmd !== 'dropdown') { |
1233 | if (cmd !== 'dropdown') { |
|
1229 | t.$ed.focus(); |
1234 | t.$ed.focus(); |
|
1230 | } |
1235 | } |
|
1231 | |
1236 | |
|
1232 | try { |
1237 | try { |
|
1233 | t.doc.execCommand('styleWithCSS', false, forceCss || false); |
1238 | t.doc.execCommand('styleWithCSS', false, forceCss || false); |
|
1234 | } catch (c) { |
1239 | } catch (c) { |
|
1235 | } |
1240 | } |
|
1236 | |
1241 | |
|
1237 | try { |
1242 | try { |
|
1238 | t[cmd + skipTrumbowyg](param); |
1243 | t[cmd + skipTrumbowyg](param); |
|
1239 | } catch (c) { |
1244 | } catch (c) { |
|
1240 | try { |
1245 | try { |
|
1241 | cmd(param); |
1246 | cmd(param); |
|
1242 | } catch (e2) { |
1247 | } catch (e2) { |
|
1243 | if (cmd === 'insertHorizontalRule') { |
1248 | if (cmd === 'insertHorizontalRule') { |
|
1244 | param = undefined; |
1249 | param = undefined; |
|
1245 | } else if (cmd === 'formatBlock' && t.isIE) { |
1250 | } else if (cmd === 'formatBlock' && t.isIE) { |
|
1246 | param = '<' + param + '>'; |
1251 | param = '<' + param + '>'; |
|
1247 | } |
1252 | } |
|
1248 | |
1253 | |
|
1249 | t.doc.execCommand(cmd, false, param); |
1254 | t.doc.execCommand(cmd, false, param); |
|
1250 | |
1255 | |
|
1251 | t.syncCode(); |
1256 | t.syncCode(); |
|
1252 | t.semanticCode(false, true); |
1257 | t.semanticCode(false, true); |
|
1253 | } |
1258 | } |
|
1254 | |
1259 | |
|
1255 | if (cmd !== 'dropdown') { |
1260 | if (cmd !== 'dropdown') { |
|
1256 | t.updateButtonPaneStatus(); |
1261 | t.updateButtonPaneStatus(); |
|
1257 | t.$c.trigger('tbwchange'); |
1262 | t.$c.trigger('tbwchange'); |
|
1258 | } |
1263 | } |
|
1259 | } |
1264 | } |
|
1260 | }, |
1265 | }, |
|
1261 | |
1266 | |
|
1262 | |
1267 | |
|
1263 | // Open a modal box |
1268 | // Open a modal box |
|
1264 | openModal: function (title, content) { |
1269 | openModal: function (title, content) { |
|
1265 | var t = this, |
1270 | var t = this, |
|
1266 | prefix = t.o.prefix; |
1271 | prefix = t.o.prefix; |
|
1267 | |
1272 | |
|
1268 | // No open a modal box when exist other modal box |
1273 | // No open a modal box when exist other modal box |
|
1269 | if ($('.' + prefix + 'modal-box', t.$box).length > 0) { |
1274 | if ($('.' + prefix + 'modal-box', t.$box).length > 0) { |
|
1270 | return false; |
1275 | return false; |
|
1271 | } |
1276 | } |
|
1272 | |
1277 | |
|
1273 | t.saveRange(); |
1278 | t.saveRange(); |
|
1274 | t.showOverlay(); |
1279 | t.showOverlay(); |
|
1275 | |
1280 | |
|
1276 | // Disable all btnPane btns |
1281 | // Disable all btnPane btns |
|
1277 | t.$btnPane.addClass(prefix + 'disable'); |
1282 | t.$btnPane.addClass(prefix + 'disable'); |
|
1278 | |
1283 | |
|
1279 | // Build out of ModalBox, it's the mask for animations |
1284 | // Build out of ModalBox, it's the mask for animations |
|
1280 | var $modal = $('<div/>', { |
1285 | var $modal = $('<div/>', { |
|
1281 | class: prefix + 'modal ' + prefix + 'fixed-top' |
1286 | class: prefix + 'modal ' + prefix + 'fixed-top' |
|
1282 | }).css({ |
1287 | }).css({ |
|
1283 | top: t.$btnPane.height() |
1288 | top: t.$btnPane.height() |
|
1284 | }).appendTo(t.$box); |
1289 | }).appendTo(t.$box); |
|
1285 | |
1290 | |
|
1286 | // Click on overlay close modal by cancelling them |
1291 | // Click on overlay close modal by cancelling them |
|
1287 | t.$overlay.one('click', function () { |
1292 | t.$overlay.one('click', function () { |
|
1288 | $modal.trigger('tbwcancel'); |
1293 | $modal.trigger('tbwcancel'); |
|
1289 | return false; |
1294 | return false; |
|
1290 | }); |
1295 | }); |
|
1291 | |
1296 | |
|
1292 | // Build the form |
1297 | // Build the form |
|
1293 | var $form = $('<form/>', { |
1298 | var $form = $('<form/>', { |
|
1294 | action: '', |
1299 | action: '', |
|
1295 | html: content |
1300 | html: content |
|
1296 | }) |
1301 | }) |
|
1297 | .on('submit', function () { |
1302 | .on('submit', function () { |
|
1298 | $modal.trigger('tbwconfirm'); |
1303 | $modal.trigger('tbwconfirm'); |
|
1299 | return false; |
1304 | return false; |
|
1300 | }) |
1305 | }) |
|
1301 | .on('reset', function () { |
1306 | .on('reset', function () { |
|
1302 | $modal.trigger('tbwcancel'); |
1307 | $modal.trigger('tbwcancel'); |
|
1303 | return false; |
1308 | return false; |
|
1304 | }); |
1309 | }); |
|
1305 | |
1310 | |
|
1306 | |
1311 | |
|
1307 | // Build ModalBox and animate to show them |
1312 | // Build ModalBox and animate to show them |
|
1308 | var $box = $('<div/>', { |
1313 | var $box = $('<div/>', { |
|
1309 | class: prefix + 'modal-box', |
1314 | class: prefix + 'modal-box', |
|
1310 | html: $form |
1315 | html: $form |
|
1311 | }) |
1316 | }) |
|
1312 | .css({ |
1317 | .css({ |
|
1313 | top: '-' + t.$btnPane.outerHeight() + 'px', |
1318 | top: '-' + t.$btnPane.outerHeight() + 'px', |
|
1314 | opacity: 0 |
1319 | opacity: 0 |
|
1315 | }) |
1320 | }) |
|
1316 | .appendTo($modal) |
1321 | .appendTo($modal) |
|
1317 | .animate({ |
1322 | .animate({ |
|
1318 | top: 0, |
1323 | top: 0, |
|
1319 | opacity: 1 |
1324 | opacity: 1 |
|
1320 | }, 100); |
1325 | }, 100); |
|
1321 | |
1326 | |
|
1322 | |
1327 | |
|
1323 | // Append title |
1328 | // Append title |
|
1324 | $('<span/>', { |
1329 | $('<span/>', { |
|
1325 | text: title, |
1330 | text: title, |
|
1326 | class: prefix + 'modal-title' |
1331 | class: prefix + 'modal-title' |
|
1327 | }).prependTo($box); |
1332 | }).prependTo($box); |
|
1328 | |
1333 | |
|
1329 | $modal.height($box.outerHeight() + 10); |
1334 | $modal.height($box.outerHeight() + 10); |
|
1330 | |
1335 | |
|
1331 | |
1336 | |
|
1332 | // Focus in modal box |
1337 | // Focus in modal box |
|
1333 | $('input:first', $box).focus(); |
1338 | $('input:first', $box).focus(); |
|
1334 | |
1339 | |
|
1335 | |
1340 | |
|
1336 | // Append Confirm and Cancel buttons |
1341 | // Append Confirm and Cancel buttons |
|
1337 | t.buildModalBtn('submit', $box); |
1342 | t.buildModalBtn('submit', $box); |
|
1338 | t.buildModalBtn('reset', $box); |
1343 | t.buildModalBtn('reset', $box); |
|
1339 | |
1344 | |
|
1340 | |
1345 | |
|
1341 | $(window).trigger('scroll'); |
1346 | $(window).trigger('scroll'); |
|
1342 | |
1347 | |
|
1343 | return $modal; |
1348 | return $modal; |
|
1344 | }, |
1349 | }, |
|
1345 | // @param n is name of modal |
1350 | // @param n is name of modal |
|
1346 | buildModalBtn: function (n, $modal) { |
1351 | buildModalBtn: function (n, $modal) { |
|
1347 | var t = this, |
1352 | var t = this, |
|
1348 | prefix = t.o.prefix; |
1353 | prefix = t.o.prefix; |
|
1349 | |
1354 | |
|
1350 | return $('<button/>', { |
1355 | return $('<button/>', { |
|
1351 | class: prefix + 'modal-button ' + prefix + 'modal-' + n, |
1356 | class: prefix + 'modal-button ' + prefix + 'modal-' + n, |
|
1352 | type: n, |
1357 | type: n, |
|
1353 | text: t.lang[n] || n |
1358 | text: t.lang[n] || n |
|
1354 | }).appendTo($('form', $modal)); |
1359 | }).appendTo($('form', $modal)); |
|
1355 | }, |
1360 | }, |
|
1356 | // close current modal box |
1361 | // close current modal box |
|
1357 | closeModal: function () { |
1362 | closeModal: function () { |
|
1358 | var t = this, |
1363 | var t = this, |
|
1359 | prefix = t.o.prefix; |
1364 | prefix = t.o.prefix; |
|
1360 | |
1365 | |
|
1361 | t.$btnPane.removeClass(prefix + 'disable'); |
1366 | t.$btnPane.removeClass(prefix + 'disable'); |
|
1362 | t.$overlay.off(); |
1367 | t.$overlay.off(); |
|
1363 | |
1368 | |
|
1364 | // Find the modal box |
1369 | // Find the modal box |
|
1365 | var $modalBox = $('.' + prefix + 'modal-box', t.$box); |
1370 | var $modalBox = $('.' + prefix + 'modal-box', t.$box); |
|
1366 | |
1371 | |
|
1367 | $modalBox.animate({ |
1372 | $modalBox.animate({ |
|
1368 | top: '-' + $modalBox.height() |
1373 | top: '-' + $modalBox.height() |
|
1369 | }, 100, function () { |
1374 | }, 100, function () { |
|
1370 | $modalBox.parent().remove(); |
1375 | $modalBox.parent().remove(); |
|
1371 | t.hideOverlay(); |
1376 | t.hideOverlay(); |
|
1372 | }); |
1377 | }); |
|
1373 | |
1378 | |
|
1374 | t.restoreRange(); |
1379 | t.restoreRange(); |
|
1375 | }, |
1380 | }, |
|
1376 | // Preformated build and management modal |
1381 | // Preformated build and management modal |
|
1377 | openModalInsert: function (title, fields, cmd) { |
1382 | openModalInsert: function (title, fields, cmd) { |
|
1378 | var t = this, |
1383 | var t = this, |
|
1379 | prefix = t.o.prefix, |
1384 | prefix = t.o.prefix, |
|
1380 | lg = t.lang, |
1385 | lg = t.lang, |
|
1381 | html = '', |
1386 | html = '', |
|
1382 | CONFIRM_EVENT = 'tbwconfirm'; |
1387 | CONFIRM_EVENT = 'tbwconfirm'; |
|
1383 | |
1388 | |
|
1384 | $.each(fields, function (fieldName, field) { |
1389 | $.each(fields, function (fieldName, field) { |
|
1385 | var l = field.label, |
1390 | var l = field.label, |
|
1386 | n = field.name || fieldName, |
1391 | n = field.name || fieldName, |
|
1387 | a = field.attributes || {}; |
1392 | a = field.attributes || {}; |
|
1388 | |
1393 | |
|
1389 | var attr = Object.keys(a).map(function (prop) { |
1394 | var attr = Object.keys(a).map(function (prop) { |
|
1390 | return prop + '="' + a[prop] + '"'; |
1395 | return prop + '="' + a[prop] + '"'; |
|
1391 | }).join(' '); |
1396 | }).join(' '); |
|
1392 | |
1397 | |
|
1393 | html += '<label><input type="' + (field.type || 'text') + '" name="' + n + '" value="' + (field.value || '').replace(/"/g, '"') + '"' + attr + '><span class="' + prefix + 'input-infos"><span>' + |
1398 | html += '<label><input type="' + (field.type || 'text') + '" name="' + n + '" value="' + (field.value || '').replace(/"/g, '"') + '"' + attr + '><span class="' + prefix + 'input-infos"><span>' + |
|
1394 | ((!l) ? (lg[fieldName] ? lg[fieldName] : fieldName) : (lg[l] ? lg[l] : l)) + |
1399 | ((!l) ? (lg[fieldName] ? lg[fieldName] : fieldName) : (lg[l] ? lg[l] : l)) + |
|
1395 | '</span></span></label>'; |
1400 | '</span></span></label>'; |
|
1396 | }); |
1401 | }); |
|
1397 | |
1402 | |
|
1398 | return t.openModal(title, html) |
1403 | return t.openModal(title, html) |
|
1399 | .on(CONFIRM_EVENT, function () { |
1404 | .on(CONFIRM_EVENT, function () { |
|
1400 | var $form = $('form', $(this)), |
1405 | var $form = $('form', $(this)), |
|
1401 | valid = true, |
1406 | valid = true, |
|
1402 | values = {}; |
1407 | values = {}; |
|
1403 | |
1408 | |
|
1404 | $.each(fields, function (fieldName, field) { |
1409 | $.each(fields, function (fieldName, field) { |
|
1405 | var $field = $('input[name="' + fieldName + '"]', $form), |
1410 | var $field = $('input[name="' + fieldName + '"]', $form), |
|
1406 | inputType = $field.attr('type'); |
1411 | inputType = $field.attr('type'); |
|
1407 | |
1412 | |
|
1408 | if (inputType.toLowerCase() === 'checkbox') { |
1413 | if (inputType.toLowerCase() === 'checkbox') { |
|
1409 | values[fieldName] = $field.is(':checked'); |
1414 | values[fieldName] = $field.is(':checked'); |
|
1410 | } else { |
1415 | } else { |
|
1411 | values[fieldName] = $.trim($field.val()); |
1416 | values[fieldName] = $.trim($field.val()); |
|
1412 | } |
1417 | } |
|
1413 | // Validate value |
1418 | // Validate value |
|
1414 | if (field.required && values[fieldName] === '') { |
1419 | if (field.required && values[fieldName] === '') { |
|
1415 | valid = false; |
1420 | valid = false; |
|
1416 | t.addErrorOnModalField($field, t.lang.required); |
1421 | t.addErrorOnModalField($field, t.lang.required); |
|
1417 | } else if (field.pattern && !field.pattern.test(values[fieldName])) { |
1422 | } else if (field.pattern && !field.pattern.test(values[fieldName])) { |
|
1418 | valid = false; |
1423 | valid = false; |
|
1419 | t.addErrorOnModalField($field, field.patternError); |
1424 | t.addErrorOnModalField($field, field.patternError); |
|
1420 | } |
1425 | } |
|
1421 | }); |
1426 | }); |
|
1422 | |
1427 | |
|
1423 | if (valid) { |
1428 | if (valid) { |
|
1424 | t.restoreRange(); |
1429 | t.restoreRange(); |
|
1425 | |
1430 | |
|
1426 | if (cmd(values, fields)) { |
1431 | if (cmd(values, fields)) { |
|
1427 | t.syncCode(); |
1432 | t.syncCode(); |
|
1428 | t.$c.trigger('tbwchange'); |
1433 | t.$c.trigger('tbwchange'); |
|
1429 | t.closeModal(); |
1434 | t.closeModal(); |
|
1430 | $(this).off(CONFIRM_EVENT); |
1435 | $(this).off(CONFIRM_EVENT); |
|
1431 | } |
1436 | } |
|
1432 | } |
1437 | } |
|
1433 | }) |
1438 | }) |
|
1434 | .one('tbwcancel', function () { |
1439 | .one('tbwcancel', function () { |
|
1435 | $(this).off(CONFIRM_EVENT); |
1440 | $(this).off(CONFIRM_EVENT); |
|
1436 | t.closeModal(); |
1441 | t.closeModal(); |
|
1437 | }); |
1442 | }); |
|
1438 | }, |
1443 | }, |
|
1439 | addErrorOnModalField: function ($field, err) { |
1444 | addErrorOnModalField: function ($field, err) { |
|
1440 | var prefix = this.o.prefix, |
1445 | var prefix = this.o.prefix, |
|
1441 | $label = $field.parent(); |
1446 | $label = $field.parent(); |
|
1442 | |
1447 | |
|
1443 | $field |
1448 | $field |
|
1444 | .on('change keyup', function () { |
1449 | .on('change keyup', function () { |
|
1445 | $label.removeClass(prefix + 'input-error'); |
1450 | $label.removeClass(prefix + 'input-error'); |
|
1446 | }); |
1451 | }); |
|
1447 | |
1452 | |
|
1448 | $label |
1453 | $label |
|
1449 | .addClass(prefix + 'input-error') |
1454 | .addClass(prefix + 'input-error') |
|
1450 | .find('input+span') |
1455 | .find('input+span') |
|
1451 | .append( |
1456 | .append( |
|
1452 | $('<span/>', { |
1457 | $('<span/>', { |
|
1453 | class: prefix + 'msg-error', |
1458 | class: prefix + 'msg-error', |
|
1454 | text: err |
1459 | text: err |
|
1455 | }) |
1460 | }) |
|
1456 | ); |
1461 | ); |
|
1457 | }, |
1462 | }, |
|
1458 | |
1463 | |
|
1459 | |
1464 | |
|
1460 | // Range management |
1465 | // Range management |
|
1461 | saveRange: function () { |
1466 | saveRange: function () { |
|
1462 | var t = this, |
1467 | var t = this, |
|
1463 | documentSelection = t.doc.getSelection(); |
1468 | documentSelection = t.doc.getSelection(); |
|
1464 | |
1469 | |
|
1465 | t.range = null; |
1470 | t.range = null; |
|
1466 | |
1471 | |
|
1467 | if (documentSelection.rangeCount) { |
1472 | if (documentSelection.rangeCount) { |
|
1468 | var savedRange = t.range = documentSelection.getRangeAt(0), |
1473 | var savedRange = t.range = documentSelection.getRangeAt(0), |
|
1469 | range = t.doc.createRange(), |
1474 | range = t.doc.createRange(), |
|
1470 | rangeStart; |
1475 | rangeStart; |
|
1471 | range.selectNodeContents(t.$ed[0]); |
1476 | range.selectNodeContents(t.$ed[0]); |
|
1472 | range.setEnd(savedRange.startContainer, savedRange.startOffset); |
1477 | range.setEnd(savedRange.startContainer, savedRange.startOffset); |
|
1473 | rangeStart = (range + '').length; |
1478 | rangeStart = (range + '').length; |
|
1474 | t.metaRange = { |
1479 | t.metaRange = { |
|
1475 | start: rangeStart, |
1480 | start: rangeStart, |
|
1476 | end: rangeStart + (savedRange + '').length |
1481 | end: rangeStart + (savedRange + '').length |
|
1477 | }; |
1482 | }; |
|
1478 | } |
1483 | } |
|
1479 | }, |
1484 | }, |
|
1480 | restoreRange: function () { |
1485 | restoreRange: function () { |
|
1481 | var t = this, |
1486 | var t = this, |
|
1482 | metaRange = t.metaRange, |
1487 | metaRange = t.metaRange, |
|
1483 | savedRange = t.range, |
1488 | savedRange = t.range, |
|
1484 | documentSelection = t.doc.getSelection(), |
1489 | documentSelection = t.doc.getSelection(), |
|
1485 | range; |
1490 | range; |
|
1486 | |
1491 | |
|
1487 | if (!savedRange) { |
1492 | if (!savedRange) { |
|
1488 | return; |
1493 | return; |
|
1489 | } |
1494 | } |
|
1490 | |
1495 | |
|
1491 | if (metaRange && metaRange.start !== metaRange.end) { // Algorithm from http://jsfiddle.net/WeWy7/3/ |
1496 | if (metaRange && metaRange.start !== metaRange.end) { // Algorithm from http://jsfiddle.net/WeWy7/3/ |
|
1492 | var charIndex = 0, |
1497 | var charIndex = 0, |
|
1493 | nodeStack = [t.$ed[0]], |
1498 | nodeStack = [t.$ed[0]], |
|
1494 | node, |
1499 | node, |
|
1495 | foundStart = false, |
1500 | foundStart = false, |
|
1496 | stop = false; |
1501 | stop = false; |
|
1497 | |
1502 | |
|
1498 | range = t.doc.createRange(); |
1503 | range = t.doc.createRange(); |
|
1499 | |
1504 | |
|
1500 | while (!stop && (node = nodeStack.pop())) { |
1505 | while (!stop && (node = nodeStack.pop())) { |
|
1501 | if (node.nodeType === 3) { |
1506 | if (node.nodeType === 3) { |
|
1502 | var nextCharIndex = charIndex + node.length; |
1507 | var nextCharIndex = charIndex + node.length; |
|
1503 | if (!foundStart && metaRange.start >= charIndex && metaRange.start <= nextCharIndex) { |
1508 | if (!foundStart && metaRange.start >= charIndex && metaRange.start <= nextCharIndex) { |
|
1504 | range.setStart(node, metaRange.start - charIndex); |
1509 | range.setStart(node, metaRange.start - charIndex); |
|
1505 | foundStart = true; |
1510 | foundStart = true; |
|
1506 | } |
1511 | } |
|
1507 | if (foundStart && metaRange.end >= charIndex && metaRange.end <= nextCharIndex) { |
1512 | if (foundStart && metaRange.end >= charIndex && metaRange.end <= nextCharIndex) { |
|
1508 | range.setEnd(node, metaRange.end - charIndex); |
1513 | range.setEnd(node, metaRange.end - charIndex); |
|
1509 | stop = true; |
1514 | stop = true; |
|
1510 | } |
1515 | } |
|
1511 | charIndex = nextCharIndex; |
1516 | charIndex = nextCharIndex; |
|
1512 | } else { |
1517 | } else { |
|
1513 | var cn = node.childNodes, |
1518 | var cn = node.childNodes, |
|
1514 | i = cn.length; |
1519 | i = cn.length; |
|
1515 | |
1520 | |
|
1516 | while (i > 0) { |
1521 | while (i > 0) { |
|
1517 | i -= 1; |
1522 | i -= 1; |
|
1518 | nodeStack.push(cn[i]); |
1523 | nodeStack.push(cn[i]); |
|
1519 | } |
1524 | } |
|
1520 | } |
1525 | } |
|
1521 | } |
1526 | } |
|
1522 | } |
1527 | } |
|
1523 | |
1528 | |
|
1524 | documentSelection.removeAllRanges(); |
1529 | documentSelection.removeAllRanges(); |
|
1525 | documentSelection.addRange(range || savedRange); |
1530 | documentSelection.addRange(range || savedRange); |
|
1526 | }, |
1531 | }, |
|
1527 | getRangeText: function () { |
1532 | getRangeText: function () { |
|
1528 | return this.range + ''; |
1533 | return this.range + ''; |
|
1529 | }, |
1534 | }, |
|
1530 | |
1535 | |
|
1531 | updateButtonPaneStatus: function () { |
1536 | updateButtonPaneStatus: function () { |
|
1532 | var t = this, |
1537 | var t = this, |
|
1533 | prefix = t.o.prefix, |
1538 | prefix = t.o.prefix, |
|
1534 | tags = t.getTagsRecursive(t.doc.getSelection().focusNode), |
1539 | tags = t.getTagsRecursive(t.doc.getSelection().focusNode), |
|
1535 | activeClasses = prefix + 'active-button ' + prefix + 'active'; |
1540 | activeClasses = prefix + 'active-button ' + prefix + 'active'; |
|
1536 | |
1541 | |
|
1537 | $('.' + prefix + 'active-button', t.$btnPane).removeClass(activeClasses); |
1542 | $('.' + prefix + 'active-button', t.$btnPane).removeClass(activeClasses); |
|
1538 | $.each(tags, function (i, tag) { |
1543 | $.each(tags, function (i, tag) { |
|
1539 | var btnName = t.tagToButton[tag.toLowerCase()], |
1544 | var btnName = t.tagToButton[tag.toLowerCase()], |
|
1540 | $btn = $('.' + prefix + btnName + '-button', t.$btnPane); |
1545 | $btn = $('.' + prefix + btnName + '-button', t.$btnPane); |
|
1541 | |
1546 | |
|
1542 | if ($btn.length > 0) { |
1547 | if ($btn.length > 0) { |
|
1543 | $btn.addClass(activeClasses); |
1548 | $btn.addClass(activeClasses); |
|
1544 | } else { |
1549 | } else { |
|
1545 | try { |
1550 | try { |
|
1546 | $btn = $('.' + prefix + 'dropdown .' + prefix + btnName + '-dropdown-button', t.$box); |
1551 | $btn = $('.' + prefix + 'dropdown .' + prefix + btnName + '-dropdown-button', t.$box); |
|
1547 | var dropdownBtnName = $btn.parent().data('dropdown'); |
1552 | var dropdownBtnName = $btn.parent().data('dropdown'); |
|
1548 | $('.' + prefix + dropdownBtnName + '-button', t.$box).addClass(activeClasses); |
1553 | $('.' + prefix + dropdownBtnName + '-button', t.$box).addClass(activeClasses); |
|
1549 | } catch (e) { |
1554 | } catch (e) { |
|
1550 | } |
1555 | } |
|
1551 | } |
1556 | } |
|
1552 | }); |
1557 | }); |
|
1553 | }, |
1558 | }, |
|
1554 | getTagsRecursive: function (element, tags) { |
1559 | getTagsRecursive: function (element, tags) { |
|
1555 | var t = this; |
1560 | var t = this; |
|
1556 | tags = tags || (element && element.tagName ? [element.tagName] : []); |
1561 | tags = tags || (element && element.tagName ? [element.tagName] : []); |
|
1557 | |
1562 | |
|
1558 | if (element && element.parentNode) { |
1563 | if (element && element.parentNode) { |
|
1559 | element = element.parentNode; |
1564 | element = element.parentNode; |
|
1560 | } else { |
1565 | } else { |
|
1561 | return tags; |
1566 | return tags; |
|
1562 | } |
1567 | } |
|
1563 | |
1568 | |
|
1564 | var tag = element.tagName; |
1569 | var tag = element.tagName; |
|
1565 | if (tag === 'DIV') { |
1570 | if (tag === 'DIV') { |
|
1566 | return tags; |
1571 | return tags; |
|
1567 | } |
1572 | } |
|
1568 | if (tag === 'P' && element.style.textAlign !== '') { |
1573 | if (tag === 'P' && element.style.textAlign !== '') { |
|
1569 | tags.push(element.style.textAlign); |
1574 | tags.push(element.style.textAlign); |
|
1570 | } |
1575 | } |
|
1571 | |
1576 | |
|
1572 | $.each(t.tagHandlers, function (i, tagHandler) { |
1577 | $.each(t.tagHandlers, function (i, tagHandler) { |
|
1573 | tags = tags.concat(tagHandler(element, t)); |
1578 | tags = tags.concat(tagHandler(element, t)); |
|
1574 | }); |
1579 | }); |
|
1575 | |
1580 | |
|
1576 | tags.push(tag); |
1581 | tags.push(tag); |
|
1577 | |
1582 | |
|
1578 | return t.getTagsRecursive(element, tags); |
1583 | return t.getTagsRecursive(element, tags); |
|
1579 | }, |
1584 | }, |
|
1580 | |
1585 | |
|
1581 | // Plugins |
1586 | // Plugins |
|
1582 | initPlugins: function () { |
1587 | initPlugins: function () { |
|
1583 | var t = this; |
1588 | var t = this; |
|
1584 | t.loadedPlugins = []; |
1589 | t.loadedPlugins = []; |
|
1585 | $.each($.trumbowyg.plugins, function (name, plugin) { |
1590 | $.each($.trumbowyg.plugins, function (name, plugin) { |
|
1586 | if (!plugin.shouldInit || plugin.shouldInit(t)) { |
1591 | if (!plugin.shouldInit || plugin.shouldInit(t)) { |
|
1587 | plugin.init(t); |
1592 | plugin.init(t); |
|
1588 | if (plugin.tagHandler) { |
1593 | if (plugin.tagHandler) { |
|
1589 | t.tagHandlers.push(plugin.tagHandler); |
1594 | t.tagHandlers.push(plugin.tagHandler); |
|
1590 | } |
1595 | } |
|
1591 | t.loadedPlugins.push(plugin); |
1596 | t.loadedPlugins.push(plugin); |
|
1592 | } |
1597 | } |
|
1593 | }); |
1598 | }); |
|
1594 | }, |
1599 | }, |
|
1595 | destroyPlugins: function () { |
1600 | destroyPlugins: function () { |
|
1596 | $.each(this.loadedPlugins, function (i, plugin) { |
1601 | $.each(this.loadedPlugins, function (i, plugin) { |
|
1597 | if (plugin.destroy) { |
1602 | if (plugin.destroy) { |
|
1598 | plugin.destroy(); |
1603 | plugin.destroy(); |
|
1599 | } |
1604 | } |
|
1600 | }); |
1605 | }); |
|
1601 | } |
1606 | } |
|
1602 | }; |
1607 | }; |
|
1603 | })(navigator, window, document, jQuery); |
1608 | })(navigator, window, document, jQuery); |
|
1604 | |
1609 | |