Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

First commit

  • Loading branch information...
commit 9b198460911132b33925ceb5f429922de2354a46 0 parents
@ibrow authored
29 LICENSE
@@ -0,0 +1,29 @@
+----------------------------------------------------------------------
+node-rss is released under the MIT License
+
+Copyright (c) 2010 Rob Searles - http://www.robsearles.com
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+----------------------------------------------------------------------
+node-xml, which node-rss makes heavy usage is also released under the
+MIT License - see http://github.com/robrighter/node-xml for more info
+----------------------------------------------------------------------
40 README
@@ -0,0 +1,40 @@
+----------------------------------------------------------------------
+node-rss - an RSS parser for node.
+http://github.com/ibrow/node-rss
+Rob Searles - http://www.robsearles.com
+----------------------------------------------------------------------
+node-rss makes heavy use of the node-xml module written by
+Rob Righter - @robrighter
+http://github.com/robrighter/node-xml
+----------------------------------------------------------------------
+node-rss is released under the MIT licence
+----------------------------------------------------------------------
+See examples.js for working examples of node-rss
+
+----------------------------------------------------------------------
+TODO
+----------------------------------------------------------------------
+Lots, mainly:
+ - error checking
+ - writing tests
+ - make parsing mode robust
+ - conform to all specifications
+
+----------------------------------------------------------------------
+HISTORY
+----------------------------------------------------------------------
+21 May 2010
+Initial release, working on v0.1.95-17-g1036aa9
+----------------------------------------------------------------------
+
+----------------------------------------------------------------------
+REFERENCE
+----------------------------------------------------------------------
+RSS 2.0 specification
+http://cyber.law.harvard.edu/rss/rss.html
+
+RSS 1.0 specification
+http://web.resource.org/rss/1.0/spec
+
+Atom 1.0 specification
+http://atompub.org/2005/07/11/draft-ietf-atompub-format-10.html
45 example.js
@@ -0,0 +1,45 @@
+/**********************************************************************
+example.js
+Example of the node-rss feed parser
+
+**********************************************************************/
+var sys = require('sys');
+var rss = require('./node-rss');
+
+
+/**********************************************************************
+Example One:
+Getting a remote RSS feed and parsing
+rss.parseURL(feed_url, use_excerpt, callback);
+**********************************************************************/
+// URL of the feed you want to parse
+var feed_url = 'http://feeds.feedburner.com/github';
+
+var response = rss.parseURL(feed_url, function(articles) {
+ sys.puts(articles.length);
+ for(i=0; i<articles.length; i++) {
+ sys.puts("Article: "+i+", "+
+ articles[i].title+"\n"+
+ articles[i].link+"\n"+
+ articles[i].description+"\n"+
+ articles[i].content
+ );
+ }
+});
+
+/**********************************************************************
+Example Two:
+Getting a local RSS feed and parsing
+rss.parseFile(feed_file, use_excerpt, callback);
+**********************************************************************/
+var response = rss.parseFile('nodeblogs.com.feed.xml', function(articles) {
+ sys.puts(articles.length);
+ for(i=0; i<articles.length; i++) {
+ sys.puts("Article: "+i+", "+
+ articles[i].title+"\n"+
+ articles[i].link+"\n"+
+ articles[i].description+"\n"+
+ articles[i].content
+ );
+ }
+});
171 node-rss.js
@@ -0,0 +1,171 @@
+/**********************************************************************
+ node-rss - an RSS parser for node.
+ http://github.com/ibrow/node-rss
+
+ Copyright (c) 2010 Rob Searles
+ http://www.robsearles.com
+
+ node-rss is released under the MIT license
+ - see LICENSE for more info
+
+ *********************************************************************
+ node-rss makes heavy use of the node-xml module written by
+ Rob Righter - @robrighter
+ http://github.com/robrighter/node-xml
+**********************************************************************/
+var sys = require('sys'), http = require('http');
+var xml = require("./node-xml");
+
+// variable for holding the callback function which is passed to the
+// exported function. This callback is passed the articles array
+var callback = function() {};
+
+// The main "meat" of this module - parses an rss feed and triggers
+// the callback when done.
+// using node-xml: http://github.com/robrighter/node-xml
+var parser = new xml.SaxParser(function(cb) {
+ var articles = Array();
+ var current_element = false;
+ var article_count = 0;
+ var in_item = false;
+ var current_chars = '';
+
+
+ cb.onStartDocument(function() { });
+
+ // when finished parsing the RSS feed, trigger the callback
+ cb.onEndDocument(function() {
+ callback(articles);
+ });
+
+
+ //track what element we are currently in. If it is an <item> this is
+ // an article, add container array to the list of articles
+ cb.onStartElementNS(function(elem, attrs, prefix, uri, namespaces) {
+ current_element = elem.toLowerCase();
+ if(current_element == 'item' || current_element == 'entry') {
+ in_item = true;
+ articles[article_count] = Array();
+ }
+ });
+ // when we are at the end of an element, save its related content
+ cb.onEndElementNS(function(elem, prefix, uri) {
+ if(in_item) {
+ switch(current_element)
+ {
+ case 'description':
+ case 'summary':
+ articles[article_count][current_element] = current_chars.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+ break;
+ case 'content':
+ case 'encoded': // feedburner is <content:encoded>, node-xml reads as <encoded>
+ current_element = 'content';
+ articles[article_count][current_element] = current_chars.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+ break;
+ case 'link':
+ case 'title':
+ articles[article_count][current_element] = current_chars.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+ break;
+ }
+
+ current_element = false;
+ current_chars = '';
+ if(elem.toLowerCase() == 'item' || elem.toString() == 'entry') {
+ in_item = false;
+ article_count ++;
+ }
+ }
+ });
+
+ cb.onCharacters(addContent);
+ cb.onCdata(addContent);
+ function addContent(chars) {
+ if(in_item) {
+ current_chars += chars;
+ }
+ };
+
+ // @TODO handle warnings and errors properly
+ cb.onWarning(function(msg) {
+ sys.puts('<WARNING>'+msg+"</WARNING>");
+ });
+ cb.onError(function(msg) {
+ sys.puts('<ERROR>'+JSON.stringify(msg)+"</ERROR>");
+ });
+});
+
+
+/**
+ * parseFile()
+ * Parses an RSS feed from a file.
+ * @param file - path to the RSS feed file
+ * @param cb - callback function to be triggered at end of parsing
+ */
+exports.parseFile = function(file, cb) {
+ callback = cb;
+ parser.parseFile(file);
+}
+/**
+ * parseURL()
+ * Parses an RSS feed from a URL.
+ * @param url - URL of the RSS feed file
+ * @param cb - callback function to be triggered at end of parsing
+ *
+ * @TODO - decent error checking
+ */
+exports.parseURL = function(url, cb) {
+ callback = cb;
+
+ get_rss(url);
+ function get_rss(url) {
+ var u = require('url'), http = require('http');
+ var parts = u.parse(url);
+ //sys.puts(JSON.stringify(parts));
+
+ // set the default port to 80
+ if(!parts.port) { parts.port = 80; }
+
+
+ var redirection_level = 0;
+ var client = http.createClient(parts.port, parts.hostname);
+ var request = client.request('GET', parts.pathname, {'host': parts.hostname});
+ request.addListener('response', function (response) {
+ //sys.puts('STATUS: ' + response.statusCode);
+ //sys.puts('HEADERS: ' + JSON.stringify(response.headers));
+
+ // check to see the type of status
+ switch(response.statusCode) {
+ // check for ALL OK
+ case 200:
+ var body = '';
+ response.addListener('data', function (chunk) {
+ body += chunk;
+ });
+ response.addListener('end', function() {
+ parser.parseString(body);
+ });
+ break;
+ // redirect status returned
+ case 301:
+ case 302:
+ if(redirection_level > 10) {
+ sys.puts("too many redirects");
+ }
+ else {
+ sys.puts("redirect to "+response.headers.location);
+ get_rss(response.headers.location);
+ }
+ break;
+ default:
+ /*
+ response.setEncoding('utf8');
+ response.addListener('data', function (chunk) {
+ //sys.puts('BODY: ' + chunk);
+ });
+*/
+ break;
+ }
+ });
+ request.end();
+ }
+};
1,248 node-xml.js
@@ -0,0 +1,1248 @@
+// node-xml
+// An xml parser for node.js
+// (C) Rob Righter (@robrighter) 2009 - 2010, Licensed under the MIT-LICENSE
+// Contributions from David Joham
+
+
+(function () {
+
+// CONSTANTS
+var whitespace = "\n\r\t ";
+
+
+//XMLP is a pull-based parser. The calling application passes in a XML string
+//to the constructor, then repeatedly calls .next() to parse the next segment.
+//.next() returns a flag indicating what type of segment was found, and stores
+//data temporarily in couple member variables (name, content, array of
+//attributes), which can be accessed by several .get____() methods.
+//
+//Basically, XMLP is the lowest common denominator parser - an very simple
+//API which other wrappers can be built against.
+
+
+var XMLP = function(strXML) {
+ // Normalize line breaks
+ strXML = SAXStrings.replace(strXML, null, null, "\r\n", "\n");
+ strXML = SAXStrings.replace(strXML, null, null, "\r", "\n");
+
+ this.m_xml = strXML;
+ this.m_iP = 0;
+ this.m_iState = XMLP._STATE_PROLOG;
+ this.m_stack = new Stack();
+ this._clearAttributes();
+ this.m_pause = false;
+ this.m_preInterruptIState = XMLP._STATE_PROLOG;
+ this.m_namespaceList = new Array();
+ this.m_chunkTransitionContinuation = null;
+
+}
+
+
+// CONSTANTS (these must be below the constructor)
+XMLP._NONE = 0;
+XMLP._ELM_B = 1;
+XMLP._ELM_E = 2;
+XMLP._ELM_EMP = 3;
+XMLP._ATT = 4;
+XMLP._TEXT = 5;
+XMLP._ENTITY = 6;
+XMLP._PI = 7;
+XMLP._CDATA = 8;
+XMLP._COMMENT = 9;
+XMLP._DTD = 10;
+XMLP._ERROR = 11;
+XMLP._INTERRUPT = 12;
+
+XMLP._CONT_XML = 0;
+XMLP._CONT_ALT = 1;
+
+XMLP._ATT_NAME = 0;
+XMLP._ATT_VAL = 1;
+
+XMLP._STATE_PROLOG = 1;
+XMLP._STATE_DOCUMENT = 2;
+XMLP._STATE_MISC = 3;
+
+XMLP._errs = new Array();
+XMLP._errs[XMLP.ERR_CLOSE_PI = 0 ] = "PI: missing closing sequence";
+XMLP._errs[XMLP.ERR_CLOSE_DTD = 1 ] = "DTD: missing closing sequence";
+XMLP._errs[XMLP.ERR_CLOSE_COMMENT = 2 ] = "Comment: missing closing sequence";
+XMLP._errs[XMLP.ERR_CLOSE_CDATA = 3 ] = "CDATA: missing closing sequence";
+XMLP._errs[XMLP.ERR_CLOSE_ELM = 4 ] = "Element: missing closing sequence";
+XMLP._errs[XMLP.ERR_CLOSE_ENTITY = 5 ] = "Entity: missing closing sequence";
+XMLP._errs[XMLP.ERR_PI_TARGET = 6 ] = "PI: target is required";
+XMLP._errs[XMLP.ERR_ELM_EMPTY = 7 ] = "Element: cannot be both empty and closing";
+XMLP._errs[XMLP.ERR_ELM_NAME = 8 ] = "Element: name must immediatly follow \"<\"";
+XMLP._errs[XMLP.ERR_ELM_LT_NAME = 9 ] = "Element: \"<\" not allowed in element names";
+XMLP._errs[XMLP.ERR_ATT_VALUES = 10] = "Attribute: values are required and must be in quotes";
+XMLP._errs[XMLP.ERR_ATT_LT_NAME = 11] = "Element: \"<\" not allowed in attribute names";
+XMLP._errs[XMLP.ERR_ATT_LT_VALUE = 12] = "Attribute: \"<\" not allowed in attribute values";
+XMLP._errs[XMLP.ERR_ATT_DUP = 13] = "Attribute: duplicate attributes not allowed";
+XMLP._errs[XMLP.ERR_ENTITY_UNKNOWN = 14] = "Entity: unknown entity";
+XMLP._errs[XMLP.ERR_INFINITELOOP = 15] = "Infininte loop";
+XMLP._errs[XMLP.ERR_DOC_STRUCTURE = 16] = "Document: only comments, processing instructions, or whitespace allowed outside of document element";
+XMLP._errs[XMLP.ERR_ELM_NESTING = 17] = "Element: must be nested correctly";
+
+
+
+XMLP.prototype.continueParsing = function(strXML) {
+
+ if(this.m_chunkTransitionContinuation){
+ strXML = this.m_chunkTransitionContinuation + strXML;
+ }
+ // Normalize line breaks
+ strXML = SAXStrings.replace(strXML, null, null, "\r\n", "\n");
+ strXML = SAXStrings.replace(strXML, null, null, "\r", "\n");
+
+ this.m_xml = strXML;
+ this.m_iP = 0;
+ this.m_iState = XMLP._STATE_DOCUMENT;
+ //this.m_stack = new Stack();
+ //this._clearAttributes();
+ this.m_pause = false;
+ this.m_preInterruptIState = XMLP._STATE_PROLOG;
+ this.m_chunkTransitionContinuation = null;
+
+}
+
+XMLP.prototype._addAttribute = function(name, value) {
+ this.m_atts[this.m_atts.length] = new Array(name, value);
+}
+
+XMLP.prototype._checkStructure = function(iEvent) {
+ if(XMLP._STATE_PROLOG == this.m_iState) {
+ if((XMLP._TEXT == iEvent) || (XMLP._ENTITY == iEvent)) {
+ if(SAXStrings.indexOfNonWhitespace(this.getContent(), this.getContentBegin(), this.getContentEnd()) != -1) {
+ return this._setErr(XMLP.ERR_DOC_STRUCTURE);
+ }
+ }
+
+ if((XMLP._ELM_B == iEvent) || (XMLP._ELM_EMP == iEvent)) {
+ this.m_iState = XMLP._STATE_DOCUMENT;
+ // Don't return - fall through to next state
+ }
+ }
+ if(XMLP._STATE_DOCUMENT == this.m_iState) {
+ if((XMLP._ELM_B == iEvent) || (XMLP._ELM_EMP == iEvent)) {
+ this.m_stack.push(this.getName());
+ }
+
+ if((XMLP._ELM_E == iEvent) || (XMLP._ELM_EMP == iEvent)) {
+ var strTop = this.m_stack.pop();
+ if((strTop == null) || (strTop != this.getName())) {
+ return this._setErr(XMLP.ERR_ELM_NESTING);
+ }
+ }
+
+ if(this.m_stack.count() == 0) {
+ this.m_iState = XMLP._STATE_MISC;
+ return iEvent;
+ }
+ }
+ if(XMLP._STATE_MISC == this.m_iState) {
+ if((XMLP._ELM_B == iEvent) || (XMLP._ELM_E == iEvent) || (XMLP._ELM_EMP == iEvent) || (XMLP.EVT_DTD == iEvent)) {
+ return this._setErr(XMLP.ERR_DOC_STRUCTURE);
+ }
+
+ if((XMLP._TEXT == iEvent) || (XMLP._ENTITY == iEvent)) {
+ if(SAXStrings.indexOfNonWhitespace(this.getContent(), this.getContentBegin(), this.getContentEnd()) != -1) {
+ return this._setErr(XMLP.ERR_DOC_STRUCTURE);
+ }
+ }
+ }
+
+ return iEvent;
+
+}
+
+XMLP.prototype._clearAttributes = function() {
+ this.m_atts = new Array();
+}
+
+XMLP.prototype._findAttributeIndex = function(name) {
+ for(var i = 0; i < this.m_atts.length; i++) {
+ if(this.m_atts[i][XMLP._ATT_NAME] == name) {
+ return i;
+ }
+ }
+ return -1;
+
+}
+
+XMLP.prototype.getAttributeCount = function() {
+ return this.m_atts ? this.m_atts.length : 0;
+}
+
+XMLP.prototype.getAttributeName = function(index) {
+ return ((index < 0) || (index >= this.m_atts.length)) ? null : this.m_atts[index][XMLP._ATT_NAME];
+}
+
+XMLP.prototype.getAttributeValue = function(index) {
+ return ((index < 0) || (index >= this.m_atts.length)) ? null : __unescapeString(this.m_atts[index][XMLP._ATT_VAL]);
+}
+
+XMLP.prototype.getAttributeValueByName = function(name) {
+ return this.getAttributeValue(this._findAttributeIndex(name));
+}
+
+XMLP.prototype.getColumnNumber = function() {
+ return SAXStrings.getColumnNumber(this.m_xml, this.m_iP);
+}
+
+XMLP.prototype.getContent = function() {
+ return (this.m_cSrc == XMLP._CONT_XML) ? this.m_xml : this.m_cAlt;
+}
+
+XMLP.prototype.getContentBegin = function() {
+ return this.m_cB;
+}
+
+XMLP.prototype.getContentEnd = function() {
+ return this.m_cE;
+}
+
+XMLP.prototype.getLineNumber = function() {
+ return SAXStrings.getLineNumber(this.m_xml, this.m_iP);
+}
+
+XMLP.prototype.getName = function() {
+ return this.m_name;
+}
+
+XMLP.prototype.pause = function(){
+ this.m_pause = true;
+}
+
+XMLP.prototype.resume = function(){
+ this.m_pause = false;
+ this.m_iState = this.m_preInterruptIState;
+}
+
+XMLP.prototype.next = function() {
+ if(!this.m_pause){
+ return this._checkStructure(this._parse());
+ }
+ else{
+ //save off the current event loop state and set the state to interrupt
+ this.m_preInterruptIState = this.m_iState;
+ return XMLP._INTERRUPT;
+ }
+}
+
+XMLP.prototype._parse = function() {
+ if(this.m_iP == this.m_xml.length) {
+ return XMLP._NONE;
+ }
+
+ if(this.m_iP == this.m_xml.indexOf("<?", this.m_iP)) {
+ return this._parsePI (this.m_iP + 2);
+ }
+ else if(this.m_iP == this.m_xml.indexOf("<!DOCTYPE", this.m_iP)) {
+ return this._parseDTD (this.m_iP + 9);
+ }
+ else if(this.m_iP == this.m_xml.indexOf("<!--", this.m_iP)) {
+ return this._parseComment(this.m_iP + 4);
+ }
+ else if(this.m_iP == this.m_xml.indexOf("<![CDATA[", this.m_iP)) {
+ return this._parseCDATA (this.m_iP + 9);
+ }
+ else if(this.m_iP == this.m_xml.indexOf("<", this.m_iP)) {
+ return this._parseElement(this.m_iP + 1);
+ }
+ else if(this.m_iP == this.m_xml.indexOf("&", this.m_iP)) {
+ return this._parseEntity (this.m_iP + 1);
+ }
+ else{
+ return this._parseText (this.m_iP);
+ }
+
+
+}
+
+////////// NAMESPACE SUPPORT //////////////////////////////////////////
+XMLP.prototype._parsePrefixAndElementName = function (elementlabel){
+ splits = elementlabel.split(':',2);
+ return { prefix : ((splits.length === 1) ? '' : splits[0]), name : ((splits.length === 1) ? elementlabel : splits[1]), };
+}
+
+XMLP.prototype._parseNamespacesAndAtts = function (atts){
+ //translate namespaces into objects with "prefix","uri", "scopetag" Add them to: this.m_namespaceList
+ //The function should return a new list of tag attributes with the namespaces filtered
+ that = this;
+ var newnamespaces = [];
+ var filteredatts = [];
+ atts.map(function (item){
+ if(item[0].slice(0,5) === "xmlns"){
+ newnamespaces.push({
+ prefix : item[0].slice(6),
+ uri : item[1],
+ scopetag : that.m_name,
+ });
+ }
+ else{
+ filteredatts.push(item);
+ }
+ return "not used";
+ });
+ this.m_namespaceList = this.m_namespaceList.concat(newnamespaces);
+ return [ filteredatts, newnamespaces.map(function(item){return [item.prefix,item.uri];}) ];
+}
+
+XMLP.prototype._getContextualNamespace = function (prefix){
+ if(prefix !== ''){
+ for(item in this.m_namespaceList){
+ item = this.m_namespaceList[item];
+ if(item.prefix === prefix){
+ return item.uri;
+ }
+ }
+ }
+
+ //no match was found for the prefix so pop off the first non-prefix namespace
+ for(var i = (this.m_namespaceList.length-1); i>= 0; i--){
+ var item = this.m_namespaceList[i];
+ if(item.prefix === ''){
+ return item.uri;
+ }
+ }
+
+ //still nothing, lets just return an empty string
+ return '';
+}
+
+XMLP.prototype._removeExpiredNamesapces = function (closingtagname) {
+ //remove the expiring namespaces from the list (you can id them by scopetag)
+ var keeps = [];
+ this.m_namespaceList.map(function (item){
+ if(item.scopetag !== closingtagname){
+ keeps.push(item);
+ }
+ });
+
+ this.m_namespaceList = keeps;
+
+}
+
+////////////////////////////////////////////////////////////////////////
+
+
+XMLP.prototype._parseAttribute = function(iB, iE) {
+ var iNB, iNE, iEq, iVB, iVE;
+ var cQuote, strN, strV;
+
+ this.m_cAlt = ""; //resets the value so we don't use an old one by accident (see testAttribute7 in the test suite)
+
+ iNB = SAXStrings.indexOfNonWhitespace(this.m_xml, iB, iE);
+ if((iNB == -1) ||(iNB >= iE)) {
+ return iNB;
+ }
+
+ iEq = this.m_xml.indexOf("=", iNB);
+ if((iEq == -1) || (iEq > iE)) {
+ return this._setErr(XMLP.ERR_ATT_VALUES);
+ }
+
+ iNE = SAXStrings.lastIndexOfNonWhitespace(this.m_xml, iNB, iEq);
+
+ iVB = SAXStrings.indexOfNonWhitespace(this.m_xml, iEq + 1, iE);
+ if((iVB == -1) ||(iVB > iE)) {
+ return this._setErr(XMLP.ERR_ATT_VALUES);
+ }
+
+ cQuote = this.m_xml.charAt(iVB);
+ if(SAXStrings.QUOTES.indexOf(cQuote) == -1) {
+ return this._setErr(XMLP.ERR_ATT_VALUES);
+ }
+
+ iVE = this.m_xml.indexOf(cQuote, iVB + 1);
+ if((iVE == -1) ||(iVE > iE)) {
+ return this._setErr(XMLP.ERR_ATT_VALUES);
+ }
+
+ strN = this.m_xml.substring(iNB, iNE + 1);
+ strV = this.m_xml.substring(iVB + 1, iVE);
+
+ if(strN.indexOf("<") != -1) {
+ return this._setErr(XMLP.ERR_ATT_LT_NAME);
+ }
+
+ if(strV.indexOf("<") != -1) {
+ return this._setErr(XMLP.ERR_ATT_LT_VALUE);
+ }
+
+ strV = SAXStrings.replace(strV, null, null, "\n", " ");
+ strV = SAXStrings.replace(strV, null, null, "\t", " ");
+ iRet = this._replaceEntities(strV);
+ if(iRet == XMLP._ERROR) {
+ return iRet;
+ }
+
+ strV = this.m_cAlt;
+
+ if(this._findAttributeIndex(strN) == -1) {
+ this._addAttribute(strN, strV);
+ }
+ else {
+ return this._setErr(XMLP.ERR_ATT_DUP);
+ }
+
+ this.m_iP = iVE + 2;
+
+ return XMLP._ATT;
+
+}
+
+XMLP.prototype._parseCDATA = function(iB) {
+ var iE = this.m_xml.indexOf("]]>", iB);
+ if (iE == -1) {
+ //This item never closes, although it could be a malformed document, we will assume that we are mid-chunck, save the string and reurn as interrupted
+ this.m_chunkTransitionContinuation = this.m_xml.slice(iB-9);//the '-<![CDATA[ adds the '<!DOCTYPE' back into the string
+ return XMLP._INTERRUPT;
+ //return this._setErr(XMLP.ERR_CLOSE_CDATA);
+ }
+
+ this._setContent(XMLP._CONT_XML, iB, iE);
+
+ this.m_iP = iE + 3;
+
+ return XMLP._CDATA;
+
+}
+
+XMLP.prototype._parseComment = function(iB) {
+ var iE = this.m_xml.indexOf("-" + "->", iB);
+ if (iE == -1) {
+ //This item never closes, although it could be a malformed document, we will assume that we are mid-chunck, save the string and reurn as interrupted
+ this.m_chunkTransitionContinuation = this.m_xml.slice(iB-4);//the '-4' adds the '<!--' back into the string
+ return XMLP._INTERRUPT;
+ //return this._setErr(XMLP.ERR_CLOSE_COMMENT);
+ }
+
+ this._setContent(XMLP._CONT_XML, iB, iE);
+
+ this.m_iP = iE + 3;
+
+ return XMLP._COMMENT;
+
+}
+
+XMLP.prototype._parseDTD = function(iB) {
+ // Eat DTD
+ var iE, strClose, iInt, iLast;
+
+ iE = this.m_xml.indexOf(">", iB);
+ if(iE == -1) {
+ //This item never closes, although it could be a malformed document, we will assume that we are mid-chunck, save the string and reurn as interrupted
+ this.m_chunkTransitionContinuation = this.m_xml.slice(iB-9);//the '-9' adds the '<!DOCTYPE' back into the string
+ return XMLP._INTERRUPT;
+ //return this._setErr(XMLP.ERR_CLOSE_DTD);
+ }
+
+ iInt = this.m_xml.indexOf("[", iB);
+ strClose = ((iInt != -1) && (iInt < iE)) ? "]>" : ">";
+
+ while(true) {
+ // DEBUG: Remove
+ if(iE == iLast) {
+ return this._setErr(XMLP.ERR_INFINITELOOP);
+ }
+
+ iLast = iE;
+ // DEBUG: Remove End
+
+ iE = this.m_xml.indexOf(strClose, iB);
+ if(iE == -1) {
+ return this._setErr(XMLP.ERR_CLOSE_DTD);
+ }
+
+ // Make sure it is not the end of a CDATA section
+ if (this.m_xml.substring(iE - 1, iE + 2) != "]]>") {
+ break;
+ }
+ }
+
+ this.m_iP = iE + strClose.length;
+
+ return XMLP._DTD;
+
+}
+
+XMLP.prototype._parseElement = function(iB) {
+ sys = require('sys');
+ var iE, iDE, iNE, iRet;
+ var iType, strN, iLast;
+
+ iDE = iE = this.m_xml.indexOf(">", iB);
+ if(iE == -1) {
+ //This element never closes, although it could be a malformed document, we will assume that we are mid-chunck, save the string and reurn as interrupted
+ this.m_chunkTransitionContinuation = this.m_xml.slice(iB-1);//the '-1' adds the '<' back into the string
+ return XMLP._INTERRUPT;
+ //return this._setErr(XMLP.ERR_CLOSE_ELM);
+ }
+
+ if(this.m_xml.charAt(iB) == "/") {
+ iType = XMLP._ELM_E;
+ iB++;
+ } else {
+ iType = XMLP._ELM_B;
+ }
+
+ if(this.m_xml.charAt(iE - 1) == "/") {
+ if(iType == XMLP._ELM_E) {
+ return this._setErr(XMLP.ERR_ELM_EMPTY);
+ }
+ iType = XMLP._ELM_EMP;
+ iDE--;
+ }
+
+ iDE = SAXStrings.lastIndexOfNonWhitespace(this.m_xml, iB, iDE);
+
+ //djohack
+ //hack to allow for elements with single character names to be recognized
+
+ if (iE - iB != 1 ) {
+ if(SAXStrings.indexOfNonWhitespace(this.m_xml, iB, iDE) != iB) {
+ return this._setErr(XMLP.ERR_ELM_NAME);
+ }
+ }
+ // end hack -- original code below
+
+ /*
+ if(SAXStrings.indexOfNonWhitespace(this.m_xml, iB, iDE) != iB)
+ return this._setErr(XMLP.ERR_ELM_NAME);
+ */
+ this._clearAttributes();
+
+ iNE = SAXStrings.indexOfWhitespace(this.m_xml, iB, iDE);
+ if(iNE == -1) {
+ iNE = iDE + 1;
+ }
+ else {
+ this.m_iP = iNE;
+ while(this.m_iP < iDE) {
+ // DEBUG: Remove
+ if(this.m_iP == iLast) return this._setErr(XMLP.ERR_INFINITELOOP);
+ iLast = this.m_iP;
+ // DEBUG: Remove End
+
+
+ iRet = this._parseAttribute(this.m_iP, iDE);
+ if(iRet == XMLP._ERROR) return iRet;
+ }
+ }
+
+ strN = this.m_xml.substring(iB, iNE);
+
+ if(strN.indexOf("<") != -1) {
+ return this._setErr(XMLP.ERR_ELM_LT_NAME);
+ }
+
+ this.m_name = strN;
+ this.m_iP = iE + 1;
+
+ return iType;
+
+}
+
+XMLP.prototype._parseEntity = function(iB) {
+ var iE = this.m_xml.indexOf(";", iB);
+ if(iE == -1) {
+ //This item never closes, although it could be a malformed document, we will assume that we are mid-chunck, save the string and reurn as interrupted
+ this.m_chunkTransitionContinuation = this.m_xml.slice(iB-1);//the '-1' adds the '&' back into the string
+ return XMLP._INTERRUPT;
+ //return this._setErr(XMLP.ERR_CLOSE_ENTITY);
+ }
+
+ this.m_iP = iE + 1;
+
+ return this._replaceEntity(this.m_xml, iB, iE);
+
+}
+
+XMLP.prototype._parsePI = function(iB) {
+ var iE, iTB, iTE, iCB, iCE;
+
+ iE = this.m_xml.indexOf("?>", iB);
+ if(iE == -1) {
+ //This item never closes, although it could be a malformed document, we will assume that we are mid-chunck, save the string and reurn as interrupted
+ this.m_chunkTransitionContinuation = this.m_xml.slice(iB-2);//the '-2' adds the '?>' back into the string
+ return XMLP._INTERRUPT;
+ return this._setErr(XMLP.ERR_CLOSE_PI);
+ }
+
+ iTB = SAXStrings.indexOfNonWhitespace(this.m_xml, iB, iE);
+ if(iTB == -1) {
+ return this._setErr(XMLP.ERR_PI_TARGET);
+ }
+
+ iTE = SAXStrings.indexOfWhitespace(this.m_xml, iTB, iE);
+ if(iTE == -1) {
+ iTE = iE;
+ }
+
+ iCB = SAXStrings.indexOfNonWhitespace(this.m_xml, iTE, iE);
+ if(iCB == -1) {
+ iCB = iE;
+ }
+
+ iCE = SAXStrings.lastIndexOfNonWhitespace(this.m_xml, iCB, iE);
+ if(iCE == -1) {
+ iCE = iE - 1;
+ }
+
+ this.m_name = this.m_xml.substring(iTB, iTE);
+ this._setContent(XMLP._CONT_XML, iCB, iCE + 1);
+ this.m_iP = iE + 2;
+
+ return XMLP._PI;
+
+}
+
+XMLP.prototype._parseText = function(iB) {
+ var iE, iEE;
+
+ iE = this.m_xml.indexOf("<", iB);
+ if(iE == -1) {
+ iE = this.m_xml.length;
+ }
+
+ iEE = this.m_xml.indexOf("&", iB);
+ if((iEE != -1) && (iEE <= iE)) {
+ iE = iEE;
+ }
+
+ this._setContent(XMLP._CONT_XML, iB, iE);
+
+ this.m_iP = iE;
+
+ return XMLP._TEXT;
+
+}
+
+XMLP.prototype._replaceEntities = function(strD, iB, iE) {
+ if(SAXStrings.isEmpty(strD)) return "";
+ iB = iB || 0;
+ iE = iE || strD.length;
+
+
+ var iEB, iEE, strRet = "";
+
+ iEB = strD.indexOf("&", iB);
+ iEE = iB;
+
+ while((iEB > 0) && (iEB < iE)) {
+ strRet += strD.substring(iEE, iEB);
+
+ iEE = strD.indexOf(";", iEB) + 1;
+
+ if((iEE == 0) || (iEE > iE)) {
+ return this._setErr(XMLP.ERR_CLOSE_ENTITY);
+ }
+
+ iRet = this._replaceEntity(strD, iEB + 1, iEE - 1);
+ if(iRet == XMLP._ERROR) {
+ return iRet;
+ }
+
+ strRet += this.m_cAlt;
+
+ iEB = strD.indexOf("&", iEE);
+ }
+
+ if(iEE != iE) {
+ strRet += strD.substring(iEE, iE);
+ }
+
+ this._setContent(XMLP._CONT_ALT, strRet);
+
+ return XMLP._ENTITY;
+
+}
+
+XMLP.prototype._replaceEntity = function(strD, iB, iE) {
+ if(SAXStrings.isEmpty(strD)) return -1;
+ iB = iB || 0;
+ iE = iE || strD.length;
+
+ switch(strD.substring(iB, iE)) {
+ case "amp": strEnt = "&"; break;
+ case "lt": strEnt = "<"; break;
+ case "gt": strEnt = ">"; break;
+ case "apos": strEnt = "'"; break;
+ case "quot": strEnt = "\""; break;
+ case "nbsp":strEnt = ''; break;
+ case "lt":strEnt = '<'; break;
+ case "gt":strEnt = '>'; break;
+ case "amp":strEnt = '&'; break;
+ case "cent":strEnt = "¢"; break;
+ case "pound":strEnt = '£'; break;
+ case "yen":strEnt = '¥'; break;
+ case "euro":strEnt = ''; break;
+ case "sect":strEnt = '§'; break;
+ case "copy":strEnt = '©'; break;
+ case "reg":strEnt = '®'; break;
+ default:
+ if(strD.charAt(iB) == "#") {
+ strEnt = String.fromCharCode(parseInt(strD.substring(iB + 1, iE)));
+ } else {
+ strEnt = ' ';
+ //return this._setErr(XMLP.ERR_ENTITY_UNKNOWN);
+ }
+ break;
+ }
+ this._setContent(XMLP._CONT_ALT, strEnt);
+
+ return XMLP._ENTITY;
+}
+
+XMLP.prototype._setContent = function(iSrc) {
+ var args = arguments;
+
+ if(XMLP._CONT_XML == iSrc) {
+ this.m_cAlt = null;
+ this.m_cB = args[1];
+ this.m_cE = args[2];
+ } else {
+ this.m_cAlt = args[1];
+ this.m_cB = 0;
+ this.m_cE = args[1].length;
+ }
+ this.m_cSrc = iSrc;
+
+}
+
+XMLP.prototype._setErr = function(iErr) {
+ var strErr = XMLP._errs[iErr];
+
+ this.m_cAlt = strErr;
+ this.m_cB = 0;
+ this.m_cE = strErr.length;
+ this.m_cSrc = XMLP._CONT_ALT;
+
+ return XMLP._ERROR;
+
+} // end function _setErr
+
+
+//SaxParser is an object that basically wraps an XMLP instance, and provides an
+//event-based interface for parsing. This is the object users interact with when coding
+//with XML for <SCRIPT>
+var SaxParser = function(eventhandlerfactory) {
+
+ var eventhandler = new function(){
+
+ }
+
+ var thehandler = function() {};
+ thehandler.prototype.onStartDocument = function (funct){
+ eventhandler.onStartDocument = funct;
+ }
+ thehandler.prototype.onEndDocument = function (funct){
+ eventhandler.onEndDocument = funct;
+ }
+ thehandler.prototype.onStartElementNS = function (funct){
+ eventhandler.onStartElementNS = funct;
+ }
+ thehandler.prototype.onEndElementNS = function (funct){
+ eventhandler.onEndElementNS = funct;
+ }
+ thehandler.prototype.onCharacters = function(funct) {
+ eventhandler.onCharacters = funct;
+ }
+ thehandler.prototype.onCdata = function(funct) {
+ eventhandler.onCdata = funct;
+ }
+ thehandler.prototype.onComment = function(funct) {
+ eventhandler.onComment = funct;
+ }
+ thehandler.prototype.onWarning = function(funct) {
+ eventhandler.onWarning = funct;
+ }
+
+ thehandler.prototype.onError = function(funct) {
+ eventhandler.onError = funct;
+ }
+
+
+ eventhandlerfactory(new thehandler());
+ //eventhandler = eventhandler(eventhandler);
+ this.m_hndDoc = eventhandler;
+ this.m_hndErr = eventhandler;
+ this.m_hndLex = eventhandler;
+ this.m_interrupted = false;
+}
+
+
+// CONSTANTS (these must be below the constructor)
+SaxParser.DOC_B = 1;
+SaxParser.DOC_E = 2;
+SaxParser.ELM_B = 3;
+SaxParser.ELM_E = 4;
+SaxParser.CHARS = 5;
+SaxParser.PI = 6;
+SaxParser.CD_B = 7;
+SaxParser.CD_E = 8;
+SaxParser.CMNT = 9;
+SaxParser.DTD_B = 10;
+SaxParser.DTD_E = 11;
+
+SaxParser.prototype.parseFile = function(filename) { //This function will only work in the node.js environment.
+ var fs = require('fs');
+ var that = this;
+ fs.readFile(filename, function (err, data) {
+ that.parseString(data);
+ });
+}
+
+
+SaxParser.prototype.parseString = function(strD) {
+ sys = require('sys');
+ var that = this;
+
+ setTimeout(function(){
+ var startnew = true;
+ if(!that.m_parser){
+ that.m_parser = new XMLP(strD);
+ startnew = false;
+ }
+ else{
+ that.m_parser.continueParsing(strD);
+ startnew = true;
+ }
+
+ //if(that.m_hndDoc && that.m_hndDoc.setDocumentLocator) {
+ // that.m_hndDoc.setDocumentLocator(that);
+ //}
+
+ that.m_bErr = false;
+
+ if(!that.m_bErr && !startnew) {
+ that._fireEvent(SaxParser.DOC_B);
+ }
+ that._parseLoop();
+ if(!that.m_bErr && !that.m_interrupted) {
+ that._fireEvent(SaxParser.DOC_E);
+ }
+
+ that.m_xml = null;
+ that.m_iP = 0;
+ that.m_interrupted = false;
+ }, 0);
+
+}
+
+SaxParser.prototype.pause = function() {
+ this.m_parser.pause();
+}
+
+SaxParser.prototype.resume = function() {
+ //reset the state
+ this.m_parser.resume();
+ //now start up the parse loop
+ var that = this;
+ setTimeout(function(){
+ that._parseLoop();
+ }, 0);
+}
+
+SaxParser.prototype.setDocumentHandler = function(hnd) {
+ this.m_hndDoc = hnd;
+}
+
+SaxParser.prototype.setErrorHandler = function(hnd) {
+ this.m_hndErr = hnd;
+}
+
+SaxParser.prototype.setLexicalHandler = function(hnd) {
+ this.m_hndLex = hnd;
+}
+
+SaxParser.prototype.getColumnNumber = function() {
+ return this.m_parser.getColumnNumber();
+}
+
+SaxParser.prototype.getLineNumber = function() {
+ return this.m_parser.getLineNumber();
+}
+
+SaxParser.prototype.getMessage = function() {
+ return this.m_strErrMsg;
+}
+
+SaxParser.prototype.getPublicId = function() {
+ return null;
+}
+
+SaxParser.prototype.getSystemId = function() {
+ return null;
+}
+
+SaxParser.prototype.getLength = function() {
+ return this.m_parser.getAttributeCount();
+}
+
+SaxParser.prototype.getName = function(index) {
+ return this.m_parser.getAttributeName(index);
+}
+
+SaxParser.prototype.getValue = function(index) {
+ return this.m_parser.getAttributeValue(index);
+}
+
+SaxParser.prototype.getValueByName = function(name) {
+ return this.m_parser.getAttributeValueByName(name);
+}
+
+SaxParser.prototype._fireError = function(strMsg) {
+ this.m_strErrMsg = strMsg;
+ this.m_bErr = true;
+
+ if(this.m_hndErr && this.m_hndErr.onError) {
+ this.m_hndErr.onError(this.m_strErrMsg);
+ }
+}
+
+
+
+SaxParser.prototype._fireEvent = function(iEvt) {
+ var hnd, func, args = arguments, iLen = args.length - 1;
+
+
+ if(this.m_bErr) return;
+
+ if(SaxParser.DOC_B == iEvt) {
+ func = "onStartDocument"; hnd = this.m_hndDoc;
+ }
+ else if (SaxParser.DOC_E == iEvt) {
+ func = "onEndDocument"; hnd = this.m_hndDoc;
+ }
+ else if (SaxParser.ELM_B == iEvt) {
+ func = "onStartElementNS"; hnd = this.m_hndDoc;
+ }
+ else if (SaxParser.ELM_E == iEvt) {
+ func = "onEndElementNS"; hnd = this.m_hndDoc;
+ }
+ else if (SaxParser.CHARS == iEvt) {
+ func = "onCharacters"; hnd = this.m_hndDoc;
+ }
+ else if (SaxParser.PI == iEvt) {
+ func = "processingInstruction"; hnd = this.m_hndDoc;
+ }
+ else if (SaxParser.CD_B == iEvt) {
+ func = "onCdata"; hnd = this.m_hndLex;
+ }
+ else if (SaxParser.CD_E == iEvt) {
+ func = "onEndCDATA"; hnd = this.m_hndLex;
+ }
+ else if (SaxParser.CMNT == iEvt) {
+ func = "onComment"; hnd = this.m_hndLex;
+ }
+
+ if(hnd && hnd[func]) {
+ if(0 == iLen) {
+ hnd[func]();
+ }
+ else if (1 == iLen) {
+ hnd[func](args[1]);
+ }
+ else if (2 == iLen) {
+ hnd[func](args[1], args[2]);
+ }
+ else if (3 == iLen) {
+ hnd[func](args[1], args[2], args[3]);
+ }
+ else if (4 == iLen) {
+ hnd[func](args[1], args[2], args[3], args[4]);
+ }
+ else if (5 == iLen) {
+ hnd[func](args[1], args[2], args[3], args[4], args[5]);
+ }
+ }
+
+}
+
+
+
+
+SaxParser.prototype._parseLoop = function(parser) {
+ var iEvent, parser;
+
+ parser = this.m_parser;
+ while(!this.m_bErr) {
+ iEvent = parser.next();
+
+ if(iEvent == XMLP._ELM_B) {
+ theatts = this.m_parser.m_atts;
+ nameobject = parser._parsePrefixAndElementName(parser.getName());
+ theattsandnamespace = parser._parseNamespacesAndAtts(theatts);
+ var theuri = parser._getContextualNamespace(nameobject.prefix);
+ this._fireEvent(SaxParser.ELM_B, nameobject.name, theattsandnamespace[0], (nameobject.prefix === '')? null : nameobject.prefix, (theuri === '')? null : theuri ,theattsandnamespace[1] );
+ }
+ else if(iEvent == XMLP._ELM_E) {
+ nameobject = parser._parsePrefixAndElementName(parser.getName());
+ var theuri = parser._getContextualNamespace(nameobject.prefix);
+ parser._removeExpiredNamesapces(parser.getName());
+ this._fireEvent(SaxParser.ELM_E, nameobject.name, (nameobject.prefix === '')? null : nameobject.prefix, (theuri === '')? null : theuri);
+ }
+ else if(iEvent == XMLP._ELM_EMP) {
+ //this is both a begin and end element
+ theatts = this.m_parser.m_atts;
+ nameobject = parser._parsePrefixAndElementName(parser.getName());
+ theattsandnamespace = parser._parseNamespacesAndAtts(theatts);
+ var theuri = parser._getContextualNamespace(nameobject.prefix);
+ this._fireEvent(SaxParser.ELM_B, nameobject.name, theattsandnamespace[0], (nameobject.prefix === '')? null : nameobject.prefix, (theuri === '')? null : theuri ,theattsandnamespace[1] );
+
+ parser._removeExpiredNamesapces(parser.getName());
+ this._fireEvent(SaxParser.ELM_E, nameobject.name, (nameobject.prefix === '')? null : nameobject.prefix, (theuri === '')? null : theuri);
+ //this._fireEvent(SaxParser.ELM_B, parser.getName(), this.m_parser.m_atts.map(function(item){return { name : item[0], value : item[1], };}) );
+ //this._fireEvent(SaxParser.ELM_E, parser.getName());
+ }
+ else if(iEvent == XMLP._TEXT) {
+ this._fireEvent(SaxParser.CHARS, parser.getContent().slice(parser.getContentBegin(),parser.getContentEnd()));
+ }
+ else if(iEvent == XMLP._ENTITY) {
+ this._fireEvent(SaxParser.CHARS, parser.getContent(), parser.getContentBegin(), parser.getContentEnd() - parser.getContentBegin());
+ }
+ else if(iEvent == XMLP._PI) {
+ this._fireEvent(SaxParser.PI, parser.getName(), parser.getContent().substring(parser.getContentBegin(), parser.getContentEnd()));
+ }
+ else if(iEvent == XMLP._CDATA) {
+ this._fireEvent(SaxParser.CD_B, parser.getContent().slice(parser.getContentBegin(),parser.getContentEnd()));
+ //this._fireEvent(SaxParser.CHARS, parser.getContent(), parser.getContentBegin(), parser.getContentEnd() - parser.getContentBegin());
+ //this._fireEvent(SaxParser.CD_E);
+ }
+ else if(iEvent == XMLP._COMMENT) {
+ this._fireEvent(SaxParser.CMNT, parser.getContent().slice(parser.getContentBegin(),parser.getContentEnd()));
+ }
+ else if(iEvent == XMLP._DTD) {
+ }
+ else if(iEvent == XMLP._ERROR) {
+ this._fireError(parser.getContent());
+ }
+ else if(iEvent == XMLP._INTERRUPT){
+ this.m_interrupted = true;
+ return;//just return and wait to be restarted
+ }
+ else if(iEvent == XMLP._NONE) {
+ return;
+ }
+ }
+
+}
+
+//SAXStrings: a useful object containing string manipulation functions
+var SAXStrings = function() {
+//This is the constructor of the SAXStrings object
+}
+
+
+// CONSTANTS (these must be below the constructor)
+SAXStrings.WHITESPACE = " \t\n\r";
+SAXStrings.QUOTES = "\"'";
+
+
+SAXStrings.getColumnNumber = function(strD, iP) {
+ if(SAXStrings.isEmpty(strD)) {
+ return -1;
+ }
+ iP = iP || strD.length;
+
+ var arrD = strD.substring(0, iP).split("\n");
+ var strLine = arrD[arrD.length - 1];
+ arrD.length--;
+ var iLinePos = arrD.join("\n").length;
+
+ return iP - iLinePos;
+
+}
+
+SAXStrings.getLineNumber = function(strD, iP) {
+ if(SAXStrings.isEmpty(strD)) {
+ return -1;
+ }
+ iP = iP || strD.length;
+
+ return strD.substring(0, iP).split("\n").length
+}
+
+SAXStrings.indexOfNonWhitespace = function(strD, iB, iE) {
+ if(SAXStrings.isEmpty(strD)) {
+ return -1;
+ }
+ iB = iB || 0;
+ iE = iE || strD.length;
+
+ for(var i = iB; i < iE; i++){
+ if(SAXStrings.WHITESPACE.indexOf(strD.charAt(i)) == -1) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+SAXStrings.indexOfWhitespace = function(strD, iB, iE) {
+ if(SAXStrings.isEmpty(strD)) {
+ return -1;
+ }
+ iB = iB || 0;
+ iE = iE || strD.length;
+
+ for(var i = iB; i < iE; i++) {
+ if(SAXStrings.WHITESPACE.indexOf(strD.charAt(i)) != -1) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+SAXStrings.isEmpty = function(strD) {
+ return (strD == null) || (strD.length == 0);
+}
+
+SAXStrings.lastIndexOfNonWhitespace = function(strD, iB, iE) {
+ if(SAXStrings.isEmpty(strD)) {
+ return -1;
+ }
+ iB = iB || 0;
+ iE = iE || strD.length;
+
+ for(var i = iE - 1; i >= iB; i--){
+ if(SAXStrings.WHITESPACE.indexOf(strD.charAt(i)) == -1){
+ return i;
+ }
+ }
+ return -1;
+}
+
+SAXStrings.replace = function(strD, iB, iE, strF, strR) {
+ if(SAXStrings.isEmpty(strD)) {
+ return "";
+ }
+ iB = iB || 0;
+ iE = iE || strD.length;
+
+ return strD.toString().substring(iB, iE).split(strF).join(strR);
+
+}
+
+var Stack = function() {
+ this.m_arr = new Array();
+}
+
+Stack.prototype.clear = function() {
+ this.m_arr = new Array();
+}
+
+Stack.prototype.count = function() {
+ return this.m_arr.length;
+}
+
+Stack.prototype.destroy = function() {
+ this.m_arr = null;
+}
+
+Stack.prototype.peek = function() {
+ if(this.m_arr.length == 0) {
+ return null;
+ }
+
+ return this.m_arr[this.m_arr.length - 1];
+
+}
+
+Stack.prototype.pop = function() {
+ if(this.m_arr.length == 0) {
+ return null;
+ }
+
+ var o = this.m_arr[this.m_arr.length - 1];
+ this.m_arr.length--;
+ return o;
+
+}
+
+Stack.prototype.push = function(o) {
+ this.m_arr[this.m_arr.length] = o;
+}
+
+// CONVENIENCE FUNCTIONS
+function isEmpty(str) {
+ return (str==null) || (str.length==0);
+}
+
+
+function trim(trimString, leftTrim, rightTrim) {
+ if (isEmpty(trimString)) {
+ return "";
+ }
+
+ // the general focus here is on minimal method calls - hence only one
+ // substring is done to complete the trim.
+
+ if (leftTrim == null) {
+ leftTrim = true;
+ }
+
+ if (rightTrim == null) {
+ rightTrim = true;
+ }
+
+ var left=0;
+ var right=0;
+ var i=0;
+ var k=0;
+
+
+ // modified to properly handle strings that are all whitespace
+ if (leftTrim == true) {
+ while ((i<trimString.length) && (whitespace.indexOf(trimString.charAt(i++))!=-1)) {
+ left++;
+ }
+ }
+ if (rightTrim == true) {
+ k=trimString.length-1;
+ while((k>=left) && (whitespace.indexOf(trimString.charAt(k--))!=-1)) {
+ right++;
+ }
+ }
+ return trimString.substring(left, trimString.length - right);
+}
+
+function __escapeString(str) {
+
+ var escAmpRegEx = /&/g;
+ var escLtRegEx = /</g;
+ var escGtRegEx = />/g;
+ var quotRegEx = /"/g;
+ var aposRegEx = /'/g;
+
+ str = str.replace(escAmpRegEx, "&amp;");
+ str = str.replace(escLtRegEx, "&lt;");
+ str = str.replace(escGtRegEx, "&gt;");
+ str = str.replace(quotRegEx, "&quot;");
+ str = str.replace(aposRegEx, "&apos;");
+
+ return str;
+}
+
+function __unescapeString(str) {
+
+ var escAmpRegEx = /&amp;/g;
+ var escLtRegEx = /&lt;/g;
+ var escGtRegEx = /&gt;/g;
+ var quotRegEx = /&quot;/g;
+ var aposRegEx = /&apos;/g;
+
+ str = str.replace(escAmpRegEx, "&");
+ str = str.replace(escLtRegEx, "<");
+ str = str.replace(escGtRegEx, ">");
+ str = str.replace(quotRegEx, "\"");
+ str = str.replace(aposRegEx, "'");
+
+ return str;
+}
+
+exports.SaxParser = SaxParser;
+
+
+})()
4,120 nodeblogs.com.feed.xml
4,120 additions, 0 deletions not shown
Please sign in to comment.
Something went wrong with that request. Please try again.