// ### filter.js >> angular .module('angular-duration-format.filter', [ ]) .filter('duration', function() { var DURATION_FORMATS_SPLIT = /((?:[^ydhms']+)|(?:'(?:[^']|'')*')|(?:y+|d+|h+|m+|s+))(.*)/; var DURATION_FORMATS = { y: { // years // "longer" years are not supported value: 365 * 24 * 60 * 60 * 1000, }, yy: { value: 'y', pad: 2, }, d: { // days value: 24 * 60 * 60 * 1000, }, dd: { value: 'd', pad: 2, }, h: { // hours value: 60 * 60 * 1000, }, hh: { // padded hours value: 'h', pad: 2, }, m: { // minutes value: 60 * 1000, }, mm: { // padded minutes value: 'm', pad: 2, }, s: { // seconds value: 1000, }, ss: { // padded seconds value: 's', pad: 2, }, sss: { // milliseconds value: 1, }, ssss: { // padded milliseconds value: 'sss', pad: 4, }, }; function _parseFormat(string) { // @inspiration AngularJS date filter var parts = []; var format = string ? string.toString() : ''; while (format) { var match = DURATION_FORMATS_SPLIT.exec(format); if (match) { parts = parts.concat(match.slice(1)); format = parts.pop(); } else { parts.push(format); format = null; } } return parts; } function _formatDuration(timestamp, format) { var text = ''; var values = { }; format.filter(function(format) { // filter only value parts of format return DURATION_FORMATS.hasOwnProperty(format); }).map(function(format) { // get formats with values only var config = DURATION_FORMATS[format]; if (config.hasOwnProperty('pad')) { return config.value; } else { return format; } }).filter(function(format, index, arr) { // remove duplicates return (arr.indexOf(format) === index); }).map(function(format) { // get format configurations with values return angular.extend({ name: format, }, DURATION_FORMATS[format]); }).sort(function(a, b) { // sort formats descending by value return b.value - a.value; }).forEach(function(format) { // create values for format parts var value = values[format.name] = Math.floor(timestamp / format.value); timestamp = timestamp - (value * format.value); }); format.forEach(function(part) { var format = DURATION_FORMATS[part]; if (format) { var value = values[format.value]; text += (format.hasOwnProperty('pad') ? _padNumber(value, Math.max(format.pad, value.toString().length)) : values[part]); } else { text += part.replace(/(^'|'$)/g, '').replace(/''/g, '\''); } }); return text; } function _padNumber(number, len) { return ((new Array(len + 1)).join('0') + number).slice(-len); } return function(value, format) { var parsedValue = parseFloat(value, 10); var parsedFormat = _parseFormat(format); if (isNaN(parsedValue) || (parsedFormat.length === 0)) { return value; } else { return _formatDuration(parsedValue, parsedFormat); } }; }); // ### << filter.js // ### main.js >> angular .module('angular-duration-format', [ 'angular-duration-format.filter', ]); // ### << main.js