mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
410 lines
14 KiB
JavaScript
410 lines
14 KiB
JavaScript
"use strict"
|
|
|
|
module.exports = function() {
|
|
function isModernEvent(type) {
|
|
return type === "transitionstart" || type === "transitionend" || type === "animationstart" || type === "animationend"
|
|
}
|
|
function appendChild(child) {
|
|
var ancestor = this
|
|
while (ancestor !== child && ancestor !== null) ancestor = ancestor.parentNode
|
|
if (ancestor === child) throw new Error("Node cannot be inserted at the specified point in the hierarchy")
|
|
|
|
if (child.nodeType == null) throw new Error("Argument is not a DOM element")
|
|
|
|
var index = this.childNodes.indexOf(child)
|
|
if (index > -1) this.childNodes.splice(index, 1)
|
|
if (child.nodeType === 11) {
|
|
while (child.firstChild != null) this.appendChild(child.firstChild)
|
|
child.childNodes = []
|
|
}
|
|
else {
|
|
this.childNodes.push(child)
|
|
if (child.parentNode != null && child.parentNode !== this) child.parentNode.removeChild(child)
|
|
child.parentNode = this
|
|
}
|
|
}
|
|
function removeChild(child) {
|
|
var index = this.childNodes.indexOf(child)
|
|
if (index > -1) {
|
|
this.childNodes.splice(index, 1)
|
|
child.parentNode = null
|
|
}
|
|
else throw new TypeError("Failed to execute 'removeChild'")
|
|
}
|
|
function insertBefore(child, reference) {
|
|
var ancestor = this
|
|
while (ancestor !== child && ancestor !== null) ancestor = ancestor.parentNode
|
|
if (ancestor === child) throw new Error("Node cannot be inserted at the specified point in the hierarchy")
|
|
|
|
if (child.nodeType == null) throw new Error("Argument is not a DOM element")
|
|
|
|
var refIndex = this.childNodes.indexOf(reference)
|
|
var index = this.childNodes.indexOf(child)
|
|
if (reference !== null && refIndex < 0) throw new TypeError("Invalid argument")
|
|
if (index > -1) this.childNodes.splice(index, 1)
|
|
if (reference === null) this.appendChild(child)
|
|
else {
|
|
if (child.nodeType === 11) {
|
|
this.childNodes.splice.apply(this.childNodes, [refIndex, 0].concat(child.childNodes))
|
|
while (child.firstChild) {
|
|
var subchild = child.firstChild
|
|
child.removeChild(subchild)
|
|
subchild.parentNode = this
|
|
}
|
|
child.childNodes = []
|
|
}
|
|
else {
|
|
this.childNodes.splice(refIndex, 0, child)
|
|
if (child.parentNode != null && child.parentNode !== this) child.parentNode.removeChild(child)
|
|
child.parentNode = this
|
|
}
|
|
}
|
|
}
|
|
function getAttribute(name) {
|
|
if (this.attributes[name] == null) return null
|
|
return this.attributes[name].nodeValue
|
|
}
|
|
function setAttribute(name, value) {
|
|
var nodeValue = String(value)
|
|
this.attributes[name] = {
|
|
namespaceURI: null,
|
|
get nodeValue() {return nodeValue},
|
|
set nodeValue(value) {nodeValue = String(value)},
|
|
}
|
|
}
|
|
function setAttributeNS(ns, name, value) {
|
|
this.setAttribute(name, value)
|
|
this.attributes[name].namespaceURI = ns
|
|
}
|
|
function removeAttribute(name) {
|
|
delete this.attributes[name]
|
|
}
|
|
var declListTokenizer = /;|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'/g
|
|
/**
|
|
* This will split a semicolon-separated CSS declaration list into an array of
|
|
* individual declarations, ignoring semicolons in strings.
|
|
*
|
|
* Comments are also stripped.
|
|
*
|
|
* @param {string} declList
|
|
* @return {string[]}
|
|
*/
|
|
function splitDeclList(declList) {
|
|
var indices = [], res = [], match
|
|
|
|
// remove comments, preserving comments in strings.
|
|
declList = declList.replace(
|
|
/("(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*')|\/\*[\s\S]*?\*\//g,
|
|
function(m, str){
|
|
return str || ''
|
|
}
|
|
)
|
|
/*eslint-disable no-cond-assign*/
|
|
while (match = declListTokenizer.exec(declList)) {
|
|
if (match[0] === ";") indices.push(match.index)
|
|
}
|
|
/*eslint-enable no-cond-assign*/
|
|
for (var i = indices.length; i--;){
|
|
res.unshift(declList.slice(indices[i] + 1))
|
|
declList = declList.slice(0, indices[i])
|
|
}
|
|
res.unshift(declList)
|
|
return res
|
|
}
|
|
|
|
var activeElement
|
|
var $window = {
|
|
document: {
|
|
createElement: function(tag, is) {
|
|
var cssText = ""
|
|
var style = {}
|
|
Object.defineProperty(style, "cssText", {
|
|
get: function() {return cssText},
|
|
set: function (value) {
|
|
var buf = []
|
|
if (typeof value === "string") {
|
|
for (var key in style) style[key] = ""
|
|
var rules = splitDeclList(value)
|
|
for (var i = 0; i < rules.length; i++) {
|
|
var rule = rules[i]
|
|
var colonIndex = rule.indexOf(":")
|
|
if (colonIndex > -1) {
|
|
var rawKey = rule.slice(0, colonIndex).trim()
|
|
var key = rawKey.replace(/-\D/g, function(match) {return match[1].toUpperCase()})
|
|
var value = rule.slice(colonIndex + 1).trim()
|
|
if (key !== "cssText") {
|
|
style[key] = value
|
|
buf.push(rawKey + ": " + value + ";")
|
|
}
|
|
}
|
|
}
|
|
cssText = buf.join(" ")
|
|
}
|
|
}
|
|
})
|
|
var events = {}
|
|
var element = {
|
|
nodeType: 1,
|
|
nodeName: tag.toUpperCase(),
|
|
namespaceURI: "http://www.w3.org/1999/xhtml",
|
|
appendChild: appendChild,
|
|
removeChild: removeChild,
|
|
insertBefore: insertBefore,
|
|
getAttribute: getAttribute,
|
|
setAttribute: setAttribute,
|
|
setAttributeNS: setAttributeNS,
|
|
removeAttribute: removeAttribute,
|
|
parentNode: null,
|
|
childNodes: [],
|
|
attributes: {},
|
|
get firstChild() {
|
|
return this.childNodes[0] || null
|
|
},
|
|
get nextSibling() {
|
|
if (this.parentNode == null) return null
|
|
var index = this.parentNode.childNodes.indexOf(this)
|
|
if (index < 0) throw new TypeError("Parent's childNodes is out of sync")
|
|
return this.parentNode.childNodes[index + 1] || null
|
|
},
|
|
set textContent(value) {
|
|
this.childNodes = []
|
|
if (value !== "") this.appendChild($window.document.createTextNode(value))
|
|
},
|
|
set innerHTML(value) {
|
|
while (this.firstChild) this.removeChild(this.firstChild)
|
|
|
|
var stack = [this], depth = 0, voidElements = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]
|
|
value.replace(/<([a-z0-9\-]+?)((?:\s+?[^=]+?=(?:"[^"]*?"|'[^']*?'|[^\s>]*))*?)(\s*\/)?>|<\/([a-z0-9\-]+?)>|([^<]+)/g, function(match, startTag, attrs, selfClosed, endTag, text) {
|
|
if (startTag) {
|
|
var element = $window.document.createElement(startTag)
|
|
attrs.replace(/\s+?([^=]+?)=(?:"([^"]*?)"|'([^']*?)'|([^\s>]*))/g, function(match, key, doubleQuoted, singleQuoted, unquoted) {
|
|
var keyParts = key.split(":")
|
|
var name = keyParts.pop()
|
|
var ns = keyParts[0]
|
|
var value = doubleQuoted || singleQuoted || unquoted || ""
|
|
if (ns != null) element.setAttributeNS(ns, name, value)
|
|
else element.setAttribute(name, value)
|
|
})
|
|
stack[depth].appendChild(element)
|
|
if (!selfClosed && voidElements.indexOf(startTag.toLowerCase()) < 0) stack[++depth] = element
|
|
}
|
|
else if (endTag) {
|
|
depth--
|
|
}
|
|
else if (text) {
|
|
stack[depth].appendChild($window.document.createTextNode(text)) // FIXME handle html entities
|
|
}
|
|
})
|
|
},
|
|
get style() {
|
|
return style
|
|
},
|
|
set style(_){
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style#Setting_style
|
|
throw new Error("setting element.style is not portable")
|
|
},
|
|
get className() {
|
|
return this.attributes["class"] ? this.attributes["class"].nodeValue : ""
|
|
},
|
|
set className(value) {
|
|
if (this.namespaceURI === "http://www.w3.org/2000/svg") throw new Error("Cannot set property className of SVGElement")
|
|
else this.setAttribute("class", value)
|
|
},
|
|
focus: function() {activeElement = this},
|
|
addEventListener: function(type, callback, useCapture) {
|
|
if (events[type] == null) events[type] = [callback]
|
|
else events[type].push(callback)
|
|
},
|
|
removeEventListener: function(type, callback, useCapture) {
|
|
if (events[type] != null) {
|
|
var index = events[type].indexOf(callback)
|
|
if (index > -1) events[type].splice(index, 1)
|
|
}
|
|
},
|
|
dispatchEvent: function(e) {
|
|
if (this.nodeName === "INPUT" && this.attributes["type"] != null && this.attributes["type"].nodeValue === "checkbox" && e.type === "click") {
|
|
this.checked = !this.checked
|
|
}
|
|
|
|
e.target = this
|
|
if (events[e.type] != null) {
|
|
for (var i = 0; i < events[e.type].length; i++) {
|
|
events[e.type][i].call(this, e)
|
|
}
|
|
}
|
|
e.preventDefault = function() {
|
|
// TODO: should this do something?
|
|
}
|
|
if (typeof this["on" + e.type] === "function" && !isModernEvent(e.type)) this["on" + e.type](e)
|
|
},
|
|
onclick: null,
|
|
}
|
|
|
|
if (element.nodeName === "A") {
|
|
var href
|
|
Object.defineProperty(element, "href", {
|
|
get: function() {return this.attributes["href"] === undefined ? "" : "[FIXME implement]"},
|
|
set: function(value) {this.setAttribute("href", value)},
|
|
enumerable: true,
|
|
})
|
|
}
|
|
|
|
if (element.nodeName === "INPUT") {
|
|
var checked
|
|
Object.defineProperty(element, "checked", {
|
|
get: function() {return checked === undefined ? this.attributes["checked"] !== undefined : checked},
|
|
set: function(value) {checked = Boolean(value)},
|
|
enumerable: true,
|
|
})
|
|
|
|
element.value = ""
|
|
}
|
|
|
|
if (element.nodeName === "TEXTAREA") {
|
|
var value
|
|
Object.defineProperty(element, "value", {
|
|
get: function() {
|
|
return value != null ? value :
|
|
this.firstChild ? this.firstChild.nodeValue : ""
|
|
},
|
|
set: function(v) {value = v},
|
|
enumerable: true,
|
|
})
|
|
}
|
|
|
|
if (element.nodeName === "CANVAS") {
|
|
Object.defineProperty(element, "width", {
|
|
get: function() {return this.attributes["width"] ? Math.floor(parseInt(this.attributes["width"].nodeValue) || 0) : 300},
|
|
set: function(value) {this.setAttribute("width", Math.floor(Number(value) || 0).toString())},
|
|
})
|
|
Object.defineProperty(element, "height", {
|
|
get: function() {return this.attributes["height"] ? Math.floor(parseInt(this.attributes["height"].nodeValue) || 0) : 300},
|
|
set: function(value) {this.setAttribute("height", Math.floor(Number(value) || 0).toString())},
|
|
})
|
|
}
|
|
|
|
function getOptions(element) {
|
|
var options = []
|
|
for (var i = 0; i < element.childNodes.length; i++) {
|
|
if (element.childNodes[i].nodeName === "OPTION") options.push(element.childNodes[i])
|
|
else if (element.childNodes[i].nodeName === "OPTGROUP") options = options.concat(getOptions(element.childNodes[i]))
|
|
}
|
|
return options
|
|
}
|
|
function getOptionValue(element) {
|
|
return element.attributes["value"] != null ?
|
|
element.attributes["value"].nodeValue :
|
|
element.firstChild != null ? element.firstChild.nodeValue : ""
|
|
}
|
|
if (element.nodeName === "SELECT") {
|
|
var selectedValue, selectedIndex = 0
|
|
Object.defineProperty(element, "selectedIndex", {
|
|
get: function() {return getOptions(this).length > 0 ? selectedIndex : -1},
|
|
set: function(value) {
|
|
var options = getOptions(this)
|
|
if (value >= 0 && value < options.length) {
|
|
selectedValue = getOptionValue(options[selectedIndex])
|
|
selectedIndex = value
|
|
}
|
|
else {
|
|
selectedValue = ""
|
|
selectedIndex = -1
|
|
}
|
|
},
|
|
enumerable: true,
|
|
})
|
|
Object.defineProperty(element, "value", {
|
|
get: function() {
|
|
if (this.selectedIndex > -1) return getOptionValue(getOptions(this)[this.selectedIndex])
|
|
return ""
|
|
},
|
|
set: function(value) {
|
|
var options = getOptions(this)
|
|
var stringValue = String(value)
|
|
for (var i = 0; i < options.length; i++) {
|
|
if (getOptionValue(options[i]) === stringValue) {
|
|
selectedValue = stringValue
|
|
selectedIndex = i
|
|
return
|
|
}
|
|
}
|
|
selectedValue = stringValue
|
|
selectedIndex = -1
|
|
},
|
|
enumerable: true,
|
|
})
|
|
}
|
|
if (element.nodeName === "OPTION") {
|
|
Object.defineProperty(element, "value", {
|
|
get: function() {return getOptionValue(this)},
|
|
set: function(value) {
|
|
this.setAttribute("value", value)
|
|
},
|
|
enumerable: true,
|
|
})
|
|
Object.defineProperty(element, "selected", {
|
|
get: function() {
|
|
var options = getOptions(this.parentNode)
|
|
var index = options.indexOf(this)
|
|
return index === this.parentNode.selectedIndex
|
|
},
|
|
set: function(value) {
|
|
if (value) {
|
|
var options = getOptions(this.parentNode)
|
|
var index = options.indexOf(this)
|
|
if (index > -1) this.parentNode.selectedIndex = index
|
|
}
|
|
else this.parentNode.selectedIndex = 0
|
|
},
|
|
enumerable: true,
|
|
})
|
|
}
|
|
return element
|
|
},
|
|
createElementNS: function(ns, tag, is) {
|
|
var element = this.createElement(tag, is)
|
|
element.nodeName = tag
|
|
element.namespaceURI = ns
|
|
return element
|
|
},
|
|
createTextNode: function(text) {
|
|
var nodeValue = String(text)
|
|
return {
|
|
nodeType: 3,
|
|
nodeName: "#text",
|
|
parentNode: null,
|
|
get nodeValue() {return nodeValue},
|
|
set nodeValue(value) {nodeValue = String(value)},
|
|
}
|
|
},
|
|
createDocumentFragment: function() {
|
|
return {
|
|
nodeType: 11,
|
|
nodeName: "#document-fragment",
|
|
appendChild: appendChild,
|
|
insertBefore: insertBefore,
|
|
removeChild: removeChild,
|
|
parentNode: null,
|
|
childNodes: [],
|
|
get firstChild() {
|
|
return this.childNodes[0] || null
|
|
},
|
|
}
|
|
},
|
|
createEvent: function() {
|
|
return {
|
|
initEvent: function(type) {this.type = type},
|
|
}
|
|
},
|
|
get activeElement() {return activeElement},
|
|
},
|
|
}
|
|
$window.document.documentElement = $window.document.createElement("html")
|
|
$window.document.documentElement.appendChild($window.document.createElement("head"))
|
|
$window.document.body = $window.document.createElement("body")
|
|
$window.document.documentElement.appendChild($window.document.body)
|
|
activeElement = $window.document.body
|
|
|
|
return $window
|
|
}
|