i18n_locale_editor 0.0.1 → 0.0.2

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