//--- Methods --- private XmlNode Parse(XmlElement current, List <DekiScriptExpression> list) { XmlNode next = current.NextSibling; string value; // check if element needs to be evaluated try { if (current.NamespaceURI.EqualsInvariant(XDekiScript.ScriptNS)) { // element has "eval:" prefix switch (current.LocalName) { #region <if test="bool-expr">...</if>{<elseif test="bool-expr">...</elseif>}[<else>...</else>] case "if": { List <Tuplet <DekiScriptExpression, DekiScriptExpression> > conditionals = new List <Tuplet <DekiScriptExpression, DekiScriptExpression> >(); // initial "if" statement DekiScriptExpression condition = Parse(SubLocation("/@test"), current.GetAttribute("test")); conditionals.Add(new Tuplet <DekiScriptExpression, DekiScriptExpression>(condition, BuildChildren(current))); // check for subsequent "elseif" and "else" statements while (true) { // move to next node XmlNode originalNext = next; // skip empty text nodes while ((next != null) && ((next.NodeType == XmlNodeType.Whitespace) || (next.NodeType == XmlNodeType.SignificantWhitespace) || ((next.NodeType == XmlNodeType.Text) && (next.Value.Trim().Length == 0)))) { next = next.NextSibling; } // check if next node is an alternate branch if ((next != null) && next.NamespaceURI.EqualsInvariant(XDekiScript.ScriptNS) && (next.LocalName.EqualsInvariant("elseif") || next.LocalName.EqualsInvariant("else"))) { current = (XmlElement)next; PopNode(); PushNode(current); next = current.NextSibling; if (current.LocalName.EqualsInvariant("elseif")) { // process "elseif" branch condition = Parse(SubLocation("/@test"), current.GetAttribute("test")); conditionals.Add(new Tuplet <DekiScriptExpression, DekiScriptExpression>(condition, BuildChildren(current))); } else { // process "else" branch conditionals.Add(new Tuplet <DekiScriptExpression, DekiScriptExpression>(null, BuildChildren(current))); break; } } else { // couln't find an alternatte branch, restore the original next node next = originalNext; break; } } list.Add(DekiScriptExpression.IfElseStatements(Location, conditionals)); } break; #endregion #region <foreach [var="id"] in="list-or-map-or-xml-expr" [where|test="bool-expr"]>...</foreach> case "foreach": { string variable = current.HasAttribute("var") ? current.GetAttribute("var").Trim() : DekiScriptRuntime.DEFAULT_ID; string where = current.GetAttribute("where"); if (string.IsNullOrEmpty(where)) { where = current.GetAttribute("test"); } DekiScriptGenerator generator = null; if (!string.IsNullOrEmpty(where)) { var location = SubLocation("/@where"); generator = new DekiScriptGeneratorIf(location, Parse(location, where), null); } generator = new DekiScriptGeneratorForeachValue(Location, new[] { variable }, Parse(Location, current.GetAttribute("in")), generator); list.Add(DekiScriptExpression.ForeachStatement(Location, generator, BuildChildren(current))); } break; #endregion #region <expr value="expr" /> -OR- <expr>expr</expr> case "expr": { string code = current.HasAttribute("value") ? current.GetAttribute("value") : current.InnerText; DekiScriptExpression expr = Parse(Location, code); list.Add(expr); } break; #endregion #region <js value="expr" /> -OR- <js>expr</js> case "js": { string code = current.HasAttribute("value") ? current.GetAttribute("value") : current.InnerText; DekiScriptExpression expr = Parse(Location, code); list.Add(DekiScriptExpression.Call(Location, DekiScriptExpression.Access(Location, DekiScriptExpression.Id(Location, "json"), DekiScriptExpression.Constant("emit")), new DekiScriptListConstructor(null, expr))); } break; #endregion #region <block value="expr">...</block> case "block": { // TODO (steveb): it seems odd we use InnerText here instead of value; what is the motivation? string code = current.HasAttribute("value") ? current.GetAttribute("value") : current.InnerText; list.Add(DekiScriptExpression.BlockWithDeclaration(Location, Parse(Location, code), BuildChildren(current))); } break; #endregion default: throw new DekiScriptParserException(string.Format("{0}, unknown elementn <eval:{1}>", Location, current.LocalName), Location.None); } } else { List <DekiScriptExpression> nodes = new List <DekiScriptExpression>(); // process "function" attribute if (!string.IsNullOrEmpty(value = current.GetAttribute("function"))) { // NOTE (steveb): process content transform // check if function contains '$' sign, which is a place holder for the main argument DekiScriptExpression evaluation = Parse(SubLocation("/@function"), (value.IndexOf('$') < 0) ? value + "($)" : value); // determine if main argument is a string or an xml document DekiScriptExpression arg; if (current.LocalName.EqualsInvariant("pre")) { ConvertBrToNewline(current); // pass argument in as a string arg = DekiScriptExpression.Constant(StripCode(current.InnerText)); } else { // pass argument in as a HTML document List <DekiScriptExpression> inner = new List <DekiScriptExpression>(); BuildElement(current, inner); DekiScriptExpression body = DekiScriptExpression.XmlElement(Location, null, DekiScriptExpression.Constant("body"), null, DekiScriptExpression.Block(Location, inner)); DekiScriptExpression html = DekiScriptExpression.XmlElement(Location, null, DekiScriptExpression.Constant("html"), null, body); arg = html; } // create DOM expression DekiScriptExpression assign = DekiScriptExpression.VarStatement(Location, DekiScriptExpression.Id(Location, DekiScriptRuntime.DEFAULT_ID), arg); DekiScriptExpression statements = DekiScriptExpression.BlockWithDeclaration(Location, assign, evaluation); nodes.Add(TryCatch(statements, true)); } else if (current.LocalName.EqualsInvariant("span") && current.GetAttribute("class").EqualsInvariant("script")) { ConvertBrToNewline(current); // convert <span class="script">...</span> to <eval:expr>...</eval:expr> nodes.Add(TryCatch(Parse(Location, new XmlNodePlainTextReadonlyByteStream(current)), true)); } else if (current.LocalName.EqualsInvariant("pre")) { string cls = current.GetAttribute("class"); if (cls.EqualsInvariant("script")) { ConvertBrToNewline(current); DekiScriptExpression expr = TryCatch(Parse(Location, new XmlNodePlainTextReadonlyByteStream(current)), true); nodes.Add(expr); } else if (cls.EqualsInvariant("script-jem")) { ConvertBrToNewline(current); // convert <pre class="script-jem">...</pre> to <html><body><script type="text/jem">...</script></body></html> DekiScriptExpression html = Html("body", "script", "text/jem", DekiScriptExpression.Constant(current.InnerText.ReplaceAll("\u00A0", " ", "\00AD", ""))); nodes.Add(html); } else if (cls.EqualsInvariant("script-js")) { ConvertBrToNewline(current); // convert <pre class="script-js">...</pre> to <html><body><script type="text/js">...</script></body></html> DekiScriptExpression html = Html("body", "script", "text/javascript", DekiScriptExpression.Constant(current.InnerText.ReplaceAll("\u00A0", " ", "\00AD", ""))); nodes.Add(html); } else if (cls.EqualsInvariant("script-css")) { ConvertBrToNewline(current); // convert <pre class="script-css">...</pre> to <html><head><style type="text/css">...</style></head></html> DekiScriptExpression html = Html("head", "style", "text/css", DekiScriptExpression.Constant(current.InnerText.ReplaceAll("\u00A0", " ", "\00AD", ""))); nodes.Add(html); } else { BuildElement(current, nodes); } } else { BuildElement(current, nodes); } // process "block" attribute bool scripted = false; if (!string.IsNullOrEmpty(value = current.GetAttribute("block"))) { scripted = true; // attribute "block" is present var location = SubLocation("/@block"); DekiScriptExpression blockExpr = Parse(location, value); blockExpr = DekiScriptExpression.BlockWithDeclaration(Location, blockExpr, nodes); nodes.Clear(); nodes.Add(blockExpr); } // process "foreach" attribute if (!string.IsNullOrEmpty(value = current.GetAttribute("foreach"))) { scripted = true; // attribute "foreach" is present StringBuilder expression = new StringBuilder(); expression.Append(value); string where = current.GetAttribute("where"); if (!string.IsNullOrEmpty(where)) { expression.Append(", if ").Append(where); } var location = SubLocation("/@foreach"); DekiScriptForeach foreachExpr = (DekiScriptForeach)Parse(location, "foreach(" + expression + "){}"); DekiScriptExpression foreachExpr2 = DekiScriptExpression.ForeachStatement(location, foreachExpr.Generator, nodes); nodes.Clear(); nodes.Add(foreachExpr2); } // process "if" attribute if (!string.IsNullOrEmpty(value = current.GetAttribute("if"))) { scripted = true; // attribute "if" is present var location = SubLocation("/@if"); DekiScriptExpression condition = Parse(location, value); condition = DekiScriptExpression.IfElseStatements(location, new[] { new Tuplet <DekiScriptExpression, DekiScriptExpression>(condition, DekiScriptExpression.Block(Location, nodes)) }); nodes.Clear(); nodes.Add(condition); } // process "init" attribute if (!string.IsNullOrEmpty(value = current.GetAttribute("init"))) { scripted = true; // attribute "init" is present DekiScriptExpression init = Parse(Location, value); DekiScriptExpression dom = DekiScriptExpression.BlockWithDeclaration(SubLocation("/@init"), init, nodes); nodes.Clear(); nodes.Add(dom); } // append inner nodes switch (nodes.Count) { case 0: // nothing to do break; case 1: list.Add(TryCatch(nodes[0], scripted)); break; default: list.AddRange(nodes); break; } } } catch (Exception e) { XDoc warning = new XDoc("html").Start("body").Add(DekiScriptRuntime.CreateWarningFromException(null, Location, e)).End(); list.Add(new DekiScriptXml(warning)); } return(next); }