SvgTidy/Snippets
< SvgTidy
@@
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());