Saturday, June 1, 2013

jQuery custom pagination class

Html data load as follows:

<input type="hidden" class="contact_admin_total_row_count" value="5"/>
<input type="hidden" class="contact_admin_pagination_limit" value="2"/>
<input type="hidden" class="contact_admin_total_row_found" value="1"/>

<table class="table_design_1">
/* data row here */ 
</table> 

Start pagination as follows:


var options = {
    currentPage: 1,
    loadUrl: BASE_URL + "contact/admin",
    autoLoad: true,
    total: parseInt(jQuery(".contact_admin_total_row_count").attr("value")),
    limit: parseInt(jQuery(".contact_admin_pagination_limit").attr("value")),
    totalFinderId: ".contact_admin_total_row_count",
    resultCountElm: ".contact_admin_total_row_found",
    counter: 100
};
jQuery(".table_design_1").startPagination(options, 
function(paginationCounterId,options,elm,afterCall,targetUrl,currentPage) {
    /* after some work done, refresh pagination */
    elm.refreshPagination(paginationCounterId,options,elm,afterCall,targetUrl,currentPage); 
});

And the pagination class

var paginationCounter = 1;
jQuery.fn.startPagination = function(opts, afterCall) {
    var elm = jQuery(this);
    jQuery.fn.startPagination.defaults = {
        loadUrl: "", /* Define data load url, suppose: http://pritom.com/blog_list */
        total: 5, /* Define total number of items, such 200 */
        limit: 20, /* Define pagination limit such 20 items per page */
        currentPage: 1, /* Current selected page, if 2 then items from 20-39 would load */
        place: "both", /* Pagination link for both upper and lower portion, options: ['top', 'bottom', 'both'] */
        directHtml: false, /* Would replace direct html to element or search in body */
        counter: 0, /* If one page contains more than one pagination list use integer number to defferentiate them */
        offset: 0, /* Start loading index */
        configs: {},
        sortClass: 0, /* make a header class 'sortable' to make data table sortable and define property 'sort-data'
         * to set sort data. */
        sortType: "",
        formParams: {}, /* If any params value need to submit as POST */
        showPagination: true,
        changeBrowserHistory: false, /* Change browser url if you refresh the page */
        browserHistoryURL: null, /* Then define browser history url: http://pritom.com/blog_list */
        autoLoad: false, /* Load first time if needed */
        totalFinderId: null, /* jQuery element to find total items found for re-organize pagination */
        resultCountElm: null, /* jQuery element to find current items loaded for re-load pagination if zero result found */
        parentDiv: null, /* Define if possible a parent div */
        limitFinderElm: null  /* jQuery element to find limit to paginate */
    };
    var options = $.extend(jQuery.fn.startPagination.defaults, opts);
    var paginationCounterId = null;
    if(options.counter == null || isNaN(options.counter) || options.counter == 0) {
        options.counter = paginationCounter++;
    }

    options.offset = parseInt(options.offset);
    if(isNaN(options.offset)) {
        return;
    }
    options.limit = parseInt(options.limit);
    if(isNaN(options.limit)) {
        return;
    }

    if(options.counter != 0) {
        paginationCounterId = "paginationCounter"+options.counter;
    }
    jQuery("."+paginationCounterId).remove();

    var total = parseInt(options.total);
    if(isNaN(total)) {
        return;
    }
    var limit = parseInt(options.limit);
    if(isNaN(limit)) {
        return;
    }
    var start = options.currentPage * limit;
    var pageCount = Math.ceil(total / limit);
    if(options.offset != 0) {
        start = options.offset;
        options.currentPage = Math.ceil(start/ limit) + 1;
    }
    if(isNaN(options.currentPage)) {
        options.currentPage = 1;
    }
    var propertyElm = "<input type='hidden' class='url' value='"+options.loadUrl+"'/>";
    propertyElm += "<input type='hidden' class='limit' value='"+limit+"'/>";
    var pages = [];
    var hasPrevious = 1;
    var hasNext = pageCount;
    if(options.currentPage == 1) {
        pages.push(1);
        if(pageCount >= 2) {
            pages.push(2);
        }
        if(pageCount > 2) {
            pages.push(3);
        }
    } else if(options.currentPage == pageCount) {
        if(pageCount - 2 > 0) {
            pages.push(pageCount - 2);
        }
        if(pageCount - 1 > 0) {
            pages.push(pageCount - 1);
        }
        pages.push(pageCount);
    } else {
        if(pageCount - 1 > 0) {
            pages.push(options.currentPage - 1);
        }
        pages.push(options.currentPage);
        if(options.currentPage + 1 <= pageCount) {
            pages.push(options.currentPage + 1);
        }
    }
    var classElm = "<style type=\"text/css\">" +
        ".pagination-disable {" +
        "    pointer-events: none;" +
        "}" +
        "";
    var hasPreviousClass = " pagination-disable ";
    var hasNextClass = " pagination-disable ";
    if(options.currentPage > 1) {
        hasPrevious = options.currentPage - 1;
        hasPreviousClass = "";
    }
    if(options.currentPage < pageCount) {
        hasNext = options.currentPage + 1;
        hasNextClass = "";
    }
    classElm += "</style>";
    var firstPage = "<a href='javascript:void(0)' pc='1' class='pagination-link pagination-first "+hasPreviousClass+"'><span>First&nbsp;</span></a>";
    var lastPage = "<a href='javascript:void(0)' pc='"+pageCount+"' class='pagination-link pagination-last "+hasNextClass+"'><span>&nbsp;Last</span></a>";
    var prevPage = "<a href='javascript:void(0)' pc='"+hasPrevious+"' class='pagination-link pagination-prev "+hasPreviousClass+"'><span>Prev</span></a>";
    var nextPage = "<a href='javascript:void(0)' pc='"+hasNext+"' class='pagination-link pagination-next "+hasNextClass+"'><span>Next</span></a>";
    var middlePage = "";
    jQuery.each(pages, function() {
        var _n = this;
        if(_n == options.currentPage) {
            middlePage += "<a href='javascript:void(0)' pc='"+_n+"' class='pagination-link pagination-next pagination-disable pagination-active'><span>&nbsp;"+_n+"&nbsp;</span></a>";
        } else {
            middlePage += "<a href='javascript:void(0)' pc='"+_n+"' class='pagination-link pagination-next'><span>&nbsp;"+_n+"&nbsp;</span></a>";
        }
    });
    var showingPage = "<span class='showing'>Showing "+((options.currentPage*options.limit)-options.limit+1)
        +" - "+((options.currentPage*options.limit)>options.total ? options.total : (options.currentPage*options.limit))+" of "+options.total+"</span>";
    var paginationElm = "<div class='PAGINATION_POSITION pagination "+paginationCounterId+"'>"+propertyElm;
    paginationElm += "<div class='sub-pagination'>";
    paginationElm += showingPage+firstPage+prevPage+middlePage+nextPage+lastPage;
    paginationElm += classElm+"</div></div>";
    if(options.showPagination == true && total > 0 && pageCount > 1) {
        if(options.place == "both") {
            elm.after(paginationElm.replace("PAGINATION_POSITION", "pagination_bottom "+paginationCounterId+"_bottom"));
            elm.before(paginationElm.replace("PAGINATION_POSITION", "pagination_top "+paginationCounterId+"_top"));
        } else if(options.place == "top") {
            elm.before(paginationElm.replace("PAGINATION_POSITION", "pagination_top "+paginationCounterId+"_top"));
        } else {
            elm.after(paginationElm.replace("PAGINATION_POSITION", "pagination_bottom "+paginationCounterId+"_bottom"));
        }
    }
    /**
     * START SORTING AND SEARCHING PROCESS
     */
    startSorting(paginationCounterId,options,elm,afterCall);
    startSearching(paginationCounterId,options,elm,afterCall);

    jQuery("body").find("."+paginationCounterId).delegate(".pagination-link", "click", function() {
        var elmTargetClick = jQuery(this);
        paginationLinkClickEvent(options, paginationCounterId, elm, afterCall, elmTargetClick);
    });
    if(options.autoLoad == true) {
        options.autoLoad = false;
        paginationLinkClickEvent(options, paginationCounterId, elm, afterCall, jQuery("body").find("."+paginationCounterId).find(".pagination-active"));
    }
};
function paginationLinkClickEvent(options, paginationCounterId, elm, afterCall, elmTargetClick) {
    var linkElm = elmTargetClick;
    var pc = parseInt(jQuery.trim(linkElm.attr("pc")));
    if(isNaN(pc)){
        pc = 1;
    }
    options.currentPage = pc;
    var limit = options.limit;
    var offset = (parseInt(pc) * parseInt(limit)) - parseInt(limit);
    setBrowserHistoryIfNeed(options, offset, limit);
    elm.refreshPagination(paginationCounterId,options,elm,afterCall);
}
function setBrowserHistoryIfNeed(options, offset, limit) {
    if(options.changeBrowserHistory == true) {
        if(options.browserHistoryURL != null) {
            var theURL = options.browserHistoryURL;
            if(theURL.indexOf("?") >= 0) {
                theURL = theURL + "&offset="+offset+"&limit="+limit;
            } else {
                theURL = theURL + "?offset="+offset+"&limit="+limit;
            }
            jQuery.setBrowserHistoryToCurrentState(theURL);
        }
    }
}
function startSorting(paginationCounterId,options,elm,afterCall) {
    paginationCounterId += "Sortable";
    var clickImage = "<span sortType='asc' class='img img-asc'><img style='cursor: pointer;' border='0' src='"+BASE_URL+"images/sort.png'/></span>";
    var clickImageReverse = "<span sortType='desc' class='img img-desc'><img style='cursor: pointer;' border='0' src='"+BASE_URL+"images/sort_r.png'/></span>";
    var clickMe = "<span class='sort-click "+paginationCounterId+"'><span class='sort-text'>"+clickImage+clickImageReverse+"</span></span>";
    var sortNumber = 0;
    elm.find(".sortable").each(function() {
        sortNumber++;
        var mainElm = jQuery(this);

        var sortBy = mainElm.attr("sort-data");
        if(sortBy === undefined) {
            console.log("sort by undefined");
            return;
        }
        mainElm.append(clickMe);
        mainElm.find(".sort-click").attr("sort-number", sortNumber);
        if(sortNumber == options.sortClass) {
            mainElm.find(".sort-click").addClass("active-"+options.sortType);
            mainElm.find(".sort-click").find(".img-"+options.sortType).remove();
        }
        mainElm.find(".img").bind("click", function() {
            var sortElm = jQuery(this);
            elm.find(".img").removeClass("active");
            sortElm.addClass("active");
            var sortType = jQuery(this).attr("sortType");
            if(sortType === undefined) {
                sortType = "asc";
            }
            if(sortType != "asc" && sortType != "desc") {
                sortType = "asc";
            }
            sortElm.closest(".sort-click").removeClass("asc").removeClass("desc");
            sortElm.closest(".sort-click").addClass(sortType);
            options.sortClass = sortElm.closest(".sort-click").attr("sort-number");
            options.sortType = sortType;
            options.configs['sortBy'] = sortBy;
            options.configs['sortType'] = sortType;
            elm.refreshPagination(paginationCounterId,options,elm,afterCall);
            return false;
        });
        mainElm.bind("click", function() {
            var sortElm = null;
            if(jQuery(this).find("span.sort-click").find("span.img-asc").length) {
                sortElm = jQuery(this).find("span.sort-click").find("span.img-asc");
            } else if(jQuery(this).find("span.sort-click").find("span.img-desc").length) {
                sortElm = jQuery(this).find("span.sort-click").find("span.img-desc");
            } else {
                return false;
            }
            sortElm.trigger("click");
            return false;
        });
    });
}
function startSearching(paginationCounterId,options,elm,afterCall) {
    paginationCounterId += "Searchable";
    var sortNumber = 0;
    console.log(".searchable class to header make it searchabge by rendering a text box");
    elm.find(".searchable").each(function() {
        sortNumber++;
        var mainElm = jQuery(this);
        var search_name = mainElm.attr("search_name");
        if(search_name === undefined) {
            console.log("need attribute 'search_name'");
            return;
        }
        var clickMe = "<div style='clear: both;'></div>" +
            "<input type='text' class='search_txt' placeholder='Enter search value' name='"+search_name+"' />";
        mainElm.append(clickMe);
        mainElm.find(".search_txt").change(function() {
            var searchValue = jQuery.trim(jQuery(this).attr("value"));
            options.formParams["__s[" + search_name + "]"] = searchValue;
            loadPaginationData(paginationCounterId,options,elm,afterCall);
        });
        mainElm.find(".search_txt").keypress(function(event) {
            if(event.which == 13) {
                jQuery(this).trigger("change");
            }
        });
    });
}
jQuery.fn.refreshPagination = function(paginationCounterId,options,elm,afterCall) {
    var elm = jQuery(this);
    var appendTo = jQuery("body");
    var pageHeight = parseInt(appendTo.css("height").replace(/px/g, "")) + jQuery("body").scrollTop();
    var pageWidth = appendTo.css("width");
    if(options.parentDiv != null && jQuery(options.parentDiv).length) {
        appendTo = jQuery(options.parentDiv);
        jQuery(options.parentDiv).css({
            position: "relative"
        });
        pageHeight = parseInt(jQuery(options.parentDiv).css("height").replace(/px/g, "")) + 50;
        pageWidth = appendTo.css("width");
    }
    var newDiv = jQuery('<div class="body-over" style="filter:alpha(opacity=0.3); -moz-opacity:0.3; opacity:0.3;">').appendTo(appendTo);
    newDiv.css({
        width: pageWidth,
        height: pageHeight + "px",
        position: "absolute",
        top: "0px",
        left: "0px",
        backgroundColor: "black",
        zIndex: 1000000000
    });

    var moreConfigLink = "";
    jQuery.each(options.configs, function(key, value) {
        if(moreConfigLink != "") {
            moreConfigLink += "&";
        }
        moreConfigLink += key + "=" + value;
    });

    var targetUrl = "";
    var _a = options.limit;
    var _b = (_a * options.currentPage) - _a;
    if(options.loadUrl.indexOf("?") >= 0) {
        targetUrl = options.loadUrl + "&offset="+_b+"&limit="+_a;
    } else {
        targetUrl = options.loadUrl + "?offset="+_b+"&limit="+_a;
    }
    if(targetUrl.indexOf("?") >= 0) {
        targetUrl += "&" + moreConfigLink;
    } else {
        targetUrl += "?" + moreConfigLink;
    }
    jQuery.ajax({
        type: "POST",
        dataType: "HTML",
        data: options.formParams,
        url: targetUrl,
        success: function(htmlData) {
            if(options.directHtml == true) {
                elm.html(htmlData);
            } else {
                var newElm = $("<div>"+htmlData+"</div>");
                if(newElm.find("#"+elm.attr("id")).length) {
                    elm.html(newElm.find("#"+elm.attr("id")).html());
                } else if(newElm.find("."+elm.attr("class")).length) {
                    elm.html(newElm.find("."+elm.attr("class")).html());
                }
            }
            var newElement = jQuery("<div>"+htmlData+"</div>");
            if(options.totalFinderId != null) {
                if(newElement.find(options.totalFinderId).length) {
                    var tempTotal = newElement.find(options.totalFinderId).attr("value");
                    if( tempTotal !== undefined ) {
                        tempTotal = parseInt(tempTotal);
                        if( !isNaN(tempTotal) ) {
                            options.total = tempTotal;
                        }
                    }
                }
            }
            var resultCount = null;
            if(options.resultCountElm != null) {
                if(newElement.find(options.resultCountElm).length) {
                    var tempTotal = newElement.find(options.resultCountElm).attr("value");
                    if( tempTotal !== undefined ) {
                        tempTotal = parseInt(tempTotal);
                        if( !isNaN(tempTotal) ) {
                            resultCount = tempTotal;
                        }
                    }
                }
            }
            if(options.limitFinderElm != null) {
                if(newElement.find(options.limitFinderElm).length) {
                    var tempTotal = newElement.find(options.limitFinderElm).attr("value");
                    if( tempTotal !== undefined ) {
                        tempTotal = parseInt(tempTotal);
                        if( !isNaN(tempTotal) ) {
                            options.limit = tempTotal;
                        }
                    }
                }
            }
            if(resultCount != null && resultCount == 0 && options.currentPage > 1) {
                options.currentPage--;
                while(true) {
                    var _a = options.limit;
                    var _b = (_a * options.currentPage) - _a;
                    if(_b > options.total && options.currentPage > 1) {
                        options.currentPage--;
                    } else {
                        break;
                    }
                }
                elm.refreshPagination(paginationCounterId,options,elm,afterCall);
                return false;
            }
            jQuery("."+paginationCounterId).remove();
            elm.startPagination(options, afterCall);

            if(afterCall) {
                afterCall(paginationCounterId,options,elm,afterCall);
            }
            jQuery(".body-over").remove();
        }
    });
};

YII CGridView with custom action button


<?php 
$this->widget('zii.widgets.grid.CGridView', array(
    'dataProvider'=>$model->search(),
    'filter'=>$model,
    "itemsCssClass" => "table_design_1",
    "htmlOptions" => array(
        "class" => "div_contact_admin_grid_view"
    ),
    "ajaxUpdate" => false,
    'columns'=>array(
        array(
            'name'=>'family_name', 
            'header'=>'First name',
            'type' => 'raw',
            'value' => 'CHtml::link($data->family_name,$data->id)'
        ),
        array(
            'name'=>'given_name', 
            'header'=>'Last name',
            'type' => 'raw',
            'value' => 'CHtml::link($data->given_name,$data->id)'
        ),
        array(
            'class'=>'CButtonColumn',
            'template'=>'{delete_contact}{view}{update}',
            'buttons'=>array (
                'delete_contact' => array (
                    'label'=>'Delete this contact',
                    'imageUrl'=>Yii::app()->request->baseUrl.'/images/delete.png',
                    'url'=>'Yii::app()->createUrl("contact/delete", array("id"=>$data->id))',
                    'visible' => '1',
                    "options" => array(
                        "class" => "delete_contact"
                    )
                )
            ),
            'viewButtonUrl'=>'Yii::app()->request->getBaseUrl(true)."/contact/view/".$data["id"]',
            'updateButtonUrl'=>'Yii::app()->request->getBaseUrl(true)."/contact/update/".$data["id"]',
            "htmlOptions" => array(
                'style'=>'width: 60px;',
                'class' => 'action_class'
            )
        )
    )
)); ?>