From: Hypolite Petovan <hypolite@mrpetovan.com>
Date: Wed, 24 Oct 2018 06:15:24 +0000 (-0400)
Subject: Move $pager and $page_offset out of App
X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=14237a95998bed6a5fe5e7e9122970b9dea77a27;p=friendica.git

Move $pager and $page_offset out of App

- Move infinite scroll data output in a module hook
- Use Pager instead of paginate() and alt_pager()
---

diff --git a/boot.php b/boot.php
index 7fb87ca4eb..6b66625c6c 100644
--- a/boot.php
+++ b/boot.php
@@ -1069,45 +1069,3 @@ function validate_include(&$file)
 	// Simply return flag
 	return $valid;
 }
-
-/**
- * @brief Get the data which is needed for infinite scroll
- *
- * For invinite scroll we need the page number of the actual page
- * and the the URI where the content of the next page comes from.
- * This data is needed for the js part in main.js.
- * Note: infinite scroll does only work for the network page (module)
- *
- * @param string $module The name of the module (e.g. "network")
- * @return array Of infinite scroll data
- * 	'pageno' => $pageno The number of the actual page
- * 	'reload_uri' => $reload_uri The URI of the content we have to load
- */
-function infinite_scroll_data($module)
-{
-	if (PConfig::get(local_user(), 'system', 'infinite_scroll')
-		&& $module == 'network'
-		&& defaults($_GET, 'mode', '') != 'minimal'
-	) {
-		// get the page number
-		$pageno = defaults($_GET, 'page', 1);
-
-		$reload_uri = "";
-
-		// try to get the uri from which we load the content
-		foreach ($_GET as $param => $value) {
-			if (($param != "page") && ($param != "q")) {
-				$reload_uri .= "&" . $param . "=" . urlencode($value);
-			}
-		}
-
-		$a = get_app();
-		if ($a->page_offset != "" && !strstr($reload_uri, "&offset=")) {
-			$reload_uri .= "&offset=" . urlencode($a->page_offset);
-		}
-
-		$arr = ["pageno" => $pageno, "reload_uri" => $reload_uri];
-
-		return $arr;
-	}
-}
diff --git a/include/conversation.php b/include/conversation.php
index 4a05c916ba..27cbd4289b 100644
--- a/include/conversation.php
+++ b/include/conversation.php
@@ -6,6 +6,7 @@
 use Friendica\App;
 use Friendica\Content\ContactSelector;
 use Friendica\Content\Feature;
+use Friendica\Content\Pager;
 use Friendica\Content\Text\BBCode;
 use Friendica\Core\Addon;
 use Friendica\Core\Config;
@@ -433,8 +434,8 @@ function conv_get_blocklist()
  * that are based on unique features of the calling module.
  *
  */
-function conversation(App $a, array $items, $mode, $update, $preview = false, $order = 'commented', $uid = 0) {
-
+function conversation(App $a, array $items, Pager $pager, $mode, $update, $preview = false, $order = 'commented', $uid = 0)
+{
 	$ssl_state = (local_user() ? true : false);
 
 	$profile_owner = 0;
@@ -469,7 +470,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
 				. ((x($_GET, 'cmax'))   ? '&cmax='   . $_GET['cmax']   : '')
 				. ((x($_GET, 'file'))   ? '&file='   . $_GET['file']   : '')
 
-				. "'; var profile_page = " . $a->pager['page'] . "; </script>\r\n";
+				. "'; var profile_page = " . $pager->getPage() . "; </script>\r\n";
 		}
 	} elseif ($mode === 'profile') {
 		$items = conversation_add_children($items, false, $order, $uid);
@@ -488,7 +489,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
 
 				$live_update_div = '<div id="live-profile"></div>' . "\r\n"
 					. "<script> var profile_uid = " . $a->profile['profile_uid']
-					. "; var netargs = '?f='; var profile_page = " . $a->pager['page'] . "; </script>\r\n";
+					. "; var netargs = '?f='; var profile_page = " . $pager->getPage() . "; </script>\r\n";
 			}
 		}
 	} elseif ($mode === 'notes') {
@@ -498,7 +499,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
 		if (!$update) {
 			$live_update_div = '<div id="live-notes"></div>' . "\r\n"
 				. "<script> var profile_uid = " . local_user()
-				. "; var netargs = '/?f='; var profile_page = " . $a->pager['page'] . "; </script>\r\n";
+				. "; var netargs = '/?f='; var profile_page = " . $pager->getPage() . "; </script>\r\n";
 		}
 	} elseif ($mode === 'display') {
 		$items = conversation_add_children($items, false, $order, $uid);
@@ -516,7 +517,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
 		if (!$update) {
 			$live_update_div = '<div id="live-community"></div>' . "\r\n"
 				. "<script> var profile_uid = -1; var netargs = '" . substr($a->cmd, 10)
-				."/?f='; var profile_page = " . $a->pager['page'] . "; </script>\r\n";
+				."/?f='; var profile_page = " . $pager->getPage() . "; </script>\r\n";
 		}
 	} elseif ($mode === 'contacts') {
 		$items = conversation_add_children($items, true, $order, $uid);
@@ -525,7 +526,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
 		if (!$update) {
 			$live_update_div = '<div id="live-contacts"></div>' . "\r\n"
 				. "<script> var profile_uid = -1; var netargs = '" . substr($a->cmd, 9)
-				."/?f='; var profile_page = " . $a->pager['page'] . "; </script>\r\n";
+				."/?f='; var profile_page = " . $pager->getPage() . "; </script>\r\n";
 		}
 	} elseif ($mode === 'search') {
 		$live_update_div = '<div id="live-search"></div>' . "\r\n";
diff --git a/include/text.php b/include/text.php
index 5a18529d93..2d497bd58b 100644
--- a/include/text.php
+++ b/include/text.php
@@ -262,132 +262,6 @@ function unxmlify($s) {
 	return $ret;
 }
 
-
-/**
- * @brief Paginator function. Pushes relevant links in a pager array structure.
- *
- * Links are generated depending on the current page and the total number of items.
- * Inactive links (like "first" and "prev" on page 1) are given the "disabled" class.
- * Current page link is given the "active" CSS class
- *
- * @param App $a App instance
- * @param int $count [optional] item count (used with minimal pager)
- * @return Array data for pagination template
- */
-function paginate_data(App $a, $count = null) {
-	$stripped = preg_replace('/([&?]page=[0-9]*)/', '', $a->query_string);
-
-	$stripped = str_replace('q=', '', $stripped);
-	$stripped = trim($stripped, '/');
-	$pagenum = $a->pager['page'];
-
-	if (($a->page_offset != '') && !preg_match('/[?&].offset=/', $stripped)) {
-		$stripped .= '&offset=' . urlencode($a->page_offset);
-	}
-
-	$url = $stripped;
-	$data = [];
-
-	function _l(&$d, $name, $url, $text, $class = '') {
-		if (strpos($url, '?') === false && ($pos = strpos($url, '&')) !== false) {
-			$url = substr($url, 0, $pos) . '?' . substr($url, $pos + 1);
-		}
-
-		$d[$name] = ['url' => $url, 'text' => $text, 'class' => $class];
-	}
-
-	if (!is_null($count)) {
-		// minimal pager (newer / older)
-		$data['class'] = 'pager';
-		_l($data, 'prev', $url . '&page=' . ($a->pager['page'] - 1), L10n::t('newer'), 'previous' . ($a->pager['page'] == 1 ? ' disabled' : ''));
-		_l($data, 'next', $url . '&page=' . ($a->pager['page'] + 1), L10n::t('older'), 'next' . ($count <= 0 ? ' disabled' : ''));
-	} else {
-		// full pager (first / prev / 1 / 2 / ... / 14 / 15 / next / last)
-		$data['class'] = 'pagination';
-		if ($a->pager['total'] > $a->pager['itemspage']) {
-			_l($data, 'first', $url . '&page=1', L10n::t('first'), $a->pager['page'] == 1 ? 'disabled' : '');
-			_l($data, 'prev', $url . '&page=' . ($a->pager['page'] - 1), L10n::t('prev'), $a->pager['page'] == 1 ? 'disabled' : '');
-
-			$numpages = $a->pager['total'] / $a->pager['itemspage'];
-
-			$numstart = 1;
-			$numstop = $numpages;
-
-			// Limit the number of displayed page number buttons.
-			if ($numpages > 8) {
-				$numstart = (($pagenum > 4) ? ($pagenum - 4) : 1);
-				$numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 8));
-			}
-
-			$pages = [];
-
-			for ($i = $numstart; $i <= $numstop; $i++) {
-				if ($i == $a->pager['page']) {
-					_l($pages, $i, '#',  $i, 'current active');
-				} else {
-					_l($pages, $i, $url . '&page='. $i, $i, 'n');
-				}
-			}
-
-			if (($a->pager['total'] % $a->pager['itemspage']) != 0) {
-				if ($i == $a->pager['page']) {
-					_l($pages, $i, '#',  $i, 'current active');
-				} else {
-					_l($pages, $i, $url . '&page=' . $i, $i, 'n');
-				}
-			}
-
-			$data['pages'] = $pages;
-
-			$lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages);
-			_l($data, 'next', $url . '&page=' . ($a->pager['page'] + 1), L10n::t('next'), $a->pager['page'] == $lastpage ? 'disabled' : '');
-			_l($data, 'last', $url . '&page=' . $lastpage, L10n::t('last'), $a->pager['page'] == $lastpage ? 'disabled' : '');
-		}
-	}
-
-	return $data;
-}
-
-
-/**
- * Automatic pagination.
- *
- *  To use, get the count of total items.
- * Then call $a->set_pager_total($number_items);
- * Optionally call $a->set_pager_itemspage($n) to the number of items to display on each page
- * Then call paginate($a) after the end of the display loop to insert the pager block on the page
- * (assuming there are enough items to paginate).
- * When using with SQL, the setting LIMIT %d, %d => $a->pager['start'],$a->pager['itemspage']
- * will limit the results to the correct items for the current page.
- * The actual page handling is then accomplished at the application layer.
- *
- * @param App $a App instance
- * @return string html for pagination #FIXME remove html
- */
-function paginate(App $a) {
-
-	$data = paginate_data($a);
-	$tpl = get_markup_template("paginate.tpl");
-	return replace_macros($tpl, ["pager" => $data]);
-
-}
-
-
-/**
- * Alternative pager
- * @param App $a App instance
- * @param int $i
- * @return string html for pagination #FIXME remove html
- */
-function alt_pager(App $a, $i) {
-
-	$data = paginate_data($a, $i);
-	$tpl = get_markup_template("paginate.tpl");
-	return replace_macros($tpl, ['pager' => $data]);
-
-}
-
-
 /**
  * Loader for infinite scrolling
  * @return string html for loader
diff --git a/mod/admin.php b/mod/admin.php
index c50927e97c..d90cfb7ab9 100644
--- a/mod/admin.php
+++ b/mod/admin.php
@@ -8,6 +8,7 @@
 use Friendica\App;
 use Friendica\BaseModule;
 use Friendica\Content\Feature;
+use Friendica\Content\Pager;
 use Friendica\Content\Text\Markdown;
 use Friendica\Core\Addon;
 use Friendica\Core\Config;
@@ -482,10 +483,9 @@ function admin_page_contactblock(App $a)
 
 	$total = DBA::count('contact', $condition);
 
-	$a->setPagerTotal($total);
-	$a->setPagerItemsPage(30);
+	$pager = new Pager($a->query_string, $total, 30);
 
-	$statement = DBA::select('contact', [], $condition, ['limit' => [$a->pager['start'], $a->pager['itemspage']]]);
+	$statement = DBA::select('contact', [], $condition, ['limit' => [$pager->getStart(), $pager->getItemsPerPage()]]);
 
 	$contacts = DBA::toArray($statement);
 
@@ -513,7 +513,7 @@ function admin_page_contactblock(App $a)
 
 		'$contacts'   => $contacts,
 		'$total_contacts' => L10n::tt('%s total blocked contact', '%s total blocked contacts', $total),
-		'$paginate'   => paginate($a),
+		'$paginate'   => $pager->renderFull(),
 		'$contacturl' => ['contact_url', L10n::t("Profile URL"), '', L10n::t("URL of the remote contact to block.")],
 	]);
 	return $o;
@@ -1812,12 +1812,7 @@ function admin_page_users(App $a)
 	/* get pending */
 	$pending = Register::getPending();
 
-	/* get users */
-	$total = q("SELECT COUNT(*) AS `total` FROM `user` WHERE 1");
-	if (count($total)) {
-		$a->setPagerTotal($total[0]['total']);
-		$a->setPagerItemsPage(100);
-	}
+	$pager = new Pager($a->query_string, DBA::count('user'), 100);
 
 	/* ordering */
 	$valid_orders = [
@@ -1849,7 +1844,7 @@ function admin_page_users(App $a)
 				FROM `user`
 				INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self`
 				WHERE `user`.`verified`
-				ORDER BY $sql_order $sql_order_direction LIMIT %d, %d", intval($a->pager['start']), intval($a->pager['itemspage'])
+				ORDER BY $sql_order $sql_order_direction LIMIT %d, %d", $pager->getStart(), $pager->getItemsPerPage()
 	);
 
 	$adminlist = explode(",", str_replace(" ", "", Config::get('config', 'admin_email')));
@@ -1947,7 +1942,7 @@ function admin_page_users(App $a)
 		'$form_security_token' => BaseModule::getFormSecurityToken("admin_users"),
 
 		// values //
-		'$baseurl' => System::baseUrl(true),
+		'$baseurl' => $a->getBaseURL(true),
 
 		'$pending' => $pending,
 		'deleted' => $deleted,
@@ -1956,7 +1951,7 @@ function admin_page_users(App $a)
 		'$newusernickname' => ['new_user_nickname', L10n::t("Nickname"), '', L10n::t("Nickname of the new user.")],
 		'$newuseremail' => ['new_user_email', L10n::t("Email"), '', L10n::t("Email address of the new user."), '', '', 'email'],
 	]);
-	$o .= paginate($a);
+	$o .= $pager->renderFull();
 	return $o;
 }
 
diff --git a/mod/allfriends.php b/mod/allfriends.php
index cee067e97a..d2501e9fe4 100644
--- a/mod/allfriends.php
+++ b/mod/allfriends.php
@@ -5,6 +5,7 @@
 
 use Friendica\App;
 use Friendica\Content\ContactSelector;
+use Friendica\Content\Pager;
 use Friendica\Core\L10n;
 use Friendica\Core\System;
 use Friendica\Database\DBA;
@@ -45,9 +46,9 @@ function allfriends_content(App $a)
 
 	$total = Model\GContact::countAllFriends(local_user(), $cid);
 
-	$a->setPagerTotal($total);
+	$pager = new Pager($a->query_string, $total);
 
-	$r = Model\GContact::allFriends(local_user(), $cid, $a->pager['start'], $a->pager['itemspage']);
+	$r = Model\GContact::allFriends(local_user(), $cid, $pager->getStart(), $pager->getItemsPerPage());
 	if (!DBA::isResult($r)) {
 		$o .= L10n::t('No friends to display.');
 		return $o;
@@ -103,7 +104,7 @@ function allfriends_content(App $a)
 		//'$title' => L10n::t('Friends of %s', htmlentities($c[0]['name'])),
 		'$tab_str' => $tab_str,
 		'$contacts' => $entries,
-		'$paginate' => paginate($a),
+		'$paginate' => $pager->renderFull(),
 	]);
 
 	return $o;
diff --git a/mod/common.php b/mod/common.php
index 0b51f20d73..993bc58777 100644
--- a/mod/common.php
+++ b/mod/common.php
@@ -5,6 +5,7 @@
 
 use Friendica\App;
 use Friendica\Content\ContactSelector;
+use Friendica\Content\Pager;
 use Friendica\Core\L10n;
 use Friendica\Database\DBA;
 use Friendica\Model;
@@ -87,16 +88,16 @@ function common_content(App $a)
 	}
 
 	if ($t > 0) {
-		$a->setPagerTotal($t);
+		$pager = new Pager($a->query_string, $t);
 	} else {
 		notice(L10n::t('No contacts in common.') . EOL);
 		return $o;
 	}
 
 	if ($cid) {
-		$r = Model\GContact::commonFriends($uid, $cid, $a->pager['start'], $a->pager['itemspage']);
+		$r = Model\GContact::commonFriends($uid, $cid, $pager->getStart(), $pager->getItemsPerPage());
 	} else {
-		$r = Model\GContact::commonFriendsZcid($uid, $zcid, $a->pager['start'], $a->pager['itemspage']);
+		$r = Model\GContact::commonFriendsZcid($uid, $zcid, $pager->getStart(), $pager->getItemsPerPage());
 	}
 
 	if (!DBA::isResult($r)) {
@@ -147,7 +148,7 @@ function common_content(App $a)
 		'$title'    => $title,
 		'$tab_str'  => $tab_str,
 		'$contacts' => $entries,
-		'$paginate' => paginate($a),
+		'$paginate' => $pager->renderFull(),
 	]);
 
 	return $o;
diff --git a/mod/community.php b/mod/community.php
index 0eaccc0db0..7474fc2429 100644
--- a/mod/community.php
+++ b/mod/community.php
@@ -5,6 +5,7 @@
 
 use Friendica\App;
 use Friendica\Content\Nav;
+use Friendica\Content\Pager;
 use Friendica\Core\ACL;
 use Friendica\Core\Config;
 use Friendica\Core\L10n;
@@ -153,9 +154,9 @@ function community_content(App $a, $update = 0)
 		$itemspage_network = $a->force_max_items;
 	}
 
-	$a->setPagerItemsPage($itemspage_network);
+	$pager = new Pager($a->query_string, null, $itemspage_network);
 
-	$r = community_getitems($a->pager['start'], $a->pager['itemspage'], $content, $accounttype);
+	$r = community_getitems($pager->getStart(), $pager->getItemsPerPage(), $content, $accounttype);
 
 	if (!DBA::isResult($r)) {
 		info(L10n::t('No results.') . EOL);
@@ -179,22 +180,22 @@ function community_content(App $a, $update = 0)
 				}
 				$previousauthor = $item["author-link"];
 
-				if (($numposts < $maxpostperauthor) && (count($s) < $a->pager['itemspage'])) {
+				if (($numposts < $maxpostperauthor) && (count($s) < $pager->getItemsPerPage())) {
 					$s[] = $item;
 				}
 			}
-			if (count($s) < $a->pager['itemspage']) {
-				$r = community_getitems($a->pager['start'] + ($count * $a->pager['itemspage']), $a->pager['itemspage'], $content, $accounttype);
+			if (count($s) < $pager->getItemsPerPage()) {
+				$r = community_getitems($pager->getStart() + ($count * $pager->getItemsPerPage()), $pager->getItemsPerPage(), $content, $accounttype);
 			}
-		} while ((count($s) < $a->pager['itemspage']) && ( ++$count < 50) && (count($r) > 0));
+		} while ((count($s) < $pager->getItemsPerPage()) && ( ++$count < 50) && (count($r) > 0));
 	} else {
 		$s = $r;
 	}
 
-	$o .= conversation($a, $s, 'community', $update, false, 'commented', local_user());
+	$o .= conversation($a, $s, $pager, 'community', $update, false, 'commented', local_user());
 
 	if (!$update) {
-		$o .= alt_pager($a, count($r));
+		$o .= $pager->renderMinimal(count($r));
 	}
 
 	$t = get_markup_template("community.tpl");
diff --git a/mod/directory.php b/mod/directory.php
index 202132e366..f9104b9a53 100644
--- a/mod/directory.php
+++ b/mod/directory.php
@@ -5,6 +5,7 @@
 
 use Friendica\App;
 use Friendica\Content\Nav;
+use Friendica\Content\Pager;
 use Friendica\Content\Widget;
 use Friendica\Core\Addon;
 use Friendica\Core\Config;
@@ -16,8 +17,6 @@ use Friendica\Util\Proxy as ProxyUtils;
 
 function directory_init(App $a)
 {
-	$a->setPagerItemsPage(60);
-
 	if (local_user()) {
 		$a->page['aside'] .= Widget::findPeople();
 		$a->page['aside'] .= Widget::follow();
@@ -83,16 +82,18 @@ function directory_content(App $a)
 	$publish = (Config::get('system', 'publish_all') ? '' : " AND `publish` = 1 " );
 
 
+	$total = 0;
 	$cnt = DBA::fetchFirst("SELECT COUNT(*) AS `total` FROM `profile`
 				LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid`
 				WHERE `is-default` $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` $sql_extra");
 	if (DBA::isResult($cnt)) {
-		$a->setPagerTotal($cnt['total']);
+		$total = $cnt['total'];
 	}
+	$pager = new Pager($a->query_string, $total, 60);
 
 	$order = " ORDER BY `name` ASC ";
 
-	$limit = intval($a->pager['start'])."," . intval($a->pager['itemspage']);
+	$limit = $pager->getStart()."," . $pager->getItemsPerPage();
 
 	$r = DBA::p("SELECT `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname`, `user`.`timezone` , `user`.`page-flags`,
 			`contact`.`addr`, `contact`.`url` AS profile_url FROM `profile`
@@ -212,7 +213,7 @@ function directory_content(App $a)
 			'$findterm'  => (strlen($search) ? $search : ""),
 			'$title'     => L10n::t('Site Directory'),
 			'$submit'    => L10n::t('Find'),
-			'$paginate'  => paginate($a),
+			'$paginate'  => $pager->renderFull(),
 		]);
 	} else {
 		info(L10n::t("No entries \x28some entries may be hidden\x29.") . EOL);
diff --git a/mod/dirfind.php b/mod/dirfind.php
index 3e5aa83a72..7cff5f28a3 100644
--- a/mod/dirfind.php
+++ b/mod/dirfind.php
@@ -5,6 +5,7 @@
 
 use Friendica\App;
 use Friendica\Content\ContactSelector;
+use Friendica\Content\Pager;
 use Friendica\Content\Widget;
 use Friendica\Core\Config;
 use Friendica\Core\L10n;
@@ -71,7 +72,7 @@ function dirfind_content(App $a, $prefix = "") {
 			$j = new stdClass();
 			$j->total = 1;
 			$j->items_page = 1;
-			$j->page = $a->pager['page'];
+			$j->page = $pager->getPage();
 
 			$objresult = new stdClass();
 			$objresult->cid = 0;
@@ -100,7 +101,7 @@ function dirfind_content(App $a, $prefix = "") {
 				$extra_sql = "";
 
 			$perpage = 80;
-			$startrec = (($a->pager['page']) * $perpage) - $perpage;
+			$startrec = (($pager->getPage()) * $perpage) - $perpage;
 
 			if (Config::get('system','diaspora_enabled')) {
 				$diaspora = Protocol::DIASPORA;
@@ -141,7 +142,7 @@ function dirfind_content(App $a, $prefix = "") {
 			$j = new stdClass();
 			$j->total = $count[0]["total"];
 			$j->items_page = $perpage;
-			$j->page = $a->pager['page'];
+			$j->page = $pager->getPage();
 			foreach ($results AS $result) {
 				if (PortableContact::alternateOStatusUrl($result["nurl"])) {
 					continue;
@@ -178,7 +179,7 @@ function dirfind_content(App $a, $prefix = "") {
 			Worker::add(PRIORITY_LOW, 'DiscoverPoCo', "dirsearch", urlencode($search));
 		} else {
 
-			$p = (($a->pager['page'] != 1) ? '&p=' . $a->pager['page'] : '');
+			$p = (($pager->getPage() != 1) ? '&p=' . $pager->getPage() : '');
 
 			if(strlen(Config::get('system','directory')))
 				$x = Network::fetchUrl(get_server().'/lsearch?f=' . $p .  '&search=' . urlencode($search));
@@ -186,12 +187,8 @@ function dirfind_content(App $a, $prefix = "") {
 			$j = json_decode($x);
 		}
 
-		if ($j->total) {
-			$a->setPagerTotal($j->total);
-			$a->setPagerItemsPage($j->items_page);
-		}
-
 		if (!empty($j->results)) {
+			$pager = new Pager($a->query_string, $j->total, $j->items_page);
 
 			$id = 0;
 
@@ -252,13 +249,13 @@ function dirfind_content(App $a, $prefix = "") {
 				$entries[] = $entry;
 			}
 
-		$tpl = get_markup_template('viewcontact_template.tpl');
+			$tpl = get_markup_template('viewcontact_template.tpl');
 
-		$o .= replace_macros($tpl,[
-			'title' => $header,
-			'$contacts' => $entries,
-			'$paginate' => paginate($a),
-		]);
+			$o .= replace_macros($tpl,[
+				'title' => $header,
+				'$contacts' => $entries,
+				'$paginate' => $pager->renderFull(),
+			]);
 
 		} else {
 			info(L10n::t('No matches') . EOL);
diff --git a/mod/display.php b/mod/display.php
index 38cdfe41d3..e4a17804b3 100644
--- a/mod/display.php
+++ b/mod/display.php
@@ -4,6 +4,7 @@
  */
 
 use Friendica\App;
+use Friendica\Content\Pager;
 use Friendica\Content\Text\BBCode;
 use Friendica\Content\Text\HTML;
 use Friendica\Core\ACL;
@@ -16,9 +17,8 @@ use Friendica\Model\Contact;
 use Friendica\Model\Group;
 use Friendica\Model\Item;
 use Friendica\Model\Profile;
-use Friendica\Protocol\DFRN;
 use Friendica\Protocol\ActivityPub;
-use Friendica\Util\Security;
+use Friendica\Protocol\DFRN;
 
 function display_init(App $a)
 {
@@ -358,7 +358,7 @@ function display_content(App $a, $update = false, $update_uid = 0)
 		$o .= "<script> var netargs = '?f=&item_id=" . $item_id . "'; </script>";
 	}
 
-	$o .= conversation($a, [$item], 'display', $update_uid, false, 'commented', local_user());
+	$o .= conversation($a, [$item], new Pager($a->query_string), 'display', $update_uid, false, 'commented', local_user());
 
 	// Preparing the meta header
 	$description = trim(HTML::toPlaintext(BBCode::convert($item["body"], false), 0, true));
diff --git a/mod/item.php b/mod/item.php
index cd64b70f85..dcccf5d75c 100644
--- a/mod/item.php
+++ b/mod/item.php
@@ -16,6 +16,7 @@
  */
 
 use Friendica\App;
+use Friendica\Content\Pager;
 use Friendica\Content\Text\BBCode;
 use Friendica\Content\Text\HTML;
 use Friendica\Core\Addon;
@@ -33,6 +34,7 @@ use Friendica\Protocol\Email;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Emailer;
 use Friendica\Util\Security;
+use function Friendica\Core\function_exists;
 
 require_once 'include/enotify.php';
 require_once 'include/text.php';
@@ -667,10 +669,10 @@ function item_post(App $a) {
 		$datarray["item_id"] = -1;
 		$datarray["author-network"] = Protocol::DFRN;
 
-		$o = conversation($a,[array_merge($contact_record,$datarray)],'search', false, true);
+		$o = conversation($a, [array_merge($contact_record, $datarray)], new Pager($a->query_string), 'search', false, true);
 		logger('preview: ' . $o);
 		echo json_encode(['preview' => $o]);
-		killme();
+		exit();
 	}
 
 	Addon::callHooks('post_local',$datarray);
diff --git a/mod/match.php b/mod/match.php
index b299619940..4254e4c45a 100644
--- a/mod/match.php
+++ b/mod/match.php
@@ -4,6 +4,7 @@
  */
 
 use Friendica\App;
+use Friendica\Content\Pager;
 use Friendica\Content\Widget;
 use Friendica\Core\Config;
 use Friendica\Core\L10n;
@@ -54,8 +55,8 @@ function match_content(App $a)
 
 	if ($tags) {
 		$params['s'] = $tags;
-		if ($a->pager['page'] != 1) {
-			$params['p'] = $a->pager['page'];
+		if ($pager->getPage() != 1) {
+			$params['p'] = $pager->getPage();
 		}
 
 		if (strlen(Config::get('system', 'directory'))) {
@@ -66,12 +67,9 @@ function match_content(App $a)
 
 		$j = json_decode($x);
 
-		if ($j->total) {
-			$a->setPagerTotal($j->total);
-			$a->setPagerItemsPage($j->items_page);
-		}
-
 		if (count($j->results)) {
+			$pager = new Pager($a->query_string, $j->total, $j->items_page);
+
 			$id = 0;
 
 			foreach ($j->results as $jj) {
@@ -119,7 +117,7 @@ function match_content(App $a)
 				[
 				'$title' => L10n::t('Profile Match'),
 				'$contacts' => $entries,
-				'$paginate' => paginate($a)]
+				'$paginate' => $pager->renderFull()]
 			);
 		} else {
 			info(L10n::t('No matches') . EOL);
diff --git a/mod/message.php b/mod/message.php
index 8a04f9abef..de2ee3cf01 100644
--- a/mod/message.php
+++ b/mod/message.php
@@ -5,6 +5,7 @@
 
 use Friendica\App;
 use Friendica\Content\Nav;
+use Friendica\Content\Pager;
 use Friendica\Content\Smilies;
 use Friendica\Content\Text\BBCode;
 use Friendica\Core\ACL;
@@ -13,10 +14,10 @@ use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Model\Mail;
+use Friendica\Module\Login;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Proxy as ProxyUtils;
 use Friendica\Util\Temporal;
-use Friendica\Module\Login;
 
 require_once 'include/conversation.php';
 
@@ -273,16 +274,18 @@ function message_content(App $a)
 
 		$o .= $header;
 
+		$total = 0;
 		$r = q("SELECT count(*) AS `total`, ANY_VALUE(`created`) AS `created` FROM `mail`
 			WHERE `mail`.`uid` = %d GROUP BY `parent-uri` ORDER BY `created` DESC",
 			intval(local_user())
 		);
-
 		if (DBA::isResult($r)) {
-			$a->setPagerTotal($r[0]['total']);
+			$total = $r[0]['total'];
 		}
 
-		$r = get_messages(local_user(), $a->pager['start'], $a->pager['itemspage']);
+		$pager = new Pager($a->query_string, $total);
+
+		$r = get_messages(local_user(), $pager->getStart(), $pager->getItemsPerPage());
 
 		if (!DBA::isResult($r)) {
 			info(L10n::t('No messages.') . EOL);
@@ -291,7 +294,7 @@ function message_content(App $a)
 
 		$o .= render_messages($r, 'mail_list.tpl');
 
-		$o .= paginate($a);
+		$o .= $pager->renderFull();
 
 		return $o;
 	}
diff --git a/mod/network.php b/mod/network.php
index b697cb3a63..6f02e8c113 100644
--- a/mod/network.php
+++ b/mod/network.php
@@ -8,14 +8,15 @@ use Friendica\App;
 use Friendica\Content\Feature;
 use Friendica\Content\ForumManager;
 use Friendica\Content\Nav;
+use Friendica\Content\Pager;
 use Friendica\Content\Widget;
 use Friendica\Core\ACL;
 use Friendica\Core\Addon;
 use Friendica\Core\Config;
+use Friendica\Core\Hook;
 use Friendica\Core\L10n;
 use Friendica\Core\PConfig;
 use Friendica\Core\Protocol;
-use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Model\Group;
@@ -35,6 +36,8 @@ function network_init(App $a)
 		return;
 	}
 
+	Hook::add('head', __FILE__, 'network_infinite_scroll_head');
+
 	$search = (x($_GET, 'search') ? escape_tags($_GET['search']) : '');
 
 	if (($search != '') && !empty($_GET['submit'])) {
@@ -279,7 +282,7 @@ function network_query_get_sel_group(App $a)
  * @param integer $update Used for the automatic reloading
  * @return string SQL with the appropriate LIMIT clause
  */
-function networkPager($a, $update)
+function networkPager(App $a, Pager $pager, $update)
 {
 	if ($update) {
 		// only setup pagination on initial page view
@@ -302,9 +305,9 @@ function networkPager($a, $update)
 		$itemspage_network = $a->force_max_items;
 	}
 
-	$a->setPagerItemsPage($itemspage_network);
+	$pager->setItemsPerPage($itemspage_network);
 
-	return sprintf(" LIMIT %d, %d ", intval($a->pager['start']), intval($a->pager['itemspage']));
+	return sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
 }
 
 /**
@@ -328,24 +331,24 @@ function networkSetSeen($condition)
 /**
  * @brief Create the conversation HTML
  *
- * @param App $a The global App
- * @param array $items Items of the conversation
- * @param string $mode Display mode for the conversation
+ * @param App     $a      The global App
+ * @param array   $items  Items of the conversation
+ * @param string  $mode   Display mode for the conversation
  * @param integer $update Used for the automatic reloading
  * @return string HTML of the conversation
  */
-function networkConversation($a, $items, $mode, $update, $ordering = '')
+function networkConversation(App $a, $items, Pager $pager, $mode, $update, $ordering = '')
 {
 	// Set this so that the conversation function can find out contact info for our wall-wall items
 	$a->page_contact = $a->contact;
 
-	$o = conversation($a, $items, $mode, $update, false, $ordering, local_user());
+	$o = conversation($a, $items, $pager, $mode, $update, false, $ordering, local_user());
 
 	if (!$update) {
 		if (PConfig::get(local_user(), 'system', 'infinite_scroll')) {
 			$o .= scroll_loader();
 		} else {
-			$o .= alt_pager($a, count($items));
+			$o .= $pager->renderMinimal(count($items));
 		}
 	}
 
@@ -372,7 +375,7 @@ function network_content(App $a, $update = 0, $parent = 0)
 		}
 	}
 
-	if (x($_GET, 'file')) {
+	if (!empty($_GET['file'])) {
 		$flat_mode = true;
 	}
 
@@ -388,12 +391,14 @@ function network_content(App $a, $update = 0, $parent = 0)
 /**
  * @brief Get the network content in flat view
  *
- * @param App $a The global App
+ * @param Pager   $pager
+ * @param App     $a      The global App
  * @param integer $update Used for the automatic reloading
  * @return string HTML of the network content in flat view
  */
 function networkFlatView(App $a, $update = 0)
 {
+	global $pager;
 	// Rawmode is used for fetching new content at the end of the page
 	$rawmode = (isset($_GET['mode']) && ($_GET['mode'] == 'raw'));
 
@@ -405,7 +410,7 @@ function networkFlatView(App $a, $update = 0)
 
 	$o = '';
 
-	$file = ((x($_GET, 'file')) ? $_GET['file'] : '');
+	$file = defaults($_GET, 'file', '');
 
 	if (!$update && !$rawmode) {
 		$tabs = network_tabs($a);
@@ -437,12 +442,15 @@ function networkFlatView(App $a, $update = 0)
 		}
 	}
 
-	$pager_sql = networkPager($a, $update);
+	$pager = new Pager($a->query_string);
+
+	/// @TODO Figure out why this variable is unused
+	$pager_sql = networkPager($a, $pager, $update);
 
 	if (strlen($file)) {
 		$condition = ["`term` = ? AND `otype` = ? AND `type` = ? AND `uid` = ?",
 			$file, TERM_OBJ_POST, TERM_FILE, local_user()];
-		$params = ['order' => ['tid' => true], 'limit' => [$a->pager['start'], $a->pager['itemspage']]];
+		$params = ['order' => ['tid' => true], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
 		$result = DBA::select('term', ['oid'], $condition);
 
 		$posts = [];
@@ -456,14 +464,14 @@ function networkFlatView(App $a, $update = 0)
 		$condition = ['uid' => local_user()];
 	}
 
-	$params = ['order' => ['id' => true], 'limit' => [$a->pager['start'], $a->pager['itemspage']]];
+	$params = ['order' => ['id' => true], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
 	$result = Item::selectForUser(local_user(), [], $condition, $params);
 	$items = Item::inArray($result);
 
 	$condition = ['unseen' => true, 'uid' => local_user()];
 	networkSetSeen($condition);
 
-	$o .= networkConversation($a, $items, 'network-new', $update);
+	$o .= networkConversation($a, $items, $pager, 'network-new', $update);
 
 	return $o;
 }
@@ -471,12 +479,17 @@ function networkFlatView(App $a, $update = 0)
 /**
  * @brief Get the network content in threaded view
  *
- * @param App $a The global App
- * @param integer $update Used for the automatic reloading
+ * @global Pager   $pager
+ * @param  App     $a      The global App
+ * @param  integer $update Used for the automatic reloading
+ * @param  integer $parent
  * @return string HTML of the network content in flat view
  */
 function networkThreadedView(App $a, $update, $parent)
 {
+	/// @TODO this will have to be converted to a static property of the converted Module\Network class
+	global $pager;
+
 	// Rawmode is used for fetching new content at the end of the page
 	$rawmode = (isset($_GET['mode']) AND ( $_GET['mode'] == 'raw'));
 
@@ -543,13 +556,11 @@ function networkThreadedView(App $a, $update, $parent)
 		$tabs = network_tabs($a);
 		$o .= $tabs;
 
-		if ($gid) {
-			if (($t = Contact::getOStatusCountByGroupId($gid)) && !PConfig::get(local_user(), 'system', 'nowarn_insecure')) {
-				notice(L10n::tt("Warning: This group contains %s member from a network that doesn't allow non public messages.",
-						"Warning: This group contains %s members from a network that doesn't allow non public messages.",
-						$t) . EOL);
-				notice(L10n::t("Messages in this group won't be send to these receivers.").EOL);
-			}
+		if ($gid && ($t = Contact::getOStatusCountByGroupId($gid)) && !PConfig::get(local_user(), 'system', 'nowarn_insecure')) {
+			notice(L10n::tt("Warning: This group contains %s member from a network that doesn't allow non public messages.",
+				"Warning: This group contains %s members from a network that doesn't allow non public messages.",
+				$t) . EOL);
+			notice(L10n::t("Messages in this group won't be send to these receivers.").EOL);
 		}
 
 		Nav::setSelected('network');
@@ -706,13 +717,15 @@ function networkThreadedView(App $a, $update, $parent)
 
 	$sql_order = "$sql_table.$ordering";
 
-	if (x($_GET, 'offset')) {
+	if (!empty($_GET['offset'])) {
 		$sql_range = sprintf(" AND $sql_order <= '%s'", DBA::escape($_GET['offset']));
 	} else {
 		$sql_range = '';
 	}
 
-	$pager_sql = networkPager($a, $update);
+	$pager = new Pager($a->query_string);
+
+	$pager_sql = networkPager($a, $pager, $update);
 
 	$last_date = '';
 
@@ -721,31 +734,31 @@ function networkThreadedView(App $a, $update, $parent)
 			if ($last_received != '') {
 				$last_date = $last_received;
 				$sql_range .= sprintf(" AND $sql_table.`received` < '%s'", DBA::escape($last_received));
-				$a->setPagerPage(1);
-				$pager_sql = sprintf(" LIMIT %d, %d ", intval($a->pager['start']), intval($a->pager['itemspage']));
+				$pager->setPage(1);
+				$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
 			}
 			break;
 		case 'commented':
 			if ($last_commented != '') {
 				$last_date = $last_commented;
 				$sql_range .= sprintf(" AND $sql_table.`commented` < '%s'", DBA::escape($last_commented));
-				$a->setPagerPage(1);
-				$pager_sql = sprintf(" LIMIT %d, %d ", intval($a->pager['start']), intval($a->pager['itemspage']));
+				$pager->setPage(1);
+				$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
 			}
 			break;
 		case 'created':
 			if ($last_created != '') {
 				$last_date = $last_created;
 				$sql_range .= sprintf(" AND $sql_table.`created` < '%s'", DBA::escape($last_created));
-				$a->setPagerPage(1);
-				$pager_sql = sprintf(" LIMIT %d, %d ", intval($a->pager['start']), intval($a->pager['itemspage']));
+				$pager->setPage(1);
+				$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
 			}
 			break;
 		case 'id':
 			if (($last_id > 0) && ($sql_table == '`thread`')) {
 				$sql_range .= sprintf(" AND $sql_table.`iid` < '%s'", DBA::escape($last_id));
-				$a->setPagerPage(1);
-				$pager_sql = sprintf(" LIMIT %d, %d ", intval($a->pager['start']), intval($a->pager['itemspage']));
+				$pager->setPage(1);
+				$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
 			}
 			break;
 	}
@@ -835,7 +848,7 @@ function networkThreadedView(App $a, $update, $parent)
 
 		if ($last_date > $top_limit) {
 			$top_limit = $last_date;
-		} elseif ($a->pager['page'] == 1) {
+		} elseif ($pager->getPage() == 1) {
 			// Highest possible top limit when we are on the first page
 			$top_limit = DateTimeFormat::utcNow();
 		}
@@ -898,7 +911,12 @@ function networkThreadedView(App $a, $update, $parent)
 		$date_offset = $_GET['offset'];
 	}
 
-	$a->page_offset = $date_offset;
+	$query_string = $a->query_string;
+	if ($date_offset && !preg_match('/[?&].offset=/', $query_string)) {
+		$query_string .= '&offset=' . urlencode($date_offset);
+	}
+
+	$pager->setQueryString($query_string);
 
 	// We aren't going to try and figure out at the item, group, and page
 	// level which items you've seen and which you haven't. If you're looking
@@ -914,7 +932,7 @@ function networkThreadedView(App $a, $update, $parent)
 
 
 	$mode = 'network';
-	$o .= networkConversation($a, $items, $mode, $update, $ordering);
+	$o .= networkConversation($a, $items, $pager, $mode, $update, $ordering);
 
 	return $o;
 }
@@ -1019,3 +1037,30 @@ function network_tabs(App $a)
 
 	// --- end item filter tabs
 }
+
+/**
+ * Network hook into the HTML head to enable infinite scroll.
+ *
+ * Since the HTML head is built after the module content has been generated, we need to retrieve the base query string
+ * of the page to make the correct asynchronous call. This is obtained through the Pager that was instantiated in
+ * networkThreadedView or networkFlatView.
+ *
+ * @global Pager  $pager
+ * @param  App    $a
+ * @param  string $htmlhead The head tag HTML string
+ */
+function network_infinite_scroll_head(App $a, &$htmlhead)
+{
+	/// @TODO this will have to be converted to a static property of the converted Module\Network class
+	global $pager;
+
+	if (PConfig::get(local_user(), 'system', 'infinite_scroll')
+		&& defaults($_GET, 'mode', '') != 'minimal'
+	) {
+		$tpl = get_markup_template('infinite_scroll_head.tpl');
+		$htmlhead .= replace_macros($tpl, [
+			'$pageno'     => $pager->getPage(),
+			'$reload_uri' => $pager->getBaseQueryString()
+		]);
+	}
+}
\ No newline at end of file
diff --git a/mod/notes.php b/mod/notes.php
index 55a92f8e64..b6b6faefab 100644
--- a/mod/notes.php
+++ b/mod/notes.php
@@ -5,6 +5,7 @@
 
 use Friendica\App;
 use Friendica\Content\Nav;
+use Friendica\Content\Pager;
 use Friendica\Core\L10n;
 use Friendica\Database\DBA;
 use Friendica\Model\Item;
@@ -60,10 +61,10 @@ function notes_content(App $a, $update = false)
 	$condition = ['uid' => local_user(), 'post-type' => Item::PT_PERSONAL_NOTE, 'gravity' => GRAVITY_PARENT,
 		'wall' => false, 'contact-id'=> $a->contact['id']];
 
-	$a->setPagerItemsPage(40);
+	$pager = new Pager($a->query_string, null, 40);
 
 	$params = ['order' => ['created' => true],
-		'limit' => [$a->pager['start'], $a->pager['itemspage']]];
+		'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
 	$r = Item::selectThreadForUser(local_user(), ['uri'], $condition, $params);
 
 	$count = 0;
@@ -73,10 +74,10 @@ function notes_content(App $a, $update = false)
 
 		$count = count($notes);
 
-		$o .= conversation($a, $notes, 'notes', $update);
+		$o .= conversation($a, $notes, $pager, 'notes', $update);
 	}
 
-	$o .= alt_pager($a, $count);
+	$o .= $pager->renderMinimal($count);
 
 	return $o;
 }
diff --git a/mod/notifications.php b/mod/notifications.php
index d5cfbf276d..748af40d5d 100644
--- a/mod/notifications.php
+++ b/mod/notifications.php
@@ -7,6 +7,7 @@
 use Friendica\App;
 use Friendica\Content\ContactSelector;
 use Friendica\Content\Nav;
+use Friendica\Content\Pager;
 use Friendica\Core\L10n;
 use Friendica\Core\NotificationsManager;
 use Friendica\Core\Protocol;
@@ -120,11 +121,11 @@ function notifications_content(App $a)
 	}
 
 	// Set the pager
-	$a->setPagerItemsPage($perpage);
+	$pager = new Pager($a->query_string, null, $perpage);
 
 	// Add additional informations (needed for json output)
-	$notifs['items_page'] = $a->pager['itemspage'];
-	$notifs['page'] = $a->pager['page'];
+	$notifs['items_page'] = $pager->getItemsPerPage();
+	$notifs['page'] = $pager->getPage();
 
 	// Json output
 	if (intval($json) === 1) {
@@ -315,7 +316,7 @@ function notifications_content(App $a)
 		'$notif_content'   => $notif_content,
 		'$notif_nocontent' => $notif_nocontent,
 		'$notif_show_lnk'  => $notif_show_lnk,
-		'$notif_paginate'  => alt_pager($a, count($notif_content))
+		'$notif_paginate'  => $pager->renderMinimal(count($notif_content))
 	]);
 
 	return $o;
diff --git a/mod/photos.php b/mod/photos.php
index ef6428d7ab..b8f00c358a 100644
--- a/mod/photos.php
+++ b/mod/photos.php
@@ -6,6 +6,7 @@
 use Friendica\App;
 use Friendica\Content\Feature;
 use Friendica\Content\Nav;
+use Friendica\Content\Pager;
 use Friendica\Content\Text\BBCode;
 use Friendica\Core\ACL;
 use Friendica\Core\Addon;
@@ -25,8 +26,8 @@ use Friendica\Object\Image;
 use Friendica\Protocol\DFRN;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Map;
-use Friendica\Util\Temporal;
 use Friendica\Util\Security;
+use Friendica\Util\Temporal;
 
 require_once 'include/items.php';
 
@@ -1135,16 +1136,18 @@ function photos_content(App $a)
 	if ($datatype === 'album') {
 		$album = hex2bin($datum);
 
+		$total = 0;
 		$r = q("SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = %d AND `album` = '%s'
 			AND `scale` <= 4 $sql_extra GROUP BY `resource-id`",
 			intval($owner_uid),
 			DBA::escape($album)
 		);
 		if (DBA::isResult($r)) {
-			$a->setPagerTotal(count($r));
-			$a->setPagerItemsPage(20);
+			$total = count($r);
 		}
 
+		$pager = new Pager($a->query_string, $total, 20);
+
 		/// @TODO I have seen this many times, maybe generalize it script-wide and encapsulate it?
 		$order_field = defaults($_GET, 'order', '');
 		if ($order_field === 'posted') {
@@ -1160,8 +1163,8 @@ function photos_content(App $a)
 			AND `scale` <= 4 $sql_extra GROUP BY `resource-id` ORDER BY `created` $order LIMIT %d , %d",
 			intval($owner_uid),
 			DBA::escape($album),
-			intval($a->pager['start']),
-			intval($a->pager['itemspage'])
+			$pager->getStart(),
+			$pager->getItemsPerPage()
 		);
 
 		// edit album name
@@ -1230,7 +1233,7 @@ function photos_content(App $a)
 				'$upload' => [L10n::t('Upload New Photos'), 'photos/' . $a->data['user']['nickname'] . '/upload/' . bin2hex($album)],
 				'$order' => $order,
 				'$edit' => $edit,
-				'$paginate' => paginate($a),
+				'$paginate' => $pager->renderFull(),
 			]);
 
 		return $o;
@@ -1391,9 +1394,9 @@ function photos_content(App $a)
 			$link_item = Item::selectFirst([], ['id' => $linked_items[0]['id']]);
 
 			$condition = ["`parent` = ? AND `parent` != `id`",  $link_item['parent']];
-			$a->setPagerTotal(DBA::count('item', $condition));
+			$pager = new Pager($a->query_string, DBA::count('item', $condition));
 
-			$params = ['order' => ['id'], 'limit' => [$a->pager['start'], $a->pager['itemspage']]];
+			$params = ['order' => ['id'], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
 			$result = Item::selectForUser($link_item['uid'], Item::ITEM_FIELDLIST, $condition, $params);
 			$items = Item::inArray($result);
 
@@ -1608,7 +1611,7 @@ function photos_content(App $a)
 			}
 			$responses = get_responses($conv_responses, $response_verbs, '', $link_item);
 
-			$paginate = paginate($a);
+			$paginate = $pager->renderFull();
 		}
 
 		$photo_tpl = get_markup_template('photo_view.tpl');
@@ -1653,8 +1656,7 @@ function photos_content(App $a)
 	);
 
 	if (DBA::isResult($r)) {
-		$a->setPagerTotal(count($r));
-		$a->setPagerItemsPage(20);
+		$pager = new Pager($a->query_string, count($r), 20);
 	}
 
 	$r = q("SELECT `resource-id`, ANY_VALUE(`id`) AS `id`, ANY_VALUE(`filename`) AS `filename`,
@@ -1665,8 +1667,8 @@ function photos_content(App $a)
 		intval($a->data['user']['uid']),
 		DBA::escape('Contact Photos'),
 		DBA::escape(L10n::t('Contact Photos')),
-		intval($a->pager['start']),
-		intval($a->pager['itemspage'])
+		$pager->getStart(),
+		$pager->getItemsPerPage()
 	);
 
 	$photos = [];
@@ -1709,7 +1711,7 @@ function photos_content(App $a)
 		'$can_post' => $can_post,
 		'$upload' => [L10n::t('Upload New Photos'), 'photos/'.$a->data['user']['nickname'].'/upload'],
 		'$photos' => $photos,
-		'$paginate' => paginate($a),
+		'$paginate' => $pager->renderFull(),
 	]);
 
 	return $o;
diff --git a/mod/profile.php b/mod/profile.php
index 6f0ab9e077..2cea4eedf4 100644
--- a/mod/profile.php
+++ b/mod/profile.php
@@ -5,6 +5,7 @@
 
 use Friendica\App;
 use Friendica\Content\Nav;
+use Friendica\Content\Pager;
 use Friendica\Content\Widget;
 use Friendica\Core\ACL;
 use Friendica\Core\Addon;
@@ -18,10 +19,10 @@ use Friendica\Model\Group;
 use Friendica\Model\Item;
 use Friendica\Model\Profile;
 use Friendica\Module\Login;
+use Friendica\Protocol\ActivityPub;
 use Friendica\Protocol\DFRN;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Security;
-use Friendica\Protocol\ActivityPub;
 
 function profile_init(App $a)
 {
@@ -307,9 +308,9 @@ function profile_content(App $a, $update = 0)
 			$itemspage_network = $a->force_max_items;
 		}
 
-		$a->setPagerItemsPage($itemspage_network);
+		$pager = new Pager($a->query_string, null, $itemspage_network);
 
-		$pager_sql = sprintf(" LIMIT %d, %d ", intval($a->pager['start']), intval($a->pager['itemspage']));
+		$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
 
 		$items = q("SELECT `item`.`uri`
 			FROM `thread`
@@ -344,10 +345,10 @@ function profile_content(App $a, $update = 0)
 		}
 	}
 
-	$o .= conversation($a, $items, 'profile', $update, false, 'created', local_user());
+	$o .= conversation($a, $items, $pager, 'profile', $update, false, 'created', local_user());
 
 	if (!$update) {
-		$o .= alt_pager($a, count($items));
+		$o .= $pager->renderMinimal(count($items));
 	}
 
 	return $o;
diff --git a/mod/search.php b/mod/search.php
index 80b1c184ff..e5ac7588e3 100644
--- a/mod/search.php
+++ b/mod/search.php
@@ -6,6 +6,7 @@
 use Friendica\App;
 use Friendica\Content\Feature;
 use Friendica\Content\Nav;
+use Friendica\Content\Pager;
 use Friendica\Core\Cache;
 use Friendica\Core\Config;
 use Friendica\Core\L10n;
@@ -200,6 +201,8 @@ function search_content(App $a) {
 	// OR your own posts if you are a logged in member
 	// No items will be shown if the member has a blocked profile wall.
 
+	$pager = new Pager($a->query_string);
+
 	if ($tag) {
 		logger("Start tag search for '".$search."'", LOGGER_DEBUG);
 
@@ -207,7 +210,7 @@ function search_content(App $a) {
 			AND `otype` = ? AND `type` = ? AND `term` = ?",
 			local_user(), TERM_OBJ_POST, TERM_HASHTAG, $search];
 		$params = ['order' => ['created' => true],
-			'limit' => [$a->pager['start'], $a->pager['itemspage']]];
+			'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
 		$terms = DBA::select('term', ['oid'], $condition, $params);
 
 		$itemids = [];
@@ -230,7 +233,7 @@ function search_content(App $a) {
 			AND `body` LIKE CONCAT('%',?,'%')",
 			local_user(), $search];
 		$params = ['order' => ['id' => true],
-			'limit' => [$a->pager['start'], $a->pager['itemspage']]];
+			'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
 		$items = Item::selectForUser(local_user(), [], $condition, $params);
 		$r = Item::inArray($items);
 	}
@@ -252,9 +255,9 @@ function search_content(App $a) {
 	]);
 
 	logger("Start Conversation for '".$search."'", LOGGER_DEBUG);
-	$o .= conversation($a, $r, 'search', false, false, 'commented', local_user());
+	$o .= conversation($a, $r, $pager, 'search', false, false, 'commented', local_user());
 
-	$o .= alt_pager($a,count($r));
+	$o .= $pager->renderMinimal(count($r));
 
 	logger("Done '".$search."'", LOGGER_DEBUG);
 
diff --git a/mod/videos.php b/mod/videos.php
index 521201394b..ee50d26d77 100644
--- a/mod/videos.php
+++ b/mod/videos.php
@@ -5,6 +5,7 @@
 
 use Friendica\App;
 use Friendica\Content\Nav;
+use Friendica\Content\Pager;
 use Friendica\Core\Config;
 use Friendica\Core\L10n;
 use Friendica\Core\System;
@@ -334,15 +335,15 @@ function videos_content(App $a)
 	// Default - show recent videos (no upload link for now)
 	//$o = '';
 
+	$total = 0;
 	$r = q("SELECT hash FROM `attach` WHERE `uid` = %d AND filetype LIKE '%%video%%'
 		$sql_extra GROUP BY hash",
 		intval($a->data['user']['uid'])
 	);
-
 	if (DBA::isResult($r)) {
-		$a->setPagerTotal(count($r));
-		$a->setPagerItemsPage(20);
+		$total = count($r);
 	}
+	$pager = new Pager($a->query_string, $total, 20);
 
 	$r = q("SELECT hash, ANY_VALUE(`id`) AS `id`, ANY_VALUE(`created`) AS `created`,
 		ANY_VALUE(`filename`) AS `filename`, ANY_VALUE(`filetype`) as `filetype`
@@ -350,8 +351,8 @@ function videos_content(App $a)
 		WHERE `uid` = %d AND filetype LIKE '%%video%%'
 		$sql_extra GROUP BY hash ORDER BY `created` DESC LIMIT %d , %d",
 		intval($a->data['user']['uid']),
-		intval($a->pager['start']),
-		intval($a->pager['itemspage'])
+		$pager->getStart(),
+		$pager->getItemsPerPage()
 	);
 
 	$videos = [];
@@ -388,7 +389,7 @@ function videos_content(App $a)
 		'$delete_url' => (($can_post) ? System::baseUrl() . '/videos/' . $a->data['user']['nickname'] : false)
 	]);
 
-	$o .= paginate($a);
+	$o .= $pager->renderFull();
 
 	return $o;
 }
diff --git a/mod/viewcontacts.php b/mod/viewcontacts.php
index 563c13c6d4..ea5bcc6527 100644
--- a/mod/viewcontacts.php
+++ b/mod/viewcontacts.php
@@ -2,17 +2,19 @@
 /**
  * @file mod/viewcontacts.php
  */
+
 use Friendica\App;
 use Friendica\Content\ContactSelector;
 use Friendica\Content\Nav;
+use Friendica\Content\Pager;
 use Friendica\Core\Config;
 use Friendica\Core\L10n;
 use Friendica\Core\Protocol;
+use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Model\Contact;
 use Friendica\Model\Profile;
 use Friendica\Util\Proxy as ProxyUtils;
-use Friendica\Core\System;
 
 function viewcontacts_init(App $a)
 {
@@ -61,6 +63,7 @@ function viewcontacts_content(App $a)
 		return $o;
 	}
 
+	$total = 0;
 	$r = q("SELECT COUNT(*) AS `total` FROM `contact`
 		WHERE `uid` = %d AND NOT `blocked` AND NOT `pending`
 			AND NOT `hidden` AND NOT `archive`
@@ -71,8 +74,9 @@ function viewcontacts_content(App $a)
 		DBA::escape(Protocol::OSTATUS)
 	);
 	if (DBA::isResult($r)) {
-		$a->setPagerTotal($r[0]['total']);
+		$total = $r[0]['total'];
 	}
+	$pager = new Pager($a->query_string, $total);
 
 	$r = q("SELECT * FROM `contact`
 		WHERE `uid` = %d AND NOT `blocked` AND NOT `pending`
@@ -83,8 +87,8 @@ function viewcontacts_content(App $a)
 		DBA::escape(Protocol::DFRN),
 		DBA::escape(Protocol::DIASPORA),
 		DBA::escape(Protocol::OSTATUS),
-		intval($a->pager['start']),
-		intval($a->pager['itemspage'])
+		$pager->getStart(),
+		$pager->getItemsPerPage()
 	);
 	if (!DBA::isResult($r)) {
 		info(L10n::t('No contacts.').EOL);
@@ -124,7 +128,7 @@ function viewcontacts_content(App $a)
 	$o .= replace_macros($tpl, [
 		'$title' => L10n::t('Contacts'),
 		'$contacts' => $contacts,
-		'$paginate' => paginate($a),
+		'$paginate' => $pager->renderFull(),
 	]);
 
 	return $o;
diff --git a/src/App.php b/src/App.php
index 9fb63ce679..5fadab1f23 100644
--- a/src/App.php
+++ b/src/App.php
@@ -34,8 +34,6 @@ class App
 	public $query_string = '';
 	public $config = [];
 	public $page = [];
-	public $pager = [];
-	public $page_offset;
 	public $profile;
 	public $profile_uid;
 	public $user;
@@ -303,16 +301,6 @@ class App
 			$this->module = 'home';
 		}
 
-		// See if there is any page number information, and initialise pagination
-		$this->pager['page'] = !empty($_GET['page']) && intval($_GET['page']) > 0 ? intval($_GET['page']) : 1;
-		$this->pager['itemspage'] = 50;
-		$this->pager['start'] = ($this->pager['page'] * $this->pager['itemspage']) - $this->pager['itemspage'];
-
-		if ($this->pager['start'] < 0) {
-			$this->pager['start'] = 0;
-		}
-		$this->pager['total'] = 0;
-
 		// Detect mobile devices
 		$mobile_detect = new MobileDetect();
 		$this->is_mobile = $mobile_detect->isMobile();
@@ -732,23 +720,6 @@ class App
 		return $this->urlPath;
 	}
 
-	public function setPagerTotal($n)
-	{
-		$this->pager['total'] = intval($n);
-	}
-
-	public function setPagerItemsPage($n)
-	{
-		$this->pager['itemspage'] = ((intval($n) > 0) ? intval($n) : 0);
-		$this->pager['start'] = ($this->pager['page'] * $this->pager['itemspage']) - $this->pager['itemspage'];
-	}
-
-	public function setPagerPage($n)
-	{
-		$this->pager['page'] = $n;
-		$this->pager['start'] = ($this->pager['page'] * $this->pager['itemspage']) - $this->pager['itemspage'];
-	}
-
 	/**
 	 * Initializes App->page['htmlhead'].
 	 *
@@ -798,9 +769,6 @@ class App
 			$touch_icon = 'images/friendica-128.png';
 		}
 
-		// get data wich is needed for infinite scroll on the network page
-		$infinite_scroll = infinite_scroll_data($this->module);
-
 		Core\Addon::callHooks('head', $this->page['htmlhead']);
 
 		$tpl = get_markup_template('head.tpl');
@@ -818,7 +786,6 @@ class App
 			'$update_interval' => $interval,
 			'$shortcut_icon'   => $shortcut_icon,
 			'$touch_icon'      => $touch_icon,
-			'$infinite_scroll' => $infinite_scroll,
 			'$block_public'    => intval(Core\Config::get('system', 'block_public')),
 			'$stylesheets'     => $this->stylesheets,
 		]) . $this->page['htmlhead'];
diff --git a/src/Core/ACL.php b/src/Core/ACL.php
index 8f630409ff..bbe24545c7 100644
--- a/src/Core/ACL.php
+++ b/src/Core/ACL.php
@@ -341,8 +341,7 @@ class ACL extends BaseObject
 		if (Config::get('system', 'poco_local_search')) {
 			$return = GContact::searchByName($search, $mode);
 		} else {
-			$a = self::getApp();
-			$p = $a->pager['page'] != 1 ? '&p=' . $a->pager['page'] : '';
+			$p = defaults($_GET, 'page', 1) != 1 ? '&p=' . defaults($_GET, 'page', 1) : '';
 
 			$curlResult = Network::curl(get_server() . '/lsearch?f=' . $p . '&search=' . urlencode($search));
 			if ($curlResult->isSuccess()) {
diff --git a/src/Model/Contact.php b/src/Model/Contact.php
index fdb49ac133..f653f9ccd5 100644
--- a/src/Model/Contact.php
+++ b/src/Model/Contact.php
@@ -5,6 +5,7 @@
 namespace Friendica\Model;
 
 use Friendica\BaseObject;
+use Friendica\Content\Pager;
 use Friendica\Core\Addon;
 use Friendica\Core\Config;
 use Friendica\Core\L10n;
@@ -1345,25 +1346,27 @@ class Contact extends BaseObject
 				$cid, GRAVITY_PARENT, GRAVITY_COMMENT, local_user()];
 		}
 
+		$pager = new Pager($a->query_string);
+
 		$params = ['order' => ['created' => true],
-			'limit' => [$a->pager['start'], $a->pager['itemspage']]];
+			'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
 
 		if ($thread_mode) {
 			$r = Item::selectThreadForUser(local_user(), ['uri'], $condition, $params);
 
 			$items = Item::inArray($r);
 
-			$o = conversation($a, $items, 'contacts', $update);
+			$o = conversation($a, $items, $pager, 'contacts', $update);
 		} else {
 			$r = Item::selectForUser(local_user(), [], $condition, $params);
 
 			$items = Item::inArray($r);
 
-			$o = conversation($a, $items, 'contact-posts', false);
+			$o = conversation($a, $items, $pager, 'contact-posts', false);
 		}
 
 		if (!$update) {
-			$o .= alt_pager($a, count($items));
+			$o .= $pager->renderMinimal(count($items));
 		}
 
 		return $o;
diff --git a/src/Module/Contact.php b/src/Module/Contact.php
index fb0bf0edee..b6ead69f0f 100644
--- a/src/Module/Contact.php
+++ b/src/Module/Contact.php
@@ -6,6 +6,7 @@ use Friendica\App;
 use Friendica\BaseModule;
 use Friendica\Content\ContactSelector;
 use Friendica\Content\Nav;
+use Friendica\Content\Pager;
 use Friendica\Content\Text\BBCode;
 use Friendica\Content\Widget;
 use Friendica\Core\ACL;
@@ -778,9 +779,9 @@ class Contact extends BaseModule
 			intval($_SESSION['uid'])
 		);
 		if (DBA::isResult($r)) {
-			$a->setPagerTotal($r[0]['total']);
 			$total = $r[0]['total'];
 		}
+		$pager = new Pager($a->query_string, $total);
 
 		$sql_extra3 = Widget::unavailableNetworks();
 
@@ -788,8 +789,8 @@ class Contact extends BaseModule
 
 		$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `pending` = 0 $sql_extra $sql_extra2 $sql_extra3 ORDER BY `name` ASC LIMIT %d , %d ",
 			intval($_SESSION['uid']),
-			intval($a->pager['start']),
-			intval($a->pager['itemspage'])
+			$pager->getStart(),
+			$pager->getItemsPerPage()
 		);
 		if (DBA::isResult($r)) {
 			foreach ($r as $rr) {
@@ -821,7 +822,7 @@ class Contact extends BaseModule
 				'contacts_batch_drop'    => L10n::t('Delete'),
 			],
 			'$h_batch_actions' => L10n::t('Batch Actions'),
-			'$paginate'   => paginate($a),
+			'$paginate'   => $pager->renderFull(),
 		]);
 
 		return $o;
diff --git a/view/js/main.js b/view/js/main.js
index b2c469a6d9..384b355230 100644
--- a/view/js/main.js
+++ b/view/js/main.js
@@ -733,7 +733,7 @@ function loadScrollContent() {
 
 	// get the raw content from the next page and insert this content
 	// right before "#conversation-end"
-	$.get('network?mode=raw' + infinite_scroll.reload_uri + '&last_received=' + received + '&last_commented=' + commented + '&last_created=' + created + '&last_id=' + id + '&page=' + infinite_scroll.pageno, function(data) {
+	$.get(infinite_scroll.reload_uri + '&mode=raw&last_received=' + received + '&last_commented=' + commented + '&last_created=' + created + '&last_id=' + id + '&page=' + infinite_scroll.pageno, function(data) {
 		$("#scroll-loader").hide();
 		if ($(data).length > 0) {
 			$(data).insertBefore('#conversation-end');
diff --git a/view/templates/head.tpl b/view/templates/head.tpl
index 6c55f99771..e76b97b8b9 100644
--- a/view/templates/head.tpl
+++ b/view/templates/head.tpl
@@ -48,15 +48,6 @@
 	var updateInterval = {{$update_interval}};
 	var localUser = {{if $local_user}}{{$local_user}}{{else}}false{{/if}};
 
-	{{* Create an object with the data which is needed for infinite scroll.
-	For the relevant js part look at function loadContent() in main.js. *}}
-	{{if $infinite_scroll}}
-	var infinite_scroll = {
-		"pageno"	: {{$infinite_scroll.pageno}},
-		"reload_uri"	: "{{$infinite_scroll.reload_uri}}"
-	}
-	{{/if}}
-
 	function confirmDelete() { return confirm("{{$delitem}}"); }
 	function commentExpand(id) {
 		$("#comment-edit-text-" + id).value = "";
diff --git a/view/templates/infinite_scroll_head.tpl b/view/templates/infinite_scroll_head.tpl
new file mode 100644
index 0000000000..b46fb1844c
--- /dev/null
+++ b/view/templates/infinite_scroll_head.tpl
@@ -0,0 +1,8 @@
+<script>
+{{* Create an object with the data which is needed for infinite scroll.
+	For the relevant js part look at function loadContent() in main.js. *}}
+	var infinite_scroll = {
+		"pageno"    : {{$pageno}},
+		"reload_uri": "{{$reload_uri}}"
+	}
+</script>
diff --git a/view/theme/frio/templates/js_strings.tpl b/view/theme/frio/templates/js_strings.tpl
index c4173197aa..b649159b9b 100644
--- a/view/theme/frio/templates/js_strings.tpl
+++ b/view/theme/frio/templates/js_strings.tpl
@@ -1,5 +1,4 @@
 
-
 {{* Strings which are needed for some js functions (e.g. translation or the interval for page update)
 They are loaded into the html <head> so that js functions can use them *}}
 <script type="text/javascript">
@@ -9,13 +8,4 @@ They are loaded into the html <head> so that js functions can use them *}}
 	var aStr = {
 		'delitem'     : "{{$delitem}}",
 	};
-
-	{{* Create an object with the data which is needed for infinite scroll.
-	For the relevant js part look at function loadContent() in main.js. *}}
-	{{if $infinite_scroll}}
-	var infinite_scroll = {
-				'pageno'	: {{$infinite_scroll.pageno}},
-				'reload_uri'	: "{{$infinite_scroll.reload_uri}}"
-				}
-	{{/if}}
 </script>