1 | /** |
---|
2 | * @version 0.5-rc1 |
---|
3 | * |
---|
4 | * WYMeditor : what you see is What You Mean web-based editor |
---|
5 | * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/ |
---|
6 | * Dual licensed under the MIT (MIT-license.txt) |
---|
7 | * and GPL (GPL-license.txt) licenses. |
---|
8 | * |
---|
9 | * For further information visit: |
---|
10 | * http://www.wymeditor.org/ |
---|
11 | * |
---|
12 | * File: jquery.wymeditor.js |
---|
13 | * |
---|
14 | * Main JS file with core classes and functions. |
---|
15 | * See the documentation for more info. |
---|
16 | * |
---|
17 | * About: authors |
---|
18 | * |
---|
19 | * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg) |
---|
20 | * Volker Mische (vmx a-t gmx dotde) |
---|
21 | * Scott Lewis (lewiscot a-t gmail dotcom) |
---|
22 | * Bermi Ferrer (wymeditor a-t bermi dotorg) |
---|
23 | * Daniel Reszka (d.reszka a-t wymeditor dotorg) |
---|
24 | * Jonatan Lundin (jonatan.lundin _at_ gmail.com) |
---|
25 | */ |
---|
26 | |
---|
27 | /* |
---|
28 | Namespace: WYMeditor |
---|
29 | Global WYMeditor namespace. |
---|
30 | */ |
---|
31 | if(!WYMeditor) var WYMeditor = {}; |
---|
32 | |
---|
33 | //Wrap the Firebug console in WYMeditor.console |
---|
34 | (function() { |
---|
35 | if ( !window.console || !console.firebug ) { |
---|
36 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", |
---|
37 | "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; |
---|
38 | |
---|
39 | WYMeditor.console = {}; |
---|
40 | for (var i = 0; i < names.length; ++i) |
---|
41 | WYMeditor.console[names[i]] = function() {} |
---|
42 | |
---|
43 | } else WYMeditor.console = window.console; |
---|
44 | })(); |
---|
45 | |
---|
46 | jQuery.extend(WYMeditor, { |
---|
47 | |
---|
48 | /* |
---|
49 | Constants: Global WYMeditor constants. |
---|
50 | |
---|
51 | VERSION - Defines WYMeditor version. |
---|
52 | INSTANCES - An array of loaded WYMeditor.editor instances. |
---|
53 | STRINGS - An array of loaded WYMeditor language pairs/values. |
---|
54 | SKINS - An array of loaded WYMeditor skins. |
---|
55 | NAME - The "name" attribute. |
---|
56 | INDEX - A string replaced by the instance index. |
---|
57 | WYM_INDEX - A string used to get/set the instance index. |
---|
58 | BASE_PATH - A string replaced by WYMeditor's base path. |
---|
59 | SKIN_PATH - A string replaced by WYMeditor's skin path. |
---|
60 | WYM_PATH - A string replaced by WYMeditor's main JS file path. |
---|
61 | SKINS_DEFAULT_PATH - The skins default base path. |
---|
62 | SKINS_DEFAULT_CSS - The skins default CSS file. |
---|
63 | LANG_DEFAULT_PATH - The language files default path. |
---|
64 | IFRAME_BASE_PATH - A string replaced by the designmode iframe's base path. |
---|
65 | IFRAME_DEFAULT - The iframe's default base path. |
---|
66 | JQUERY_PATH - A string replaced by the computed jQuery path. |
---|
67 | DIRECTION - A string replaced by the text direction (rtl or ltr). |
---|
68 | LOGO - A string replaced by WYMeditor logo. |
---|
69 | TOOLS - A string replaced by the toolbar's HTML. |
---|
70 | TOOLS_ITEMS - A string replaced by the toolbar items. |
---|
71 | TOOL_NAME - A string replaced by a toolbar item's name. |
---|
72 | TOOL_TITLE - A string replaced by a toolbar item's title. |
---|
73 | TOOL_CLASS - A string replaced by a toolbar item's class. |
---|
74 | CLASSES - A string replaced by the classes panel's HTML. |
---|
75 | CLASSES_ITEMS - A string replaced by the classes items. |
---|
76 | CLASS_NAME - A string replaced by a class item's name. |
---|
77 | CLASS_TITLE - A string replaced by a class item's title. |
---|
78 | CONTAINERS - A string replaced by the containers panel's HTML. |
---|
79 | CONTAINERS_ITEMS - A string replaced by the containers items. |
---|
80 | CONTAINER_NAME - A string replaced by a container item's name. |
---|
81 | CONTAINER_TITLE - A string replaced by a container item's title. |
---|
82 | CONTAINER_CLASS - A string replaced by a container item's class. |
---|
83 | HTML - A string replaced by the HTML view panel's HTML. |
---|
84 | IFRAME - A string replaced by the designmode iframe. |
---|
85 | STATUS - A string replaced by the status panel's HTML. |
---|
86 | DIALOG_TITLE - A string replaced by a dialog's title. |
---|
87 | DIALOG_BODY - A string replaced by a dialog's HTML body. |
---|
88 | BODY - The BODY element. |
---|
89 | STRING - The "string" type. |
---|
90 | BODY,DIV,P, |
---|
91 | H1,H2,H3,H4,H5,H6, |
---|
92 | PRE,BLOCKQUOTE, |
---|
93 | A,BR,IMG, |
---|
94 | TABLE,TD,TH, |
---|
95 | UL,OL,LI - HTML elements string representation. |
---|
96 | CLASS,HREF,SRC, |
---|
97 | TITLE,ALT - HTML attributes string representation. |
---|
98 | DIALOG_LINK - A link dialog type. |
---|
99 | DIALOG_IMAGE - An image dialog type. |
---|
100 | DIALOG_TABLE - A table dialog type. |
---|
101 | DIALOG_PASTE - A 'Paste from Word' dialog type. |
---|
102 | BOLD - Command: (un)set selection to <strong>. |
---|
103 | ITALIC - Command: (un)set selection to <em>. |
---|
104 | CREATE_LINK - Command: open the link dialog or (un)set link. |
---|
105 | INSERT_IMAGE - Command: open the image dialog or insert an image. |
---|
106 | INSERT_TABLE - Command: open the table dialog. |
---|
107 | PASTE - Command: open the paste dialog. |
---|
108 | INDENT - Command: nest a list item. |
---|
109 | OUTDENT - Command: unnest a list item. |
---|
110 | TOGGLE_HTML - Command: display/hide the HTML view. |
---|
111 | FORMAT_BLOCK - Command: set a block element to another type. |
---|
112 | PREVIEW - Command: open the preview dialog. |
---|
113 | UNLINK - Command: unset a link. |
---|
114 | INSERT_UNORDEREDLIST- Command: insert an unordered list. |
---|
115 | INSERT_ORDEREDLIST - Command: insert an ordered list. |
---|
116 | MAIN_CONTAINERS - An array of the main HTML containers used in WYMeditor. |
---|
117 | BLOCKS - An array of the HTML block elements. |
---|
118 | KEY - Standard key codes. |
---|
119 | NODE - Node types. |
---|
120 | |
---|
121 | */ |
---|
122 | |
---|
123 | VERSION : "0.5-rc1", |
---|
124 | INSTANCES : [], |
---|
125 | STRINGS : [], |
---|
126 | SKINS : [], |
---|
127 | NAME : "name", |
---|
128 | INDEX : "{Wym_Index}", |
---|
129 | WYM_INDEX : "wym_index", |
---|
130 | BASE_PATH : "{Wym_Base_Path}", |
---|
131 | CSS_PATH : "{Wym_Css_Path}", |
---|
132 | WYM_PATH : "{Wym_Wym_Path}", |
---|
133 | SKINS_DEFAULT_PATH : "skins/", |
---|
134 | SKINS_DEFAULT_CSS : "skin.css", |
---|
135 | SKINS_DEFAULT_JS : "skin.js", |
---|
136 | LANG_DEFAULT_PATH : "lang/", |
---|
137 | IFRAME_BASE_PATH : "{Wym_Iframe_Base_Path}", |
---|
138 | IFRAME_DEFAULT : "iframe/default/", |
---|
139 | JQUERY_PATH : "{Wym_Jquery_Path}", |
---|
140 | DIRECTION : "{Wym_Direction}", |
---|
141 | LOGO : "{Wym_Logo}", |
---|
142 | TOOLS : "{Wym_Tools}", |
---|
143 | TOOLS_ITEMS : "{Wym_Tools_Items}", |
---|
144 | TOOL_NAME : "{Wym_Tool_Name}", |
---|
145 | TOOL_TITLE : "{Wym_Tool_Title}", |
---|
146 | TOOL_CLASS : "{Wym_Tool_Class}", |
---|
147 | CLASSES : "{Wym_Classes}", |
---|
148 | CLASSES_ITEMS : "{Wym_Classes_Items}", |
---|
149 | CLASS_NAME : "{Wym_Class_Name}", |
---|
150 | CLASS_TITLE : "{Wym_Class_Title}", |
---|
151 | CONTAINERS : "{Wym_Containers}", |
---|
152 | CONTAINERS_ITEMS : "{Wym_Containers_Items}", |
---|
153 | CONTAINER_NAME : "{Wym_Container_Name}", |
---|
154 | CONTAINER_TITLE : "{Wym_Containers_Title}", |
---|
155 | CONTAINER_CLASS : "{Wym_Container_Class}", |
---|
156 | HTML : "{Wym_Html}", |
---|
157 | IFRAME : "{Wym_Iframe}", |
---|
158 | STATUS : "{Wym_Status}", |
---|
159 | DIALOG_TITLE : "{Wym_Dialog_Title}", |
---|
160 | DIALOG_BODY : "{Wym_Dialog_Body}", |
---|
161 | STRING : "string", |
---|
162 | BODY : "body", |
---|
163 | DIV : "div", |
---|
164 | P : "p", |
---|
165 | H1 : "h1", |
---|
166 | H2 : "h2", |
---|
167 | H3 : "h3", |
---|
168 | H4 : "h4", |
---|
169 | H5 : "h5", |
---|
170 | H6 : "h6", |
---|
171 | PRE : "pre", |
---|
172 | BLOCKQUOTE : "blockquote", |
---|
173 | A : "a", |
---|
174 | BR : "br", |
---|
175 | IMG : "img", |
---|
176 | TABLE : "table", |
---|
177 | TD : "td", |
---|
178 | TH : "th", |
---|
179 | UL : "ul", |
---|
180 | OL : "ol", |
---|
181 | LI : "li", |
---|
182 | CLASS : "class", |
---|
183 | HREF : "href", |
---|
184 | SRC : "src", |
---|
185 | TITLE : "title", |
---|
186 | ALT : "alt", |
---|
187 | DIALOG_LINK : "Link", |
---|
188 | DIALOG_IMAGE : "Image", |
---|
189 | DIALOG_TABLE : "Table", |
---|
190 | DIALOG_PASTE : "Paste_From_Word", |
---|
191 | BOLD : "Bold", |
---|
192 | ITALIC : "Italic", |
---|
193 | CREATE_LINK : "CreateLink", |
---|
194 | INSERT_IMAGE : "InsertImage", |
---|
195 | INSERT_TABLE : "InsertTable", |
---|
196 | INSERT_HTML : "InsertHTML", |
---|
197 | PASTE : "Paste", |
---|
198 | INDENT : "Indent", |
---|
199 | OUTDENT : "Outdent", |
---|
200 | TOGGLE_HTML : "ToggleHtml", |
---|
201 | FORMAT_BLOCK : "FormatBlock", |
---|
202 | PREVIEW : "Preview", |
---|
203 | UNLINK : "Unlink", |
---|
204 | INSERT_UNORDEREDLIST: "InsertUnorderedList", |
---|
205 | INSERT_ORDEREDLIST : "InsertOrderedList", |
---|
206 | |
---|
207 | MAIN_CONTAINERS : new Array("p","h1","h2","h3","h4","h5","h6","pre","blockquote"), |
---|
208 | |
---|
209 | BLOCKS : new Array("address", "blockquote", "div", "dl", |
---|
210 | "fieldset", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", |
---|
211 | "noscript", "ol", "p", "pre", "table", "ul", "dd", "dt", |
---|
212 | "li", "tbody", "td", "tfoot", "th", "thead", "tr"), |
---|
213 | |
---|
214 | KEY : { |
---|
215 | BACKSPACE: 8, |
---|
216 | ENTER: 13, |
---|
217 | END: 35, |
---|
218 | HOME: 36, |
---|
219 | LEFT: 37, |
---|
220 | UP: 38, |
---|
221 | RIGHT: 39, |
---|
222 | DOWN: 40, |
---|
223 | CURSOR: new Array(37, 38, 39, 40), |
---|
224 | DELETE: 46 |
---|
225 | }, |
---|
226 | |
---|
227 | NODE : { |
---|
228 | ELEMENT: 1, |
---|
229 | ATTRIBUTE: 2, |
---|
230 | TEXT: 3 |
---|
231 | }, |
---|
232 | |
---|
233 | /* |
---|
234 | Class: WYMeditor.editor |
---|
235 | WYMeditor editor main class, instanciated for each editor occurrence. |
---|
236 | */ |
---|
237 | |
---|
238 | editor : function(elem, options) { |
---|
239 | |
---|
240 | /* |
---|
241 | Constructor: WYMeditor.editor |
---|
242 | |
---|
243 | Initializes main values (index, elements, paths, ...) |
---|
244 | and call WYMeditor.editor.init which initializes the editor. |
---|
245 | |
---|
246 | Parameters: |
---|
247 | |
---|
248 | elem - The HTML element to be replaced by the editor. |
---|
249 | options - The hash of options. |
---|
250 | |
---|
251 | Returns: |
---|
252 | |
---|
253 | Nothing. |
---|
254 | |
---|
255 | See Also: |
---|
256 | |
---|
257 | <WYMeditor.editor.init> |
---|
258 | */ |
---|
259 | |
---|
260 | //store the instance in the INSTANCES array and store the index |
---|
261 | this._index = WYMeditor.INSTANCES.push(this) - 1; |
---|
262 | //store the element replaced by the editor |
---|
263 | this._element = elem; |
---|
264 | //store the options |
---|
265 | this._options = options; |
---|
266 | //store the element's inner value |
---|
267 | this._html = jQuery(elem).val(); |
---|
268 | |
---|
269 | //store the HTML option, if any |
---|
270 | if(this._options.html) this._html = this._options.html; |
---|
271 | //get or compute the base path (where the main JS file is located) |
---|
272 | this._options.basePath = this._options.basePath |
---|
273 | || this.computeBasePath(); |
---|
274 | //get or set the skin path (where the skin files are located) |
---|
275 | this._options.skinPath = this._options.skinPath |
---|
276 | || this._options.basePath + WYMeditor.SKINS_DEFAULT_PATH |
---|
277 | + this._options.skin + '/'; |
---|
278 | //get or compute the main JS file location |
---|
279 | this._options.wymPath = this._options.wymPath |
---|
280 | || this.computeWymPath(); |
---|
281 | //get or set the language files path |
---|
282 | this._options.langPath = this._options.langPath |
---|
283 | || this._options.basePath + WYMeditor.LANG_DEFAULT_PATH; |
---|
284 | //get or set the designmode iframe's base path |
---|
285 | this._options.iframeBasePath = this._options.iframeBasePath |
---|
286 | || this._options.basePath + WYMeditor.IFRAME_DEFAULT; |
---|
287 | //get or compute the jQuery JS file location |
---|
288 | this._options.jQueryPath = this._options.jQueryPath |
---|
289 | || this.computeJqueryPath(); |
---|
290 | |
---|
291 | //initialize the editor instance |
---|
292 | this.init(); |
---|
293 | |
---|
294 | } |
---|
295 | |
---|
296 | }); |
---|
297 | |
---|
298 | /********** JQUERY **********/ |
---|
299 | |
---|
300 | /** |
---|
301 | * Replace an HTML element by WYMeditor |
---|
302 | * |
---|
303 | * @example jQuery(".wymeditor").wymeditor( |
---|
304 | * { |
---|
305 | * |
---|
306 | * } |
---|
307 | * ); |
---|
308 | * @desc Example description here |
---|
309 | * |
---|
310 | * @name WYMeditor |
---|
311 | * @description WYMeditor is a web-based WYSIWYM XHTML editor |
---|
312 | * @param Hash hash A hash of parameters |
---|
313 | * @option Integer iExample Description here |
---|
314 | * @option String sExample Description here |
---|
315 | * |
---|
316 | * @type jQuery |
---|
317 | * @cat Plugins/WYMeditor |
---|
318 | * @author Jean-Francois Hovinne |
---|
319 | */ |
---|
320 | jQuery.fn.wymeditor = function(options) { |
---|
321 | |
---|
322 | options = jQuery.extend({ |
---|
323 | |
---|
324 | html: "", |
---|
325 | |
---|
326 | basePath: false, |
---|
327 | |
---|
328 | skinPath: false, |
---|
329 | |
---|
330 | wymPath: false, |
---|
331 | |
---|
332 | iframeBasePath: false, |
---|
333 | |
---|
334 | jQueryPath: false, |
---|
335 | |
---|
336 | styles: false, |
---|
337 | |
---|
338 | stylesheet: false, |
---|
339 | |
---|
340 | skin: "default", |
---|
341 | initSkin: true, |
---|
342 | loadSkin: true, |
---|
343 | |
---|
344 | lang: "en", |
---|
345 | |
---|
346 | direction: "ltr", |
---|
347 | |
---|
348 | boxHtml: "<div class='wym_box'>" |
---|
349 | + "<div class='wym_area_top'>" |
---|
350 | + WYMeditor.TOOLS |
---|
351 | + "</div>" |
---|
352 | + "<div class='wym_area_left'></div>" |
---|
353 | + "<div class='wym_area_right'>" |
---|
354 | + WYMeditor.CONTAINERS |
---|
355 | + WYMeditor.CLASSES |
---|
356 | + "</div>" |
---|
357 | + "<div class='wym_area_main'>" |
---|
358 | + WYMeditor.HTML |
---|
359 | + WYMeditor.IFRAME |
---|
360 | + WYMeditor.STATUS |
---|
361 | + "</div>" |
---|
362 | + "<div class='wym_area_bottom'>" |
---|
363 | + WYMeditor.LOGO |
---|
364 | + "</div>" |
---|
365 | + "</div>", |
---|
366 | |
---|
367 | logoHtml: "<a class='wym_wymeditor_link' " |
---|
368 | + "href='http://www.wymeditor.org/'>WYMeditor</a>", |
---|
369 | |
---|
370 | iframeHtml:"<div class='wym_iframe wym_section'>" |
---|
371 | + "<iframe " |
---|
372 | + "src='" |
---|
373 | + "/page/get_editor_iframe' " |
---|
374 | //+ WYMeditor.IFRAME_BASE_PATH |
---|
375 | //+ "wymiframe.html' " |
---|
376 | + "onload='this.contentWindow.parent.WYMeditor.INSTANCES[" |
---|
377 | + WYMeditor.INDEX + "].initIframe(this)'" |
---|
378 | + "></iframe>" |
---|
379 | + "</div>", |
---|
380 | |
---|
381 | editorStyles: [], |
---|
382 | |
---|
383 | toolsHtml: "<div class='wym_tools wym_section'>" |
---|
384 | + "<h2>{Tools}</h2>" |
---|
385 | + "<ul>" |
---|
386 | + WYMeditor.TOOLS_ITEMS |
---|
387 | + "</ul>" |
---|
388 | + "</div>", |
---|
389 | |
---|
390 | toolsItemHtml: "<li class='" |
---|
391 | + WYMeditor.TOOL_CLASS |
---|
392 | + "'><a href='#' name='" |
---|
393 | + WYMeditor.TOOL_NAME |
---|
394 | + "' title='" |
---|
395 | + WYMeditor.TOOL_TITLE |
---|
396 | + "'>" |
---|
397 | + WYMeditor.TOOL_TITLE |
---|
398 | + "</a></li>", |
---|
399 | |
---|
400 | toolsItems: [ |
---|
401 | {'name': 'Bold', 'title': 'Strong', 'css': 'wym_tools_strong'}, |
---|
402 | {'name': 'Italic', 'title': 'Emphasis', 'css': 'wym_tools_emphasis'}, |
---|
403 | {'name': 'Superscript', 'title': 'Superscript', |
---|
404 | 'css': 'wym_tools_superscript'}, |
---|
405 | {'name': 'Subscript', 'title': 'Subscript', |
---|
406 | 'css': 'wym_tools_subscript'}, |
---|
407 | {'name': 'InsertOrderedList', 'title': 'Ordered_List', |
---|
408 | 'css': 'wym_tools_ordered_list'}, |
---|
409 | {'name': 'InsertUnorderedList', 'title': 'Unordered_List', |
---|
410 | 'css': 'wym_tools_unordered_list'}, |
---|
411 | {'name': 'Indent', 'title': 'Indent', 'css': 'wym_tools_indent'}, |
---|
412 | {'name': 'Outdent', 'title': 'Outdent', 'css': 'wym_tools_outdent'}, |
---|
413 | {'name': 'Undo', 'title': 'Undo', 'css': 'wym_tools_undo'}, |
---|
414 | {'name': 'Redo', 'title': 'Redo', 'css': 'wym_tools_redo'}, |
---|
415 | {'name': 'CreateLink', 'title': 'Link', 'css': 'wym_tools_link'}, |
---|
416 | {'name': 'Unlink', 'title': 'Unlink', 'css': 'wym_tools_unlink'}, |
---|
417 | {'name': 'InsertImage', 'title': 'Image', 'css': 'wym_tools_image'}, |
---|
418 | {'name': 'InsertTable', 'title': 'Table', 'css': 'wym_tools_table'}, |
---|
419 | {'name': 'Paste', 'title': 'Paste_From_Word', |
---|
420 | 'css': 'wym_tools_paste'}, |
---|
421 | {'name': 'ToggleHtml', 'title': 'HTML', 'css': 'wym_tools_html'}, |
---|
422 | {'name': 'Preview', 'title': 'Preview', 'css': 'wym_tools_preview'} |
---|
423 | ], |
---|
424 | |
---|
425 | containersHtml: "<div class='wym_containers wym_section'>" |
---|
426 | + "<h2>{Containers}</h2>" |
---|
427 | + "<ul>" |
---|
428 | + WYMeditor.CONTAINERS_ITEMS |
---|
429 | + "</ul>" |
---|
430 | + "</div>", |
---|
431 | |
---|
432 | containersItemHtml:"<li class='" |
---|
433 | + WYMeditor.CONTAINER_CLASS |
---|
434 | + "'>" |
---|
435 | + "<a href='#' name='" |
---|
436 | + WYMeditor.CONTAINER_NAME |
---|
437 | + "'>" |
---|
438 | + WYMeditor.CONTAINER_TITLE |
---|
439 | + "</a></li>", |
---|
440 | |
---|
441 | containersItems: [ |
---|
442 | {'name': 'P', 'title': 'Paragraph', 'css': 'wym_containers_p'}, |
---|
443 | {'name': 'H1', 'title': 'Heading_1', 'css': 'wym_containers_h1'}, |
---|
444 | {'name': 'H2', 'title': 'Heading_2', 'css': 'wym_containers_h2'}, |
---|
445 | {'name': 'H3', 'title': 'Heading_3', 'css': 'wym_containers_h3'}, |
---|
446 | {'name': 'H4', 'title': 'Heading_4', 'css': 'wym_containers_h4'}, |
---|
447 | {'name': 'H5', 'title': 'Heading_5', 'css': 'wym_containers_h5'}, |
---|
448 | {'name': 'H6', 'title': 'Heading_6', 'css': 'wym_containers_h6'}, |
---|
449 | {'name': 'PRE', 'title': 'Preformatted', 'css': 'wym_containers_pre'}, |
---|
450 | {'name': 'BLOCKQUOTE', 'title': 'Blockquote', |
---|
451 | 'css': 'wym_containers_blockquote'}, |
---|
452 | {'name': 'TH', 'title': 'Table_Header', 'css': 'wym_containers_th'} |
---|
453 | ], |
---|
454 | |
---|
455 | classesHtml: "<div class='wym_classes wym_section'>" |
---|
456 | + "<h2>{Classes}</h2><ul>" |
---|
457 | + WYMeditor.CLASSES_ITEMS |
---|
458 | + "</ul></div>", |
---|
459 | |
---|
460 | classesItemHtml: "<li><a href='#' name='" |
---|
461 | + WYMeditor.CLASS_NAME |
---|
462 | + "'>" |
---|
463 | + WYMeditor.CLASS_TITLE |
---|
464 | + "</a></li>", |
---|
465 | |
---|
466 | classesItems: [], |
---|
467 | |
---|
468 | statusHtml: "<div class='wym_status wym_section'>" |
---|
469 | + "<h2>{Status}</h2>" |
---|
470 | + "</div>", |
---|
471 | |
---|
472 | htmlHtml: "<div class='wym_html wym_section'>" |
---|
473 | + "<h2>{Source_Code}</h2>" |
---|
474 | + "<textarea class='wym_html_val'></textarea>" |
---|
475 | + "</div>", |
---|
476 | |
---|
477 | boxSelector: ".wym_box", |
---|
478 | toolsSelector: ".wym_tools", |
---|
479 | toolsListSelector: " ul", |
---|
480 | containersSelector:".wym_containers", |
---|
481 | classesSelector: ".wym_classes", |
---|
482 | htmlSelector: ".wym_html", |
---|
483 | iframeSelector: ".wym_iframe iframe", |
---|
484 | iframeBodySelector:".wym_iframe", |
---|
485 | statusSelector: ".wym_status", |
---|
486 | toolSelector: ".wym_tools a", |
---|
487 | containerSelector: ".wym_containers a", |
---|
488 | classSelector: ".wym_classes a", |
---|
489 | htmlValSelector: ".wym_html_val", |
---|
490 | |
---|
491 | hrefSelector: ".wym_href", |
---|
492 | srcSelector: ".wym_src", |
---|
493 | titleSelector: ".wym_title", |
---|
494 | altSelector: ".wym_alt", |
---|
495 | textSelector: ".wym_text", |
---|
496 | |
---|
497 | rowsSelector: ".wym_rows", |
---|
498 | colsSelector: ".wym_cols", |
---|
499 | captionSelector: ".wym_caption", |
---|
500 | summarySelector: ".wym_summary", |
---|
501 | |
---|
502 | submitSelector: ".wym_submit", |
---|
503 | cancelSelector: ".wym_cancel", |
---|
504 | previewSelector: "", |
---|
505 | |
---|
506 | dialogTypeSelector: ".wym_dialog_type", |
---|
507 | dialogLinkSelector: ".wym_dialog_link", |
---|
508 | dialogImageSelector: ".wym_dialog_image", |
---|
509 | dialogTableSelector: ".wym_dialog_table", |
---|
510 | dialogPasteSelector: ".wym_dialog_paste", |
---|
511 | dialogPreviewSelector: ".wym_dialog_preview", |
---|
512 | |
---|
513 | updateSelector: ".wymupdate", |
---|
514 | updateEvent: "click", |
---|
515 | |
---|
516 | dialogFeatures: "menubar=no,titlebar=no,toolbar=no,resizable=no" |
---|
517 | + ",width=560,height=300,top=0,left=0", |
---|
518 | dialogFeaturesPreview: "menubar=no,titlebar=no,toolbar=no,resizable=no" |
---|
519 | + ",scrollbars=yes,width=560,height=300,top=0,left=0", |
---|
520 | |
---|
521 | dialogHtml: "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'" |
---|
522 | + " 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>" |
---|
523 | + "<html dir='" |
---|
524 | + WYMeditor.DIRECTION |
---|
525 | + "'><head>" |
---|
526 | + "<link rel='stylesheet' type='text/css' media='screen'" |
---|
527 | + " href='" |
---|
528 | + WYMeditor.CSS_PATH |
---|
529 | + "' />" |
---|
530 | + "<title>" |
---|
531 | + WYMeditor.DIALOG_TITLE |
---|
532 | + "</title>" |
---|
533 | + "<script type='text/javascript'" |
---|
534 | + " src='" |
---|
535 | + WYMeditor.JQUERY_PATH |
---|
536 | + "'></script>" |
---|
537 | + "<script type='text/javascript'" |
---|
538 | + " src='" |
---|
539 | + WYMeditor.WYM_PATH |
---|
540 | + "'></script>" |
---|
541 | + "</head>" |
---|
542 | + WYMeditor.DIALOG_BODY |
---|
543 | + "</html>", |
---|
544 | |
---|
545 | dialogLinkHtml: "<body class='wym_dialog wym_dialog_link'" |
---|
546 | + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'" |
---|
547 | + ">" |
---|
548 | + "<form>" |
---|
549 | + "<fieldset>" |
---|
550 | + "<input type='hidden' class='wym_dialog_type' value='" |
---|
551 | + WYMeditor.DIALOG_LINK |
---|
552 | + "' />" |
---|
553 | + "<legend>{Link}</legend>" |
---|
554 | + "<div class='row'>" |
---|
555 | + "<label>{URL}</label>" |
---|
556 | + "<input type='text' class='wym_href' value='' size='40' />" |
---|
557 | + "</div>" |
---|
558 | + "<div class='row'>" |
---|
559 | + "<label>{Title}</label>" |
---|
560 | + "<input type='text' class='wym_title' value='' size='40' />" |
---|
561 | + "</div>" |
---|
562 | + "<div class='row row-indent'>" |
---|
563 | + "<input class='wym_submit' type='button'" |
---|
564 | + " value='{Submit}' />" |
---|
565 | + "<input class='wym_cancel' type='button'" |
---|
566 | + "value='{Cancel}' />" |
---|
567 | + "</div>" |
---|
568 | + "</fieldset>" |
---|
569 | + "</form>" |
---|
570 | + "</body>", |
---|
571 | |
---|
572 | dialogImageHtml: "<body class='wym_dialog wym_dialog_image'" |
---|
573 | + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'" |
---|
574 | + ">" |
---|
575 | + "<form>" |
---|
576 | + "<fieldset>" |
---|
577 | + "<input type='hidden' class='wym_dialog_type' value='" |
---|
578 | + WYMeditor.DIALOG_IMAGE |
---|
579 | + "' />" |
---|
580 | + "<legend>{Image}</legend>" |
---|
581 | + "<div class='row'>" |
---|
582 | + "<label>{URL}</label>" |
---|
583 | + "<input type='text' class='wym_src' value='' size='40' />" |
---|
584 | + "</div>" |
---|
585 | + "<div class='row'>" |
---|
586 | + "<label>{Alternative_Text}</label>" |
---|
587 | + "<input type='text' class='wym_alt' value='' size='40' />" |
---|
588 | + "</div>" |
---|
589 | + "<div class='row'>" |
---|
590 | + "<label>{Title}</label>" |
---|
591 | + "<input type='text' class='wym_title' value='' size='40' />" |
---|
592 | + "</div>" |
---|
593 | + "<div class='row row-indent'>" |
---|
594 | + "<input class='wym_submit' type='button'" |
---|
595 | + " value='{Submit}' />" |
---|
596 | + "<input class='wym_cancel' type='button'" |
---|
597 | + "value='{Cancel}' />" |
---|
598 | + "</div>" |
---|
599 | + "</fieldset>" |
---|
600 | + "</form>" |
---|
601 | + "</body>", |
---|
602 | |
---|
603 | dialogTableHtml: "<body class='wym_dialog wym_dialog_table'" |
---|
604 | + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'" |
---|
605 | + ">" |
---|
606 | + "<form>" |
---|
607 | + "<fieldset>" |
---|
608 | + "<input type='hidden' class='wym_dialog_type' value='" |
---|
609 | + WYMeditor.DIALOG_TABLE |
---|
610 | + "' />" |
---|
611 | + "<legend>{Table}</legend>" |
---|
612 | + "<div class='row'>" |
---|
613 | + "<label>{Caption}</label>" |
---|
614 | + "<input type='text' class='wym_caption' value='' size='40' />" |
---|
615 | + "</div>" |
---|
616 | + "<div class='row'>" |
---|
617 | + "<label>{Summary}</label>" |
---|
618 | + "<input type='text' class='wym_summary' value='' size='40' />" |
---|
619 | + "</div>" |
---|
620 | + "<div class='row'>" |
---|
621 | + "<label>{Number_Of_Rows}</label>" |
---|
622 | + "<input type='text' class='wym_rows' value='3' size='3' />" |
---|
623 | + "</div>" |
---|
624 | + "<div class='row'>" |
---|
625 | + "<label>{Number_Of_Cols}</label>" |
---|
626 | + "<input type='text' class='wym_cols' value='2' size='3' />" |
---|
627 | + "</div>" |
---|
628 | + "<div class='row row-indent'>" |
---|
629 | + "<input class='wym_submit' type='button'" |
---|
630 | + " value='{Submit}' />" |
---|
631 | + "<input class='wym_cancel' type='button'" |
---|
632 | + "value='{Cancel}' />" |
---|
633 | + "</div>" |
---|
634 | + "</fieldset>" |
---|
635 | + "</form>" |
---|
636 | + "</body>", |
---|
637 | |
---|
638 | dialogPasteHtml: "<body class='wym_dialog wym_dialog_paste'" |
---|
639 | + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'" |
---|
640 | + ">" |
---|
641 | + "<form>" |
---|
642 | + "<input type='hidden' class='wym_dialog_type' value='" |
---|
643 | + WYMeditor.DIALOG_PASTE |
---|
644 | + "' />" |
---|
645 | + "<fieldset>" |
---|
646 | + "<legend>{Paste_From_Word}</legend>" |
---|
647 | + "<div class='row'>" |
---|
648 | + "<textarea class='wym_text' rows='10' cols='50'></textarea>" |
---|
649 | + "</div>" |
---|
650 | + "<div class='row'>" |
---|
651 | + "<input class='wym_submit' type='button'" |
---|
652 | + " value='{Submit}' />" |
---|
653 | + "<input class='wym_cancel' type='button'" |
---|
654 | + "value='{Cancel}' />" |
---|
655 | + "</div>" |
---|
656 | + "</fieldset>" |
---|
657 | + "</form>" |
---|
658 | + "</body>", |
---|
659 | |
---|
660 | dialogPreviewHtml: "<body class='wym_dialog wym_dialog_preview'" |
---|
661 | + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'" |
---|
662 | + "></body>", |
---|
663 | |
---|
664 | dialogStyles: [], |
---|
665 | |
---|
666 | stringDelimiterLeft: "{", |
---|
667 | stringDelimiterRight:"}", |
---|
668 | |
---|
669 | preInit: null, |
---|
670 | preBind: null, |
---|
671 | postInit: null, |
---|
672 | |
---|
673 | preInitDialog: null, |
---|
674 | postInitDialog: null |
---|
675 | |
---|
676 | }, options); |
---|
677 | |
---|
678 | return this.each(function() { |
---|
679 | |
---|
680 | new WYMeditor.editor(jQuery(this),options); |
---|
681 | }); |
---|
682 | }; |
---|
683 | |
---|
684 | /* @name extend |
---|
685 | * @description Returns the WYMeditor instance based on its index |
---|
686 | */ |
---|
687 | jQuery.extend({ |
---|
688 | wymeditors: function(i) { |
---|
689 | return (WYMeditor.INSTANCES[i]); |
---|
690 | } |
---|
691 | }); |
---|
692 | |
---|
693 | |
---|
694 | /********** WYMeditor **********/ |
---|
695 | |
---|
696 | /* @name Wymeditor |
---|
697 | * @description WYMeditor class |
---|
698 | */ |
---|
699 | |
---|
700 | /* @name init |
---|
701 | * @description Initializes a WYMeditor instance |
---|
702 | */ |
---|
703 | WYMeditor.editor.prototype.init = function() { |
---|
704 | |
---|
705 | //load subclass - browser specific |
---|
706 | //unsupported browsers: do nothing |
---|
707 | if (jQuery.browser.msie) { |
---|
708 | var WymClass = new WYMeditor.WymClassExplorer(this); |
---|
709 | } |
---|
710 | else if (jQuery.browser.mozilla) { |
---|
711 | var WymClass = new WYMeditor.WymClassMozilla(this); |
---|
712 | } |
---|
713 | else if (jQuery.browser.opera) { |
---|
714 | var WymClass = new WYMeditor.WymClassOpera(this); |
---|
715 | } |
---|
716 | else if (jQuery.browser.safari) { |
---|
717 | var WymClass = new WYMeditor.WymClassSafari(this); |
---|
718 | } |
---|
719 | |
---|
720 | if(WymClass) { |
---|
721 | |
---|
722 | if(jQuery.isFunction(this._options.preInit)) this._options.preInit(this); |
---|
723 | |
---|
724 | var SaxListener = new WYMeditor.XhtmlSaxListener(); |
---|
725 | jQuery.extend(SaxListener, WymClass); |
---|
726 | this.parser = new WYMeditor.XhtmlParser(SaxListener); |
---|
727 | |
---|
728 | if(this._options.styles || this._options.stylesheet){ |
---|
729 | this.configureEditorUsingRawCss(); |
---|
730 | } |
---|
731 | |
---|
732 | this.helper = new WYMeditor.XmlHelper(); |
---|
733 | |
---|
734 | //extend the Wymeditor object |
---|
735 | //don't use jQuery.extend since 1.1.4 |
---|
736 | //jQuery.extend(this, WymClass); |
---|
737 | for (var prop in WymClass) { this[prop] = WymClass[prop]; } |
---|
738 | |
---|
739 | //load wymbox |
---|
740 | this._box = jQuery(this._element).hide().after(this._options.boxHtml).next().addClass('wym_box_' + this._index); |
---|
741 | |
---|
742 | //store the instance index in wymbox and element replaced by editor instance |
---|
743 | //but keep it compatible with jQuery < 1.2.3, see #122 |
---|
744 | if( jQuery.isFunction( jQuery.fn.data ) ) { |
---|
745 | jQuery.data(this._box.get(0), WYMeditor.WYM_INDEX, this._index); |
---|
746 | jQuery.data(this._element.get(0), WYMeditor.WYM_INDEX, this._index); |
---|
747 | } |
---|
748 | |
---|
749 | var h = WYMeditor.Helper; |
---|
750 | |
---|
751 | //construct the iframe |
---|
752 | var iframeHtml = this._options.iframeHtml; |
---|
753 | iframeHtml = h.replaceAll(iframeHtml, WYMeditor.INDEX, this._index); |
---|
754 | iframeHtml = h.replaceAll(iframeHtml, WYMeditor.IFRAME_BASE_PATH, this._options.iframeBasePath); |
---|
755 | |
---|
756 | //construct wymbox |
---|
757 | var boxHtml = jQuery(this._box).html(); |
---|
758 | |
---|
759 | boxHtml = h.replaceAll(boxHtml, WYMeditor.LOGO, this._options.logoHtml); |
---|
760 | boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS, this._options.toolsHtml); |
---|
761 | boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS,this._options.containersHtml); |
---|
762 | boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES, this._options.classesHtml); |
---|
763 | boxHtml = h.replaceAll(boxHtml, WYMeditor.HTML, this._options.htmlHtml); |
---|
764 | boxHtml = h.replaceAll(boxHtml, WYMeditor.IFRAME, iframeHtml); |
---|
765 | boxHtml = h.replaceAll(boxHtml, WYMeditor.STATUS, this._options.statusHtml); |
---|
766 | |
---|
767 | //construct tools list |
---|
768 | var aTools = eval(this._options.toolsItems); |
---|
769 | var sTools = ""; |
---|
770 | |
---|
771 | for(var i = 0; i < aTools.length; i++) { |
---|
772 | var oTool = aTools[i]; |
---|
773 | if(oTool.name && oTool.title) |
---|
774 | var sTool = this._options.toolsItemHtml; |
---|
775 | var sTool = h.replaceAll(sTool, WYMeditor.TOOL_NAME, oTool.name); |
---|
776 | sTool = h.replaceAll(sTool, WYMeditor.TOOL_TITLE, this._options.stringDelimiterLeft |
---|
777 | + oTool.title |
---|
778 | + this._options.stringDelimiterRight); |
---|
779 | sTool = h.replaceAll(sTool, WYMeditor.TOOL_CLASS, oTool.css); |
---|
780 | sTools += sTool; |
---|
781 | } |
---|
782 | |
---|
783 | boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS_ITEMS, sTools); |
---|
784 | |
---|
785 | //construct classes list |
---|
786 | var aClasses = eval(this._options.classesItems); |
---|
787 | var sClasses = ""; |
---|
788 | |
---|
789 | for(var i = 0; i < aClasses.length; i++) { |
---|
790 | var oClass = aClasses[i]; |
---|
791 | if(oClass.name && oClass.title) |
---|
792 | var sClass = this._options.classesItemHtml; |
---|
793 | sClass = h.replaceAll(sClass, WYMeditor.CLASS_NAME, oClass.name); |
---|
794 | sClass = h.replaceAll(sClass, WYMeditor.CLASS_TITLE, oClass.title); |
---|
795 | sClasses += sClass; |
---|
796 | } |
---|
797 | |
---|
798 | boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES_ITEMS, sClasses); |
---|
799 | |
---|
800 | //construct containers list |
---|
801 | var aContainers = eval(this._options.containersItems); |
---|
802 | var sContainers = ""; |
---|
803 | |
---|
804 | for(var i = 0; i < aContainers.length; i++) { |
---|
805 | var oContainer = aContainers[i]; |
---|
806 | if(oContainer.name && oContainer.title) |
---|
807 | var sContainer = this._options.containersItemHtml; |
---|
808 | sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_NAME, oContainer.name); |
---|
809 | sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_TITLE, |
---|
810 | this._options.stringDelimiterLeft |
---|
811 | + oContainer.title |
---|
812 | + this._options.stringDelimiterRight); |
---|
813 | sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_CLASS, oContainer.css); |
---|
814 | sContainers += sContainer; |
---|
815 | } |
---|
816 | |
---|
817 | boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS_ITEMS, sContainers); |
---|
818 | |
---|
819 | //l10n |
---|
820 | boxHtml = this.replaceStrings(boxHtml); |
---|
821 | |
---|
822 | //load html in wymbox |
---|
823 | jQuery(this._box).html(boxHtml); |
---|
824 | |
---|
825 | //hide the html value |
---|
826 | jQuery(this._box).find(this._options.htmlSelector).hide(); |
---|
827 | |
---|
828 | //enable the skin |
---|
829 | this.loadSkin(); |
---|
830 | |
---|
831 | } |
---|
832 | }; |
---|
833 | |
---|
834 | WYMeditor.editor.prototype.bindEvents = function() { |
---|
835 | |
---|
836 | //copy the instance |
---|
837 | var wym = this; |
---|
838 | |
---|
839 | //handle click event on tools buttons |
---|
840 | jQuery(this._box).find(this._options.toolSelector).click(function() { |
---|
841 | wym._iframe.contentWindow.focus(); //See #154 |
---|
842 | wym.exec(jQuery(this).attr(WYMeditor.NAME)); |
---|
843 | return(false); |
---|
844 | }); |
---|
845 | |
---|
846 | //handle click event on containers buttons |
---|
847 | jQuery(this._box).find(this._options.containerSelector).click(function() { |
---|
848 | wym.container(jQuery(this).attr(WYMeditor.NAME)); |
---|
849 | return(false); |
---|
850 | }); |
---|
851 | |
---|
852 | //handle keyup event on html value: set the editor value |
---|
853 | //handle focus/blur events to check if the element has focus, see #147 |
---|
854 | jQuery(this._box).find(this._options.htmlValSelector) |
---|
855 | .keyup(function() { jQuery(wym._doc.body).html(jQuery(this).val());}) |
---|
856 | .focus(function() { jQuery(this).toggleClass('hasfocus'); }) |
---|
857 | .blur(function() { jQuery(this).toggleClass('hasfocus'); }); |
---|
858 | |
---|
859 | //handle click event on classes buttons |
---|
860 | jQuery(this._box).find(this._options.classSelector).click(function() { |
---|
861 | |
---|
862 | var aClasses = eval(wym._options.classesItems); |
---|
863 | var sName = jQuery(this).attr(WYMeditor.NAME); |
---|
864 | |
---|
865 | var oClass = WYMeditor.Helper.findByName(aClasses, sName); |
---|
866 | |
---|
867 | if(oClass) { |
---|
868 | var jqexpr = oClass.expr; |
---|
869 | wym.toggleClass(sName, jqexpr); |
---|
870 | } |
---|
871 | wym._iframe.contentWindow.focus(); //See #154 |
---|
872 | return(false); |
---|
873 | }); |
---|
874 | |
---|
875 | //handle event on update element |
---|
876 | jQuery(this._options.updateSelector) |
---|
877 | .bind(this._options.updateEvent, function() { |
---|
878 | wym.update(); |
---|
879 | }); |
---|
880 | }; |
---|
881 | |
---|
882 | WYMeditor.editor.prototype.ready = function() { |
---|
883 | return(this._doc != null); |
---|
884 | }; |
---|
885 | |
---|
886 | |
---|
887 | /********** METHODS **********/ |
---|
888 | |
---|
889 | /* @name box |
---|
890 | * @description Returns the WYMeditor container |
---|
891 | */ |
---|
892 | WYMeditor.editor.prototype.box = function() { |
---|
893 | return(this._box); |
---|
894 | }; |
---|
895 | |
---|
896 | /* @name html |
---|
897 | * @description Get/Set the html value |
---|
898 | */ |
---|
899 | WYMeditor.editor.prototype.html = function(html) { |
---|
900 | |
---|
901 | if(typeof html === 'string') jQuery(this._doc.body).html(html); |
---|
902 | else return(jQuery(this._doc.body).html()); |
---|
903 | }; |
---|
904 | |
---|
905 | /* @name xhtml |
---|
906 | * @description Cleans up the HTML |
---|
907 | */ |
---|
908 | WYMeditor.editor.prototype.xhtml = function() { |
---|
909 | return this.parser.parse(this.html()); |
---|
910 | }; |
---|
911 | |
---|
912 | /* @name exec |
---|
913 | * @description Executes a button command |
---|
914 | */ |
---|
915 | WYMeditor.editor.prototype.exec = function(cmd) { |
---|
916 | |
---|
917 | //base function for execCommand |
---|
918 | //open a dialog or exec |
---|
919 | switch(cmd) { |
---|
920 | case WYMeditor.CREATE_LINK: |
---|
921 | var container = this.container(); |
---|
922 | if(container || this._selected_image) this.dialog(WYMeditor.DIALOG_LINK); |
---|
923 | break; |
---|
924 | |
---|
925 | case WYMeditor.INSERT_IMAGE: |
---|
926 | this.dialog(WYMeditor.DIALOG_IMAGE); |
---|
927 | break; |
---|
928 | |
---|
929 | case WYMeditor.INSERT_TABLE: |
---|
930 | this.dialog(WYMeditor.DIALOG_TABLE); |
---|
931 | break; |
---|
932 | |
---|
933 | case WYMeditor.PASTE: |
---|
934 | this.dialog(WYMeditor.DIALOG_PASTE); |
---|
935 | break; |
---|
936 | |
---|
937 | case WYMeditor.TOGGLE_HTML: |
---|
938 | this.update(); |
---|
939 | this.toggleHtml(); |
---|
940 | |
---|
941 | //partially fixes #121 when the user manually inserts an image |
---|
942 | if(!jQuery(this._box).find(this._options.htmlSelector).is(':visible')) |
---|
943 | this.listen(); |
---|
944 | break; |
---|
945 | |
---|
946 | case WYMeditor.PREVIEW: |
---|
947 | this.dialog(WYMeditor.PREVIEW, this._options.dialogFeaturesPreview); |
---|
948 | break; |
---|
949 | |
---|
950 | default: |
---|
951 | this._exec(cmd); |
---|
952 | break; |
---|
953 | } |
---|
954 | }; |
---|
955 | |
---|
956 | /* @name container |
---|
957 | * @description Get/Set the selected container |
---|
958 | */ |
---|
959 | WYMeditor.editor.prototype.container = function(sType) { |
---|
960 | |
---|
961 | if(sType) { |
---|
962 | |
---|
963 | var container = null; |
---|
964 | |
---|
965 | if(sType.toLowerCase() == WYMeditor.TH) { |
---|
966 | |
---|
967 | container = this.container(); |
---|
968 | |
---|
969 | //find the TD or TH container |
---|
970 | switch(container.tagName.toLowerCase()) { |
---|
971 | |
---|
972 | case WYMeditor.TD: case WYMeditor.TH: |
---|
973 | break; |
---|
974 | default: |
---|
975 | var aTypes = new Array(WYMeditor.TD,WYMeditor.TH); |
---|
976 | container = this.findUp(this.container(), aTypes); |
---|
977 | break; |
---|
978 | } |
---|
979 | |
---|
980 | //if it exists, switch |
---|
981 | if(container!=null) { |
---|
982 | |
---|
983 | sType = (container.tagName.toLowerCase() == WYMeditor.TD)? WYMeditor.TH: WYMeditor.TD; |
---|
984 | this.switchTo(container,sType); |
---|
985 | this.update(); |
---|
986 | } |
---|
987 | } else { |
---|
988 | |
---|
989 | //set the container type |
---|
990 | var aTypes=new Array(WYMeditor.P,WYMeditor.H1,WYMeditor.H2,WYMeditor.H3,WYMeditor.H4,WYMeditor.H5, |
---|
991 | WYMeditor.H6,WYMeditor.PRE,WYMeditor.BLOCKQUOTE); |
---|
992 | container = this.findUp(this.container(), aTypes); |
---|
993 | |
---|
994 | if(container) { |
---|
995 | |
---|
996 | var newNode = null; |
---|
997 | |
---|
998 | //blockquotes must contain a block level element |
---|
999 | if(sType.toLowerCase() == WYMeditor.BLOCKQUOTE) { |
---|
1000 | |
---|
1001 | var blockquote = this.findUp(this.container(), WYMeditor.BLOCKQUOTE); |
---|
1002 | |
---|
1003 | if(blockquote == null) { |
---|
1004 | |
---|
1005 | newNode = this._doc.createElement(sType); |
---|
1006 | container.parentNode.insertBefore(newNode,container); |
---|
1007 | newNode.appendChild(container); |
---|
1008 | this.setFocusToNode(newNode.firstChild); |
---|
1009 | |
---|
1010 | } else { |
---|
1011 | |
---|
1012 | var nodes = blockquote.childNodes; |
---|
1013 | var lgt = nodes.length; |
---|
1014 | var firstNode = null; |
---|
1015 | |
---|
1016 | if(lgt > 0) firstNode = nodes.item(0); |
---|
1017 | for(var x=0; x<lgt; x++) { |
---|
1018 | blockquote.parentNode.insertBefore(nodes.item(0),blockquote); |
---|
1019 | } |
---|
1020 | blockquote.parentNode.removeChild(blockquote); |
---|
1021 | if(firstNode) this.setFocusToNode(firstNode); |
---|
1022 | } |
---|
1023 | } |
---|
1024 | |
---|
1025 | else this.switchTo(container,sType); |
---|
1026 | |
---|
1027 | this.update(); |
---|
1028 | } |
---|
1029 | } |
---|
1030 | } |
---|
1031 | else return(this.selected()); |
---|
1032 | }; |
---|
1033 | |
---|
1034 | /* @name toggleClass |
---|
1035 | * @description Toggles class on selected element, or one of its parents |
---|
1036 | */ |
---|
1037 | WYMeditor.editor.prototype.toggleClass = function(sClass, jqexpr) { |
---|
1038 | |
---|
1039 | var container = (this._selected_image |
---|
1040 | ? this._selected_image |
---|
1041 | : jQuery(this.selected())); |
---|
1042 | container = jQuery(container).parentsOrSelf(jqexpr); |
---|
1043 | jQuery(container).toggleClass(sClass); |
---|
1044 | |
---|
1045 | if(!jQuery(container).attr(WYMeditor.CLASS)) jQuery(container).removeAttr(this._class); |
---|
1046 | |
---|
1047 | }; |
---|
1048 | |
---|
1049 | /* @name findUp |
---|
1050 | * @description Returns the first parent or self container, based on its type |
---|
1051 | */ |
---|
1052 | WYMeditor.editor.prototype.findUp = function(node, filter) { |
---|
1053 | |
---|
1054 | //filter is a string or an array of strings |
---|
1055 | |
---|
1056 | if(node) { |
---|
1057 | |
---|
1058 | var tagname = node.tagName.toLowerCase(); |
---|
1059 | |
---|
1060 | if(typeof(filter) == WYMeditor.STRING) { |
---|
1061 | |
---|
1062 | while(tagname != filter && tagname != WYMeditor.BODY) { |
---|
1063 | |
---|
1064 | node = node.parentNode; |
---|
1065 | tagname = node.tagName.toLowerCase(); |
---|
1066 | } |
---|
1067 | |
---|
1068 | } else { |
---|
1069 | |
---|
1070 | var bFound = false; |
---|
1071 | |
---|
1072 | while(!bFound && tagname != WYMeditor.BODY) { |
---|
1073 | for(var i = 0; i < filter.length; i++) { |
---|
1074 | if(tagname == filter[i]) { |
---|
1075 | bFound = true; |
---|
1076 | break; |
---|
1077 | } |
---|
1078 | } |
---|
1079 | if(!bFound) { |
---|
1080 | node = node.parentNode; |
---|
1081 | tagname = node.tagName.toLowerCase(); |
---|
1082 | } |
---|
1083 | } |
---|
1084 | } |
---|
1085 | |
---|
1086 | if(tagname != WYMeditor.BODY) return(node); |
---|
1087 | else return(null); |
---|
1088 | |
---|
1089 | } else return(null); |
---|
1090 | }; |
---|
1091 | |
---|
1092 | /* @name switchTo |
---|
1093 | * @description Switch the node's type |
---|
1094 | */ |
---|
1095 | WYMeditor.editor.prototype.switchTo = function(node,sType) { |
---|
1096 | |
---|
1097 | var newNode = this._doc.createElement(sType); |
---|
1098 | var html = jQuery(node).html(); |
---|
1099 | node.parentNode.replaceChild(newNode,node); |
---|
1100 | jQuery(newNode).html(html); |
---|
1101 | this.setFocusToNode(newNode); |
---|
1102 | }; |
---|
1103 | |
---|
1104 | WYMeditor.editor.prototype.replaceStrings = function(sVal) { |
---|
1105 | //check if the language file has already been loaded |
---|
1106 | //if not, get it via a synchronous ajax call |
---|
1107 | if(!WYMeditor.STRINGS[this._options.lang]) { |
---|
1108 | try { |
---|
1109 | eval(jQuery.ajax({url:this._options.langPath |
---|
1110 | + this._options.lang + '.js', async:false}).responseText); |
---|
1111 | } catch(e) { |
---|
1112 | WYMeditor.console.error("WYMeditor: error while parsing language file."); |
---|
1113 | return sVal; |
---|
1114 | } |
---|
1115 | } |
---|
1116 | |
---|
1117 | //replace all the strings in sVal and return it |
---|
1118 | for (var key in WYMeditor.STRINGS[this._options.lang]) { |
---|
1119 | sVal = WYMeditor.Helper.replaceAll(sVal, this._options.stringDelimiterLeft + key |
---|
1120 | + this._options.stringDelimiterRight, |
---|
1121 | WYMeditor.STRINGS[this._options.lang][key]); |
---|
1122 | }; |
---|
1123 | return(sVal); |
---|
1124 | }; |
---|
1125 | |
---|
1126 | WYMeditor.editor.prototype.encloseString = function(sVal) { |
---|
1127 | |
---|
1128 | return(this._options.stringDelimiterLeft |
---|
1129 | + sVal |
---|
1130 | + this._options.stringDelimiterRight); |
---|
1131 | }; |
---|
1132 | |
---|
1133 | /* @name status |
---|
1134 | * @description Prints a status message |
---|
1135 | */ |
---|
1136 | WYMeditor.editor.prototype.status = function(sMessage) { |
---|
1137 | |
---|
1138 | //print status message |
---|
1139 | jQuery(this._box).find(this._options.statusSelector).html(sMessage); |
---|
1140 | }; |
---|
1141 | |
---|
1142 | /* @name update |
---|
1143 | * @description Updates the element and textarea values |
---|
1144 | */ |
---|
1145 | WYMeditor.editor.prototype.update = function() { |
---|
1146 | |
---|
1147 | var html = this.xhtml(); |
---|
1148 | jQuery(this._element).val(html); |
---|
1149 | jQuery(this._box).find(this._options.htmlValSelector).not('.hasfocus').val(html); //#147 |
---|
1150 | }; |
---|
1151 | |
---|
1152 | /* @name dialog |
---|
1153 | * @description Opens a dialog box |
---|
1154 | */ |
---|
1155 | WYMeditor.editor.prototype.dialog = function( dialogType, dialogFeatures, bodyHtml ) { |
---|
1156 | var features = dialogFeatures || this._wym._options.dialogFeatures; |
---|
1157 | var wDialog = window.open('', 'dialog', features); |
---|
1158 | |
---|
1159 | if(wDialog) { |
---|
1160 | |
---|
1161 | var sBodyHtml = ""; |
---|
1162 | |
---|
1163 | switch( dialogType ) { |
---|
1164 | |
---|
1165 | case(WYMeditor.DIALOG_LINK): |
---|
1166 | sBodyHtml = this._options.dialogLinkHtml; |
---|
1167 | break; |
---|
1168 | case(WYMeditor.DIALOG_IMAGE): |
---|
1169 | sBodyHtml = this._options.dialogImageHtml; |
---|
1170 | break; |
---|
1171 | case(WYMeditor.DIALOG_TABLE): |
---|
1172 | sBodyHtml = this._options.dialogTableHtml; |
---|
1173 | break; |
---|
1174 | case(WYMeditor.DIALOG_PASTE): |
---|
1175 | sBodyHtml = this._options.dialogPasteHtml; |
---|
1176 | break; |
---|
1177 | case(WYMeditor.PREVIEW): |
---|
1178 | sBodyHtml = this._options.dialogPreviewHtml; |
---|
1179 | break; |
---|
1180 | default: |
---|
1181 | sBodyHtml = bodyHtml; |
---|
1182 | } |
---|
1183 | var h = WYMeditor.Helper; |
---|
1184 | |
---|
1185 | //construct the dialog |
---|
1186 | var dialogHtml = this._options.dialogHtml; |
---|
1187 | dialogHtml = h.replaceAll(dialogHtml, WYMeditor.BASE_PATH, this._options.basePath); |
---|
1188 | dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIRECTION, this._options.direction); |
---|
1189 | dialogHtml = h.replaceAll(dialogHtml, WYMeditor.CSS_PATH, this._options.skinPath + WYMeditor.SKINS_DEFAULT_CSS); |
---|
1190 | dialogHtml = h.replaceAll(dialogHtml, WYMeditor.WYM_PATH, this._options.wymPath); |
---|
1191 | dialogHtml = h.replaceAll(dialogHtml, WYMeditor.JQUERY_PATH, this._options.jQueryPath); |
---|
1192 | dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIALOG_TITLE, this.encloseString( dialogType )); |
---|
1193 | dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIALOG_BODY, sBodyHtml); |
---|
1194 | dialogHtml = h.replaceAll(dialogHtml, WYMeditor.INDEX, this._index); |
---|
1195 | |
---|
1196 | dialogHtml = this.replaceStrings(dialogHtml); |
---|
1197 | var doc = wDialog.document; |
---|
1198 | doc.write(dialogHtml); |
---|
1199 | doc.close(); |
---|
1200 | } |
---|
1201 | }; |
---|
1202 | |
---|
1203 | /* @name toggleHtml |
---|
1204 | * @description Shows/Hides the HTML |
---|
1205 | */ |
---|
1206 | WYMeditor.editor.prototype.toggleHtml = function() { |
---|
1207 | jQuery(this._box).find(this._options.htmlSelector).toggle(); |
---|
1208 | }; |
---|
1209 | |
---|
1210 | WYMeditor.editor.prototype.uniqueStamp = function() { |
---|
1211 | var now = new Date(); |
---|
1212 | return("wym-" + now.getTime()); |
---|
1213 | }; |
---|
1214 | |
---|
1215 | WYMeditor.editor.prototype.paste = function(sData) { |
---|
1216 | |
---|
1217 | var sTmp; |
---|
1218 | var container = this.selected(); |
---|
1219 | |
---|
1220 | //split the data, using double newlines as the separator |
---|
1221 | var aP = sData.split(this._newLine + this._newLine); |
---|
1222 | var rExp = new RegExp(this._newLine, "g"); |
---|
1223 | |
---|
1224 | //add a P for each item |
---|
1225 | if(container && container.tagName.toLowerCase() != WYMeditor.BODY) { |
---|
1226 | for(x = aP.length - 1; x >= 0; x--) { |
---|
1227 | sTmp = aP[x]; |
---|
1228 | //simple newlines are replaced by a break |
---|
1229 | sTmp = sTmp.replace(rExp, "<br />"); |
---|
1230 | jQuery(container).after("<p>" + sTmp + "</p>"); |
---|
1231 | } |
---|
1232 | } else { |
---|
1233 | for(x = 0; x < aP.length; x++) { |
---|
1234 | sTmp = aP[x]; |
---|
1235 | //simple newlines are replaced by a break |
---|
1236 | sTmp = sTmp.replace(rExp, "<br />"); |
---|
1237 | jQuery(this._doc.body).append("<p>" + sTmp + "</p>"); |
---|
1238 | } |
---|
1239 | |
---|
1240 | } |
---|
1241 | }; |
---|
1242 | |
---|
1243 | WYMeditor.editor.prototype.insert = function(html) { |
---|
1244 | // Do we have a selection? |
---|
1245 | if (this._iframe.contentWindow.getSelection().focusNode != null) { |
---|
1246 | // Overwrite selection with provided html |
---|
1247 | this._exec( WYMeditor.INSERT_HTML, html); |
---|
1248 | } else { |
---|
1249 | // Fall back to the internal paste function if there's no selection |
---|
1250 | this.paste(html) |
---|
1251 | } |
---|
1252 | }; |
---|
1253 | |
---|
1254 | WYMeditor.editor.prototype.wrap = function(left, right) { |
---|
1255 | // Do we have a selection? |
---|
1256 | if (this._iframe.contentWindow.getSelection().focusNode != null) { |
---|
1257 | // Wrap selection with provided html |
---|
1258 | this._exec( WYMeditor.INSERT_HTML, left + this._iframe.contentWindow.getSelection().toString() + right); |
---|
1259 | } |
---|
1260 | }; |
---|
1261 | |
---|
1262 | WYMeditor.editor.prototype.unwrap = function() { |
---|
1263 | // Do we have a selection? |
---|
1264 | if (this._iframe.contentWindow.getSelection().focusNode != null) { |
---|
1265 | // Unwrap selection |
---|
1266 | this._exec( WYMeditor.INSERT_HTML, this._iframe.contentWindow.getSelection().toString() ); |
---|
1267 | } |
---|
1268 | }; |
---|
1269 | |
---|
1270 | WYMeditor.editor.prototype.addCssRules = function(doc, aCss) { |
---|
1271 | var styles = doc.styleSheets[0]; |
---|
1272 | if(styles) { |
---|
1273 | for(var i = 0; i < aCss.length; i++) { |
---|
1274 | var oCss = aCss[i]; |
---|
1275 | if(oCss.name && oCss.css) this.addCssRule(styles, oCss); |
---|
1276 | } |
---|
1277 | } |
---|
1278 | }; |
---|
1279 | |
---|
1280 | /********** CONFIGURATION **********/ |
---|
1281 | |
---|
1282 | WYMeditor.editor.prototype.computeBasePath = function() { |
---|
1283 | return jQuery(jQuery.grep(jQuery('script'), function(s){ |
---|
1284 | return (s.src && s.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/ )) |
---|
1285 | })).attr('src').replace(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/, ''); |
---|
1286 | }; |
---|
1287 | |
---|
1288 | WYMeditor.editor.prototype.computeWymPath = function() { |
---|
1289 | return jQuery(jQuery.grep(jQuery('script'), function(s){ |
---|
1290 | return (s.src && s.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/ )) |
---|
1291 | })).attr('src'); |
---|
1292 | }; |
---|
1293 | |
---|
1294 | WYMeditor.editor.prototype.computeJqueryPath = function() { |
---|
1295 | return jQuery(jQuery.grep(jQuery('script'), function(s){ |
---|
1296 | return (s.src && s.src.match(/jquery(-(.*)){0,1}(\.pack|\.min|\.packed)?\.js(\?.*)?$/ )) |
---|
1297 | })).attr('src'); |
---|
1298 | }; |
---|
1299 | |
---|
1300 | WYMeditor.editor.prototype.computeCssPath = function() { |
---|
1301 | return jQuery(jQuery.grep(jQuery('link'), function(s){ |
---|
1302 | return (s.href && s.href.match(/wymeditor\/skins\/(.*)screen\.css(\?.*)?$/ )) |
---|
1303 | })).attr('href'); |
---|
1304 | }; |
---|
1305 | |
---|
1306 | WYMeditor.editor.prototype.configureEditorUsingRawCss = function() { |
---|
1307 | |
---|
1308 | var CssParser = new WYMeditor.WymCssParser(); |
---|
1309 | if(this._options.stylesheet){ |
---|
1310 | CssParser.parse(jQuery.ajax({url: this._options.stylesheet,async:false}).responseText); |
---|
1311 | }else{ |
---|
1312 | CssParser.parse(this._options.styles, false); |
---|
1313 | } |
---|
1314 | |
---|
1315 | if(this._options.classesItems.length == 0) { |
---|
1316 | this._options.classesItems = CssParser.css_settings.classesItems; |
---|
1317 | } |
---|
1318 | if(this._options.editorStyles.length == 0) { |
---|
1319 | this._options.editorStyles = CssParser.css_settings.editorStyles; |
---|
1320 | } |
---|
1321 | if(this._options.dialogStyles.length == 0) { |
---|
1322 | this._options.dialogStyles = CssParser.css_settings.dialogStyles; |
---|
1323 | } |
---|
1324 | }; |
---|
1325 | |
---|
1326 | /********** EVENTS **********/ |
---|
1327 | |
---|
1328 | WYMeditor.editor.prototype.listen = function() { |
---|
1329 | |
---|
1330 | //don't use jQuery.find() on the iframe body |
---|
1331 | //because of MSIE + jQuery + expando issue (#JQ1143) |
---|
1332 | //jQuery(this._doc.body).find("*").bind("mouseup", this.mouseup); |
---|
1333 | |
---|
1334 | jQuery(this._doc.body).bind("mousedown", this.mousedown); |
---|
1335 | var images = this._doc.body.getElementsByTagName("img"); |
---|
1336 | for(var i=0; i < images.length; i++) { |
---|
1337 | jQuery(images[i]).bind("mousedown", this.mousedown); |
---|
1338 | } |
---|
1339 | }; |
---|
1340 | |
---|
1341 | WYMeditor.editor.prototype.mousedown = function(evt) { |
---|
1342 | |
---|
1343 | var wym = WYMeditor.INSTANCES[this.ownerDocument.title]; |
---|
1344 | wym._selected_image = (this.tagName.toLowerCase() == WYMeditor.IMG) ? this : null; |
---|
1345 | evt.stopPropagation(); |
---|
1346 | }; |
---|
1347 | |
---|
1348 | /********** SKINS **********/ |
---|
1349 | |
---|
1350 | /* |
---|
1351 | * Function: WYMeditor.loadCss |
---|
1352 | * Loads a stylesheet in the document. |
---|
1353 | * |
---|
1354 | * Parameters: |
---|
1355 | * href - The CSS path. |
---|
1356 | */ |
---|
1357 | WYMeditor.loadCss = function(href) { |
---|
1358 | |
---|
1359 | var link = document.createElement('link'); |
---|
1360 | link.rel = 'stylesheet'; |
---|
1361 | link.href = href; |
---|
1362 | |
---|
1363 | var head = jQuery('head').get(0); |
---|
1364 | head.appendChild(link); |
---|
1365 | }; |
---|
1366 | |
---|
1367 | /* |
---|
1368 | * Function: WYMeditor.editor.loadSkin |
---|
1369 | * Loads the skin CSS and initialization script (if needed). |
---|
1370 | */ |
---|
1371 | WYMeditor.editor.prototype.loadSkin = function() { |
---|
1372 | |
---|
1373 | //does the user want to automatically load the CSS (default: yes)? |
---|
1374 | //we also test if it hasn't been already loaded by another instance |
---|
1375 | //see below for a better (second) test |
---|
1376 | if(this._options.loadSkin && !WYMeditor.SKINS[this._options.skin]) { |
---|
1377 | |
---|
1378 | //check if it hasn't been already loaded |
---|
1379 | //so we don't load it more than once |
---|
1380 | //(we check the existing <link> elements) |
---|
1381 | |
---|
1382 | var found = false; |
---|
1383 | var rExp = new RegExp(this._options.skin |
---|
1384 | + '\/' + WYMeditor.SKINS_DEFAULT_CSS + '$'); |
---|
1385 | |
---|
1386 | jQuery('link').each( function() { |
---|
1387 | if(this.href.match(rExp)) found = true; |
---|
1388 | }); |
---|
1389 | |
---|
1390 | //load it, using the skin path |
---|
1391 | if(!found) WYMeditor.loadCss( this._options.skinPath |
---|
1392 | + WYMeditor.SKINS_DEFAULT_CSS ); |
---|
1393 | } |
---|
1394 | |
---|
1395 | //put the classname (ex. wym_skin_default) on wym_box |
---|
1396 | jQuery(this._box).addClass( "wym_skin_" + this._options.skin ); |
---|
1397 | |
---|
1398 | //does the user want to use some JS to initialize the skin (default: yes)? |
---|
1399 | //also check if it hasn't already been loaded by another instance |
---|
1400 | if(this._options.initSkin && !WYMeditor.SKINS[this._options.skin]) { |
---|
1401 | |
---|
1402 | eval(jQuery.ajax({url:this._options.skinPath |
---|
1403 | + WYMeditor.SKINS_DEFAULT_JS, async:false}).responseText); |
---|
1404 | } |
---|
1405 | |
---|
1406 | //init the skin, if needed |
---|
1407 | if(WYMeditor.SKINS[this._options.skin] |
---|
1408 | && WYMeditor.SKINS[this._options.skin].init) |
---|
1409 | WYMeditor.SKINS[this._options.skin].init(this); |
---|
1410 | |
---|
1411 | }; |
---|
1412 | |
---|
1413 | |
---|
1414 | /********** DIALOGS **********/ |
---|
1415 | |
---|
1416 | WYMeditor.INIT_DIALOG = function(index) { |
---|
1417 | |
---|
1418 | var wym = window.opener.WYMeditor.INSTANCES[index]; |
---|
1419 | var doc = window.document; |
---|
1420 | var selected = wym.selected(); |
---|
1421 | var dialogType = jQuery(wym._options.dialogTypeSelector).val(); |
---|
1422 | var sStamp = wym.uniqueStamp(); |
---|
1423 | |
---|
1424 | switch(dialogType) { |
---|
1425 | |
---|
1426 | case WYMeditor.DIALOG_LINK: |
---|
1427 | //ensure that we select the link to populate the fields |
---|
1428 | if(selected && selected.tagName && selected.tagName.toLowerCase != WYMeditor.A) |
---|
1429 | selected = jQuery(selected).parentsOrSelf(WYMeditor.A); |
---|
1430 | |
---|
1431 | //fix MSIE selection if link image has been clicked |
---|
1432 | if(!selected && wym._selected_image) |
---|
1433 | selected = jQuery(wym._selected_image).parentsOrSelf(WYMeditor.A); |
---|
1434 | break; |
---|
1435 | |
---|
1436 | } |
---|
1437 | |
---|
1438 | //pre-init functions |
---|
1439 | if(jQuery.isFunction(wym._options.preInitDialog)) |
---|
1440 | wym._options.preInitDialog(wym,window); |
---|
1441 | |
---|
1442 | //add css rules from options |
---|
1443 | var styles = doc.styleSheets[0]; |
---|
1444 | var aCss = eval(wym._options.dialogStyles); |
---|
1445 | |
---|
1446 | wym.addCssRules(doc, aCss); |
---|
1447 | |
---|
1448 | //auto populate fields if selected container (e.g. A) |
---|
1449 | if(selected) { |
---|
1450 | jQuery(wym._options.hrefSelector).val(jQuery(selected).attr(WYMeditor.HREF)); |
---|
1451 | jQuery(wym._options.srcSelector).val(jQuery(selected).attr(WYMeditor.SRC)); |
---|
1452 | jQuery(wym._options.titleSelector).val(jQuery(selected).attr(WYMeditor.TITLE)); |
---|
1453 | jQuery(wym._options.altSelector).val(jQuery(selected).attr(WYMeditor.ALT)); |
---|
1454 | } |
---|
1455 | |
---|
1456 | //auto populate image fields if selected image |
---|
1457 | if(wym._selected_image) { |
---|
1458 | jQuery(wym._options.dialogImageSelector + " " + wym._options.srcSelector) |
---|
1459 | .val(jQuery(wym._selected_image).attr(WYMeditor.SRC)); |
---|
1460 | jQuery(wym._options.dialogImageSelector + " " + wym._options.titleSelector) |
---|
1461 | .val(jQuery(wym._selected_image).attr(WYMeditor.TITLE)); |
---|
1462 | jQuery(wym._options.dialogImageSelector + " " + wym._options.altSelector) |
---|
1463 | .val(jQuery(wym._selected_image).attr(WYMeditor.ALT)); |
---|
1464 | } |
---|
1465 | |
---|
1466 | jQuery(wym._options.dialogLinkSelector + " " |
---|
1467 | + wym._options.submitSelector).click(function() { |
---|
1468 | |
---|
1469 | var sUrl = jQuery(wym._options.hrefSelector).val(); |
---|
1470 | if(sUrl.length > 0) { |
---|
1471 | |
---|
1472 | wym._exec(WYMeditor.CREATE_LINK, sStamp); |
---|
1473 | |
---|
1474 | jQuery("a[href=" + sStamp + "]", wym._doc.body) |
---|
1475 | .attr(WYMeditor.HREF, sUrl) |
---|
1476 | .attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val()); |
---|
1477 | |
---|
1478 | } |
---|
1479 | window.close(); |
---|
1480 | }); |
---|
1481 | |
---|
1482 | jQuery(wym._options.dialogImageSelector + " " |
---|
1483 | + wym._options.submitSelector).click(function() { |
---|
1484 | |
---|
1485 | var sUrl = jQuery(wym._options.srcSelector).val(); |
---|
1486 | if(sUrl.length > 0) { |
---|
1487 | |
---|
1488 | wym._exec(WYMeditor.INSERT_IMAGE, sStamp); |
---|
1489 | |
---|
1490 | jQuery("img[src$=" + sStamp + "]", wym._doc.body) |
---|
1491 | .attr(WYMeditor.SRC, sUrl) |
---|
1492 | .attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val()) |
---|
1493 | .attr(WYMeditor.ALT, jQuery(wym._options.altSelector).val()); |
---|
1494 | } |
---|
1495 | window.close(); |
---|
1496 | }); |
---|
1497 | |
---|
1498 | jQuery(wym._options.dialogTableSelector + " " |
---|
1499 | + wym._options.submitSelector).click(function() { |
---|
1500 | |
---|
1501 | var iRows = jQuery(wym._options.rowsSelector).val(); |
---|
1502 | var iCols = jQuery(wym._options.colsSelector).val(); |
---|
1503 | |
---|
1504 | if(iRows > 0 && iCols > 0) { |
---|
1505 | |
---|
1506 | var table = wym._doc.createElement(WYMeditor.TABLE); |
---|
1507 | var newRow = null; |
---|
1508 | var newCol = null; |
---|
1509 | |
---|
1510 | var sCaption = jQuery(wym._options.captionSelector).val(); |
---|
1511 | |
---|
1512 | //we create the caption |
---|
1513 | var newCaption = table.createCaption(); |
---|
1514 | newCaption.innerHTML = sCaption; |
---|
1515 | |
---|
1516 | //we create the rows and cells |
---|
1517 | for(x=0; x<iRows; x++) { |
---|
1518 | newRow = table.insertRow(x); |
---|
1519 | for(y=0; y<iCols; y++) {newRow.insertCell(y);} |
---|
1520 | } |
---|
1521 | |
---|
1522 | //set the summary attr |
---|
1523 | jQuery(table).attr('summary', |
---|
1524 | jQuery(wym._options.summarySelector).val()); |
---|
1525 | |
---|
1526 | //append the table after the selected container |
---|
1527 | var node = jQuery(wym.findUp(wym.container(), |
---|
1528 | WYMeditor.MAIN_CONTAINERS)).get(0); |
---|
1529 | if(!node || !node.parentNode) jQuery(wym._doc.body).append(table); |
---|
1530 | else jQuery(node).after(table); |
---|
1531 | } |
---|
1532 | window.close(); |
---|
1533 | }); |
---|
1534 | |
---|
1535 | jQuery(wym._options.dialogPasteSelector + " " |
---|
1536 | + wym._options.submitSelector).click(function() { |
---|
1537 | |
---|
1538 | var sText = jQuery(wym._options.textSelector).val(); |
---|
1539 | wym.paste(sText); |
---|
1540 | window.close(); |
---|
1541 | }); |
---|
1542 | |
---|
1543 | jQuery(wym._options.dialogPreviewSelector + " " |
---|
1544 | + wym._options.previewSelector) |
---|
1545 | .html(wym.xhtml()); |
---|
1546 | |
---|
1547 | //cancel button |
---|
1548 | jQuery(wym._options.cancelSelector).mousedown(function() { |
---|
1549 | window.close(); |
---|
1550 | }); |
---|
1551 | |
---|
1552 | //pre-init functions |
---|
1553 | if(jQuery.isFunction(wym._options.postInitDialog)) |
---|
1554 | wym._options.postInitDialog(wym,window); |
---|
1555 | |
---|
1556 | }; |
---|
1557 | |
---|
1558 | /********** XHTML LEXER/PARSER **********/ |
---|
1559 | |
---|
1560 | /* |
---|
1561 | * @name xml |
---|
1562 | * @description Use these methods to generate XML and XHTML compliant tags and |
---|
1563 | * escape tag attributes correctly |
---|
1564 | * @author Bermi Ferrer - http://bermi.org |
---|
1565 | * @author David Heinemeier Hansson http://loudthinking.com |
---|
1566 | */ |
---|
1567 | WYMeditor.XmlHelper = function() |
---|
1568 | { |
---|
1569 | this._entitiesDiv = document.createElement('div'); |
---|
1570 | return this; |
---|
1571 | }; |
---|
1572 | |
---|
1573 | |
---|
1574 | /* |
---|
1575 | * @name tag |
---|
1576 | * @description |
---|
1577 | * Returns an empty HTML tag of type *name* which by default is XHTML |
---|
1578 | * compliant. Setting *open* to true will create an open tag compatible |
---|
1579 | * with HTML 4.0 and below. Add HTML attributes by passing an attributes |
---|
1580 | * array to *options*. For attributes with no value like (disabled and |
---|
1581 | * readonly), give it a value of true in the *options* array. |
---|
1582 | * |
---|
1583 | * Examples: |
---|
1584 | * |
---|
1585 | * this.tag('br') |
---|
1586 | * # => <br /> |
---|
1587 | * this.tag ('br', false, true) |
---|
1588 | * # => <br> |
---|
1589 | * this.tag ('input', jQuery({type:'text',disabled:true }) ) |
---|
1590 | * # => <input type="text" disabled="disabled" /> |
---|
1591 | */ |
---|
1592 | WYMeditor.XmlHelper.prototype.tag = function(name, options, open) |
---|
1593 | { |
---|
1594 | options = options || false; |
---|
1595 | open = open || false; |
---|
1596 | return '<'+name+(options ? this.tagOptions(options) : '')+(open ? '>' : ' />'); |
---|
1597 | }; |
---|
1598 | |
---|
1599 | /* |
---|
1600 | * @name contentTag |
---|
1601 | * @description |
---|
1602 | * Returns a XML block tag of type *name* surrounding the *content*. Add |
---|
1603 | * XML attributes by passing an attributes array to *options*. For attributes |
---|
1604 | * with no value like (disabled and readonly), give it a value of true in |
---|
1605 | * the *options* array. You can use symbols or strings for the attribute names. |
---|
1606 | * |
---|
1607 | * this.contentTag ('p', 'Hello world!' ) |
---|
1608 | * # => <p>Hello world!</p> |
---|
1609 | * this.contentTag('div', this.contentTag('p', "Hello world!"), jQuery({class : "strong"})) |
---|
1610 | * # => <div class="strong"><p>Hello world!</p></div> |
---|
1611 | * this.contentTag("select", options, jQuery({multiple : true})) |
---|
1612 | * # => <select multiple="multiple">...options...</select> |
---|
1613 | */ |
---|
1614 | WYMeditor.XmlHelper.prototype.contentTag = function(name, content, options) |
---|
1615 | { |
---|
1616 | options = options || false; |
---|
1617 | return '<'+name+(options ? this.tagOptions(options) : '')+'>'+content+'</'+name+'>'; |
---|
1618 | }; |
---|
1619 | |
---|
1620 | /* |
---|
1621 | * @name cdataSection |
---|
1622 | * @description |
---|
1623 | * Returns a CDATA section for the given +content+. CDATA sections |
---|
1624 | * are used to escape blocks of text containing characters which would |
---|
1625 | * otherwise be recognized as markup. CDATA sections begin with the string |
---|
1626 | * <tt><![CDATA[</tt> and } with (and may not contain) the string |
---|
1627 | * <tt>]]></tt>. |
---|
1628 | */ |
---|
1629 | WYMeditor.XmlHelper.prototype.cdataSection = function(content) |
---|
1630 | { |
---|
1631 | return '<![CDATA['+content+']]>'; |
---|
1632 | }; |
---|
1633 | |
---|
1634 | |
---|
1635 | /* |
---|
1636 | * @name escapeOnce |
---|
1637 | * @description |
---|
1638 | * Returns the escaped +xml+ without affecting existing escaped entities. |
---|
1639 | * |
---|
1640 | * this.escapeOnce( "1 > 2 & 3") |
---|
1641 | * # => "1 > 2 & 3" |
---|
1642 | */ |
---|
1643 | WYMeditor.XmlHelper.prototype.escapeOnce = function(xml) |
---|
1644 | { |
---|
1645 | return this._fixDoubleEscape(this.escapeEntities(xml)); |
---|
1646 | }; |
---|
1647 | |
---|
1648 | /* |
---|
1649 | * @name _fixDoubleEscape |
---|
1650 | * @description |
---|
1651 | * Fix double-escaped entities, such as &amp;, &#123;, etc. |
---|
1652 | */ |
---|
1653 | WYMeditor.XmlHelper.prototype._fixDoubleEscape = function(escaped) |
---|
1654 | { |
---|
1655 | return escaped.replace(/&([a-z]+|(#\d+));/ig, "&$1;"); |
---|
1656 | }; |
---|
1657 | |
---|
1658 | /* |
---|
1659 | * @name tagOptions |
---|
1660 | * @description |
---|
1661 | * Takes an array like the one generated by Tag.parseAttributes |
---|
1662 | * [["src", "http://www.editam.com/?a=b&c=d&f=g"], ["title", "Editam, <Simplified> CMS"]] |
---|
1663 | * or an object like {src:"http://www.editam.com/?a=b&c=d&f=g", title:"Editam, <Simplified> CMS"} |
---|
1664 | * and returns a string properly escaped like |
---|
1665 | * ' src = "http://www.editam.com/?a=b&c=d&f=g" title = "Editam, <Simplified> CMS"' |
---|
1666 | * which is valid for strict XHTML |
---|
1667 | */ |
---|
1668 | WYMeditor.XmlHelper.prototype.tagOptions = function(options) |
---|
1669 | { |
---|
1670 | var xml = this; |
---|
1671 | xml._formated_options = ''; |
---|
1672 | |
---|
1673 | for (var key in options) { |
---|
1674 | var formated_options = ''; |
---|
1675 | var value = options[key]; |
---|
1676 | if(typeof value != 'function' && value.length > 0) { |
---|
1677 | |
---|
1678 | if(parseInt(key) == key && typeof value == 'object'){ |
---|
1679 | key = value.shift(); |
---|
1680 | value = value.pop(); |
---|
1681 | } |
---|
1682 | if(key != '' && value != ''){ |
---|
1683 | xml._formated_options += ' '+key+'="'+xml.escapeOnce(value)+'"'; |
---|
1684 | } |
---|
1685 | } |
---|
1686 | } |
---|
1687 | return xml._formated_options; |
---|
1688 | }; |
---|
1689 | |
---|
1690 | /* |
---|
1691 | * @name escapeEntities |
---|
1692 | * @description |
---|
1693 | * Escapes XML/HTML entities <, >, & and ". If seccond parameter is set to false it |
---|
1694 | * will not escape ". If set to true it will also escape ' |
---|
1695 | */ |
---|
1696 | WYMeditor.XmlHelper.prototype.escapeEntities = function(string, escape_quotes) |
---|
1697 | { |
---|
1698 | this._entitiesDiv.innerHTML = string; |
---|
1699 | this._entitiesDiv.textContent = string; |
---|
1700 | var result = this._entitiesDiv.innerHTML; |
---|
1701 | if(typeof escape_quotes == 'undefined'){ |
---|
1702 | if(escape_quotes != false) result = result.replace('"', '"'); |
---|
1703 | if(escape_quotes == true) result = result.replace('"', '''); |
---|
1704 | } |
---|
1705 | return result; |
---|
1706 | }; |
---|
1707 | |
---|
1708 | /* |
---|
1709 | * Parses a string conatining tag attributes and values an returns an array formated like |
---|
1710 | * [["src", "http://www.editam.com"], ["title", "Editam, Simplified CMS"]] |
---|
1711 | */ |
---|
1712 | WYMeditor.XmlHelper.prototype.parseAttributes = function(tag_attributes) |
---|
1713 | { |
---|
1714 | // Use a compounded regex to match single quoted, double quoted and unquoted attribute pairs |
---|
1715 | var result = []; |
---|
1716 | var matches = tag_attributes.split(/((=\s*")(")("))|((=\s*\')(\')(\'))|((=\s*[^>\s]*))/g); |
---|
1717 | if(matches.toString() != tag_attributes){ |
---|
1718 | for (var k in matches) { |
---|
1719 | var v = matches[k]; |
---|
1720 | if(typeof v != 'function' && v.length != 0){ |
---|
1721 | var re = new RegExp('(\\w+)\\s*'+v); |
---|
1722 | if(match = tag_attributes.match(re) ){ |
---|
1723 | var value = v.replace(/^[\s=]+/, ""); |
---|
1724 | var delimiter = value.charAt(0); |
---|
1725 | delimiter = delimiter == '"' ? '"' : (delimiter=="'"?"'":''); |
---|
1726 | if(delimiter != ''){ |
---|
1727 | value = delimiter == '"' ? value.replace(/^"|"+$/g, '') : value.replace(/^'|'+$/g, ''); |
---|
1728 | } |
---|
1729 | tag_attributes = tag_attributes.replace(match[0],''); |
---|
1730 | result.push([match[1] , value]); |
---|
1731 | } |
---|
1732 | } |
---|
1733 | } |
---|
1734 | } |
---|
1735 | return result; |
---|
1736 | }; |
---|
1737 | |
---|
1738 | /** |
---|
1739 | * XhtmlValidator for validating tag attributes |
---|
1740 | * |
---|
1741 | * @author Bermi Ferrer - http://bermi.org |
---|
1742 | */ |
---|
1743 | WYMeditor.XhtmlValidator = { |
---|
1744 | "_attributes": |
---|
1745 | { |
---|
1746 | "core": |
---|
1747 | { |
---|
1748 | "except":[ |
---|
1749 | "base", |
---|
1750 | "head", |
---|
1751 | "html", |
---|
1752 | "meta", |
---|
1753 | "param", |
---|
1754 | "script", |
---|
1755 | "style", |
---|
1756 | "title" |
---|
1757 | ], |
---|
1758 | "attributes":[ |
---|
1759 | "class", |
---|
1760 | "id", |
---|
1761 | "style", |
---|
1762 | "title", |
---|
1763 | "accesskey", |
---|
1764 | "tabindex" |
---|
1765 | ] |
---|
1766 | }, |
---|
1767 | "language": |
---|
1768 | { |
---|
1769 | "except":[ |
---|
1770 | "base", |
---|
1771 | "br", |
---|
1772 | "hr", |
---|
1773 | "iframe", |
---|
1774 | "param", |
---|
1775 | "script" |
---|
1776 | ], |
---|
1777 | "attributes": |
---|
1778 | { |
---|
1779 | "dir":[ |
---|
1780 | "ltr", |
---|
1781 | "rtl" |
---|
1782 | ], |
---|
1783 | "0":"lang", |
---|
1784 | "1":"xml:lang" |
---|
1785 | } |
---|
1786 | }, |
---|
1787 | "keyboard": |
---|
1788 | { |
---|
1789 | "attributes": |
---|
1790 | { |
---|
1791 | "accesskey":/^(\w){1}$/, |
---|
1792 | "tabindex":/^(\d)+$/ |
---|
1793 | } |
---|
1794 | } |
---|
1795 | }, |
---|
1796 | "_events": |
---|
1797 | { |
---|
1798 | "window": |
---|
1799 | { |
---|
1800 | "only":[ |
---|
1801 | "body" |
---|
1802 | ], |
---|
1803 | "attributes":[ |
---|
1804 | "onload", |
---|
1805 | "onunload" |
---|
1806 | ] |
---|
1807 | }, |
---|
1808 | "form": |
---|
1809 | { |
---|
1810 | "only":[ |
---|
1811 | "form", |
---|
1812 | "input", |
---|
1813 | "textarea", |
---|
1814 | "select", |
---|
1815 | "a", |
---|
1816 | "label", |
---|
1817 | "button" |
---|
1818 | ], |
---|
1819 | "attributes":[ |
---|
1820 | "onchange", |
---|
1821 | "onsubmit", |
---|
1822 | "onreset", |
---|
1823 | "onselect", |
---|
1824 | "onblur", |
---|
1825 | "onfocus" |
---|
1826 | ] |
---|
1827 | }, |
---|
1828 | "keyboard": |
---|
1829 | { |
---|
1830 | "except":[ |
---|
1831 | "base", |
---|
1832 | "bdo", |
---|
1833 | "br", |
---|
1834 | "frame", |
---|
1835 | "frameset", |
---|
1836 | "head", |
---|
1837 | "html", |
---|
1838 | "iframe", |
---|
1839 | "meta", |
---|
1840 | "param", |
---|
1841 | "script", |
---|
1842 | "style", |
---|
1843 | "title" |
---|
1844 | ], |
---|
1845 | "attributes":[ |
---|
1846 | "onkeydown", |
---|
1847 | "onkeypress", |
---|
1848 | "onkeyup" |
---|
1849 | ] |
---|
1850 | }, |
---|
1851 | "mouse": |
---|
1852 | { |
---|
1853 | "except":[ |
---|
1854 | "base", |
---|
1855 | "bdo", |
---|
1856 | "br", |
---|
1857 | "head", |
---|
1858 | "html", |
---|
1859 | "meta", |
---|
1860 | "param", |
---|
1861 | "script", |
---|
1862 | "style", |
---|
1863 | "title" |
---|
1864 | ], |
---|
1865 | "attributes":[ |
---|
1866 | "onclick", |
---|
1867 | "ondblclick", |
---|
1868 | "onmousedown", |
---|
1869 | "onmousemove", |
---|
1870 | "onmouseover", |
---|
1871 | "onmouseout", |
---|
1872 | "onmouseup" |
---|
1873 | ] |
---|
1874 | } |
---|
1875 | }, |
---|
1876 | "_tags": |
---|
1877 | { |
---|
1878 | "a": |
---|
1879 | { |
---|
1880 | "attributes": |
---|
1881 | { |
---|
1882 | "0":"charset", |
---|
1883 | "1":"coords", |
---|
1884 | "2":"href", |
---|
1885 | "3":"hreflang", |
---|
1886 | "4":"name", |
---|
1887 | "rel":/^(alternate|designates|stylesheet|start|next|prev|contents|index|glossary|copyright|chapter|section|subsection|appendix|help|bookmark| |shortcut|icon)+$/, |
---|
1888 | "rev":/^(alternate|designates|stylesheet|start|next|prev|contents|index|glossary|copyright|chapter|section|subsection|appendix|help|bookmark| |shortcut|icon)+$/, |
---|
1889 | "shape":/^(rect|rectangle|circ|circle|poly|polygon)$/, |
---|
1890 | "5":"type" |
---|
1891 | } |
---|
1892 | }, |
---|
1893 | "0":"abbr", |
---|
1894 | "1":"acronym", |
---|
1895 | "2":"address", |
---|
1896 | "area": |
---|
1897 | { |
---|
1898 | "attributes": |
---|
1899 | { |
---|
1900 | "0":"alt", |
---|
1901 | "1":"coords", |
---|
1902 | "2":"href", |
---|
1903 | "nohref":/^(true|false)$/, |
---|
1904 | "shape":/^(rect|rectangle|circ|circle|poly|polygon)$/ |
---|
1905 | }, |
---|
1906 | "required":[ |
---|
1907 | "alt" |
---|
1908 | ] |
---|
1909 | }, |
---|
1910 | "3":"b", |
---|
1911 | "base": |
---|
1912 | { |
---|
1913 | "attributes":[ |
---|
1914 | "href" |
---|
1915 | ], |
---|
1916 | "required":[ |
---|
1917 | "href" |
---|
1918 | ] |
---|
1919 | }, |
---|
1920 | "bdo": |
---|
1921 | { |
---|
1922 | "attributes": |
---|
1923 | { |
---|
1924 | "dir":/^(ltr|rtl)$/ |
---|
1925 | }, |
---|
1926 | "required":[ |
---|
1927 | "dir" |
---|
1928 | ] |
---|
1929 | }, |
---|
1930 | "4":"big", |
---|
1931 | "blockquote": |
---|
1932 | { |
---|
1933 | "attributes":[ |
---|
1934 | "cite" |
---|
1935 | ] |
---|
1936 | }, |
---|
1937 | "5":"body", |
---|
1938 | "6":"br", |
---|
1939 | "button": |
---|
1940 | { |
---|
1941 | "attributes": |
---|
1942 | { |
---|
1943 | "disabled":/^(disabled)$/, |
---|
1944 | "type":/^(button|reset|submit)$/, |
---|
1945 | "0":"value" |
---|
1946 | }, |
---|
1947 | "inside":"form" |
---|
1948 | }, |
---|
1949 | "7":"caption", |
---|
1950 | "8":"cite", |
---|
1951 | "9":"code", |
---|
1952 | "col": |
---|
1953 | { |
---|
1954 | "attributes": |
---|
1955 | { |
---|
1956 | "align":/^(right|left|center|justify)$/, |
---|
1957 | "0":"char", |
---|
1958 | "1":"charoff", |
---|
1959 | "span":/^(\d)+$/, |
---|
1960 | "valign":/^(top|middle|bottom|baseline)$/, |
---|
1961 | "2":"width" |
---|
1962 | }, |
---|
1963 | "inside":"colgroup" |
---|
1964 | }, |
---|
1965 | "colgroup": |
---|
1966 | { |
---|
1967 | "attributes": |
---|
1968 | { |
---|
1969 | "align":/^(right|left|center|justify)$/, |
---|
1970 | "0":"char", |
---|
1971 | "1":"charoff", |
---|
1972 | "span":/^(\d)+$/, |
---|
1973 | "valign":/^(top|middle|bottom|baseline)$/, |
---|
1974 | "2":"width" |
---|
1975 | } |
---|
1976 | }, |
---|
1977 | "10":"dd", |
---|
1978 | "del": |
---|
1979 | { |
---|
1980 | "attributes": |
---|
1981 | { |
---|
1982 | "0":"cite", |
---|
1983 | "datetime":/^([0-9]){8}/ |
---|
1984 | } |
---|
1985 | }, |
---|
1986 | "11":"div", |
---|
1987 | "12":"dfn", |
---|
1988 | "13":"dl", |
---|
1989 | "14":"dt", |
---|
1990 | "15":"em", |
---|
1991 | "fieldset": |
---|
1992 | { |
---|
1993 | "inside":"form" |
---|
1994 | }, |
---|
1995 | "form": |
---|
1996 | { |
---|
1997 | "attributes": |
---|
1998 | { |
---|
1999 | "0":"action", |
---|
2000 | "1":"accept", |
---|
2001 | "2":"accept-charset", |
---|
2002 | "3":"enctype", |
---|
2003 | "method":/^(get|post)$/ |
---|
2004 | }, |
---|
2005 | "required":[ |
---|
2006 | "action" |
---|
2007 | ] |
---|
2008 | }, |
---|
2009 | "head": |
---|
2010 | { |
---|
2011 | "attributes":[ |
---|
2012 | "profile" |
---|
2013 | ] |
---|
2014 | }, |
---|
2015 | "16":"h1", |
---|
2016 | "17":"h2", |
---|
2017 | "18":"h3", |
---|
2018 | "19":"h4", |
---|
2019 | "20":"h5", |
---|
2020 | "21":"h6", |
---|
2021 | "22":"hr", |
---|
2022 | "html": |
---|
2023 | { |
---|
2024 | "attributes":[ |
---|
2025 | "xmlns" |
---|
2026 | ] |
---|
2027 | }, |
---|
2028 | "23":"i", |
---|
2029 | "img": |
---|
2030 | { |
---|
2031 | "attributes":[ |
---|
2032 | "alt", |
---|
2033 | "src", |
---|
2034 | "height", |
---|
2035 | "ismap", |
---|
2036 | "longdesc", |
---|
2037 | "usemap", |
---|
2038 | "width" |
---|
2039 | ], |
---|
2040 | "required":[ |
---|
2041 | "alt", |
---|
2042 | "src" |
---|
2043 | ] |
---|
2044 | }, |
---|
2045 | "input": |
---|
2046 | { |
---|
2047 | "attributes": |
---|
2048 | { |
---|
2049 | "0":"accept", |
---|
2050 | "1":"alt", |
---|
2051 | "checked":/^(checked)$/, |
---|
2052 | "disabled":/^(disabled)$/, |
---|
2053 | "maxlength":/^(\d)+$/, |
---|
2054 | "2":"name", |
---|
2055 | "readonly":/^(readonly)$/, |
---|
2056 | "size":/^(\d)+$/, |
---|
2057 | "3":"src", |
---|
2058 | "type":/^(button|checkbox|file|hidden|image|password|radio|reset|submit|text)$/, |
---|
2059 | "4":"value" |
---|
2060 | }, |
---|
2061 | "inside":"form" |
---|
2062 | }, |
---|
2063 | "ins": |
---|
2064 | { |
---|
2065 | "attributes": |
---|
2066 | { |
---|
2067 | "0":"cite", |
---|
2068 | "datetime":/^([0-9]){8}/ |
---|
2069 | } |
---|
2070 | }, |
---|
2071 | "24":"kbd", |
---|
2072 | "label": |
---|
2073 | { |
---|
2074 | "attributes":[ |
---|
2075 | "for" |
---|
2076 | ], |
---|
2077 | "inside":"form" |
---|
2078 | }, |
---|
2079 | "25":"legend", |
---|
2080 | "26":"li", |
---|
2081 | "link": |
---|
2082 | { |
---|
2083 | "attributes": |
---|
2084 | { |
---|
2085 | "0":"charset", |
---|
2086 | "1":"href", |
---|
2087 | "2":"hreflang", |
---|
2088 | "media":/^(all|braille|print|projection|screen|speech|,|;| )+$/i, |
---|
2089 | //next comment line required by Opera! |
---|
2090 | /*"rel":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,*/ |
---|
2091 | "rel":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i, |
---|
2092 | "rev":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i, |
---|
2093 | "3":"type" |
---|
2094 | }, |
---|
2095 | "inside":"head" |
---|
2096 | }, |
---|
2097 | "map": |
---|
2098 | { |
---|
2099 | "attributes":[ |
---|
2100 | "id", |
---|
2101 | "name" |
---|
2102 | ], |
---|
2103 | "required":[ |
---|
2104 | "id" |
---|
2105 | ] |
---|
2106 | }, |
---|
2107 | "meta": |
---|
2108 | { |
---|
2109 | "attributes": |
---|
2110 | { |
---|
2111 | "0":"content", |
---|
2112 | "http-equiv":/^(content\-type|expires|refresh|set\-cookie)$/i, |
---|
2113 | "1":"name", |
---|
2114 | "2":"scheme" |
---|
2115 | }, |
---|
2116 | "required":[ |
---|
2117 | "content" |
---|
2118 | ] |
---|
2119 | }, |
---|
2120 | "27":"noscript", |
---|
2121 | "object": |
---|
2122 | { |
---|
2123 | "attributes":[ |
---|
2124 | "archive", |
---|
2125 | "classid", |
---|
2126 | "codebase", |
---|
2127 | "codetype", |
---|
2128 | "data", |
---|
2129 | "declare", |
---|
2130 | "height", |
---|
2131 | "name", |
---|
2132 | "standby", |
---|
2133 | "type", |
---|
2134 | "usemap", |
---|
2135 | "width" |
---|
2136 | ] |
---|
2137 | }, |
---|
2138 | "28":"ol", |
---|
2139 | "optgroup": |
---|
2140 | { |
---|
2141 | "attributes": |
---|
2142 | { |
---|
2143 | "0":"label", |
---|
2144 | "disabled": /^(disabled)$/ |
---|
2145 | }, |
---|
2146 | "required":[ |
---|
2147 | "label" |
---|
2148 | ] |
---|
2149 | }, |
---|
2150 | "option": |
---|
2151 | { |
---|
2152 | "attributes": |
---|
2153 | { |
---|
2154 | "0":"label", |
---|
2155 | "disabled":/^(disabled)$/, |
---|
2156 | "selected":/^(selected)$/, |
---|
2157 | "1":"value" |
---|
2158 | }, |
---|
2159 | "inside":"select" |
---|
2160 | }, |
---|
2161 | "29":"p", |
---|
2162 | "param": |
---|
2163 | { |
---|
2164 | "attributes": |
---|
2165 | { |
---|
2166 | "0":"type", |
---|
2167 | "valuetype":/^(data|ref|object)$/, |
---|
2168 | "1":"valuetype", |
---|
2169 | "2":"value" |
---|
2170 | }, |
---|
2171 | "required":[ |
---|
2172 | "name" |
---|
2173 | ] |
---|
2174 | }, |
---|
2175 | "30":"pre", |
---|
2176 | "q": |
---|
2177 | { |
---|
2178 | "attributes":[ |
---|
2179 | "cite" |
---|
2180 | ] |
---|
2181 | }, |
---|
2182 | "31":"samp", |
---|
2183 | "script": |
---|
2184 | { |
---|
2185 | "attributes": |
---|
2186 | { |
---|
2187 | "type":/^(text\/ecmascript|text\/javascript|text\/jscript|text\/vbscript|text\/vbs|text\/xml)$/, |
---|
2188 | "0":"charset", |
---|
2189 | "defer":/^(defer)$/, |
---|
2190 | "1":"src" |
---|
2191 | }, |
---|
2192 | "required":[ |
---|
2193 | "type" |
---|
2194 | ] |
---|
2195 | }, |
---|
2196 | "select": |
---|
2197 | { |
---|
2198 | "attributes": |
---|
2199 | { |
---|
2200 | "disabled":/^(disabled)$/, |
---|
2201 | "multiple":/^(multiple)$/, |
---|
2202 | "0":"name", |
---|
2203 | "1":"size" |
---|
2204 | }, |
---|
2205 | "inside":"form" |
---|
2206 | }, |
---|
2207 | "32":"small", |
---|
2208 | "33":"span", |
---|
2209 | "34":"strong", |
---|
2210 | "style": |
---|
2211 | { |
---|
2212 | "attributes": |
---|
2213 | { |
---|
2214 | "0":"type", |
---|
2215 | "media":/^(screen|tty|tv|projection|handheld|print|braille|aural|all)$/ |
---|
2216 | }, |
---|
2217 | "required":[ |
---|
2218 | "type" |
---|
2219 | ] |
---|
2220 | }, |
---|
2221 | "35":"sub", |
---|
2222 | "36":"sup", |
---|
2223 | "table": |
---|
2224 | { |
---|
2225 | "attributes": |
---|
2226 | { |
---|
2227 | "0":"border", |
---|
2228 | "1":"cellpadding", |
---|
2229 | "2":"cellspacing", |
---|
2230 | "frame":/^(void|above|below|hsides|lhs|rhs|vsides|box|border)$/, |
---|
2231 | "rules":/^(none|groups|rows|cols|all)$/, |
---|
2232 | "3":"summary", |
---|
2233 | "4":"width" |
---|
2234 | } |
---|
2235 | }, |
---|
2236 | "tbody": |
---|
2237 | { |
---|
2238 | "attributes": |
---|
2239 | { |
---|
2240 | "align":/^(right|left|center|justify)$/, |
---|
2241 | "0":"char", |
---|
2242 | "1":"charoff", |
---|
2243 | "valign":/^(top|middle|bottom|baseline)$/ |
---|
2244 | } |
---|
2245 | }, |
---|
2246 | "td": |
---|
2247 | { |
---|
2248 | "attributes": |
---|
2249 | { |
---|
2250 | "0":"abbr", |
---|
2251 | "align":/^(left|right|center|justify|char)$/, |
---|
2252 | "1":"axis", |
---|
2253 | "2":"char", |
---|
2254 | "3":"charoff", |
---|
2255 | "colspan":/^(\d)+$/, |
---|
2256 | "4":"headers", |
---|
2257 | "rowspan":/^(\d)+$/, |
---|
2258 | "scope":/^(col|colgroup|row|rowgroup)$/, |
---|
2259 | "valign":/^(top|middle|bottom|baseline)$/ |
---|
2260 | } |
---|
2261 | }, |
---|
2262 | "textarea": |
---|
2263 | { |
---|
2264 | "attributes":[ |
---|
2265 | "cols", |
---|
2266 | "rows", |
---|
2267 | "disabled", |
---|
2268 | "name", |
---|
2269 | "readonly" |
---|
2270 | ], |
---|
2271 | "required":[ |
---|
2272 | "cols", |
---|
2273 | "rows" |
---|
2274 | ], |
---|
2275 | "inside":"form" |
---|
2276 | }, |
---|
2277 | "tfoot": |
---|
2278 | { |
---|
2279 | "attributes": |
---|
2280 | { |
---|
2281 | "align":/^(right|left|center|justify)$/, |
---|
2282 | "0":"char", |
---|
2283 | "1":"charoff", |
---|
2284 | "valign":/^(top|middle|bottom)$/, |
---|
2285 | "2":"baseline" |
---|
2286 | } |
---|
2287 | }, |
---|
2288 | "th": |
---|
2289 | { |
---|
2290 | "attributes": |
---|
2291 | { |
---|
2292 | "0":"abbr", |
---|
2293 | "align":/^(left|right|center|justify|char)$/, |
---|
2294 | "1":"axis", |
---|
2295 | "2":"char", |
---|
2296 | "3":"charoff", |
---|
2297 | "colspan":/^(\d)+$/, |
---|
2298 | "4":"headers", |
---|
2299 | "rowspan":/^(\d)+$/, |
---|
2300 | "scope":/^(col|colgroup|row|rowgroup)$/, |
---|
2301 | "valign":/^(top|middle|bottom|baseline)$/ |
---|
2302 | } |
---|
2303 | }, |
---|
2304 | "thead": |
---|
2305 | { |
---|
2306 | "attributes": |
---|
2307 | { |
---|
2308 | "align":/^(right|left|center|justify)$/, |
---|
2309 | "0":"char", |
---|
2310 | "1":"charoff", |
---|
2311 | "valign":/^(top|middle|bottom|baseline)$/ |
---|
2312 | } |
---|
2313 | }, |
---|
2314 | "37":"title", |
---|
2315 | "tr": |
---|
2316 | { |
---|
2317 | "attributes": |
---|
2318 | { |
---|
2319 | "align":/^(right|left|center|justify|char)$/, |
---|
2320 | "0":"char", |
---|
2321 | "1":"charoff", |
---|
2322 | "valign":/^(top|middle|bottom|baseline)$/ |
---|
2323 | } |
---|
2324 | }, |
---|
2325 | "38":"tt", |
---|
2326 | "39":"ul", |
---|
2327 | "40":"var" |
---|
2328 | }, |
---|
2329 | |
---|
2330 | // Temporary skiped attributes |
---|
2331 | skiped_attributes : [], |
---|
2332 | skiped_attribute_values : [], |
---|
2333 | |
---|
2334 | getValidTagAttributes: function(tag, attributes) |
---|
2335 | { |
---|
2336 | var valid_attributes = {}; |
---|
2337 | var possible_attributes = this.getPossibleTagAttributes(tag); |
---|
2338 | for(var attribute in attributes) { |
---|
2339 | var value = attributes[attribute]; |
---|
2340 | var h = WYMeditor.Helper; |
---|
2341 | if(!h.contains(this.skiped_attributes, attribute) && !h.contains(this.skiped_attribute_values, value)){ |
---|
2342 | if (typeof value != 'function' && h.contains(possible_attributes, attribute)) { |
---|
2343 | if (this.doesAttributeNeedsValidation(tag, attribute)) { |
---|
2344 | if(this.validateAttribute(tag, attribute, value)){ |
---|
2345 | valid_attributes[attribute] = value; |
---|
2346 | } |
---|
2347 | }else{ |
---|
2348 | valid_attributes[attribute] = value; |
---|
2349 | } |
---|
2350 | } |
---|
2351 | } |
---|
2352 | } |
---|
2353 | return valid_attributes; |
---|
2354 | }, |
---|
2355 | getUniqueAttributesAndEventsForTag : function(tag) |
---|
2356 | { |
---|
2357 | var result = []; |
---|
2358 | |
---|
2359 | if (this._tags[tag] && this._tags[tag]['attributes']) { |
---|
2360 | for (k in this._tags[tag]['attributes']) { |
---|
2361 | result.push(parseInt(k) == k ? this._tags[tag]['attributes'][k] : k); |
---|
2362 | } |
---|
2363 | } |
---|
2364 | return result; |
---|
2365 | }, |
---|
2366 | getDefaultAttributesAndEventsForTags : function() |
---|
2367 | { |
---|
2368 | var result = []; |
---|
2369 | for (var key in this._events){ |
---|
2370 | result.push(this._events[key]); |
---|
2371 | } |
---|
2372 | for (var key in this._attributes){ |
---|
2373 | result.push(this._attributes[key]); |
---|
2374 | } |
---|
2375 | return result; |
---|
2376 | }, |
---|
2377 | isValidTag : function(tag) |
---|
2378 | { |
---|
2379 | if(this._tags[tag]){ |
---|
2380 | return true; |
---|
2381 | } |
---|
2382 | for(var key in this._tags){ |
---|
2383 | if(this._tags[key] == tag){ |
---|
2384 | return true; |
---|
2385 | } |
---|
2386 | } |
---|
2387 | return false; |
---|
2388 | }, |
---|
2389 | getDefaultAttributesAndEventsForTag : function(tag) |
---|
2390 | { |
---|
2391 | var default_attributes = []; |
---|
2392 | if (this.isValidTag(tag)) { |
---|
2393 | var default_attributes_and_events = this.getDefaultAttributesAndEventsForTags(); |
---|
2394 | |
---|
2395 | for(var key in default_attributes_and_events) { |
---|
2396 | var defaults = default_attributes_and_events[key]; |
---|
2397 | if(typeof defaults == 'object'){ |
---|
2398 | var h = WYMeditor.Helper; |
---|
2399 | if ((defaults['except'] && h.contains(defaults['except'], tag)) || (defaults['only'] && !h.contains(defaults['only'], tag))) { |
---|
2400 | continue; |
---|
2401 | } |
---|
2402 | |
---|
2403 | var tag_defaults = defaults['attributes'] ? defaults['attributes'] : defaults['events']; |
---|
2404 | for(var k in tag_defaults) { |
---|
2405 | default_attributes.push(typeof tag_defaults[k] != 'string' ? k : tag_defaults[k]); |
---|
2406 | } |
---|
2407 | } |
---|
2408 | } |
---|
2409 | } |
---|
2410 | return default_attributes; |
---|
2411 | }, |
---|
2412 | doesAttributeNeedsValidation: function(tag, attribute) |
---|
2413 | { |
---|
2414 | return this._tags[tag] && ((this._tags[tag]['attributes'] && this._tags[tag]['attributes'][attribute]) || (this._tags[tag]['required'] && |
---|
2415 | WYMeditor.Helper.contains(this._tags[tag]['required'], attribute))); |
---|
2416 | }, |
---|
2417 | validateAttribute : function(tag, attribute, value) |
---|
2418 | { |
---|
2419 | if ( this._tags[tag] && |
---|
2420 | (this._tags[tag]['attributes'] && this._tags[tag]['attributes'][attribute] && value.length > 0 && !value.match(this._tags[tag]['attributes'][attribute])) || // invalid format |
---|
2421 | (this._tags[tag] && this._tags[tag]['required'] && WYMeditor.Helper.contains(this._tags[tag]['required'], attribute) && value.length == 0) // required attribute |
---|
2422 | ) { |
---|
2423 | return false; |
---|
2424 | } |
---|
2425 | return typeof this._tags[tag] != 'undefined'; |
---|
2426 | }, |
---|
2427 | getPossibleTagAttributes : function(tag) |
---|
2428 | { |
---|
2429 | if (!this._possible_tag_attributes) { |
---|
2430 | this._possible_tag_attributes = {}; |
---|
2431 | } |
---|
2432 | if (!this._possible_tag_attributes[tag]) { |
---|
2433 | this._possible_tag_attributes[tag] = this.getUniqueAttributesAndEventsForTag(tag).concat(this.getDefaultAttributesAndEventsForTag(tag)); |
---|
2434 | } |
---|
2435 | return this._possible_tag_attributes[tag]; |
---|
2436 | } |
---|
2437 | }; |
---|
2438 | |
---|
2439 | |
---|
2440 | /** |
---|
2441 | * Compounded regular expression. Any of |
---|
2442 | * the contained patterns could match and |
---|
2443 | * when one does, it's label is returned. |
---|
2444 | * |
---|
2445 | * Constructor. Starts with no patterns. |
---|
2446 | * @param boolean case True for case sensitive, false |
---|
2447 | * for insensitive. |
---|
2448 | * @access public |
---|
2449 | * @author Marcus Baker (http://lastcraft.com) |
---|
2450 | * @author Bermi Ferrer (http://bermi.org) |
---|
2451 | */ |
---|
2452 | WYMeditor.ParallelRegex = function(case_sensitive) |
---|
2453 | { |
---|
2454 | this._case = case_sensitive; |
---|
2455 | this._patterns = []; |
---|
2456 | this._labels = []; |
---|
2457 | this._regex = null; |
---|
2458 | return this; |
---|
2459 | }; |
---|
2460 | |
---|
2461 | |
---|
2462 | /** |
---|
2463 | * Adds a pattern with an optional label. |
---|
2464 | * @param string pattern Perl style regex, but ( and ) |
---|
2465 | * lose the usual meaning. |
---|
2466 | * @param string label Label of regex to be returned |
---|
2467 | * on a match. |
---|
2468 | * @access public |
---|
2469 | */ |
---|
2470 | WYMeditor.ParallelRegex.prototype.addPattern = function(pattern, label) |
---|
2471 | { |
---|
2472 | label = label || true; |
---|
2473 | var count = this._patterns.length; |
---|
2474 | this._patterns[count] = pattern; |
---|
2475 | this._labels[count] = label; |
---|
2476 | this._regex = null; |
---|
2477 | }; |
---|
2478 | |
---|
2479 | /** |
---|
2480 | * Attempts to match all patterns at once against |
---|
2481 | * a string. |
---|
2482 | * @param string subject String to match against. |
---|
2483 | * |
---|
2484 | * @return boolean True on success. |
---|
2485 | * @return string match First matched portion of |
---|
2486 | * subject. |
---|
2487 | * @access public |
---|
2488 | */ |
---|
2489 | WYMeditor.ParallelRegex.prototype.match = function(subject) |
---|
2490 | { |
---|
2491 | if (this._patterns.length == 0) { |
---|
2492 | return [false, '']; |
---|
2493 | } |
---|
2494 | var matches = subject.match(this._getCompoundedRegex()); |
---|
2495 | |
---|
2496 | if(!matches){ |
---|
2497 | return [false, '']; |
---|
2498 | } |
---|
2499 | var match = matches[0]; |
---|
2500 | for (var i = 1; i < matches.length; i++) { |
---|
2501 | if (matches[i]) { |
---|
2502 | return [this._labels[i-1], match]; |
---|
2503 | } |
---|
2504 | } |
---|
2505 | return [true, matches[0]]; |
---|
2506 | }; |
---|
2507 | |
---|
2508 | /** |
---|
2509 | * Compounds the patterns into a single |
---|
2510 | * regular expression separated with the |
---|
2511 | * "or" operator. Caches the regex. |
---|
2512 | * Will automatically escape (, ) and / tokens. |
---|
2513 | * @param array patterns List of patterns in order. |
---|
2514 | * @access private |
---|
2515 | */ |
---|
2516 | WYMeditor.ParallelRegex.prototype._getCompoundedRegex = function() |
---|
2517 | { |
---|
2518 | if (this._regex == null) { |
---|
2519 | for (var i = 0, count = this._patterns.length; i < count; i++) { |
---|
2520 | this._patterns[i] = '(' + this._untokenizeRegex(this._tokenizeRegex(this._patterns[i]).replace(/([\/\(\)])/g,'\\$1')) + ')'; |
---|
2521 | } |
---|
2522 | this._regex = new RegExp(this._patterns.join("|") ,this._getPerlMatchingFlags()); |
---|
2523 | } |
---|
2524 | return this._regex; |
---|
2525 | }; |
---|
2526 | |
---|
2527 | /** |
---|
2528 | * Escape lookahead/lookbehind blocks |
---|
2529 | */ |
---|
2530 | WYMeditor.ParallelRegex.prototype._tokenizeRegex = function(regex) |
---|
2531 | { |
---|
2532 | return regex. |
---|
2533 | replace(/\(\?(i|m|s|x|U)\)/, '~~~~~~Tk1\$1~~~~~~'). |
---|
2534 | replace(/\(\?(\-[i|m|s|x|U])\)/, '~~~~~~Tk2\$1~~~~~~'). |
---|
2535 | replace(/\(\?\=(.*)\)/, '~~~~~~Tk3\$1~~~~~~'). |
---|
2536 | replace(/\(\?\!(.*)\)/, '~~~~~~Tk4\$1~~~~~~'). |
---|
2537 | replace(/\(\?\<\=(.*)\)/, '~~~~~~Tk5\$1~~~~~~'). |
---|
2538 | replace(/\(\?\<\!(.*)\)/, '~~~~~~Tk6\$1~~~~~~'). |
---|
2539 | replace(/\(\?\:(.*)\)/, '~~~~~~Tk7\$1~~~~~~'); |
---|
2540 | }; |
---|
2541 | |
---|
2542 | /** |
---|
2543 | * Unscape lookahead/lookbehind blocks |
---|
2544 | */ |
---|
2545 | WYMeditor.ParallelRegex.prototype._untokenizeRegex = function(regex) |
---|
2546 | { |
---|
2547 | return regex. |
---|
2548 | replace(/~~~~~~Tk1(.{1})~~~~~~/, "(?\$1)"). |
---|
2549 | replace(/~~~~~~Tk2(.{2})~~~~~~/, "(?\$1)"). |
---|
2550 | replace(/~~~~~~Tk3(.*)~~~~~~/, "(?=\$1)"). |
---|
2551 | replace(/~~~~~~Tk4(.*)~~~~~~/, "(?!\$1)"). |
---|
2552 | replace(/~~~~~~Tk5(.*)~~~~~~/, "(?<=\$1)"). |
---|
2553 | replace(/~~~~~~Tk6(.*)~~~~~~/, "(?<!\$1)"). |
---|
2554 | replace(/~~~~~~Tk7(.*)~~~~~~/, "(?:\$1)"); |
---|
2555 | }; |
---|
2556 | |
---|
2557 | |
---|
2558 | /** |
---|
2559 | * Accessor for perl regex mode flags to use. |
---|
2560 | * @return string Perl regex flags. |
---|
2561 | * @access private |
---|
2562 | */ |
---|
2563 | WYMeditor.ParallelRegex.prototype._getPerlMatchingFlags = function() |
---|
2564 | { |
---|
2565 | return (this._case ? "m" : "mi"); |
---|
2566 | }; |
---|
2567 | |
---|
2568 | |
---|
2569 | |
---|
2570 | /** |
---|
2571 | * States for a stack machine. |
---|
2572 | * |
---|
2573 | * Constructor. Starts in named state. |
---|
2574 | * @param string start Starting state name. |
---|
2575 | * @access public |
---|
2576 | * @author Marcus Baker (http://lastcraft.com) |
---|
2577 | * @author Bermi Ferrer (http://bermi.org) |
---|
2578 | */ |
---|
2579 | WYMeditor.StateStack = function(start) |
---|
2580 | { |
---|
2581 | this._stack = [start]; |
---|
2582 | return this; |
---|
2583 | }; |
---|
2584 | |
---|
2585 | /** |
---|
2586 | * Accessor for current state. |
---|
2587 | * @return string State. |
---|
2588 | * @access public |
---|
2589 | */ |
---|
2590 | WYMeditor.StateStack.prototype.getCurrent = function() |
---|
2591 | { |
---|
2592 | return this._stack[this._stack.length - 1]; |
---|
2593 | }; |
---|
2594 | |
---|
2595 | /** |
---|
2596 | * Adds a state to the stack and sets it |
---|
2597 | * to be the current state. |
---|
2598 | * @param string state New state. |
---|
2599 | * @access public |
---|
2600 | */ |
---|
2601 | WYMeditor.StateStack.prototype.enter = function(state) |
---|
2602 | { |
---|
2603 | this._stack.push(state); |
---|
2604 | }; |
---|
2605 | |
---|
2606 | /** |
---|
2607 | * Leaves the current state and reverts |
---|
2608 | * to the previous one. |
---|
2609 | * @return boolean False if we drop off |
---|
2610 | * the bottom of the list. |
---|
2611 | * @access public |
---|
2612 | */ |
---|
2613 | WYMeditor.StateStack.prototype.leave = function() |
---|
2614 | { |
---|
2615 | if (this._stack.length == 1) { |
---|
2616 | return false; |
---|
2617 | } |
---|
2618 | this._stack.pop(); |
---|
2619 | return true; |
---|
2620 | }; |
---|
2621 | |
---|
2622 | |
---|
2623 | // GLOBALS |
---|
2624 | WYMeditor.LEXER_ENTER = 1; |
---|
2625 | WYMeditor.LEXER_MATCHED = 2; |
---|
2626 | WYMeditor.LEXER_UNMATCHED = 3; |
---|
2627 | WYMeditor.LEXER_EXIT = 4; |
---|
2628 | WYMeditor.LEXER_SPECIAL = 5; |
---|
2629 | |
---|
2630 | |
---|
2631 | /** |
---|
2632 | * Accepts text and breaks it into tokens. |
---|
2633 | * Some optimisation to make the sure the |
---|
2634 | * content is only scanned by the PHP regex |
---|
2635 | * parser once. Lexer modes must not start |
---|
2636 | * with leading underscores. |
---|
2637 | * |
---|
2638 | * Sets up the lexer in case insensitive matching |
---|
2639 | * by default. |
---|
2640 | * @param Parser parser Handling strategy by reference. |
---|
2641 | * @param string start Starting handler. |
---|
2642 | * @param boolean case True for case sensitive. |
---|
2643 | * @access public |
---|
2644 | * @author Marcus Baker (http://lastcraft.com) |
---|
2645 | * @author Bermi Ferrer (http://bermi.org) |
---|
2646 | */ |
---|
2647 | WYMeditor.Lexer = function(parser, start, case_sensitive) |
---|
2648 | { |
---|
2649 | start = start || 'accept'; |
---|
2650 | this._case = case_sensitive || false; |
---|
2651 | this._regexes = {}; |
---|
2652 | this._parser = parser; |
---|
2653 | this._mode = new WYMeditor.StateStack(start); |
---|
2654 | this._mode_handlers = {}; |
---|
2655 | this._mode_handlers[start] = start; |
---|
2656 | return this; |
---|
2657 | }; |
---|
2658 | |
---|
2659 | /** |
---|
2660 | * Adds a token search pattern for a particular |
---|
2661 | * parsing mode. The pattern does not change the |
---|
2662 | * current mode. |
---|
2663 | * @param string pattern Perl style regex, but ( and ) |
---|
2664 | * lose the usual meaning. |
---|
2665 | * @param string mode Should only apply this |
---|
2666 | * pattern when dealing with |
---|
2667 | * this type of input. |
---|
2668 | * @access public |
---|
2669 | */ |
---|
2670 | WYMeditor.Lexer.prototype.addPattern = function(pattern, mode) |
---|
2671 | { |
---|
2672 | var mode = mode || "accept"; |
---|
2673 | if (typeof this._regexes[mode] == 'undefined') { |
---|
2674 | this._regexes[mode] = new WYMeditor.ParallelRegex(this._case); |
---|
2675 | } |
---|
2676 | this._regexes[mode].addPattern(pattern); |
---|
2677 | if (typeof this._mode_handlers[mode] == 'undefined') { |
---|
2678 | this._mode_handlers[mode] = mode; |
---|
2679 | } |
---|
2680 | }; |
---|
2681 | |
---|
2682 | /** |
---|
2683 | * Adds a pattern that will enter a new parsing |
---|
2684 | * mode. Useful for entering parenthesis, strings, |
---|
2685 | * tags, etc. |
---|
2686 | * @param string pattern Perl style regex, but ( and ) |
---|
2687 | * lose the usual meaning. |
---|
2688 | * @param string mode Should only apply this |
---|
2689 | * pattern when dealing with |
---|
2690 | * this type of input. |
---|
2691 | * @param string new_mode Change parsing to this new |
---|
2692 | * nested mode. |
---|
2693 | * @access public |
---|
2694 | */ |
---|
2695 | WYMeditor.Lexer.prototype.addEntryPattern = function(pattern, mode, new_mode) |
---|
2696 | { |
---|
2697 | if (typeof this._regexes[mode] == 'undefined') { |
---|
2698 | this._regexes[mode] = new WYMeditor.ParallelRegex(this._case); |
---|
2699 | } |
---|
2700 | this._regexes[mode].addPattern(pattern, new_mode); |
---|
2701 | if (typeof this._mode_handlers[new_mode] == 'undefined') { |
---|
2702 | this._mode_handlers[new_mode] = new_mode; |
---|
2703 | } |
---|
2704 | }; |
---|
2705 | |
---|
2706 | /** |
---|
2707 | * Adds a pattern that will exit the current mode |
---|
2708 | * and re-enter the previous one. |
---|
2709 | * @param string pattern Perl style regex, but ( and ) |
---|
2710 | * lose the usual meaning. |
---|
2711 | * @param string mode Mode to leave. |
---|
2712 | * @access public |
---|
2713 | */ |
---|
2714 | WYMeditor.Lexer.prototype.addExitPattern = function(pattern, mode) |
---|
2715 | { |
---|
2716 | if (typeof this._regexes[mode] == 'undefined') { |
---|
2717 | this._regexes[mode] = new WYMeditor.ParallelRegex(this._case); |
---|
2718 | } |
---|
2719 | this._regexes[mode].addPattern(pattern, "__exit"); |
---|
2720 | if (typeof this._mode_handlers[mode] == 'undefined') { |
---|
2721 | this._mode_handlers[mode] = mode; |
---|
2722 | } |
---|
2723 | }; |
---|
2724 | |
---|
2725 | /** |
---|
2726 | * Adds a pattern that has a special mode. Acts as an entry |
---|
2727 | * and exit pattern in one go, effectively calling a special |
---|
2728 | * parser handler for this token only. |
---|
2729 | * @param string pattern Perl style regex, but ( and ) |
---|
2730 | * lose the usual meaning. |
---|
2731 | * @param string mode Should only apply this |
---|
2732 | * pattern when dealing with |
---|
2733 | * this type of input. |
---|
2734 | * @param string special Use this mode for this one token. |
---|
2735 | * @access public |
---|
2736 | */ |
---|
2737 | WYMeditor.Lexer.prototype.addSpecialPattern = function(pattern, mode, special) |
---|
2738 | { |
---|
2739 | if (typeof this._regexes[mode] == 'undefined') { |
---|
2740 | this._regexes[mode] = new WYMeditor.ParallelRegex(this._case); |
---|
2741 | } |
---|
2742 | this._regexes[mode].addPattern(pattern, '_'+special); |
---|
2743 | if (typeof this._mode_handlers[special] == 'undefined') { |
---|
2744 | this._mode_handlers[special] = special; |
---|
2745 | } |
---|
2746 | }; |
---|
2747 | |
---|
2748 | /** |
---|
2749 | * Adds a mapping from a mode to another handler. |
---|
2750 | * @param string mode Mode to be remapped. |
---|
2751 | * @param string handler New target handler. |
---|
2752 | * @access public |
---|
2753 | */ |
---|
2754 | WYMeditor.Lexer.prototype.mapHandler = function(mode, handler) |
---|
2755 | { |
---|
2756 | this._mode_handlers[mode] = handler; |
---|
2757 | }; |
---|
2758 | |
---|
2759 | /** |
---|
2760 | * Splits the page text into tokens. Will fail |
---|
2761 | * if the handlers report an error or if no |
---|
2762 | * content is consumed. If successful then each |
---|
2763 | * unparsed and parsed token invokes a call to the |
---|
2764 | * held listener. |
---|
2765 | * @param string raw Raw HTML text. |
---|
2766 | * @return boolean True on success, else false. |
---|
2767 | * @access public |
---|
2768 | */ |
---|
2769 | WYMeditor.Lexer.prototype.parse = function(raw) |
---|
2770 | { |
---|
2771 | if (typeof this._parser == 'undefined') { |
---|
2772 | return false; |
---|
2773 | } |
---|
2774 | |
---|
2775 | var length = raw.length; |
---|
2776 | var parsed; |
---|
2777 | while (typeof (parsed = this._reduce(raw)) == 'object') { |
---|
2778 | var raw = parsed[0]; |
---|
2779 | var unmatched = parsed[1]; |
---|
2780 | var matched = parsed[2]; |
---|
2781 | var mode = parsed[3]; |
---|
2782 | |
---|
2783 | if (! this._dispatchTokens(unmatched, matched, mode)) { |
---|
2784 | return false; |
---|
2785 | } |
---|
2786 | |
---|
2787 | if (raw == '') { |
---|
2788 | return true; |
---|
2789 | } |
---|
2790 | if (raw.length == length) { |
---|
2791 | return false; |
---|
2792 | } |
---|
2793 | length = raw.length; |
---|
2794 | } |
---|
2795 | if (! parsed ) { |
---|
2796 | return false; |
---|
2797 | } |
---|
2798 | |
---|
2799 | return this._invokeParser(raw, WYMeditor.LEXER_UNMATCHED); |
---|
2800 | }; |
---|
2801 | |
---|
2802 | /** |
---|
2803 | * Sends the matched token and any leading unmatched |
---|
2804 | * text to the parser changing the lexer to a new |
---|
2805 | * mode if one is listed. |
---|
2806 | * @param string unmatched Unmatched leading portion. |
---|
2807 | * @param string matched Actual token match. |
---|
2808 | * @param string mode Mode after match. A boolean |
---|
2809 | * false mode causes no change. |
---|
2810 | * @return boolean False if there was any error |
---|
2811 | * from the parser. |
---|
2812 | * @access private |
---|
2813 | */ |
---|
2814 | WYMeditor.Lexer.prototype._dispatchTokens = function(unmatched, matched, mode) |
---|
2815 | { |
---|
2816 | mode = mode || false; |
---|
2817 | |
---|
2818 | if (! this._invokeParser(unmatched, WYMeditor.LEXER_UNMATCHED)) { |
---|
2819 | return false; |
---|
2820 | } |
---|
2821 | |
---|
2822 | if (typeof mode == 'boolean') { |
---|
2823 | return this._invokeParser(matched, WYMeditor.LEXER_MATCHED); |
---|
2824 | } |
---|
2825 | if (this._isModeEnd(mode)) { |
---|
2826 | if (! this._invokeParser(matched, WYMeditor.LEXER_EXIT)) { |
---|
2827 | return false; |
---|
2828 | } |
---|
2829 | return this._mode.leave(); |
---|
2830 | } |
---|
2831 | if (this._isSpecialMode(mode)) { |
---|
2832 | this._mode.enter(this._decodeSpecial(mode)); |
---|
2833 | if (! this._invokeParser(matched, WYMeditor.LEXER_SPECIAL)) { |
---|
2834 | return false; |
---|
2835 | } |
---|
2836 | return this._mode.leave(); |
---|
2837 | } |
---|
2838 | this._mode.enter(mode); |
---|
2839 | |
---|
2840 | return this._invokeParser(matched, WYMeditor.LEXER_ENTER); |
---|
2841 | }; |
---|
2842 | |
---|
2843 | /** |
---|
2844 | * Tests to see if the new mode is actually to leave |
---|
2845 | * the current mode and pop an item from the matching |
---|
2846 | * mode stack. |
---|
2847 | * @param string mode Mode to test. |
---|
2848 | * @return boolean True if this is the exit mode. |
---|
2849 | * @access private |
---|
2850 | */ |
---|
2851 | WYMeditor.Lexer.prototype._isModeEnd = function(mode) |
---|
2852 | { |
---|
2853 | return (mode === "__exit"); |
---|
2854 | }; |
---|
2855 | |
---|
2856 | /** |
---|
2857 | * Test to see if the mode is one where this mode |
---|
2858 | * is entered for this token only and automatically |
---|
2859 | * leaves immediately afterwoods. |
---|
2860 | * @param string mode Mode to test. |
---|
2861 | * @return boolean True if this is the exit mode. |
---|
2862 | * @access private |
---|
2863 | */ |
---|
2864 | WYMeditor.Lexer.prototype._isSpecialMode = function(mode) |
---|
2865 | { |
---|
2866 | return (mode.substring(0,1) == "_"); |
---|
2867 | }; |
---|
2868 | |
---|
2869 | /** |
---|
2870 | * Strips the magic underscore marking single token |
---|
2871 | * modes. |
---|
2872 | * @param string mode Mode to decode. |
---|
2873 | * @return string Underlying mode name. |
---|
2874 | * @access private |
---|
2875 | */ |
---|
2876 | WYMeditor.Lexer.prototype._decodeSpecial = function(mode) |
---|
2877 | { |
---|
2878 | return mode.substring(1); |
---|
2879 | }; |
---|
2880 | |
---|
2881 | /** |
---|
2882 | * Calls the parser method named after the current |
---|
2883 | * mode. Empty content will be ignored. The lexer |
---|
2884 | * has a parser handler for each mode in the lexer. |
---|
2885 | * @param string content Text parsed. |
---|
2886 | * @param boolean is_match Token is recognised rather |
---|
2887 | * than unparsed data. |
---|
2888 | * @access private |
---|
2889 | */ |
---|
2890 | WYMeditor.Lexer.prototype._invokeParser = function(content, is_match) |
---|
2891 | { |
---|
2892 | |
---|
2893 | if (!/ +/.test(content) && ((content === '') || (content == false))) { |
---|
2894 | return true; |
---|
2895 | } |
---|
2896 | var current = this._mode.getCurrent(); |
---|
2897 | var handler = this._mode_handlers[current]; |
---|
2898 | var result; |
---|
2899 | eval('result = this._parser.' + handler + '(content, is_match);'); |
---|
2900 | return result; |
---|
2901 | }; |
---|
2902 | |
---|
2903 | /** |
---|
2904 | * Tries to match a chunk of text and if successful |
---|
2905 | * removes the recognised chunk and any leading |
---|
2906 | * unparsed data. Empty strings will not be matched. |
---|
2907 | * @param string raw The subject to parse. This is the |
---|
2908 | * content that will be eaten. |
---|
2909 | * @return array/boolean Three item list of unparsed |
---|
2910 | * content followed by the |
---|
2911 | * recognised token and finally the |
---|
2912 | * action the parser is to take. |
---|
2913 | * True if no match, false if there |
---|
2914 | * is a parsing error. |
---|
2915 | * @access private |
---|
2916 | */ |
---|
2917 | WYMeditor.Lexer.prototype._reduce = function(raw) |
---|
2918 | { |
---|
2919 | var matched = this._regexes[this._mode.getCurrent()].match(raw); |
---|
2920 | var match = matched[1]; |
---|
2921 | var action = matched[0]; |
---|
2922 | if (action) { |
---|
2923 | var unparsed_character_count = raw.indexOf(match); |
---|
2924 | var unparsed = raw.substr(0, unparsed_character_count); |
---|
2925 | raw = raw.substring(unparsed_character_count + match.length); |
---|
2926 | return [raw, unparsed, match, action]; |
---|
2927 | } |
---|
2928 | return true; |
---|
2929 | }; |
---|
2930 | |
---|
2931 | |
---|
2932 | |
---|
2933 | /** |
---|
2934 | * This are the rules for breaking the XHTML code into events |
---|
2935 | * handled by the provided parser. |
---|
2936 | * |
---|
2937 | * @author Marcus Baker (http://lastcraft.com) |
---|
2938 | * @author Bermi Ferrer (http://bermi.org) |
---|
2939 | */ |
---|
2940 | WYMeditor.XhtmlLexer = function(parser) |
---|
2941 | { |
---|
2942 | jQuery.extend(this, new WYMeditor.Lexer(parser, 'Text')); |
---|
2943 | |
---|
2944 | this.mapHandler('Text', 'Text'); |
---|
2945 | |
---|
2946 | this.addTokens(); |
---|
2947 | |
---|
2948 | this.init(); |
---|
2949 | |
---|
2950 | return this; |
---|
2951 | }; |
---|
2952 | |
---|
2953 | |
---|
2954 | WYMeditor.XhtmlLexer.prototype.init = function() |
---|
2955 | { |
---|
2956 | }; |
---|
2957 | |
---|
2958 | WYMeditor.XhtmlLexer.prototype.addTokens = function() |
---|
2959 | { |
---|
2960 | this.addCommentTokens('Text'); |
---|
2961 | this.addScriptTokens('Text'); |
---|
2962 | this.addCssTokens('Text'); |
---|
2963 | this.addTagTokens('Text'); |
---|
2964 | }; |
---|
2965 | |
---|
2966 | WYMeditor.XhtmlLexer.prototype.addCommentTokens = function(scope) |
---|
2967 | { |
---|
2968 | this.addEntryPattern("<!--", scope, 'Comment'); |
---|
2969 | this.addExitPattern("-->", 'Comment'); |
---|
2970 | }; |
---|
2971 | |
---|
2972 | WYMeditor.XhtmlLexer.prototype.addScriptTokens = function(scope) |
---|
2973 | { |
---|
2974 | this.addEntryPattern("<script", scope, 'Script'); |
---|
2975 | this.addExitPattern("</script>", 'Script'); |
---|
2976 | }; |
---|
2977 | |
---|
2978 | WYMeditor.XhtmlLexer.prototype.addCssTokens = function(scope) |
---|
2979 | { |
---|
2980 | this.addEntryPattern("<style", scope, 'Css'); |
---|
2981 | this.addExitPattern("</style>", 'Css'); |
---|
2982 | }; |
---|
2983 | |
---|
2984 | WYMeditor.XhtmlLexer.prototype.addTagTokens = function(scope) |
---|
2985 | { |
---|
2986 | this.addSpecialPattern("<\\s*[a-z0-9:\-]+\\s*>", scope, 'OpeningTag'); |
---|
2987 | this.addEntryPattern("<[a-z0-9:\-]+"+'[\\\/ \\\>]+', scope, 'OpeningTag'); |
---|
2988 | this.addInTagDeclarationTokens('OpeningTag'); |
---|
2989 | |
---|
2990 | this.addSpecialPattern("</\\s*[a-z0-9:\-]+\\s*>", scope, 'ClosingTag'); |
---|
2991 | |
---|
2992 | }; |
---|
2993 | |
---|
2994 | WYMeditor.XhtmlLexer.prototype.addInTagDeclarationTokens = function(scope) |
---|
2995 | { |
---|
2996 | this.addSpecialPattern('\\s+', scope, 'Ignore'); |
---|
2997 | |
---|
2998 | this.addAttributeTokens(scope); |
---|
2999 | |
---|
3000 | this.addExitPattern('/>', scope); |
---|
3001 | this.addExitPattern('>', scope); |
---|
3002 | |
---|
3003 | }; |
---|
3004 | |
---|
3005 | WYMeditor.XhtmlLexer.prototype.addAttributeTokens = function(scope) |
---|
3006 | { |
---|
3007 | this.addSpecialPattern("\\s*[a-z-_0-9]*:?[a-z-_0-9]+\\s*(?=\=)\\s*", scope, 'TagAttributes'); |
---|
3008 | |
---|
3009 | this.addEntryPattern('=\\s*"', scope, 'DoubleQuotedAttribute'); |
---|
3010 | this.addPattern("\\\\\"", 'DoubleQuotedAttribute'); |
---|
3011 | this.addExitPattern('"', 'DoubleQuotedAttribute'); |
---|
3012 | |
---|
3013 | this.addEntryPattern("=\\s*'", scope, 'SingleQuotedAttribute'); |
---|
3014 | this.addPattern("\\\\'", 'SingleQuotedAttribute'); |
---|
3015 | this.addExitPattern("'", 'SingleQuotedAttribute'); |
---|
3016 | |
---|
3017 | this.addSpecialPattern('=\\s*[^>\\s]*', scope, 'UnquotedAttribute'); |
---|
3018 | }; |
---|
3019 | |
---|
3020 | |
---|
3021 | |
---|
3022 | /** |
---|
3023 | * XHTML Parser. |
---|
3024 | * |
---|
3025 | * This XHTML parser will trigger the events available on on |
---|
3026 | * current SaxListener |
---|
3027 | * |
---|
3028 | * @author Bermi Ferrer (http://bermi.org) |
---|
3029 | */ |
---|
3030 | WYMeditor.XhtmlParser = function(Listener, mode) |
---|
3031 | { |
---|
3032 | var mode = mode || 'Text'; |
---|
3033 | this._Lexer = new WYMeditor.XhtmlLexer(this); |
---|
3034 | this._Listener = Listener; |
---|
3035 | this._mode = mode; |
---|
3036 | this._matches = []; |
---|
3037 | this._last_match = ''; |
---|
3038 | this._current_match = ''; |
---|
3039 | |
---|
3040 | return this; |
---|
3041 | }; |
---|
3042 | |
---|
3043 | WYMeditor.XhtmlParser.prototype.parse = function(raw) |
---|
3044 | { |
---|
3045 | this._Lexer.parse(this.beforeParsing(raw)); |
---|
3046 | return this.afterParsing(this._Listener.getResult()); |
---|
3047 | }; |
---|
3048 | |
---|
3049 | WYMeditor.XhtmlParser.prototype.beforeParsing = function(raw) |
---|
3050 | { |
---|
3051 | if(raw.match(/class="MsoNormal"/) || raw.match(/ns = "urn:schemas-microsoft-com/)){ |
---|
3052 | // Usefull for cleaning up content pasted from other sources (MSWord) |
---|
3053 | this._Listener.avoidStylingTagsAndAttributes(); |
---|
3054 | } |
---|
3055 | return this._Listener.beforeParsing(raw); |
---|
3056 | }; |
---|
3057 | |
---|
3058 | WYMeditor.XhtmlParser.prototype.afterParsing = function(parsed) |
---|
3059 | { |
---|
3060 | if(this._Listener._avoiding_tags_implicitly){ |
---|
3061 | this._Listener.allowStylingTagsAndAttributes(); |
---|
3062 | } |
---|
3063 | return this._Listener.afterParsing(parsed); |
---|
3064 | }; |
---|
3065 | |
---|
3066 | |
---|
3067 | WYMeditor.XhtmlParser.prototype.Ignore = function(match, state) |
---|
3068 | { |
---|
3069 | return true; |
---|
3070 | }; |
---|
3071 | |
---|
3072 | WYMeditor.XhtmlParser.prototype.Text = function(text) |
---|
3073 | { |
---|
3074 | this._Listener.addContent(text); |
---|
3075 | return true; |
---|
3076 | }; |
---|
3077 | |
---|
3078 | WYMeditor.XhtmlParser.prototype.Comment = function(match, status) |
---|
3079 | { |
---|
3080 | return this._addNonTagBlock(match, status, 'addComment'); |
---|
3081 | }; |
---|
3082 | |
---|
3083 | WYMeditor.XhtmlParser.prototype.Script = function(match, status) |
---|
3084 | { |
---|
3085 | return this._addNonTagBlock(match, status, 'addScript'); |
---|
3086 | }; |
---|
3087 | |
---|
3088 | WYMeditor.XhtmlParser.prototype.Css = function(match, status) |
---|
3089 | { |
---|
3090 | return this._addNonTagBlock(match, status, 'addCss'); |
---|
3091 | }; |
---|
3092 | |
---|
3093 | WYMeditor.XhtmlParser.prototype._addNonTagBlock = function(match, state, type) |
---|
3094 | { |
---|
3095 | switch (state){ |
---|
3096 | case WYMeditor.LEXER_ENTER: |
---|
3097 | this._non_tag = match; |
---|
3098 | break; |
---|
3099 | case WYMeditor.LEXER_UNMATCHED: |
---|
3100 | this._non_tag += match; |
---|
3101 | break; |
---|
3102 | case WYMeditor.LEXER_EXIT: |
---|
3103 | switch(type) { |
---|
3104 | case 'addComment': |
---|
3105 | this._Listener.addComment(this._non_tag+match); |
---|
3106 | break; |
---|
3107 | case 'addScript': |
---|
3108 | this._Listener.addScript(this._non_tag+match); |
---|
3109 | break; |
---|
3110 | case 'addCss': |
---|
3111 | this._Listener.addCss(this._non_tag+match); |
---|
3112 | break; |
---|
3113 | } |
---|
3114 | } |
---|
3115 | return true; |
---|
3116 | }; |
---|
3117 | |
---|
3118 | WYMeditor.XhtmlParser.prototype.OpeningTag = function(match, state) |
---|
3119 | { |
---|
3120 | switch (state){ |
---|
3121 | case WYMeditor.LEXER_ENTER: |
---|
3122 | this._tag = this.normalizeTag(match); |
---|
3123 | this._tag_attributes = {}; |
---|
3124 | break; |
---|
3125 | case WYMeditor.LEXER_SPECIAL: |
---|
3126 | this._callOpenTagListener(this.normalizeTag(match)); |
---|
3127 | break; |
---|
3128 | case WYMeditor.LEXER_EXIT: |
---|
3129 | this._callOpenTagListener(this._tag, this._tag_attributes); |
---|
3130 | } |
---|
3131 | return true; |
---|
3132 | }; |
---|
3133 | |
---|
3134 | WYMeditor.XhtmlParser.prototype.ClosingTag = function(match, state) |
---|
3135 | { |
---|
3136 | this._callCloseTagListener(this.normalizeTag(match)); |
---|
3137 | return true; |
---|
3138 | }; |
---|
3139 | |
---|
3140 | WYMeditor.XhtmlParser.prototype._callOpenTagListener = function(tag, attributes) |
---|
3141 | { |
---|
3142 | var attributes = attributes || {}; |
---|
3143 | this.autoCloseUnclosedBeforeNewOpening(tag); |
---|
3144 | |
---|
3145 | if(this._Listener.isBlockTag(tag)){ |
---|
3146 | this._Listener._tag_stack.push(tag); |
---|
3147 | this._Listener.fixNestingBeforeOpeningBlockTag(tag, attributes); |
---|
3148 | this._Listener.openBlockTag(tag, attributes); |
---|
3149 | this._increaseOpenTagCounter(tag); |
---|
3150 | }else if(this._Listener.isInlineTag(tag)){ |
---|
3151 | this._Listener.inlineTag(tag, attributes); |
---|
3152 | }else{ |
---|
3153 | this._Listener.openUnknownTag(tag, attributes); |
---|
3154 | this._increaseOpenTagCounter(tag); |
---|
3155 | } |
---|
3156 | this._Listener.last_tag = tag; |
---|
3157 | this._Listener.last_tag_opened = true; |
---|
3158 | this._Listener.last_tag_attributes = attributes; |
---|
3159 | }; |
---|
3160 | |
---|
3161 | WYMeditor.XhtmlParser.prototype._callCloseTagListener = function(tag) |
---|
3162 | { |
---|
3163 | if(this._decreaseOpenTagCounter(tag)){ |
---|
3164 | this.autoCloseUnclosedBeforeTagClosing(tag); |
---|
3165 | |
---|
3166 | if(this._Listener.isBlockTag(tag)){ |
---|
3167 | var expected_tag = this._Listener._tag_stack.pop(); |
---|
3168 | if(expected_tag == false){ |
---|
3169 | return; |
---|
3170 | }else if(expected_tag != tag){ |
---|
3171 | tag = expected_tag; |
---|
3172 | } |
---|
3173 | this._Listener.closeBlockTag(tag); |
---|
3174 | }else{ |
---|
3175 | this._Listener.closeUnknownTag(tag); |
---|
3176 | } |
---|
3177 | }else{ |
---|
3178 | this._Listener.closeUnopenedTag(tag); |
---|
3179 | } |
---|
3180 | this._Listener.last_tag = tag; |
---|
3181 | this._Listener.last_tag_opened = false; |
---|
3182 | }; |
---|
3183 | |
---|
3184 | WYMeditor.XhtmlParser.prototype._increaseOpenTagCounter = function(tag) |
---|
3185 | { |
---|
3186 | this._Listener._open_tags[tag] = this._Listener._open_tags[tag] || 0; |
---|
3187 | this._Listener._open_tags[tag]++; |
---|
3188 | }; |
---|
3189 | |
---|
3190 | WYMeditor.XhtmlParser.prototype._decreaseOpenTagCounter = function(tag) |
---|
3191 | { |
---|
3192 | if(this._Listener._open_tags[tag]){ |
---|
3193 | this._Listener._open_tags[tag]--; |
---|
3194 | if(this._Listener._open_tags[tag] == 0){ |
---|
3195 | this._Listener._open_tags[tag] = undefined; |
---|
3196 | } |
---|
3197 | return true; |
---|
3198 | } |
---|
3199 | return false; |
---|
3200 | }; |
---|
3201 | |
---|
3202 | WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeNewOpening = function(new_tag) |
---|
3203 | { |
---|
3204 | this._autoCloseUnclosed(new_tag, false); |
---|
3205 | }; |
---|
3206 | |
---|
3207 | WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeTagClosing = function(tag) |
---|
3208 | { |
---|
3209 | this._autoCloseUnclosed(tag, true); |
---|
3210 | }; |
---|
3211 | |
---|
3212 | WYMeditor.XhtmlParser.prototype._autoCloseUnclosed = function(new_tag, closing) |
---|
3213 | { |
---|
3214 | var closing = closing || false; |
---|
3215 | if(this._Listener._open_tags){ |
---|
3216 | for (var tag in this._Listener._open_tags) { |
---|
3217 | var counter = this._Listener._open_tags[tag]; |
---|
3218 | if(counter > 0 && this._Listener.shouldCloseTagAutomatically(tag, new_tag, closing)){ |
---|
3219 | this._callCloseTagListener(tag, true); |
---|
3220 | } |
---|
3221 | } |
---|
3222 | } |
---|
3223 | }; |
---|
3224 | |
---|
3225 | WYMeditor.XhtmlParser.prototype.getTagReplacements = function() |
---|
3226 | { |
---|
3227 | return this._Listener.getTagReplacements(); |
---|
3228 | }; |
---|
3229 | |
---|
3230 | WYMeditor.XhtmlParser.prototype.normalizeTag = function(tag) |
---|
3231 | { |
---|
3232 | tag = tag.replace(/^([\s<\/>]*)|([\s<\/>]*)$/gm,'').toLowerCase(); |
---|
3233 | var tags = this._Listener.getTagReplacements(); |
---|
3234 | if(tags[tag]){ |
---|
3235 | return tags[tag]; |
---|
3236 | } |
---|
3237 | return tag; |
---|
3238 | }; |
---|
3239 | |
---|
3240 | WYMeditor.XhtmlParser.prototype.TagAttributes = function(match, state) |
---|
3241 | { |
---|
3242 | if(WYMeditor.LEXER_SPECIAL == state){ |
---|
3243 | this._current_attribute = match; |
---|
3244 | } |
---|
3245 | return true; |
---|
3246 | }; |
---|
3247 | |
---|
3248 | WYMeditor.XhtmlParser.prototype.DoubleQuotedAttribute = function(match, state) |
---|
3249 | { |
---|
3250 | if(WYMeditor.LEXER_UNMATCHED == state){ |
---|
3251 | this._tag_attributes[this._current_attribute] = match; |
---|
3252 | } |
---|
3253 | return true; |
---|
3254 | }; |
---|
3255 | |
---|
3256 | WYMeditor.XhtmlParser.prototype.SingleQuotedAttribute = function(match, state) |
---|
3257 | { |
---|
3258 | if(WYMeditor.LEXER_UNMATCHED == state){ |
---|
3259 | this._tag_attributes[this._current_attribute] = match; |
---|
3260 | } |
---|
3261 | return true; |
---|
3262 | }; |
---|
3263 | |
---|
3264 | WYMeditor.XhtmlParser.prototype.UnquotedAttribute = function(match, state) |
---|
3265 | { |
---|
3266 | this._tag_attributes[this._current_attribute] = match.replace(/^=/,''); |
---|
3267 | return true; |
---|
3268 | }; |
---|
3269 | |
---|
3270 | |
---|
3271 | |
---|
3272 | /** |
---|
3273 | * XHTML Sax parser. |
---|
3274 | * |
---|
3275 | * @author Bermi Ferrer (http://bermi.org) |
---|
3276 | */ |
---|
3277 | WYMeditor.XhtmlSaxListener = function() |
---|
3278 | { |
---|
3279 | this.output = ''; |
---|
3280 | this.helper = new WYMeditor.XmlHelper(); |
---|
3281 | this._open_tags = {}; |
---|
3282 | this.validator = WYMeditor.XhtmlValidator; |
---|
3283 | this._tag_stack = []; |
---|
3284 | this.avoided_tags = []; |
---|
3285 | |
---|
3286 | this.entities = { |
---|
3287 | ' ':' ','¡':'¡','¢':'¢', |
---|
3288 | '£':'£','¤':'¤','¥':'¥', |
---|
3289 | '¦':'¦','§':'§','¨':'¨', |
---|
3290 | '©':'©','ª':'ª','«':'«', |
---|
3291 | '¬':'¬','­':'­','®':'®', |
---|
3292 | '¯':'¯','°':'°','±':'±', |
---|
3293 | '²':'²','³':'³','´':'´', |
---|
3294 | 'µ':'µ','¶':'¶','·':'·', |
---|
3295 | '¸':'¸','¹':'¹','º':'º', |
---|
3296 | '»':'»','¼':'¼','½':'½', |
---|
3297 | '¾':'¾','¿':'¿','À':'À', |
---|
3298 | 'Á':'Á','Â':'Â','Ã':'Ã', |
---|
3299 | 'Ä':'Ä','Å':'Å','Æ':'Æ', |
---|
3300 | 'Ç':'Ç','È':'È','É':'É', |
---|
3301 | 'Ê':'Ê','Ë':'Ë','Ì':'Ì', |
---|
3302 | 'Í':'Í','Î':'Î','Ï':'Ï', |
---|
3303 | 'Ð':'Ð','Ñ':'Ñ','Ò':'Ò', |
---|
3304 | 'Ó':'Ó','Ô':'Ô','Õ':'Õ', |
---|
3305 | 'Ö':'Ö','×':'×','Ø':'Ø', |
---|
3306 | 'Ù':'Ù','Ú':'Ú','Û':'Û', |
---|
3307 | 'Ü':'Ü','Ý':'Ý','Þ':'Þ', |
---|
3308 | 'ß':'ß','à':'à','á':'á', |
---|
3309 | 'â':'â','ã':'ã','ä':'ä', |
---|
3310 | 'å':'å','æ':'æ','ç':'ç', |
---|
3311 | 'è':'è','é':'é','ê':'ê', |
---|
3312 | 'ë':'ë','ì':'ì','í':'í', |
---|
3313 | 'î':'î','ï':'ï','ð':'ð', |
---|
3314 | 'ñ':'ñ','ò':'ò','ó':'ó', |
---|
3315 | 'ô':'ô','õ':'õ','ö':'ö', |
---|
3316 | '÷':'÷','ø':'ø','ù':'ù', |
---|
3317 | 'ú':'ú','û':'û','ü':'ü', |
---|
3318 | 'ý':'ý','þ':'þ','ÿ':'ÿ', |
---|
3319 | 'Œ':'Œ','œ':'œ','Š':'Š', |
---|
3320 | 'š':'š','Ÿ':'Ÿ','ƒ':'ƒ', |
---|
3321 | 'ˆ':'ˆ','˜':'˜','Α':'Α', |
---|
3322 | 'Β':'Β','Γ':'Γ','Δ':'Δ', |
---|
3323 | 'Ε':'Ε','Ζ':'Ζ','Η':'Η', |
---|
3324 | 'Θ':'Θ','Ι':'Ι','Κ':'Κ', |
---|
3325 | 'Λ':'Λ','Μ':'Μ','Ν':'Ν', |
---|
3326 | 'Ξ':'Ξ','Ο':'Ο','Π':'Π', |
---|
3327 | 'Ρ':'Ρ','Σ':'Σ','Τ':'Τ', |
---|
3328 | 'Υ':'Υ','Φ':'Φ','Χ':'Χ', |
---|
3329 | 'Ψ':'Ψ','Ω':'Ω','α':'α', |
---|
3330 | 'β':'β','γ':'γ','δ':'δ', |
---|
3331 | 'ε':'ε','ζ':'ζ','η':'η', |
---|
3332 | 'θ':'θ','ι':'ι','κ':'κ', |
---|
3333 | 'λ':'λ','μ':'μ','ν':'ν', |
---|
3334 | 'ξ':'ξ','ο':'ο','π':'π', |
---|
3335 | 'ρ':'ρ','ς':'ς','σ':'σ', |
---|
3336 | 'τ':'τ','υ':'υ','φ':'φ', |
---|
3337 | 'χ':'χ','ψ':'ψ','ω':'ω', |
---|
3338 | 'ϑ':'ϑ','ϒ':'ϒ','ϖ':'ϖ', |
---|
3339 | ' ':' ',' ':' ',' ':' ', |
---|
3340 | '‌':'‌','‍':'‍','‎':'‎', |
---|
3341 | '‏':'‏','–':'–','—':'—', |
---|
3342 | '‘':'‘','’':'’','‚':'‚', |
---|
3343 | '“':'“','”':'”','„':'„', |
---|
3344 | '†':'†','‡':'‡','•':'•', |
---|
3345 | '…':'…','‰':'‰','′':'′', |
---|
3346 | '″':'″','‹':'‹','›':'›', |
---|
3347 | '‾':'‾','⁄':'⁄','€':'€', |
---|
3348 | 'ℑ':'ℑ','℘':'℘','ℜ':'ℜ', |
---|
3349 | '™':'™','ℵ':'ℵ','←':'←', |
---|
3350 | '↑':'↑','→':'→','↓':'↓', |
---|
3351 | '↔':'↔','↵':'↵','⇐':'⇐', |
---|
3352 | '⇑':'⇑','⇒':'⇒','⇓':'⇓', |
---|
3353 | '⇔':'⇔','∀':'∀','∂':'∂', |
---|
3354 | '∃':'∃','∅':'∅','∇':'∇', |
---|
3355 | '∈':'∈','∉':'∉','∋':'∋', |
---|
3356 | '∏':'∏','∑':'∑','−':'−', |
---|
3357 | '∗':'∗','√':'√','∝':'∝', |
---|
3358 | '∞':'∞','∠':'∠','∧':'∧', |
---|
3359 | '∨':'∨','∩':'∩','∪':'∪', |
---|
3360 | '∫':'∫','∴':'∴','∼':'∼', |
---|
3361 | '≅':'≅','≈':'≈','≠':'≠', |
---|
3362 | '≡':'≡','≤':'≤','≥':'≥', |
---|
3363 | '⊂':'⊂','⊃':'⊃','⊄':'⊄', |
---|
3364 | '⊆':'⊆','⊇':'⊇','⊕':'⊕', |
---|
3365 | '⊗':'⊗','⊥':'⊥','⋅':'⋅', |
---|
3366 | '⌈':'⌈','⌉':'⌉','⌊':'⌊', |
---|
3367 | '⌋':'⌋','⟨':'〈','⟩':'〉', |
---|
3368 | '◊':'◊','♠':'♠','♣':'♣', |
---|
3369 | '♥':'♥','♦':'♦'}; |
---|
3370 | |
---|
3371 | this.block_tags = ["a", "abbr", "acronym", "address", "area", "b", |
---|
3372 | "base", "bdo", "big", "blockquote", "body", "button", |
---|
3373 | "caption", "cite", "code", "col", "colgroup", "dd", "del", "div", |
---|
3374 | "dfn", "dl", "dt", "em", "fieldset", "form", "head", "h1", "h2", |
---|
3375 | "h3", "h4", "h5", "h6", "html", "i", "ins", |
---|
3376 | "kbd", "label", "legend", "li", "map", "noscript", |
---|
3377 | "object", "ol", "optgroup", "option", "p", "param", "pre", "q", |
---|
3378 | "samp", "script", "select", "small", "span", "strong", "style", |
---|
3379 | "sub", "sup", "table", "tbody", "td", "textarea", "tfoot", "th", |
---|
3380 | "thead", "title", "tr", "tt", "ul", "var", "extends"]; |
---|
3381 | |
---|
3382 | |
---|
3383 | this.inline_tags = ["br", "hr", "img", "input"]; |
---|
3384 | |
---|
3385 | return this; |
---|
3386 | }; |
---|
3387 | |
---|
3388 | WYMeditor.XhtmlSaxListener.prototype.shouldCloseTagAutomatically = function(tag, now_on_tag, closing) |
---|
3389 | { |
---|
3390 | var closing = closing || false; |
---|
3391 | if(tag == 'td'){ |
---|
3392 | if((closing && now_on_tag == 'tr') || (!closing && now_on_tag == 'td')){ |
---|
3393 | return true; |
---|
3394 | } |
---|
3395 | } |
---|
3396 | if(tag == 'option'){ |
---|
3397 | if((closing && now_on_tag == 'select') || (!closing && now_on_tag == 'option')){ |
---|
3398 | return true; |
---|
3399 | } |
---|
3400 | } |
---|
3401 | return false; |
---|
3402 | }; |
---|
3403 | |
---|
3404 | WYMeditor.XhtmlSaxListener.prototype.beforeParsing = function(raw) |
---|
3405 | { |
---|
3406 | this.output = ''; |
---|
3407 | return raw; |
---|
3408 | }; |
---|
3409 | |
---|
3410 | WYMeditor.XhtmlSaxListener.prototype.afterParsing = function(xhtml) |
---|
3411 | { |
---|
3412 | xhtml = this.replaceNamedEntities(xhtml); |
---|
3413 | xhtml = this.joinRepeatedEntities(xhtml); |
---|
3414 | xhtml = this.removeEmptyTags(xhtml); |
---|
3415 | xhtml = this.removeBrInPre(xhtml); |
---|
3416 | return xhtml; |
---|
3417 | }; |
---|
3418 | |
---|
3419 | WYMeditor.XhtmlSaxListener.prototype.replaceNamedEntities = function(xhtml) |
---|
3420 | { |
---|
3421 | for (var entity in this.entities) { |
---|
3422 | xhtml = xhtml.replace(new RegExp(entity, 'g'), this.entities[entity]); |
---|
3423 | } |
---|
3424 | return xhtml; |
---|
3425 | }; |
---|
3426 | |
---|
3427 | WYMeditor.XhtmlSaxListener.prototype.joinRepeatedEntities = function(xhtml) |
---|
3428 | { |
---|
3429 | var tags = 'em|strong|sub|sup|acronym|pre|del|address'; |
---|
3430 | return xhtml.replace(new RegExp('<\/('+tags+')><\\1>' ,''),''). |
---|
3431 | replace(new RegExp('(\s*<('+tags+')>\s*){2}(.*)(\s*<\/\\2>\s*){2}' ,''),'<\$2>\$3<\$2>'); |
---|
3432 | }; |
---|
3433 | |
---|
3434 | WYMeditor.XhtmlSaxListener.prototype.removeEmptyTags = function(xhtml) |
---|
3435 | { |
---|
3436 | return xhtml.replace(new RegExp('<('+this.block_tags.join("|").replace(/\|td/,'').replace(/\|th/, '')+')>(<br \/>| | |\\s)*<\/\\1>' ,'g'),''); |
---|
3437 | }; |
---|
3438 | |
---|
3439 | WYMeditor.XhtmlSaxListener.prototype.removeBrInPre = function(xhtml) |
---|
3440 | { |
---|
3441 | var matches = xhtml.match(new RegExp('<pre[^>]*>(.*?)<\/pre>','gmi')); |
---|
3442 | if(matches) { |
---|
3443 | for(var i=0; i<matches.length; i++) { |
---|
3444 | xhtml = xhtml.replace(matches[i], matches[i].replace(new RegExp('<br \/>', 'g'), String.fromCharCode(13,10))); |
---|
3445 | } |
---|
3446 | } |
---|
3447 | return xhtml; |
---|
3448 | }; |
---|
3449 | |
---|
3450 | WYMeditor.XhtmlSaxListener.prototype.getResult = function() |
---|
3451 | { |
---|
3452 | return this.output; |
---|
3453 | }; |
---|
3454 | |
---|
3455 | WYMeditor.XhtmlSaxListener.prototype.getTagReplacements = function() |
---|
3456 | { |
---|
3457 | return {'b':'strong', 'i':'em'}; |
---|
3458 | }; |
---|
3459 | |
---|
3460 | WYMeditor.XhtmlSaxListener.prototype.addContent = function(text) |
---|
3461 | { |
---|
3462 | this.output += text; |
---|
3463 | }; |
---|
3464 | |
---|
3465 | WYMeditor.XhtmlSaxListener.prototype.addComment = function(text) |
---|
3466 | { |
---|
3467 | if(this.remove_comments){ |
---|
3468 | this.output += text; |
---|
3469 | } |
---|
3470 | }; |
---|
3471 | |
---|
3472 | WYMeditor.XhtmlSaxListener.prototype.addScript = function(text) |
---|
3473 | { |
---|
3474 | if(!this.remove_scripts){ |
---|
3475 | this.output += text; |
---|
3476 | } |
---|
3477 | }; |
---|
3478 | |
---|
3479 | WYMeditor.XhtmlSaxListener.prototype.addCss = function(text) |
---|
3480 | { |
---|
3481 | if(!this.remove_embeded_styles){ |
---|
3482 | this.output += text; |
---|
3483 | } |
---|
3484 | }; |
---|
3485 | |
---|
3486 | WYMeditor.XhtmlSaxListener.prototype.openBlockTag = function(tag, attributes) |
---|
3487 | { |
---|
3488 | this.output += this.helper.tag(tag, this.validator.getValidTagAttributes(tag, attributes), true); |
---|
3489 | }; |
---|
3490 | |
---|
3491 | WYMeditor.XhtmlSaxListener.prototype.inlineTag = function(tag, attributes) |
---|
3492 | { |
---|
3493 | this.output += this.helper.tag(tag, this.validator.getValidTagAttributes(tag, attributes)); |
---|
3494 | }; |
---|
3495 | |
---|
3496 | WYMeditor.XhtmlSaxListener.prototype.openUnknownTag = function(tag, attributes) |
---|
3497 | { |
---|
3498 | //this.output += this.helper.tag(tag, attributes, true); |
---|
3499 | }; |
---|
3500 | |
---|
3501 | WYMeditor.XhtmlSaxListener.prototype.closeBlockTag = function(tag) |
---|
3502 | { |
---|
3503 | this.output = this.output.replace(/<br \/>$/, '')+this._getClosingTagContent('before', tag)+"</"+tag+">"+this._getClosingTagContent('after', tag); |
---|
3504 | }; |
---|
3505 | |
---|
3506 | WYMeditor.XhtmlSaxListener.prototype.closeUnknownTag = function(tag) |
---|
3507 | { |
---|
3508 | //this.output += "</"+tag+">"; |
---|
3509 | }; |
---|
3510 | |
---|
3511 | WYMeditor.XhtmlSaxListener.prototype.closeUnopenedTag = function(tag) |
---|
3512 | { |
---|
3513 | this.output += "</"+tag+">"; |
---|
3514 | }; |
---|
3515 | |
---|
3516 | WYMeditor.XhtmlSaxListener.prototype.avoidStylingTagsAndAttributes = function() |
---|
3517 | { |
---|
3518 | this.avoided_tags = ['div','span']; |
---|
3519 | this.validator.skiped_attributes = ['style']; |
---|
3520 | this.validator.skiped_attribute_values = ['MsoNormal','main1']; // MS Word attributes for class |
---|
3521 | this._avoiding_tags_implicitly = true; |
---|
3522 | }; |
---|
3523 | |
---|
3524 | WYMeditor.XhtmlSaxListener.prototype.allowStylingTagsAndAttributes = function() |
---|
3525 | { |
---|
3526 | this.avoided_tags = []; |
---|
3527 | this.validator.skiped_attributes = []; |
---|
3528 | this.validator.skiped_attribute_values = []; |
---|
3529 | this._avoiding_tags_implicitly = false; |
---|
3530 | }; |
---|
3531 | |
---|
3532 | WYMeditor.XhtmlSaxListener.prototype.isBlockTag = function(tag) |
---|
3533 | { |
---|
3534 | return !WYMeditor.Helper.contains(this.avoided_tags, tag) && WYMeditor.Helper.contains(this.block_tags, tag); |
---|
3535 | }; |
---|
3536 | |
---|
3537 | WYMeditor.XhtmlSaxListener.prototype.isInlineTag = function(tag) |
---|
3538 | { |
---|
3539 | return !WYMeditor.Helper.contains(this.avoided_tags, tag) && WYMeditor.Helper.contains(this.inline_tags, tag); |
---|
3540 | }; |
---|
3541 | |
---|
3542 | WYMeditor.XhtmlSaxListener.prototype.insertContentAfterClosingTag = function(tag, content) |
---|
3543 | { |
---|
3544 | this._insertContentWhenClosingTag('after', tag, content); |
---|
3545 | }; |
---|
3546 | |
---|
3547 | WYMeditor.XhtmlSaxListener.prototype.insertContentBeforeClosingTag = function(tag, content) |
---|
3548 | { |
---|
3549 | this._insertContentWhenClosingTag('before', tag, content); |
---|
3550 | }; |
---|
3551 | |
---|
3552 | WYMeditor.XhtmlSaxListener.prototype.fixNestingBeforeOpeningBlockTag = function(tag, attributes) |
---|
3553 | { |
---|
3554 | if(tag != 'li' && (tag == 'ul' || tag == 'ol') && this.last_tag && !this.last_tag_opened && this.last_tag == 'li'){ |
---|
3555 | this.output = this.output.replace(/<\/li>$/, ''); |
---|
3556 | this.insertContentAfterClosingTag(tag, '</li>'); |
---|
3557 | } |
---|
3558 | }; |
---|
3559 | |
---|
3560 | WYMeditor.XhtmlSaxListener.prototype._insertContentWhenClosingTag = function(position, tag, content) |
---|
3561 | { |
---|
3562 | if(!this['_insert_'+position+'_closing']){ |
---|
3563 | this['_insert_'+position+'_closing'] = []; |
---|
3564 | } |
---|
3565 | if(!this['_insert_'+position+'_closing'][tag]){ |
---|
3566 | this['_insert_'+position+'_closing'][tag] = []; |
---|
3567 | } |
---|
3568 | this['_insert_'+position+'_closing'][tag].push(content); |
---|
3569 | }; |
---|
3570 | |
---|
3571 | WYMeditor.XhtmlSaxListener.prototype._getClosingTagContent = function(position, tag) |
---|
3572 | { |
---|
3573 | if( this['_insert_'+position+'_closing'] && |
---|
3574 | this['_insert_'+position+'_closing'][tag] && |
---|
3575 | this['_insert_'+position+'_closing'][tag].length > 0){ |
---|
3576 | return this['_insert_'+position+'_closing'][tag].pop(); |
---|
3577 | } |
---|
3578 | return ''; |
---|
3579 | }; |
---|
3580 | |
---|
3581 | |
---|
3582 | /********** CSS PARSER **********/ |
---|
3583 | |
---|
3584 | |
---|
3585 | WYMeditor.WymCssLexer = function(parser, only_wym_blocks) |
---|
3586 | { |
---|
3587 | var only_wym_blocks = (typeof only_wym_blocks == 'undefined' ? true : only_wym_blocks); |
---|
3588 | |
---|
3589 | jQuery.extend(this, new WYMeditor.Lexer(parser, (only_wym_blocks?'Ignore':'WymCss'))); |
---|
3590 | |
---|
3591 | this.mapHandler('WymCss', 'Ignore'); |
---|
3592 | |
---|
3593 | if(only_wym_blocks == true){ |
---|
3594 | this.addEntryPattern("/\\\x2a[<\\s]*WYMeditor[>\\s]*\\\x2a/", 'Ignore', 'WymCss'); |
---|
3595 | this.addExitPattern("/\\\x2a[<\/\\s]*WYMeditor[>\\s]*\\\x2a/", 'WymCss'); |
---|
3596 | } |
---|
3597 | |
---|
3598 | this.addSpecialPattern("[\\sa-z1-6]*\\\x2e[a-z-_0-9]+", 'WymCss', 'WymCssStyleDeclaration'); |
---|
3599 | |
---|
3600 | this.addEntryPattern("/\\\x2a", 'WymCss', 'WymCssComment'); |
---|
3601 | this.addExitPattern("\\\x2a/", 'WymCssComment'); |
---|
3602 | |
---|
3603 | this.addEntryPattern("\x7b", 'WymCss', 'WymCssStyle'); |
---|
3604 | this.addExitPattern("\x7d", 'WymCssStyle'); |
---|
3605 | |
---|
3606 | this.addEntryPattern("/\\\x2a", 'WymCssStyle', 'WymCssFeedbackStyle'); |
---|
3607 | this.addExitPattern("\\\x2a/", 'WymCssFeedbackStyle'); |
---|
3608 | |
---|
3609 | return this; |
---|
3610 | }; |
---|
3611 | |
---|
3612 | WYMeditor.WymCssParser = function() |
---|
3613 | { |
---|
3614 | this._in_style = false; |
---|
3615 | this._has_title = false; |
---|
3616 | this.only_wym_blocks = true; |
---|
3617 | this.css_settings = {'classesItems':[], 'editorStyles':[], 'dialogStyles':[]}; |
---|
3618 | return this; |
---|
3619 | }; |
---|
3620 | |
---|
3621 | WYMeditor.WymCssParser.prototype.parse = function(raw, only_wym_blocks) |
---|
3622 | { |
---|
3623 | var only_wym_blocks = (typeof only_wym_blocks == 'undefined' ? this.only_wym_blocks : only_wym_blocks); |
---|
3624 | this._Lexer = new WYMeditor.WymCssLexer(this, only_wym_blocks); |
---|
3625 | this._Lexer.parse(raw); |
---|
3626 | }; |
---|
3627 | |
---|
3628 | WYMeditor.WymCssParser.prototype.Ignore = function(match, state) |
---|
3629 | { |
---|
3630 | return true; |
---|
3631 | }; |
---|
3632 | |
---|
3633 | WYMeditor.WymCssParser.prototype.WymCssComment = function(text, status) |
---|
3634 | { |
---|
3635 | if(text.match(/end[a-z0-9\s]*wym[a-z0-9\s]*/mi)){ |
---|
3636 | return false; |
---|
3637 | } |
---|
3638 | if(status == WYMeditor.LEXER_UNMATCHED){ |
---|
3639 | if(!this._in_style){ |
---|
3640 | this._has_title = true; |
---|
3641 | this._current_item = {'title':WYMeditor.Helper.trim(text)}; |
---|
3642 | }else{ |
---|
3643 | if(this._current_item[this._current_element]){ |
---|
3644 | if(!this._current_item[this._current_element].expressions){ |
---|
3645 | this._current_item[this._current_element].expressions = [text]; |
---|
3646 | }else{ |
---|
3647 | this._current_item[this._current_element].expressions.push(text); |
---|
3648 | } |
---|
3649 | } |
---|
3650 | } |
---|
3651 | this._in_style = true; |
---|
3652 | } |
---|
3653 | return true; |
---|
3654 | }; |
---|
3655 | |
---|
3656 | WYMeditor.WymCssParser.prototype.WymCssStyle = function(match, status) |
---|
3657 | { |
---|
3658 | if(status == WYMeditor.LEXER_UNMATCHED){ |
---|
3659 | match = WYMeditor.Helper.trim(match); |
---|
3660 | if(match != ''){ |
---|
3661 | this._current_item[this._current_element].style = match; |
---|
3662 | } |
---|
3663 | }else if (status == WYMeditor.LEXER_EXIT){ |
---|
3664 | this._in_style = false; |
---|
3665 | this._has_title = false; |
---|
3666 | this.addStyleSetting(this._current_item); |
---|
3667 | } |
---|
3668 | return true; |
---|
3669 | }; |
---|
3670 | |
---|
3671 | WYMeditor.WymCssParser.prototype.WymCssFeedbackStyle = function(match, status) |
---|
3672 | { |
---|
3673 | if(status == WYMeditor.LEXER_UNMATCHED){ |
---|
3674 | this._current_item[this._current_element].feedback_style = match.replace(/^([\s\/\*]*)|([\s\/\*]*)$/gm,''); |
---|
3675 | } |
---|
3676 | return true; |
---|
3677 | }; |
---|
3678 | |
---|
3679 | WYMeditor.WymCssParser.prototype.WymCssStyleDeclaration = function(match) |
---|
3680 | { |
---|
3681 | match = match.replace(/^([\s\.]*)|([\s\.*]*)$/gm, ''); |
---|
3682 | |
---|
3683 | var tag = ''; |
---|
3684 | if(match.indexOf('.') > 0){ |
---|
3685 | var parts = match.split('.'); |
---|
3686 | this._current_element = parts[1]; |
---|
3687 | var tag = parts[0]; |
---|
3688 | }else{ |
---|
3689 | this._current_element = match; |
---|
3690 | } |
---|
3691 | |
---|
3692 | if(!this._has_title){ |
---|
3693 | this._current_item = {'title':(!tag?'':tag.toUpperCase()+': ')+this._current_element}; |
---|
3694 | this._has_title = true; |
---|
3695 | } |
---|
3696 | |
---|
3697 | if(!this._current_item[this._current_element]){ |
---|
3698 | this._current_item[this._current_element] = {'name':this._current_element}; |
---|
3699 | } |
---|
3700 | if(tag){ |
---|
3701 | if(!this._current_item[this._current_element].tags){ |
---|
3702 | this._current_item[this._current_element].tags = [tag]; |
---|
3703 | }else{ |
---|
3704 | this._current_item[this._current_element].tags.push(tag); |
---|
3705 | } |
---|
3706 | } |
---|
3707 | return true; |
---|
3708 | }; |
---|
3709 | |
---|
3710 | WYMeditor.WymCssParser.prototype.addStyleSetting = function(style_details) |
---|
3711 | { |
---|
3712 | for (var name in style_details){ |
---|
3713 | var details = style_details[name]; |
---|
3714 | if(typeof details == 'object' && name != 'title'){ |
---|
3715 | |
---|
3716 | this.css_settings.classesItems.push({ |
---|
3717 | 'name': WYMeditor.Helper.trim(details.name), |
---|
3718 | 'title': style_details.title, |
---|
3719 | 'expr' : WYMeditor.Helper.trim((details.expressions||details.tags).join(', ')) |
---|
3720 | }); |
---|
3721 | if(details.feedback_style){ |
---|
3722 | this.css_settings.editorStyles.push({ |
---|
3723 | 'name': '.'+ WYMeditor.Helper.trim(details.name), |
---|
3724 | 'css': details.feedback_style |
---|
3725 | }); |
---|
3726 | } |
---|
3727 | if(details.style){ |
---|
3728 | this.css_settings.dialogStyles.push({ |
---|
3729 | 'name': '.'+ WYMeditor.Helper.trim(details.name), |
---|
3730 | 'css': details.style |
---|
3731 | }); |
---|
3732 | } |
---|
3733 | } |
---|
3734 | } |
---|
3735 | }; |
---|
3736 | |
---|
3737 | /********** HELPERS **********/ |
---|
3738 | |
---|
3739 | // Returns true if it is a text node with whitespaces only |
---|
3740 | jQuery.fn.isPhantomNode = function() { |
---|
3741 | if (this[0].nodeType == 3) |
---|
3742 | return !(/[^\t\n\r ]/.test(this[0].data)); |
---|
3743 | |
---|
3744 | return false; |
---|
3745 | }; |
---|
3746 | |
---|
3747 | WYMeditor.isPhantomNode = function(n) { |
---|
3748 | if (n.nodeType == 3) |
---|
3749 | return !(/[^\t\n\r ]/.test(n.data)); |
---|
3750 | |
---|
3751 | return false; |
---|
3752 | }; |
---|
3753 | |
---|
3754 | WYMeditor.isPhantomString = function(str) { |
---|
3755 | return !(/[^\t\n\r ]/.test(str)); |
---|
3756 | }; |
---|
3757 | |
---|
3758 | // Returns the Parents or the node itself |
---|
3759 | // jqexpr = a jQuery expression |
---|
3760 | jQuery.fn.parentsOrSelf = function(jqexpr) { |
---|
3761 | var n = this; |
---|
3762 | |
---|
3763 | if (n[0].nodeType == 3) |
---|
3764 | n = n.parents().slice(0,1); |
---|
3765 | |
---|
3766 | // if (n.is(jqexpr)) // XXX should work, but doesn't (probably a jQuery bug) |
---|
3767 | if (n.filter(jqexpr).size() == 1) |
---|
3768 | return n; |
---|
3769 | else |
---|
3770 | return n.parents(jqexpr).slice(0,1); |
---|
3771 | }; |
---|
3772 | |
---|
3773 | // String & array helpers |
---|
3774 | |
---|
3775 | WYMeditor.Helper = { |
---|
3776 | |
---|
3777 | //replace all instances of 'old' by 'rep' in 'str' string |
---|
3778 | replaceAll: function(str, old, rep) { |
---|
3779 | var rExp = new RegExp(old, "g"); |
---|
3780 | return(str.replace(rExp, rep)); |
---|
3781 | }, |
---|
3782 | |
---|
3783 | //insert 'inserted' at position 'pos' in 'str' string |
---|
3784 | insertAt: function(str, inserted, pos) { |
---|
3785 | return(str.substr(0,pos) + inserted + str.substring(pos)); |
---|
3786 | }, |
---|
3787 | |
---|
3788 | //trim 'str' string |
---|
3789 | trim: function(str) { |
---|
3790 | return str.replace(/^(\s*)|(\s*)$/gm,''); |
---|
3791 | }, |
---|
3792 | |
---|
3793 | //return true if 'arr' array contains 'elem', or false |
---|
3794 | contains: function(arr, elem) { |
---|
3795 | for (var i = 0; i < arr.length; i++) { |
---|
3796 | if (arr[i] === elem) return true; |
---|
3797 | } |
---|
3798 | return false; |
---|
3799 | }, |
---|
3800 | |
---|
3801 | //return 'item' position in 'arr' array, or -1 |
---|
3802 | indexOf: function(arr, item) { |
---|
3803 | var ret=-1; |
---|
3804 | for(var i = 0; i < arr.length; i++) { |
---|
3805 | if (arr[i] == item) { |
---|
3806 | ret = i; |
---|
3807 | break; |
---|
3808 | } |
---|
3809 | } |
---|
3810 | return(ret); |
---|
3811 | }, |
---|
3812 | |
---|
3813 | //return 'item' object in 'arr' array, checking its 'name' property, or null |
---|
3814 | findByName: function(arr, name) { |
---|
3815 | for(var i = 0; i < arr.length; i++) { |
---|
3816 | var item = arr[i]; |
---|
3817 | if(item.name == name) return(item); |
---|
3818 | } |
---|
3819 | return(null); |
---|
3820 | } |
---|
3821 | }; |
---|
3822 | |
---|
3823 | |
---|
3824 | /* |
---|
3825 | * WYMeditor : what you see is What You Mean web-based editor |
---|
3826 | * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/ |
---|
3827 | * Dual licensed under the MIT (MIT-license.txt) |
---|
3828 | * and GPL (GPL-license.txt) licenses. |
---|
3829 | * |
---|
3830 | * For further information visit: |
---|
3831 | * http://www.wymeditor.org/ |
---|
3832 | * |
---|
3833 | * File Name: |
---|
3834 | * jquery.wymeditor.explorer.js |
---|
3835 | * MSIE specific class and functions. |
---|
3836 | * See the documentation for more info. |
---|
3837 | * |
---|
3838 | * File Authors: |
---|
3839 | * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg) |
---|
3840 | * Bermi Ferrer (wymeditor a-t bermi dotorg) |
---|
3841 | * Frテゥdテゥric Palluel-Lafleur (fpalluel a-t gmail dotcom) |
---|
3842 | * Jonatan Lundin (jonatan.lundin _at_ gmail.com) |
---|
3843 | */ |
---|
3844 | |
---|
3845 | WYMeditor.WymClassExplorer = function(wym) { |
---|
3846 | |
---|
3847 | this._wym = wym; |
---|
3848 | this._class = "className"; |
---|
3849 | this._newLine = "\r\n"; |
---|
3850 | |
---|
3851 | }; |
---|
3852 | |
---|
3853 | WYMeditor.WymClassExplorer.prototype.initIframe = function(iframe) { |
---|
3854 | |
---|
3855 | //This function is executed twice, though it is called once! |
---|
3856 | //But MSIE needs that, otherwise designMode won't work. |
---|
3857 | //Weird. |
---|
3858 | |
---|
3859 | this._iframe = iframe; |
---|
3860 | this._doc = iframe.contentWindow.document; |
---|
3861 | |
---|
3862 | //add css rules from options |
---|
3863 | var styles = this._doc.styleSheets[0]; |
---|
3864 | var aCss = eval(this._options.editorStyles); |
---|
3865 | |
---|
3866 | this.addCssRules(this._doc, aCss); |
---|
3867 | |
---|
3868 | this._doc.title = this._wym._index; |
---|
3869 | |
---|
3870 | //set the text direction |
---|
3871 | jQuery('html', this._doc).attr('dir', this._options.direction); |
---|
3872 | |
---|
3873 | //init html value |
---|
3874 | jQuery(this._doc.body).html(this._wym._html); |
---|
3875 | |
---|
3876 | //handle events |
---|
3877 | var wym = this; |
---|
3878 | |
---|
3879 | this._doc.body.onfocus = function() |
---|
3880 | {wym._doc.designMode = "on"; wym._doc = iframe.contentWindow.document;}; |
---|
3881 | this._doc.onbeforedeactivate = function() {wym.saveCaret();}; |
---|
3882 | this._doc.onkeyup = function() { |
---|
3883 | wym.saveCaret(); |
---|
3884 | wym.keyup(); |
---|
3885 | }; |
---|
3886 | this._doc.onclick = function() {wym.saveCaret();}; |
---|
3887 | |
---|
3888 | this._doc.body.onbeforepaste = function() { |
---|
3889 | wym._iframe.contentWindow.event.returnValue = false; |
---|
3890 | }; |
---|
3891 | |
---|
3892 | this._doc.body.onpaste = function() { |
---|
3893 | wym._iframe.contentWindow.event.returnValue = false; |
---|
3894 | wym.paste(window.clipboardData.getData("Text")); |
---|
3895 | }; |
---|
3896 | |
---|
3897 | //callback can't be executed twice, so we check |
---|
3898 | if(this._initialized) { |
---|
3899 | |
---|
3900 | //pre-bind functions |
---|
3901 | if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this); |
---|
3902 | |
---|
3903 | //bind external events |
---|
3904 | this._wym.bindEvents(); |
---|
3905 | |
---|
3906 | //post-init functions |
---|
3907 | if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this); |
---|
3908 | |
---|
3909 | //add event listeners to doc elements, e.g. images |
---|
3910 | this.listen(); |
---|
3911 | } |
---|
3912 | |
---|
3913 | this._initialized = true; |
---|
3914 | |
---|
3915 | //init designMode |
---|
3916 | this._doc.designMode="on"; |
---|
3917 | try{ |
---|
3918 | // (bermi's note) noticed when running unit tests on IE6 |
---|
3919 | // Is this really needed, it trigger an unexisting property on IE6 |
---|
3920 | this._doc = iframe.contentWindow.document; |
---|
3921 | }catch(e){} |
---|
3922 | }; |
---|
3923 | |
---|
3924 | WYMeditor.WymClassExplorer.prototype._exec = function(cmd,param) { |
---|
3925 | |
---|
3926 | switch(cmd) { |
---|
3927 | |
---|
3928 | case WYMeditor.INDENT: case WYMeditor.OUTDENT: |
---|
3929 | |
---|
3930 | var container = this.findUp(this.container(), WYMeditor.LI); |
---|
3931 | if(container) { |
---|
3932 | var ancestor = container.parentNode.parentNode; |
---|
3933 | if(container.parentNode.childNodes.length>1 |
---|
3934 | || ancestor.tagName.toLowerCase() == WYMeditor.OL |
---|
3935 | || ancestor.tagName.toLowerCase() == WYMeditor.UL) |
---|
3936 | this._doc.execCommand(cmd); |
---|
3937 | } |
---|
3938 | break; |
---|
3939 | default: |
---|
3940 | if(param) this._doc.execCommand(cmd,false,param); |
---|
3941 | else this._doc.execCommand(cmd); |
---|
3942 | break; |
---|
3943 | } |
---|
3944 | |
---|
3945 | this.listen(); |
---|
3946 | }; |
---|
3947 | |
---|
3948 | WYMeditor.WymClassExplorer.prototype.selected = function() { |
---|
3949 | |
---|
3950 | var caretPos = this._iframe.contentWindow.document.caretPos; |
---|
3951 | if(caretPos!=null) { |
---|
3952 | if(caretPos.parentElement!=undefined) |
---|
3953 | return(caretPos.parentElement()); |
---|
3954 | } |
---|
3955 | }; |
---|
3956 | |
---|
3957 | WYMeditor.WymClassExplorer.prototype.saveCaret = function() { |
---|
3958 | |
---|
3959 | this._doc.caretPos = this._doc.selection.createRange(); |
---|
3960 | }; |
---|
3961 | |
---|
3962 | WYMeditor.WymClassExplorer.prototype.addCssRule = function(styles, oCss) { |
---|
3963 | |
---|
3964 | styles.addRule(oCss.name, oCss.css); |
---|
3965 | }; |
---|
3966 | |
---|
3967 | WYMeditor.WymClassExplorer.prototype.insert = function(html) { |
---|
3968 | |
---|
3969 | // Get the current selection |
---|
3970 | var range = this._doc.selection.createRange(); |
---|
3971 | |
---|
3972 | // Check if the current selection is inside the editor |
---|
3973 | if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) { |
---|
3974 | try { |
---|
3975 | // Overwrite selection with provided html |
---|
3976 | range.pasteHTML(html); |
---|
3977 | } catch (e) { } |
---|
3978 | } else { |
---|
3979 | // Fall back to the internal paste function if there's no selection |
---|
3980 | this.paste(html); |
---|
3981 | } |
---|
3982 | }; |
---|
3983 | |
---|
3984 | WYMeditor.WymClassExplorer.prototype.wrap = function(left, right) { |
---|
3985 | |
---|
3986 | // Get the current selection |
---|
3987 | var range = this._doc.selection.createRange(); |
---|
3988 | |
---|
3989 | // Check if the current selection is inside the editor |
---|
3990 | if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) { |
---|
3991 | try { |
---|
3992 | // Overwrite selection with provided html |
---|
3993 | range.pasteHTML(left + range.text + right); |
---|
3994 | } catch (e) { } |
---|
3995 | } |
---|
3996 | }; |
---|
3997 | |
---|
3998 | WYMeditor.WymClassExplorer.prototype.unwrap = function() { |
---|
3999 | |
---|
4000 | // Get the current selection |
---|
4001 | var range = this._doc.selection.createRange(); |
---|
4002 | |
---|
4003 | // Check if the current selection is inside the editor |
---|
4004 | if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) { |
---|
4005 | try { |
---|
4006 | // Unwrap selection |
---|
4007 | var text = range.text; |
---|
4008 | this._exec( 'Cut' ); |
---|
4009 | range.pasteHTML( text ); |
---|
4010 | } catch (e) { } |
---|
4011 | } |
---|
4012 | }; |
---|
4013 | |
---|
4014 | //keyup handler |
---|
4015 | WYMeditor.WymClassExplorer.prototype.keyup = function() { |
---|
4016 | this._selected_image = null; |
---|
4017 | }; |
---|
4018 | |
---|
4019 | WYMeditor.WymClassExplorer.prototype.setFocusToNode = function(node) { |
---|
4020 | var range = this._doc.selection.createRange(); |
---|
4021 | range.moveToElementText(node); |
---|
4022 | range.collapse(false); |
---|
4023 | range.move('character',-1); |
---|
4024 | range.select(); |
---|
4025 | node.focus(); |
---|
4026 | }; |
---|
4027 | |
---|
4028 | /* |
---|
4029 | * WYMeditor : what you see is What You Mean web-based editor |
---|
4030 | * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/ |
---|
4031 | * Dual licensed under the MIT (MIT-license.txt) |
---|
4032 | * and GPL (GPL-license.txt) licenses. |
---|
4033 | * |
---|
4034 | * For further information visit: |
---|
4035 | * http://www.wymeditor.org/ |
---|
4036 | * |
---|
4037 | * File Name: |
---|
4038 | * jquery.wymeditor.mozilla.js |
---|
4039 | * Gecko specific class and functions. |
---|
4040 | * See the documentation for more info. |
---|
4041 | * |
---|
4042 | * File Authors: |
---|
4043 | * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg) |
---|
4044 | * Volker Mische (vmx a-t gmx dotde) |
---|
4045 | * Bermi Ferrer (wymeditor a-t bermi dotorg) |
---|
4046 | * Frテゥdテゥric Palluel-Lafleur (fpalluel a-t gmail dotcom) |
---|
4047 | */ |
---|
4048 | |
---|
4049 | WYMeditor.WymClassMozilla = function(wym) { |
---|
4050 | |
---|
4051 | this._wym = wym; |
---|
4052 | this._class = "class"; |
---|
4053 | this._newLine = "\n"; |
---|
4054 | }; |
---|
4055 | |
---|
4056 | WYMeditor.WymClassMozilla.prototype.initIframe = function(iframe) { |
---|
4057 | |
---|
4058 | this._iframe = iframe; |
---|
4059 | this._doc = iframe.contentDocument; |
---|
4060 | |
---|
4061 | //add css rules from options |
---|
4062 | |
---|
4063 | var styles = this._doc.styleSheets[0]; |
---|
4064 | var aCss = eval(this._options.editorStyles); |
---|
4065 | |
---|
4066 | this.addCssRules(this._doc, aCss); |
---|
4067 | |
---|
4068 | this._doc.title = this._wym._index; |
---|
4069 | |
---|
4070 | //set the text direction |
---|
4071 | jQuery('html', this._doc).attr('dir', this._options.direction); |
---|
4072 | |
---|
4073 | //init html value |
---|
4074 | this.html(this._wym._html); |
---|
4075 | |
---|
4076 | //init designMode |
---|
4077 | this.enableDesignMode(); |
---|
4078 | |
---|
4079 | //pre-bind functions |
---|
4080 | if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this); |
---|
4081 | |
---|
4082 | //bind external events |
---|
4083 | this._wym.bindEvents(); |
---|
4084 | |
---|
4085 | //bind editor keydown events |
---|
4086 | jQuery(this._doc).bind("keydown", this.keydown); |
---|
4087 | |
---|
4088 | //bind editor keyup events |
---|
4089 | jQuery(this._doc).bind("keyup", this.keyup); |
---|
4090 | |
---|
4091 | //bind editor focus events (used to reset designmode - Gecko bug) |
---|
4092 | jQuery(this._doc).bind("focus", this.enableDesignMode); |
---|
4093 | |
---|
4094 | //post-init functions |
---|
4095 | if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this); |
---|
4096 | |
---|
4097 | //add event listeners to doc elements, e.g. images |
---|
4098 | this.listen(); |
---|
4099 | }; |
---|
4100 | |
---|
4101 | /* @name html |
---|
4102 | * @description Get/Set the html value |
---|
4103 | */ |
---|
4104 | WYMeditor.WymClassMozilla.prototype.html = function(html) { |
---|
4105 | |
---|
4106 | if(typeof html === 'string') { |
---|
4107 | |
---|
4108 | //disable designMode |
---|
4109 | try { this._doc.designMode = "off"; } catch(e) { }; |
---|
4110 | |
---|
4111 | //replace em by i and strong by bold |
---|
4112 | //(designMode issue) |
---|
4113 | html = html.replace(/<em(\b[^>]*)>/gi, "<i$1>") |
---|
4114 | .replace(/<\/em>/gi, "</i>") |
---|
4115 | .replace(/<strong(\b[^>]*)>/gi, "<b$1>") |
---|
4116 | .replace(/<\/strong>/gi, "</b>"); |
---|
4117 | |
---|
4118 | //update the html body |
---|
4119 | jQuery(this._doc.body).html(html); |
---|
4120 | |
---|
4121 | //re-init designMode |
---|
4122 | this.enableDesignMode(); |
---|
4123 | } |
---|
4124 | else return(jQuery(this._doc.body).html()); |
---|
4125 | }; |
---|
4126 | |
---|
4127 | WYMeditor.WymClassMozilla.prototype._exec = function(cmd,param) { |
---|
4128 | |
---|
4129 | if(!this.selected()) return(false); |
---|
4130 | |
---|
4131 | switch(cmd) { |
---|
4132 | |
---|
4133 | case WYMeditor.INDENT: case WYMeditor.OUTDENT: |
---|
4134 | |
---|
4135 | var focusNode = this.selected(); |
---|
4136 | var sel = this._iframe.contentWindow.getSelection(); |
---|
4137 | var anchorNode = sel.anchorNode; |
---|
4138 | if(anchorNode.nodeName == "#text") anchorNode = anchorNode.parentNode; |
---|
4139 | |
---|
4140 | focusNode = this.findUp(focusNode, WYMeditor.BLOCKS); |
---|
4141 | anchorNode = this.findUp(anchorNode, WYMeditor.BLOCKS); |
---|
4142 | |
---|
4143 | if(focusNode && focusNode == anchorNode |
---|
4144 | && focusNode.tagName.toLowerCase() == WYMeditor.LI) { |
---|
4145 | |
---|
4146 | var ancestor = focusNode.parentNode.parentNode; |
---|
4147 | |
---|
4148 | if(focusNode.parentNode.childNodes.length>1 |
---|
4149 | || ancestor.tagName.toLowerCase() == WYMeditor.OL |
---|
4150 | || ancestor.tagName.toLowerCase() == WYMeditor.UL) |
---|
4151 | this._doc.execCommand(cmd,'',null); |
---|
4152 | } |
---|
4153 | |
---|
4154 | break; |
---|
4155 | |
---|
4156 | default: |
---|
4157 | |
---|
4158 | if(param) this._doc.execCommand(cmd,'',param); |
---|
4159 | else this._doc.execCommand(cmd,'',null); |
---|
4160 | } |
---|
4161 | |
---|
4162 | //set to P if parent = BODY |
---|
4163 | var container = this.selected(); |
---|
4164 | if(container.tagName.toLowerCase() == WYMeditor.BODY) |
---|
4165 | this._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); |
---|
4166 | |
---|
4167 | //add event handlers on doc elements |
---|
4168 | |
---|
4169 | this.listen(); |
---|
4170 | }; |
---|
4171 | |
---|
4172 | /* @name selected |
---|
4173 | * @description Returns the selected container |
---|
4174 | */ |
---|
4175 | WYMeditor.WymClassMozilla.prototype.selected = function() { |
---|
4176 | |
---|
4177 | var sel = this._iframe.contentWindow.getSelection(); |
---|
4178 | var node = sel.focusNode; |
---|
4179 | if(node) { |
---|
4180 | if(node.nodeName == "#text") return(node.parentNode); |
---|
4181 | else return(node); |
---|
4182 | } else return(null); |
---|
4183 | }; |
---|
4184 | |
---|
4185 | WYMeditor.WymClassMozilla.prototype.addCssRule = function(styles, oCss) { |
---|
4186 | |
---|
4187 | styles.insertRule(oCss.name + " {" + oCss.css + "}", |
---|
4188 | styles.cssRules.length); |
---|
4189 | }; |
---|
4190 | |
---|
4191 | |
---|
4192 | //keydown handler, mainly used for keyboard shortcuts |
---|
4193 | WYMeditor.WymClassMozilla.prototype.keydown = function(evt) { |
---|
4194 | |
---|
4195 | //'this' is the doc |
---|
4196 | var wym = WYMeditor.INSTANCES[this.title]; |
---|
4197 | var container = null; |
---|
4198 | |
---|
4199 | if(evt.ctrlKey){ |
---|
4200 | if(evt.keyCode == 66){ |
---|
4201 | //CTRL+b => STRONG |
---|
4202 | wym._exec(WYMeditor.BOLD); |
---|
4203 | return false; |
---|
4204 | } |
---|
4205 | if(evt.keyCode == 73){ |
---|
4206 | //CTRL+i => EMPHASIS |
---|
4207 | wym._exec(WYMeditor.ITALIC); |
---|
4208 | return false; |
---|
4209 | } |
---|
4210 | } |
---|
4211 | |
---|
4212 | else if(evt.keyCode == 13) { |
---|
4213 | if(!evt.shiftKey){ |
---|
4214 | //fix PRE bug #73 |
---|
4215 | container = wym.selected(); |
---|
4216 | if(container && container.tagName.toLowerCase() == WYMeditor.PRE) { |
---|
4217 | evt.preventDefault(); |
---|
4218 | wym.insert('<p></p>'); |
---|
4219 | } |
---|
4220 | } |
---|
4221 | } |
---|
4222 | }; |
---|
4223 | |
---|
4224 | //keyup handler, mainly used for cleanups |
---|
4225 | WYMeditor.WymClassMozilla.prototype.keyup = function(evt) { |
---|
4226 | |
---|
4227 | //'this' is the doc |
---|
4228 | var wym = WYMeditor.INSTANCES[this.title]; |
---|
4229 | |
---|
4230 | wym._selected_image = null; |
---|
4231 | var container = null; |
---|
4232 | |
---|
4233 | if(evt.keyCode == 13 && !evt.shiftKey) { |
---|
4234 | |
---|
4235 | //RETURN key |
---|
4236 | //cleanup <br><br> between paragraphs |
---|
4237 | jQuery(wym._doc.body).children(WYMeditor.BR).remove(); |
---|
4238 | } |
---|
4239 | |
---|
4240 | else if(evt.keyCode != 8 |
---|
4241 | && evt.keyCode != 17 |
---|
4242 | && evt.keyCode != 46 |
---|
4243 | && evt.keyCode != 224 |
---|
4244 | && !evt.metaKey |
---|
4245 | && !evt.ctrlKey) { |
---|
4246 | |
---|
4247 | //NOT BACKSPACE, NOT DELETE, NOT CTRL, NOT COMMAND |
---|
4248 | //text nodes replaced by P |
---|
4249 | |
---|
4250 | container = wym.selected(); |
---|
4251 | var name = container.tagName.toLowerCase(); |
---|
4252 | |
---|
4253 | //fix forbidden main containers |
---|
4254 | if( |
---|
4255 | name == "strong" || |
---|
4256 | name == "b" || |
---|
4257 | name == "em" || |
---|
4258 | name == "i" || |
---|
4259 | name == "sub" || |
---|
4260 | name == "sup" || |
---|
4261 | name == "a" |
---|
4262 | |
---|
4263 | ) name = container.parentNode.tagName.toLowerCase(); |
---|
4264 | |
---|
4265 | if(name == WYMeditor.BODY) wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); |
---|
4266 | } |
---|
4267 | }; |
---|
4268 | |
---|
4269 | WYMeditor.WymClassMozilla.prototype.enableDesignMode = function() { |
---|
4270 | if(this.designMode == "off") { |
---|
4271 | try { |
---|
4272 | this.designMode = "on"; |
---|
4273 | this.execCommand("styleWithCSS", '', false); |
---|
4274 | } catch(e) { } |
---|
4275 | } |
---|
4276 | }; |
---|
4277 | |
---|
4278 | WYMeditor.WymClassMozilla.prototype.setFocusToNode = function(node) { |
---|
4279 | var range = document.createRange(); |
---|
4280 | range.selectNode(node); |
---|
4281 | var selected = this._iframe.contentWindow.getSelection(); |
---|
4282 | selected.addRange(range); |
---|
4283 | selected.collapse(node, node.childNodes.length); |
---|
4284 | this._iframe.contentWindow.focus(); |
---|
4285 | }; |
---|
4286 | |
---|
4287 | WYMeditor.WymClassMozilla.prototype.openBlockTag = function(tag, attributes) |
---|
4288 | { |
---|
4289 | var attributes = this.validator.getValidTagAttributes(tag, attributes); |
---|
4290 | |
---|
4291 | // Handle Mozilla styled spans |
---|
4292 | if(tag == 'span' && attributes.style){ |
---|
4293 | var new_tag = this.getTagForStyle(attributes.style); |
---|
4294 | if(new_tag){ |
---|
4295 | this._tag_stack.pop(); |
---|
4296 | var tag = new_tag; |
---|
4297 | this._tag_stack.push(new_tag); |
---|
4298 | attributes.style = ''; |
---|
4299 | }else{ |
---|
4300 | return; |
---|
4301 | } |
---|
4302 | } |
---|
4303 | |
---|
4304 | this.output += this.helper.tag(tag, attributes, true); |
---|
4305 | }; |
---|
4306 | |
---|
4307 | WYMeditor.WymClassMozilla.prototype.getTagForStyle = function(style) { |
---|
4308 | |
---|
4309 | if(/bold/.test(style)) return 'strong'; |
---|
4310 | if(/italic/.test(style)) return 'em'; |
---|
4311 | if(/sub/.test(style)) return 'sub'; |
---|
4312 | if(/sub/.test(style)) return 'super'; |
---|
4313 | return false; |
---|
4314 | }; |
---|
4315 | |
---|
4316 | /* |
---|
4317 | * WYMeditor : what you see is What You Mean web-based editor |
---|
4318 | * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/ |
---|
4319 | * Dual licensed under the MIT (MIT-license.txt) |
---|
4320 | * and GPL (GPL-license.txt) licenses. |
---|
4321 | * |
---|
4322 | * For further information visit: |
---|
4323 | * http://www.wymeditor.org/ |
---|
4324 | * |
---|
4325 | * File Name: |
---|
4326 | * jquery.wymeditor.opera.js |
---|
4327 | * Opera specific class and functions. |
---|
4328 | * See the documentation for more info. |
---|
4329 | * |
---|
4330 | * File Authors: |
---|
4331 | * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg) |
---|
4332 | */ |
---|
4333 | |
---|
4334 | WYMeditor.WymClassOpera = function(wym) { |
---|
4335 | |
---|
4336 | this._wym = wym; |
---|
4337 | this._class = "class"; |
---|
4338 | this._newLine = "\r\n"; |
---|
4339 | }; |
---|
4340 | |
---|
4341 | WYMeditor.WymClassOpera.prototype.initIframe = function(iframe) { |
---|
4342 | |
---|
4343 | this._iframe = iframe; |
---|
4344 | this._doc = iframe.contentWindow.document; |
---|
4345 | |
---|
4346 | //add css rules from options |
---|
4347 | var styles = this._doc.styleSheets[0]; |
---|
4348 | var aCss = eval(this._options.editorStyles); |
---|
4349 | |
---|
4350 | this.addCssRules(this._doc, aCss); |
---|
4351 | |
---|
4352 | this._doc.title = this._wym._index; |
---|
4353 | |
---|
4354 | //set the text direction |
---|
4355 | jQuery('html', this._doc).attr('dir', this._options.direction); |
---|
4356 | |
---|
4357 | //init designMode |
---|
4358 | this._doc.designMode = "on"; |
---|
4359 | |
---|
4360 | //init html value |
---|
4361 | this.html(this._wym._html); |
---|
4362 | |
---|
4363 | //pre-bind functions |
---|
4364 | if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this); |
---|
4365 | |
---|
4366 | //bind external events |
---|
4367 | this._wym.bindEvents(); |
---|
4368 | |
---|
4369 | //bind editor keydown events |
---|
4370 | jQuery(this._doc).bind("keydown", this.keydown); |
---|
4371 | |
---|
4372 | //bind editor events |
---|
4373 | jQuery(this._doc).bind("keyup", this.keyup); |
---|
4374 | |
---|
4375 | //post-init functions |
---|
4376 | if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this); |
---|
4377 | |
---|
4378 | //add event listeners to doc elements, e.g. images |
---|
4379 | this.listen(); |
---|
4380 | }; |
---|
4381 | |
---|
4382 | WYMeditor.WymClassOpera.prototype._exec = function(cmd,param) { |
---|
4383 | |
---|
4384 | if(param) this._doc.execCommand(cmd,false,param); |
---|
4385 | else this._doc.execCommand(cmd); |
---|
4386 | |
---|
4387 | this.listen(); |
---|
4388 | }; |
---|
4389 | |
---|
4390 | WYMeditor.WymClassOpera.prototype.selected = function() { |
---|
4391 | |
---|
4392 | var sel=this._iframe.contentWindow.getSelection(); |
---|
4393 | var node=sel.focusNode; |
---|
4394 | if(node) { |
---|
4395 | if(node.nodeName=="#text")return(node.parentNode); |
---|
4396 | else return(node); |
---|
4397 | } else return(null); |
---|
4398 | }; |
---|
4399 | |
---|
4400 | WYMeditor.WymClassOpera.prototype.addCssRule = function(styles, oCss) { |
---|
4401 | |
---|
4402 | styles.insertRule(oCss.name + " {" + oCss.css + "}", |
---|
4403 | styles.cssRules.length); |
---|
4404 | }; |
---|
4405 | |
---|
4406 | //keydown handler |
---|
4407 | WYMeditor.WymClassOpera.prototype.keydown = function(evt) { |
---|
4408 | |
---|
4409 | //'this' is the doc |
---|
4410 | var wym = WYMeditor.INSTANCES[this.title]; |
---|
4411 | var sel = wym._iframe.contentWindow.getSelection(); |
---|
4412 | startNode = sel.getRangeAt(0).startContainer; |
---|
4413 | |
---|
4414 | //Get a P instead of no container |
---|
4415 | if(!jQuery(startNode).parentsOrSelf( |
---|
4416 | WYMeditor.MAIN_CONTAINERS.join(","))[0] |
---|
4417 | && !jQuery(startNode).parentsOrSelf('li') |
---|
4418 | && evt.keyCode != WYMeditor.KEY.ENTER |
---|
4419 | && evt.keyCode != WYMeditor.KEY.LEFT |
---|
4420 | && evt.keyCode != WYMeditor.KEY.UP |
---|
4421 | && evt.keyCode != WYMeditor.KEY.RIGHT |
---|
4422 | && evt.keyCode != WYMeditor.KEY.DOWN |
---|
4423 | && evt.keyCode != WYMeditor.KEY.BACKSPACE |
---|
4424 | && evt.keyCode != WYMeditor.KEY.DELETE) |
---|
4425 | wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); |
---|
4426 | |
---|
4427 | }; |
---|
4428 | |
---|
4429 | //keyup handler |
---|
4430 | WYMeditor.WymClassOpera.prototype.keyup = function(evt) { |
---|
4431 | |
---|
4432 | //'this' is the doc |
---|
4433 | var wym = WYMeditor.INSTANCES[this.title]; |
---|
4434 | wym._selected_image = null; |
---|
4435 | }; |
---|
4436 | |
---|
4437 | // TODO: implement me |
---|
4438 | WYMeditor.WymClassOpera.prototype.setFocusToNode = function(node) { |
---|
4439 | |
---|
4440 | }; |
---|
4441 | |
---|
4442 | /* |
---|
4443 | * WYMeditor : what you see is What You Mean web-based editor |
---|
4444 | * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/ |
---|
4445 | * Dual licensed under the MIT (MIT-license.txt) |
---|
4446 | * and GPL (GPL-license.txt) licenses. |
---|
4447 | * |
---|
4448 | * For further information visit: |
---|
4449 | * http://www.wymeditor.org/ |
---|
4450 | * |
---|
4451 | * File Name: |
---|
4452 | * jquery.wymeditor.safari.js |
---|
4453 | * Safari specific class and functions. |
---|
4454 | * See the documentation for more info. |
---|
4455 | * |
---|
4456 | * File Authors: |
---|
4457 | * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg) |
---|
4458 | * Scott Lewis (lewiscot a-t gmail dotcom) |
---|
4459 | */ |
---|
4460 | |
---|
4461 | WYMeditor.WymClassSafari = function(wym) { |
---|
4462 | |
---|
4463 | this._wym = wym; |
---|
4464 | this._class = "class"; |
---|
4465 | this._newLine = "\n"; |
---|
4466 | }; |
---|
4467 | |
---|
4468 | WYMeditor.WymClassSafari.prototype.initIframe = function(iframe) { |
---|
4469 | |
---|
4470 | this._iframe = iframe; |
---|
4471 | this._doc = iframe.contentDocument; |
---|
4472 | |
---|
4473 | //add css rules from options |
---|
4474 | |
---|
4475 | var styles = this._doc.styleSheets[0]; |
---|
4476 | var aCss = eval(this._options.editorStyles); |
---|
4477 | |
---|
4478 | this.addCssRules(this._doc, aCss); |
---|
4479 | |
---|
4480 | this._doc.title = this._wym._index; |
---|
4481 | |
---|
4482 | //set the text direction |
---|
4483 | jQuery('html', this._doc).attr('dir', this._options.direction); |
---|
4484 | |
---|
4485 | //init designMode |
---|
4486 | this._doc.designMode = "on"; |
---|
4487 | |
---|
4488 | //init html value |
---|
4489 | this.html(this._wym._html); |
---|
4490 | |
---|
4491 | //pre-bind functions |
---|
4492 | if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this); |
---|
4493 | |
---|
4494 | //bind external events |
---|
4495 | this._wym.bindEvents(); |
---|
4496 | |
---|
4497 | //bind editor keydown events |
---|
4498 | jQuery(this._doc).bind("keydown", this.keydown); |
---|
4499 | |
---|
4500 | //bind editor keyup events |
---|
4501 | jQuery(this._doc).bind("keyup", this.keyup); |
---|
4502 | |
---|
4503 | //post-init functions |
---|
4504 | if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this); |
---|
4505 | |
---|
4506 | //add event listeners to doc elements, e.g. images |
---|
4507 | this.listen(); |
---|
4508 | }; |
---|
4509 | |
---|
4510 | WYMeditor.WymClassSafari.prototype._exec = function(cmd,param) { |
---|
4511 | |
---|
4512 | if(!this.selected()) return(false); |
---|
4513 | |
---|
4514 | switch(cmd) { |
---|
4515 | |
---|
4516 | case WYMeditor.INDENT: case WYMeditor.OUTDENT: |
---|
4517 | |
---|
4518 | var focusNode = this.selected(); |
---|
4519 | var sel = this._iframe.contentWindow.getSelection(); |
---|
4520 | var anchorNode = sel.anchorNode; |
---|
4521 | if(anchorNode.nodeName == "#text") anchorNode = anchorNode.parentNode; |
---|
4522 | |
---|
4523 | focusNode = this.findUp(focusNode, WYMeditor.BLOCKS); |
---|
4524 | anchorNode = this.findUp(anchorNode, WYMeditor.BLOCKS); |
---|
4525 | |
---|
4526 | if(focusNode && focusNode == anchorNode |
---|
4527 | && focusNode.tagName.toLowerCase() == WYMeditor.LI) { |
---|
4528 | |
---|
4529 | var ancestor = focusNode.parentNode.parentNode; |
---|
4530 | |
---|
4531 | if(focusNode.parentNode.childNodes.length>1 |
---|
4532 | || ancestor.tagName.toLowerCase() == WYMeditor.OL |
---|
4533 | || ancestor.tagName.toLowerCase() == WYMeditor.UL) |
---|
4534 | this._doc.execCommand(cmd,'',null); |
---|
4535 | } |
---|
4536 | |
---|
4537 | break; |
---|
4538 | |
---|
4539 | case WYMeditor.INSERT_ORDEREDLIST: case WYMeditor.INSERT_UNORDEREDLIST: |
---|
4540 | |
---|
4541 | this._doc.execCommand(cmd,'',null); |
---|
4542 | |
---|
4543 | //Safari creates lists in e.g. paragraphs. |
---|
4544 | //Find the container, and remove it. |
---|
4545 | var focusNode = this.selected(); |
---|
4546 | var container = this.findUp(focusNode, WYMeditor.MAIN_CONTAINERS); |
---|
4547 | if(container) jQuery(container).replaceWith(jQuery(container).html()); |
---|
4548 | |
---|
4549 | break; |
---|
4550 | |
---|
4551 | default: |
---|
4552 | |
---|
4553 | if(param) this._doc.execCommand(cmd,'',param); |
---|
4554 | else this._doc.execCommand(cmd,'',null); |
---|
4555 | } |
---|
4556 | |
---|
4557 | //set to P if parent = BODY |
---|
4558 | var container = this.selected(); |
---|
4559 | if(container && container.tagName.toLowerCase() == WYMeditor.BODY) |
---|
4560 | this._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); |
---|
4561 | |
---|
4562 | //add event handlers on doc elements |
---|
4563 | this.listen(); |
---|
4564 | }; |
---|
4565 | |
---|
4566 | /* @name selected |
---|
4567 | * @description Returns the selected container |
---|
4568 | */ |
---|
4569 | WYMeditor.WymClassSafari.prototype.selected = function() { |
---|
4570 | |
---|
4571 | var sel = this._iframe.contentWindow.getSelection(); |
---|
4572 | var node = sel.focusNode; |
---|
4573 | if(node) { |
---|
4574 | if(node.nodeName == "#text") return(node.parentNode); |
---|
4575 | else return(node); |
---|
4576 | } else return(null); |
---|
4577 | }; |
---|
4578 | |
---|
4579 | WYMeditor.WymClassSafari.prototype.addCssRule = function(styles, oCss) { |
---|
4580 | |
---|
4581 | styles.insertRule(oCss.name + " {" + oCss.css + "}", |
---|
4582 | styles.cssRules.length); |
---|
4583 | }; |
---|
4584 | |
---|
4585 | |
---|
4586 | //keydown handler, mainly used for keyboard shortcuts |
---|
4587 | WYMeditor.WymClassSafari.prototype.keydown = function(evt) { |
---|
4588 | |
---|
4589 | //'this' is the doc |
---|
4590 | var wym = WYMeditor.INSTANCES[this.title]; |
---|
4591 | |
---|
4592 | if(evt.ctrlKey){ |
---|
4593 | if(evt.keyCode == 66){ |
---|
4594 | //CTRL+b => STRONG |
---|
4595 | wym._exec(WYMeditor.BOLD); |
---|
4596 | return false; |
---|
4597 | } |
---|
4598 | if(evt.keyCode == 73){ |
---|
4599 | //CTRL+i => EMPHASIS |
---|
4600 | wym._exec(WYMeditor.ITALIC); |
---|
4601 | return false; |
---|
4602 | } |
---|
4603 | } |
---|
4604 | }; |
---|
4605 | |
---|
4606 | //keyup handler, mainly used for cleanups |
---|
4607 | WYMeditor.WymClassSafari.prototype.keyup = function(evt) { |
---|
4608 | |
---|
4609 | //'this' is the doc |
---|
4610 | var wym = WYMeditor.INSTANCES[this.title]; |
---|
4611 | |
---|
4612 | wym._selected_image = null; |
---|
4613 | var container = null; |
---|
4614 | |
---|
4615 | if(evt.keyCode == 13 && !evt.shiftKey) { |
---|
4616 | |
---|
4617 | //RETURN key |
---|
4618 | //cleanup <br><br> between paragraphs |
---|
4619 | jQuery(wym._doc.body).children(WYMeditor.BR).remove(); |
---|
4620 | |
---|
4621 | //fix PRE bug #73 |
---|
4622 | container = wym.selected(); |
---|
4623 | if(container && container.tagName.toLowerCase() == WYMeditor.PRE) |
---|
4624 | wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); //create P after PRE |
---|
4625 | } |
---|
4626 | |
---|
4627 | //fix #112 |
---|
4628 | if(evt.keyCode == 13 && evt.shiftKey) { |
---|
4629 | wym._exec('InsertLineBreak'); |
---|
4630 | } |
---|
4631 | |
---|
4632 | if(evt.keyCode != 8 |
---|
4633 | && evt.keyCode != 17 |
---|
4634 | && evt.keyCode != 46 |
---|
4635 | && evt.keyCode != 224 |
---|
4636 | && !evt.metaKey |
---|
4637 | && !evt.ctrlKey) { |
---|
4638 | |
---|
4639 | //NOT BACKSPACE, NOT DELETE, NOT CTRL, NOT COMMAND |
---|
4640 | //text nodes replaced by P |
---|
4641 | |
---|
4642 | container = wym.selected(); |
---|
4643 | var name = container.tagName.toLowerCase(); |
---|
4644 | |
---|
4645 | //fix forbidden main containers |
---|
4646 | if( |
---|
4647 | name == "strong" || |
---|
4648 | name == "b" || |
---|
4649 | name == "em" || |
---|
4650 | name == "i" || |
---|
4651 | name == "sub" || |
---|
4652 | name == "sup" || |
---|
4653 | name == "a" || |
---|
4654 | name == "span" //fix #110 |
---|
4655 | |
---|
4656 | ) name = container.parentNode.tagName.toLowerCase(); |
---|
4657 | |
---|
4658 | if(name == WYMeditor.BODY || name == WYMeditor.DIV) wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); //fix #110 for DIV |
---|
4659 | } |
---|
4660 | }; |
---|
4661 | |
---|
4662 | WYMeditor.WymClassSafari.prototype.setFocusToNode = function(node) { |
---|
4663 | var range = this._iframe.contentDocument.createRange(); |
---|
4664 | range.selectNode(node); |
---|
4665 | var selected = this._iframe.contentWindow.getSelection(); |
---|
4666 | selected.addRange(range); |
---|
4667 | selected.collapse(node, node.childNodes.length); |
---|
4668 | this._iframe.contentWindow.focus(); |
---|
4669 | }; |
---|
4670 | |
---|
4671 | WYMeditor.WymClassSafari.prototype.openBlockTag = function(tag, attributes) |
---|
4672 | { |
---|
4673 | var attributes = this.validator.getValidTagAttributes(tag, attributes); |
---|
4674 | |
---|
4675 | // Handle Safari styled spans |
---|
4676 | if(tag == 'span' && attributes.style) { |
---|
4677 | var new_tag = this.getTagForStyle(attributes.style); |
---|
4678 | if(new_tag){ |
---|
4679 | this._tag_stack.pop(); |
---|
4680 | var tag = new_tag; |
---|
4681 | this._tag_stack.push(new_tag); |
---|
4682 | attributes.style = ''; |
---|
4683 | |
---|
4684 | //should fix #125 - also removed the xhtml() override |
---|
4685 | if(typeof attributes['class'] == 'string') |
---|
4686 | attributes['class'] = attributes['class'].replace(/apple-style-span/gi, ''); |
---|
4687 | |
---|
4688 | } else { |
---|
4689 | return; |
---|
4690 | } |
---|
4691 | } |
---|
4692 | |
---|
4693 | this.output += this.helper.tag(tag, attributes, true); |
---|
4694 | }; |
---|
4695 | |
---|
4696 | WYMeditor.WymClassSafari.prototype.getTagForStyle = function(style) { |
---|
4697 | |
---|
4698 | if(/bold/.test(style)) return 'strong'; |
---|
4699 | if(/italic/.test(style)) return 'em'; |
---|
4700 | if(/sub/.test(style)) return 'sub'; |
---|
4701 | if(/super/.test(style)) return 'sup'; |
---|
4702 | return false; |
---|
4703 | }; |
---|