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