Exemple #1
0
        /// <summary>
        /// Creates tab header from tab.
        /// </summary>
        private void CreateTabHeader(Tab tab, int index = -1, object itemData = null)
        {
            // check if the tab has a custom tab header
            var tabHeader = tab.Find <TabHeader>(false);

            if (tabHeader == null)
            {
                // create default TabHeader
                tabHeader           = ViewData.CreateView <TabHeader>(TabHeaderList.Content, tab.Parent, null, Theme, String.Empty, Style);
                tabHeader.ParentTab = tab;
                if (index >= 0)
                {
                    tabHeader.transform.SetSiblingIndex(index + 1);
                }

                if (itemData != null)
                {
                    SetItemData(tabHeader, itemData);
                }

                // initialize tab header
                tabHeader.InitializeViews();
            }
            else
            {
                // move tab header to list
                tabHeader.MoveTo(TabHeaderList.Content, index >= 0 ? index + 1 : -1);
                TabHeaderList.QueueChangeHandler("LayoutChanged");
            }

            // make sure tab bindings are propagated to header
            tab.PropagateBindings();
            TabHeaderList.UpdatePresentedListItems();
        }
Exemple #2
0
        /// <summary>
        /// Generates XSD schema from view type data.
        /// </summary>
        public static void GenerateXsdSchema()
        {
            if (ViewPresenter.Instance == null)
            {
                Debug.LogError("[MarkLight] Unable to generate XSD schema. View presenter can't be found in scene. Make sure the view presenter is enabled.");
                return;
            }

            var sb = new StringBuilder();

            sb.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
            sb.AppendLine("<xs:schema id=\"MarkLight\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" targetNamespace=\"MarkLight\" xmlns=\"MarkLight\" attributeFormDefault=\"unqualified\" elementFormDefault=\"qualified\">");

            // create temporary root view where instantiate each view to get info about view fields
            if (ViewPresenter.Instance.RootView == null)
            {
                ViewPresenter.Instance.RootView = ViewData.CreateView <View>(ViewPresenter.Instance, ViewPresenter.Instance).gameObject;
            }
            var layoutRoot        = ViewPresenter.Instance.RootView.GetComponent <View>();
            var temporaryRootView = ViewData.CreateView <View>(layoutRoot, layoutRoot);
            var enums             = new HashSet <Type>();

            Utils.SuppressLogging = true;

            // generate XSD schema based on view type data
            foreach (var viewType in ViewPresenter.Instance.ViewTypeDataList)
            {
                sb.AppendLine();
                sb.AppendFormat("  <xs:element name=\"{0}\" type=\"{0}\" />{1}", viewType.ViewTypeName, Environment.NewLine);
                sb.AppendFormat("  <xs:complexType name=\"{0}\">{1}", viewType.ViewTypeName, Environment.NewLine);
                sb.AppendFormat("    <xs:sequence>{0}", Environment.NewLine);
                sb.AppendFormat("      <xs:any processContents=\"lax\" minOccurs=\"0\" maxOccurs=\"unbounded\" />{0}", Environment.NewLine);
                sb.AppendFormat("    </xs:sequence>{0}", Environment.NewLine);

                // instantiate view to get detailed information about each view field
                var view = ViewData.CreateView(viewType.ViewTypeName, temporaryRootView, temporaryRootView);
                view.InitializeViews();

                var viewFields = new List <string>(viewType.ViewFields);
                viewFields.AddRange(viewType.DependencyFields);
                viewFields.AddRange(viewType.MapViewFields.Select(x => x.From));
                viewFields.AddRange(viewType.ViewActionFields);
                viewFields = viewFields.Distinct().ToList();

                // create attributes
                foreach (var viewField in viewFields)
                {
                    bool isEnum        = false;
                    var  viewFieldData = view.GetViewFieldData(viewField);
                    if (viewFieldData.ViewFieldType != null && viewFieldData.ViewFieldType.IsEnum)
                    {
                        isEnum = true;
                        enums.Add(viewFieldData.ViewFieldType);
                    }

                    sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", viewField, isEnum ? "Enum" + viewFieldData.ViewFieldTypeName : "xs:string", Environment.NewLine);
                }

                sb.AppendFormat("    <xs:anyAttribute processContents=\"skip\" />{0}", Environment.NewLine);

                sb.AppendFormat("  </xs:complexType>{0}", Environment.NewLine);
            }

            Utils.SuppressLogging = false;

            // destroy temporary root view
            GameObject.DestroyImmediate(temporaryRootView.gameObject);

            // add enums
            foreach (var enumType in enums)
            {
                sb.AppendLine();
                sb.AppendFormat("  <xs:simpleType name=\"{0}\">{1}", "Enum" + enumType.Name, Environment.NewLine);
                sb.AppendFormat("    <xs:restriction base=\"xs:string\">{0}", Environment.NewLine);

                foreach (var enumTypeName in Enum.GetNames(enumType))
                {
                    sb.AppendFormat("      <xs:enumeration value=\"{0}\" />{1}", enumTypeName, Environment.NewLine);
                }

                sb.AppendFormat("    </xs:restriction>{0}", Environment.NewLine);
                sb.AppendFormat("  </xs:simpleType>{0}", Environment.NewLine);
            }

            // add theme element
            sb.AppendLine();
            sb.AppendFormat("  <xs:element name=\"{0}\" type=\"{0}\" />{1}", "Theme", Environment.NewLine);
            sb.AppendFormat("  <xs:complexType name=\"{0}\">{1}", "Theme", Environment.NewLine);
            sb.AppendFormat("    <xs:sequence>{0}", Environment.NewLine);
            sb.AppendFormat("      <xs:any processContents=\"lax\" minOccurs=\"0\" maxOccurs=\"unbounded\" />{0}", Environment.NewLine);
            sb.AppendFormat("    </xs:sequence>{0}", Environment.NewLine);
            sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", "BaseDirectory", "xs:string", Environment.NewLine);
            sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", "Name", "xs:string", Environment.NewLine);
            sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", "UnitSize", "xs:string", Environment.NewLine);
            sb.AppendFormat("  </xs:complexType>{0}", Environment.NewLine);

            // add resource dictionary element
            sb.AppendLine();
            sb.AppendFormat("  <xs:element name=\"{0}\" type=\"{0}\" />{1}", "ResourceDictionary", Environment.NewLine);
            sb.AppendFormat("  <xs:complexType name=\"{0}\">{1}", "ResourceDictionary", Environment.NewLine);
            sb.AppendFormat("    <xs:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">{0}", Environment.NewLine);
            sb.AppendFormat("      <xs:element name=\"Resource\" type=\"Resource\" minOccurs=\"0\" maxOccurs=\"unbounded\" />{0}", Environment.NewLine);
            sb.AppendFormat("      <xs:element name=\"ResourceGroup\" type=\"ResourceGroup\" minOccurs=\"0\" maxOccurs=\"unbounded\" />{0}", Environment.NewLine);
            sb.AppendFormat("    </xs:sequence>{0}", Environment.NewLine);
            sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", "Name", "xs:string", Environment.NewLine);
            sb.AppendFormat("  </xs:complexType>{0}", Environment.NewLine);

            // add resource element
            sb.AppendLine();
            sb.AppendFormat("  <xs:element name=\"{0}\" type=\"{0}\" />{1}", "Resource", Environment.NewLine);
            sb.AppendFormat("  <xs:complexType name=\"{0}\">{1}", "Resource", Environment.NewLine);
            sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", "Key", "xs:string", Environment.NewLine);
            sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", "Value", "xs:string", Environment.NewLine);
            sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", "Language", "xs:string", Environment.NewLine);
            sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", "Platform", "xs:string", Environment.NewLine);
            sb.AppendFormat("  </xs:complexType>{0}", Environment.NewLine);

            // add resource group element
            sb.AppendLine();
            sb.AppendFormat("  <xs:element name=\"{0}\" type=\"{0}\" />{1}", "ResourceGroup", Environment.NewLine);
            sb.AppendFormat("  <xs:complexType name=\"{0}\">{1}", "ResourceGroup", Environment.NewLine);
            sb.AppendFormat("    <xs:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">{0}", Environment.NewLine);
            sb.AppendFormat("      <xs:element name=\"Resource\" type=\"Resource\" minOccurs=\"0\" maxOccurs=\"unbounded\" />{0}", Environment.NewLine);
            sb.AppendFormat("      <xs:element name=\"ResourceGroup\" type=\"ResourceGroup\" minOccurs=\"0\" maxOccurs=\"unbounded\" />{0}", Environment.NewLine);
            sb.AppendFormat("    </xs:sequence>{0}", Environment.NewLine);
            sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", "Key", "xs:string", Environment.NewLine);
            sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", "Value", "xs:string", Environment.NewLine);
            sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", "Language", "xs:string", Environment.NewLine);
            sb.AppendFormat("    <xs:attribute name=\"{0}\" type=\"{1}\" />{2}", "Platform", "xs:string", Environment.NewLine);
            sb.AppendFormat("  </xs:complexType>{0}", Environment.NewLine);

            sb.AppendLine("</xs:schema>");

            // save file
            var    path      = Configuration.Instance.SchemaFile;
            string localPath = path.StartsWith("Assets/") ? path.Substring(7) : path;

            File.WriteAllText(String.Format("{0}/{1}", Application.dataPath, localPath), sb.ToString());

            // print result
            Debug.Log(String.Format("[MarkLight] Schema generated at \"{0}\"", Configuration.Instance.SchemaFile));
        }
        /// <summary>
        /// Generates documentation from an XML file.
        /// </summary>
        public void GenerateDocumentation()
        {
            // Add the following to the Source.CSharp.csproj file in first PropertyGroup
            // <DocumentationFile>Assets\DevTools\Docs\Documentation.XML</DocumentationFile>
            // parse Assets\DevTools\Docs\Documentation.XML
            var documentationXml = File.ReadAllText("Assets/DevTools/Docs/Documentation.XML");
            var viewTemplate     = File.ReadAllText("Assets/DevTools/Docs/ViewDocTemplate.html");

            XElement xmlElement = null;

            try
            {
                xmlElement = XElement.Parse(documentationXml);
            }
            catch (Exception e)
            {
                Debug.LogErrorFormat("Error parsing documentation XML. Exception thrown: {0}", Utils.GetError(e));
                return;
            }

            var docData = new List <DocData>();

            Utils.SuppressLogging = true;

            // parse XML comments and create document data objects
            foreach (var element in xmlElement.Descendants("member").Where(x => x.Attribute("name").Value.StartsWith("T:")))
            {
                var data = new DocData();

                // ignore examples, editor, textmeshpro and dev-tools
                data.FullTypeName = element.Attribute("name").Value.Substring(2);
                if (data.FullTypeName.StartsWith("MarkLight.Examples") || data.FullTypeName.StartsWith("MarkLight.DevTools") ||
                    data.FullTypeName.StartsWith("MarkLight.Editor") || data.FullTypeName.StartsWith("MarkLight.Views.UI.DemoMessage") ||
                    data.FullTypeName.StartsWith("TMPro"))
                {
                    continue;
                }

                data.TypeName = data.FullTypeName.Substring(data.FullTypeName.LastIndexOf(".") + 1);

                // rename `1 for generic types
                data.FileName     = String.Format("{0}.html", data.FullTypeName.Replace("`1", "T"));
                data.HtmlTypeName = data.TypeName.Replace("`1", "<T>");

                data.IsView = ViewPresenter.Instance.ViewTypeDataList.Any(x => x.ViewTypeName == data.TypeName);
                data.IsType = true;

                // add summary
                data.Summary = element.Element("summary").Value.Trim();

                // add description
                var description = element.Element("d");
                if (description != null)
                {
                    data.Description = description.Value.Trim();
                }

                // find all view fields associated with this type
                foreach (var fieldElement in xmlElement.Descendants("member").Where(x => x.Attribute("name").Value.StartsWith(String.Format("F:{0}.", data.FullTypeName)) ||
                                                                                    x.Attribute("name").Value.StartsWith(String.Format("P:{0}.", data.FullTypeName))))
                {
                    string fieldName = string.Empty;

                    try
                    {
                        // get field summaries and descriptions
                        fieldName = fieldElement.Attribute("name").Value.Substring(2 + data.FullTypeName.Length + 1);
                        if (fieldName.Count(x => x == '.') > 0)
                        {
                            continue;
                        }

                        data.FieldSummaries.Add(fieldName, fieldElement.Element("summary").Value.Trim());

                        var fieldDescription = fieldElement.Element("d");
                        if (fieldDescription != null)
                        {
                            data.FieldDescriptions.Add(fieldName, fieldDescription.Value.Trim());
                        }

                        var fieldActionData = fieldElement.Element("actionData");
                        if (fieldActionData != null)
                        {
                            data.FieldActionData.Add(fieldName, fieldActionData.Value.Trim());
                        }

                        // get mapped view field summaries and descriptions
                        foreach (var mappedSummary in fieldElement.Elements("maps"))
                        {
                            var mapField = mappedSummary.Attribute("field").Value;
                            data.FieldSummaries.Add(mapField, mappedSummary.Value.Trim());
                            data.MappedFields.Add(mapField);
                        }

                        foreach (var mappedDescription in fieldElement.Elements("mapd"))
                        {
                            var mapField = mappedDescription.Attribute("field").Value;
                            data.FieldDescriptions.Add(mapField, mappedDescription.Value.Trim());
                        }
                    }
                    catch (Exception e)
                    {
                        Utils.SuppressLogging = false;
                        Utils.LogError("Error generating documentation for {0}, when processing field {1}. {2}{3}", data.HtmlTypeName, fieldName, e.Message, e.StackTrace);
                        Utils.SuppressLogging = true;
                    }
                }

                docData.Add(data);
                //Debug.LogFormat("{0}: {1}", data.FileName, data.HtmlTypeName);
            }

            System.IO.Directory.CreateDirectory("Assets/DevTools/Docs/API/");

            // sort by name
            docData = docData.OrderBy(x => x.TypeName).ToList();

            // generate TOC for views and types
            var viewDocs   = docData.Where(x => x.IsView);
            var viewsTocSb = new StringBuilder();

            foreach (var viewDoc in viewDocs)
            {
                viewsTocSb.AppendFormat("<h5><a href=\"{0}\">{1}</a></h5>", viewDoc.FileName, viewDoc.HtmlTypeName);
            }
            var viewsToc = viewsTocSb.ToString();

            var typesDocs = docData.Where(x => !x.IsView);

            foreach (var viewDoc in viewDocs)
            {
                viewsTocSb.AppendFormat("<h5><a href=\"{0}\">{1}</a></h5>", viewDoc.FileName, viewDoc.HtmlTypeName);
            }
            var typesToc = viewsTocSb.ToString();

            var layoutRoot = ViewPresenter.Instance.RootView.GetComponent <View>();
            var rootView   = ViewData.CreateView <View>(layoutRoot, layoutRoot);

            // generate view content documentation
            foreach (var data in viewDocs)
            {
                var viewTypeData = ViewPresenter.Instance.ViewTypeDataList.First(x => x.ViewTypeName == data.TypeName);
                var type         = ViewData.GetViewType(data.TypeName);
                if (type == null)
                {
                    continue;
                }

                // section: title
                var sb = new StringBuilder();
                sb.AppendFormat("<h1>{0}</h1>", data.HtmlTypeName);

                // section: inherits from
                if (type.BaseType != null && type.BaseType.FullName.StartsWith("MarkLight"))
                {
                    var inheritsFromData = docData.FirstOrDefault(x => x.FullTypeName == type.BaseType.FullName);
                    if (inheritsFromData != null)
                    {
                        sb.AppendFormat("<h3 class=\"inherit-info\">Inherits from <a href=\"{0}\">{1}</a></h3>", inheritsFromData.FileName, inheritsFromData.HtmlTypeName);
                    }
                }

                // section: used by
                var usedByList = ViewPresenter.Instance.ViewTypeDataList.Where(x => x.DependencyNames.Contains(data.TypeName))
                                 .Select(x => viewDocs.FirstOrDefault(y => y.TypeName == x.ViewTypeName)).Where(x => x != null).ToList();
                if (usedByList.Count > 0)
                {
                    sb.Append("<small class=\"inherit-info\">Used by:");
                    foreach (var usedBy in usedByList)
                    {
                        sb.AppendFormat(" <a href=\"{0}\">{1}</a>", usedBy.FileName, usedBy.HtmlTypeName);
                    }
                    sb.Append("</small><br>");
                }
                sb.Append("<br>");

                // section: description
                if (!String.IsNullOrEmpty(data.Summary))
                {
                    sb.AppendFormat("<h2>Description</h2>{0}<br><br>", !String.IsNullOrEmpty(data.Description) ? data.Description : data.Summary);
                }

                // section: fields
                var viewFields = new List <string>(viewTypeData.ViewFields);
                viewFields.AddRange(viewTypeData.DependencyFields);
                viewFields.AddRange(viewTypeData.MapViewFields.Select(x => x.From));
                viewFields = viewFields.Distinct().ToList();

                if (viewFields.Count > 0)
                {
                    sb.Append("<h2>View Fields</h2>");
                    sb.Append("<table class=\"table table-condensed table-hover viewTableViewFields\">");
                    sb.Append("<thead><tr><th>Name</th><th class=\"col-lg-2\">Type</th><th>Description</th><th class=\"tableExpandColumn\"></th></tr></thead>");
                    sb.Append("<tbody>");

                    // get information about view field
                    var view = ViewData.CreateView(data.TypeName, rootView, rootView);
                    view.InitializeViews();

                    int viewFieldIndex = 0;
                    foreach (var viewField in viewFields.OrderBy(x => x))
                    {
                        // get view field type information
                        var viewFieldData        = view.GetViewFieldData(viewField);
                        var viewFieldTypeSummary = viewFieldData.ViewFieldTypeName;
                        var viewFieldTypeDoc     = docData.FirstOrDefault(x => x.TypeName == viewFieldTypeSummary);

                        if (viewFieldTypeDoc != null)
                        {
                            viewFieldTypeSummary = String.Format("<a href=\"{0}\">{1}</a>", viewFieldTypeDoc.FileName, viewFieldTypeDoc.HtmlTypeName);
                        }
                        else
                        {
                            // check if the type is a Unity type
                            if (viewFieldData.ViewFieldType.FullName.StartsWith("UnityEngine"))
                            {
                                // reference the unity docs
                                viewFieldTypeSummary = String.Format("<a href=\"http://docs.unity3d.com/ScriptReference/{0}.html\">{1} <i class=\"fa fa-external-link fa-tiny\"></i></a>", viewFieldTypeSummary, viewFieldTypeSummary);
                            }
                            else
                            {
                                // replace Boolean, String, Single, Object, etc.
                                if (viewFieldTypeSummary == "Boolean")
                                {
                                    viewFieldTypeSummary = "bool";
                                }
                                else if (viewFieldTypeSummary == "Int32")
                                {
                                    viewFieldTypeSummary = "int";
                                }
                                else if (viewFieldTypeSummary == "Single")
                                {
                                    viewFieldTypeSummary = "float";
                                }
                                else if (viewFieldTypeSummary == "String")
                                {
                                    viewFieldTypeSummary = "string";
                                }
                                else if (viewFieldTypeSummary == "Object")
                                {
                                    viewFieldTypeSummary = "object";
                                }
                            }
                        }

                        // add summary row
                        var baseViewDoc = data;
                        if (String.IsNullOrEmpty(baseViewDoc.GetFieldSummary(viewField)))
                        {
                            // check if field summary exist in any of the base types
                            var baseType = view.GetType().BaseType;
                            while (baseType != null && baseType != typeof(object))
                            {
                                baseViewDoc = docData.FirstOrDefault(x => x.TypeName == baseType.Name);
                                if (baseViewDoc != null && !String.IsNullOrEmpty(baseViewDoc.GetFieldSummary(viewField)))
                                {
                                    break;
                                }

                                baseType = baseType.BaseType;
                            }
                        }

                        if (baseViewDoc == null)
                        {
                            baseViewDoc = data;
                        }

                        // find mapped field path
                        string mappedPath        = String.Empty;
                        bool   isMappedViewField = viewTypeData.MapViewFields.Any(x => x.From == viewField);
                        if (isMappedViewField)
                        {
                            ViewFieldData prevVp = null;
                            for (int i = 0; ; ++i)
                            {
                                var vp = view.GetViewFieldData(viewField, i);
                                if (vp == prevVp)
                                {
                                    break;
                                }

                                prevVp = vp;

                                // strip away the last view field which points to the new mapped field
                                int lastDot = mappedPath.LastIndexOf(".");
                                mappedPath  = lastDot > 0 ? mappedPath.Substring(0, lastDot + 1) : String.Empty;
                                mappedPath += vp.ViewFieldPath;
                            }
                        }

                        sb.AppendFormat("<tr data-toggle=\"collapse\" data-target=\"#viewFieldDetails{0}\" class=\"accordion-toggle clickable\"><td class=\"{1}\">{2}{3}</td><td class=\"viewFieldType\">{4}</td><td class=\"viewTableSummary\">{5}</td><td><button class=\"btn btn-default btn-xs\"><span class=\"glyphicon glyphicon-plus\"></span></button></td></tr>",
                                        viewFieldIndex,
                                        isMappedViewField ? "mappedViewFieldName" : "viewFieldName",
                                        viewField,
                                        isMappedViewField ? String.Format(" <i class=\"fa fa-mail-forward\" data-toggle=\"tooltip\" data-placement=\"right\" title=\"{0}\"></i>", mappedPath) : String.Empty,
                                        viewFieldTypeSummary,
                                        baseViewDoc.GetFieldSummary(viewField));

                        // add expandable details row
                        var fieldDescription = baseViewDoc.GetFieldDescription(viewField);
                        sb.AppendFormat("<tr><td colspan=\"4\" class=\"hiddenRow\"><div class=\"accordion-body collapse viewFieldDetails\" id=\"viewFieldDetails{0}\"><div class=\"viewFieldDetailsContent\">{1}", viewFieldIndex, baseViewDoc.GetFieldDescription(viewField));

                        // ... add additional information if it's an enum type
                        if (viewFieldData.ViewFieldType != null && viewFieldData.ViewFieldType.IsEnum)
                        {
                            if (!String.IsNullOrEmpty(fieldDescription))
                            {
                                sb.Append("<br><br>");
                            }
                            sb.Append("<b>Enum Values</b><table class=\"table table-condensed\"><thead><tr><th>Name</th><th>Description</th></tr></thead><tbody>");

                            var enumData = docData.FirstOrDefault(x => x.FullTypeName == viewFieldData.ViewFieldType.FullName);
                            if (enumData != null)
                            {
                                foreach (var enumFieldName in enumData.FieldSummaries.Keys)
                                {
                                    sb.AppendFormat("<tr><td>{0}</td><td>{1}</td></tr>", enumFieldName, enumData.FieldSummaries[enumFieldName]);
                                }
                            }
                            else
                            {
                                var memberInfos = viewFieldData.ViewFieldType.GetMembers(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
                                for (int i = 0; i < memberInfos.Length; ++i)
                                {
                                    sb.AppendFormat("<tr><td>{0}</td><td></td></tr>", memberInfos[i].Name);
                                }
                            }
                            sb.Append("</tbody></table>");
                        }
                        sb.Append("</div></div></td></tr>");

                        ++viewFieldIndex;
                    }
                    sb.Append("</tbody>");
                    sb.Append("</table><br>");

                    // section: view actions
                    var viewActionFieldData = viewTypeData.ViewActionFields;
                    if (viewActionFieldData.Count > 0)
                    {
                        sb.Append("<h2>View Actions</h2>");
                        sb.Append("<table class=\"table table-condensed table-hover viewTableViewActions\">");
                        sb.Append("<thead><tr><th>Name</th><th>Action Data</th><th>Description</th><th class=\"tableExpandColumn\"></th></tr></thead>");
                        sb.Append("<tbody>");

                        int viewActionIndex = 0;
                        foreach (var viewFieldData in viewActionFieldData)
                        {
                            var viewActionData = data.GetFieldActionData(viewFieldData);
                            if (!String.IsNullOrEmpty(viewActionData))
                            {
                                // find the type
                                var actionDataDoc = docData.FirstOrDefault(x => x.IsType && x.TypeName == viewActionData);
                                if (actionDataDoc != null)
                                {
                                    viewActionData = String.Format("<a href=\"{0}\">{1}</a>", actionDataDoc.FileName, actionDataDoc.HtmlTypeName);
                                }
                            }
                            else
                            {
                                viewActionData = "none";
                            }

                            // get view action field summary
                            var baseViewDoc = data;
                            if (String.IsNullOrEmpty(baseViewDoc.GetFieldSummary(viewFieldData)))
                            {
                                // check if field summary exist in any of the base types
                                var baseType = view.GetType().BaseType;
                                while (baseType != null && baseType != typeof(object))
                                {
                                    baseViewDoc = docData.FirstOrDefault(x => x.TypeName == baseType.Name);
                                    if (baseViewDoc != null && !String.IsNullOrEmpty(baseViewDoc.GetFieldSummary(viewFieldData)))
                                    {
                                        break;
                                    }

                                    baseType = baseType.BaseType;
                                }
                            }

                            if (baseViewDoc == null)
                            {
                                baseViewDoc = data;
                            }

                            // get action data
                            sb.AppendFormat("<tr data-toggle=\"collapse\" data-target=\"#viewActionDetails{0}\" class=\"accordion-toggle clickable\"><td class=\"viewFieldName\">{1}{2}</td><td class=\"viewFieldType\">{3}</td><td class=\"viewTableSummary\">{4}</td><td><button class=\"btn btn-default btn-xs\"><span class=\"glyphicon glyphicon-plus\"></span></button></td></tr>", viewActionIndex, viewFieldData,
                                            String.Empty,
                                            viewActionData,
                                            baseViewDoc.GetFieldSummary(viewFieldData));

                            // add expandable details row
                            sb.AppendFormat("<tr><td colspan=\"4\" class=\"hiddenRow\"><div class=\"accordion-body collapse viewFieldDetails\" id=\"viewActionDetails{0}\"><div class=\"viewFieldDetailsContent\">{1}</div></div></td></tr>", viewActionIndex, baseViewDoc.GetFieldDescription(viewFieldData));
                            ++viewActionIndex;
                        }

                        sb.Append("</tbody>");
                        sb.Append("</table><br>");
                    }
                }

                data.DocContent = sb.ToString();
            }

            GameObject.DestroyImmediate(rootView.gameObject);


            // generate view content documentation
            foreach (var data in typesDocs)
            {
                // section: title
                var sb = new StringBuilder();
                sb.AppendFormat("<h1>{0}</h1>", data.HtmlTypeName);
                sb.Append("<br>");

                // section: description
                if (!String.IsNullOrEmpty(data.Summary))
                {
                    sb.AppendFormat("<h2>Description</h2>{0}<br><br>", !String.IsNullOrEmpty(data.Description) ? data.Description : data.Summary);
                }

                data.DocContent = sb.ToString();
            }

            // generate HTML files
            foreach (var doc in docData)
            {
                var viewToc = viewsToc.Replace(String.Format(">{0}<", doc.HtmlTypeName),
                                               String.Format(" class=\"sectionSelected\">{0}<", doc.HtmlTypeName));

                var fileContent = viewTemplate.Replace("__TITLE__", doc.HtmlTypeName)
                                  .Replace("__TOC__", doc.IsView ? viewToc : typesToc).Replace("__CONTENT__", doc.DocContent);

                // write text to file
                File.WriteAllText(String.Format("C:/Projects/Websites/marklightforunity.com/www/docs/api/{0}", doc.FileName),
                                  fileContent);
            }

            // generate sitemap.xml file
            var sitemapSb = new StringBuilder();

            sitemapSb.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            sitemapSb.AppendLine("<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"");
            sitemapSb.AppendLine("        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
            sitemapSb.AppendLine("        xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\">");

            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/index.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/introduction.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/tutorials.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/tutorials/animations.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/tutorials/data-binding.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/tutorials/gettingstarted.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/tutorials/how-to-get-intellisense.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/tutorials/how-to-move-marklight.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/tutorials/resource-dictionaries.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/tutorials/state-management.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/tutorials/themes-and-styles.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/tutorials/view-model.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/tutorials/working-with-list-data.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/news/marklight-2.1.0-released.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/news/marklight-2.2.0-released.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/news/marklight-2.3.0-released.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/news/marklight-2.4.1-released.html</loc>");
            sitemapSb.AppendLine("    </url>");
            sitemapSb.AppendLine("    <url>");
            sitemapSb.AppendLine("        <loc>http://www.marklightforunity.com/docs/news/marklight-2.5.0-released.html</loc>");
            sitemapSb.AppendLine("    </url>");

            foreach (var doc in docData)
            {
                sitemapSb.AppendLine("    <url>");
                sitemapSb.AppendFormat("        <loc>http://www.marklightforunity.com/docs/api/{0}</loc>{1}", doc.FileName, Environment.NewLine);
                sitemapSb.AppendLine("    </url>");
            }

            sitemapSb.AppendLine("</urlset>");

            File.WriteAllText("C:/Projects/Websites/marklightforunity.com/www/sitemap.xml", sitemapSb.ToString());

            Utils.SuppressLogging = false;

            Utils.Log("Documentation generated");
        }