refinerycms-wymeditor 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. data/.gitignore +88 -0
  2. data/app/assets/images/wymeditor/skins/refinery/arrow_redo.png +0 -0
  3. data/app/assets/images/wymeditor/skins/refinery/arrow_undo.png +0 -0
  4. data/app/assets/images/wymeditor/skins/refinery/eye.png +0 -0
  5. data/app/assets/images/wymeditor/skins/refinery/html5/article.png +0 -0
  6. data/app/assets/images/wymeditor/skins/refinery/html5/aside.png +0 -0
  7. data/app/assets/images/wymeditor/skins/refinery/html5/blockquote.png +0 -0
  8. data/app/assets/images/wymeditor/skins/refinery/html5/command.png +0 -0
  9. data/app/assets/images/wymeditor/skins/refinery/html5/details.png +0 -0
  10. data/app/assets/images/wymeditor/skins/refinery/html5/figcaption.png +0 -0
  11. data/app/assets/images/wymeditor/skins/refinery/html5/figure.png +0 -0
  12. data/app/assets/images/wymeditor/skins/refinery/html5/footer.png +0 -0
  13. data/app/assets/images/wymeditor/skins/refinery/html5/h1.png +0 -0
  14. data/app/assets/images/wymeditor/skins/refinery/html5/h2.png +0 -0
  15. data/app/assets/images/wymeditor/skins/refinery/html5/h3.png +0 -0
  16. data/app/assets/images/wymeditor/skins/refinery/html5/h4.png +0 -0
  17. data/app/assets/images/wymeditor/skins/refinery/html5/h5.png +0 -0
  18. data/app/assets/images/wymeditor/skins/refinery/html5/h6.png +0 -0
  19. data/app/assets/images/wymeditor/skins/refinery/html5/header.png +0 -0
  20. data/app/assets/images/wymeditor/skins/refinery/html5/hgroup.png +0 -0
  21. data/app/assets/images/wymeditor/skins/refinery/html5/mark.png +0 -0
  22. data/app/assets/images/wymeditor/skins/refinery/html5/meter.png +0 -0
  23. data/app/assets/images/wymeditor/skins/refinery/html5/nav.png +0 -0
  24. data/app/assets/images/wymeditor/skins/refinery/html5/p.png +0 -0
  25. data/app/assets/images/wymeditor/skins/refinery/html5/pre.png +0 -0
  26. data/app/assets/images/wymeditor/skins/refinery/html5/progress.png +0 -0
  27. data/app/assets/images/wymeditor/skins/refinery/html5/readme.md +1 -0
  28. data/app/assets/images/wymeditor/skins/refinery/html5/rp.png +0 -0
  29. data/app/assets/images/wymeditor/skins/refinery/html5/rt.png +0 -0
  30. data/app/assets/images/wymeditor/skins/refinery/html5/ruby.png +0 -0
  31. data/app/assets/images/wymeditor/skins/refinery/html5/section.png +0 -0
  32. data/app/assets/images/wymeditor/skins/refinery/html5/summary.png +0 -0
  33. data/app/assets/images/wymeditor/skins/refinery/html5/time.png +0 -0
  34. data/app/assets/images/wymeditor/skins/refinery/iframe/lbl-blockquote.png +0 -0
  35. data/app/assets/images/wymeditor/skins/refinery/iframe/lbl-h1.png +0 -0
  36. data/app/assets/images/wymeditor/skins/refinery/iframe/lbl-h2.png +0 -0
  37. data/app/assets/images/wymeditor/skins/refinery/iframe/lbl-h3.png +0 -0
  38. data/app/assets/images/wymeditor/skins/refinery/iframe/lbl-h4.png +0 -0
  39. data/app/assets/images/wymeditor/skins/refinery/iframe/lbl-h5.png +0 -0
  40. data/app/assets/images/wymeditor/skins/refinery/iframe/lbl-h6.png +0 -0
  41. data/app/assets/images/wymeditor/skins/refinery/iframe/lbl-p.png +0 -0
  42. data/app/assets/images/wymeditor/skins/refinery/iframe/lbl-pre.png +0 -0
  43. data/app/assets/images/wymeditor/skins/refinery/link_add.png +0 -0
  44. data/app/assets/images/wymeditor/skins/refinery/link_break.png +0 -0
  45. data/app/assets/images/wymeditor/skins/refinery/page_code.png +0 -0
  46. data/app/assets/images/wymeditor/skins/refinery/page_paste.png +0 -0
  47. data/app/assets/images/wymeditor/skins/refinery/photo_add.png +0 -0
  48. data/app/assets/images/wymeditor/skins/refinery/right.png +0 -0
  49. data/app/assets/images/wymeditor/skins/refinery/style.png +0 -0
  50. data/app/assets/images/wymeditor/skins/refinery/table_add.png +0 -0
  51. data/app/assets/images/wymeditor/skins/refinery/text_align_center.png +0 -0
  52. data/app/assets/images/wymeditor/skins/refinery/text_align_justify.png +0 -0
  53. data/app/assets/images/wymeditor/skins/refinery/text_align_left.png +0 -0
  54. data/app/assets/images/wymeditor/skins/refinery/text_align_right.png +0 -0
  55. data/app/assets/images/wymeditor/skins/refinery/text_bold.png +0 -0
  56. data/app/assets/images/wymeditor/skins/refinery/text_heading_1.png +0 -0
  57. data/app/assets/images/wymeditor/skins/refinery/text_heading_2.png +0 -0
  58. data/app/assets/images/wymeditor/skins/refinery/text_heading_3.png +0 -0
  59. data/app/assets/images/wymeditor/skins/refinery/text_heading_4.png +0 -0
  60. data/app/assets/images/wymeditor/skins/refinery/text_heading_5.png +0 -0
  61. data/app/assets/images/wymeditor/skins/refinery/text_heading_6.png +0 -0
  62. data/app/assets/images/wymeditor/skins/refinery/text_indent.png +0 -0
  63. data/app/assets/images/wymeditor/skins/refinery/text_indent_remove.png +0 -0
  64. data/app/assets/images/wymeditor/skins/refinery/text_italic.png +0 -0
  65. data/app/assets/images/wymeditor/skins/refinery/text_list_bullets.png +0 -0
  66. data/app/assets/images/wymeditor/skins/refinery/text_list_numbers.png +0 -0
  67. data/app/assets/images/wymeditor/skins/refinery/text_paragraph.png +0 -0
  68. data/app/assets/images/wymeditor/skins/refinery/text_strikethrough.png +0 -0
  69. data/app/assets/images/wymeditor/skins/refinery/text_subscript.png +0 -0
  70. data/app/assets/images/wymeditor/skins/refinery/text_superscript.png +0 -0
  71. data/app/assets/images/wymeditor/skins/refinery/text_underline.png +0 -0
  72. data/app/assets/images/wymeditor/skins/wymeditor_icon.png +0 -0
  73. data/app/assets/javascripts/refinery/boot_wym.js.erb +289 -0
  74. data/app/assets/javascripts/refinery/wymeditor.js +37 -0
  75. data/app/assets/javascripts/wymeditor/browsers/ie.js.erb +224 -0
  76. data/app/assets/javascripts/wymeditor/browsers/mozilla.js.erb +293 -0
  77. data/app/assets/javascripts/wymeditor/browsers/opera.js.erb +123 -0
  78. data/app/assets/javascripts/wymeditor/browsers/webkit.js.erb +284 -0
  79. data/app/assets/javascripts/wymeditor/classes.js.erb +1577 -0
  80. data/app/assets/javascripts/wymeditor/functions.js.erb +218 -0
  81. data/app/assets/javascripts/wymeditor/lang/bg.js +57 -0
  82. data/app/assets/javascripts/wymeditor/lang/ca.js +45 -0
  83. data/app/assets/javascripts/wymeditor/lang/cs.js +58 -0
  84. data/app/assets/javascripts/wymeditor/lang/da.js +48 -0
  85. data/app/assets/javascripts/wymeditor/lang/de.js +45 -0
  86. data/app/assets/javascripts/wymeditor/lang/en.js +57 -0
  87. data/app/assets/javascripts/wymeditor/lang/es.js +58 -0
  88. data/app/assets/javascripts/wymeditor/lang/fa.js +46 -0
  89. data/app/assets/javascripts/wymeditor/lang/fi.js +57 -0
  90. data/app/assets/javascripts/wymeditor/lang/fr.js +57 -0
  91. data/app/assets/javascripts/wymeditor/lang/he.js +45 -0
  92. data/app/assets/javascripts/wymeditor/lang/hu.js +45 -0
  93. data/app/assets/javascripts/wymeditor/lang/it.js +48 -0
  94. data/app/assets/javascripts/wymeditor/lang/ja.js +47 -0
  95. data/app/assets/javascripts/wymeditor/lang/lv.js +47 -0
  96. data/app/assets/javascripts/wymeditor/lang/nb.js +48 -0
  97. data/app/assets/javascripts/wymeditor/lang/nl.js +47 -0
  98. data/app/assets/javascripts/wymeditor/lang/nn.js +45 -0
  99. data/app/assets/javascripts/wymeditor/lang/pl.js +45 -0
  100. data/app/assets/javascripts/wymeditor/lang/pt-BR.js +58 -0
  101. data/app/assets/javascripts/wymeditor/lang/pt.js +45 -0
  102. data/app/assets/javascripts/wymeditor/lang/rs.js +47 -0
  103. data/app/assets/javascripts/wymeditor/lang/ru.js +56 -0
  104. data/app/assets/javascripts/wymeditor/lang/sk.js +56 -0
  105. data/app/assets/javascripts/wymeditor/lang/sl.js +47 -0
  106. data/app/assets/javascripts/wymeditor/lang/sv.js +45 -0
  107. data/app/assets/javascripts/wymeditor/lang/tr.js +45 -0
  108. data/app/assets/javascripts/wymeditor/lang/vi.js +47 -0
  109. data/app/assets/javascripts/wymeditor/lang/zh_cn.js +47 -0
  110. data/app/assets/javascripts/wymeditor/prototypes.js.erb +1106 -0
  111. data/app/assets/javascripts/wymeditor/setup.js.erb +622 -0
  112. data/app/assets/javascripts/wymeditor/skins/refinery/skin.js +48 -0
  113. data/app/assets/javascripts/wymeditor/validators.js.erb +776 -0
  114. data/app/assets/stylesheets/theme.css.scss +36 -0
  115. data/app/assets/stylesheets/wymeditor/skins/refinery/skin.css.scss +530 -0
  116. data/app/assets/stylesheets/wymeditor/skins/refinery/wymiframe.css.scss +156 -0
  117. data/app/assets/stylesheets/wymeditor.css.scss +0 -0
  118. data/app/controllers/refinery/wymeditor_controller.rb +9 -0
  119. data/app/views/refinery/wymiframe.html.erb +14 -0
  120. data/config/initializers/refinery/wymeditor.rb.erb +4 -0
  121. data/lib/refinery/wymeditor/configuration.rb +10 -0
  122. data/lib/refinery/wymeditor/engine.rb +30 -0
  123. data/lib/refinery/wymeditor.rb +12 -0
  124. data/public/wymeditor/GPL-license.txt +278 -0
  125. data/public/wymeditor/MIT-license.txt +20 -0
  126. data/public/wymeditor/README +35 -0
  127. data/readme.md +96 -0
  128. data/refinerycms-wymeditor.gemspec +17 -0
  129. metadata +173 -0
@@ -0,0 +1,1577 @@
1
+ /********** XHTML LEXER/PARSER **********/
2
+
3
+ /*
4
+ * @name xml
5
+ * @description Use these methods to generate XML and XHTML compliant tags and
6
+ * escape tag attributes correctly
7
+ * @author Bermi Ferrer - http://bermi.org
8
+ * @author David Heinemeier Hansson http://loudthinking.com
9
+ */
10
+ WYMeditor.XmlHelper = function()
11
+ {
12
+ this._entitiesDiv = document.createElement('div');
13
+ return this;
14
+ };
15
+
16
+
17
+ /*
18
+ * @name tag
19
+ * @description
20
+ * Returns an empty HTML tag of type *name* which by default is XHTML
21
+ * compliant. Setting *open* to true will create an open tag compatible
22
+ * with HTML 4.0 and below. Add HTML attributes by passing an attributes
23
+ * array to *options*. For attributes with no value like (disabled and
24
+ * readonly), give it a value of true in the *options* array.
25
+ *
26
+ * Examples:
27
+ *
28
+ * this.tag('br')
29
+ * # => <br />
30
+ * this.tag ('br', false, true)
31
+ * # => <br>
32
+ * this.tag ('input', $({type:'text',disabled:true }) )
33
+ * # => <input type="text" disabled="disabled" />
34
+ */
35
+ WYMeditor.XmlHelper.prototype.tag = function(name, options, open)
36
+ {
37
+ options = options || false;
38
+ open = open || false;
39
+ return '<'+name+(options ? this.tagOptions(options) : '')+(open ? '>' : ' />');
40
+ };
41
+
42
+ /*
43
+ * @name contentTag
44
+ * @description
45
+ * Returns a XML block tag of type *name* surrounding the *content*. Add
46
+ * XML attributes by passing an attributes array to *options*. For attributes
47
+ * with no value like (disabled and readonly), give it a value of true in
48
+ * the *options* array. You can use symbols or strings for the attribute names.
49
+ *
50
+ * this.contentTag ('p', 'Hello world!' )
51
+ * # => <p>Hello world!</p>
52
+ * this.contentTag('div', this.contentTag('p', "Hello world!"), $({class : "strong"}))
53
+ * # => <div class="strong"><p>Hello world!</p></div>
54
+ * this.contentTag("select", options, $({multiple : true}))
55
+ * # => <select multiple="multiple">...options...</select>
56
+ */
57
+ WYMeditor.XmlHelper.prototype.contentTag = function(name, content, options)
58
+ {
59
+ options = options || false;
60
+ return '<'+name+(options ? this.tagOptions(options) : '')+'>'+content+'</'+name+'>';
61
+ };
62
+
63
+ /*
64
+ * @name cdataSection
65
+ * @description
66
+ * Returns a CDATA section for the given +content+. CDATA sections
67
+ * are used to escape blocks of text containing characters which would
68
+ * otherwise be recognized as markup. CDATA sections begin with the string
69
+ * <tt>&lt;![CDATA[</tt> and } with (and may not contain) the string
70
+ * <tt>]]></tt>.
71
+ */
72
+ WYMeditor.XmlHelper.prototype.cdataSection = function(content)
73
+ {
74
+ return '<![CDATA['+content+']]>';
75
+ };
76
+
77
+
78
+ /*
79
+ * @name escapeOnce
80
+ * @description
81
+ * Returns the escaped +xml+ without affecting existing escaped entities.
82
+ *
83
+ * this.escapeOnce( "1 > 2 &amp; 3")
84
+ * # => "1 &gt; 2 &amp; 3"
85
+ */
86
+ WYMeditor.XmlHelper.prototype.escapeOnce = function(xml)
87
+ {
88
+ return this._fixDoubleEscape(this.escapeEntities(xml));
89
+ };
90
+
91
+ /*
92
+ * @name _fixDoubleEscape
93
+ * @description
94
+ * Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc.
95
+ */
96
+ WYMeditor.XmlHelper.prototype._fixDoubleEscape = function(escaped)
97
+ {
98
+ return escaped.replace(/&amp;([a-z]+|(#\d+));/ig, "&$1;");
99
+ };
100
+
101
+ /*
102
+ * @name tagOptions
103
+ * @description
104
+ * Takes an array like the one generated by Tag.parseAttributes
105
+ * [["src", "http://www.editam.com/?a=b&c=d&amp;f=g"], ["title", "Editam, <Simplified> CMS"]]
106
+ * or an object like {src:"http://www.editam.com/?a=b&c=d&amp;f=g", title:"Editam, <Simplified> CMS"}
107
+ * and returns a string properly escaped like
108
+ * ' src = "http://www.editam.com/?a=b&amp;c=d&amp;f=g" title = "Editam, &lt;Simplified&gt; CMS"'
109
+ * which is valid for strict XHTML
110
+ */
111
+ WYMeditor.XmlHelper.prototype.tagOptions = function(options)
112
+ {
113
+ var xml = this;
114
+ xml._formated_options = '';
115
+
116
+ for (var key in options) {
117
+ var formated_options = '';
118
+ var value = options[key];
119
+ if(typeof value != 'function' && value.length > 0) {
120
+
121
+ if(parseInt(key) == key && typeof value == 'object'){
122
+ key = value.shift();
123
+ value = value.pop();
124
+ }
125
+ if(key != '' && value != ''){
126
+ xml._formated_options += ' '+key+'="'+xml.escapeOnce(value)+'"';
127
+ }
128
+ }
129
+ }
130
+ return xml._formated_options;
131
+ };
132
+
133
+ /*
134
+ * @name escapeEntities
135
+ * @description
136
+ * Escapes XML/HTML entities <, >, & and ". If seccond parameter is set to false it
137
+ * will not escape ". If set to true it will also escape '
138
+ */
139
+ WYMeditor.XmlHelper.prototype.escapeEntities = function(string, escape_quotes)
140
+ {
141
+ this._entitiesDiv.innerHTML = string;
142
+ this._entitiesDiv.textContent = string;
143
+ var result = this._entitiesDiv.innerHTML;
144
+ if(typeof escape_quotes == 'undefined'){
145
+ if(escape_quotes != false) result = result.replace('"', '&quot;');
146
+ if(escape_quotes == true) result = result.replace('"', '&#039;');
147
+ }
148
+ return result;
149
+ };
150
+
151
+ /*
152
+ * Parses a string conatining tag attributes and values an returns an array formated like
153
+ * [["src", "http://www.editam.com"], ["title", "Editam, Simplified CMS"]]
154
+ */
155
+ WYMeditor.XmlHelper.prototype.parseAttributes = function(tag_attributes)
156
+ {
157
+ // Use a compounded regex to match single quoted, double quoted and unquoted attribute pairs
158
+ var result = [];
159
+ var matches = tag_attributes.split(/((=\s*")(")("))|((=\s*\')(\')(\'))|((=\s*[^>\s]*))/g);
160
+ if(matches.toString() != tag_attributes){
161
+ for (var k in matches) {
162
+ var v = matches[k];
163
+ if(typeof v != 'function' && v.length != 0){
164
+ var re = new RegExp('(\\w+)\\s*'+v);
165
+ if(match = tag_attributes.match(re) ){
166
+ var value = v.replace(/^[\s=]+/, "");
167
+ var delimiter = value.charAt(0);
168
+ delimiter = delimiter == '"' ? '"' : (delimiter=="'"?"'":'');
169
+ if(delimiter != ''){
170
+ value = delimiter == '"' ? value.replace(/^"|"+$/g, '') : value.replace(/^'|'+$/g, '');
171
+ }
172
+ tag_attributes = tag_attributes.replace(match[0],'');
173
+ result.push([match[1] , value]);
174
+ }
175
+ }
176
+ }
177
+ }
178
+ return result;
179
+ };
180
+
181
+ /**
182
+ * Compounded regular expression. Any of
183
+ * the contained patterns could match and
184
+ * when one does, it's label is returned.
185
+ *
186
+ * Constructor. Starts with no patterns.
187
+ * @param boolean case True for case sensitive, false
188
+ * for insensitive.
189
+ * @access public
190
+ * @author Marcus Baker (http://lastcraft.com)
191
+ * @author Bermi Ferrer (http://bermi.org)
192
+ */
193
+ WYMeditor.ParallelRegex = function(case_sensitive)
194
+ {
195
+ this._case = case_sensitive;
196
+ this._patterns = [];
197
+ this._labels = [];
198
+ this._regex = null;
199
+ return this;
200
+ };
201
+
202
+
203
+ /**
204
+ * Adds a pattern with an optional label.
205
+ * @param string pattern Perl style regex, but ( and )
206
+ * lose the usual meaning.
207
+ * @param string label Label of regex to be returned
208
+ * on a match.
209
+ * @access public
210
+ */
211
+ WYMeditor.ParallelRegex.prototype.addPattern = function(pattern, label)
212
+ {
213
+ label = label || true;
214
+ var count = this._patterns.length;
215
+ this._patterns[count] = pattern;
216
+ this._labels[count] = label;
217
+ this._regex = null;
218
+ };
219
+
220
+ /**
221
+ * Attempts to match all patterns at once against
222
+ * a string.
223
+ * @param string subject String to match against.
224
+ *
225
+ * @return boolean True on success.
226
+ * @return string match First matched portion of
227
+ * subject.
228
+ * @access public
229
+ */
230
+ WYMeditor.ParallelRegex.prototype.match = function(subject)
231
+ {
232
+ if (this._patterns.length == 0) {
233
+ return [false, ''];
234
+ }
235
+ var matches = subject.match(this._getCompoundedRegex());
236
+
237
+ if(!matches){
238
+ return [false, ''];
239
+ }
240
+ var match = matches[0];
241
+ for (var i = 1; i < matches.length; i++) {
242
+ if (matches[i]) {
243
+ return [this._labels[i-1], match];
244
+ }
245
+ }
246
+ return [true, matches[0]];
247
+ };
248
+
249
+ /**
250
+ * Compounds the patterns into a single
251
+ * regular expression separated with the
252
+ * "or" operator. Caches the regex.
253
+ * Will automatically escape (, ) and / tokens.
254
+ * @param array patterns List of patterns in order.
255
+ * @access private
256
+ */
257
+ WYMeditor.ParallelRegex.prototype._getCompoundedRegex = function()
258
+ {
259
+ if (this._regex == null) {
260
+ for (var i = 0, count = this._patterns.length; i < count; i++) {
261
+ this._patterns[i] = '(' + this._untokenizeRegex(this._tokenizeRegex(this._patterns[i]).replace(/([\/\(\)])/g,'\\$1')) + ')';
262
+ }
263
+ this._regex = new RegExp(this._patterns.join("|") ,this._getPerlMatchingFlags());
264
+ }
265
+ return this._regex;
266
+ };
267
+
268
+ /**
269
+ * Escape lookahead/lookbehind blocks
270
+ */
271
+ WYMeditor.ParallelRegex.prototype._tokenizeRegex = function(regex)
272
+ {
273
+ return regex.
274
+ replace(/\(\?(i|m|s|x|U)\)/, '~~~~~~Tk1\$1~~~~~~').
275
+ replace(/\(\?(\-[i|m|s|x|U])\)/, '~~~~~~Tk2\$1~~~~~~').
276
+ replace(/\(\?\=(.*)\)/, '~~~~~~Tk3\$1~~~~~~').
277
+ replace(/\(\?\!(.*)\)/, '~~~~~~Tk4\$1~~~~~~').
278
+ replace(/\(\?\<\=(.*)\)/, '~~~~~~Tk5\$1~~~~~~').
279
+ replace(/\(\?\<\!(.*)\)/, '~~~~~~Tk6\$1~~~~~~').
280
+ replace(/\(\?\:(.*)\)/, '~~~~~~Tk7\$1~~~~~~');
281
+ };
282
+
283
+ /**
284
+ * Unscape lookahead/lookbehind blocks
285
+ */
286
+ WYMeditor.ParallelRegex.prototype._untokenizeRegex = function(regex)
287
+ {
288
+ return regex.
289
+ replace(/~~~~~~Tk1(.{1})~~~~~~/, "(?\$1)").
290
+ replace(/~~~~~~Tk2(.{2})~~~~~~/, "(?\$1)").
291
+ replace(/~~~~~~Tk3(.*)~~~~~~/, "(?=\$1)").
292
+ replace(/~~~~~~Tk4(.*)~~~~~~/, "(?!\$1)").
293
+ replace(/~~~~~~Tk5(.*)~~~~~~/, "(?<=\$1)").
294
+ replace(/~~~~~~Tk6(.*)~~~~~~/, "(?<!\$1)").
295
+ replace(/~~~~~~Tk7(.*)~~~~~~/, "(?:\$1)");
296
+ };
297
+
298
+
299
+ /**
300
+ * Accessor for perl regex mode flags to use.
301
+ * @return string Perl regex flags.
302
+ * @access private
303
+ */
304
+ WYMeditor.ParallelRegex.prototype._getPerlMatchingFlags = function()
305
+ {
306
+ return (this._case ? "m" : "mi");
307
+ };
308
+
309
+
310
+
311
+ /**
312
+ * States for a stack machine.
313
+ *
314
+ * Constructor. Starts in named state.
315
+ * @param string start Starting state name.
316
+ * @access public
317
+ * @author Marcus Baker (http://lastcraft.com)
318
+ * @author Bermi Ferrer (http://bermi.org)
319
+ */
320
+ WYMeditor.StateStack = function(start)
321
+ {
322
+ this._stack = [start];
323
+ return this;
324
+ };
325
+
326
+ /**
327
+ * Accessor for current state.
328
+ * @return string State.
329
+ * @access public
330
+ */
331
+ WYMeditor.StateStack.prototype.getCurrent = function()
332
+ {
333
+ return this._stack[this._stack.length - 1];
334
+ };
335
+
336
+ /**
337
+ * Adds a state to the stack and sets it
338
+ * to be the current state.
339
+ * @param string state New state.
340
+ * @access public
341
+ */
342
+ WYMeditor.StateStack.prototype.enter = function(state)
343
+ {
344
+ this._stack.push(state);
345
+ };
346
+
347
+ /**
348
+ * Leaves the current state and reverts
349
+ * to the previous one.
350
+ * @return boolean False if we drop off
351
+ * the bottom of the list.
352
+ * @access public
353
+ */
354
+ WYMeditor.StateStack.prototype.leave = function()
355
+ {
356
+ if (this._stack.length == 1) {
357
+ return false;
358
+ }
359
+ this._stack.pop();
360
+ return true;
361
+ };
362
+
363
+
364
+ // GLOBALS
365
+ WYMeditor.LEXER_ENTER = 1;
366
+ WYMeditor.LEXER_MATCHED = 2;
367
+ WYMeditor.LEXER_UNMATCHED = 3;
368
+ WYMeditor.LEXER_EXIT = 4;
369
+ WYMeditor.LEXER_SPECIAL = 5;
370
+
371
+
372
+ /**
373
+ * Accepts text and breaks it into tokens.
374
+ * Some optimisation to make the sure the
375
+ * content is only scanned by the PHP regex
376
+ * parser once. Lexer modes must not start
377
+ * with leading underscores.
378
+ *
379
+ * Sets up the lexer in case insensitive matching
380
+ * by default.
381
+ * @param Parser parser Handling strategy by reference.
382
+ * @param string start Starting handler.
383
+ * @param boolean case True for case sensitive.
384
+ * @access public
385
+ * @author Marcus Baker (http://lastcraft.com)
386
+ * @author Bermi Ferrer (http://bermi.org)
387
+ */
388
+ WYMeditor.Lexer = function(parser, start, case_sensitive)
389
+ {
390
+ start = start || 'accept';
391
+ this._case = case_sensitive || false;
392
+ this._regexes = {};
393
+ this._parser = parser;
394
+ this._mode = new WYMeditor.StateStack(start);
395
+ this._mode_handlers = {};
396
+ this._mode_handlers[start] = start;
397
+ return this;
398
+ };
399
+
400
+ /**
401
+ * Adds a token search pattern for a particular
402
+ * parsing mode. The pattern does not change the
403
+ * current mode.
404
+ * @param string pattern Perl style regex, but ( and )
405
+ * lose the usual meaning.
406
+ * @param string mode Should only apply this
407
+ * pattern when dealing with
408
+ * this type of input.
409
+ * @access public
410
+ */
411
+ WYMeditor.Lexer.prototype.addPattern = function(pattern, mode)
412
+ {
413
+ var mode = mode || "accept";
414
+ if (typeof this._regexes[mode] == 'undefined') {
415
+ this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
416
+ }
417
+ this._regexes[mode].addPattern(pattern);
418
+ if (typeof this._mode_handlers[mode] == 'undefined') {
419
+ this._mode_handlers[mode] = mode;
420
+ }
421
+ };
422
+
423
+ /**
424
+ * Adds a pattern that will enter a new parsing
425
+ * mode. Useful for entering parenthesis, strings,
426
+ * tags, etc.
427
+ * @param string pattern Perl style regex, but ( and )
428
+ * lose the usual meaning.
429
+ * @param string mode Should only apply this
430
+ * pattern when dealing with
431
+ * this type of input.
432
+ * @param string new_mode Change parsing to this new
433
+ * nested mode.
434
+ * @access public
435
+ */
436
+ WYMeditor.Lexer.prototype.addEntryPattern = function(pattern, mode, new_mode)
437
+ {
438
+ if (typeof this._regexes[mode] == 'undefined') {
439
+ this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
440
+ }
441
+ this._regexes[mode].addPattern(pattern, new_mode);
442
+ if (typeof this._mode_handlers[new_mode] == 'undefined') {
443
+ this._mode_handlers[new_mode] = new_mode;
444
+ }
445
+ };
446
+
447
+ /**
448
+ * Adds a pattern that will exit the current mode
449
+ * and re-enter the previous one.
450
+ * @param string pattern Perl style regex, but ( and )
451
+ * lose the usual meaning.
452
+ * @param string mode Mode to leave.
453
+ * @access public
454
+ */
455
+ WYMeditor.Lexer.prototype.addExitPattern = function(pattern, mode)
456
+ {
457
+ if (typeof this._regexes[mode] == 'undefined') {
458
+ this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
459
+ }
460
+ this._regexes[mode].addPattern(pattern, "__exit");
461
+ if (typeof this._mode_handlers[mode] == 'undefined') {
462
+ this._mode_handlers[mode] = mode;
463
+ }
464
+ };
465
+
466
+ /**
467
+ * Adds a pattern that has a special mode. Acts as an entry
468
+ * and exit pattern in one go, effectively calling a special
469
+ * parser handler for this token only.
470
+ * @param string pattern Perl style regex, but ( and )
471
+ * lose the usual meaning.
472
+ * @param string mode Should only apply this
473
+ * pattern when dealing with
474
+ * this type of input.
475
+ * @param string special Use this mode for this one token.
476
+ * @access public
477
+ */
478
+ WYMeditor.Lexer.prototype.addSpecialPattern = function(pattern, mode, special)
479
+ {
480
+ if (typeof this._regexes[mode] == 'undefined') {
481
+ this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
482
+ }
483
+ this._regexes[mode].addPattern(pattern, '_'+special);
484
+ if (typeof this._mode_handlers[special] == 'undefined') {
485
+ this._mode_handlers[special] = special;
486
+ }
487
+ };
488
+
489
+ /**
490
+ * Adds a mapping from a mode to another handler.
491
+ * @param string mode Mode to be remapped.
492
+ * @param string handler New target handler.
493
+ * @access public
494
+ */
495
+ WYMeditor.Lexer.prototype.mapHandler = function(mode, handler)
496
+ {
497
+ this._mode_handlers[mode] = handler;
498
+ };
499
+
500
+ /**
501
+ * Splits the page text into tokens. Will fail
502
+ * if the handlers report an error or if no
503
+ * content is consumed. If successful then each
504
+ * unparsed and parsed token invokes a call to the
505
+ * held listener.
506
+ * @param string raw Raw HTML text.
507
+ * @return boolean True on success, else false.
508
+ * @access public
509
+ */
510
+ WYMeditor.Lexer.prototype.parse = function(raw)
511
+ {
512
+ if (typeof this._parser == 'undefined') {
513
+ return false;
514
+ }
515
+
516
+ var length = raw.length;
517
+ var parsed;
518
+ while (typeof (parsed = this._reduce(raw)) == 'object') {
519
+ var raw = parsed[0];
520
+ var unmatched = parsed[1];
521
+ var matched = parsed[2];
522
+ var mode = parsed[3];
523
+
524
+ if (! this._dispatchTokens(unmatched, matched, mode)) {
525
+ return false;
526
+ }
527
+
528
+ if (raw == '') {
529
+ return true;
530
+ }
531
+ if (raw.length == length) {
532
+ return false;
533
+ }
534
+ length = raw.length;
535
+ }
536
+ if (! parsed ) {
537
+ return false;
538
+ }
539
+
540
+ return this._invokeParser(raw, WYMeditor.LEXER_UNMATCHED);
541
+ };
542
+
543
+ /**
544
+ * Sends the matched token and any leading unmatched
545
+ * text to the parser changing the lexer to a new
546
+ * mode if one is listed.
547
+ * @param string unmatched Unmatched leading portion.
548
+ * @param string matched Actual token match.
549
+ * @param string mode Mode after match. A boolean
550
+ * false mode causes no change.
551
+ * @return boolean False if there was any error
552
+ * from the parser.
553
+ * @access private
554
+ */
555
+ WYMeditor.Lexer.prototype._dispatchTokens = function(unmatched, matched, mode)
556
+ {
557
+ mode = mode || false;
558
+
559
+ if (! this._invokeParser(unmatched, WYMeditor.LEXER_UNMATCHED)) {
560
+ return false;
561
+ }
562
+
563
+ if (typeof mode == 'boolean') {
564
+ return this._invokeParser(matched, WYMeditor.LEXER_MATCHED);
565
+ }
566
+ if (this._isModeEnd(mode)) {
567
+ if (! this._invokeParser(matched, WYMeditor.LEXER_EXIT)) {
568
+ return false;
569
+ }
570
+ return this._mode.leave();
571
+ }
572
+ if (this._isSpecialMode(mode)) {
573
+ this._mode.enter(this._decodeSpecial(mode));
574
+ if (! this._invokeParser(matched, WYMeditor.LEXER_SPECIAL)) {
575
+ return false;
576
+ }
577
+ return this._mode.leave();
578
+ }
579
+ this._mode.enter(mode);
580
+
581
+ return this._invokeParser(matched, WYMeditor.LEXER_ENTER);
582
+ };
583
+
584
+ /**
585
+ * Tests to see if the new mode is actually to leave
586
+ * the current mode and pop an item from the matching
587
+ * mode stack.
588
+ * @param string mode Mode to test.
589
+ * @return boolean True if this is the exit mode.
590
+ * @access private
591
+ */
592
+ WYMeditor.Lexer.prototype._isModeEnd = function(mode)
593
+ {
594
+ return (mode === "__exit");
595
+ };
596
+
597
+ /**
598
+ * Test to see if the mode is one where this mode
599
+ * is entered for this token only and automatically
600
+ * leaves immediately afterwoods.
601
+ * @param string mode Mode to test.
602
+ * @return boolean True if this is the exit mode.
603
+ * @access private
604
+ */
605
+ WYMeditor.Lexer.prototype._isSpecialMode = function(mode)
606
+ {
607
+ return (mode.substring(0,1) == "_");
608
+ };
609
+
610
+ /**
611
+ * Strips the magic underscore marking single token
612
+ * modes.
613
+ * @param string mode Mode to decode.
614
+ * @return string Underlying mode name.
615
+ * @access private
616
+ */
617
+ WYMeditor.Lexer.prototype._decodeSpecial = function(mode)
618
+ {
619
+ return mode.substring(1);
620
+ };
621
+
622
+ /**
623
+ * Calls the parser method named after the current
624
+ * mode. Empty content will be ignored. The lexer
625
+ * has a parser handler for each mode in the lexer.
626
+ * @param string content Text parsed.
627
+ * @param boolean is_match Token is recognised rather
628
+ * than unparsed data.
629
+ * @access private
630
+ */
631
+ WYMeditor.Lexer.prototype._invokeParser = function(content, is_match)
632
+ {
633
+
634
+ if (content === '') {
635
+ return true;
636
+ }
637
+ var current = this._mode.getCurrent();
638
+ var handler = this._mode_handlers[current];
639
+ var result;
640
+ eval('result = this._parser.' + handler + '(content, is_match);');
641
+ return result;
642
+ };
643
+
644
+ /**
645
+ * Tries to match a chunk of text and if successful
646
+ * removes the recognised chunk and any leading
647
+ * unparsed data. Empty strings will not be matched.
648
+ * @param string raw The subject to parse. This is the
649
+ * content that will be eaten.
650
+ * @return array/boolean Three item list of unparsed
651
+ * content followed by the
652
+ * recognised token and finally the
653
+ * action the parser is to take.
654
+ * True if no match, false if there
655
+ * is a parsing error.
656
+ * @access private
657
+ */
658
+ WYMeditor.Lexer.prototype._reduce = function(raw)
659
+ {
660
+ var matched = this._regexes[this._mode.getCurrent()].match(raw);
661
+ var match = matched[1];
662
+ var action = matched[0];
663
+ if (action) {
664
+ var unparsed_character_count = raw.indexOf(match);
665
+ var unparsed = raw.substr(0, unparsed_character_count);
666
+ raw = raw.substring(unparsed_character_count + match.length);
667
+ return [raw, unparsed, match, action];
668
+ }
669
+ return true;
670
+ };
671
+
672
+
673
+
674
+ /**
675
+ * This are the rules for breaking the XHTML code into events
676
+ * handled by the provided parser.
677
+ *
678
+ * @author Marcus Baker (http://lastcraft.com)
679
+ * @author Bermi Ferrer (http://bermi.org)
680
+ */
681
+ WYMeditor.XhtmlLexer = function(parser)
682
+ {
683
+ $.extend(this, new WYMeditor.Lexer(parser, 'Text'));
684
+
685
+ this.mapHandler('Text', 'Text');
686
+
687
+ this.addTokens();
688
+
689
+ this.init();
690
+
691
+ return this;
692
+ };
693
+
694
+
695
+ WYMeditor.XhtmlLexer.prototype.init = function()
696
+ {
697
+ };
698
+
699
+ WYMeditor.XhtmlLexer.prototype.addTokens = function()
700
+ {
701
+ this.addCommentTokens('Text');
702
+ this.addScriptTokens('Text');
703
+ this.addCssTokens('Text');
704
+ this.addTagTokens('Text');
705
+ };
706
+
707
+ WYMeditor.XhtmlLexer.prototype.addCommentTokens = function(scope)
708
+ {
709
+ this.addEntryPattern("<!--", scope, 'Comment');
710
+ this.addExitPattern("-->", 'Comment');
711
+ };
712
+
713
+ WYMeditor.XhtmlLexer.prototype.addScriptTokens = function(scope)
714
+ {
715
+ this.addEntryPattern("<script", scope, 'Script');
716
+ this.addExitPattern("</script>", 'Script');
717
+ };
718
+
719
+ WYMeditor.XhtmlLexer.prototype.addCssTokens = function(scope)
720
+ {
721
+ this.addEntryPattern("<style", scope, 'Css');
722
+ this.addExitPattern("</style>", 'Css');
723
+ };
724
+
725
+ WYMeditor.XhtmlLexer.prototype.addTagTokens = function(scope)
726
+ {
727
+ this.addSpecialPattern("<\\s*[a-z0-9:\-]+\\s*>", scope, 'OpeningTag');
728
+ this.addEntryPattern("<[a-z0-9:\-]+"+'[\\\/ \\\>]+', scope, 'OpeningTag');
729
+ this.addInTagDeclarationTokens('OpeningTag');
730
+
731
+ this.addSpecialPattern("</\\s*[a-z0-9:\-]+\\s*>", scope, 'ClosingTag');
732
+
733
+ };
734
+
735
+ WYMeditor.XhtmlLexer.prototype.addInTagDeclarationTokens = function(scope)
736
+ {
737
+ this.addSpecialPattern('\\s+', scope, 'Ignore');
738
+
739
+ this.addAttributeTokens(scope);
740
+
741
+ this.addExitPattern('/>', scope);
742
+ this.addExitPattern('>', scope);
743
+
744
+ };
745
+
746
+ WYMeditor.XhtmlLexer.prototype.addAttributeTokens = function(scope)
747
+ {
748
+ this.addSpecialPattern("\\s*[a-z-_0-9]*:?[a-z-_0-9]+\\s*(?=\=)\\s*", scope, 'TagAttributes');
749
+
750
+ this.addEntryPattern('=\\s*"', scope, 'DoubleQuotedAttribute');
751
+ this.addPattern("\\\\\"", 'DoubleQuotedAttribute');
752
+ this.addExitPattern('"', 'DoubleQuotedAttribute');
753
+
754
+ this.addEntryPattern("=\\s*'", scope, 'SingleQuotedAttribute');
755
+ this.addPattern("\\\\'", 'SingleQuotedAttribute');
756
+ this.addExitPattern("'", 'SingleQuotedAttribute');
757
+
758
+ this.addSpecialPattern('=\\s*[^>\\s]*', scope, 'UnquotedAttribute');
759
+ };
760
+
761
+
762
+
763
+ /**
764
+ * XHTML Parser.
765
+ *
766
+ * This XHTML parser will trigger the events available on on
767
+ * current SaxListener
768
+ *
769
+ * @author Bermi Ferrer (http://bermi.org)
770
+ */
771
+ WYMeditor.XhtmlParser = function(Listener, mode)
772
+ {
773
+ var mode = mode || 'Text';
774
+ this._Lexer = new WYMeditor.XhtmlLexer(this);
775
+ this._Listener = Listener;
776
+ this._mode = mode;
777
+ this._matches = [];
778
+ this._last_match = '';
779
+ this._current_match = '';
780
+
781
+ return this;
782
+ };
783
+
784
+ WYMeditor.XhtmlParser.prototype.parse = function(raw)
785
+ {
786
+ this._Lexer.parse(this.beforeParsing(raw));
787
+ return this.afterParsing(this._Listener.getResult());
788
+ };
789
+
790
+ WYMeditor.XhtmlParser.prototype.beforeParsing = function(raw)
791
+ {
792
+ if(raw.match(/class="MsoNormal"/) || raw.match(/ns = "urn:schemas-microsoft-com/)){
793
+ // Useful for cleaning up content pasted from other sources (MSWord)
794
+ this._Listener.avoidStylingTagsAndAttributes();
795
+ }
796
+ return this._Listener.beforeParsing(raw);
797
+ };
798
+
799
+ WYMeditor.XhtmlParser.prototype.afterParsing = function(parsed)
800
+ {
801
+ if(this._Listener._avoiding_tags_implicitly){
802
+ this._Listener.allowStylingTagsAndAttributes();
803
+ }
804
+ return this._Listener.afterParsing(parsed);
805
+ };
806
+
807
+
808
+ WYMeditor.XhtmlParser.prototype.Ignore = function(match, state)
809
+ {
810
+ return true;
811
+ };
812
+
813
+ WYMeditor.XhtmlParser.prototype.Text = function(text)
814
+ {
815
+ this._Listener.addContent(text);
816
+ return true;
817
+ };
818
+
819
+ WYMeditor.XhtmlParser.prototype.Comment = function(match, status)
820
+ {
821
+ return this._addNonTagBlock(match, status, 'addComment');
822
+ };
823
+
824
+ WYMeditor.XhtmlParser.prototype.Script = function(match, status)
825
+ {
826
+ return this._addNonTagBlock(match, status, 'addScript');
827
+ };
828
+
829
+ WYMeditor.XhtmlParser.prototype.Css = function(match, status)
830
+ {
831
+ return this._addNonTagBlock(match, status, 'addCss');
832
+ };
833
+
834
+ WYMeditor.XhtmlParser.prototype._addNonTagBlock = function(match, state, type)
835
+ {
836
+ switch (state){
837
+ case WYMeditor.LEXER_ENTER:
838
+ this._non_tag = match;
839
+ break;
840
+ case WYMeditor.LEXER_UNMATCHED:
841
+ this._non_tag += match;
842
+ break;
843
+ case WYMeditor.LEXER_EXIT:
844
+ switch(type) {
845
+ case 'addComment':
846
+ this._Listener.addComment(this._non_tag+match);
847
+ break;
848
+ case 'addScript':
849
+ this._Listener.addScript(this._non_tag+match);
850
+ break;
851
+ case 'addCss':
852
+ this._Listener.addCss(this._non_tag+match);
853
+ break;
854
+ }
855
+ }
856
+ return true;
857
+ };
858
+
859
+ WYMeditor.XhtmlParser.prototype.OpeningTag = function(match, state)
860
+ {
861
+ switch (state){
862
+ case WYMeditor.LEXER_ENTER:
863
+ this._tag = this.normalizeTag(match);
864
+ this._tag_attributes = {};
865
+ break;
866
+ case WYMeditor.LEXER_SPECIAL:
867
+ this._callOpenTagListener(this.normalizeTag(match));
868
+ break;
869
+ case WYMeditor.LEXER_EXIT:
870
+ this._callOpenTagListener(this._tag, this._tag_attributes);
871
+ }
872
+ return true;
873
+ };
874
+
875
+ WYMeditor.XhtmlParser.prototype.ClosingTag = function(match, state)
876
+ {
877
+ this._callCloseTagListener(this.normalizeTag(match));
878
+ return true;
879
+ };
880
+
881
+ WYMeditor.XhtmlParser.prototype._callOpenTagListener = function(tag, attributes)
882
+ {
883
+ var attributes = attributes || {};
884
+ this.autoCloseUnclosedBeforeNewOpening(tag);
885
+
886
+ if(this._Listener.isBlockTag(tag)){
887
+ this._Listener._tag_stack.push(tag);
888
+ this._Listener.fixNestingBeforeOpeningBlockTag(tag, attributes);
889
+ this._Listener.openBlockTag(tag, attributes);
890
+ this._increaseOpenTagCounter(tag);
891
+ }else if(this._Listener.isInlineTag(tag)){
892
+ this._Listener.inlineTag(tag, attributes);
893
+ }else{
894
+ this._Listener.openUnknownTag(tag, attributes);
895
+ this._increaseOpenTagCounter(tag);
896
+ }
897
+ this._Listener.last_tag = tag;
898
+ this._Listener.last_tag_opened = true;
899
+ this._Listener.last_tag_attributes = attributes;
900
+ };
901
+
902
+ WYMeditor.XhtmlParser.prototype._callCloseTagListener = function(tag)
903
+ {
904
+ if(this._decreaseOpenTagCounter(tag)){
905
+ this.autoCloseUnclosedBeforeTagClosing(tag);
906
+
907
+ if(this._Listener.isBlockTag(tag)){
908
+ var expected_tag = this._Listener._tag_stack.pop();
909
+ if(expected_tag == false){
910
+ return;
911
+ }else if(expected_tag != tag){
912
+ tag = expected_tag;
913
+ }
914
+ this._Listener.closeBlockTag(tag);
915
+ }else{
916
+ this._Listener.closeUnknownTag(tag);
917
+ }
918
+ }else{
919
+ this._Listener.closeUnopenedTag(tag);
920
+ }
921
+ this._Listener.last_tag = tag;
922
+ this._Listener.last_tag_opened = false;
923
+ };
924
+
925
+ WYMeditor.XhtmlParser.prototype._increaseOpenTagCounter = function(tag)
926
+ {
927
+ this._Listener._open_tags[tag] = this._Listener._open_tags[tag] || 0;
928
+ this._Listener._open_tags[tag]++;
929
+ };
930
+
931
+ WYMeditor.XhtmlParser.prototype._decreaseOpenTagCounter = function(tag)
932
+ {
933
+ if(this._Listener._open_tags[tag]){
934
+ this._Listener._open_tags[tag]--;
935
+ if(this._Listener._open_tags[tag] == 0){
936
+ this._Listener._open_tags[tag] = undefined;
937
+ }
938
+ return true;
939
+ }
940
+ return false;
941
+ };
942
+
943
+ WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeNewOpening = function(new_tag)
944
+ {
945
+ this._autoCloseUnclosed(new_tag, false);
946
+ };
947
+
948
+ WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeTagClosing = function(tag)
949
+ {
950
+ this._autoCloseUnclosed(tag, true);
951
+ };
952
+
953
+ WYMeditor.XhtmlParser.prototype._autoCloseUnclosed = function(new_tag, closing)
954
+ {
955
+ var closing = closing || false;
956
+ if(this._Listener._open_tags){
957
+ for (var tag in this._Listener._open_tags) {
958
+ var counter = this._Listener._open_tags[tag];
959
+ if(counter > 0 && this._Listener.shouldCloseTagAutomatically(tag, new_tag, closing)){
960
+ this._callCloseTagListener(tag, true);
961
+ }
962
+ }
963
+ }
964
+ };
965
+
966
+ WYMeditor.XhtmlParser.prototype.getTagReplacements = function()
967
+ {
968
+ return this._Listener.getTagReplacements();
969
+ };
970
+
971
+ WYMeditor.XhtmlParser.prototype.normalizeTag = function(tag)
972
+ {
973
+ tag = tag.replace(/^([\s<\/>]*)|([\s<\/>]*)$/gm,'').toLowerCase();
974
+ var tags = this._Listener.getTagReplacements();
975
+ if(tags[tag]){
976
+ return tags[tag];
977
+ }
978
+ return tag;
979
+ };
980
+
981
+ WYMeditor.XhtmlParser.prototype.TagAttributes = function(match, state)
982
+ {
983
+ if(WYMeditor.LEXER_SPECIAL == state){
984
+ this._current_attribute = match;
985
+ }
986
+ return true;
987
+ };
988
+
989
+ WYMeditor.XhtmlParser.prototype.DoubleQuotedAttribute = function(match, state)
990
+ {
991
+ if(WYMeditor.LEXER_UNMATCHED == state){
992
+ this._tag_attributes[this._current_attribute] = match;
993
+ }
994
+ return true;
995
+ };
996
+
997
+ WYMeditor.XhtmlParser.prototype.SingleQuotedAttribute = function(match, state)
998
+ {
999
+ if(WYMeditor.LEXER_UNMATCHED == state){
1000
+ this._tag_attributes[this._current_attribute] = match;
1001
+ }
1002
+ return true;
1003
+ };
1004
+
1005
+ WYMeditor.XhtmlParser.prototype.UnquotedAttribute = function(match, state)
1006
+ {
1007
+ this._tag_attributes[this._current_attribute] = match.replace(/^=/,'');
1008
+ return true;
1009
+ };
1010
+
1011
+
1012
+
1013
+ /**
1014
+ * XHTML Sax parser.
1015
+ *
1016
+ * @author Bermi Ferrer (http://bermi.org)
1017
+ */
1018
+ WYMeditor.XhtmlSaxListener = function()
1019
+ {
1020
+ this.output = '';
1021
+ this.helper = new WYMeditor.XmlHelper();
1022
+ this._open_tags = {};
1023
+ this.validator = WYMeditor.XhtmlValidator;
1024
+ this._tag_stack = [];
1025
+
1026
+ this.avoided_tags = ['area'];
1027
+
1028
+ this.entities = {
1029
+ '&nbsp;':'&#160;','&iexcl;':'&#161;','&cent;':'&#162;',
1030
+ '&pound;':'&#163;','&curren;':'&#164;','&yen;':'&#165;',
1031
+ '&brvbar;':'&#166;','&sect;':'&#167;','&uml;':'&#168;',
1032
+ '&copy;':'&#169;','&ordf;':'&#170;','&laquo;':'&#171;',
1033
+ '&not;':'&#172;','&shy;':'&#173;','&reg;':'&#174;',
1034
+ '&macr;':'&#175;','&deg;':'&#176;','&plusmn;':'&#177;',
1035
+ '&sup2;':'&#178;','&sup3;':'&#179;','&acute;':'&#180;',
1036
+ '&micro;':'&#181;','&para;':'&#182;','&middot;':'&#183;',
1037
+ '&cedil;':'&#184;','&sup1;':'&#185;','&ordm;':'&#186;',
1038
+ '&raquo;':'&#187;','&frac14;':'&#188;','&frac12;':'&#189;',
1039
+ '&frac34;':'&#190;','&iquest;':'&#191;','&Agrave;':'&#192;',
1040
+ '&Aacute;':'&#193;','&Acirc;':'&#194;','&Atilde;':'&#195;',
1041
+ '&Auml;':'&#196;','&Aring;':'&#197;','&AElig;':'&#198;',
1042
+ '&Ccedil;':'&#199;','&Egrave;':'&#200;','&Eacute;':'&#201;',
1043
+ '&Ecirc;':'&#202;','&Euml;':'&#203;','&Igrave;':'&#204;',
1044
+ '&Iacute;':'&#205;','&Icirc;':'&#206;','&Iuml;':'&#207;',
1045
+ '&ETH;':'&#208;','&Ntilde;':'&#209;','&Ograve;':'&#210;',
1046
+ '&Oacute;':'&#211;','&Ocirc;':'&#212;','&Otilde;':'&#213;',
1047
+ '&Ouml;':'&#214;','&times;':'&#215;','&Oslash;':'&#216;',
1048
+ '&Ugrave;':'&#217;','&Uacute;':'&#218;','&Ucirc;':'&#219;',
1049
+ '&Uuml;':'&#220;','&Yacute;':'&#221;','&THORN;':'&#222;',
1050
+ '&szlig;':'&#223;','&agrave;':'&#224;','&aacute;':'&#225;',
1051
+ '&acirc;':'&#226;','&atilde;':'&#227;','&auml;':'&#228;',
1052
+ '&aring;':'&#229;','&aelig;':'&#230;','&ccedil;':'&#231;',
1053
+ '&egrave;':'&#232;','&eacute;':'&#233;','&ecirc;':'&#234;',
1054
+ '&euml;':'&#235;','&igrave;':'&#236;','&iacute;':'&#237;',
1055
+ '&icirc;':'&#238;','&iuml;':'&#239;','&eth;':'&#240;',
1056
+ '&ntilde;':'&#241;','&ograve;':'&#242;','&oacute;':'&#243;',
1057
+ '&ocirc;':'&#244;','&otilde;':'&#245;','&ouml;':'&#246;',
1058
+ '&divide;':'&#247;','&oslash;':'&#248;','&ugrave;':'&#249;',
1059
+ '&uacute;':'&#250;','&ucirc;':'&#251;','&uuml;':'&#252;',
1060
+ '&yacute;':'&#253;','&thorn;':'&#254;','&yuml;':'&#255;',
1061
+ '&OElig;':'&#338;','&oelig;':'&#339;','&Scaron;':'&#352;',
1062
+ '&scaron;':'&#353;','&Yuml;':'&#376;','&fnof;':'&#402;',
1063
+ '&circ;':'&#710;','&tilde;':'&#732;','&Alpha;':'&#913;',
1064
+ '&Beta;':'&#914;','&Gamma;':'&#915;','&Delta;':'&#916;',
1065
+ '&Epsilon;':'&#917;','&Zeta;':'&#918;','&Eta;':'&#919;',
1066
+ '&Theta;':'&#920;','&Iota;':'&#921;','&Kappa;':'&#922;',
1067
+ '&Lambda;':'&#923;','&Mu;':'&#924;','&Nu;':'&#925;',
1068
+ '&Xi;':'&#926;','&Omicron;':'&#927;','&Pi;':'&#928;',
1069
+ '&Rho;':'&#929;','&Sigma;':'&#931;','&Tau;':'&#932;',
1070
+ '&Upsilon;':'&#933;','&Phi;':'&#934;','&Chi;':'&#935;',
1071
+ '&Psi;':'&#936;','&Omega;':'&#937;','&alpha;':'&#945;',
1072
+ '&beta;':'&#946;','&gamma;':'&#947;','&delta;':'&#948;',
1073
+ '&epsilon;':'&#949;','&zeta;':'&#950;','&eta;':'&#951;',
1074
+ '&theta;':'&#952;','&iota;':'&#953;','&kappa;':'&#954;',
1075
+ '&lambda;':'&#955;','&mu;':'&#956;','&nu;':'&#957;',
1076
+ '&xi;':'&#958;','&omicron;':'&#959;','&pi;':'&#960;',
1077
+ '&rho;':'&#961;','&sigmaf;':'&#962;','&sigma;':'&#963;',
1078
+ '&tau;':'&#964;','&upsilon;':'&#965;','&phi;':'&#966;',
1079
+ '&chi;':'&#967;','&psi;':'&#968;','&omega;':'&#969;',
1080
+ '&thetasym;':'&#977;','&upsih;':'&#978;','&piv;':'&#982;',
1081
+ '&ensp;':'&#8194;','&emsp;':'&#8195;','&thinsp;':'&#8201;',
1082
+ '&zwnj;':'&#8204;','&zwj;':'&#8205;','&lrm;':'&#8206;',
1083
+ '&rlm;':'&#8207;','&ndash;':'&#8211;','&mdash;':'&#8212;',
1084
+ '&lsquo;':'&#8216;','&rsquo;':'&#8217;','&sbquo;':'&#8218;',
1085
+ '&ldquo;':'&#8220;','&rdquo;':'&#8221;','&bdquo;':'&#8222;',
1086
+ '&dagger;':'&#8224;','&Dagger;':'&#8225;','&bull;':'&#8226;',
1087
+ '&hellip;':'&#8230;','&permil;':'&#8240;','&prime;':'&#8242;',
1088
+ '&Prime;':'&#8243;','&lsaquo;':'&#8249;','&rsaquo;':'&#8250;',
1089
+ '&oline;':'&#8254;','&frasl;':'&#8260;','&euro;':'&#8364;',
1090
+ '&image;':'&#8465;','&weierp;':'&#8472;','&real;':'&#8476;',
1091
+ '&trade;':'&#8482;','&alefsym;':'&#8501;','&larr;':'&#8592;',
1092
+ '&uarr;':'&#8593;','&rarr;':'&#8594;','&darr;':'&#8595;',
1093
+ '&harr;':'&#8596;','&crarr;':'&#8629;','&lArr;':'&#8656;',
1094
+ '&uArr;':'&#8657;','&rArr;':'&#8658;','&dArr;':'&#8659;',
1095
+ '&hArr;':'&#8660;','&forall;':'&#8704;','&part;':'&#8706;',
1096
+ '&exist;':'&#8707;','&empty;':'&#8709;','&nabla;':'&#8711;',
1097
+ '&isin;':'&#8712;','&notin;':'&#8713;','&ni;':'&#8715;',
1098
+ '&prod;':'&#8719;','&sum;':'&#8721;','&minus;':'&#8722;',
1099
+ '&lowast;':'&#8727;','&radic;':'&#8730;','&prop;':'&#8733;',
1100
+ '&infin;':'&#8734;','&ang;':'&#8736;','&and;':'&#8743;',
1101
+ '&or;':'&#8744;','&cap;':'&#8745;','&cup;':'&#8746;',
1102
+ '&int;':'&#8747;','&there4;':'&#8756;','&sim;':'&#8764;',
1103
+ '&cong;':'&#8773;','&asymp;':'&#8776;','&ne;':'&#8800;',
1104
+ '&equiv;':'&#8801;','&le;':'&#8804;','&ge;':'&#8805;',
1105
+ '&sub;':'&#8834;','&sup;':'&#8835;','&nsub;':'&#8836;',
1106
+ '&sube;':'&#8838;','&supe;':'&#8839;','&oplus;':'&#8853;',
1107
+ '&otimes;':'&#8855;','&perp;':'&#8869;','&sdot;':'&#8901;',
1108
+ '&lceil;':'&#8968;','&rceil;':'&#8969;','&lfloor;':'&#8970;',
1109
+ '&rfloor;':'&#8971;','&lang;':'&#9001;','&rang;':'&#9002;',
1110
+ '&loz;':'&#9674;','&spades;':'&#9824;','&clubs;':'&#9827;',
1111
+ '&hearts;':'&#9829;','&diams;':'&#9830;'};
1112
+
1113
+ this.block_tags = ["a", "abbr", "acronym", "address", "area", "b",
1114
+ "base", "bdo", "big", "blockquote", "body", "button",
1115
+ "caption", "cite", "code", "col", "colgroup", "dd", "del", "div",
1116
+ "dfn", "dl", "dt", "em", "fieldset", "form", "head", "h1", "h2",
1117
+ "h3", "h4", "h5", "h6", "html", "i", "iframe", "ins",
1118
+ "kbd", "label", "legend", "li", "map", "noscript",
1119
+ "object", "ol", "optgroup", "option", "p", "pre", "q",
1120
+ "samp", "script", "select", "small", "span", "strong", "style",
1121
+ "sub", "sup", "table", "tbody", "td", "textarea", "tfoot", "th",
1122
+ "thead", "title", "tr", "tt", "ul", "var", "extends", "meter",
1123
+ "section", "article", "aside", "details", "header", "footer",
1124
+ "nav", "dialog", "figure", "figcaption", "address", "hgroup",
1125
+ "mark", "time", "canvas", "audio", "video", "source", "output",
1126
+ "progress", "ruby", "rt", "rp", "summary", "command"<%= ", #{Refinery::Wymeditor.whitelist_tags.keys.map{|k| %Q{"#{k}"}}.join(', ')}" if Refinery::Wymeditor.whitelist_tags.any? %>];
1127
+
1128
+
1129
+ // Defines self-closing tags.
1130
+ this.inline_tags = ["br", "embed", "hr", "img", "input", "param", "source", "wbr"];
1131
+
1132
+ return this;
1133
+ };
1134
+
1135
+ WYMeditor.XhtmlSaxListener.prototype.shouldCloseTagAutomatically = function(tag, now_on_tag, closing)
1136
+ {
1137
+ var closing = closing || false;
1138
+ if(tag == 'td'){
1139
+ if((closing && now_on_tag == 'tr') || (!closing && now_on_tag == 'td')){
1140
+ return true;
1141
+ }
1142
+ }
1143
+ if(tag == 'option'){
1144
+ if((closing && now_on_tag == 'select') || (!closing && now_on_tag == 'option')){
1145
+ return true;
1146
+ }
1147
+ }
1148
+ return false;
1149
+ };
1150
+
1151
+ WYMeditor.XhtmlSaxListener.prototype.beforeParsing = function(raw)
1152
+ {
1153
+ this.output = '';
1154
+ return raw;
1155
+ };
1156
+
1157
+ WYMeditor.XhtmlSaxListener.prototype.afterParsing = function(xhtml)
1158
+ {
1159
+ xhtml = this.replaceNamedEntities(xhtml);
1160
+ xhtml = this.joinRepeatedEntities(xhtml);
1161
+ xhtml = this.removeEmptyTags(xhtml);
1162
+ return xhtml;
1163
+ };
1164
+
1165
+ WYMeditor.XhtmlSaxListener.prototype.replaceNamedEntities = function(xhtml)
1166
+ {
1167
+ for (var entity in this.entities) {
1168
+ xhtml = xhtml.replace(new RegExp(entity, 'g'), this.entities[entity]);
1169
+ }
1170
+ return xhtml;
1171
+ };
1172
+
1173
+ WYMeditor.XhtmlSaxListener.prototype.joinRepeatedEntities = function(xhtml)
1174
+ {
1175
+ var tags = 'em|strong|sub|sup|acronym|pre|del|address';
1176
+ return xhtml.replace(new RegExp('<\/('+tags+')><\\1>' ,''),'').
1177
+ replace(new RegExp('(\s*<('+tags+')>\s*){2}(.*)(\s*<\/\\2>\s*){2}' ,''),'<\$2>\$3<\$2>');
1178
+ };
1179
+
1180
+ WYMeditor.XhtmlSaxListener.prototype.removeEmptyTags = function(xhtml)
1181
+ {
1182
+ return xhtml.replace(new RegExp('<('+this.block_tags.join("|").replace(/\|td/,'').replace(/\|th/, '')+')>(<br \/>|&#160;|&nbsp;|\\s)*<\/\\1>' ,'g'),'');
1183
+ };
1184
+
1185
+ WYMeditor.XhtmlSaxListener.prototype.removeBrInPre = function(xhtml)
1186
+ {
1187
+ var matches = xhtml.match(new RegExp('<pre[^>]*>(.*?)<\/pre>','gmi'));
1188
+ if(matches) {
1189
+ for(var i=0; i<matches.length; i++) {
1190
+ xhtml = xhtml.replace(matches[i], matches[i].replace(new RegExp('<br \/>', 'g'), String.fromCharCode(13,10)));
1191
+ }
1192
+ }
1193
+ return xhtml;
1194
+ };
1195
+
1196
+ WYMeditor.XhtmlSaxListener.prototype.getResult = function()
1197
+ {
1198
+ return this.output;
1199
+ };
1200
+
1201
+ WYMeditor.XhtmlSaxListener.prototype.getTagReplacements = function()
1202
+ {
1203
+ return {'b':'strong', 'i':'em'};
1204
+ };
1205
+
1206
+ WYMeditor.XhtmlSaxListener.prototype.addContent = function(text)
1207
+ {
1208
+ this.output += text;
1209
+ };
1210
+
1211
+ WYMeditor.XhtmlSaxListener.prototype.addComment = function(text)
1212
+ {
1213
+ if(this.remove_comments){
1214
+ this.output += text;
1215
+ }
1216
+ };
1217
+
1218
+ WYMeditor.XhtmlSaxListener.prototype.addScript = function(text)
1219
+ {
1220
+ if(!this.remove_scripts){
1221
+ this.output += text;
1222
+ }
1223
+ };
1224
+
1225
+ WYMeditor.XhtmlSaxListener.prototype.addCss = function(text)
1226
+ {
1227
+ if(!this.remove_embeded_styles){
1228
+ this.output += text;
1229
+ }
1230
+ };
1231
+
1232
+ WYMeditor.XhtmlSaxListener.prototype.openBlockTag = function(tag, attributes)
1233
+ {
1234
+ this.output += this.helper.tag(tag, this.validator.getValidTagAttributes(tag, attributes), true);
1235
+ };
1236
+
1237
+ WYMeditor.XhtmlSaxListener.prototype.inlineTag = function(tag, attributes)
1238
+ {
1239
+ this.output += this.helper.tag(tag, this.validator.getValidTagAttributes(tag, attributes));
1240
+ };
1241
+
1242
+ WYMeditor.XhtmlSaxListener.prototype.openUnknownTag = function(tag, attributes)
1243
+ {
1244
+ if(tag === 'area') {
1245
+ this.output += this.helper.tag(tag, attributes, true);
1246
+ }
1247
+ };
1248
+
1249
+ WYMeditor.XhtmlSaxListener.prototype.closeBlockTag = function(tag)
1250
+ {
1251
+ this.output = this.output.replace(/<br \/>$/, '')+this._getClosingTagContent('before', tag)+"</"+tag+">"+this._getClosingTagContent('after', tag);
1252
+ };
1253
+
1254
+ WYMeditor.XhtmlSaxListener.prototype.closeUnknownTag = function(tag)
1255
+ {
1256
+ //this.output += "</"+tag+">";
1257
+ };
1258
+
1259
+ WYMeditor.XhtmlSaxListener.prototype.closeUnopenedTag = function(tag)
1260
+ {
1261
+ this.output += "</"+tag+">";
1262
+ };
1263
+
1264
+ WYMeditor.XhtmlSaxListener.prototype.avoidStylingTagsAndAttributes = function()
1265
+ {
1266
+ this.avoided_tags = ['div','span'];
1267
+ this.validator.skipped_attributes = ['style'];
1268
+ this.validator.skipped_attribute_values = ['MsoNormal','main1']; // MS Word attributes for class
1269
+ this._avoiding_tags_implicitly = true;
1270
+ };
1271
+
1272
+ WYMeditor.XhtmlSaxListener.prototype.allowStylingTagsAndAttributes = function()
1273
+ {
1274
+ this.avoided_tags = [];
1275
+ this.validator.skipped_attributes = [];
1276
+ this.validator.skipped_attribute_values = [];
1277
+ this._avoiding_tags_implicitly = false;
1278
+ };
1279
+
1280
+ WYMeditor.XhtmlSaxListener.prototype.isBlockTag = function(tag)
1281
+ {
1282
+ return !WYMeditor.Helper.contains(this.avoided_tags, tag) && WYMeditor.Helper.contains(this.block_tags, tag);
1283
+ };
1284
+
1285
+ WYMeditor.XhtmlSaxListener.prototype.isInlineTag = function(tag)
1286
+ {
1287
+ return !WYMeditor.Helper.contains(this.avoided_tags, tag) && WYMeditor.Helper.contains(this.inline_tags, tag);
1288
+ };
1289
+
1290
+ WYMeditor.XhtmlSaxListener.prototype.insertContentAfterClosingTag = function(tag, content)
1291
+ {
1292
+ this._insertContentWhenClosingTag('after', tag, content);
1293
+ };
1294
+
1295
+ WYMeditor.XhtmlSaxListener.prototype.insertContentBeforeClosingTag = function(tag, content)
1296
+ {
1297
+ this._insertContentWhenClosingTag('before', tag, content);
1298
+ };
1299
+
1300
+ WYMeditor.XhtmlSaxListener.prototype.fixNestingBeforeOpeningBlockTag = function(tag, attributes)
1301
+ {
1302
+ if(tag != 'li' && (tag == 'ul' || tag == 'ol') && this.last_tag && !this.last_tag_opened && this.last_tag == 'li'){
1303
+ this.output = this.output.replace(/<\/li>$/, '');
1304
+ this.insertContentAfterClosingTag(tag, '</li>');
1305
+ }
1306
+ };
1307
+
1308
+ WYMeditor.XhtmlSaxListener.prototype._insertContentWhenClosingTag = function(position, tag, content)
1309
+ {
1310
+ if(!this['_insert_'+position+'_closing']){
1311
+ this['_insert_'+position+'_closing'] = [];
1312
+ }
1313
+ if(!this['_insert_'+position+'_closing'][tag]){
1314
+ this['_insert_'+position+'_closing'][tag] = [];
1315
+ }
1316
+ this['_insert_'+position+'_closing'][tag].push(content);
1317
+ };
1318
+
1319
+ WYMeditor.XhtmlSaxListener.prototype._getClosingTagContent = function(position, tag)
1320
+ {
1321
+ if( this['_insert_'+position+'_closing'] &&
1322
+ this['_insert_'+position+'_closing'][tag] &&
1323
+ this['_insert_'+position+'_closing'][tag].length > 0){
1324
+ return this['_insert_'+position+'_closing'][tag].pop();
1325
+ }
1326
+ return '';
1327
+ };
1328
+
1329
+
1330
+ /********** CSS PARSER **********/
1331
+
1332
+
1333
+ WYMeditor.WymCssLexer = function(parser, only_wym_blocks)
1334
+ {
1335
+ var only_wym_blocks = (typeof only_wym_blocks == 'undefined' ? true : only_wym_blocks);
1336
+
1337
+ $.extend(this, new WYMeditor.Lexer(parser, (only_wym_blocks?'Ignore':'WymCss')));
1338
+
1339
+ this.mapHandler('WymCss', 'Ignore');
1340
+
1341
+ if(only_wym_blocks == true){
1342
+ this.addEntryPattern("/\\\x2a[<\\s]*WYMeditor[>\\s]*\\\x2a/", 'Ignore', 'WymCss');
1343
+ this.addExitPattern("/\\\x2a[<\/\\s]*WYMeditor[>\\s]*\\\x2a/", 'WymCss');
1344
+ }
1345
+
1346
+ this.addSpecialPattern("[\\sa-z1-6]*\\\x2e[a-z-_0-9]+", 'WymCss', 'WymCssStyleDeclaration');
1347
+
1348
+ this.addEntryPattern("/\\\x2a", 'WymCss', 'WymCssComment');
1349
+ this.addExitPattern("\\\x2a/", 'WymCssComment');
1350
+
1351
+ this.addEntryPattern("\x7b", 'WymCss', 'WymCssStyle');
1352
+ this.addExitPattern("\x7d", 'WymCssStyle');
1353
+
1354
+ this.addEntryPattern("/\\\x2a", 'WymCssStyle', 'WymCssFeedbackStyle');
1355
+ this.addExitPattern("\\\x2a/", 'WymCssFeedbackStyle');
1356
+
1357
+ return this;
1358
+ };
1359
+
1360
+ WYMeditor.WymCssParser = function()
1361
+ {
1362
+ this._in_style = false;
1363
+ this._has_title = false;
1364
+ this.only_wym_blocks = true;
1365
+ this.css_settings = {'classesItems':[], 'editorStyles':[], 'dialogStyles':[]};
1366
+ return this;
1367
+ };
1368
+
1369
+ WYMeditor.WymCssParser.prototype.parse = function(raw, only_wym_blocks)
1370
+ {
1371
+ var only_wym_blocks = (typeof only_wym_blocks == 'undefined' ? this.only_wym_blocks : only_wym_blocks);
1372
+ this._Lexer = new WYMeditor.WymCssLexer(this, only_wym_blocks);
1373
+ this._Lexer.parse(raw);
1374
+ };
1375
+
1376
+ WYMeditor.WymCssParser.prototype.Ignore = function(match, state)
1377
+ {
1378
+ return true;
1379
+ };
1380
+
1381
+ WYMeditor.WymCssParser.prototype.WymCssComment = function(text, status)
1382
+ {
1383
+ if(text.match(/end[a-z0-9\s]*wym[a-z0-9\s]*/mi)){
1384
+ return false;
1385
+ }
1386
+ if(status == WYMeditor.LEXER_UNMATCHED){
1387
+ if(!this._in_style){
1388
+ this._has_title = true;
1389
+ this._current_item = {'title':WYMeditor.Helper.trim(text)};
1390
+ }else{
1391
+ if(this._current_item[this._current_element]){
1392
+ if(!this._current_item[this._current_element].expressions){
1393
+ this._current_item[this._current_element].expressions = [text];
1394
+ }else{
1395
+ this._current_item[this._current_element].expressions.push(text);
1396
+ }
1397
+ }
1398
+ }
1399
+ this._in_style = true;
1400
+ }
1401
+ return true;
1402
+ };
1403
+
1404
+ WYMeditor.WymCssParser.prototype.WymCssStyle = function(match, status)
1405
+ {
1406
+ if(status == WYMeditor.LEXER_UNMATCHED){
1407
+ match = WYMeditor.Helper.trim(match);
1408
+ if(match != ''){
1409
+ this._current_item[this._current_element].style = match;
1410
+ }
1411
+ }else if (status == WYMeditor.LEXER_EXIT){
1412
+ this._in_style = false;
1413
+ this._has_title = false;
1414
+ this.addStyleSetting(this._current_item);
1415
+ }
1416
+ return true;
1417
+ };
1418
+
1419
+ WYMeditor.WymCssParser.prototype.WymCssFeedbackStyle = function(match, status)
1420
+ {
1421
+ if(status == WYMeditor.LEXER_UNMATCHED){
1422
+ this._current_item[this._current_element].feedback_style = match.replace(/^([\s\/\*]*)|([\s\/\*]*)$/gm,'');
1423
+ }
1424
+ return true;
1425
+ };
1426
+
1427
+ WYMeditor.WymCssParser.prototype.WymCssStyleDeclaration = function(match)
1428
+ {
1429
+ match = match.replace(/^([\s\.]*)|([\s\.*]*)$/gm, '');
1430
+
1431
+ var tag = '';
1432
+ if(match.indexOf('.') > 0){
1433
+ var parts = match.split('.');
1434
+ this._current_element = parts[1];
1435
+ var tag = parts[0];
1436
+ }else{
1437
+ this._current_element = match;
1438
+ }
1439
+
1440
+ if(!this._has_title){
1441
+ this._current_item = {'title':(!tag?'':tag.toUpperCase()+': ')+this._current_element};
1442
+ this._has_title = true;
1443
+ }
1444
+
1445
+ if(!this._current_item[this._current_element]){
1446
+ this._current_item[this._current_element] = {'name':this._current_element};
1447
+ }
1448
+ if(tag){
1449
+ if(!this._current_item[this._current_element].tags){
1450
+ this._current_item[this._current_element].tags = [tag];
1451
+ }else{
1452
+ this._current_item[this._current_element].tags.push(tag);
1453
+ }
1454
+ }
1455
+ return true;
1456
+ };
1457
+
1458
+ WYMeditor.WymCssParser.prototype.addStyleSetting = function(style_details)
1459
+ {
1460
+ for (var name in style_details){
1461
+ var details = style_details[name];
1462
+ if(typeof details == 'object' && name != 'title'){
1463
+
1464
+ this.css_settings.classesItems.push({
1465
+ 'name': WYMeditor.Helper.trim(details.name),
1466
+ 'title': style_details.title,
1467
+ 'expr' : WYMeditor.Helper.trim((details.expressions||details.tags).join(', '))
1468
+ });
1469
+ if(details.feedback_style){
1470
+ this.css_settings.editorStyles.push({
1471
+ 'name': '.'+ WYMeditor.Helper.trim(details.name),
1472
+ 'css': details.feedback_style
1473
+ });
1474
+ }
1475
+ if(details.style){
1476
+ this.css_settings.dialogStyles.push({
1477
+ 'name': '.'+ WYMeditor.Helper.trim(details.name),
1478
+ 'css': details.style
1479
+ });
1480
+ }
1481
+ }
1482
+ }
1483
+ };
1484
+
1485
+ /********** HELPERS **********/
1486
+
1487
+ // Returns true if it is a text node with whitespaces only
1488
+ $.fn.isPhantomNode = function() {
1489
+ if (this[0].nodeType == 3)
1490
+ return !(/[^\t\n\r ]/.test(this[0].data));
1491
+
1492
+ return false;
1493
+ };
1494
+
1495
+ WYMeditor.isPhantomNode = function(n) {
1496
+ if (n.nodeType == 3)
1497
+ return !(/[^\t\n\r ]/.test(n.data));
1498
+
1499
+ return false;
1500
+ };
1501
+
1502
+ WYMeditor.isPhantomString = function(str) {
1503
+ return !(/[^\t\n\r ]/.test(str));
1504
+ };
1505
+
1506
+ // Returns the Parents or the node itself
1507
+ // jqexpr = a jQuery expression
1508
+ $.fn.parentsOrSelf = function(jqexpr) {
1509
+ var n = this;
1510
+
1511
+ if (n[0].nodeType == 3)
1512
+ n = n.parents().slice(0,1);
1513
+
1514
+ // if (n.is(jqexpr)) // XXX should work, but doesn't (probably a jQuery bug)
1515
+ if (n.filter(jqexpr).size() == 1)
1516
+ return n;
1517
+ else
1518
+ return n.parents(jqexpr).slice(0,1);
1519
+ };
1520
+
1521
+ // String & array helpers
1522
+
1523
+ WYMeditor.Helper = {
1524
+
1525
+ //replace all instances of 'old' by 'rep' in 'str' string
1526
+ replaceAll: function(str, old, rep) {
1527
+ return(str.replace(new RegExp(old, "g"), rep));
1528
+ },
1529
+
1530
+ //insert 'inserted' at position 'pos' in 'str' string
1531
+ insertAt: function(str, inserted, pos) {
1532
+ return(str.substr(0,pos) + inserted + str.substring(pos));
1533
+ },
1534
+
1535
+ //trim 'str' string
1536
+ trim: function(str) {
1537
+ return str.replace(/^(\s*)|(\s*)$/gm,'');
1538
+ },
1539
+
1540
+ //return true if 'arr' array contains 'elem', or false
1541
+ contains: function(arr, elem) {
1542
+ for (var i = 0; i < arr.length; i++) {
1543
+ if (arr[i] === elem) return true;
1544
+ }
1545
+ return false;
1546
+ },
1547
+
1548
+ //return 'item' position in 'arr' array, or -1
1549
+ indexOf: function(arr, item) {
1550
+ var ret=-1;
1551
+ for(var i = 0; i < arr.length; i++) {
1552
+ if (arr[i] == item) {
1553
+ ret = i;
1554
+ break;
1555
+ }
1556
+ }
1557
+ return ret;
1558
+ },
1559
+
1560
+ //return 'item' object in 'arr' array, checking its 'name' property, or null
1561
+ findByName: function(arr, name) {
1562
+ for(var i = 0; i < arr.length; i++) {
1563
+ var item = arr[i];
1564
+ if(item.name == name) return(item);
1565
+ }
1566
+ return null;
1567
+ }
1568
+ };
1569
+
1570
+ function titleize(words) {
1571
+ if (words == null) return words;
1572
+ parts = [];
1573
+ $.each(words.replace(/\./, '').replace(/[-_]/, ' ').split(' '), function(index, part){
1574
+ parts.push(part.substring(0,1).toUpperCase() + part.substring(1));
1575
+ });
1576
+ return parts.join(" ");
1577
+ }