Skip to content

Instantly share code, notes, and snippets.

@niaher
Created May 6, 2016 05:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save niaher/f01c5829862567fe66d640261dc97e6b to your computer and use it in GitHub Desktop.
Save niaher/f01c5829862567fe66d640261dc97e6b to your computer and use it in GitHub Desktop.
"pagination" factory for angular
angular.module("paginator", [])
.service("base64", function() {
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function utf8Encode(string) {
string = string.replace(/\r\n/g, "\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
function utf8Decode(utftext) {
var string = "", i = 0, c, c2, c3;
while (i < utftext.length) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if ((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i + 1);
c3 = utftext.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
this.encode = function(input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = utf8Encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
}
return output;
};
this.decode = function(input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 !== 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 !== 64) {
output = output + String.fromCharCode(chr3);
}
}
return utf8Decode(output);
};
})
.factory("paginator", [
"$location", "base64", "$rootScope", function($location, base64, $rootScope) {
function getRouteParameterStore(useLocation) {
if (useLocation || angular.isUndefined(useLocation)) {
return {
get: function(key) {
return $location.search()[key];
},
set: function(value) {
var current = $location.search();
var newValue = angular.extend(current, value);
return $location.search(newValue);
}
};
} else {
return {
value: {},
get: function(key) {
return this.value[key];
},
set: function(value) {
return this.value = value;
}
};
}
}
function parseParams(p) {
var d = base64.decode(p).replace(/\-/gi, "=").replace(/[\x00-\x1F\x7F-\x9F]/g, "") || "{}";
return JSON.parse(d);
}
function removeEmtpyProperties(obj) {
var result = obj;
for (var propertyName in result) {
if (result.hasOwnProperty(propertyName)) {
var value = result[propertyName];
if (angular.isUndefined(value) || value == null || value.length === 0) {
delete result[propertyName];
}
}
}
return result;
}
function stringifyParams(params) {
var compressedParams = removeEmtpyProperties(params);
if (Object.keys(compressedParams).length === 0) {
return "";
}
var s = JSON.stringify(compressedParams);
return base64.encode(s).replace(/=/gi, "-");
}
function initFromRoute(paginator, routeParameters) {
paginator.currentPage = parseInt(routeParameters.get("p"), 10) || 1;
paginator.pageSize = parseInt(routeParameters.get("s"), 10) || options.pageSize || 10;
paginator.query = parseParams(routeParameters.get("q") || "");
// Set the total items to make sure it fits the currentPage. This is needed to make
// sure that the pager renders correctly and doesn't reset currentPage to 1.
paginator.totalItems = paginator.currentPage * paginator.pageSize;
}
function Paginator(options) {
var self = this;
this.items = [];
this.currentPage = 1;
var routeParameters = getRouteParameterStore(options.useLocation);
initFromRoute(this, routeParameters);
var reading = false;
this.render = function(items, inlineCount) {
self.items = items;
self.totalItems = inlineCount;
reading = false;
};
this.read = function(page) {
if (!reading) {
reading = true;
if (angular.isUndefined(page) || page == null) {
page = self.currentPage;
}
self.currentPage = page;
var pageSize = parseInt(self.pageSize, 10);
routeParameters.set({ p: page, s: self.pageSize, q: stringifyParams(self.query) });
options.read(self.query, page, pageSize).then(function(data) {
self.render(data.results, data.inlineCount, page);
});
}
};
$rootScope.$on("$locationChangeSuccess", function () {
initFromRoute(self, routeParameters);
self.read();
});
return this;
}
Paginator.prototype.bindToScope = function($scope, property) {
var paginator = this;
$scope[property] = paginator;
var baseRender = this.render;
this.render = function(items, inlineCount, page) {
$scope.$evalAsync(function() {
baseRender(items, inlineCount, page);
});
};
$scope.$watch(property + ".currentPage", function(newValue, oldValue) {
if (newValue !== oldValue) {
paginator.read(newValue);
}
});
$scope.$watch(property + ".pageSize", function(newValue, oldValue) {
if (newValue !== oldValue) {
paginator.read(1);
}
});
return this;
};
return function(options) {
return new Paginator(options);
};
}
]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment