Where to Discuss?

Local Group

Preface

Goal: Explaining Glenn McComb Pagination using Math and Table.

Source Code

You can download the source code of this article here.

Extract and run on CLI using $ npm install.


1: Preview: Detail

Consider, have a look at the pagination below in a stripped down model.

Layout: EJS Index

We are still using pagination/03-adjacent layout, in index.ejs

Structure

This will only show one part:

  • Middle Pagination: Glenn McComb

Each Pagination

Consider, have a look at the animation above, frame by frame. I’m going to do some reverse engineering, to accomplish better understanding on how this pagination works.

We have from first page (1), to last page (9).

Hexo Pagination: Adjacent Page 1

Hexo Pagination: Adjacent Page 2

Hexo Pagination: Adjacent Page 3

Hexo Pagination: Adjacent Page 4

Hexo Pagination: Adjacent Page 5

Hexo Pagination: Adjacent Page 6

Hexo Pagination: Adjacent Page 7

Hexo Pagination: Adjacent Page 8

Hexo Pagination: Adjacent Page 9

Table

We can rewrite the table with additional rows as below.

+-------------+-------+-------+-------+-------+-------+
| pagination  |   2   |   3   |   5   |  10   |  20   |
+-------------+-------+-------+-------+-------+-------+
| VARIABLE                                            |
| page.total  |   9   |   6   |   4   |   2   |  N/A  |
| max_links   |   5   |   5   |   5   |   5   |  N/A  |
| lower_limit |   3   |   3   |   3   |   3   |  N/A  |
| upper_limit |   7   |   4   |   2   |   0   |  N/A  |
+-------------+-------+-------+-------+-------+-------+
| MIDDLE PAGINATION                                   |
| page   = 1  | 1..5  | 1..5  | 1..4  | 1..2  |-------+
| page   = 2  | 1..5  | 1..5  | 1..4  | 1..2  |       |
| page   = 3  | 1..5  | 1..5  | 1..4  |-------+       |
| page   = 4  | 2..6  | 4..6  | 1..4  |               |
| page   = 5  | 3..7  | 4..6  |-------+               |
| page   = 6  | 4..8  | 4..6  |                       |
| page   = 7  | 5..9  |-------+                       |
| page   = 8  | 5..9  |                               |
| page   = 9  | 5..9  |                               |
+-------------+-------+-------------------------------+

2: Math: Conditional

Part: Middle Pages

This is already discussed in, so I won’t explain it nomore.

      if (page.current <= lower_limit) {
        // Lower limit pages.
        ...
      } else if (page.current >= upper_limit) {
        // Upper limit pages.
        ...
      } else {
        // Middle pages.
        if ( (cursor >= page.current - adjacent_links) 
        &&   (cursor <= page.current + adjacent_links) ) {
          show_cursor_flag = true;
        }
      }

What you need to know is the conditional result in table:

+--------------+-------+
| pagination  |   2   |
| adjacent    |   2   |
| total post  |  17   |
+--------------+-------+
| VARIABLE            |
| page.total  |   9   |
| max_links   |   5   |
| lower_limit |   3   |
| upper_limit |   7   |
+-------------+-------+--+
| selected    | adjacent |
+-------------+----------+
| page   =  1 |   1..3   |
| page   =  2 |   1..4   |
| page   =  3 |   1..5   |
| page   =  4 |   2..6   |
| page   =  5 |   3..7   |
| page   =  6 |   4..8   |
| page   =  7 |   5..9   |
| page   =  8 |   5..9   |
| page   =  9 |   5..9   |
+-------------+----------+

Part: Lower Limit Pages

Consider stripped more for each part.

      if (page.current <= lower_limit) {
        // Lower limit pages.
        // If the user is on a page which is in the lower limit.
        if (cursor <= max_links) {
          // If the current loop page is less than max_links.
          show_cursor_flag = true;
        }
      } else if (page.current >= upper_limit) {
        // Upper limit pages.
        ...
      } else {
        // Middle pages.
        ...
      }

Notice that there is two part of conditional.

  • Outer conditional: result true for the first three row, as defined by lower_limit.

  • Inner conditional: always result 1..5

Thus, the conditional result in table:

+--------------+-------+-------+--------+
| selected     | lower | l max | result |
+--------------+-------+-------+--------+
| page   =  1  |   T   | 1..5  |  1..5  |
| page   =  2  |   T   | 1..5  |  1..5  |
| page   =  3  |   T   | 1..5  |  1..5  |
| page   =  4  |       | 1..5  |        |
| page   =  5  |       | 1..5  |        |
| page   =  6  |       | 1..5  |        |
| page   =  7  |       | 1..5  |        |
| page   =  8  |       | 1..5  |        |
| page   =  9  |       | 1..5  |        |
+--------------+-------+-------+--------+

Combined: All Conditional

Now we have all the logic combined at once.

+-------------+-------+
| pagination  |   1   |
| adjacent    |   2   |
| totalPost   |  10   |
+-------------+-------+
| VARIABLE            |
| totalPages  |  10   |
| max_links   |   5   |
| lower_limit |   3   |
| upper_limit |   7   |
+-------------+-------+-+-------+-------+-------+-------+
| selected    | adjacent| lower | l max | upper | u max |
+-------------+---------+-------+-------+-------+-------+
| cursor = 1  |  1..3   |   T   | 1..5  |       | 5..9  |
| cursor = 2  |  1..4   |   T   | 1..5  |       | 5..9  |
| cursor = 3  |  1..5   |   T   | 1..5  |       | 5..9  |
| cursor = 4  |  2..6   |       | 1..5  |       | 5..9  |
| cursor = 5  |  3..7   |       | 1..5  |       | 5..9  |
| cursor = 6  |  4..8   |       | 1..5  |       | 5..9  |
| cursor = 7  |  5..9   |       | 1..5  |   T   | 5..9  |
| cursor = 8  |  5..9   |       | 1..5  |   T   | 5..9  |
| cursor = 9  |  5..9   |       | 1..5  |   T   | 5..9  |
+-------------+---------+-------+-------+-------+-------+

Final Result

As a conclusion table.

+-------------+-------+
| VARIABLE            |
| totalPages  |  10   |
| max_links   |   5   |
| lower_limit |   3   |
| upper_limit |   8   |
+-------------+-------+-------+---------+
| selected    | lower | upper | adjacent|
+-------------+-------+-------+---------+
| cursor =  1 | 1..5  |       |         |
| cursor =  2 | 1..5  |       |         |
| cursor =  3 | 1..5  |       |         |
| cursor =  4 |       |       |  2..6   |
| cursor =  5 |       |       |  3..7   |
| cursor =  6 |       |       |  4..8   |
| cursor =  7 |       | 5..9  |         |
| cursor =  8 |       | 5..9  |         |
| cursor =  9 |       | 5..9  |         |
+-------------+-------+-------+---------+
| selected    | if elsif else | result  |
+-------------+---------------+---------+
| cursor = 1  |               |  1..5   |
| cursor = 2  |               |  1..5   |
| cursor = 3  |               |  1..5   |
| cursor = 4  |               |  2..6   |
| cursor = 5  |               |  3..7   |
| cursor = 6  |               |  4..8   |
| cursor = 7  |               |  5..9   |
| cursor = 8  |               |  5..9   |
| cursor = 9  |               |  5..9   |
+-------------+---------------+---------+

3: Summary: Navigation: Adjacent

Now you can enjoy the complete adjacent code as below:

<%
/*
* Helper function
*/

function pagination_url(number) {
  var path = config.index_generator.path;
  
  // dirty quick fix, avoid double (//)
  if (path=='/') { path = '' }

  if (number>1) {
      path = path + '/' + config.pagination_dir + '/' + number;
  }

  return url_for(path);
}

/* 
* Pagination links 
* https://glennmccomb.com/articles/how-to-build-custom-hugo-pagination/
* Adjacent: Number of links either side of the current page
*/

const adjacent_links = 2;
var current          = page.current;
var max_links        = (adjacent_links * 2) + 1;
var lower_limit      = 1 + adjacent_links;
var upper_limit      = page.total - adjacent_links;
%>

<nav class="pagination is-small is-centered"
     role="navigation" aria-label="pagination">
  <% if (page.total > 1) { %>
    <ul class="pagination-list">

    <% 
  var cursor; 
  for (cursor = 1; cursor <= page.total; cursor++) { 
    var show_cursor_flag = false;

    if (page.total > max_links) {
      // Complex page numbers.

      if (current <= lower_limit) {
        // Lower limit pages.
        // If the user is on a page which is in the lower limit.
        if (cursor <= max_links) {
          // If the current loop page is less than max_links.
          show_cursor_flag = true;
        }
      } else if (current >= upper_limit) {
        // Upper limit pages.
        // If the user is on a page which is in the upper limit.
        if (cursor > (page.total - max_links)) {
          // If the current loop page is less than max_links.
          show_cursor_flag = true;
        }
      } else {
        // Middle pages.
        if ( (cursor >= current - adjacent_links) 
        &&   (cursor <= current + adjacent_links) ) {
          show_cursor_flag = true;
        }
      }
    } else {
      // Simple page numbers.
      show_cursor_flag = true;
    }

    // Show Pager.
    if (show_cursor_flag) { %>
      <li>
        <% if (current != cursor) { %>
        <a href="<%= pagination_url(cursor) %>"
           class="pagination-link" 
           aria-label="Goto page <%= cursor %>">
          <%= cursor %>
        </a>
        <% } else { %>
        <a class="pagination-link is-current" 
           aria-label="Page <%= current %>">
          <%= current %>
        </a>
        <% } %>
      </li>
      <% } /* if */ %>
    <% } /* for */ %>

    </ul>
  <% } %>
</nav>

Notice that this is not the final code, as we want to add some indicator and cosmetic later.

  • .

What is Next ?

Consider continue reading [ Hexo - Pagination - Indicator ]. There are, some interesting topic about Pagination in Hexo.

Thank you for reading.