Created
June 17, 2013 12:33
-
-
Save hoehrmann/5796534 to your computer and use it in GitHub Desktop.
SVG Tidy snippet, originally http://esw.w3.org/SvgTidy/Snippets in 2005
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var properties = { | |
"alignment-baseline" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : false, | |
"ini" : "", | |
"key" : [] | |
}, | |
"baseline-shift" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : false, | |
"ini" : "baseline", | |
"key" : [] | |
}, | |
"clip" : { | |
"ani" : true, | |
"app" : [ | |
"foreignObject", | |
"image", | |
"marker", | |
"pattern", | |
"svg", | |
"symbol", | |
"use" | |
], | |
"inh" : false, | |
"ini" : "auto", | |
"key" : [] | |
}, | |
"clip-path" : { | |
"ani" : true, | |
"app" : [ | |
"a", | |
"circle", | |
"clipPath", | |
"defs", | |
"ellipse", | |
"g", | |
"image", | |
"line", | |
"marker", | |
"mask", | |
"path", | |
"pattern", | |
"polygon", | |
"polyline", | |
"rect", | |
"svg", | |
"switch", | |
"symbol", | |
"text", | |
"use" | |
], | |
"inh" : false, | |
"ini" : "none", | |
"key" : [] | |
}, | |
"clip-rule" : { | |
"ani" : true, | |
"app" : [ | |
"circle", | |
"ellipse", | |
"image", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"use" | |
], | |
"inh" : true, | |
"ini" : "nonzero", | |
"key" : [] | |
}, | |
"color" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"feDiffuseLighting", | |
"feFlood", | |
"feSpecularLighting", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"stop", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "", | |
"key" : [] | |
}, | |
"color-interpolation" : { | |
"ani" : true, | |
"app" : [ | |
"a", | |
"animateColor", | |
"circle", | |
"clipPath", | |
"defs", | |
"ellipse", | |
"g", | |
"image", | |
"line", | |
"marker", | |
"mask", | |
"path", | |
"pattern", | |
"polygon", | |
"polyline", | |
"rect", | |
"svg", | |
"switch", | |
"symbol", | |
"text", | |
"use" | |
], | |
"inh" : true, | |
"ini" : "sRGB", | |
"key" : [ | |
"sRGB", | |
"linearRGB" | |
] | |
}, | |
"color-interpolation-filters" : { | |
"ani" : true, | |
"app" : [ | |
"feBlend", | |
"feColorMatrix", | |
"feComponentTransfer", | |
"feComposite", | |
"feConvolveMatrix", | |
"feDiffuseLighting", | |
"feDisplacementMap", | |
"feFlood", | |
"feGaussianBlur", | |
"feImage", | |
"feMerge", | |
"feMorphology", | |
"feOffset", | |
"feSpecularLighting", | |
"feTile", | |
"feTurbulence" | |
], | |
"inh" : true, | |
"ini" : "linearRGB", | |
"key" : [ | |
"sRGB", | |
"linearRGB" | |
] | |
}, | |
"color-profile" : { | |
"ani" : true, | |
"app" : [ | |
"*", | |
"image" | |
], | |
"inh" : true, | |
"ini" : "auto", | |
"key" : [ | |
"sRGB" | |
] | |
}, | |
"color-rendering" : { | |
"ani" : true, | |
"app" : [ | |
"a", | |
"animateColor", | |
"circle", | |
"clipPath", | |
"defs", | |
"ellipse", | |
"g", | |
"image", | |
"line", | |
"marker", | |
"mask", | |
"path", | |
"pattern", | |
"polygon", | |
"polyline", | |
"rect", | |
"svg", | |
"switch", | |
"symbol", | |
"text", | |
"use" | |
], | |
"inh" : true, | |
"ini" : "auto", | |
"key" : [ | |
"optimizeSpeed", | |
"optimizeQuality" | |
] | |
}, | |
"cursor" : { | |
"ani" : true, | |
"app" : [ | |
"a", | |
"circle", | |
"clipPath", | |
"defs", | |
"ellipse", | |
"g", | |
"image", | |
"line", | |
"marker", | |
"mask", | |
"path", | |
"pattern", | |
"polygon", | |
"polyline", | |
"rect", | |
"svg", | |
"switch", | |
"symbol", | |
"text", | |
"use" | |
], | |
"inh" : true, | |
"ini" : "auto", | |
"key" : [] | |
}, | |
"direction" : { | |
"ani" : false, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "ltr", | |
"key" : [] | |
}, | |
"display" : { | |
"ani" : true, | |
"app" : [ | |
"a", | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"foreignObject", | |
"g", | |
"image", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"svg", | |
"switch", | |
"text", | |
"textPath", | |
"tref", | |
"tspan", | |
"use" | |
], | |
"inh" : false, | |
"ini" : "inline", | |
"key" : [] | |
}, | |
"dominant-baseline" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : false, | |
"ini" : "auto", | |
"key" : [] | |
}, | |
"enable-background" : { | |
"ani" : false, | |
"app" : [ | |
"a", | |
"clipPath", | |
"defs", | |
"g", | |
"marker", | |
"mask", | |
"pattern", | |
"svg", | |
"switch", | |
"symbol" | |
], | |
"inh" : false, | |
"ini" : "accumulate", | |
"key" : [] | |
}, | |
"fill" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "black", | |
"key" : [] | |
}, | |
"fill-opacity" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "1", | |
"key" : [] | |
}, | |
"fill-rule" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "nonzero", | |
"key" : [] | |
}, | |
"filter" : { | |
"ani" : true, | |
"app" : [ | |
"a", | |
"circle", | |
"clipPath", | |
"defs", | |
"ellipse", | |
"g", | |
"image", | |
"line", | |
"marker", | |
"mask", | |
"path", | |
"pattern", | |
"polygon", | |
"polyline", | |
"rect", | |
"svg", | |
"switch", | |
"symbol", | |
"text", | |
"use" | |
], | |
"inh" : false, | |
"ini" : "none", | |
"key" : [] | |
}, | |
"flood-color" : { | |
"ani" : true, | |
"app" : [ | |
"feFlood" | |
], | |
"inh" : false, | |
"ini" : "black", | |
"key" : [ | |
"currentColor" | |
] | |
}, | |
"flood-opacity" : { | |
"ani" : true, | |
"app" : [ | |
"feFlood" | |
], | |
"inh" : false, | |
"ini" : "1", | |
"key" : [] | |
}, | |
"font" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "", | |
"key" : [] | |
}, | |
"font-family" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "", | |
"key" : [] | |
}, | |
"font-size" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "medium", | |
"key" : [] | |
}, | |
"font-size-adjust" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "none", | |
"key" : [] | |
}, | |
"font-stretch" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "normal", | |
"key" : [] | |
}, | |
"font-style" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "normal", | |
"key" : [] | |
}, | |
"font-variant" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "normal", | |
"key" : [] | |
}, | |
"font-weight" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "normal", | |
"key" : [] | |
}, | |
"glyph-orientation-horizontal" : { | |
"ani" : false, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "0deg", | |
"key" : [] | |
}, | |
"glyph-orientation-vertical" : { | |
"ani" : false, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "auto", | |
"key" : [] | |
}, | |
"image-rendering" : { | |
"ani" : true, | |
"app" : [ | |
"*" | |
], | |
"inh" : true, | |
"ini" : "auto", | |
"key" : [ | |
"optimizeSpeed", | |
"optimizeQuality" | |
] | |
}, | |
"kerning" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "auto", | |
"key" : [] | |
}, | |
"letter-spacing" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "normal", | |
"key" : [] | |
}, | |
"lighting-color" : { | |
"ani" : true, | |
"app" : [ | |
"feDiffuseLighting", | |
"feSpecularLighting" | |
], | |
"inh" : false, | |
"ini" : "white", | |
"key" : [ | |
"currentColor" | |
] | |
}, | |
"marker" : { | |
"ani" : true, | |
"app" : [ | |
"line", | |
"path", | |
"polygon", | |
"polyline" | |
], | |
"inh" : true, | |
"ini" : "", | |
"key" : [] | |
}, | |
"marker-end" : { | |
"ani" : true, | |
"app" : [ | |
"line", | |
"path", | |
"polygon", | |
"polyline" | |
], | |
"inh" : true, | |
"ini" : "none", | |
"key" : [] | |
}, | |
"marker-mid" : { | |
"ani" : true, | |
"app" : [ | |
"line", | |
"path", | |
"polygon", | |
"polyline" | |
], | |
"inh" : true, | |
"ini" : "none", | |
"key" : [] | |
}, | |
"marker-start" : { | |
"ani" : true, | |
"app" : [ | |
"line", | |
"path", | |
"polygon", | |
"polyline" | |
], | |
"inh" : true, | |
"ini" : "none", | |
"key" : [] | |
}, | |
"mask" : { | |
"ani" : true, | |
"app" : [ | |
"a", | |
"circle", | |
"clipPath", | |
"defs", | |
"ellipse", | |
"g", | |
"image", | |
"line", | |
"marker", | |
"mask", | |
"path", | |
"pattern", | |
"polygon", | |
"polyline", | |
"rect", | |
"svg", | |
"switch", | |
"symbol", | |
"text", | |
"use" | |
], | |
"inh" : false, | |
"ini" : "none", | |
"key" : [] | |
}, | |
"opacity" : { | |
"ani" : true, | |
"app" : [ | |
"a", | |
"circle", | |
"clipPath", | |
"defs", | |
"ellipse", | |
"g", | |
"image", | |
"line", | |
"marker", | |
"mask", | |
"path", | |
"pattern", | |
"polygon", | |
"polyline", | |
"rect", | |
"svg", | |
"switch", | |
"symbol", | |
"text", | |
"use" | |
], | |
"inh" : false, | |
"ini" : "1", | |
"key" : [] | |
}, | |
"overflow" : { | |
"ani" : true, | |
"app" : [ | |
"foreignObject", | |
"image", | |
"marker", | |
"pattern", | |
"svg", | |
"symbol", | |
"use" | |
], | |
"inh" : false, | |
"ini" : "", | |
"key" : [] | |
}, | |
"pointer-events" : { | |
"ani" : true, | |
"app" : [ | |
"circle", | |
"ellipse", | |
"image", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"use" | |
], | |
"inh" : true, | |
"ini" : "visiblePainted", | |
"key" : [ | |
"visiblePainted", | |
"visibleFill", | |
"visibleStroke" | |
] | |
}, | |
"shape-rendering" : { | |
"ani" : true, | |
"app" : [ | |
"circle", | |
"ellipse", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect" | |
], | |
"inh" : true, | |
"ini" : "auto", | |
"key" : [ | |
"optimizeSpeed", | |
"crispEdges", | |
"geometricPrecision" | |
] | |
}, | |
"stop-color" : { | |
"ani" : true, | |
"app" : [ | |
"stop" | |
], | |
"inh" : false, | |
"ini" : "black", | |
"key" : [ | |
"currentColor" | |
] | |
}, | |
"stop-opacity" : { | |
"ani" : true, | |
"app" : [ | |
"stop" | |
], | |
"inh" : false, | |
"ini" : "1", | |
"key" : [] | |
}, | |
"stroke" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "none", | |
"key" : [] | |
}, | |
"stroke-dasharray" : { | |
"ani" : false, | |
"app" : [ | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "none", | |
"key" : [] | |
}, | |
"stroke-dashoffset" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "0", | |
"key" : [] | |
}, | |
"stroke-linecap" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "butt", | |
"key" : [] | |
}, | |
"stroke-linejoin" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "miter", | |
"key" : [] | |
}, | |
"stroke-miterlimit" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "4", | |
"key" : [] | |
}, | |
"stroke-opacity" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "1", | |
"key" : [] | |
}, | |
"stroke-width" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "1", | |
"key" : [] | |
}, | |
"text-anchor" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "start", | |
"key" : [] | |
}, | |
"text-decoration" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : false, | |
"ini" : "none", | |
"key" : [] | |
}, | |
"text-rendering" : { | |
"ani" : true, | |
"app" : [ | |
"text" | |
], | |
"inh" : true, | |
"ini" : "auto", | |
"key" : [ | |
"optimizeSpeed", | |
"optimizeLegibility", | |
"geometricPrecision" | |
] | |
}, | |
"unicode-bidi" : { | |
"ani" : false, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : false, | |
"ini" : "normal", | |
"key" : [] | |
}, | |
"visibility" : { | |
"ani" : true, | |
"app" : [ | |
"a", | |
"altGlyph", | |
"circle", | |
"ellipse", | |
"image", | |
"line", | |
"path", | |
"polygon", | |
"polyline", | |
"rect", | |
"text", | |
"textPath", | |
"tref", | |
"tspan", | |
"use" | |
], | |
"inh" : true, | |
"ini" : "visible", | |
"key" : [] | |
}, | |
"word-spacing" : { | |
"ani" : true, | |
"app" : [ | |
"altGlyph", | |
"text", | |
"textPath", | |
"tref", | |
"tspan" | |
], | |
"inh" : true, | |
"ini" : "normal", | |
"key" : [] | |
}, | |
"writing-mode" : { | |
"ani" : false, | |
"app" : [ | |
"text" | |
], | |
"inh" : true, | |
"ini" : "lr-tb", | |
"key" : [] | |
} | |
}; | |
var elementPropertyMap = new Array(); | |
for (var x in properties) | |
{ | |
for (var y = 0; y < properties[x]["app"].length; ++y) | |
{ | |
var name = properties[x]["app"][y]; | |
if (!elementPropertyMap[name]) | |
elementPropertyMap[name] = new Array(); | |
elementPropertyMap[name][x] = true; | |
} | |
} | |
// TODO: namespace name constants should probably be declared in | |
// just one global script file... | |
var svgns = "http://www.w3.org/2000/svg"; | |
var xlinkns = "http://www.w3.org/1999/xlink"; | |
var csns = "http://example.org/computed-styles"; | |
var ssns = "http://example.org/specified-styles"; | |
/** | |
* ... | |
* | |
* @author Bjoern Hoehrmann | |
* @version $Id$ | |
* | |
* @param style the CSSStyleDeclaration holding the properties. | |
* | |
* @returns ... | |
* | |
*/ | |
function CSSStyleDeclarationHasCompetingDecls(style) | |
{ | |
var test = new Array(); | |
// build a hash from the property names | |
for (var i = 0; i < style.length; ++i) | |
test[style.item(i)] = 0; | |
// return whether there are more declarations in the | |
// block than unique names reported by the accessors | |
return style.length > test.length; | |
} | |
/** | |
* ... | |
* | |
* @author Bjoern Hoehrmann | |
* @version $Id$ | |
* | |
* @param element the element the attributes should be added to. | |
* @param style the CSSStyleDeclaration holding the properties. | |
* @param ns the namespace for the attribute | |
* | |
* @returns element | |
*/ | |
function CSSStyleDeclarationToAttributesSimple(element, style, ns) | |
{ | |
// return if there is nothing to do | |
if (!element || !style) | |
return element; | |
for (var i = 0; i < style.length; ++i) | |
{ | |
var name = style.item(i); | |
var valu = style.getPropertyCSSValue(name); | |
valu = canonicalCSSValue(element, name, valu); | |
// TODO: See remarks in CSSStyleDeclarationToAttributesBrute | |
// Add the property as new attribute in no namespace | |
element.setAttributeNS(ns, name, valu); | |
} | |
// return the element | |
return element; | |
} | |
/** | |
* ... | |
* | |
* @author Bjoern Hoehrmann | |
* @version $Id$ | |
* | |
* @param element the element the attributes should be added to. | |
* @param style the CSSStyleDeclaration holding the properties. | |
* @param ns the namespace for the attribute | |
* | |
* @returns element | |
*/ | |
function CSSStyleDeclarationToAttributesBrute(element, style, ns) | |
{ | |
// return if there is nothing to do | |
if (!element || !style) | |
return element; | |
// remember the old cssText to restore the declaration block | |
var oldCssText = style.cssText; | |
// local cascade memory | |
var importance = new Array(); | |
// track whether properties can be removed | |
var canRemove = true; | |
while (style.length) | |
{ | |
try | |
{ | |
var name = style.item(0); | |
var valu = style.getPropertyCSSValue(name); | |
var impo = style.getPropertyPriority(name) ? true : false; | |
// ... | |
valu = canonicalCSSValue(element, name, valu); | |
// skip this declaration if previous had !important and this | |
// one does not as the important declaration overrides it | |
if (importance[name] && !impo) | |
{ | |
style.removeProperty(name); | |
continue; | |
} | |
// remember whether the declaration had !important | |
importance[name] = impo; | |
// TODO: This should first check whether the attribute may | |
// be added to this element without making the document non- | |
// conforming. | |
// TODO: If the attribute may not be added there might be | |
// problems with inheritance if there are elements which | |
// cannot take the property but allow descendant elements | |
// that take the property as those could inherit the | |
// value. Though, if there is such a case that might be a | |
// bug in the specification. | |
// remove the property for now to access declarations for | |
// the same property that some implementations incorrectly | |
// include in the CSSStyleDeclaration. If this fails the | |
// error will be caught and iterative access is attempted. | |
try | |
{ | |
style.removeProperty(name); | |
} | |
catch (e) | |
{ | |
// Removing the property failed so try the simpler | |
// annotation method | |
return CSSStyleDeclarationToAttributesSimple(element, style, ns); | |
} | |
// Add the property as new attribute in no namespace | |
element.setAttributeNS(ns, name, valu); | |
} | |
catch (e) | |
{ | |
// ignore errors | |
break; | |
} | |
} | |
// restore the declaration block assuming that style.cssText | |
// had a proper representation of the declaration block | |
style.cssText = oldCssText; | |
// return the element | |
return element; | |
} | |
/** | |
* ... | |
* | |
* @author Bjoern Hoehrmann | |
* @version $Id$ | |
* | |
* @param element the element the attributes should be added to. | |
* @param style the CSSStyleDeclaration holding the properties. | |
* @param ns the namespace for the attribute | |
* | |
* @returns element | |
*/ | |
function CSSStyleDeclarationToAttributes(element, style, ns) | |
{ | |
if (!element || !style) | |
return null; | |
// If there are multiple declarations for the same | |
// property and the implementation is non-compliant | |
if (CSSStyleDeclarationHasCompetingDecls(style)) | |
{ | |
// Try hard to get the winning declaration | |
CSSStyleDeclarationToAttributesBrute(element, style, ns); | |
} | |
else | |
{ | |
// otherwise use a simple method to find it | |
CSSStyleDeclarationToAttributesSimple(element, style, ns); | |
} | |
// return the element | |
return element; | |
} | |
/** | |
* Converts a CSSFontFaceRule object to its SVG representation | |
* | |
* @author Bjoern Hoehrmann | |
* @version $Id$ | |
* | |
* @param ff an object that implements CSSFontFaceRule | |
* | |
* @returns a new svg:font-face element or null | |
*/ | |
function CSSFontFaceRuleToSVGElements(ff) | |
{ | |
if (!ff || !ff.style) | |
return null; | |
var style = ff.style; | |
// create the new svg:font-face element | |
var svgff = document.createElementNS(svgns, "font-face"); | |
// add CSS properties as attributes | |
CSSStyleDeclarationToAttributes(svgff, style); | |
// TODO: Once CSSStyleDeclarationToAttributes checks whether | |
// the property may be added this becomes re-dundant | |
// SVG represents the font-face-src and definition-src font | |
// descriptors as child elements, not as attributes | |
if (svgff.hasAttributeNS(null, "src")) | |
svgff.removeAttributeNS(null, "src"); | |
if (svgff.hasAttributeNS(null, "definition-src")) | |
svgff.removeAttributeNS(null, "definition-src"); | |
// TODO: DOM Level 2 Style does not define how to represent the | |
// src descriptor and e.g. ASV6 makes the content only available | |
// through the cssText property, which would then require to | |
// implement a special parser for it... The following code | |
// assumes that such a function exists that returns a list of | |
// associative arrays with "uri", "formats", and "name" fields. | |
var srcArray = new Array(); | |
// Create new svg:font-face-src element | |
var ffSrc = document.createElementNS(svgns, "font-face-src"); | |
// Iterate over the src descriptor parts | |
for (var i = 0; i < srcArray.length; ++i) | |
{ | |
var srcDesc = srcArray[i]; | |
if (srcDesc["uri"] !== null) | |
{ | |
// Create new svg:font-face-src element | |
var ffUri = | |
document.createElementNS(svgns, "font-face-uri"); | |
// TODO: This might need to ensure that the base URI | |
// is set correctly, subject to what the parsing | |
// routine gurantees | |
// set the xlink:href attribute | |
ffUri.setAttributeNS(xlinkns, "href", srcDesc["uri"]); | |
// retrieve array of format strings | |
var formats = srcDesc["formats"]; | |
// skip if there are no formats | |
if (!formats) | |
continue; | |
// Append all formats as elements | |
for (var j = 0; j < formats.length; ++j) | |
{ | |
// Create new svg:font-face-format element | |
var ffFormat = | |
document.createElementNS(svgns, "font-face-format"); | |
// set the format attribute | |
ffFormat.setAttributeNS(null, "string", formats[i]); | |
// append the svg:font-face-format element | |
ffUri.appendChild(ffFormat); | |
} | |
// append the svg:font-face-uri element | |
ffSrc.appendChild(ffUri); | |
// next item | |
continue; | |
} | |
// no URI has been specified, so there must be a name | |
var ffName = | |
document.createElementNS(svgns, "font-face-name"); | |
// add the name as name attribute | |
ffName.setAttributeNS(null, "name", srcDesc["name"]); | |
// append the svg:font-face-name element | |
ffSrc.appendChild(ffName); | |
} | |
// append the svg:font-face-src to the svg:font-face element | |
// if there was a proper src descriptor in the style sheet. | |
if (ffSrc.hasChildNodes()) | |
svgff.appendChild(ffSrc); | |
// TODO: This fails if there are multiple definition-src | |
// descriptors and the implementation does not propertly | |
// collapse such declarations, see the remarks in the | |
// CSSStyleDeclarationToAttributes description. | |
// Get the CSSValue of the definition-src descriptor | |
var defSrc = style.getPropertyCSSValue("definition-src"); | |
// TODO: 1 is CSS_PRIMITIVE_VALUE and 20 is CSS_URI | |
// If there is a definition-src descriptor of the right type... | |
if (defSrc && defSrc.cssValueType == 1 && defSrc.primitiveType == 20) | |
{ | |
var defUri = defSrc.getStringValue(); | |
// TODO: This needs to ensure that relative URIs are made | |
// relative to the base URI of the svgff node, otherwise, | |
// if the base URI of the svgff node and the @font-face | |
// differ, this would break the reference. This might re- | |
// quire to change the prototype of the function to allow | |
// passing base URIs... | |
// create the new svg:definition-src element | |
var svgDefSrc = | |
document.createElementNS(svgns, "definition-src"); | |
// set the definition-src URI | |
svgDefSrc.setAttributeNS(xlinkns, "href", defUri); | |
// add it to the end of the svg:font-face element | |
svgff.appendChild(svgDefSrc); | |
} | |
return svgff; | |
} | |
/** | |
* Converts a SVGColorProfileRule object to its SVG representation | |
* | |
* @author Bjoern Hoehrmann | |
* @version $Id$ | |
* | |
* @param cp an object that implements SVGColorProfileRule | |
* | |
* @returns a new svg:color-profile element or null | |
*/ | |
function SVGColorProfileRuleToSVGElements(cp) | |
{ | |
// return null if nothing to do | |
if (!cp) | |
return null; | |
// create new svg:color-profile element | |
var svgcp = document.createElementNS(svgns, "color-profile"); | |
// determine rendering-intent keyword | |
var ri = { | |
1: "auto", | |
2: "perceptual", | |
3: "relative-colorimetric", | |
4: "saturation", | |
5: "absolute-colorimetric" | |
}[cp.renderingIntent]; | |
// | |
if (ri) | |
svgcp.setAttributeNS(null, "rendering-intent", ri); | |
// | |
if (cp.name !== null) | |
svgcp.setAttributeNS(null, "name", cp.name); | |
// TODO: parse src descriptor and add xlink:href, local | |
// attributes to the element | |
return svgcp; | |
} | |
/** | |
* Drop all svg:style elements and <?xml-stylesheet?> PIs | |
* | |
* @author Bjoern Hoehrmann | |
* @version $Id$ | |
* | |
* @returns void | |
*/ | |
function dropAllStyles() | |
{ | |
// drop xml-stylesheet processing instructions assuming that the | |
// document is xml-stylesheet conforming and uses them only pre- | |
// ceding the root element. | |
for (var node = document.firstChild; node; node = node.nextSibling) | |
{ | |
if (node.nodeType != 7 || node.nodeName != "xml-stylesheet") | |
continue; | |
node.parentNode.removeChild(node); | |
} | |
// get all style elements | |
var styleElements = document.getElementsByTagNameNS(svgns, "style"); | |
// iterate over the style elements | |
for (var i = 0; i < styleElements.length; ++i) | |
{ | |
var element = styleElements.item(i); | |
// and drop them | |
element.parentNode.removeChild(element); | |
} | |
} | |
function canonicalColorPrimitive(element, propName, value) | |
{ | |
if (!value) | |
return null; | |
// TODO: check if this works in all cases... | |
var fV = null; | |
switch (value.primitiveType) | |
{ | |
case 1: // CSS_NUMBER | |
fV = value.getFloatValue(1); | |
break; | |
case 2: // CSS_PERCENTAGE | |
fV = value.getFloatValue(2); | |
fV *= 256 / 100; | |
break; | |
default: | |
break; | |
} | |
// canonical value cannot be determined | |
if (fV === null) | |
return null; | |
// round | |
fV = Math.round(fV); | |
// clip | |
if (fV > 255) | |
fV = 255; | |
// clip | |
if (fV < 0) | |
fV = 0; | |
// convert to hex | |
var sV = fV.toString(16).toUpperCase(); | |
// prepend zero | |
if (sV.length < 2) | |
sV = "0" + sV; | |
return sV; | |
} | |
function canonicalRGBColor(element, propName, value) | |
{ | |
// return if nothing to do | |
if (!value) | |
return null; | |
// TODO: The canonical form should probably be configurable, e.g. | |
// some people might not want to loose color information outside | |
// the sRGB range or prefer "black" over "#000000", etc. | |
// canonical red | |
var r = canonicalColorPrimitive(element, propName, value.red); | |
// canonical green | |
var g = canonicalColorPrimitive(element, propName, value.green); | |
// canonical blue | |
var b = canonicalColorPrimitive(element, propName, value.blue); | |
// yields in #HHHHHH | |
return "#" + r + g + b; | |
} | |
function canonicalCSSPrimitiveValue(element, propName, value) | |
{ | |
if (!value) | |
return null; | |
var retVal = null; | |
switch (value.primitiveType) | |
{ | |
case 0: // CSS_UNKNOWN | |
case 1: // CSS_NUMBER | |
case 2: // CSS_PERCENTAGE | |
case 3: // CSS_EMS | |
case 4: // CSS_EXS | |
case 5: // CSS_PX | |
case 6: // CSS_CM | |
case 7: // CSS_MM | |
case 8: // CSS_IN | |
case 9: // CSS_PT | |
case 10: // CSS_PC | |
break; | |
case 11: // CSS_DEG | |
case 12: // CSS_RAD | |
case 13: // CSS_GRAD | |
// TODO: this requires checking whether the property is a | |
// CSS property or a SVG property as only SVG properties | |
// may omit the unit identifier. There are however no such | |
// properties in SVG 1.1 so this should be safe for now. | |
retVal = new String(value.getFloatValue(11)); // CSS_DEG | |
break; | |
case 14: // CSS_MS | |
case 15: // CSS_S | |
case 16: // CSS_HZ | |
case 17: // CSS_KHZ | |
case 18: // CSS_DIMENSION | |
case 19: // CSS_STRING | |
case 20: // CSS_URI | |
break; | |
case 21: // CSS_IDENT | |
retVal = | |
mapKeywordCase(element, propName, value.getStringValue()); | |
break; | |
case 22: // CSS_ATTR | |
case 23: // CSS_COUNTER | |
break; | |
case 24: // CSS_RECT | |
// ... | |
break; | |
case 25: // CSS_RGBCOLOR | |
retVal = canonicalRGBColor(element, propName, value); | |
break; | |
default: | |
break; | |
} | |
if (retVal === null) | |
return value.cssText; | |
return retVal; | |
} | |
function canonicalCSSValueList(element, propName, value) | |
{ | |
// TODO: implement... | |
return value.cssText; | |
} | |
function canonicalSVGPaint(element, propName, value) | |
{ | |
if (!value) | |
return null; | |
var retVal = null; | |
switch (value.paintType) | |
{ | |
case 0: // SVG_PAINTTYPE_UNKNOWN | |
break; | |
case 1: // SVG_PAINTTYPE_RGBCOLOR | |
retVal = canonicalRGBColor(element, propName, value.RGBColor); // TODO: should be rgbColor | |
break; | |
case 2: // SVG_PAINTTYPE_RGBCOLOR_ICCCOLOR | |
case 101: // SVG_PAINTTYPE_NONE | |
break; | |
case 102: // SVG_PAINTTYPE_CURRENTCOLOR | |
retVal = "currentColor"; | |
break; | |
case 103: // SVG_PAINTTYPE_URI_NONE | |
case 104: // SVG_PAINTTYPE_URI_CURRENTCOLOR | |
case 105: // SVG_PAINTTYPE_URI_RGBCOLOR | |
case 106: // SVG_PAINTTYPE_URI_RGBCOLOR_ICCCOLOR | |
case 107: // SVG_PAINTTYPE_URI | |
default: | |
break; | |
} | |
if (retVal === null) | |
return value.cssText; | |
return retVal; | |
} | |
function canonicalSVGColor(element, propName, value) | |
{ | |
if (!value) | |
return null; | |
var retVal = null; | |
switch (value.colorType) | |
{ | |
case 0: // SVG_COLORTYPE_UNKNOWN | |
break; | |
case 1: // SVG_COLORTYPE_RGBCOLOR | |
retVal = canonicalRGBColor(element, propName, value.RGBColor); // TODO: should be rgbColor | |
break; | |
case 2: // SVG_COLORTYPE_RGBCOLOR_ICCCOLOR | |
break; | |
case 3: // SVG_COLORTYPE_CURRENTCOLOR | |
retVal = "currentColor"; | |
break; | |
default: | |
break; | |
} | |
if (retVal === null) | |
return value.cssText; | |
return retVal; | |
} | |
function canonicalCSSCustom(element, propName, value) | |
{ | |
// SVG uses CSS_CUSTOM to represent SVGPaint and SVGColor | |
// in the Style DOM. In ECMAScript there is however no way | |
// to cast the CSSValue into a SVGPaint or SVGColor, so | |
// this does some trial and error to figure out the type... | |
try | |
{ | |
if (value.paintType !== null) | |
return canonicalSVGPaint(element, propName, value); | |
} | |
catch (e) | |
{ | |
// ignore errors | |
} | |
try | |
{ | |
if (value.colorType !== null) | |
return canonicalSVGColor(element, propName, value); | |
} | |
catch (e) | |
{ | |
// ignore errors | |
} | |
return value.cssText; | |
} | |
function canonicalCSSValue(element, propName, value) | |
{ | |
// TODO: check element/propName/value | |
if (!value || !propName) | |
return null; | |
// CSS_INHERIT | |
if (value.cssValueType == 0) | |
{ | |
return "inherit"; | |
} | |
// CSS_PRIMITIVE_VALUE | |
else if (value.cssValueType == 1) | |
{ | |
return canonicalCSSPrimitiveValue(element, propName, value); | |
} | |
// CSS_VALUE_LIST | |
else if (value.cssValueType == 2) | |
{ | |
return canonicalCSSValueList(element, propName, value); | |
} | |
// CSS_CUSTOM | |
else if (value.cssValueType == 3) | |
{ | |
return canonicalCSSCustom(element, propName, value); | |
} | |
return null; | |
} | |
function atRulesToElements() | |
{ | |
// @@ | |
for (var i = 0; i < document.styleSheets.length; ++i) | |
{ | |
var ss = document.styleSheets.item(i); | |
for (var j = 0; j < ss.cssRules.length; ++j) | |
{ | |
// ... | |
} | |
} | |
return props; | |
} | |
function mapKeywordCase(element, propName, keyword) | |
{ | |
// TODO: currentColor is handled elsewhere. | |
// TODO: this needs a better table... | |
// TODO: check args... | |
var newKey = { | |
"flood-color" : { "currentcolor" : "currentColor" }, | |
"lighting-color" : { "currentcolor" : "currentColor" }, | |
"stop-color" : { "currentcolor" : "currentColor" }, | |
"shape-rendering" : { "optimizespeed" : "optimizeSpeed", | |
"crispedges" : "crispEdges", | |
"geometricprecision" : "geometricPrecision" }, | |
"text-rendering" : { "optimizespeed" : "optimizeSpeed", | |
"optimizelegibility" : "optimizeLegibility", | |
"geometricprecision" : "geometricPrecision" }, | |
"color-rendering" : { "optimizespeed" : "optimizeSpeed", | |
"optimizequality" : "optimizeQuality" }, | |
"image-rendering" : { "optimizespeed" : "optimizeSpeed", | |
"optimizequality" : "optimizeQuality" }, | |
"color-profile" : { "srgb" : "sRGB" }, | |
"color-interpolation" : { "srgb" : "sRGB", | |
"linearrgb" : "linearRGB" }, | |
"color-interpolation-filters" : { "srgb" : "sRGB", | |
"linearrgb" : "linearRGB" }, | |
"pointer-events" : { "visiblepainted" : "visiblePainted", | |
"visiblefill" : "visibleFill", | |
"visiblestroke" : "visibleStroke" } | |
}[propName]; | |
if (!newKey || !newKey[keyword]) | |
return null; | |
return newKey[keyword]; | |
} | |
function pushInScopePresAttrs(element, presAttrStack) | |
{ | |
var attrs = element.attributes; | |
// return if nothing to do | |
if (!attrs) | |
return; | |
// iterate over the attributes to find the presentation | |
// attributes on the current element and add them to the | |
// presentation attribute stack | |
for (var j = 0; j < attrs.length; ++j) | |
{ | |
var attr = attrs.item(j); | |
// skip if in the wrong namespace | |
if (attr.namespaceURI != null) | |
continue; | |
// skip if this is not a presentation attribute | |
if (!properties[attr.name]) | |
continue; | |
// create a new array for it | |
if (!presAttrStack[attr.name]) | |
presAttrStack[attr.name] = new Array(); | |
// remember the attribute node and whether the | |
// presentation attribute is applied to any of | |
// its descendands (false, at this point) | |
presAttrStack[attr.name].push([attr, false]); | |
} | |
} | |
function popInScopePresAttrs(element, presAttrStack) | |
{ | |
// iterate over the presentation attribute stack | |
// to find those that concern the current element | |
for (var x in presAttrStack) | |
{ | |
// get array of attributes from the stack | |
var p = presAttrStack[x]; | |
// skip if the array is empty | |
if (!p.length) | |
continue; | |
// if the current element is on the stack | |
if (p[p.length - 1][0].ownerElement == element) | |
{ | |
// remove it from the stack | |
var oldEntry = p.pop(); | |
var oldElem = oldEntry[0].ownerElement; | |
// and if it is never applied, remove the attribute | |
if (!oldEntry[1]) | |
oldElem.removeAttributeNS(null, oldEntry[0].name); | |
} | |
} | |
} | |
/** | |
* ... | |
* | |
* @param property ... | |
* | |
* @returns void | |
*/ | |
function computedIsSpecified(property) | |
{ | |
// well, not quite... | |
return true; | |
} | |
/** | |
* ... | |
* | |
* @param element ... | |
* @param presAttrStack | |
* | |
* @returns void | |
*/ | |
function isRedundantAttribute(element, attr, presAttrStack) | |
{ | |
/* | |
remove if | |
* | |
* if computed == specified and | |
* inherits and has value == value of first ancestor with it | |
* ... | |
*/ | |
var p = properties[attr.name]; | |
// has inherit and inherits | |
if (p["inh"] && attr.nodeValue == "inherit") | |
return true; | |
// has initial value and does not inherit | |
if (!p["inh"] && p["ini"].length && attr.nodeValue == p["ini"]) | |
return true; | |
// has initial value, inherits, and no ancestor specifies it | |
if (p["inh"] && p["ini"].length && attr.nodeValue == p["ini"] && | |
(!presAttrStack[attr.name] || presAttrStack[attr.name].length == 0)) | |
return true; | |
return false; | |
} | |
/** | |
* ... | |
* | |
* @param element ... | |
* @param presAttrStack | |
* | |
* @returns void | |
*/ | |
function dropRedundantHelper(element, presAttrStack) | |
{ | |
var remove = new Array(); | |
var attrs = element.attributes; | |
// iterate over attributes to find redundant presentation | |
// attributes | |
for (var j = 0; attrs && j < attrs.length; ++j) | |
{ | |
var attr = attrs.item(j); | |
// skip if in the wrong namespace | |
if (attr.namespaceURI != null) | |
continue; | |
// skip if this is not a presentation attribute | |
if (properties[attr.name] == null) | |
continue; | |
// if the attribute is redundant, remember to remove it | |
if (isRedundantAttribute(element, attr, presAttrStack)) | |
remove.push(attr.name); | |
} | |
// drop all redundant attributes | |
for (var j = 0; j < remove.length; ++j) | |
element.removeAttributeNS(null, remove[j]); | |
} | |
/** | |
* Converts cascaded declarations and style attributes to | |
* presentation attributes | |
* | |
* @param element ... | |
* | |
* @returns void | |
*/ | |
function cleanupStyles(element) | |
{ | |
var style = element.style; | |
var attrs = element.attributes; | |
// list of local names of attributes to be removed | |
var remove = new Array(); | |
// iterate over attributes to find those in the csns namespace | |
for (var j = 0; j < attrs.length; ++j) | |
{ | |
var attr = attrs.item(j); | |
// skip if in the wrong namespace | |
if (attr.namespaceURI != ssns) | |
continue; | |
// remember to remove it later | |
remove.push(attr.localName); | |
// continue with next if there is no style object | |
if (!style) | |
continue; | |
// get string representation of property value | |
var propVal = style.getPropertyValue(attr.localName); | |
// if the property is not set to a value, att the cascaded | |
// property value to the style declaration block. This | |
if (propVal == null || propVal.length == 0) | |
style.setProperty(attr.localName, attr.nodeValue, ""); | |
} | |
// drop all redundant attributes | |
for (var j = 0; j < remove.length; ++j) | |
element.removeAttributeNS(ssns, remove[j]); | |
// if the element has a style object and a style attribute, | |
// now is the time to convert it to presentation attributes | |
// and then drop the attribute from the element. | |
if (style && element.hasAttributeNS(null, "style")) | |
{ | |
CSSStyleDeclarationToAttributes(element, style, null); | |
element.removeAttributeNS(null, "style"); | |
} | |
} | |
/** | |
* Traverses element depth-first post-order converting cascaded | |
* declarations and style attributes to presentation attributes | |
* while dropping all redundant presentation attributes. | |
* | |
* @param element the element to traverse | |
* @param presAttrStack an array as stack | |
* | |
* @returns void | |
*/ | |
function dropRedundantPres(element, presAttrStack) | |
{ | |
// iterate over the presentation attributes on the stack and | |
// mark all presentation attributes used that apply to the | |
// current element. All presentation attributes that are not | |
// marked used will later be removed from the document. | |
for (var x in presAttrStack) | |
{ | |
// lookup property in stack | |
var p = presAttrStack[x]; | |
// skip if no attributes in scope | |
if (p.length == 0) | |
continue; | |
// lookup applies to map for the element | |
var m = elementPropertyMap[element.localName]; | |
// mark presentation attribute used if it applies | |
if (m && m[x]) | |
p[p.length - 1][1] = true; | |
} | |
// start with the current element as sibling | |
var sib = element; | |
// iterate over the sibling axis | |
while (sib != null) | |
{ | |
// find the next element sibling | |
if (sib.nodeType != 1) | |
{ | |
sib = sib.nextSibling; | |
continue; | |
} | |
// convert style attribute and cascaded styles from | |
// linked style sheets to presentation attributes | |
cleanupStyles(sib); | |
// find the first element child starting with the | |
// first child of the current sibling element | |
var down = sib.firstChild; | |
while (down && down.nodeType != 1) | |
{ | |
down = down.nextSibling; | |
} | |
// if there are descendant elements | |
if (down) | |
{ | |
// add the presentation attributes on the | |
// current element to the stack | |
pushInScopePresAttrs(sib, presAttrStack); | |
// drop redundant presentation attributes | |
// on the descendants of the current element | |
dropRedundantPres(down, presAttrStack); | |
// remove presentation attributes from stack | |
popInScopePresAttrs(sib, presAttrStack); | |
} | |
// drop the redundant attributes | |
dropRedundantHelper(sib, presAttrStack); | |
// continue with next sibling | |
sib = sib.nextSibling; | |
} | |
} | |
dropRedundantPres(document.documentElement, new Array()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment