]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/debian/missing-sources/framework/Web/Javascripts/source/tinymce-405/classes/html/SaxParser.js
baculum: Add missing-sources directory in debian metadata structure
[bacula/bacula] / gui / baculum / debian / missing-sources / framework / Web / Javascripts / source / tinymce-405 / classes / html / SaxParser.js
1 /**
2  * SaxParser.js
3  *
4  * Copyright, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://www.tinymce.com/license
8  * Contributing: http://www.tinymce.com/contributing
9  */
10
11 /**
12  * This class parses HTML code using pure JavaScript and executes various events for each item it finds. It will
13  * always execute the events in the right order for tag soup code like <b><p></b></p>. It will also remove elements
14  * and attributes that doesn't fit the schema if the validate setting is enabled.
15  *
16  * @example
17  * var parser = new tinymce.html.SaxParser({
18  *     validate: true,
19  *
20  *     comment: function(text) {
21  *         console.log('Comment:', text);
22  *     },
23  *
24  *     cdata: function(text) {
25  *         console.log('CDATA:', text);
26  *     },
27  *
28  *     text: function(text, raw) {
29  *         console.log('Text:', text, 'Raw:', raw);
30  *     },
31  *
32  *     start: function(name, attrs, empty) {
33  *         console.log('Start:', name, attrs, empty);
34  *     },
35  *
36  *     end: function(name) {
37  *         console.log('End:', name);
38  *     },
39  *
40  *     pi: function(name, text) {
41  *         console.log('PI:', name, text);
42  *     },
43  *
44  *     doctype: function(text) {
45  *         console.log('DocType:', text);
46  *     }
47  * }, schema);
48  * @class tinymce.html.SaxParser
49  * @version 3.4
50  */
51 define("tinymce/html/SaxParser", [
52         "tinymce/html/Schema",
53         "tinymce/html/Entities",
54         "tinymce/util/Tools"
55 ], function(Schema, Entities, Tools) {
56         var each = Tools.each;
57
58         /**
59          * Constructs a new SaxParser instance.
60          *
61          * @constructor
62          * @method SaxParser
63          * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
64          * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
65          */
66         return function(settings, schema) {
67                 var self = this, noop = function() {};
68
69                 settings = settings || {};
70                 self.schema = schema = schema || new Schema();
71
72                 if (settings.fix_self_closing !== false) {
73                         settings.fix_self_closing = true;
74                 }
75
76                 // Add handler functions from settings and setup default handlers
77                 each('comment cdata text start end pi doctype'.split(' '), function(name) {
78                         if (name) {
79                                 self[name] = settings[name] || noop;
80                         }
81                 });
82
83                 /**
84                  * Parses the specified HTML string and executes the callbacks for each item it finds.
85                  *
86                  * @example
87                  * new SaxParser({...}).parse('<b>text</b>');
88                  * @method parse
89                  * @param {String} html Html string to sax parse.
90                  */
91                 self.parse = function(html) {
92                         var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name;
93                         var isInternalElement, removeInternalElements, shortEndedElements, fillAttrsMap, isShortEnded;
94                         var validate, elementRule, isValidElement, attr, attribsValue, validAttributesMap, validAttributePatterns;
95                         var attributesRequired, attributesDefault, attributesForced;
96                         var anyAttributesRequired, selfClosing, tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0;
97                         var decode = Entities.decode, fixSelfClosing;
98
99                         function processEndTag(name) {
100                                 var pos, i;
101
102                                 // Find position of parent of the same type
103                                 pos = stack.length;
104                                 while (pos--) {
105                                         if (stack[pos].name === name) {
106                                                 break;
107                                         }
108                                 }
109
110                                 // Found parent
111                                 if (pos >= 0) {
112                                         // Close all the open elements
113                                         for (i = stack.length - 1; i >= pos; i--) {
114                                                 name = stack[i];
115
116                                                 if (name.valid) {
117                                                         self.end(name.name);
118                                                 }
119                                         }
120
121                                         // Remove the open elements from the stack
122                                         stack.length = pos;
123                                 }
124                         }
125
126                         function parseAttribute(match, name, value, val2, val3) {
127                                 var attrRule, i;
128
129                                 name = name.toLowerCase();
130                                 value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
131
132                                 // Validate name and value pass through all data- attributes
133                                 if (validate && !isInternalElement && name.indexOf('data-') !== 0) {
134                                         attrRule = validAttributesMap[name];
135
136                                         // Find rule by pattern matching
137                                         if (!attrRule && validAttributePatterns) {
138                                                 i = validAttributePatterns.length;
139                                                 while (i--) {
140                                                         attrRule = validAttributePatterns[i];
141                                                         if (attrRule.pattern.test(name)) {
142                                                                 break;
143                                                         }
144                                                 }
145
146                                                 // No rule matched
147                                                 if (i === -1) {
148                                                         attrRule = null;
149                                                 }
150                                         }
151
152                                         // No attribute rule found
153                                         if (!attrRule) {
154                                                 return;
155                                         }
156
157                                         // Validate value
158                                         if (attrRule.validValues && !(value in attrRule.validValues)) {
159                                                 return;
160                                         }
161                                 }
162
163                                 // Add attribute to list and map
164                                 attrList.map[name] = value;
165                                 attrList.push({
166                                         name: name,
167                                         value: value
168                                 });
169                         }
170
171                         // Precompile RegExps and map objects
172                         tokenRegExp = new RegExp('<(?:' +
173                                 '(?:!--([\\w\\W]*?)-->)|' + // Comment
174                                 '(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA
175                                 '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
176                                 '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
177                                 '(?:\\/([^>]+)>)|' + // End element
178                                 '(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
179                         ')', 'g');
180
181                         attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;
182
183                         // Setup lookup tables for empty elements and boolean attributes
184                         shortEndedElements = schema.getShortEndedElements();
185                         selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
186                         fillAttrsMap = schema.getBoolAttrs();
187                         validate = settings.validate;
188                         removeInternalElements = settings.remove_internals;
189                         fixSelfClosing = settings.fix_self_closing;
190                         specialElements = schema.getSpecialElements();
191
192                         while ((matches = tokenRegExp.exec(html))) {
193                                 // Text
194                                 if (index < matches.index) {
195                                         self.text(decode(html.substr(index, matches.index - index)));
196                                 }
197
198                                 if ((value = matches[6])) { // End element
199                                         value = value.toLowerCase();
200
201                                         // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
202                                         if (value.charAt(0) === ':') {
203                                                 value = value.substr(1);
204                                         }
205
206                                         processEndTag(value);
207                                 } else if ((value = matches[7])) { // Start element
208                                         value = value.toLowerCase();
209
210                                         // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
211                                         if (value.charAt(0) === ':') {
212                                                 value = value.substr(1);
213                                         }
214
215                                         isShortEnded = value in shortEndedElements;
216
217                                         // Is self closing tag for example an <li> after an open <li>
218                                         if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value) {
219                                                 processEndTag(value);
220                                         }
221
222                                         // Validate element
223                                         if (!validate || (elementRule = schema.getElementRule(value))) {
224                                                 isValidElement = true;
225
226                                                 // Grab attributes map and patters when validation is enabled
227                                                 if (validate) {
228                                                         validAttributesMap = elementRule.attributes;
229                                                         validAttributePatterns = elementRule.attributePatterns;
230                                                 }
231
232                                                 // Parse attributes
233                                                 if ((attribsValue = matches[8])) {
234                                                         isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element
235
236                                                         // If the element has internal attributes then remove it if we are told to do so
237                                                         if (isInternalElement && removeInternalElements) {
238                                                                 isValidElement = false;
239                                                         }
240
241                                                         attrList = [];
242                                                         attrList.map = {};
243
244                                                         attribsValue.replace(attrRegExp, parseAttribute);
245                                                 } else {
246                                                         attrList = [];
247                                                         attrList.map = {};
248                                                 }
249
250                                                 // Process attributes if validation is enabled
251                                                 if (validate && !isInternalElement) {
252                                                         attributesRequired = elementRule.attributesRequired;
253                                                         attributesDefault = elementRule.attributesDefault;
254                                                         attributesForced = elementRule.attributesForced;
255                                                         anyAttributesRequired = elementRule.removeEmptyAttrs;
256
257                                                         // Check if any attribute exists
258                                                         if (anyAttributesRequired && !attrList.length) {
259                                                                 isValidElement = false;
260                                                         }
261
262                                                         // Handle forced attributes
263                                                         if (attributesForced) {
264                                                                 i = attributesForced.length;
265                                                                 while (i--) {
266                                                                         attr = attributesForced[i];
267                                                                         name = attr.name;
268                                                                         attrValue = attr.value;
269
270                                                                         if (attrValue === '{$uid}') {
271                                                                                 attrValue = 'mce_' + idCount++;
272                                                                         }
273
274                                                                         attrList.map[name] = attrValue;
275                                                                         attrList.push({name: name, value: attrValue});
276                                                                 }
277                                                         }
278
279                                                         // Handle default attributes
280                                                         if (attributesDefault) {
281                                                                 i = attributesDefault.length;
282                                                                 while (i--) {
283                                                                         attr = attributesDefault[i];
284                                                                         name = attr.name;
285
286                                                                         if (!(name in attrList.map)) {
287                                                                                 attrValue = attr.value;
288
289                                                                                 if (attrValue === '{$uid}') {
290                                                                                         attrValue = 'mce_' + idCount++;
291                                                                                 }
292
293                                                                                 attrList.map[name] = attrValue;
294                                                                                 attrList.push({name: name, value: attrValue});
295                                                                         }
296                                                                 }
297                                                         }
298
299                                                         // Handle required attributes
300                                                         if (attributesRequired) {
301                                                                 i = attributesRequired.length;
302                                                                 while (i--) {
303                                                                         if (attributesRequired[i] in attrList.map) {
304                                                                                 break;
305                                                                         }
306                                                                 }
307
308                                                                 // None of the required attributes where found
309                                                                 if (i === -1) {
310                                                                         isValidElement = false;
311                                                                 }
312                                                         }
313
314                                                         // Invalidate element if it's marked as bogus
315                                                         if (attrList.map['data-mce-bogus']) {
316                                                                 isValidElement = false;
317                                                         }
318                                                 }
319
320                                                 if (isValidElement) {
321                                                         self.start(value, attrList, isShortEnded);
322                                                 }
323                                         } else {
324                                                 isValidElement = false;
325                                         }
326
327                                         // Treat script, noscript and style a bit different since they may include code that looks like elements
328                                         if ((endRegExp = specialElements[value])) {
329                                                 endRegExp.lastIndex = index = matches.index + matches[0].length;
330
331                                                 if ((matches = endRegExp.exec(html))) {
332                                                         if (isValidElement) {
333                                                                 text = html.substr(index, matches.index - index);
334                                                         }
335
336                                                         index = matches.index + matches[0].length;
337                                                 } else {
338                                                         text = html.substr(index);
339                                                         index = html.length;
340                                                 }
341
342                                                 if (isValidElement) {
343                                                         if (text.length > 0) {
344                                                                 self.text(text, true);
345                                                         }
346
347                                                         self.end(value);
348                                                 }
349
350                                                 tokenRegExp.lastIndex = index;
351                                                 continue;
352                                         }
353
354                                         // Push value on to stack
355                                         if (!isShortEnded) {
356                                                 if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1) {
357                                                         stack.push({name: value, valid: isValidElement});
358                                                 } else if (isValidElement) {
359                                                         self.end(value);
360                                                 }
361                                         }
362                                 } else if ((value = matches[1])) { // Comment
363                                         self.comment(value);
364                                 } else if ((value = matches[2])) { // CDATA
365                                         self.cdata(value);
366                                 } else if ((value = matches[3])) { // DOCTYPE
367                                         self.doctype(value);
368                                 } else if ((value = matches[4])) { // PI
369                                         self.pi(value, matches[5]);
370                                 }
371
372                                 index = matches.index + matches[0].length;
373                         }
374
375                         // Text
376                         if (index < html.length) {
377                                 self.text(decode(html.substr(index)));
378                         }
379
380                         // Close any open elements
381                         for (i = stack.length - 1; i >= 0; i--) {
382                                 value = stack[i];
383
384                                 if (value.valid) {
385                                         self.end(value.name);
386                                 }
387                         }
388                 };
389         };
390 });