      1 /**
      2  * Validates and invalidates designmode contents
      3  */
      4 xq.Validator = Class.create({
      5 	initialize: function(curUrl, urlValidationMode, allowedTags, allowedAttrs) {
      6 		this.allowedTags = (allowedTags || ['a', 'abbr', 'acronym', 'address', 'blockquote', 'br', 'caption', 'cite', 'code', 'dd', 'dfn', 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'img', 'kbd', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'span', 'sup', 'sub', 'strong', 'table', 'thead', 'tbody', 'td', 'th', 'tr', 'ul', 'var']).join(' ') + ' ';
      7 		this.allowedAttrs = (allowedAttrs || ['alt', 'cite', 'class', 'datetime', 'height', 'href', 'id', 'rel', 'rev', 'src', 'style', 'title', 'width']).join(' ') + ' ';
      8
      9 		this.curUrl = curUrl;
     10 		this.curUrlParts = curUrl ? curUrl.parseURL() : null;
     11 		this.urlValidationMode = urlValidationMode;
     12 	},
     13
     14 	/**
     15 	 * Perform validation on given element
     16 	 *
     17 	 * @param {Element} element Target element. It is not affected by validation.
     18 	 * @param {boolean} fullValidation Perform full validation. If you just want to use the result to assign innerHTML, set it false
     19 	 *
     20 	 * @returns {String} Validated HTML string
     21 	 */
     22 	validate: function(element, fullValidation) {throw "Not implemented"},
     23
     24 	/**
     25 	 * Perform invalidation on given element to make the designmode works well.
     26 	 *
     27 	 * @param {Element} element Target element.
     28 	 * @returns {String} Invalidated HTML string
     29 	 */
     30 	invalidate: function(element) {throw "Not implemented"},
     31
     32 	validateStrike: function(content) {
     33 		content = content.replace(/<strike(>|\s+[^>]*>)/ig, "<span class=\"strike\"$1");
     34 		content = content.replace(/<\/strike>/ig, "</span>");
     35 		return content;
     36 	},
     37
     38 	validateUnderline: function(content) {
     39 		content = content.replace(/<u(>|\s+[^>]*>)/ig, "<em class=\"underline\"$1");
     40 		content = content.replace(/<\/u>/ig, "</em>");
     41 		return content;
     42 	},
     43
     44 	replaceTag: function(content, from, to) {
     45 		return content.replace(new RegExp("(</?)" + from + "(>|\\s+[^>]*>)", "ig"), "$1" + to + "$2");
     46 	},
     47
     48 	validateSelfClosingTags: function(content) {
     49 		return content.replace(/<(br|hr|img)([^>]*?)>/img, function(str, tag, attrs) {
     50 			return "<" + tag + attrs + " />"
     51 		});
     52 	},
     53
     54 	removeComments: function(content) {
     55 		return content.replace(/<!--.*?-->/img, '');
     56 	},
     57
     58 	removeDangerousElements: function(element) {
     59 		var scripts = $A(element.getElementsByTagName('SCRIPT')).reverse();
     60 		for(var i = 0; i < scripts.length; i++) {
     61 			scripts[i].parentNode.removeChild(scripts[i]);
     62 		}
     63 	},
     64
     65 	// TODO: very slow
     66 	applyWhitelist: function(content) {
     67 		var allowedTags = this.allowedTags;
     68 		var allowedAttrs = this.allowedAttrs;
     69
     70 		return content.replace(new RegExp("(</?)([^>]+?)(>|\\s+([^>]*?)(\\s?/?)>)", "g"), function(str, head, tag, tail, attrs, selfClosing) {
     71 			if(allowedTags.indexOf(tag) == -1) return '';
     72
     73 			if(attrs) {
     74 				attrs = attrs.replace(/(^|\s")([^"=]+)(\s|$)/g, '$1$2="$2"$3'); // for IE
     75
     76 				var sb = [];
     77 				var m = attrs.match(/([^=]+)="[^"]*?"/g);
     78 				for(var i = 0; i < m.length; i++) {
     79 					m[i] = m[i].strip();
     80 					var name = m[i].split('=')[0];
     81 					if(allowedAttrs.indexOf(name) != -1) sb.push(m[i]);
     82 				}
     83 				attrs = sb.join(' ');
     84 				if(attrs != '') attrs = ' ' + attrs;
     85 				return head + tag + attrs + selfClosing + '>';
     86 			} else {
     87 				return str;
     88 			}
     89 		});
     90 	},
     91
     92 	makeUrlsRelative: function(content) {
     93 		var curUrl = this.curUrl;
     94 		var urlParts = this.curUrlParts;
     95
     96 		// 1. find attributes and...
     97 		return content.replace(/(<\w+\s+)(\/|([^>]+?)(\/?))>/g, function(str, head, ignored, attrs, tail) {
     98 			if(attrs) {
     99 				// 2. validate URL part
    100 				attrs = attrs.replace(/(href|src)="([^"]+)"/g, function(str, name, url) {
    101 					// 3. first, make it absolute
    102 					var abs = null;
    103 					if(url.charAt(0) == '#') {
    104 						abs = urlParts.includeQuery + url;
    105 					} else if(url.charAt(0) == '?') {
    106 						abs = urlParts.includePath + url;
    107 					} else if(url.charAt(0) == '/') {
    108 						abs = urlParts.includeHost + url;
    109 					} else if(url.match(/^\w+:\/\//)) {
    110 						abs = url;
    111 					} else {
    112 						abs = urlParts.includeBase + url;
    113 					}
    114
    115 					// 4. make it relative by removing same part
    116 					var rel = abs;
    117
    118 					if(abs.indexOf(urlParts.includeQuery) == 0) {
    119 						rel = abs.substring(urlParts.includeQuery.length);
    120 					} else if(abs.indexOf(urlParts.includePath) == 0) {
    121 						rel = abs.substring(urlParts.includePath.length);
    122 					} else if(abs.indexOf(urlParts.includeBase) == 0) {
    123 						rel = abs.substring(urlParts.includeBase.length);
    124 					} else if(abs.indexOf(urlParts.includeHost) == 0) {
    125 						rel = abs.substring(urlParts.includeHost.length);
    126 					}
    127 					if(rel == '') rel = '#';
    128
    129 					return name + '="' + rel + '"';
    130 				});
    131
    132 				return head + attrs + tail + '>';
    133 			} else {
    134 				return str;
    135 			}
    136 		});
    137
    138 		return content;
    139 	},
    140
    141 	makeUrlsHostRelative: function(content) {
    142 		var curUrl = this.curUrl;
    143 		var urlParts = this.curUrlParts;
    144
    145 		// 1. find attributes and...
    146 		return content.replace(/(<\w+\s+)(\/|([^>]+?)(\/?))>/g, function(str, head, ignored, attrs, tail) {
    147 			if(attrs) {
    148 				// 2. validate URL part
    149 				attrs = attrs.replace(/(href|src)="([^"]+)"/g, function(str, name, url) {
    150 					// 3. first, make it absolute
    151 					var abs = null;
    152 					if(url.charAt(0) == '#') {
    153 						abs = urlParts.includeQuery + url;
    154 					} else if(url.charAt(0) == '?') {
    155 						abs = urlParts.includePath + url;
    156 					} else if(url.charAt(0) == '/') {
    157 						abs = urlParts.includeHost + url;
    158 					} else if(url.match(/^\w+:\/\//)) {
    159 						abs = url;
    160 					} else {
    161 						abs = urlParts.includeBase + url;
    162 					}
    163
    164 					// 4. make it relative by removing same part
    165 					var rel = abs;
    166 					if(abs.indexOf(urlParts.includeHost) == 0) {
    167 						rel = abs.substring(urlParts.includeHost.length);
    168 					}
    169 					if(rel == '') rel = '#';
    170
    171 					return name + '="' + rel + '"';
    172 				});
    173
    174 				return head + attrs + tail + '>';
    175 			} else {
    176 				return str;
    177 			}
    178 		});
    179
    180 		return content;
    181 	},
    182
    183 	makeUrlsAbsolute: function(content) {
    184 		var curUrl = this.curUrl;
    185 		var urlParts = this.curUrlParts;
    186
    187 		// 1. find attributes and...
    188 		return content.replace(/(<\w+\s+)(\/|([^>]+?)(\/?))>/g, function(str, head, ignored, attrs, tail) {
    189 			if(attrs) {
    190 				// 2. validate URL part
    191 				attrs = attrs.replace(/(href|src)="([^"]+)"/g, function(str, name, url) {
    192 					var abs = null;
    193 					if(url.charAt(0) == '#') {
    194 						abs = urlParts.includeQuery + url;
    195 					} else if(url.charAt(0) == '?') {
    196 						abs = urlParts.includePath + url;
    197 					} else if(url.charAt(0) == '/') {
    198 						abs = urlParts.includeHost + url;
    199 					} else if(url.match(/^\w+:\/\//)) {
    200 						abs = url;
    201 					} else {
    202 						abs = urlParts.includeBase + url;
    203 					}
    204
    205 					return name + '="' + abs + '"';
    206 				});
    207
    208 				return head + attrs + tail + '>';
    209 			} else {
    210 				return str;
    211 			}
    212 		});
    213 	}
    214 });
    215
    216 /**
    217  * Creates and returns instance of browser specific implementation.
    218  */
    219 xq.Validator.createInstance = function(curUrl, urlValidationMode, allowedTags, allowedAttrs) {
    220 	if(xq.Browser.isTrident) {
    221 		return new xq.ValidatorTrident(curUrl, urlValidationMode, allowedTags, allowedAttrs);
    222 	} else if(xq.Browser.isWebkit) {
    223 		return new xq.ValidatorWebkit(curUrl, urlValidationMode, allowedTags, allowedAttrs);
    224 	} else {
    225 		return new xq.ValidatorGecko(curUrl, urlValidationMode, allowedTags, allowedAttrs);
    226 	}
    227 }
    228