/* new function() { var parser = new EasySAXParser(); parser.ns('rss', { // or false rss: 'http://purl.org/rss/1.0/', atom: 'http://www.w3.org/2005/Atom', xhtml: 'http://www.w3.org/1999/xhtml', media: 'http://search.yahoo.com/mrss/' }); parser.on('error', function(msg) { //console.log(msg) }); parser.on('startNode', function(elem, attr, uq, tagend, getStrNode) { attr(); return; if (tagend) { console.log(' '+str) } else { console.log('+ '+str) }; }); parser.on('endNode', function(elem, uq, tagstart, str) { return; if (!tagstart) console.log('- ' + str) }); parser.on('textNode', function(s, uq) { uq(s); return console.log(' '+s) }); parser.on('cdata', function(data) { }); parser.on('comment', function(text) { //console.log('--'+text+'--') }); //parser.on('question', function() {}); // //parser.on('attention', function() {}); // console.time('easysax'); for(var z=1000;z--;) { parser.parse(xml) }; console.timeEnd('easysax'); }; */ // << ------------------------------------------------------------------------ >> // if (typeof exports === 'object' /*&& this == exports*/) { module.exports.EasySAXParser = EasySAXParser; }; function EasySAXParser() { 'use strict'; if (!this) return null; function nullFunc() {}; var onTextNode = nullFunc, onStartNode = nullFunc, onEndNode = nullFunc, onCDATA = nullFunc, onError = nullFunc, onComment, onQuestion, onAttention; var is_onComment, is_onQuestion, is_onAttention; var isNamespace = false, useNS , default_xmlns, xmlns , nsmatrix = {xmlns: xmlns} , hasSurmiseNS = false ; this.on = function(name, cb) { if (typeof cb !== 'function') { if (cb !== null) return; }; switch(name) { case 'error': onError = cb || nullFunc; break; case 'startNode': onStartNode = cb || nullFunc; break; case 'endNode': onEndNode = cb || nullFunc; break; case 'textNode': onTextNode = cb || nullFunc; break; case 'cdata': onCDATA = cb || nullFunc; break; case 'comment': onComment = cb; is_onComment = !!cb; break; case 'question': onQuestion = cb; is_onQuestion = !!cb; break; // case 'attention': onAttention = cb; is_onAttention = !!cb; break; // }; }; this.ns = function(root, ns) { if (!root || typeof root !== 'string' || !ns) { return; }; var u, x = {}, ok, v, i; for(i in ns) { v = ns[i]; if (typeof v === 'string') { if (root === v) ok = true; x[i] = v; }; }; if (ok) { isNamespace = true; default_xmlns = root; useNS = x; }; }; this.parse = function(xml) { if (typeof xml !== 'string') { return; }; if (isNamespace) { nsmatrix = {xmlns: default_xmlns}; parse(xml); nsmatrix = false; } else { parse(xml); }; attr_res = true; }; // ----------------------------------------------------- var xharsQuot={constructor: false, hasOwnProperty: false, isPrototypeOf: false, propertyIsEnumerable: false, toLocaleString: false, toString: false, valueOf: false , quot: '"' , QUOT: '"' , amp: '&' , AMP: '&' , nbsp: '\u00A0' , apos: '\'' , lt: '<' , LT: '<' , gt: '>' , GT: '>' , copy: '\u00A9' , laquo: '\u00AB' , raquo: '\u00BB' , reg: '\u00AE' , deg: '\u00B0' , plusmn: '\u00B1' , sup2: '\u00B2' , sup3: '\u00B3' , micro: '\u00B5' , para: '\u00B6' }; function rpEntities(s, d, x, z) { if (z) { return xharsQuot[z] || '\x01'; }; if (d) { return String.fromCharCode(d); }; return String.fromCharCode(parseInt(x, 16)); }; function unEntities(s, i) { s = String(s); if (s.length > 3 && s.indexOf('&') !== -1) { if (s.indexOf('>') !== -1) s = s.replace(/>/g, '>'); if (s.indexOf('<') !== -1) s = s.replace(/</g, '<'); if (s.indexOf('"') !== -1) s = s.replace(/"/g, '"'); if (s.indexOf('&') !== -1) { s = s.replace(/&#(\d+);|&#x([0123456789abcdef]+);|&(\w+);/ig, rpEntities); }; }; return s; }; var attr_string = ''; // строка атрибутов var attr_posstart = 0; // var attr_res; // закешированный результат разбора атрибутов , null - разбор не проводился, object - хеш атрибутов, true - нет атрибутов, false - невалидный xml /* парсит атрибуты по требованию. Важно! - функция не генерирует исключения. если была ошибка разбора возврашается false если атрибутов нет и разбор удачен то возврашается true если есть атрибуты то возврашается обьект(хеш) */ var RGX_ATTR_NAME = /[^\w:-]+/g; function getAttrs() { if (attr_res !== null) { return attr_res; }; /* if (xxtest !== u && attr_string.indexOf(xxtest) === -1) { / * // для ускорения if (getAttrs('html').type == 'html') { ... }; * / return true; }; */ var u , res = {} , s = attr_string , i = attr_posstart , l = s.length , attr_list = hasSurmiseNS ? [] : false , name, value = '' , ok = false , noValueAttribute = false , j, w, nn, n , hasNewMatrix , alias, newalias ; aa: for(; i < l; i++) { w = s.charCodeAt(i); if (w===32 || (w<14 && w > 8) ) { // \f\n\r\t\v continue }; if ((w < 65 && w !== 40 && w !== 41 && w !== 35) || // allow parens () -- used for angular syntax w > 122 || (w === 92 || (w > 93 && w < 97)) ) { // ожидаем символ return attr_res = false; // error. invalid char }; for(j = i + 1; j < l; j++) { // проверяем все символы имени атрибута w = s.charCodeAt(j); if (w > 96 && w < 123 || w > 64 && w < 91 || w > 47 && w < 59 || w === 45 || w === 95 || w === 46 /* https://github.com/telerik/xPlatCore/issues/179 */) { if (noValueAttribute) { j--; //Started next attribute. Get back and break out of the loop. break; } else { continue; } }; if (w === 91 || w === 93 || w === 40 || w === 41 || w === 94 || w === 35) { // Angular special attribute chars:[]()^ continue; } if (w === 32 || (w > 8 && w < 14) ) { // \f\n\r\t\v пробел noValueAttribute = true; continue; } else if (w === 61) { // "=" == 61 noValueAttribute = false; break; } else { //console.log('error 2'); if (!noValueAttribute) return attr_res = false; // error. invalid char }; break; }; name = s.substring(i, j).trim(); ok = true; if (name === 'xmlns:xmlns') { //console.log('error 6') return attr_res = false; // error. invalid name }; w = s.charCodeAt(j+1); while (w = s.charCodeAt(j+1)) { if (w===32 || (w > 8 && w<14) ) { // \f\n\r\t\v пробел j++; } else { break; } } if (!noValueAttribute) { if (w === 34) { // '"' j = s.indexOf('"', i = j+2 ); } else { if (w === 39) { j = s.indexOf('\'', i = j+2 ); } else { // "'" return attr_res = false; // error. invalid char }; }; } if (j === -1) { //console.log('error 4') return attr_res = false; // error. invalid char }; if (j+1 < l && !noValueAttribute) { w = s.charCodeAt(j+1); if (w > 32 || w < 9 || (w < 32 && w > 13)) { // error. invalid char //console.log('error 5') return attr_res = false; }; }; if (noValueAttribute) { value = ''; } else { value = s.substring(i, j); } //i = j + 1; // след. семвол уже проверен потому проверять нужно следуюший i = j; // след. семвол уже проверен потому проверять нужно следуюший if (isNamespace) { // if (hasSurmiseNS) { // есть подозрение что в атрибутах присутствует xmlns if (newalias = name === 'xmlns' ? 'xmlns' : name.charCodeAt(0) === 120 && name.substr(0, 6) === 'xmlns:' && name.substr(6) ) { alias = useNS[unEntities(value)]; if (alias) { if (nsmatrix[newalias] !== alias) { if (!hasNewMatrix) { hasNewMatrix = true; nn = {}; for (n in nsmatrix) nn[n] = nsmatrix[n]; nsmatrix = nn; }; nsmatrix[newalias] = alias; }; } else { if (nsmatrix[newalias]) { if (!hasNewMatrix) { hasNewMatrix = true; nn = {}; for (n in nsmatrix) nn[n] = nsmatrix[n]; nsmatrix = nn; }; nsmatrix[newalias] = false; }; }; res[name] = value; continue; }; attr_list.push(name, value); continue; }; w = name.length; while(--w) { if (name.charCodeAt(w) === 58) { // ':' if (w = nsmatrix[name.substring(0, w)] ) { res[w + name.substr(w)] = value; }; continue aa; // 'xml:base' ??? }; }; }; res[name] = value; noValueAttribute = false; }; if (!ok) { return attr_res = true; // атрибутов нет, ошибок тоже нет }; if (hasSurmiseNS) { bb: for (i = 0, l = attr_list.length; i < l; i++) { name = attr_list[i++]; w = name.length; while(--w) { // name.indexOf(':') if (name.charCodeAt(w) === 58) { // ':' if (w = nsmatrix[name.substring(0, w)]) { res[w + name.substr(w)] = attr_list[i]; }; continue bb; break; }; }; res[name] = attr_list[i]; }; }; return attr_res = res; }; // xml - string function parse(xml) { var u , xml = String(xml) , nodestack = [] , stacknsmatrix = [] //, string_node , elem , tagend = false , tagstart = false , j = 0, i = 0 , x, y, q, w , xmlns , stopIndex = 0 , stop // используется при разборе "namespace" . если встретился неизвестное пространство то события не генерируются , _nsmatrix , ok ; function getStringNode() { return xml.substring(i, j+1) }; while(j !== -1) { stop = stopIndex > 0; if (xml.charCodeAt(j) === 60) { // "<" i = j; } else { i = xml.indexOf('<', j); }; if (i === -1) { // конец разбора if (nodestack.length) { onError('end file'); return; }; return; }; if (j !== i && !stop) { ok = onTextNode(xml.substring(j, i), unEntities); if (ok === false) return; }; w = xml.charCodeAt(i+1); if (w === 33) { // "!" w = xml.charCodeAt(i+2); if (w === 91 && xml.substr(i+3, 6) === 'CDATA[') { // 91 == "[" j = xml.indexOf(']]>', i); if (j === -1) { onError('cdata'); return; }; //x = xml.substring(i+9, j); if (!stop) { ok = onCDATA(xml.substring(i+9, j), false); if (ok === false) return; }; j += 3; continue; }; if (w === 45 && xml.charCodeAt(i+3) === 45) { // 45 == "-" j = xml.indexOf('-->', i); if (j === -1) { onError('expected -->'); return; }; if (is_onComment && !stop) { ok = onComment(xml.substring(i+4, j), unEntities); if (ok === false) return; }; j += 3; continue; }; j = xml.indexOf('>', i+1); if (j === -1) { onError('expected ">"'); return; }; if (is_onAttention && !stop) { ok = onAttention(xml.substring(i, j+1), unEntities); if (ok === false) return; }; j += 1; continue; } else { if (w === 63) { // "?" j = xml.indexOf('?>', i); if (j === -1) { // error onError('...?>'); return; }; if (is_onQuestion) { ok = onQuestion(xml.substring(i, j+2)); if (ok === false) return; }; j += 2; continue; }; }; j = xml.indexOf('>', i+1); if (j == -1) { // error onError('...>'); return; }; attr_res = true; // атрибутов нет //if (xml.charCodeAt(i+1) === 47) { // 8 && w<14) ) { // \f\n\r\t\v пробел continue; }; onError('close tag'); return; }; } else { if (xml.charCodeAt(j-1) === 47) { // .../> x = elem = xml.substring(i+1, j-1); tagstart = true; tagend = true; } else { x = elem = xml.substring(i+1, j); tagstart = true; tagend = false; }; if ( !(w > 96 && w < 123 || w > 64 && w <91) ) { onError('first char nodeName'); return; }; for(q = 1, y = x.length; q < y; q++) { w = x.charCodeAt(q); if (w > 96 && w < 123 || w > 64 && w < 91 || w > 47 && w < 59 || w === 45 || w === 95 || w === 46 /* https://github.com/telerik/xPlatCore/issues/179 */) { continue; }; if (w===32 || (w<14 && w > 8)) { // \f\n\r\t\v пробел elem = x.substring(0, q) attr_res = null; // возможно есть атирибуты break; }; onError('invalid nodeName'); return; }; if (!tagend) { nodestack.push(elem); }; }; if (isNamespace) { if (stop) { if (tagend) { if (!tagstart) { if (--stopIndex === 0) { nsmatrix = stacknsmatrix.pop(); }; }; } else { stopIndex += 1; }; j += 1; continue; }; _nsmatrix = nsmatrix; if (!tagend) { stacknsmatrix.push(nsmatrix); if (attr_res !== true) { if (hasSurmiseNS = x.indexOf('xmlns', q) !== -1) { attr_string = x; attr_posstart = q; getAttrs(); hasSurmiseNS = false; }; }; }; w = elem.indexOf(':'); if (w !== -1) { xmlns = nsmatrix[elem.substring(0, w)]; elem = elem.substr(w+1); } else { xmlns = nsmatrix.xmlns; }; if (!xmlns) { if (tagend) { if (tagstart) { nsmatrix = _nsmatrix; } else { nsmatrix = stacknsmatrix.pop(); }; } else { stopIndex = 1; // первый элемент для которого не определено пространство имен attr_res = true; }; j += 1; continue; }; elem = xmlns + ':' + elem; }; //string_node = xml.substring(i, j+1); // текст ноды как есть if (tagstart) { // is_onStartNode attr_string = x; attr_posstart = q; ok = onStartNode(elem, getAttrs, unEntities, tagend , getStringNode ); if (ok === false) { return; }; attr_res = true; }; if (tagend) { ok = onEndNode(elem, unEntities, tagstart , getStringNode ); if (ok === false) { return; }; if (isNamespace) { if (tagstart) { nsmatrix = _nsmatrix; } else { nsmatrix = stacknsmatrix.pop(); }; }; }; j += 1; }; }; };