This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
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.
257 lines
7.0 KiB
257 lines
7.0 KiB
(function () { |
|
|
|
var sax; |
|
|
|
if (typeof module !== 'undefined' && module.exports) { |
|
// We're being used in a Node-like environment |
|
sax = require('sax'); |
|
} |
|
else { |
|
// assume it's attached to the Window object in a browser |
|
sax = this.sax; |
|
|
|
if (!sax) // no sax for you! |
|
throw new Error("Expected sax to be defined. Make sure you're including sax.js before this file."); |
|
} |
|
|
|
/* |
|
XmlElement is our basic building block. Everything is an XmlElement; even XmlDocument |
|
behaves like an XmlElement by inheriting its attributes and functions. |
|
*/ |
|
|
|
function XmlElement(tag) { |
|
// Capture the parser object off of the XmlDocument delegate |
|
var parser = delegates[delegates.length - 1].parser; |
|
|
|
this.name = tag.name; |
|
this.attr = tag.attributes || {}; |
|
this.val = ""; |
|
this.isValCdata = false; |
|
this.children = []; |
|
this.firstChild = null; |
|
this.lastChild = null; |
|
|
|
// Assign parse information |
|
this.line = parser.line; |
|
this.column = parser.column; |
|
this.position = parser.position; |
|
this.startTagPosition = parser.startTagPosition; |
|
} |
|
|
|
// SaxParser handlers |
|
|
|
XmlElement.prototype._opentag = function(tag) { |
|
|
|
var child = new XmlElement(tag); |
|
|
|
// add to our children array |
|
this.children.push(child); |
|
|
|
// update first/last pointers |
|
if (!this.firstChild) this.firstChild = child; |
|
this.lastChild = child; |
|
|
|
delegates.unshift(child); |
|
}; |
|
|
|
XmlElement.prototype._closetag = function() { |
|
delegates.shift(); |
|
}; |
|
|
|
XmlElement.prototype._text = function(text) { |
|
if (text) this.val += text; |
|
}; |
|
|
|
XmlElement.prototype._cdata = function(cdata) { |
|
if (cdata) { |
|
this.val += cdata; |
|
this.isValCdata=true; |
|
} |
|
}; |
|
|
|
XmlElement.prototype._error = function(err) { |
|
throw err; |
|
}; |
|
|
|
// Useful functions |
|
|
|
XmlElement.prototype.eachChild = function(iterator, context) { |
|
for (var i=0, l=this.children.length; i<l; i++) |
|
if (iterator.call(context, this.children[i], i, this.children) === false) return; |
|
}; |
|
|
|
XmlElement.prototype.childNamed = function(name) { |
|
for (var i=0, l=this.children.length; i<l; i++) { |
|
var child = this.children[i]; |
|
if (child.name === name) return child; |
|
} |
|
return undefined; |
|
}; |
|
|
|
XmlElement.prototype.childrenNamed = function(name) { |
|
var matches = []; |
|
|
|
for (var i=0, l=this.children.length; i<l; i++) |
|
if (this.children[i].name === name) |
|
matches.push(this.children[i]); |
|
|
|
return matches; |
|
}; |
|
|
|
XmlElement.prototype.childWithAttribute = function(name,value) { |
|
for (var i=0, l=this.children.length; i<l; i++) { |
|
var child = this.children[i]; |
|
if ( (value && child.attr[name] === value) || (!value && child.attr[name]) ) |
|
return child; |
|
} |
|
return undefined; |
|
}; |
|
|
|
XmlElement.prototype.descendantWithPath = function(path) { |
|
var descendant = this; |
|
var components = path.split('.'); |
|
|
|
for (var i=0, l=components.length; i<l; i++) |
|
if (descendant) |
|
descendant = descendant.childNamed(components[i]); |
|
else |
|
return undefined; |
|
|
|
return descendant; |
|
}; |
|
|
|
XmlElement.prototype.valueWithPath = function(path) { |
|
var components = path.split('@'); |
|
var descendant = this.descendantWithPath(components[0]); |
|
if (descendant) |
|
return components.length > 1 ? descendant.attr[components[1]] : descendant.val; |
|
else |
|
return undefined; |
|
}; |
|
|
|
// String formatting (for debugging) |
|
|
|
XmlElement.prototype.toString = function(options) { |
|
return this.toStringWithIndent("", options); |
|
}; |
|
|
|
XmlElement.prototype.toStringWithIndent = function(indent, options) { |
|
var s = indent + "<" + this.name; |
|
var linebreak = options && options.compressed ? "" : "\n"; |
|
var preserveWhitespace = options && options.preserveWhitespace; |
|
|
|
for (var name in this.attr) |
|
if (Object.prototype.hasOwnProperty.call(this.attr, name)) |
|
s += " " + name + '="' + escapeXML(this.attr[name]) + '"'; |
|
|
|
var finalVal = ''; |
|
if (this.isValCdata){ |
|
finalVal = '<![CDATA['+this.val+']]>'; |
|
} else if (preserveWhitespace) { |
|
finalVal = escapeXML(this.val); |
|
} else{ |
|
finalVal = escapeXML(this.val.trim()); |
|
} |
|
if (options && options.trimmed && finalVal.length > 25) |
|
finalVal = finalVal.substring(0,25).trim() + "…"; |
|
|
|
if (this.children.length) { |
|
s += ">" + linebreak; |
|
|
|
var childIndent = indent + (options && options.compressed ? "" : " "); |
|
|
|
if (finalVal.length) |
|
s += childIndent + finalVal + linebreak; |
|
|
|
for (var i=0, l=this.children.length; i<l; i++) |
|
s += this.children[i].toStringWithIndent(childIndent, options) + linebreak; |
|
|
|
s += indent + "</" + this.name + ">"; |
|
} |
|
else if (finalVal.length) { |
|
s += ">" + finalVal + "</" + this.name +">"; |
|
} |
|
else s += "/>"; |
|
|
|
return s; |
|
}; |
|
|
|
/* |
|
XmlDocument is the class we expose to the user; it uses the sax parser to create a hierarchy |
|
of XmlElements. |
|
*/ |
|
|
|
function XmlDocument(xml) { |
|
xml && (xml = xml.toString().trim()); |
|
|
|
if (!xml) |
|
throw new Error("No XML to parse!"); |
|
|
|
// Expose the parser to the other delegates while the parser is running |
|
this.parser = sax.parser(true); // strict |
|
addParserEvents(this.parser); |
|
|
|
// We'll use the file-scoped "delegates" var to remember what elements we're currently |
|
// parsing; they will push and pop off the stack as we get deeper into the XML hierarchy. |
|
// It's safe to use a global because JS is single-threaded. |
|
delegates = [this]; |
|
|
|
this.parser.write(xml); |
|
|
|
// Remove the parser as it is no longer needed and should not be exposed to clients |
|
delete this.parser; |
|
} |
|
|
|
// make XmlDocument inherit XmlElement's methods |
|
extend(XmlDocument.prototype, XmlElement.prototype); |
|
|
|
XmlDocument.prototype._opentag = function(tag) { |
|
if (typeof this.children === 'undefined') |
|
// the first tag we encounter should be the root - we'll "become" the root XmlElement |
|
XmlElement.call(this,tag); |
|
else |
|
// all other tags will be the root element's children |
|
XmlElement.prototype._opentag.apply(this,arguments); |
|
}; |
|
|
|
// file-scoped global stack of delegates |
|
var delegates = null; |
|
|
|
/* |
|
Helper functions |
|
*/ |
|
|
|
function addParserEvents(parser) { |
|
parser.onopentag = parser_opentag; |
|
parser.onclosetag = parser_closetag; |
|
parser.ontext = parser_text; |
|
parser.oncdata = parser_cdata; |
|
parser.onerror = parser_error; |
|
} |
|
|
|
// create these closures and cache them by keeping them file-scoped |
|
function parser_opentag() { delegates[0]._opentag.apply(delegates[0],arguments) } |
|
function parser_closetag() { delegates[0]._closetag.apply(delegates[0],arguments) } |
|
function parser_text() { delegates[0]._text.apply(delegates[0],arguments) } |
|
function parser_cdata() { delegates[0]._cdata.apply(delegates[0],arguments) } |
|
function parser_error() { delegates[0]._error.apply(delegates[0],arguments) } |
|
|
|
// a relatively standard extend method |
|
function extend(destination, source) { |
|
for (var prop in source) |
|
if (source.hasOwnProperty(prop)) |
|
destination[prop] = source[prop]; |
|
} |
|
|
|
// escapes XML entities like "<", "&", etc. |
|
function escapeXML(value){ |
|
return value.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, '&').replace(/'/g, ''').replace(/"/g, '"'); |
|
} |
|
|
|
// Are we being used in a Node-like environment? |
|
if (typeof module !== 'undefined' && module.exports) |
|
module.exports.XmlDocument = XmlDocument; |
|
else |
|
this.XmlDocument = XmlDocument; |
|
|
|
})();
|
|
|