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