cable 0.1.0

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