/* OpenDocument */
var parse_content_xml = (function() {
	/* 6.1.2 White Space Characters */
	var parse_text_p = function(text/*:string*//*::, tag*/)/*:string*/ {
		return unescapexml(text
			.replace(/[\t\r\n]/g, " ").trim().replace(/ +/g, " ")
			.replace(//g," ")
			.replace(//g, function($$,$1) { return Array(parseInt($1,10)+1).join(" "); })
			.replace(/]*\/>/g,"\t")
			.replace(//g,"\n")
			.replace(/<[^>]*>/g,"")
		);
	};
	var number_formats = {
		/* ods name: [short ssf fmt, long ssf fmt] */
		day:           ["d",   "dd"],
		month:         ["m",   "mm"],
		year:          ["y",   "yy"],
		hours:         ["h",   "hh"],
		minutes:       ["m",   "mm"],
		seconds:       ["s",   "ss"],
		"am-pm":       ["A/P", "AM/PM"],
		"day-of-week": ["ddd", "dddd"],
		era:           ["e",   "ee"],
		/* there is no native representation of LO "Q" format */
		quarter:       ["\\Qm", "m\\\"th quarter\""]
	};
	return function pcx(d/*:string*/, _opts)/*:Workbook*/ {
		var opts = _opts || {};
		if(DENSE != null && opts.dense == null) opts.dense = DENSE;
		var str = xlml_normalize(d);
		var state/*:Array*/ = [], tmp;
		var tag/*:: = {}*/;
		var NFtag = {name:""}, NF = "", pidx = 0;
		var sheetag/*:: = {name:"", '名称':""}*/;
		var rowtag/*:: = {'行号':""}*/;
		var Sheets = {}, SheetNames/*:Array*/ = [];
		var ws = opts.dense ? ([]/*:any*/) : ({}/*:any*/);
		var Rn, q/*:: :any = ({t:"", v:null, z:null, w:"",c:[],}:any)*/;
		var ctag = ({value:""}/*:any*/);
		var textp = "", textpidx = 0, textptag/*:: = {}*/;
		var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
		var row_ol = 0;
		var number_format_map = {};
		var merges/*:Array*/ = [], mrange = {}, mR = 0, mC = 0;
		var rowinfo/*:Array*/ = [], rowpeat = 1, colpeat = 1;
		var arrayf/*:Array<[Range, string]>*/ = [];
		var WB = {Names:[]};
		var atag = ({}/*:any*/);
		var _Ref/*:[string, string]*/ = ["", ""];
		var comments/*:Array*/ = [], comment/*:Comment*/ = ({}/*:any*/);
		var creator = "", creatoridx = 0;
		var isstub = false, intable = false;
		var i = 0;
		xlmlregex.lastIndex = 0;
		str = str.replace(//mg,"").replace(//gm,"");
		while((Rn = xlmlregex.exec(str))) switch((Rn[3]=Rn[3].replace(/_.*$/,""))) {
			case 'table': case '工作表': // 9.1.2 
				if(Rn[1]==='/') {
					if(range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = encode_range(range);
					if(opts.sheetRows > 0 && opts.sheetRows <= range.e.r) {
						ws['!fullref'] = ws['!ref'];
						range.e.r = opts.sheetRows - 1;
						ws['!ref'] = encode_range(range);
					}
					if(merges.length) ws['!merges'] = merges;
					if(rowinfo.length) ws["!rows"] = rowinfo;
					sheetag.name = sheetag['名称'] || sheetag.name;
					if(typeof JSON !== 'undefined') JSON.stringify(sheetag);
					SheetNames.push(sheetag.name);
					Sheets[sheetag.name] = ws;
					intable = false;
				}
				else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
					sheetag = parsexmltag(Rn[0], false);
					R = C = -1;
					range.s.r = range.s.c = 10000000; range.e.r = range.e.c = 0;
					ws = opts.dense ? ([]/*:any*/) : ({}/*:any*/); merges = [];
					rowinfo = [];
					intable = true;
				}
				break;
			case 'table-row-group': // 9.1.9 
				if(Rn[1] === "/") --row_ol; else ++row_ol;
				break;
			case 'table-row': case '行': // 9.1.3 
				if(Rn[1] === '/') { R+=rowpeat; rowpeat = 1; break; }
				rowtag = parsexmltag(Rn[0], false);
				if(rowtag['行号']) R = rowtag['行号'] - 1; else if(R == -1) R = 0;
				rowpeat = +rowtag['number-rows-repeated'] || 1;
				/* TODO: remove magic */
				if(rowpeat < 10) for(i = 0; i < rowpeat; ++i) if(row_ol > 0) rowinfo[R + i] = {level: row_ol};
				C = -1; break;
			case 'covered-table-cell': // 9.1.5 
				++C;
				if(opts.sheetStubs) {
					if(opts.dense) { if(!ws[R]) ws[R] = []; ws[R][C] = {t:'z'}; }
					else ws[encode_cell({r:R,c:C})] = {t:'z'};
				}
				break; /* stub */
			case 'table-cell': case '数据':
				if(Rn[0].charAt(Rn[0].length-2) === '/') {
					++C;
					ctag = parsexmltag(Rn[0], false);
					colpeat = parseInt(ctag['number-columns-repeated']||"1", 10);
					q = ({t:'z', v:null/*:: , z:null, w:"",c:[]*/}/*:any*/);
					if(ctag.formula && opts.cellFormula != false) q.f = ods_to_csf_formula(unescapexml(ctag.formula));
					if((ctag['数据类型'] || ctag['value-type']) == "string") {
						q.t = "s"; q.v = unescapexml(ctag['string-value'] || "");
						if(opts.dense) {
							if(!ws[R]) ws[R] = [];
							ws[R][C] = q;
						} else {
							ws[encode_cell({r:R,c:C})] = q;
						}
					}
					C+= colpeat-1;
				} else if(Rn[1]!=='/') {
					++C;
					colpeat = 1;
					var rptR = rowpeat ? R + rowpeat - 1 : R;
					if(C > range.e.c) range.e.c = C;
					if(C < range.s.c) range.s.c = C;
					if(R < range.s.r) range.s.r = R;
					if(rptR > range.e.r) range.e.r = rptR;
					ctag = parsexmltag(Rn[0], false);
					comments = []; comment = ({}/*:any*/);
					q = ({t:ctag['数据类型'] || ctag['value-type'], v:null/*:: , z:null, w:"",c:[]*/}/*:any*/);
					if(opts.cellFormula) {
						if(ctag.formula) ctag.formula = unescapexml(ctag.formula);
						if(ctag['number-matrix-columns-spanned'] && ctag['number-matrix-rows-spanned']) {
							mR = parseInt(ctag['number-matrix-rows-spanned'],10) || 0;
							mC = parseInt(ctag['number-matrix-columns-spanned'],10) || 0;
							mrange = {s: {r:R,c:C}, e:{r:R + mR-1,c:C + mC-1}};
							q.F = encode_range(mrange);
							arrayf.push([mrange, q.F]);
						}
						if(ctag.formula) q.f = ods_to_csf_formula(ctag.formula);
						else for(i = 0; i < arrayf.length; ++i)
							if(R >= arrayf[i][0].s.r && R <= arrayf[i][0].e.r)
								if(C >= arrayf[i][0].s.c && C <= arrayf[i][0].e.c)
									q.F = arrayf[i][1];
					}
					if(ctag['number-columns-spanned'] || ctag['number-rows-spanned']) {
						mR = parseInt(ctag['number-rows-spanned'],10) || 0;
						mC = parseInt(ctag['number-columns-spanned'],10) || 0;
						mrange = {s: {r:R,c:C}, e:{r:R + mR-1,c:C + mC-1}};
						merges.push(mrange);
					}
					/* 19.675.2 table:number-columns-repeated */
					if(ctag['number-columns-repeated']) colpeat = parseInt(ctag['number-columns-repeated'], 10);
					/* 19.385 office:value-type */
					switch(q.t) {
						case 'boolean': q.t = 'b'; q.v = parsexmlbool(ctag['boolean-value']); break;
						case 'float': q.t = 'n'; q.v = parseFloat(ctag.value); break;
						case 'percentage': q.t = 'n'; q.v = parseFloat(ctag.value); break;
						case 'currency': q.t = 'n'; q.v = parseFloat(ctag.value); break;
						case 'date': q.t = 'd'; q.v = parseDate(ctag['date-value']);
							if(!opts.cellDates) { q.t = 'n'; q.v = datenum(q.v); }
							q.z = 'm/d/yy'; break;
						case 'time': q.t = 'n'; q.v = parse_isodur(ctag['time-value'])/86400; break;
						case 'number': q.t = 'n'; q.v = parseFloat(ctag['数据数值']); break;
						default:
							if(q.t === 'string' || q.t === 'text' || !q.t) {
								q.t = 's';
								if(ctag['string-value'] != null) textp = unescapexml(ctag['string-value']);
							} else throw new Error('Unsupported value type ' + q.t);
					}
				} else {
					isstub = false;
					if(q.t === 's') {
						q.v = textp || '';
						isstub = textpidx == 0;
					}
					if(atag.Target) q.l = atag;
					if(comments.length > 0) { q.c = comments; comments = []; }
					if(textp && opts.cellText !== false) q.w = textp;
					if(!isstub || opts.sheetStubs) {
						if(!(opts.sheetRows && opts.sheetRows <= R)) {
							for(var rpt = 0; rpt < rowpeat; ++rpt) {
								colpeat = parseInt(ctag['number-columns-repeated']||"1", 10);
								if(opts.dense) {
									if(!ws[R + rpt]) ws[R + rpt] = [];
									ws[R + rpt][C] = rpt == 0 ? q : dup(q);
									while(--colpeat > 0) ws[R + rpt][C + colpeat] = dup(q);
								} else {
									ws[encode_cell({r:R + rpt,c:C})] = q;
									while(--colpeat > 0) ws[encode_cell({r:R + rpt,c:C + colpeat})] = dup(q);
								}
								if(range.e.c <= C) range.e.c = C;
							}
						}
					}
					colpeat = parseInt(ctag['number-columns-repeated']||"1", 10);
					C += colpeat-1; colpeat = 0;
					q = {/*:: t:"", v:null, z:null, w:"",c:[]*/};
					textp = "";
				}
				atag = ({}/*:any*/);
				break; // 9.1.4 
			/* pure state */
			case 'document': // TODO:  is the root for FODS
			case 'document-content': case '电子表格文档': // 3.1.3.2 
			case 'spreadsheet': case '主体': // 3.7 
			case 'scripts': // 3.12 
			case 'styles': // TODO 
			case 'font-face-decls': // 3.14 
				if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
				else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]);
				break;
			case 'annotation': // 14.1 
				if(Rn[1]==='/'){
					if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;
					comment.t = textp;
					comment.a = creator;
					comments.push(comment);
				}
				else if(Rn[0].charAt(Rn[0].length-2) !== '/') {state.push([Rn[3], false]);}
				creator = ""; creatoridx = 0;
				textp = ""; textpidx = 0;
				break;
			case 'creator': // 4.3.2.7 
				if(Rn[1]==='/') { creator = str.slice(creatoridx,Rn.index); }
				else creatoridx = Rn.index + Rn[0].length;
				break;
			/* ignore state */
			case 'meta': case '元数据': // TODO:   FODS/UOF
			case 'settings': // TODO: 
			case 'config-item-set': // TODO: 
			case 'config-item-map-indexed': // TODO: 
			case 'config-item-map-entry': // TODO: 
			case 'config-item-map-named': // TODO: 
			case 'shapes': // 9.2.8 
			case 'frame': // 10.4.2 
			case 'text-box': // 10.4.3 
			case 'image': // 10.4.4 
			case 'data-pilot-tables': // 9.6.2 
			case 'list-style': // 16.30 
			case 'form': // 13.13 
			case 'dde-links': // 9.8 
			case 'event-listeners': // TODO
			case 'chart': // TODO
				if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
				else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], false]);
				textp = ""; textpidx = 0;
				break;
			case 'scientific-number': // TODO: 
				break;
			case 'currency-symbol': // TODO: 
				break;
			case 'currency-style': // TODO: 
				break;
			case 'number-style': // 16.27.2 
			case 'percentage-style': // 16.27.9 
			case 'date-style': // 16.27.10 
			case 'time-style': // 16.27.18 
				if(Rn[1]==='/'){
					number_format_map[NFtag.name] = NF;
					if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;
				} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
					NF = "";
					NFtag = parsexmltag(Rn[0], false);
					state.push([Rn[3], true]);
				} break;
			case 'script': break; // 3.13 
			case 'libraries': break; // TODO: 
			case 'automatic-styles': break; // 3.15.3 
			case 'master-styles': break; // TODO: 
			case 'default-style': // TODO: 
			case 'page-layout': break; // TODO: 
			case 'style': break; // 16.2 
			case 'map': break; // 16.3 
			case 'font-face': break; // 16.21 
			case 'paragraph-properties': break; // 17.6 
			case 'table-properties': break; // 17.15 
			case 'table-column-properties': break; // 17.16 
			case 'table-row-properties': break; // 17.17 
			case 'table-cell-properties': break; // 17.18 
			case 'number': // 16.27.3 
				switch(state[state.length-1][0]) {
					case 'time-style':
					case 'date-style':
						tag = parsexmltag(Rn[0], false);
						NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
				} break;
			case 'fraction': break; // TODO 16.27.6 
			case 'day': // 16.27.11 
			case 'month': // 16.27.12 
			case 'year': // 16.27.13 
			case 'era': // 16.27.14 
			case 'day-of-week': // 16.27.15 
			case 'week-of-year': // 16.27.16 
			case 'quarter': // 16.27.17 
			case 'hours': // 16.27.19 
			case 'minutes': // 16.27.20 
			case 'seconds': // 16.27.21 
			case 'am-pm': // 16.27.22 
				switch(state[state.length-1][0]) {
					case 'time-style':
					case 'date-style':
						tag = parsexmltag(Rn[0], false);
						NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
				} break;
			case 'boolean-style': break; // 16.27.23 
			case 'boolean': break; // 16.27.24 
			case 'text-style': break; // 16.27.25 
			case 'text': // 16.27.26 
				if(Rn[0].slice(-2) === "/>") break;
				else if(Rn[1]==="/") switch(state[state.length-1][0]) {
					case 'number-style':
					case 'date-style':
					case 'time-style':
						NF += str.slice(pidx, Rn.index);
						break;
				}
				else pidx = Rn.index + Rn[0].length;
				break;
			case 'named-range': // 9.4.12 
				tag = parsexmltag(Rn[0], false);
				_Ref = ods_to_csf_3D(tag['cell-range-address']);
				var nrange = ({Name:tag.name, Ref:_Ref[0] + '!' + _Ref[1]}/*:any*/);
				if(intable) nrange.Sheet = SheetNames.length;
				WB.Names.push(nrange);
				break;
			case 'text-content': break; // 16.27.27 
			case 'text-properties': break; // 16.27.27 
			case 'embedded-text': break; // 16.27.4 
			case 'body': case '电子表格': break; // 3.3 16.9.6 19.726.3
			case 'forms': break; // 12.25.2 13.2
			case 'table-column': break; // 9.1.6 
			case 'table-header-rows': break; // 9.1.7 
			case 'table-rows': break; // 9.1.12 
			/* TODO: outline levels */
			case 'table-column-group': break; // 9.1.10 
			case 'table-header-columns': break; // 9.1.11 
			case 'table-columns': break; // 9.1.12 
			case 'null-date': break; // 9.4.2  TODO: date1904
			case 'graphic-properties': break; // 17.21 
			case 'calculation-settings': break; // 9.4.1 
			case 'named-expressions': break; // 9.4.11 
			case 'label-range': break; // 9.4.9 
			case 'label-ranges': break; // 9.4.10 
			case 'named-expression': break; // 9.4.13 
			case 'sort': break; // 9.4.19 
			case 'sort-by': break; // 9.4.20 
			case 'sort-groups': break; // 9.4.22 
			case 'tab': break; // 6.1.4 
			case 'line-break': break; // 6.1.5 
			case 'span': break; // 6.1.7 
			case 'p': case '文本串': // 5.1.3 
				if(Rn[1]==='/' && (!ctag || !ctag['string-value'])) textp = (textp.length > 0 ? textp + "\n" : "") + parse_text_p(str.slice(textpidx,Rn.index), textptag);
				else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; }
				break; // 
			case 's': break; // 
			case 'database-range': // 9.4.15 
				if(Rn[1]==='/') break;
				try {
					_Ref = ods_to_csf_3D(parsexmltag(Rn[0])['target-range-address']);
					Sheets[_Ref[0]]['!autofilter'] = { ref:_Ref[1] };
				} catch(e) {/* empty */}
				break;
			case 'date': break; // <*:date>
			case 'object': break; // 10.4.6.2 
			case 'title': case '标题': break; // <*:title> OR 
			case 'desc': break; // <*:desc>
			case 'binary-data': break; // 10.4.5 TODO: b64 blob
			/* 9.2 Advanced Tables */
			case 'table-source': break; // 9.2.6
			case 'scenario': break; // 9.2.6
			case 'iteration': break; // 9.4.3 
			case 'content-validations': break; // 9.4.4 
			case 'filter': break; // 9.5.2 
			case 'filter-and': break; // 9.5.3 
			case 'filter-or': break; // 9.5.4 
			case 'filter-condition': break; // 9.5.5 
			case 'list-level-style-bullet': break; // 16.31 
			case 'page-count': break; // TODO 
			case 'time': break; // TODO 
			/* 9.3 Advanced Table Cells */
			case 'cell-range-source': break; // 9.3.1 
			case 'property': break; // 13.8 
			case 'a': // 6.1.8 hyperlink
				if(Rn[1]!== '/') {
					atag = parsexmltag(Rn[0], false);
					if(!atag.href) break;
					atag.Target = atag.href; delete atag.href;
					if(atag.Target.charAt(0) == "#" && atag.Target.indexOf(".") > -1) {
						_Ref = ods_to_csf_3D(atag.Target.slice(1));
						atag.Target = "#" + _Ref[0] + "!" + _Ref[1];
					}
				}
				break;
			/* non-standard */
			case 'table-protection': break;
			case 'data-pilot-grand-total': break; //