You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
6.4 KiB
250 lines
6.4 KiB
/* Any copyright is dedicated to the Public Domain. |
|
* http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
|
|
function xmlEncode(s){ |
|
var i = 0, ch; |
|
s = String(s); |
|
while (i < s.length && (ch = s[i]) !== '&' && ch !== '<' && |
|
ch !== '\"' && ch !== '\n' && ch !== '\r' && ch !== '\t') { |
|
i++; |
|
} |
|
if (i >= s.length) { |
|
return s; |
|
} |
|
var buf = s.substring(0, i); |
|
while (i < s.length) { |
|
ch = s[i++]; |
|
switch (ch) { |
|
case '&': |
|
buf += '&'; |
|
break; |
|
case '<': |
|
buf += '<'; |
|
break; |
|
case '\"': |
|
buf += '"'; |
|
break; |
|
case '\n': |
|
buf += '
'; |
|
break; |
|
case '\r': |
|
buf += '
'; |
|
break; |
|
case '\t': |
|
buf += '	'; |
|
break; |
|
default: |
|
buf += ch; |
|
break; |
|
} |
|
} |
|
return buf; |
|
} |
|
|
|
function DOMElement(name) { |
|
this.nodeName = name; |
|
this.childNodes = []; |
|
this.attributes = {}; |
|
this.textContent = ''; |
|
|
|
if (name === 'style') { |
|
this.sheet = { |
|
cssRules: [], |
|
insertRule: function (rule) { |
|
this.cssRules.push(rule); |
|
}, |
|
}; |
|
} |
|
} |
|
|
|
DOMElement.prototype = { |
|
|
|
getAttributeNS: function DOMElement_getAttributeNS(NS, name) { |
|
// Fast path |
|
if (name in this.attributes) { |
|
return this.attributes[name]; |
|
} |
|
// Slow path - used by test/unit/display_svg_spec.js |
|
// Assuming that there is only one matching attribute for a given name, |
|
// across all namespaces. |
|
if (NS) { |
|
var suffix = ':' + name; |
|
for (var fullName in this.attributes) { |
|
if (fullName.slice(-suffix.length) === suffix) { |
|
return this.attributes[fullName]; |
|
} |
|
} |
|
} |
|
return null; |
|
}, |
|
|
|
setAttributeNS: function DOMElement_setAttributeNS(NS, name, value) { |
|
value = value || ''; |
|
value = xmlEncode(value); |
|
this.attributes[name] = value; |
|
}, |
|
|
|
appendChild: function DOMElement_appendChild(element) { |
|
var childNodes = this.childNodes; |
|
if (childNodes.indexOf(element) === -1) { |
|
childNodes.push(element); |
|
} |
|
}, |
|
|
|
cloneNode: function DOMElement_cloneNode() { |
|
var newNode = new DOMElement(this.nodeName); |
|
newNode.childNodes = this.childNodes; |
|
newNode.attributes = this.attributes; |
|
newNode.textContent = this.textContent; |
|
return newNode; |
|
}, |
|
|
|
// This method is offered for convenience. It is recommended to directly use |
|
// getSerializer because that allows you to process the chunks as they come |
|
// instead of requiring the whole image to fit in memory. |
|
toString: function DOMElement_toString() { |
|
var buf = []; |
|
var serializer = this.getSerializer(); |
|
var chunk; |
|
while ((chunk = serializer.getNext()) !== null) { |
|
buf.push(chunk); |
|
} |
|
return buf.join(''); |
|
}, |
|
|
|
getSerializer: function DOMElement_getSerializer() { |
|
return new DOMElementSerializer(this); |
|
} |
|
} |
|
|
|
function DOMElementSerializer(node) { |
|
this._node = node; |
|
this._state = 0; |
|
this._loopIndex = 0; |
|
this._attributeKeys = null; |
|
this._childSerializer = null; |
|
} |
|
DOMElementSerializer.prototype = { |
|
/** |
|
* Yields the next chunk in the serialization of the element. |
|
* |
|
* @returns {string|null} null if the element has fully been serialized. |
|
*/ |
|
getNext: function DOMElementSerializer_getNext() { |
|
var node = this._node; |
|
switch (this._state) { |
|
case 0: // Start opening tag. |
|
++this._state; |
|
return '<' + node.nodeName; |
|
case 1: // Add SVG namespace if this is the root element. |
|
++this._state; |
|
if (node.nodeName === 'svg:svg') { |
|
return ' xmlns:xlink="http://www.w3.org/1999/xlink"' + |
|
' xmlns:svg="http://www.w3.org/2000/svg"'; |
|
} |
|
case 2: // Initialize variables for looping over attributes. |
|
++this._state; |
|
this._loopIndex = 0; |
|
this._attributeKeys = Object.keys(node.attributes); |
|
case 3: // Serialize any attributes and end opening tag. |
|
if (this._loopIndex < this._attributeKeys.length) { |
|
var name = this._attributeKeys[this._loopIndex++]; |
|
return ' ' + name + '="' + xmlEncode(node.attributes[name]) + '"'; |
|
} |
|
++this._state; |
|
return '>'; |
|
case 4: // Serialize textContent for tspan/style elements. |
|
if (node.nodeName === 'svg:tspan' || node.nodeName === 'svg:style') { |
|
this._state = 6; |
|
return xmlEncode(node.textContent); |
|
} |
|
++this._state; |
|
this._loopIndex = 0; |
|
case 5: // Serialize child nodes (only for non-tspan/style elements). |
|
var value; |
|
while (true) { |
|
value = this._childSerializer && this._childSerializer.getNext(); |
|
if (value !== null) { |
|
return value; |
|
} |
|
var nextChild = node.childNodes[this._loopIndex++]; |
|
if (nextChild) { |
|
this._childSerializer = new DOMElementSerializer(nextChild); |
|
} else { |
|
this._childSerializer = null; |
|
++this._state; |
|
break; |
|
} |
|
} |
|
case 6: // Ending tag. |
|
++this._state; |
|
return '</' + node.nodeName + '>'; |
|
case 7: // Done. |
|
return null; |
|
default: |
|
throw new Error('Unexpected serialization state: ' + this._state); |
|
} |
|
}, |
|
}; |
|
|
|
const document = { |
|
childNodes : [], |
|
|
|
get currentScript() { |
|
return { src: '' }; |
|
}, |
|
|
|
get documentElement() { |
|
return this; |
|
}, |
|
|
|
createElementNS: function (NS, element) { |
|
var elObject = new DOMElement(element); |
|
return elObject; |
|
}, |
|
|
|
createElement: function (element) { |
|
return this.createElementNS('', element); |
|
}, |
|
|
|
getElementsByTagName: function (element) { |
|
if (element === 'head') { |
|
return [this.head || (this.head = new DOMElement('head'))]; |
|
} |
|
return []; |
|
} |
|
}; |
|
|
|
function Image () { |
|
this._src = null; |
|
this.onload = null; |
|
} |
|
Image.prototype = { |
|
get src () { |
|
return this._src; |
|
}, |
|
set src (value) { |
|
this._src = value; |
|
if (this.onload) { |
|
this.onload(); |
|
} |
|
} |
|
} |
|
|
|
exports.document = document; |
|
exports.Image = Image; |
|
|
|
var exported_symbols = Object.keys(exports); |
|
|
|
exports.setStubs = function(namespace) { |
|
exported_symbols.forEach(function(key) { |
|
console.assert(!(key in namespace), 'property should not be set: ' + key); |
|
namespace[key] = exports[key]; |
|
}); |
|
}; |
|
exports.unsetStubs = function(namespace) { |
|
exported_symbols.forEach(function(key) { |
|
console.assert(key in namespace, 'property should be set: ' + key); |
|
delete namespace[key]; |
|
}); |
|
};
|
|
|