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