mirror of
https://github.com/Lastorder-DC/rhymix.git
synced 2026-01-19 03:09:55 +09:00
17223554 : xquared upgrade to 0.7
git-svn-id: http://xe-core.googlecode.com/svn/sandbox@4968 201d5d3c-b55e-5fd7-737f-ddc643e51545
This commit is contained in:
parent
5956e254e7
commit
7c3b336e41
59 changed files with 34562 additions and 8454 deletions
391
modules/editor/skins/xquared/javascripts/validator/Base.js
Normal file
391
modules/editor/skins/xquared/javascripts/validator/Base.js
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
/**
|
||||
* @namespace
|
||||
*/
|
||||
xq.validator = {}
|
||||
|
||||
/**
|
||||
* @requires Xquared.js
|
||||
* @requires Browser.js
|
||||
* @requires rdom/Factory.js
|
||||
*/
|
||||
xq.validator.Base = xq.Class(/** @lends xq.validator.Base.prototype */{
|
||||
/**
|
||||
* @constructs
|
||||
*/
|
||||
initialize: function(curUrl, urlValidationMode, whitelist) {
|
||||
xq.addToFinalizeQueue(this);
|
||||
xq.asEventSource(this, "Validator", ["Preprocessing", "BeforeDomValidation", "AfterDomValidation", "BeforeStringValidation", "AfterStringValidation", "BeforeDomInvalidation", "AfterDomInvalidation", "BeforeStringInvalidation", "AfterStringInvalidation"]);
|
||||
|
||||
this.whitelist = whitelist || xq.predefinedWhitelist;
|
||||
this.pRGB = xq.compilePattern("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)");
|
||||
|
||||
this.curUrl = curUrl;
|
||||
this.curUrlParts = curUrl ? curUrl.parseURL() : null;
|
||||
this.urlValidationMode = urlValidationMode;
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform validation on given element
|
||||
*
|
||||
* @param {Element} element Target element. It is not affected by validation.
|
||||
*
|
||||
* @returns {String} Validated HTML string
|
||||
*/
|
||||
validate: function(element, dontClone) {
|
||||
// DOM validation
|
||||
element = dontClone ? element : element.cloneNode(true);
|
||||
this._fireOnBeforeDomValidation(element);
|
||||
this.validateDom(element);
|
||||
this._fireOnAfterDomValidation(element);
|
||||
|
||||
// String validation
|
||||
var html = {value: element.innerHTML};
|
||||
this._fireOnBeforeStringValidation(html);
|
||||
html.value = this.validateString(html.value);
|
||||
this._fireOnAfterStringValidation(html);
|
||||
|
||||
return html.value;
|
||||
},
|
||||
|
||||
validateDom: function(element) {throw "Not implemented";},
|
||||
validateString: function(html) {throw "Not implemented";},
|
||||
|
||||
/**
|
||||
* Perform invalidation on given element to make the designmode works well.
|
||||
*
|
||||
* @param {String} html HTML string.
|
||||
* @returns {String} Invalidated HTML string
|
||||
*/
|
||||
invalidate: function(html) {
|
||||
// Preprocessing
|
||||
var html = {value: html};
|
||||
this._fireOnPreprocessing(html);
|
||||
|
||||
// DOM invalidation
|
||||
var element = document.createElement("DIV");
|
||||
element.innerHTML = html.value;
|
||||
this._fireOnBeforeDomInvalidation(element);
|
||||
this.invalidateDom(element);
|
||||
this._fireOnAfterDomInvalidation(element);
|
||||
|
||||
// String invalidation
|
||||
html.value = element.innerHTML;
|
||||
this._fireOnBeforeStringInvalidation(html);
|
||||
html.value = this.invalidateString(html.value);
|
||||
this._fireOnAfterStringInvalidation(html);
|
||||
|
||||
return html.value;
|
||||
},
|
||||
|
||||
invalidateDom: function(element) {throw "Not implemented"},
|
||||
invalidateString: function(html) {throw "Not implemented"},
|
||||
|
||||
/**
|
||||
* em.class="underline" -> u
|
||||
* span.class="strike" -> strike
|
||||
*/
|
||||
invalidateStrikesAndUnderlines: function(element) {
|
||||
var rdom = xq.rdom.Base.createInstance();
|
||||
rdom.setRoot(element);
|
||||
|
||||
var nameOfClassName = xq.Browser.isTrident ? "className" : "class";
|
||||
|
||||
var underlines = xq.getElementsByClassName(rdom.getRoot(), "underline", "em");
|
||||
var pUnderline = xq.compilePattern("(^|\\s)underline($|\\s)");
|
||||
var lenOfUnderlines = underlines.length;
|
||||
for(var i = 0; i < lenOfUnderlines; i++) {
|
||||
rdom.replaceTag("u", underlines[i]).removeAttribute(nameOfClassName);
|
||||
}
|
||||
|
||||
var strikes = xq.getElementsByClassName(rdom.getRoot(), "strike", "span")
|
||||
var pStrike = xq.compilePattern("(^|\\s)strike($|\\s)");
|
||||
var lenOfStrikes = strikes.length;
|
||||
for(var i = 0; i < lenOfStrikes; i++) {
|
||||
rdom.replaceTag("strike", strikes[i]).removeAttribute(nameOfClassName);
|
||||
}
|
||||
},
|
||||
|
||||
validateStrike: function(content) {
|
||||
content = content.replace(/<strike(>|\s+[^>]*>)/ig, "<span class=\"strike\"$1");
|
||||
content = content.replace(/<\/strike>/ig, "</span>");
|
||||
return content;
|
||||
},
|
||||
|
||||
validateUnderline: function(content) {
|
||||
content = content.replace(/<u(>|\s+[^>]*>)/ig, "<em class=\"underline\"$1");
|
||||
content = content.replace(/<\/u>/ig, "</em>");
|
||||
return content;
|
||||
},
|
||||
|
||||
replaceTag: function(content, from, to) {
|
||||
return content.replace(new RegExp("(</?)" + from + "(>|\\s+[^>]*>)", "ig"), "$1" + to + "$2");
|
||||
},
|
||||
|
||||
validateSelfClosingTags: function(content) {
|
||||
return content.replace(/<(br|hr|img|value)([^>]*?)>/img, function(str, tag, attrs) {
|
||||
return "<" + tag + attrs + " />"
|
||||
});
|
||||
},
|
||||
|
||||
validateFont: function(element) {
|
||||
var rdom = xq.rdom.Base.createInstance();
|
||||
rdom.setRoot(element);
|
||||
|
||||
// It should be reversed to deal with nested elements
|
||||
var fonts = element.getElementsByTagName('FONT');
|
||||
var fontSizes = ["xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"];
|
||||
var len = fonts.length - 1;
|
||||
for(var i = len; i >= 0; i--) {
|
||||
var font = fonts[i];
|
||||
var color = font.getAttribute('color');
|
||||
var backgroundColor = font.style.backgroundColor;
|
||||
var face = font.getAttribute('face');
|
||||
var size = fontSizes[parseInt(font.getAttribute('size')) % 8 - 1];
|
||||
|
||||
if(color || backgroundColor || face || size) {
|
||||
var span = rdom.replaceTag("span", font);
|
||||
span.removeAttribute('color');
|
||||
span.removeAttribute('face');
|
||||
span.removeAttribute('size');
|
||||
|
||||
if(color) span.style.color = color;
|
||||
if(backgroundColor) span.style.backgroundColor = backgroundColor;
|
||||
if(face) span.style.fontFamily = face;
|
||||
if(size) span.style.fontSize = size;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
invalidateFont: function(element) {
|
||||
var rdom = xq.rdom.Base.createInstance();
|
||||
rdom.setRoot(element);
|
||||
|
||||
// It should be reversed to deal with nested elements
|
||||
var spans = element.getElementsByTagName('SPAN');
|
||||
var fontSizes = {"xx-small":1, "x-small":2, "small":3, "medium":4, "large":5, "x-large":6, "xx-large":7};
|
||||
var len = spans.length - 1;
|
||||
for(var i = len; i >= 0; i--) {
|
||||
var span = spans[i];
|
||||
if(span.className === "strike") continue;
|
||||
|
||||
var color = span.style.color;
|
||||
var backgroundColor = span.style.backgroundColor;
|
||||
var face = span.style.fontFamily;
|
||||
var size = fontSizes[span.style.fontSize];
|
||||
|
||||
if(color || backgroundColor || face || size) {
|
||||
var font = rdom.replaceTag("font", span);
|
||||
font.style.cssText = "";
|
||||
|
||||
if(color) font.setAttribute('color', this.asRGB(color));
|
||||
if(backgroundColor) font.style.backgroundColor = backgroundColor;
|
||||
if(face) font.setAttribute('face', face);
|
||||
if(size) font.setAttribute('size', size);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
asRGB: function(color) {
|
||||
if(color.indexOf("#") === 0) return color;
|
||||
|
||||
var m = this.pRGB.exec(color);
|
||||
if(!m) return color;
|
||||
|
||||
var r = Number(m[1]).toString(16);
|
||||
var g = Number(m[2]).toString(16);
|
||||
var b = Number(m[3]).toString(16);
|
||||
|
||||
if(r.length === 1) r = "0" + r;
|
||||
if(g.length === 1) g = "0" + g;
|
||||
if(b.length === 1) b = "0" + b;
|
||||
|
||||
return "#" + r + g + b;
|
||||
},
|
||||
|
||||
removeComments: function(content) {
|
||||
return content.replace(/<!--.*?-->/img, '');
|
||||
},
|
||||
|
||||
removeDangerousElements: function(element) {
|
||||
var scripts = element.getElementsByTagName('SCRIPT');
|
||||
for(var i = scripts.length - 1; i >= 0; i--) {
|
||||
scripts[i].parentNode.removeChild(scripts[i]);
|
||||
}
|
||||
},
|
||||
|
||||
applyWhitelist: function(content) {
|
||||
var whitelist = this.whitelist;
|
||||
var allowedAttrs = null;
|
||||
|
||||
var p1 = xq.compilePattern("(^|\\s\")([^\"=]+)(\\s|$)", "g");
|
||||
var p2 = xq.compilePattern("(\\S+?)=\"[^\"]*\"", "g");
|
||||
return content.replace(new RegExp("(</?)([^>]+?)(>|\\s+([^>]*?)(\\s?/?)>)", "g"), function(str, head, tag, tail, attrs, selfClosing) {
|
||||
if(!(allowedAttrs = whitelist[tag])) return '';
|
||||
|
||||
if(attrs) {
|
||||
if(xq.Browser.isTrident) attrs = attrs.replace(p1, '$1$2="$2"$3');
|
||||
|
||||
var sb = [];
|
||||
var m = attrs.match(p2);
|
||||
for(var i = 0; i < m.length; i++) {
|
||||
var name = m[i].split('=')[0];
|
||||
if(allowedAttrs.indexOf(name) !== -1) sb.push(m[i]);
|
||||
}
|
||||
|
||||
if(sb.length) {
|
||||
attrs = sb.join(' ');
|
||||
return head + tag + ' ' + attrs + selfClosing + '>';
|
||||
} else {
|
||||
return head + tag + selfClosing + '>';
|
||||
}
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// TODO: very expansive
|
||||
makeUrlsRelative: function(content) {
|
||||
var curUrl = this.curUrl;
|
||||
var urlParts = this.curUrlParts;
|
||||
|
||||
var p1 = xq.compilePattern("(href|src)=\"([^\"]+)\"", "g");
|
||||
var p2 = xq.compilePattern("^\\w+://");
|
||||
|
||||
// 1. find attributes and...
|
||||
return content.replace(/(<\w+\s+)(\/|([^>]+?)(\/?))>/g, function(str, head, ignored, attrs, tail) {
|
||||
if(attrs) {
|
||||
// 2. validate URL part
|
||||
attrs = attrs.replace(p1, function(str, name, url) {
|
||||
// 3. first, make it absolute
|
||||
var abs = null;
|
||||
if(url.charAt(0) === '#') {
|
||||
abs = urlParts.includeQuery + url;
|
||||
} else if(url.charAt(0) === '?') {
|
||||
abs = urlParts.includePath + url;
|
||||
} else if(url.charAt(0) === '/') {
|
||||
abs = urlParts.includeHost + url;
|
||||
} else if(url.match(p2)) {
|
||||
abs = url;
|
||||
} else {
|
||||
abs = urlParts.includeBase + url;
|
||||
}
|
||||
|
||||
// 4. make it relative by removing same part
|
||||
var rel = abs;
|
||||
|
||||
if(abs === urlParts.includeHost) {
|
||||
rel = "/";
|
||||
} else if(abs.indexOf(urlParts.includeQuery) === 0) {
|
||||
rel = abs.substring(urlParts.includeQuery.length);
|
||||
} else if(abs.indexOf(urlParts.includePath) === 0) {
|
||||
rel = abs.substring(urlParts.includePath.length);
|
||||
} else if(abs.indexOf(urlParts.includeBase) === 0) {
|
||||
rel = abs.substring(urlParts.includeBase.length);
|
||||
} else if(abs.indexOf(urlParts.includeHost) === 0) {
|
||||
rel = abs.substring(urlParts.includeHost.length);
|
||||
}
|
||||
|
||||
if(rel === '') rel = '#';
|
||||
|
||||
return name + '="' + rel + '"';
|
||||
});
|
||||
|
||||
return head + attrs + tail + '>';
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
});
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
// TODO: very expansive
|
||||
makeUrlsHostRelative: function(content) {
|
||||
var curUrl = this.curUrl;
|
||||
var urlParts = this.curUrlParts;
|
||||
|
||||
var p1 = xq.compilePattern("(href|src)=\"([^\"]+)\"", "g");
|
||||
var p2 = xq.compilePattern("^\\w+://");
|
||||
|
||||
// 1. find attributes and...
|
||||
return content.replace(/(<\w+\s+)(\/|([^>]+?)(\/?))>/g, function(str, head, ignored, attrs, tail) {
|
||||
if(attrs) {
|
||||
// 2. validate URL part
|
||||
attrs = attrs.replace(p1, function(str, name, url) {
|
||||
// 3. first, make it absolute
|
||||
var abs = null;
|
||||
if(url.charAt(0) === '#') {
|
||||
abs = urlParts.includeQuery + url;
|
||||
} else if(url.charAt(0) === '?') {
|
||||
abs = urlParts.includePath + url;
|
||||
} else if(url.charAt(0) === '/') {
|
||||
abs = urlParts.includeHost + url;
|
||||
} else if(url.match(p2)) {
|
||||
abs = url;
|
||||
} else {
|
||||
abs = urlParts.includeBase + url;
|
||||
}
|
||||
|
||||
// 4. make it relative by removing same part
|
||||
var rel = abs;
|
||||
if(abs === urlParts.includeHost) {
|
||||
rel = "/";
|
||||
} else if(abs.indexOf(urlParts.includeQuery) === 0 && abs.indexOf("#") !== -1) {
|
||||
// same except for fragment-part?
|
||||
rel = abs.substring(abs.indexOf("#"));
|
||||
} else if(abs.indexOf(urlParts.includeHost) === 0) {
|
||||
// same host?
|
||||
rel = abs.substring(urlParts.includeHost.length);
|
||||
}
|
||||
|
||||
if(rel === '') rel = '#';
|
||||
|
||||
return name + '="' + rel + '"';
|
||||
});
|
||||
|
||||
return head + attrs + tail + '>';
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
});
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
// TODO: very expansive
|
||||
makeUrlsAbsolute: function(content) {
|
||||
var curUrl = this.curUrl;
|
||||
var urlParts = this.curUrlParts;
|
||||
|
||||
var p1 = xq.compilePattern("(href|src)=\"([^\"]+)\"", "g");
|
||||
var p2 = xq.compilePattern("^\\w+://");
|
||||
|
||||
// 1. find attributes and...
|
||||
return content.replace(/(<\w+\s+)(\/|([^>]+?)(\/?))>/g, function(str, head, ignored, attrs, tail) {
|
||||
if(attrs) {
|
||||
// 2. validate URL part
|
||||
attrs = attrs.replace(p1, function(str, name, url) {
|
||||
var abs = null;
|
||||
if(url.charAt(0) === '#') {
|
||||
abs = urlParts.includeQuery + url;
|
||||
} else if(url.charAt(0) === '?') {
|
||||
abs = urlParts.includePath + url;
|
||||
} else if(url.charAt(0) === '/') {
|
||||
abs = urlParts.includeHost + url;
|
||||
} else if(url.match(p2)) {
|
||||
abs = url;
|
||||
} else {
|
||||
abs = urlParts.includeBase + url;
|
||||
}
|
||||
|
||||
return name + '="' + abs + '"';
|
||||
});
|
||||
|
||||
return head + attrs + tail + '>';
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Creates and returns instance of browser specific implementation.
|
||||
*
|
||||
* @requires Xquared.js
|
||||
* @requires validator/Base.js
|
||||
* @requires validator/Trident.js
|
||||
* @requires validator/Gecko.js
|
||||
* @requires validator/Webkit.js
|
||||
*/
|
||||
xq.validator.Base.createInstance = function(curUrl, urlValidationMode, whitelist) {
|
||||
if(xq.Browser.isTrident) {
|
||||
return new xq.validator.Trident(curUrl, urlValidationMode, whitelist);
|
||||
} else if(xq.Browser.isWebkit) {
|
||||
return new xq.validator.Webkit(curUrl, urlValidationMode, whitelist);
|
||||
} else {
|
||||
return new xq.validator.Gecko(curUrl, urlValidationMode, whitelist);
|
||||
}
|
||||
}
|
||||
13
modules/editor/skins/xquared/javascripts/validator/Gecko.js
Normal file
13
modules/editor/skins/xquared/javascripts/validator/Gecko.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* @requires Xquared.js
|
||||
* @requires validator/W3.js
|
||||
*/
|
||||
xq.validator.Gecko = xq.Class(xq.validator.W3,
|
||||
/**
|
||||
* @name xq.validator.Gecko
|
||||
* @lends xq.validator.Gecko.prototype
|
||||
* @extends xq.validator.W3
|
||||
* @constructor
|
||||
*/
|
||||
{
|
||||
});
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* @requires Xquared.js
|
||||
* @requires validator/Base.js
|
||||
*/
|
||||
xq.validator.Trident = xq.Class(xq.validator.Base,
|
||||
/**
|
||||
* @name xq.validator.Trident
|
||||
* @lends xq.validator.Trident.prototype
|
||||
* @extends xq.validator.Base
|
||||
* @constructor
|
||||
*/
|
||||
{
|
||||
validateDom: function(element) {
|
||||
this.removeDangerousElements(element);
|
||||
this.validateFont(element);
|
||||
},
|
||||
|
||||
validateString: function(html) {
|
||||
try {
|
||||
html = this.validateStrike(html);
|
||||
html = this.validateUnderline(html);
|
||||
html = this.performFullValidation(html);
|
||||
} catch(ignored) {}
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
invalidateDom: function(element) {
|
||||
this.invalidateFont(element);
|
||||
this.invalidateStrikesAndUnderlines(element);
|
||||
},
|
||||
|
||||
invalidateString: function(html) {
|
||||
html = this.removeComments(html);
|
||||
return html;
|
||||
},
|
||||
|
||||
performFullValidation: function(html) {
|
||||
html = this.lowerTagNamesAndUniformizeQuotation(html);
|
||||
html = this.validateSelfClosingTags(html);
|
||||
html = this.applyWhitelist(html);
|
||||
|
||||
if(this.urlValidationMode === 'relative') {
|
||||
html = this.makeUrlsRelative(html);
|
||||
} else if(this.urlValidationMode === 'host_relative') {
|
||||
html = this.makeUrlsHostRelative(html);
|
||||
} else if(this.urlValidationMode === 'absolute') {
|
||||
// Trident always use absolute URL so we don't need to do anything.
|
||||
//
|
||||
// html = this.makeUrlsAbsolute(html);
|
||||
}
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
lowerTagNamesAndUniformizeQuotation: function(html) {
|
||||
this.pAttrQuotation1 = xq.compilePattern("\\s(\\w+?)=\\s+\"([^\"]+)\"", "mg");
|
||||
this.pAttrQuotation2 = xq.compilePattern("\\s(\\w+?)=([^ \"]+)", "mg");
|
||||
this.pAttrQuotation3 = xq.compilePattern("\\sNAME=\"(\\w+?)\" VALUE=\"(\\w+?)\"", "mg");
|
||||
|
||||
// Uniformize quotation, turn tag names and attribute names into lower case
|
||||
html = html.replace(/<(\/?)(\w+)([^>]*?)>/img, function(str, closingMark, tagName, attrs) {
|
||||
return "<" + closingMark + tagName.toLowerCase() + this.correctHtmlAttrQuotation(attrs) + ">";
|
||||
}.bind(this));
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
correctHtmlAttrQuotation: function(html) {
|
||||
html = html.replace(this.pAttrQuotation1, function (str, name, value) {return " " + name.toLowerCase() + '=' + '"' + value + '"'});
|
||||
html = html.replace(this.pAttrQuotation2, function (str, name, value) {return " " + name.toLowerCase() + '=' + '"' + value + '"'});
|
||||
html = html.replace(this.pAttrQuotation3, function (str, name, value) {return " name=\"" + name + "\" value=\"" + value + "\""});
|
||||
return html;
|
||||
}
|
||||
});
|
||||
84
modules/editor/skins/xquared/javascripts/validator/W3.js
Normal file
84
modules/editor/skins/xquared/javascripts/validator/W3.js
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* @requires Xquared.js
|
||||
* @requires validator/Base.js
|
||||
*/
|
||||
xq.validator.W3 = xq.Class(xq.validator.Base,
|
||||
/**
|
||||
* @name xq.validator.W3
|
||||
* @lends xq.validator.W3.prototype
|
||||
* @extends xq.validator.Base
|
||||
* @constructor
|
||||
*/
|
||||
{
|
||||
validateDom: function(element) {
|
||||
var rdom = xq.rdom.Base.createInstance();
|
||||
rdom.setRoot(element);
|
||||
this.removeDangerousElements(element);
|
||||
rdom.removePlaceHoldersAndEmptyNodes(element);
|
||||
this.validateFont(element);
|
||||
},
|
||||
|
||||
validateString: function(html) {
|
||||
try {
|
||||
html = this.replaceTag(html, "b", "strong");
|
||||
html = this.replaceTag(html, "i", "em");
|
||||
|
||||
html = this.validateStrike(html);
|
||||
html = this.validateUnderline(html);
|
||||
html = this.addNbspToEmptyBlocks(html);
|
||||
html = this.performFullValidation(html);
|
||||
html = this.insertNewlineBetweenBlockElements(html);
|
||||
} catch(ignored) {}
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
invalidateDom: function(element) {
|
||||
this.invalidateFont(element);
|
||||
this.invalidateStrikesAndUnderlines(element);
|
||||
},
|
||||
|
||||
invalidateString: function(html) {
|
||||
html = this.replaceTag(html, "strong", "b");
|
||||
html = this.replaceTag(html, "em", "i");
|
||||
html = this.removeComments(html);
|
||||
html = this.replaceNbspToBr(html);
|
||||
return html;
|
||||
},
|
||||
|
||||
performFullValidation: function(html) {
|
||||
html = this.validateSelfClosingTags(html);
|
||||
html = this.applyWhitelist(html);
|
||||
|
||||
if(this.urlValidationMode === 'relative') {
|
||||
html = this.makeUrlsRelative(html);
|
||||
} else if(this.urlValidationMode === 'host_relative') {
|
||||
html = this.makeUrlsHostRelative(html);
|
||||
} else if(this.urlValidationMode === 'absolute') {
|
||||
html = this.makeUrlsAbsolute(html);
|
||||
}
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
insertNewlineBetweenBlockElements: function(html) {
|
||||
var blocks = new xq.DomTree().getBlockTags().join("|");
|
||||
var regex = new RegExp("</(" + blocks + ")>([^\n])", "img");
|
||||
return html.replace(regex, '</$1>\n$2');
|
||||
},
|
||||
|
||||
addNbspToEmptyBlocks: function(content) {
|
||||
var blocks = new xq.DomTree().getBlockTags().join("|");
|
||||
var regex = new RegExp("<(" + blocks + ")>\\s*?</(" + blocks + ")>", "img");
|
||||
return content.replace(regex, '<$1> </$2>');
|
||||
},
|
||||
|
||||
replaceNbspToBr: function(content) {
|
||||
var blocks = new xq.DomTree().getBlockTags().join("|");
|
||||
|
||||
// Safari replaces into \xA0
|
||||
var regex = new RegExp("<(" + blocks + ")>( |\xA0)?</(" + blocks + ")>", "img");
|
||||
var rdom = xq.rdom.Base.createInstance();
|
||||
return content.replace(regex, '<$1>' + rdom.makePlaceHolderString() + '</$3>');
|
||||
}
|
||||
});
|
||||
145
modules/editor/skins/xquared/javascripts/validator/Webkit.js
Normal file
145
modules/editor/skins/xquared/javascripts/validator/Webkit.js
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* @requires Xquared.js
|
||||
* @requires validator/W3.js
|
||||
*/
|
||||
xq.validator.Webkit = xq.Class(xq.validator.W3,
|
||||
/**
|
||||
* @name xq.validator.Webkit
|
||||
* @lends xq.validator.Webkit.prototype
|
||||
* @extends xq.validator.W3
|
||||
* @constructor
|
||||
*/
|
||||
{
|
||||
validateDom: function(element) {
|
||||
var rdom = xq.rdom.Base.createInstance();
|
||||
rdom.setRoot(element);
|
||||
this.removeDangerousElements(element);
|
||||
rdom.removePlaceHoldersAndEmptyNodes(element);
|
||||
this.validateAppleStyleTags(element);
|
||||
},
|
||||
|
||||
validateString: function(html) {
|
||||
try {
|
||||
html = this.addNbspToEmptyBlocks(html);
|
||||
html = this.performFullValidation(html);
|
||||
html = this.insertNewlineBetweenBlockElements(html);
|
||||
} catch(ignored) {}
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
invalidateDom: function(element) {
|
||||
this.invalidateAppleStyleTags(element);
|
||||
},
|
||||
|
||||
invalidateString: function(html) {
|
||||
html = this.replaceTag(html, "strong", "b");
|
||||
html = this.replaceTag(html, "em", "i");
|
||||
html = this.removeComments(html);
|
||||
html = this.replaceNbspToBr(html);
|
||||
return html;
|
||||
},
|
||||
|
||||
validateAppleStyleTags: function(element) {
|
||||
var rdom = xq.rdom.Base.createInstance();
|
||||
rdom.setRoot(element);
|
||||
|
||||
var nodes = xq.getElementsByClassName(rdom.getRoot(), "apple-style-span");
|
||||
for(var i = 0; i < nodes.length; i++) {
|
||||
var node = nodes[i];
|
||||
|
||||
if(node.style.fontStyle === "italic") {
|
||||
// span -> em
|
||||
node = rdom.replaceTag("em", node);
|
||||
node.removeAttribute("class");
|
||||
node.style.fontStyle = "";
|
||||
} else if(node.style.fontWeight === "bold") {
|
||||
// span -> strong
|
||||
node = rdom.replaceTag("strong", node);
|
||||
node.removeAttribute("class");
|
||||
node.style.fontWeight = "";
|
||||
} else if(node.style.textDecoration === "underline") {
|
||||
// span -> em.underline
|
||||
node = rdom.replaceTag("em", node);
|
||||
node.className = "underline";
|
||||
node.style.textDecoration = "";
|
||||
} else if(node.style.textDecoration === "line-through") {
|
||||
// span -> span.strike
|
||||
node.className = "strike";
|
||||
node.style.textDecoration = "";
|
||||
} else if(node.style.verticalAlign === "super") {
|
||||
// span -> sup
|
||||
node = rdom.replaceTag("sup", node);
|
||||
node.removeAttribute("class");
|
||||
node.style.verticalAlign = "";
|
||||
} else if(node.style.verticalAlign === "sub") {
|
||||
// span -> sup
|
||||
node = rdom.replaceTag("sub", node);
|
||||
node.removeAttribute("class");
|
||||
node.style.verticalAlign = "";
|
||||
} else if(node.style.fontFamily) {
|
||||
// span -> span font-family
|
||||
node.removeAttribute("class");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
invalidateAppleStyleTags: function(element) {
|
||||
var rdom = xq.rdom.Base.createInstance();
|
||||
rdom.setRoot(element);
|
||||
|
||||
// span.strike -> span, span... -> span
|
||||
var spans = rdom.getRoot().getElementsByTagName("span");
|
||||
for(var i = 0; i < spans.length; i++) {
|
||||
var node = spans[i];
|
||||
if(node.className == "strike") {
|
||||
node.className = "Apple-style-span";
|
||||
node.style.textDecoration = "line-through";
|
||||
} else if(node.style.fontFamily) {
|
||||
node.className = "Apple-style-span";
|
||||
}
|
||||
// TODO: bg/fg/font-size
|
||||
}
|
||||
|
||||
// em -> span, em.underline -> span
|
||||
var ems = rdom.getRoot().getElementsByTagName("em");
|
||||
for(var i = 0; i < ems.length; i++) {
|
||||
var node = ems[i];
|
||||
node = rdom.replaceTag("span", node);
|
||||
if(node.className === "underline") {
|
||||
node.className = "apple-style-span";
|
||||
node.style.textDecoration = "underline";
|
||||
} else {
|
||||
node.className = "apple-style-span";
|
||||
node.style.fontStyle = "italic";
|
||||
}
|
||||
}
|
||||
|
||||
// strong -> span
|
||||
var strongs = rdom.getRoot().getElementsByTagName("strong");
|
||||
for(var i = 0; i < strongs.length; i++) {
|
||||
var node = strongs[i];
|
||||
node = rdom.replaceTag("span", node);
|
||||
node.className = "Apple-style-span";
|
||||
node.style.fontWeight = "bold";
|
||||
}
|
||||
|
||||
// sup -> span
|
||||
var sups = rdom.getRoot().getElementsByTagName("sup");
|
||||
for(var i = 0; i < sups.length; i++) {
|
||||
var node = sups[i];
|
||||
node = rdom.replaceTag("span", node);
|
||||
node.className = "Apple-style-span";
|
||||
node.style.verticalAlign = "super";
|
||||
}
|
||||
|
||||
// sub -> span
|
||||
var subs = rdom.getRoot().getElementsByTagName("sub");
|
||||
for(var i = 0; i < subs.length; i++) {
|
||||
var node = subs[i];
|
||||
node = rdom.replaceTag("span", node);
|
||||
node.className = "Apple-style-span";
|
||||
node.style.verticalAlign = "sub";
|
||||
}
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue