void GetTag(out TagType tagtype, out string id, out TagAttributes attributes) { int token = tokenizer.get_token(); tagtype = TagType.ServerComment; id = null; attributes = null; switch (token) { case '%': GetServerTag(out tagtype, out id, out attributes); break; case '/': if (!Eat(Token.IDENTIFIER)) { OnError("expecting TAGNAME"); } id = tokenizer.Value; if (!Eat('>')) { OnError("expecting '>'. Got '" + id + "'"); } tagtype = TagType.Close; break; case '!': bool double_dash = Eat(Token.DOUBLEDASH); if (double_dash) { tokenizer.put_back(); } tokenizer.Verbatim = true; string end = double_dash ? "-->" : ">"; string comment = GetVerbatim(tokenizer.get_token(), end); tokenizer.Verbatim = false; if (comment == null) { OnError("Unfinished HTML comment/DTD"); } string pathType, filename; if (double_dash && GetInclude(comment, out pathType, out filename)) { tagtype = TagType.Include; attributes = new TagAttributes(); attributes.Add(pathType, filename); } else { tagtype = TagType.Text; id = "<!" + comment + end; } break; case Token.IDENTIFIER: if (this.filename == "@@inner_string@@") { // Actually not tag but "xxx < yyy" stuff in inner_string! tagtype = TagType.Text; tokenizer.InTag = false; id = "<" + tokenizer.Odds + tokenizer.Value; } else { id = tokenizer.Value; try { attributes = GetAttributes(); } catch (Exception e) { OnError(e.Message); break; } tagtype = TagType.Tag; if (Eat('/') && Eat('>')) { tagtype = TagType.SelfClosing; } else if (!Eat('>')) { if (attributes.IsRunAtServer()) { OnError("The server tag is not well formed."); break; } tokenizer.Verbatim = true; attributes.Add("", GetVerbatim(tokenizer.get_token(), ">") + ">"); tokenizer.Verbatim = false; } } break; default: tagtype = TagType.Text; tokenizer.InTag = false; id = "<" + tokenizer.Value; break; } }
TagAttributes GetAttributes() { int token; TagAttributes attributes; string id; bool wellFormedForServer = true; attributes = new TagAttributes(); while ((token = tokenizer.get_token()) != Token.EOF) { if (token == '<' && Eat('%')) { tokenizer.Verbatim = true; attributes.Add("", "<%" + GetVerbatim(tokenizer.get_token(), "%>") + "%>"); tokenizer.Verbatim = false; tokenizer.InTag = true; continue; } if (token != Token.IDENTIFIER) { break; } id = tokenizer.Value; if (Eat('=')) { if (Eat(Token.ATTVALUE)) { attributes.Add(id, tokenizer.Value); wellFormedForServer &= tokenizer.AlternatingQuotes; } else if (Eat('<') && Eat('%')) { tokenizer.Verbatim = true; attributes.Add(id, "<%" + GetVerbatim(tokenizer.get_token(), "%>") + "%>"); tokenizer.Verbatim = false; tokenizer.InTag = true; } else { OnError("expected ATTVALUE"); return(null); } } else { attributes.Add(id, null); } } tokenizer.put_back(); if (attributes.IsRunAtServer() && !wellFormedForServer) { OnError("The server tag is not well formed."); return(null); } return(attributes); }
/// <summary> /// Quickly add an attribute /// it always returns the tag itself again, allowing chaining of multiple add-calls /// </summary> /// <param name="name">the attribute name, or a complete value like "name='value'"</param> /// <param name="value">optional value - if the attribute already exists, it will be appended</param> /// <param name="appendSeparator">attribute appendSeparator in case the value is appended</param> /// <returns></returns> public T Attr(string name, object value = null, string appendSeparator = null) { TagAttributes.Add(name, value, appendSeparator); return((T)this); }