Пагинация в веб-приложении REST

Это более общая переформулировка этого вопроса (с устранением специфических частей Rails)

Я не уверен, как реализовать разбиение на страницы ресурса в веб-приложении RESTful. Предполагая, что у меня есть ресурс с именем products, какой из следующих, по вашему мнению, является наилучшим подходом и почему:

1. Использование только строк запроса

например. http://application/products?page=2&sort_by=date&sort_how=asc
Проблема здесь в том, что я не могу использовать полное кэширование страниц, а также URL не очень чистый и легко запоминающийся.

2. Использование страниц в качестве ресурсов и строк запроса для сортировки

например. http://application/products/page/2?sort_by=date&sort_how=asc
В этом случае проблема, которая видна, заключается в том, что http://application/products/pages/1 не является уникальным ресурсом, поскольку использование sort_by=price может дать совершенно другой результат, и я все еще не могу использовать кэширование страниц.

3. Использование страниц в качестве ресурсов и сегмента URL для сортировки

например. http://application/products/by-date/page/2
Я лично не вижу проблем с использованием этого метода, но кто-то предупредил меня, что это не очень хороший способ (он не дал причины, поэтому, если вы знаете, почему это не рекомендуется, сообщите мне об этом)

Любые предложения, мнения, критические замечания более чем приветствуются. Спасибо.

Ответ 1

Я думаю, что проблема с версией 3 является скорее проблемой "точки зрения" - вы видите страницу как ресурс или продукты на странице.

Если вы видите страницу как ресурс, это совершенно прекрасное решение, так как запрос на стр. 2 всегда будет выводить страницу 2.

Но если вы видите продукты на странице как ресурс, у вас возникла проблема с тем, что продукты на странице 2 могут измениться (старые продукты удалены или что-то еще), в этом случае URI не всегда возвращая тот же ресурс (ы).

например. Клиент сохраняет ссылку на страницу списка продуктов X, в следующий раз, когда ссылка открывается, продукт, о котором идет речь, больше не может быть на странице X.

Ответ 2

Я согласен с Fionn, но я сделаю еще один шаг и скажу, что для меня страница не, это свойство запроса. Это заставляет меня выбрать только строку запроса варианта 1. Он просто чувствует себя хорошо. Мне очень нравится, как Twitter API упорядоченно. Не слишком простой, не слишком сложный, хорошо документированный. К лучшему или к худшему это мой "идете", когда я нахожусь на заборе, делая что-то одностороннее против другого.

Ответ 3

HTTP имеет большой заголовок Range, который также подходит для разбивки на страницы. Вы можете отправить

Range: pages=1

иметь только первую страницу. Это может заставить вас переосмыслить, что такое страница. Возможно, клиент хочет другой ассортимент товаров. Заголовок диапазона также работает, чтобы объявить заказ:

Range: products-by-date=2009_03_27-

чтобы получить все продукты, более поздние, чем эта дата, или

Range: products-by-date=0-2009_11_30

чтобы получить все продукты старше этой даты. "0", вероятно, не лучшее решение, но RFC, похоже, хочет что-то для начала диапазона. Могут быть развернуты парсеры HTTP, которые не будут разбирать единицы = -range_end.

Если заголовки не являются (приемлемыми) параметрами, я считаю, что первое решение (все в строке запроса) - это способ обработки страниц. Но, пожалуйста, нормализовать строки запроса (сортировать (ключ = значение) в порядке алфавита). Это решает проблему "? = 1 & b = x" и "? B = x & a = 1".

Ответ 4

Вариант 1 кажется лучшим, насколько ваше приложение рассматривает разбиение на страницы как метод для создания другого представления того же ресурса.

Сказав это, схема URL является относительно незначительной. Если вы разрабатываете свое приложение как hypertext-driven (так как все приложения REST должны быть по определению), то ваш клиент не будет создавать какие-либо URI самостоятельно. Вместо этого ваше приложение будет предоставлять ссылки клиенту, и клиент будет следовать им.

Один вид ссылки, которую может предоставить ваш клиент, - это ссылка на страницы.

Приятным побочным эффектом всего этого является то, что, даже если вы передумаете о структуре URI разбиения на страницы и внесите в нее что-то совершенно другое на следующей неделе, ваши клиенты могут продолжать работать без каких-либо изменений.

Ответ 5

Я всегда использовал стиль варианта 1. Кэширование не вызывало беспокойства, так как данные часто меняются в любом случае. Если вы позволяете настраивать размер страницы, а затем данные не могут быть кэшированы.

Я не считаю URL-адрес трудно запоминающимся или нечистым. Для меня это точное использование параметров запроса. Ресурс явно представляет собой список продуктов, и параметры запроса просто говорят, как вы хотите, чтобы список отображался - отсортирован и какая страница.

Ответ 6

Странно, что никто не указал, что опция 3 имеет параметры в определенном порядке. HTTP//приложение/Продукты/Дата/по убыванию/Имя/Возрастание/страница/2 а также HTTP//приложения/продукты/Имя/по возрастанию/Дата/по убыванию/страница/2

указывают на один и тот же ресурс, но имеют совершенно разные URL-адреса.

Для меня вариант 1 кажется наиболее приемлемым, поскольку он четко разделяет "Что я хочу" и "Как я хочу" (он даже имеет знак вопроса между ними лол). Полностраничное кэширование может быть реализовано с использованием полного URL (все варианты будут иметь одну и ту же проблему).

При использовании параметра "Параметры в URL" единственным преимуществом является чистый URL. Хотя вам нужно придумать какой-то способ кодирования параметров и без потерь декодировать их. Конечно, вы можете пойти с URLencode/decode, но это сделает URL-адреса уродливыми еще раз:)

Ответ 7

Я бы предпочел использовать смещение и ограничение параметров запроса.

offset: для индекса элемента в коллекции.

limit: количество элементов.

Клиент может просто обновлять смещение следующим образом

offset = offset + limit

для следующей страницы.

Путь считается идентификатором ресурса. И страница не является ресурсом, а подмножеством коллекции ресурсов. Так как разбиение на страницы, как правило, представляет собой запрос GET, параметры запроса лучше всего подходят для разбиения на страницы, а не на заголовки.

Я использую metamug. Они имеют такую ​​конфигурацию. Разбиение на метаданные с выбранным запросом

Ответ 8

В настоящее время я использую схему, подобную этой, в моих приложениях ASP.NET MVC:

например. http://application/products/by-date/page/2

в частности: http://application/products/Date/Ascending/3

Тем не менее, я не очень доволен включением поисковой и сортировочной информации на маршруте таким образом.

Список элементов (продуктов в этом случае) изменен. то есть в следующий раз, когда кто-то вернется к URL-адресу, который включает параметры подкачки и сортировки, полученные результаты могут измениться. Таким образом, идея http://application/products/Date/Ascending/3 как уникального URL-адреса, указывающего на определенный, неизменный набор продуктов, теряется.

Ответ 9

В поисках лучших практик я наткнулся на этот сайт:

http://www.restapitutorial.com

На странице ресурсов есть ссылка для загрузки .pdf, которая содержит полные рекомендации REST, предложенные автором. В котором, среди прочего, есть раздел о разбиении на страницы.

Автор предлагает добавить поддержку как с использованием заголовка Range, так и с использованием параметров строки запроса.

Запрос

Пример заголовка HTTP:

Range: items=0-24

Пример параметров строки запроса:

GET http://api.example.com/resources?offset=0&limit=25

Если смещение - это начальный номер позиции, а предел - максимальное количество возвращаемых элементов.

ответ

В ответе должен содержаться заголовок Content-Range, указывающий, сколько элементов возвращается и сколько всего элементов осталось еще не найденным

Примеры заголовков HTTP:

Content-Range: items 0-24/66

Content-Range: items 40-65/*

В .pdf есть несколько других предложений для более конкретных случаев.

Ответ 10

Я склонен соглашаться с slf, что "страница" на самом деле не является ресурсом. С другой стороны, вариант 3 является более чистым, более легким для чтения и может быть более легко угадан пользователем и даже при необходимости напечатан. Я разрывается между вариантами 1 и 3, но не вижу причин не использовать вариант 3.

Кроме того, хотя они выглядят красиво, один недостаток использования скрытых параметров, как упоминалось выше, а не строк запросов или сегментов URL-адресов, заключается в том, что пользователь не может добавлять закладки или напрямую ссылаться на определенную страницу. Это может быть или не быть проблемой в зависимости от приложения, а просто что-то, о чем нужно знать.

Ответ 11

Я использовал решение 3 раньше (я пишу много приложений django). И я не думаю, что с этим что-то не так. Это так же, как и другие два (если вам нужно делать некоторые скребки или тому подобное), и он выглядит более чистым. Кроме того, ваши пользователи могут угадывать URL-адреса (если это общедоступное приложение), и людям нравится быть в состоянии идти прямо туда, где они хотят, а url-guessing чувствует наделение полномочиями.

Ответ 12

В моих проектах я использую следующие URL:

http://application/products?page=2&sort=+field1-field2

что означает - "дайте мне страницу вторую страницу, упорядоченную по полю, и затем спуститесь по полю2". Или, если мне нужна еще большая гибкость, я использую:

http://application/products?skip=20&limit=20&sort=+field1-field2

Ответ 13

<div style="text-align: right; float: left;">
                                    <table>
                                        <tr>
                                            <td>
                                                <span onclick="GoFirstPage()" title="First page">
                                                    <img width="25" alt="Go to first" src="../../Icon/first.png" />
                                                </span>
                                            </td>
                                            <td><span onclick="GoPrevPage();" title="Previous">
                                                <img width="25" alt="Previous" src="../../Icon/prev.png" />
                                            </span></td>
                                            <td><span>
                                                <input style="width: 30px; border: none; text-align: right; background: white" readonly="readonly" type="text" value="0" id="txtPageNumber" />
                                                Of </span>
                                            </td>
                                            <td>
                                                <span>
                                                    <input style="width: 35px; border: none; background: white" readonly="readonly" type="text" id="txtTotalPages" name="name" value="0" />
                                                </span></td>
                                            <td>
                                                <span title="Next" onclick="GoNextPage();">
                                                    <img id="next" width="25" alt="Next" src="../../Icon/next.png" /></span> </td>
                                            <td>
                                                <span onclick="GoLastPage();" title="Last page">
                                                    <img width="25" alt="Go to Last" src="../../Icon/last.png" />
                                                </span>
                                            </td>
                                        </tr>
                                    </table>
                                </div>    
var FistPage = 0;
    var NextPage = 10;
    var PrevPage = 0;
    var LastPage = 0;
    var PerPage = 10;
    var TotalPages = 1;
    var PageNumber = 1;
    var Obj = {};
    var ArrLen = 0;


    $(function () {

        DatePicker();
        CustomDate();
        $("#dvSalesReport").accordion({

            collapsible: true,
            active: false,
            autoHeight: false,
            autoActivate: true
        });
        // $("#spiner").show();
        //setTimeout(SearchOrders(), 3000); 
        SearchOrders();
        //  setTimeout($("#spiner").hide(), 3000);



    });
    function CustomDate() {
        $("#ddl_Date").change(function () {

            var DateSelectedIndex = $("#ddl_Date option:selected").index()
            if (DateSelectedIndex > 6) {
                $("#tr_frmdat").show();
                $("#tr_todat").show();
                $("#txt_frmdat").val('');
                $("#txt_todat").val('');

            }
            else {
                $("#tr_frmdat").hide();
                $("#tr_todat").hide();
                $("#txt_frmdat").val('');
                $("#txt_todat").val('');

            }
        });
    }
    function DatePicker() {
        $('#txt_frmdat').datepicker({
            showOn: 'both',
            buttonImage: 'calendar.gif',
            buttonImageOnly: true,
            changeMonth: true,
            changeYear: true,
            yearRange: '1920:2020',
            dateFormat: 'mm/dd/yy'
        });

        $('#txt_todat').datepicker({
            showOn: 'both',
            buttonImage: 'calendar.gif',
            buttonImageOnly: true,
            changeMonth: true,
            changeYear: true,
            yearRange: '1920:2020',
            dateFormat: 'mm/dd/yy'

        });
    }
    /// <reference path="../Webs/Sales/sales-order-review.aspx" />
    var DataSalesJson;
    $("#btnSearchNew").click(function () {
        SearchOrders();

    })
    function SearchOrders() {
        var dataRange = $('#ddl_Date').val();
        var SeltdUserIndex = $("#ddl_ShowRslts option:selected").index();
        var SelectedSitesIndex = $("#ddl_sites option:selected").index();
        var SelectedSitesVal = $('#ddl_sites').val();
        var SeltdUserVal = $('#ddl_ShowRslts').val();
        var IsShowAll = $('#chk_ShowAll').is(':checked');
        var StatusCode = $('#ddl_Status option:selected').val();
        var DateFrom = $("#txt_frmdat").val();
        var DateTo = $("#txt_todat").val();
        $("#spiner").show();
        $.ajax({
            type: 'POST',
            contentType: "application/json; charset=utf-8",
            url: 'sales-order-review.aspx/SearchOrdersReview',
            data: "{'DateRange':'" + dataRange + "','SeltdUserIndex':'" + SeltdUserIndex + "','SeltdUserVal':'" + SeltdUserVal + "','SelectedSitesIndex':'" + SelectedSitesIndex + "','SelectedSitesVal':'" + SelectedSitesVal + "','IsShowAll':'" + IsShowAll + "','StatusCode':'" + StatusCode + "','DateFrom':'" + DateFrom + "','DateTo':'" + DateTo + "'}",
            //   async: false,
            success: function (response) {
                DataSalesJson = response;
               // DataSalesJson = response.d;
                var OrderList;
                try {
                    OrderList = JSON.parse(DataSalesJson);
                } catch (e) { }
                FistPage = 0;
                NextPage = parseInt($("#ddlPerPage option:selected").text());;
                PrevPage = 0;
                LastPage = 0;
                PerPage = parseInt($("#ddlPerPage option:selected").text());;
                TotalPages = 1;
                PageNumber = 1;
                Obj = OrderList;
                if (Obj == null || (typeof Obj) === 'undefined') {
                    ArrLen = 0;
                }
                else {
                    ArrLen = Obj.length;
                }
                if (ArrLen > 0) {
                    OnSuccessGenerateOrderReview();
                }
                else {
                    $("#lblTotalAmount").text('');
                    $("#lblEntries").text('');
                    var html = '';
                    html += '<h3 style="font-size: medium;cursor: default;background:cornflowerblue;" class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-active ui-state-active" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="true" aria-expanded="true" tabindex="0"><strong>Agent name</strong><span style="left:45%;position:absolute;"><strong>Order ID</strong></span><span  style="position: absolute;left: 60%;"></span><span style="float:right;margin-right:10px;"><strong>Order amount</strong></span></h3>';
                    html += '<table style="    width: 100%;text-align: center;color: red;"><tr><td>No record found</td></tr></table>'
                    $("#dvSalesReport").html(html);
                }
                $("#spiner").hide();

            },
            error: function () {
                alert("Error");
                $("#spiner").hide();
            }


        });


        try {
            $('#anch_SearchBox').show();
        } catch (e) { }

    }
    function OnSuccessGenerateOrderReview() {

        $("#dvSalesReport").html('');
        var html = '';

        PerPage = parseInt($("#ddlPerPage option:selected").text());
        $("#lblEntries").text("Entries:" + ArrLen);
        TotalPages = ArrLen / PerPage;
        if (typeof TotalPages === 'number') {
            if (TotalPages % 1 !== 0) {
                TotalPages = parseInt(TotalPages);
                TotalPages = TotalPages + 1;
            }
        }
        if (TotalPages == 1) {
            NextPage = ArrLen;
        }
        $("#txtPageNumber").val(PageNumber);
        $("#txtTotalPages").val(TotalPages);
        var LastIndex = NextPage;
        if (LastIndex > ArrLen) {
            LastIndex = ArrLen;
        }
        var Amount_In_Dollar = 0;
        var Amount_In_Dollar_Approved = 0;
        var Amount_In_Dollar_Rejected = 0;
        var Amount_In_Dollar_Pending = 0;

        for (var k = 0; k < ArrLen; k++) {
            Amount_In_Dollar += Obj[k].Amount_In_Dollar;
            try {
                if (Obj[k].Is_Reviewed == 0) {
                    Amount_In_Dollar_Pending += Obj[k].Amount_In_Dollar;
                }
                else if (Obj[k].Is_Reviewed == 1 && Obj[k].ReviewedAs == 1) {
                    Amount_In_Dollar_Approved += Obj[k].Amount_In_Dollar;
                } else if (Obj[k].Is_Reviewed == 1 && Obj[k].ReviewedAs == 0) {
                    Amount_In_Dollar_Rejected += Obj[k].Amount_In_Dollar;
                }
            } catch (e) { }
        }

        $("#lblTotalAmount").text('$' + Amount_In_Dollar.toFixed(2));
        $("#lblApprovedAmount").text('$' + Amount_In_Dollar_Approved.toFixed(2));
        $("#lblRejectedAmount").text('$' + Amount_In_Dollar_Rejected.toFixed(2));
        $("#lblPendingAmount").text('$' + Amount_In_Dollar_Pending.toFixed(2));
        $("#countDIV").css("display", "block");
        $("#div_Details").css("margin-top", "105px");


        for (var i = PrevPage; i < LastIndex; i++) {

            var IsReviewd = Obj[i].Is_Reviewed;
            var ReviewedAs = Obj[i].ReviewedAs;
            var IsSharedOrder = Obj[i].SharedOrder;
            var ShareOrderImg = '';
            if (IsSharedOrder) {
                ShareOrderImg = '<span style="left:62%;position:absolute;"><img src="../../img/Shared.png"  title="Shared Order" style="margin-left: 10px;width: 20px;" /></span>';
            }

            if (IsReviewd == 1 && ReviewedAs == 0) {

                html += '<h3 style="font-size: medium;background:red;color: white;"><span>' + Obj[i].Users_Name + '</span>';
                html += '<span style="left:45%;position:absolute;">' + Obj[i].CustomerOrder_ID + '</span>' + ShareOrderImg + '';
                // html += '<span  onmouseover="ChatPopup(event,' + i + ');" style="position: absolute;left: 60%;"><img src="../../Icon/tooltip.png" /></span>';
                html += '<span style="float:right;margin-right:10px;">' + (Obj[i].Currency) + (Obj[i].CustomerOrder_Amount).toFixed(2) + '</span></h3>';
            }
            else if (IsReviewd == 1 && ReviewedAs == 1) {
                html += '<h3 style="font-size: medium;background: #228B5F;color: white;"><span>' + Obj[i].Users_Name + '</span>';
                html += '<span style="left:45%;position:absolute;">' + Obj[i].CustomerOrder_ID + '</span>' + ShareOrderImg + '';
                // html += '<span  onmouseover="ChatPopup(event,' + i + ');" style="position: absolute;left: 60%;"><img src="../../Icon/tooltip.png" /></span>';
                html += '<span style="float:right;margin-right:10px;">' + (Obj[i].Currency) + (Obj[i].CustomerOrder_Amount).toFixed(2) + '</span></h3>';
            }
            else {
                html += '<h3 style="font-size: medium;"><span>' + Obj[i].Users_Name + '</span>';
                html += '<span style="left:45%;position:absolute;">' + Obj[i].CustomerOrder_ID + '</span>' + ShareOrderImg + '';
                // html += '<span  onmouseover="ChatPopup(event,' + i + ');" style="position: absolute;left: 60%;"><img src="../../Icon/tooltip.png" /></span>';
                html += '<span style="float:right;margin-right:10px;">' + (Obj[i].Currency) + (Obj[i].CustomerOrder_Amount).toFixed(2) + '</span></h3>';
            }

            html += '<div>'
            html += '<div style="min-height:195px;" id=dv1' + i + ' class="reviewReport1">'
            html += '<div>'
            html += '<div style="float:left;margin-left: 20px">'
            html += '<table cellpadding="10" cellspacing="10" style="   margin-left: auto;margin-right: auto;margin-top: 10px;">'
            html += '<tr><td><strong>Customer Name:</strong></td><td>' + Obj[i].Customer_Name + '</td><td ><span onmouseover="ShowCustPopup(event,' + i + ');"><img src="../../Icon/info.png" /></span></td></tr>'
            html += '<tr><td><strong>Order Status:</strong></td><td colspan="2">' + Obj[i].OrderStatus_Description + '</td></tr>'
            html += '</table>'
            html += '</div>'
            html += '<div style="float:left;margin-left: 145px">'
            html += '<table cellpadding="10" cellspacing="10" style="   margin-left: auto;margin-right: auto;margin-top: 10px;">'
            html += '<tr><td><strong>Added On:</strong></td><td>' + Obj[i].Create_Date + '</td></tr>'
            html += '<tr><td><strong>Payment Date:</strong></td><td>' + Obj[i].Payment_Date + '</td></tr>'
            html += '</table>'
            html += '</div>'
            html += '<div style="float:left;margin-left: 145px">'
            html += '<table  cellpadding="10" cellspacing="10" style="   margin-left: auto;margin-right: auto;margin-top: 10px;">'


            var txtCmntID = "Cmnt" + i;


            var CommentField = '';
            //        if (IsReviewd) {
            //        CommentField = '<span>Okay</span>';
            //        }
            //else {
            CommentField = '<input style="height:23px;" type="text"  id=' + txtCmntID + '  value="' + (Obj[i].Review_Comments == '0' ? '' : Obj[i].Review_Comments) + '" />';
            // }

            html += '<tr><td><strong>Amount:</strong></td><td>' + Obj[i].OrderAmount + '%</td></tr>'
            html += '<tr><td><strong>Comment:</strong></td><td>' + CommentField + '</td></tr>'
            //   html += '<tr><td><img src="../../img/tick.png" /></td><td><img src="../../img/icn_delete.png" /></td></tr>'
            html += '</table>';

            html += '</div>'
            html += '</div>'
            //
            html += '<div style="clear: both;;">'
            html += '<table cellpadding="7" cellspacing="0" style="   margin-left: 23px;margin-right: auto;">'

            var Comments = Obj[i].Comments;
            var IsDisplayed = 'block';
            if (typeof Comments === "undefined") {
                Comments = '';
                IsDisplayed = 'none';
            }
            //  html += '<label ><span><strong>References:</strong></span><span style="margin-left: 47px;">' + Obj[i].Reference + '</span></label><br/>'
            //  html += '<label style="display:' + IsDisplayed + ';"><span><strong>Comments:</strong></span><span style="margin-left: 47px;">' + Comments + '</span></label>'


            html += '<tr ><td><strong>Comments:</strong></td><td colspan="5"><span style="margin-left: 36px;">' + Comments=='0'?'':Comments + '</span></td></tr>'
            html += '<tr><td><strong>References:</strong></td><td colspan="5"><span style="margin-left: 36px;">' + Obj[i].Reference + '</span></td></tr>';

            html += '</table>';
            html += '</div>'
            //

            html += '<div style="margin-left: 575px;">';
            html += '<table><tr>';


            //if (IsReviewd) {
            //    html += '<td colspan="2"><span style="margin-left: 65px;"><img src="../../Icon/flag_mark_red.png" title="Already Reviewed"/></span></td>';
            //}
            //else {
            if (IsReviewd == 1 && ReviewedAs == 0) {
                html += '<td><span onclick="Accept(' + i + ');"><input type="button" value="Approve" class="button_tick" /></span></td>';
            } else if (IsReviewd == 1 && ReviewedAs == 1) {
                html += '<td><span onclick="Delete(' + i + ');" style="margin-left: 20px;"><input type="button" value="Reject" class="button_delete" /></span></td>';
            }
            else {
                html += '<td><span onclick="Accept(' + i + ');"><input type="button" value="Approve" class="button_tick" /></span></td>';
                html += '<td><span onclick="Delete(' + i + ');" style="margin-left: 20px;"><input type="button" value="Reject" class="button_delete" /></span></td>';

            }
            html += '<tr>';

            // }

            html += '</table>';
            html += '</div>';
            html += '</div>';
            html += '</div>';
        }

        $("#dvSalesReport").html(html);




        $("#dvSalesReport").accordion("refresh");
        $("#dvSalesReport").prepend('<h3 style="font-size: medium;cursor: default;background:cornflowerblue;" class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-active ui-state-active" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="true" aria-expanded="true" tabindex="0"><strong>Agent name</strong><span style="left:45%;position:absolute;"><strong>Order ID</strong></span><span  style="position: absolute;left: 60%;"></span><span style="float:right;margin-right:10px;"><strong>Order amount</strong></span></h3>')
        $("[id^=ui-id-]").css('height', 'auto');
    }
    function ShowCustPopup(e, index) {

        var html = '';
        var UID = Obj[index].Customer_Email_Address;
        var PWD = Obj[index].Customer_Password;

        var UserPrefix = '',
         PasswordPrefix = '',
            FolderPath = '',
             CustomerCodeBind = Obj[index].Customer_Code,
                SiteID = 0,
                 TicketURL = 'http://www.24activ.com/tickets/scp/tickets.php?a=search&query=';
        if (Obj[index].DOMAIN == 'www.contentproz.net') {
            UserPrefix = 'wu';
            PasswordPrefix = 'wp';
            FolderPath = '/webs/signin.aspx?';
            SiteID = 1;
        }
        else {
            UserPrefix = 'uid';
            PasswordPrefix = 'pas';
            FolderPath = '/login.aspx?';
            SiteID = 3;
        }

        var HomeLink = 'http://' + Obj[index].DOMAIN + FolderPath + UserPrefix + '=' + Encrypt(UID) + '&' + PasswordPrefix + '=' + Encrypt(PWD) + '';
        var MailTo = 'OSTicket-PostPage.aspx?w=' + CustomerCodeBind + '&res=' + SiteID;
        html += '<div>'
        html += '<table cellspacing="5"><tr>'
        html += '<td style="width:30px;"> <a title="Home" target="_blank" id="anch_acc"  href=' + HomeLink + '><img src="/img/goto_area_customer.png" width="20px" /></a></td>'
        html += '<td style="width:30px;"> <a title="Email" id="anch_acc" target="_blank"  href=' + MailTo + '><img src="/img/mail_customer.png" width="20px" /></a></td>'
        html += '<td > <a id="anch_acc" title="Ticket" href=' + TicketURL + UID + ' target="_blank"><img src="/img/ticket_customer.png" width="20px" /></a></td>'
        html += '</tr></table>'
        html += '</div>'
        $("#dvCustomerInfo").html(html);
        var leftPos = e.pageX;
        var topPos = e.pageY;
        $('#dvCustomerInfo').toggleClass('opened');
        $("#dvCustomerInfo").offset({ left: leftPos, top: topPos });
    }
    //Encyption
    function Encrypt(stringToEncrpt) {
        var returnEncrptData = '';
        $.ajax({
            type: 'POST',
            contentType: "application/json; charset=utf-8",
            url: 'sales-order-review.aspx/Encryption',
            data: "{'stringToEncrpt':'" + stringToEncrpt + "'}",
            async: false,
            success: function (response) {

                returnEncrptData = response;

            },
            error: function () {
                alert("Error");
            }
        })
        return returnEncrptData;
    }
    $(document).click(function (e) {
        if (!$(e.target).parents().andSelf().is('.show_alerts')) {
            $("#dvCustomerInfo").removeClass("opened");
        }
    });
    $("#dvCustomerInfo").click(function (e) {
        e.stopPropagation();
    });

    function HidePopup() {
        $("#dvChat").hide();
    }
    function ChatPopup(e, index) {

        var html = '';
        html += '<div>'
        var Comments = Obj[index].Comments;
        if (typeof Comments === "undefined") {
            Comments = '';
        }
        html += '<label><strong>Comments:</strong>' + Comments + '</label><br />'
        html += '<label><strong>Reference:</strong>' + Obj[index].Reference + '</label>'
        html += '</div>'
        $("#dvChat").html(html);
        var leftPos = e.pageX;
        var topPos = e.pageY;
        $("#dvChat").show();
        $("#dvChat").offset({ left: leftPos, top: topPos });

    }
    $(document).click(function (e) {

        if ($(e.target).closest('#dvChat').length != 0) return false;
        $('#dvChat').hide();
    });
    function Accept(index) {

        var orderID = Obj[index].CustomerOrder_ID;
        var Comments = $("#Cmnt" + index).val();
        var MyOrder_ID = parseInt(Obj[index].MyOrder_ID);


        // var Comments = Obj[index].Comments;
        $.ajax({
            type: 'POST',
            contentType: "application/json; charset=utf-8",
            url: 'sales-order-review.aspx/Accept',
            data: "{'orderID':'" + orderID + "','Comments':'" + Comments + "','MyOrder_ID':'" + MyOrder_ID + "'}",
            async: false,
            success: function (response) {
                RemoveObjElement(index);
                alert("Successfully Accepted")

            },
            error: function () {
                alert("Error");
            }


        })
    }
    function RemoveObjElement(index) {
        delete Obj[index]
        ArrLen = ArrLen - 1;
        var len = ArrLen;
        var _PerPage = parseInt($("#ddlPerPage option:selected").text());
        if (ArrLen % _PerPage == 0 && index == ArrLen) {
            PageNumber = PageNumber - 1;
        }
        if (index == 0) {
            var inc = 0;
            //Delete first element from array object
            for (var i = 0; i < len; i++) {
                inc = i + 1;
                Obj[i] = Obj[inc];
            }
        }
        else if (index == ArrLen) {
            NextPage = NextPage - 1;
            PrevPage = PrevPage - _PerPage;
        }
        else if (index > 0 && index < ArrLen) {
            //Delete  element somewhere in the list from array object
            var inc = 0;
            for (var i = index; i < len; ++i) {
                inc = i + 1;
                Obj[i] = Obj[inc];
            }
        }
        OnSuccessGenerateOrderReview();
    }
    function Delete(index) {
        var orderID = Obj[index].CustomerOrder_ID;
        var Comments = $("#Cmnt" + index).val();
        var MyOrder_ID = parseInt(Obj[index].MyOrder_ID);
        // var Comments = Obj[index].Comments;
        $.ajax({
            type: 'POST',
            contentType: "application/json; charset=utf-8",
            url: 'sales-order-review.aspx/Delete',
            data: "{'orderID':'" + orderID + "','Comments':'" + Comments + "','MyOrder_ID':'" + MyOrder_ID + "'}",
            async: false,
            success: function (response) {
                RemoveObjElement(index);
                alert("Successfully Rejected")

            },
            error: function () {
                alert("Error");
            }


        })
    }
    function GoLastPage() {
        if (PageNumber == TotalPages) {
            return false;
        }
        PageNumber = TotalPages;
        $("#txtPageNumber").val(PageNumber);

        NextPage = ArrLen;
        var PageEntry = parseInt($("#ddlPerPage option:selected").text());
        var temp = (parseInt(NextPage / PageEntry)) * PageEntry;
        var isInteger = NextPage / PageEntry;
        if (typeof isInteger === 'number') {
            if (isInteger % 1 !== 0) {
                PrevPage = temp;
            }
            else {
                PrevPage = NextPage - PageEntry;
            }
        }


        OnSuccessGenerateOrderReview();
    }
    function GoFirstPage() {
        if (PageNumber == 1) {
            return false;
        }
        PageNumber = 1;
        $("#txtPageNumber").val(PageNumber);
        PrevPage = 0;
        NextPage = parseInt($("#ddlPerPage option:selected").text());
        OnSuccessGenerateOrderReview();

    }
    function GoPrevPage() {
        if (PageNumber == 1) {
            return false;
        }
        PageNumber = PageNumber - 1;
        $("#txtPageNumber").val(PageNumber);
        NextPage = PrevPage;
        PrevPage = PrevPage - parseInt($("#ddlPerPage option:selected").text());
        OnSuccessGenerateOrderReview();

    }
    function GoNextPage() {

        if (PageNumber == TotalPages) {
            return false;
        }
        PageNumber = PageNumber + 1;
        $("#txtPageNumber").val(PageNumber);
        PrevPage = NextPage;
        NextPage = NextPage + parseInt($("#ddlPerPage option:selected").text());
        OnSuccessGenerateOrderReview();

    }
    function PerPageEntries() {
        $("#spiner").show();
        PerPage = parseInt($("#ddlPerPage option:selected").text());
        TotalPages = ArrLen / PerPage;
        if (typeof TotalPages === 'number') {
            if (TotalPages % 1 !== 0) {
                TotalPages = parseInt(TotalPages);
                TotalPages = TotalPages + 1;
            }
        }
        PageNumber = 1;
        PrevPage = 0;
        $("#txtPageNumber").val(PageNumber);
        $("#txtTotalPages").val(TotalPages);
        NextPage = parseInt($("#ddlPerPage option:selected").text())
        OnSuccessGenerateOrderReview();
        $("#spiner").hide();
    }
    function DateFormat(date) {
        var orderdate = new Date(parseInt(date.replace("/Date(", "").replace(")/", ""), 0));

        return orderdate.toDateString();
    }
    function ShowCustomDate() {
        alert("test")
    }