cable 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ };