Example #1
0
        public static MvcHtmlString ImageButton(this HtmlHelper html, string imageUrl, string text,
                                                string onClickAction        = null,
                                                bool showTextAfterImage     = false,
                                                string additionalClasses    = "",
                                                string defaultButtonClasses = "t-emsg-image-button k-button k-button-icon",
                                                string defaultImageClasses  = "k-icon-image"
                                                )
        {
            var builder = new FluentTagBuilder("a");

            builder.AddCssClass(defaultButtonClasses);

            foreach (var addClass in (additionalClasses ?? string.Empty).Split(new [] { " " }, StringSplitOptions.RemoveEmptyEntries))
            {
                builder.AddCssClass(addClass);
            }
            builder.AddAttribute("title", text);
            builder.AddAttribute("tabindex", "0");
            if (!string.IsNullOrEmpty(onClickAction))
            {
                builder.AddAttribute("onclick", onClickAction);
            }
            builder.AddToInnerHtml(new FluentTagBuilder("img")
                                   .AddAttribute("alt", text)
                                   .AddAttribute("src", imageUrl)
                                   .AddCssClass(defaultImageClasses)
                                   .ToString(TagRenderMode.SelfClosing));

            if (showTextAfterImage)
            {
                builder.AddToInnerHtml(new FluentTagBuilder("span").AddCssClass("imageButtonText").AddToInnerHtml(text));
            }

            return(new MvcHtmlString(builder.ToString()));
        }
Example #2
0
        public static MvcHtmlString UnobtrusiveImageButton(this HtmlHelper html, string imageUrl, string text, bool showTextAfterImage = false, string additionalClasses = "", Action <FluentTagBuilder> customizeButtonAnchor = null)
        {
            var anchorBuilder = new FluentTagBuilder("a");

            anchorBuilder.AddCssClass("t-emsg-image-button k-button k-button-icon");

            foreach (var addClass in (additionalClasses ?? string.Empty).Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries))
            {
                anchorBuilder.AddCssClass(addClass);
            }

            anchorBuilder.AddAttribute("title", text);
            anchorBuilder.AddAttribute("tabindex", "0");

            anchorBuilder.AddToInnerHtml(new FluentTagBuilder("img")
                                         .AddAttribute("alt", text)
                                         .AddAttribute("src", imageUrl)
                                         .AddCssClass("k-icon-image")
                                         .ToString(TagRenderMode.SelfClosing));

            if (showTextAfterImage)
            {
                anchorBuilder.AddToInnerHtml(new FluentTagBuilder("span").AddCssClass("imageButtonText").AddToInnerHtml(text));
            }

            if (customizeButtonAnchor != null)
            {
                customizeButtonAnchor(anchorBuilder);
            }

            return(new MvcHtmlString(anchorBuilder.ToString()));
        }
        /// <summary>
        /// Construct list for tree view.
        /// </summary>
        /// <typeparam name="TModel">Type of model.</typeparam>
        /// <typeparam name="TProperty">Type of selected property.</typeparam>
        /// <param name="helper">Html helper.</param>
        /// <param name="expression">Expression which selects property.</param>
        /// <param name="source">Soruce of tree view.</param>
        /// <returns>Tree view list as FluentTagBuilder.</returns>
        private static FluentTagBuilder ConstructList <TModel, TProperty>(
            this HtmlHelper <TModel> helper,
            Expression <Func <TModel, TProperty> > expression,
            TreeViewSource <TProperty> source)
            where TProperty : struct
        {
            // CSS classes
            string listCssClass   = "tree-view-list";
            string loadedCssClass = "tv-loaded";

            // Construct tree view list
            FluentTagBuilder treeViewList = new FluentTagBuilder("ul")
                                            .AddCssClass(listCssClass);

            // If there is not any item return
            if (source == null ||
                source.ItemList == null ||
                source.ItemList.Count == 0)
            {
                return(treeViewList);
            }

            // If source is not empty add class
            treeViewList.AddCssClass(loadedCssClass);

            // Construct list items and add them to treeViewList
            foreach (TreeViewItem <TProperty> listItemSource in source.ItemList)
            {
                FluentTagBuilder listItem = helper
                                            .ConstructListItem(expression, listItemSource);
                treeViewList.AppendChild(listItem);
            }

            return(treeViewList);
        }
Example #4
0
        public static MvcHtmlString ActionLinkButton(this HtmlHelper helper, string text, [AspMvcAction] string action)
        {
            var builder = new FluentTagBuilder("a");

            builder.AddCssClass("t-emsg-button k-button");
            builder.AddToInnerHtml(new FluentTagBuilder("span").AddToInnerHtml(text));
            builder.AddAttribute("href", helper.ToUrlHelper().Action(action));

            return(new MvcHtmlString(builder.ToString()));
        }
        public void BeginAccordionPanel(TextWriter writer, string title, string panelId, string parentAccordionId, bool expanded)
        {
            var li = new FluentTagBuilder("li", TagRenderMode.StartTag);

            if (expanded)
            {
                li.AddCssClass("k-state-active");
            }

            writer.Write(li.ToString());
            writer.Write(title);
            writer.Write(@"<div class=""k-content"">");
        }
Example #6
0
        public static MvcHtmlString Button(this HtmlHelper helper, string text, string onClickFunction = null, string additionalCssClasses = null)
        {
            var builder = new FluentTagBuilder("a");

            builder.AddCssClass("t-emsg-button k-button");
            builder.AddAttribute("type", "button");
            builder.AddAttribute("title", text);

            foreach (var addClass in (additionalCssClasses ?? string.Empty).Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries))
            {
                builder.AddCssClass(addClass);
            }

            builder.AddToInnerHtml(new FluentTagBuilder("span").AddToInnerHtml(text));

            if (!string.IsNullOrEmpty(onClickFunction))
            {
                builder.AddAttribute("onclick", onClickFunction);
            }

            return(new MvcHtmlString(builder.ToString()));
        }
        public void BeginAccordionPanel(TextWriter writer, string title, string panelId, string parentAccordionId, bool expanded)
        {
            var li = new FluentTagBuilder("li", TagRenderMode.StartTag)
                     .AddCssClass("accordion-item");

            if (expanded)
            {
                li.AddCssClass("is-active");
            }

            writer.Write(li.ToString());
            writer.Write($@"<a href=""#"" class=""accordion-title"">{title}</a>");
            writer.Write(@"<div class=""accordion-content"" data-tab-content>");
        }
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            var urlHelper = UrlHelperFactory.GetUrlHelper(
                new ActionContext(ContextAccessor.HttpContext,
                                  new RouteData(),
                                  new Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor()));

            var itemsPerPage = (ItemsPerPage ?? 0);

            if (itemsPerPage == 0)
            {
                int.TryParse(ViewOptions.PerPageDefault, out itemsPerPage);
            }

            var currentPage = (CurrentPage ?? 1);

            var totalItems = (TotalItems ?? 0);

            if (totalItems > itemsPerPage)
            {
                var totalPages = (totalItems + (itemsPerPage - 1)) / itemsPerPage;

                string controller  = String.IsNullOrEmpty(Controller) ? (ViewContext.ActionDescriptor as ControllerActionDescriptor)?.ControllerName : Controller;
                string action      = String.IsNullOrEmpty(Action) ? "Index" : Action;
                var    httpContext = ContextAccessor.HttpContext;

                var pageList = new List <string> {
                    "«",
                    "‹"
                };

                switch (totalPages)
                {
                case 2:
                    pageList.Add("1");
                    pageList.Add("2");
                    break;

                case 3:
                case 4:
                case 5:
                    for (var i = 1; i < totalPages; i++)
                    {
                        pageList.Add(i.ToString());
                    }
                    break;

                default:
                    if (currentPage < 4)
                    {
                        for (var i = 1; i < 6; i++)
                        {
                            pageList.Add(i.ToString());
                        }
                    }
                    else
                    {
                        var endPage = currentPage + 2 < totalPages ? currentPage + 3 : totalPages + 1;
                        for (var i = endPage - 5; i < endPage; i++)
                        {
                            pageList.Add(i.ToString());
                        }
                    }
                    break;
                }

                pageList.Add("›");
                pageList.Add("»");

                var list = new FluentTagBuilder("ul").AddCssClass("pagination pagination-sm")
                           .MergeAttribute("style", "display:inline;");

                dynamic routeValues = new ExpandoObject();
                foreach (var page in pageList)
                {
                    routeValues = new ExpandoObject();

                    if (page.IsNumeric())
                    {
                        routeValues = new { page, itemsPerPage }
                    }
                    ;
                    else if (page == "«")
                    {
                        routeValues = new { page = 1, itemsPerPage }
                    }
                    ;
                    else if (page == "‹")
                    {
                        routeValues = new {
                            page = currentPage - 1 > 0 ? currentPage - 1 : 1,
                            itemsPerPage
                        }
                    }
                    ;
                    else if (page == "›")
                    {
                        routeValues = new {
                            page = currentPage + 1 <= totalPages ? currentPage + 1 : totalPages,
                            itemsPerPage
                        }
                    }
                    ;
                    else if (page == "»")
                    {
                        routeValues = new {
                            page = totalPages,
                            itemsPerPage
                        }
                    }
                    ;
                    foreach (var obj in httpContext.Request.Query)
                    {
                        if (!obj.Key.Equals("page", StringComparison.CurrentCultureIgnoreCase))
                        {
                            routeValues[obj.Key] = obj.Value;
                        }
                    }

                    var li = new FluentTagBuilder("li")
                             .AppendHtml(Generator.GenerateActionLink(ViewContext, page, action, controller, string.Empty, string.Empty, string.Empty, routeValues, null));

                    if (page.IsNumeric())
                    {
                        if (Convert.ToInt32(page) == currentPage)
                        {
                            li.AddCssClass("active");
                        }
                    }
                    list.AppendHtml(li);
                }

                routeValues = new ExpandoObject();
                foreach (var obj in httpContext.Request.Query)
                {
                    if (!obj.Key.Equals("page", StringComparison.CurrentCultureIgnoreCase))
                    {
                        routeValues[obj.Key] = obj.Value;
                    }
                }

                var perPageSelect = new FluentTagBuilder("select")
                                    .GenerateId("ddlResultsPerPage", "")
                                    .MergeAttribute("style", "width:auto;float:left;margin-right:15px;")
                                    .MergeAttribute("data-url", urlHelper.Action(Action, Controller, (object)routeValues))
                                    .AddCssClass("itemCountDropdown form-control")
                                    .MergeAttribute("onchange", "javascript:window.location.href = $('.itemCountDropdown option:selected').attr('tag')");

                routeValues.Page = 1;

                foreach (var item in ViewOptions.PerPageList)
                {
                    routeValues.itemsPerPage = item.Value;
                    item.Selected            = item.Value == itemsPerPage.ToString();
                    var option = new FluentTagBuilder("option")
                                 .MergeAttribute("value", item.Value)
                                 .Append(item.Text)
                                 .MergeAttribute("tag", urlHelper.Action(action, controller, (object)routeValues));

                    if (item.Selected)
                    {
                        option.MergeAttribute("selected", "selected");
                    }

                    perPageSelect.AppendHtml(option);
                }

                output.PostElement.AppendHtml(perPageSelect);
                output.PostElement.AppendHtml(list);
            }
        }
    }
        /// <summary>
        /// Construct one row of list editor.
        /// </summary>
        /// <param name="model">Model object to construct list row according to.</param>
        /// <returns>Constructed row of list.</returns>
        private FluentTagBuilder ConstructListDataRow(TModel model)
        {
            // Declare and set css class names
            string listDataRowCss              = "list-data-row";
            string listDataFieldRowCss         = "list-data-field-row";
            string listDataCalptionFieldRowCss = "list-data-field-row__caption";
            string listdataImageFiledRowCss    = "list-data-field-row__iamge";
            string listDataLabelCss            = "list-data-display__label";
            string listDataDisplayFieldCss     = "list-data-display__field";

            // Store current model for further use
            TModel oldModel = Helper.ViewData.Model;

            try
            {
                // Set model object of HtmlHelper to be able
                // to get correct display, editor and hidden for fields.
                Helper.ViewData.Model = model;

                // Create list data row and merge attributes
                FluentTagBuilder listDataRow = new FluentTagBuilder("div")
                                               .MergeAttributes(GetDataRowAttributes(model))
                                               .AddCssClass(listDataRowCss);

                // Loop through each configured field and add them to the row.
                foreach (IFieldCore <TModel> fieldCore in FieldCollection)
                {
                    /// Do not render field if provided condition is not met
                    /// when list is managed at server side.
                    if (BuilderType == ListEditorBuilderType.ManagedAtServerSide &&
                        !fieldCore.ShouldBeRendered(model))
                    {
                        continue;
                    }

                    if (fieldCore.EditorType != EditorType.Hidden)
                    {
                        // If EditorType has not been set to Hidden,
                        // construct normal field row

                        // Initialize field
                        FluentTagBuilder displayField = new FluentTagBuilder("div")
                                                        .AddCssClass(listDataDisplayFieldCss);

                        // If field should be rendered as image construct image tag
                        // for the field otherwise costruct span for it.
                        if (fieldCore.IsAsImage)
                        {
                            FluentTagBuilder iamgeField = new FluentTagBuilder("img")
                                                          .MergeAttribute("src", fieldCore.Evaluate(model));

                            displayField.AppendChild(iamgeField);
                        }
                        else
                        {
                            FluentTagBuilder displaySpan = new FluentTagBuilder("span")
                                                           .AppendChild(fieldCore.DisplayFor());

                            displayField.AppendChild(displaySpan);
                        }

                        // If this list editor will be managed at client side,
                        // add hidden input field to be able to bind correctly.
                        if (BuilderType == ListEditorBuilderType.ManagedAtClientSide)
                        {
                            displayField.AppendChild(fieldCore.HiddenFor());
                        }

                        // Initialize row for label and field
                        FluentTagBuilder fieldRow = new FluentTagBuilder("div")
                                                    .AddCssClass(listDataFieldRowCss);

                        // Add caption field row css class if needed
                        if (fieldCore.IsAsCaption)
                        {
                            fieldRow.AddCssClass(listDataCalptionFieldRowCss);
                        }

                        // Add image filed row css class if needed
                        if (fieldCore.IsAsImage)
                        {
                            fieldRow.AddCssClass(listdataImageFiledRowCss);
                        }

                        // Add label for field if needed.
                        if (!fieldCore.IsWithoutLabel)
                        {
                            // Initialize label for field
                            FluentTagBuilder label = new FluentTagBuilder("div")
                                                     .AddCssClass(listDataLabelCss)
                                                     .AppendChild(fieldCore.LabelFor());

                            fieldRow.AppendChild(label);
                        }

                        // Append display field to the row.
                        fieldRow.AppendChild(displayField);

                        // Add fieldRow to listEditorRow
                        listDataRow.AppendChild(fieldRow);
                    }
                    else
                    {
                        // Initialize row for label and field
                        FluentTagBuilder fieldRow = new FluentTagBuilder("div")
                                                    .AddCssClass(listDataFieldRowCss)
                                                    .AppendChild(fieldCore.HiddenFor());

                        // If EditorType has been set to Hidden,
                        // just construct hidden input for field
                        listDataRow.AppendChild(fieldRow);
                    }
                }

                return(listDataRow);
            }
            finally
            {
                // Restore model of helper
                Helper.ViewData.Model = oldModel;
            }
        }
        /// <summary>
        /// Generate page urls according to
        /// url generator and paging info.
        /// </summary>
        /// <param name="helper">HtmlHelper.</param>
        /// <param name="pagingInfo">Information about paging.</param>
        /// <param name="pageUrlGenerator">Url generator for pages.</param>
        /// <param name="htmlAttributes">Html attributes to add to links.</param>
        /// <returns>Generated html links.</returns>
        public static MvcHtmlString PageLinks(
            this HtmlHelper helper,
            PagingInfo pagingInfo,
            Func <int, string> pageUrlGenerator,
            object htmlAttributes)
        {
            if (pagingInfo == null)
            {
                throw new ArgumentNullException(nameof(pagingInfo));
            }

            if (pageUrlGenerator == null)
            {
                throw new ArgumentNullException(nameof(pageUrlGenerator));
            }

            string pagingContainerCss = "paging-container";
            string pageLinkCss        = "page-link";
            string selectedLinkCss    = "selected";

            // Convert html attributes
            RouteValueDictionary attributes = HtmlHelper
                                              .AnonymousObjectToHtmlAttributes(htmlAttributes);

            // Initialize function to generate page link
            Func <int, FluentTagBuilder> constructPageLink = pageIndex => new FluentTagBuilder("a")
                                                             .AddCssClass(pageLinkCss)
                                                             .MergeAttribute("href", pageUrlGenerator(pageIndex))
                                                             .SetInnerText(pageIndex.ToString());

            // Initialize function to constryct seperator dots
            Func <FluentTagBuilder> constructSeperatorDots = () => new FluentTagBuilder("span")
                                                             .SetInnerText("...");

            FluentTagBuilder pagingContainer = new FluentTagBuilder("div")
                                               .AddCssClass(pagingContainerCss);

            int lastPageIndex = pagingInfo.TotalPageCount;

            /// First of all define begining and ending index of page
            /// links which will be generated by loop. Maximum number of
            /// 9 pages can be generated by loop. First and last pages are added
            /// manually.

            // Maximum number of 4 pages should be rendered
            // before and after current page
            int rangeBeginIndex = Math.Max(2, pagingInfo.CurrentPage - 4);
            int rangeEndIndex   = Math.Min(lastPageIndex - 1, pagingInfo.CurrentPage + 4);

            /// If there are more than 11 page links then 9 of them must be
            /// rendered by loop, otherwise all links must be displayed.

            // Get total count of page links which must be generated by loop.
            int totalCountOfPageLinksByLoop = Math.Min(lastPageIndex - 2, 9);

            /// Get current count of page links which will be generated by loop
            /// with current settings. For expample, if maximum page count is 100
            /// and current page is 4, then two pages before current page (2, 3),
            /// current page itself and 4 pages after current page (5, 6, 7, 8)
            /// will be generated by loop. This means that total of 2 + 1 + 4 = 7
            /// pages will be generated by loop, but this number must be 9.
            /// To accomplish this we have to add remaning pages from "before the current page"
            /// to the pages after current page. 4 must be generated before current page
            /// but this number is 2, then total of 6 pages should be generated after
            /// current page (5, 6, 7, 8, 9, 10).
            int currentPageCountByLoop = rangeEndIndex - rangeBeginIndex + 1;

            if (totalCountOfPageLinksByLoop > 5 &&
                currentPageCountByLoop < totalCountOfPageLinksByLoop)
            {
                // Find remaing page count.
                // In the example above this is 4 - 2 = 2.
                int remainingPageCount = totalCountOfPageLinksByLoop - currentPageCountByLoop;

                // Add remaining pages to the before or after the current page.
                if (rangeBeginIndex == 2)
                {
                    rangeEndIndex += remainingPageCount;
                }
                else if (rangeEndIndex == lastPageIndex - 1)
                {
                    rangeBeginIndex -= remainingPageCount;
                }
            }

            // Add first page
            if (lastPageIndex >= 1)
            {
                FluentTagBuilder firstPage = constructPageLink(1)
                                             .MergeAttributes(attributes);
                if (1 == pagingInfo.CurrentPage)
                {
                    firstPage.AddCssClass(selectedLinkCss);
                }
                pagingContainer.AppendChild(firstPage);

                // Add seperator dots there is gap between first page
                // and loop range begin index.
                if (rangeBeginIndex - 1 > 1)
                {
                    pagingContainer.AppendChild(constructSeperatorDots());
                }
            }

            // Add begaes in-between first and last
            for (int i = rangeBeginIndex; i <= rangeEndIndex; i++)
            {
                FluentTagBuilder pageLink = constructPageLink(i)
                                            .MergeAttributes(attributes);

                // Add selected class if needed
                if (i == pagingInfo.CurrentPage)
                {
                    pageLink.AddCssClass(selectedLinkCss);
                }

                pagingContainer.AppendChild(pageLink);
            }

            // Add last page if needed
            if (lastPageIndex > 1)
            {
                // Add seperator dots if there is a gap between last page
                // and loop range end index.
                if (lastPageIndex - rangeEndIndex > 1)
                {
                    pagingContainer.AppendChild(constructSeperatorDots());
                }

                FluentTagBuilder lastPage = constructPageLink(lastPageIndex)
                                            .MergeAttributes(attributes);
                if (lastPageIndex == pagingInfo.CurrentPage)
                {
                    lastPage.AddCssClass(selectedLinkCss);
                }
                pagingContainer.AppendChild(lastPage);
            }

            return(MvcHtmlString.Create(pagingContainer.Render()));
        }
        /// <summary>
        /// Render list editor builder as list.
        /// </summary>
        /// <remarks>
        /// This is implementation of RenderList(object htmlAttributes) for
        /// both of IServerListEditorBuilder and IClientListEditorBuilder.
        /// </remarks>
        /// <param name="htmlAttributes">Html attributes to add to list.</param>
        /// <returns>ListEditorBuilder as MvcHtmlString format.</returns>
        public MvcHtmlString RenderList(object htmlAttributes)
        {
            // Collection name must be provided for proper binding
            // list editor which is manged at client side
            if (BuilderType == ListEditorBuilderType.ManagedAtClientSide &&
                string.IsNullOrEmpty(CollectionName))
            {
                throw new InvalidOperationException(
                          "Name of collection must be provided before rendering.");
            }

            // Declare and set css class names
            string listEditorForCss = "list-editor-for";
            string listDataCss      = "list-data";
            string listDataListActionContainerCss = "list-action_container";

            // Get html attributes
            RouteValueDictionary attributes = HtmlHelper
                                              .AnonymousObjectToHtmlAttributes(htmlAttributes);

            // Create div element and merge attributes
            FluentTagBuilder listEditorFor = new FluentTagBuilder("div")
                                             .MergeAttributes(attributes)
                                             .AddCssClass(listEditorForCss);

            // If list editor will be managed at client side we will need an editor.
            if (BuilderType == ListEditorBuilderType.ManagedAtClientSide)
            {
                // Construct list editor.
                // List editor must be constructed prior to list data.
                // Otherwise validation attributes will be
                // added to hidden input elements inside list data.
                listEditorFor.AppendChild(ConstructListEditor());
            }

            // Initialize list data div
            FluentTagBuilder listData = new FluentTagBuilder("div")
                                        .AddCssClass(listDataCss);

            // Initialize list action container and add to listData if needed.
            FluentTagBuilder listActionContainer = null;

            if (BuilderType == ListEditorBuilderType.ManagedAtClientSide)
            {
                listActionContainer = ConstructClientSideListActionContainer();
            }
            else if (BuilderType == ListEditorBuilderType.ManagedAtServerSide)
            {
                listActionContainer = ConstructServerSideListActionContainer();
            }

            if (listActionContainer != null)
            {
                listActionContainer.AddCssClass(listDataListActionContainerCss);
                listData.AppendChild(listActionContainer);
            }

            if (Data != null && Data.Count() > 0)
            {
                if (BuilderType == ListEditorBuilderType.ManagedAtClientSide)
                {
                    // If builder will be managed at client side we need
                    // to add indexers to be able to bind correctly
                    // and action buttons for managing items.

                    foreach (TModel model in Data)
                    {
                        using (Helper.BeginCollectionItem(CollectionName, false))
                        {
                            // Append hidden indexer for correctly binding
                            // data and action container for the row.
                            FluentTagBuilder listDataRow = ConstructListDataRow(model)
                                                           .AppendChild(Helper.HiddenIndexerForModel())
                                                           .AppendChild(ConstructClientSideListItemActionContainer(model));

                            // Add listDataRow to listData
                            listData.AppendChild(listDataRow);
                        }
                    }
                }
                else
                {
                    // If builder will be managed at server side we need
                    // to add links for managing items.

                    foreach (TModel model in Data)
                    {
                        FluentTagBuilder listDataRow = ConstructListDataRow(model)
                                                       .AppendChild(ConstructServerSideListItemActionContainer(model));

                        // Add listDataRow to listData
                        listData.AppendChild(listDataRow);
                    }
                }
            }

            listEditorFor.AppendChild(listData);

            return(new MvcHtmlString(listEditorFor.Render()));
        }
Example #12
0
 private FluentTagBuilder GetPrevNext(int pageNumber, FluentTagBuilder span, bool activateLink, string title)
 {
     return(activateLink
                ? Tag.A(urlBuilder_(pageNumber)).Title(title).Html(span)
                : span.AddCssClass(GridClass.GridDisabled));
 }
Example #13
0
        /// <summary>
        /// Render sorting controller.
        /// </summary>
        /// <param name="htmlAttributes">HtmlAttributes to add to control.</param>
        /// <returns>MvcHtmlString.</returns>
        public MvcHtmlString Render(object htmlAttributes)
        {
            if (SortingFieldCollection == null ||
                SortingFieldCollection.Count == 0)
            {
                throw new InvalidOperationException("At list one field must be configured to enable sorting.");
            }

            string sortingControlCss = "sorting-control";
            string controlFieldCss   = "sorting-control-field";
            string controlOrderCss   = "sorting-control-order";
            string controlHeaderCss  = "sorting-control-header";
            string controlListCss    = "sorting-control-list";
            string selectedCss       = "selected";


            // Initialize sorting field list
            FluentTagBuilder sortingFieldList = new FluentTagBuilder("ul")
                                                .AddCssClass(controlListCss);

            // Currently sorted according to this field
            ISortingFieldCore <TModel> currentlySortedField = null;

            // Loop thorugh configured fields and add links for them
            foreach (ISortingFieldCore <TModel> fieldCore
                     in SortingFieldCollection)
            {
                string fieldPropertyName = fieldCore.GetPropertyName();

                FluentTagBuilder link = new FluentTagBuilder("a")
                                        .MergeAttribute("href", SortFieldUrlGenerator(fieldPropertyName))
                                        .SetInnerText(fieldCore.FieldLabel);

                FluentTagBuilder listItem = new FluentTagBuilder("li")
                                            .AppendChild(link);

                if (fieldPropertyName == Info.PropertyName)
                {
                    // Store currently sorted field for further use
                    // and add selected class to it.
                    currentlySortedField = fieldCore;
                    listItem.AddCssClass(selectedCss);
                }

                // Append item to list
                sortingFieldList.AppendChild(listItem);
            }

            // Initialize down icon
            FluentTagBuilder downIcon = new FluentTagBuilder("i")
                                        .AddCssClass("default-icons")
                                        .SetInnerHtml("keyboard_arrow_down");

            // Initialize sorting field header
            FluentTagBuilder fieldHeaderSpan = new FluentTagBuilder("span")
                                               .SetInnerText(currentlySortedField.FieldLabel);
            FluentTagBuilder fieldHeader = new FluentTagBuilder("div")
                                           .AddCssClass(controlHeaderCss)
                                           .AppendChild(fieldHeaderSpan)
                                           .AppendChild(downIcon);

            // Initialize sorting field
            FluentTagBuilder sortingField = new FluentTagBuilder("div")
                                            .AddCssClass(controlFieldCss)
                                            .AppendChild(fieldHeader)
                                            .AppendChild(sortingFieldList);


            // Initialize order list
            FluentTagBuilder sortingOrderList = new FluentTagBuilder("ul")
                                                .AddCssClass(controlListCss);

            // Loop through availabel orders and create list items for them
            foreach (SortOrder order in Enum.GetValues(typeof(SortOrder)))
            {
                // Determine label
                string label = order == SortOrder.Asc
                    ? AscendingLabel
                    : DescendingLabel;

                FluentTagBuilder link = new FluentTagBuilder("a")
                                        .MergeAttribute("href", SortOrderUrlGenerator(order))
                                        .SetInnerText(label);

                FluentTagBuilder listItem = new FluentTagBuilder("li")
                                            .AppendChild(link);

                if (order == Info.Order)
                {
                    listItem.AddCssClass(selectedCss);
                }

                // Append item to list
                sortingOrderList.AppendChild(listItem);
            }

            // Initialize sorting order header
            string orderHeaderLabel = Info.Order == SortOrder.Asc
                ? AscendingLabel
                : DescendingLabel;

            FluentTagBuilder orderHeaderSpan = new FluentTagBuilder("span")
                                               .SetInnerText(orderHeaderLabel);
            FluentTagBuilder orderHeader = new FluentTagBuilder("div")
                                           .AddCssClass(controlHeaderCss)
                                           .AppendChild(orderHeaderSpan)
                                           .AppendChild(downIcon);

            // Initialize sorting order
            FluentTagBuilder sortingOrder = new FluentTagBuilder("div")
                                            .AddCssClass(controlOrderCss)
                                            .AppendChild(orderHeader)
                                            .AppendChild(sortingOrderList);

            // Extract htmlAttribytes
            RouteValueDictionary attributes = HtmlHelper
                                              .AnonymousObjectToHtmlAttributes(htmlAttributes);

            // Initialize sorting control, merge attributes and add controls
            FluentTagBuilder sortingControl = new FluentTagBuilder("div")
                                              .MergeAttributes(attributes)
                                              .AddCssClass(sortingControlCss)
                                              .AppendChild(sortingField)
                                              .AppendChild(sortingOrder);

            return(MvcHtmlString.Create(sortingControl.Render()));
        }
        /// <summary>
        /// Create drop down button for selected property,
        /// with given source.
        /// </summary>
        /// <exception cref="ArgumentNullException">
        /// When expression or source is null.
        /// </exception>
        /// <typeparam name="TModel">Type of model.</typeparam>
        /// <typeparam name="TProperty">Type of property.</typeparam>
        /// <param name="htmlHelper">HtmlHelper.</param>
        /// <param name="expression">Expression to select needed property.</param>
        /// <param name="source">Source of list to select from.</param>
        /// <param name="optionLabel">Label of drop down button if selection has not been made.</param>
        /// <param name="allowDefault">Allow to select default item without value.</param>
        /// <param name="htmlAttributes">Html attributes.</param>
        /// <returns>Drop down button mvc string.</returns>
        public static MvcHtmlString DropDownButtonFor <TModel, TProperty>(
            this HtmlHelper <TModel> htmlHelper,
            Expression <Func <TModel, TProperty> > expression,
            SelectList source,
            string optionLabel,
            bool allowDefault,
            object htmlAttributes)
        {
            if (expression == null)
            {
                throw new ArgumentNullException(nameof(expression));
            }

            if (source == null ||
                source.Items == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            string dropDownCssClass        = "dropdown-button";
            string buttonContainerCssClass = "dropdown-button-container";
            string headerCssClass          = "dropdown-button-header";
            string menuCssClass            = "dropdown-menu";
            string errorCssClass           = "dropdown-button__error";
            string selectedCssClass        = "selected";

            // Get model metadata
            ModelMetadata metadata = ModelMetadata
                                     .FromLambdaExpression(expression, htmlHelper.ViewData);

            // Convert html attributes
            RouteValueDictionary attributes = HtmlHelper
                                              .AnonymousObjectToHtmlAttributes(htmlAttributes);

            // Get modelValue
            string modelValue = string.Empty;

            if (metadata.Model != null &&
                !string.IsNullOrEmpty(metadata.Model.ToString()))
            {
                modelValue = metadata.Model.ToString();
            }

            // Define selected item
            string selectedValue = string.Empty;
            List <SelectListItem> listeItemSource = source
                                                    .Items
                                                    .Cast <SelectListItem>()
                                                    .ToList();

            // Check if selected value explicitly setted in the source
            selectedValue = listeItemSource
                            .Where(m => m.Selected)
                            .Select(m => m.Value)
                            .FirstOrDefault();

            /// If not explicitly set get model value as a selected value
            /// and set appropriate list item as selected
            if (string.IsNullOrEmpty(selectedValue) &&
                !string.IsNullOrEmpty(modelValue))
            {
                selectedValue = modelValue;
                SelectListItem listItem = listeItemSource
                                          .Where(m => m.Value.Equals(modelValue, StringComparison.OrdinalIgnoreCase))
                                          .FirstOrDefault();
                if (listItem != null)
                {
                    listItem.Selected = true;
                }
            }

            // Initialize parentDiv and merge attributes
            FluentTagBuilder dropDownDiv = new FluentTagBuilder("div")
                                           .MergeAttributes(attributes)
                                           .AddCssClass(dropDownCssClass);

            // Initialize and add css class of button container
            FluentTagBuilder buttonContainerDiv = new FluentTagBuilder("div")
                                                  .AddCssClass(buttonContainerCssClass);

            // If any item has been selected
            // get text of that item as option label
            string buttonHeaderText = optionLabel;

            if (!string.IsNullOrEmpty(selectedValue))
            {
                string selectedItemText = listeItemSource
                                          .Where(m => m.Value.Equals(selectedValue, StringComparison.OrdinalIgnoreCase))
                                          .Select(m => m.Text)
                                          .FirstOrDefault();

                // If selected item text found set buttonHeaderText to
                // this text
                if (!string.IsNullOrEmpty(selectedItemText))
                {
                    buttonHeaderText = selectedItemText;
                }
            }

            // Initialize down icon
            FluentTagBuilder downIcon = new FluentTagBuilder("i")
                                        .AddCssClass("default-icons")
                                        .SetInnerHtml("keyboard_arrow_down");

            // Initialize button header
            FluentTagBuilder buttonHeader = new FluentTagBuilder("span")
                                            .AddCssClass(headerCssClass)
                                            .SetInnerText(buttonHeaderText);

            // Add icon and header into container
            buttonContainerDiv.AppendChild(downIcon).AppendChild(buttonHeader);

            // Append button container to content builder of dropdown
            dropDownDiv.AppendChild(buttonContainerDiv);

            // Initialzie drop down menu
            FluentTagBuilder menu = new FluentTagBuilder("ul")
                                    .AddCssClass(menuCssClass);

            // If allow default set to true add option to
            // select default item
            if (allowDefault)
            {
                FluentTagBuilder defaultItem = htmlHelper.CreateDropDownButtonMenuItem(
                    expression,
                    new SelectListItem {
                    Text = optionLabel, Value = "", Selected = false
                });

                // If model is null then add selected class to default item
                if (metadata.Model == null ||
                    string.IsNullOrEmpty(metadata.Model.ToString()))
                {
                    defaultItem.AddCssClass(selectedCssClass);
                }

                menu.AppendChild(defaultItem);
            }

            // Loop throug list items and add them to menu
            foreach (SelectListItem listItem in listeItemSource)
            {
                // initialize menu item
                FluentTagBuilder menuItem = htmlHelper
                                            .CreateDropDownButtonMenuItem(expression, listItem);

                // If model equals to current list item
                // add selected class to menu item
                if (!string.IsNullOrEmpty(selectedValue) &&
                    string.Equals(
                        listItem.Value,
                        selectedValue,
                        StringComparison.OrdinalIgnoreCase))
                {
                    menuItem.AddCssClass(selectedCssClass);
                }

                menu.AppendChild(menuItem);
            }

            // Append menu to dropdown
            dropDownDiv.AppendChild(menu);

            // Initialize error span and add css class
            MvcHtmlString validationMessage = htmlHelper.ValidationMessageFor(expression);

            FluentTagBuilder errorSpan = new FluentTagBuilder("span")
                                         .AddCssClass(errorCssClass)
                                         .AppendChild(validationMessage);

            // Append error span to dropdown
            dropDownDiv.AppendChild(errorSpan);

            return(new MvcHtmlString(dropDownDiv.Render()));
        }
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            var urlHelper = _urlHelperFactory.GetUrlHelper(
                new ActionContext(_contextAccessor.HttpContext,
                                  new RouteData(),
                                  ViewContext.ActionDescriptor));

            var actionDescriptor = (ViewContext.ActionDescriptor as ControllerActionDescriptor);

            if (String.IsNullOrEmpty(Action))
            {
                Action = actionDescriptor.ActionName;
            }

            if (String.IsNullOrEmpty(Controller))
            {
                Controller = actionDescriptor.ControllerName;
            }

            if (!String.IsNullOrEmpty(Area))
            {
                RouteValues.Add("area", Area);
            }
            else
            {
                RouteValues.Add("area", "");
            }

            dynamic routeValues = new ExpandoObject();

            if (_routeValues != null && _routeValues.Count > 0)
            {
                foreach (var obj in _routeValues)
                {
                    ((System.Collections.Generic.IDictionary <string, object>)routeValues)[obj.Key] = obj.Value;
                }
            }

            dynamic fbValues = new ExpandoObject();

            if (_fbValues != null && _fbValues.Count > 0)
            {
                foreach (var obj in _fbValues)
                {
                    ((System.Collections.Generic.IDictionary <string, object>)fbValues)[$"fb-{obj.Key}"] = obj.Value;
                }
            }
            var color = String.Empty;

            if (!String.IsNullOrEmpty(Icon))
            {
                if (Icon.Contains(":"))
                {
                    var iconValues = Icon.Split(':');
                    Icon  = iconValues[0];
                    color = iconValues[1];
                }
            }

            var cssClass = Icon ?? String.Empty;

            if (cssClass.StartsWith("fa-"))
            {
                cssClass = $"fa {cssClass}";
            }

            var icon = new FluentTagBuilder("i")
                       .AddCssClass(cssClass);

            if (!String.IsNullOrEmpty(color))
            {
                icon.MergeAttribute("style", $"color:{color}");
            }

            var content = await output.GetChildContentAsync();

            var linkContent = content.GetContent();

            if (String.IsNullOrEmpty(linkContent))
            {
                linkContent = caption;
            }

            var link   = _generator.GenerateActionLink(ViewContext, linkContent, Action, Controller, string.Empty, string.Empty, string.Empty, routeValues, null);
            var href   = link.Attributes["href"];
            var anchor = new FluentTagBuilder("a")
                         .MergeAttribute("href", href)
                         .MergeAttributes(fbValues)
                         .AppendHtml(icon)
                         .Append(" ")
                         .Append(linkContent);

            if (IsFancybox)
            {
                anchor.AddCssClass("fancybox");
            }
            if (IsDeleteLink)
            {
                anchor.AddCssClass("remove-object");
            }

            var builder = new FluentTagBuilder("li")
                          .AppendHtml(anchor);

            output.TagName = "";

            output.Content.SetHtmlContent(builder);
        }