]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/tiny_mce/classes/html/Node.js
Release 6.2.3
[Github/sugarcrm.git] / include / javascript / tiny_mce / classes / html / Node.js
1 /**
2  * Node.js
3  *
4  * Copyright 2010, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://tinymce.moxiecode.com/license
8  * Contributing: http://tinymce.moxiecode.com/contributing
9  */
10
11 (function(tinymce) {
12         var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {
13                 '#text' : 3,
14                 '#comment' : 8,
15                 '#cdata' : 4,
16                 '#pi' : 7,
17                 '#doctype' : 10,
18                 '#document-fragment' : 11
19         };
20
21         // Walks the tree left/right
22         function walk(node, root_node, prev) {
23                 var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';
24
25                 // Walk into nodes if it has a start
26                 if (node[startName])
27                         return node[startName];
28
29                 // Return the sibling if it has one
30                 if (node !== root_node) {
31                         sibling = node[siblingName];
32
33                         if (sibling)
34                                 return sibling;
35
36                         // Walk up the parents to look for siblings
37                         for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {
38                                 sibling = parent[siblingName];
39
40                                 if (sibling)
41                                         return sibling;
42                         }
43                 }
44         };
45
46         /**
47          * This class is a minimalistic implementation of a DOM like node used by the DomParser class.
48          *
49          * @example
50          * var node = new tinymce.html.Node('strong', 1);
51          * someRoot.append(node);
52          *
53          * @class tinymce.html.Node
54          * @version 3.4
55          */
56
57         /**
58          * Constructs a new Node instance.
59          *
60          * @constructor
61          * @method Node
62          * @param {String} name Name of the node type.
63          * @param {Number} type Numeric type representing the node.
64          */
65         function Node(name, type) {
66                 this.name = name;
67                 this.type = type;
68
69                 if (type === 1) {
70                         this.attributes = [];
71                         this.attributes.map = {};
72                 }
73         }
74
75         tinymce.extend(Node.prototype, {
76                 /**
77                  * Replaces the current node with the specified one.
78                  *
79                  * @example
80                  * someNode.replace(someNewNode);
81                  *
82                  * @method replace
83                  * @param {tinymce.html.Node} node Node to replace the current node with.
84                  * @return {tinymce.html.Node} The old node that got replaced.
85                  */
86                 replace : function(node) {
87                         var self = this;
88
89                         if (node.parent)
90                                 node.remove();
91
92                         self.insert(node, self);
93                         self.remove();
94
95                         return self;
96                 },
97
98                 /**
99                  * Gets/sets or removes an attribute by name.
100                  *
101                  * @example
102                  * someNode.attr("name", "value"); // Sets an attribute
103                  * console.log(someNode.attr("name")); // Gets an attribute
104                  * someNode.attr("name", null); // Removes an attribute
105                  *
106                  * @method attr
107                  * @param {String} name Attribute name to set or get.
108                  * @param {String} value Optional value to set.
109                  * @return {String/tinymce.html.Node} String or undefined on a get operation or the current node on a set operation.
110                  */
111                 attr : function(name, value) {
112                         var self = this, attrs, i, undef;
113
114                         if (typeof name !== "string") {
115                                 for (i in name)
116                                         self.attr(i, name[i]);
117
118                                 return self;
119                         }
120
121                         if (attrs = self.attributes) {
122                                 if (value !== undef) {
123                                         // Remove attribute
124                                         if (value === null) {
125                                                 if (name in attrs.map) {
126                                                         delete attrs.map[name];
127
128                                                         i = attrs.length;
129                                                         while (i--) {
130                                                                 if (attrs[i].name === name) {
131                                                                         attrs = attrs.splice(i, 1);
132                                                                         return self;
133                                                                 }
134                                                         }
135                                                 }
136
137                                                 return self;
138                                         }
139
140                                         // Set attribute
141                                         if (name in attrs.map) {
142                                                 // Set attribute
143                                                 i = attrs.length;
144                                                 while (i--) {
145                                                         if (attrs[i].name === name) {
146                                                                 attrs[i].value = value;
147                                                                 break;
148                                                         }
149                                                 }
150                                         } else
151                                                 attrs.push({name: name, value: value});
152
153                                         attrs.map[name] = value;
154
155                                         return self;
156                                 } else {
157                                         return attrs.map[name];
158                                 }
159                         }
160                 },
161
162                 /**
163                  * Does a shallow clones the node into a new node. It will also exclude id attributes since
164                  * there should only be one id per document.
165                  *
166                  * @example
167                  * var clonedNode = node.clone();
168                  *
169                  * @method clone
170                  * @return {tinymce.html.Node} New copy of the original node.
171                  */
172                 clone : function() {
173                         var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;
174
175                         // Clone element attributes
176                         if (selfAttrs = self.attributes) {
177                                 cloneAttrs = [];
178                                 cloneAttrs.map = {};
179
180                                 for (i = 0, l = selfAttrs.length; i < l; i++) {
181                                         selfAttr = selfAttrs[i];
182
183                                         // Clone everything except id
184                                         if (selfAttr.name !== 'id') {
185                                                 cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};
186                                                 cloneAttrs.map[selfAttr.name] = selfAttr.value;
187                                         }
188                                 }
189
190                                 clone.attributes = cloneAttrs;
191                         }
192
193                         clone.value = self.value;
194                         clone.shortEnded = self.shortEnded;
195
196                         return clone;
197                 },
198
199                 /**
200                  * Wraps the node in in another node.
201                  *
202                  * @example
203                  * node.wrap(wrapperNode);
204                  *
205                  * @method wrap
206                  */
207                 wrap : function(wrapper) {
208                         var self = this;
209
210                         self.parent.insert(wrapper, self);
211                         wrapper.append(self);
212
213                         return self;
214                 },
215
216                 /**
217                  * Unwraps the node in other words it removes the node but keeps the children.
218                  *
219                  * @example
220                  * node.unwrap();
221                  *
222                  * @method unwrap
223                  */
224                 unwrap : function() {
225                         var self = this, node, next;
226
227                         for (node = self.firstChild; node; ) {
228                                 next = node.next;
229                                 self.insert(node, self, true);
230                                 node = next;
231                         }
232
233                         self.remove();
234                 },
235
236                 /**
237                  * Removes the node from it's parent.
238                  *
239                  * @example
240                  * node.remove();
241                  *
242                  * @method remove
243                  * @return {tinymce.html.Node} Current node that got removed.
244                  */
245                 remove : function() {
246                         var self = this, parent = self.parent, next = self.next, prev = self.prev;
247
248                         if (parent) {
249                                 if (parent.firstChild === self) {
250                                         parent.firstChild = next;
251
252                                         if (next)
253                                                 next.prev = null;
254                                 } else {
255                                         prev.next = next;
256                                 }
257
258                                 if (parent.lastChild === self) {
259                                         parent.lastChild = prev;
260
261                                         if (prev)
262                                                 prev.next = null;
263                                 } else {
264                                         next.prev = prev;
265                                 }
266
267                                 self.parent = self.next = self.prev = null;
268                         }
269
270                         return self;
271                 },
272
273                 /**
274                  * Appends a new node as a child of the current node.
275                  *
276                  * @example
277                  * node.append(someNode);
278                  *
279                  * @method append
280                  * @param {tinymce.html.Node} node Node to append as a child of the current one.
281                  * @return {tinymce.html.Node} The node that got appended.
282                  */
283                 append : function(node) {
284                         var self = this, last;
285
286                         if (node.parent)
287                                 node.remove();
288
289                         last = self.lastChild;
290                         if (last) {
291                                 last.next = node;
292                                 node.prev = last;
293                                 self.lastChild = node;
294                         } else
295                                 self.lastChild = self.firstChild = node;
296
297                         node.parent = self;
298
299                         return node;
300                 },
301
302                 /**
303                  * Inserts a node at a specific position as a child of the current node.
304                  *
305                  * @example
306                  * parentNode.insert(newChildNode, oldChildNode);
307                  *
308                  * @method insert
309                  * @param {tinymce.html.Node} node Node to insert as a child of the current node.
310                  * @param {tinymce.html.Node} ref_node Reference node to set node before/after.
311                  * @param {Boolean} before Optional state to insert the node before the reference node.
312                  * @return {tinymce.html.Node} The node that got inserted.
313                  */
314                 insert : function(node, ref_node, before) {
315                         var parent;
316
317                         if (node.parent)
318                                 node.remove();
319
320                         parent = ref_node.parent || this;
321
322                         if (before) {
323                                 if (ref_node === parent.firstChild)
324                                         parent.firstChild = node;
325                                 else
326                                         ref_node.prev.next = node;
327
328                                 node.prev = ref_node.prev;
329                                 node.next = ref_node;
330                                 ref_node.prev = node;
331                         } else {
332                                 if (ref_node === parent.lastChild)
333                                         parent.lastChild = node;
334                                 else
335                                         ref_node.next.prev = node;
336
337                                 node.next = ref_node.next;
338                                 node.prev = ref_node;
339                                 ref_node.next = node;
340                         }
341
342                         node.parent = parent;
343
344                         return node;
345                 },
346
347                 /**
348                  * Get all children by name.
349                  *
350                  * @method getAll
351                  * @param {String} name Name of the child nodes to collect.
352                  * @return {Array} Array with child nodes matchin the specified name.
353                  */
354                 getAll : function(name) {
355                         var self = this, node, collection = [];
356
357                         for (node = self.firstChild; node; node = walk(node, self)) {
358                                 if (node.name === name)
359                                         collection.push(node);
360                         }
361
362                         return collection;
363                 },
364
365                 /**
366                  * Removes all children of the current node.
367                  *
368                  * @method empty
369                  * @return {tinymce.html.Node} The current node that got cleared.
370                  */
371                 empty : function() {
372                         var self = this, nodes, i, node;
373
374                         // Remove all children
375                         if (self.firstChild) {
376                                 nodes = [];
377
378                                 // Collect the children
379                                 for (node = self.firstChild; node; node = walk(node, self))
380                                         nodes.push(node);
381
382                                 // Remove the children
383                                 i = nodes.length;
384                                 while (i--) {
385                                         node = nodes[i];
386                                         node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;
387                                 }
388                         }
389
390                         self.firstChild = self.lastChild = null;
391
392                         return self;
393                 },
394
395                 /**
396                  * Returns true/false if the node is to be considered empty or not.
397                  *
398                  * @example
399                  * node.isEmpty({img : true});
400                  * @method isEmpty
401                  * @param {Object} elements Name/value object with elements that are automatically treated as non empty elements.
402                  * @return {Boolean} true/false if the node is empty or not.
403                  */
404                 isEmpty : function(elements) {
405                         var self = this, node = self.firstChild, i, name;
406
407                         if (node) {
408                                 do {
409                                         if (node.type === 1) {
410                                                 // Ignore bogus elements
411                                                 if (node.attributes.map['data-mce-bogus'])
412                                                         continue;
413
414                                                 // Keep empty elements like <img />
415                                                 if (elements[node.name])
416                                                         return false;
417
418                                                 // Keep elements with data attributes or name attribute like <a name="1"></a>
419                                                 i = node.attributes.length;
420                                                 while (i--) {
421                                                         name = node.attributes[i].name;
422                                                         if (name === "name" || name.indexOf('data-') === 0)
423                                                                 return false;
424                                                 }
425                                         }
426
427                                         // Keep non whitespace text nodes
428                                         if ((node.type === 3 && !whiteSpaceRegExp.test(node.value)))
429                                                 return false;
430                                 } while (node = walk(node, self));
431                         }
432
433                         return true;
434                 },
435
436                 /**
437                  * Walks to the next or previous node and returns that node or null if it wasn't found.
438                  *
439                  * @method walk
440                  * @param {Boolean} prev Optional previous node state defaults to false.
441                  * @return {tinymce.html.Node} Node that is next to or previous of the current node.
442                  */
443                 walk : function(prev) {
444                         return walk(this, null, prev);
445                 }
446         });
447
448         tinymce.extend(Node, {
449                 /**
450                  * Creates a node of a specific type.
451                  *
452                  * @static
453                  * @method create
454                  * @param {String} name Name of the node type to create for example "b" or "#text".
455                  * @param {Object} attrs Name/value collection of attributes that will be applied to elements.
456                  */
457                 create : function(name, attrs) {
458                         var node, attrName;
459
460                         // Create node
461                         node = new Node(name, typeLookup[name] || 1);
462
463                         // Add attributes if needed
464                         if (attrs) {
465                                 for (attrName in attrs)
466                                         node.attr(attrName, attrs[attrName]);
467                         }
468
469                         return node;
470                 }
471         });
472
473         tinymce.html.Node = Node;
474 })(tinymce);