├── .bowerrc ├── .gitignore ├── bower.json ├── CHANGELOG.md ├── package.json ├── LICENSE ├── README.md ├── coffeelint.json ├── API.md ├── release ├── cachomatic.standalone.min.js ├── cachomatic.standalone.js ├── cachomatic.min.js └── cachomatic.js ├── example.html ├── gulpfile.js └── src └── cachomatic.coffee /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "vendor" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | node_modules 3 | 4 | # Bower vendor directory 5 | vendor/ 6 | 7 | # Distribution directory 8 | dist/ 9 | 10 | # Other Ignores 11 | .DS_Store 12 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cachomatic", 3 | "version": "1.1.0", 4 | "homepage": "https://github.com/ryanpager/cachomatic.git", 5 | "authors": [ 6 | "Ryan Page " 7 | ], 8 | "keywords": [ 9 | "localStorage", 10 | "cache", 11 | "angular" 12 | ], 13 | "main": [ 14 | "cachomatic.js" 15 | ], 16 | "dependencies": { 17 | "moment": "~2.8.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ### 1.1.0 (2015-07-08) 3 | 4 | #### Features 5 | 6 | * Uses the W3C local storage integration correctly; rather than using the array 7 | and dot syntax it will use the setItem, getItem, removeItem and clear methods. 8 | * Fixed various bugs with the handling of data on windows 7 devices 9 | 10 | 11 | ### 1.0.0 (2015-06-08) 12 | 13 | #### Features 14 | 15 | * Complete integration with local storage on all supported browsers and mobile 16 | devices 17 | * Bower distribution package setup 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cachomatic", 3 | "version": "1.0.0", 4 | "description": "Cachomatic: An AngularJS Local Storage Module", 5 | "homepage": "https://github.com/ryanpager/cachomatic", 6 | "name": "Ryan Page", 7 | "email": "ryanpager@gmail.com", 8 | "repository": { 9 | "type": "git", 10 | "url": "git://github.com:ryanpager/cachomatic.git" 11 | }, 12 | "license": "https://github.com/ryanpager/cachomatic/blob/master/LICENSE", 13 | "bugs": { 14 | "url": "https://github.com/ryanpager/cachomatic/issues" 15 | }, 16 | "dependencies": { 17 | "coffeelint-use-strict": "0.0.1", 18 | "gulp": "^3.5.6", 19 | "gulp-clean": "^0.3.1", 20 | "gulp-coffee": "2.3.1", 21 | "gulp-coffeelint": "0.4.0", 22 | "gulp-concat": "^2.2.0", 23 | "gulp-plumber": "1.0.0", 24 | "gulp-uglify": "1.2.0", 25 | "gulp-rename": "1.2.2", 26 | "gulp-header": "1.2.2" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015 Ryan Page http://sudobangbang.net 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Cachomatic: An AngularJS Local Storage Module 2 | 3 | This module will provide an abstraction and extension layer to the $window's object localStorage object. Since localStorage can be used as a caching layer on mobile devices, it also acts as a lightweight persistence layer for mobile applications running on a cordova platform. 4 | 5 | ###### Browser Support 6 | 7 | http://caniuse.com/#search=localstorage 8 | 9 | ###### Dependencies 10 | 11 | * AngularJS (http://angularjs.org/) 12 | * MomentJS (http://momentjs.com/) 13 | 14 | ###### Documentation 15 | 16 | * [API](https://github.com/ryanpager/cachomatic/edit/master/API.md) 17 | * [Example Usage](https://github.com/ryanpager/cachomatic/edit/master/example.html) 18 | * [License](https://github.com/ryanpager/cachomatic/edit/master/LICENSE) 19 | * [Issue Tracking](https://github.com/ryanpager/cachomatic/issues) 20 | 21 | ###### Installation 22 | 23 | ``` 24 | bower install cachomatic 25 | ``` 26 | 27 | ###### Building 28 | 29 | ``` 30 | # checkout the repository 31 | git clone git@github.com:ryanpager/cachomatic.git 32 | 33 | # install npm dependencies 34 | npm install 35 | 36 | # install bower dependencies (moment) 37 | bower install 38 | 39 | # build the source 40 | gulp build 41 | ``` 42 | 43 | ###### Coming Soon 44 | 45 | * No more reliance on MomentJS 46 | * Unit-tests 47 | * Encryption 48 | -------------------------------------------------------------------------------- /coffeelint.json: -------------------------------------------------------------------------------- 1 | { 2 | "indentation":{ 3 | "value":2 4 | }, 5 | "use_strict":{ 6 | "requireGlobal":false, 7 | "module":"coffeelint-use-strict" 8 | }, 9 | "prefer_english_operator":{ 10 | "level":"error" 11 | }, 12 | "line_endings":{ 13 | "level":"error", 14 | "value":"unix" 15 | }, 16 | "no_trailing_whitespace":{ 17 | "name":"no_trailing_whitespace", 18 | "level":"error", 19 | "allowed_in_comments":false, 20 | "allowed_in_empty_lines":false 21 | }, 22 | "colon_assignment_spacing":{ 23 | "name":"colon_assignment_spacing", 24 | "level":"error", 25 | "spacing":{ 26 | "left":0, 27 | "right":1 28 | } 29 | }, 30 | "space_operators":{ 31 | "level":"error" 32 | }, 33 | "no_stand_alone_at":{ 34 | "level":"error" 35 | }, 36 | "arrow_spacing":{ 37 | "level":"error" 38 | }, 39 | "cyclomatic_complexity":{ 40 | "value":"20", 41 | "level":"error" 42 | }, 43 | "empty_constructor_needs_parens":{ 44 | "level":"error" 45 | }, 46 | "no_empty_param_list":{ 47 | "level":"error" 48 | }, 49 | "no_unnecessary_fat_arrows":{ 50 | "level":"error" 51 | }, 52 | "no_unnecessary_double_quotes":{ 53 | "name":"no_unnecessary_double_quotes", 54 | "level":"error" 55 | }, 56 | "no_debugger":{ 57 | "name":"no_debugger", 58 | "level":"error" 59 | }, 60 | "no_interpolation_in_single_quotes":{ 61 | "name":"no_interpolation_in_single_quotes", 62 | "level":"error" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | ## Cachomatic: The API 2 | 3 | ### Provider Methods 4 | 5 | ###### setCachePrefix([string] prefix) 6 | 7 | This function will set the cache prefix for all cache keys. If you do not 8 | provide one in your module configuration then this will be ignored. 9 | 10 | ### Service Methods 11 | 12 | ###### getCachePrefix : [string] 13 | 14 | This function will return the previously set cache prefix in use for all 15 | cache keys. 16 | 17 | ###### exists([string] key) : [boolean] 18 | 19 | This function will return a boolean value denoting whether or not there is a 20 | value which exists at the supplied key value. 21 | 22 | ###### clear([string] key) 23 | 24 | This function will clear out any value which is located at the supplied key 25 | index. If the key does not exist in the cache then it will act as a no-op. 26 | 27 | ###### clearAll 28 | 29 | This function will clear the __entire__ cache. This is a special clear function 30 | as it will only reset keys that are prefixed with the cache prefix in use. 31 | 32 | ###### get([string] key) : [string] 33 | 34 | This function will retrieve the value that is stored at the supplied key. 35 | 36 | ###### set([string] key, [string|number] value, [number] expiration) 37 | 38 | This function will set a value in the cache at the supplied key. The expiration 39 | parameter is the time __from now__ in which the value will expire __in seconds__. 40 | 41 | ###### getObject([string] key) : [object] 42 | 43 | This function will retrieve the value that is stored at the supplied key. The 44 | different between this and the standard "get" is that it will JSON.parse the 45 | value being retreived. 46 | 47 | ###### setObject([string] key, [object] value, [number] expiration) 48 | 49 | This function will set a value in the cache at the supplied key. The expiration 50 | parameter is the time __from now__ in which the value will expire __in seconds__. 51 | The only difference between this function and the standard "set" is that the 52 | value being stored will be JSON.stringify. 53 | 54 | ###### getExpriation([string] key) : [number] 55 | 56 | This function will return the epoch time (in ms) in which the value stored in 57 | the cache at this key index will expire. 58 | 59 | ###### setExpiration([string] key, [number] expiration) 60 | 61 | This function will set the expiration __from now__ in which the value will 62 | expire __in seconds__. It can act as a "refresh" function for the cache. 63 | 64 | ###### isExpired([string] key) : [boolean] 65 | 66 | This function will return a boolean value denoting whether or not the value 67 | stored in the cache at this key value is expired. 68 | -------------------------------------------------------------------------------- /release/cachomatic.standalone.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cachomatic: An AngularJS Local Storage Module 3 | * @author Ryan Page 4 | * @version v1.0.0 5 | * @see https://github.com/ryanpager/cachomatic 6 | * @license https://github.com/ryanpager/cachomatic/blob/master/LICENSE 7 | */ 8 | var bind=function(t,e){return function(){return t.apply(e,arguments)}};!function(){"use strict";var t;return t=function(){function t(t,e){this.isExpired=bind(this.isExpired,this),this.setExpiration=bind(this.setExpiration,this),this.getExpiration=bind(this.getExpiration,this),this.setObject=bind(this.setObject,this),this.getObject=bind(this.getObject,this),this.set=bind(this.set,this),this.get=bind(this.get,this),this.clearAll=bind(this.clearAll,this),this.clear=bind(this.clear,this),this.exists=bind(this.exists,this),this.getCacheKey=bind(this.getCacheKey,this),this.getCachePrefix=bind(this.getCachePrefix,this),this.$window=t.$get(),this.moment=e}return t.prototype.$inject=["$windowProvider","moment"],t.prototype.cachePrefix=null,t.prototype.setCachePrefix=function(t){return this.cachePrefix=t},t.prototype.getCachePrefix=function(){return this.cachePrefix},t.prototype.getCacheKey=function(t){var e;return e=this.getCachePrefix(),null===e?t:e+"-"+t},t.prototype.getExpirationCacheKey=function(t){return this.getCacheKey(t)+"-expiration"},t.prototype.exists=function(t){return null==this.$window.localStorage.getItem(this.getCacheKey(t))?!1:!0},t.prototype.clear=function(t){var e;return e=this.getExpiration(t),null!=e&&delete this.$window.localStorage.removeItem(this.getExpirationCacheKey(t)),this.exists(t)?delete this.$window.localStorage.removeItem(this.getCacheKey(t)):void 0},t.prototype.clearAll=function(){return this.$window.localStorage.clear()},t.prototype.get=function(t){return this.isExpired(t)?(this.clear(t),null):this.exists(t)?this.$window.localStorage.getItem(this.getCacheKey(t)):null},t.prototype.set=function(t,e,i){return null==i&&(i=null),this.$window.localStorage.setItem(this.getCacheKey(t),e),null!=i?this.setExpiration(t,i):void 0},t.prototype.getObject=function(t){return JSON.parse(this.get(t))},t.prototype.setObject=function(t,e,i){return this.set(t,JSON.stringify(e),i)},t.prototype.getExpiration=function(t){var e,i;return e=this.getExpirationCacheKey(t),i=this.$window.localStorage.getItem(e),i?i:null},t.prototype.setExpiration=function(t,e){var i;return i=this.getExpirationCacheKey(t),this.$window.localStorage.setItem(i,this.moment().add(parseInt(e),"s").valueOf())},t.prototype.isExpired=function(t){var e;return e=this.getExpiration(t),e&&this.moment().valueOf()>parseInt(e)?!0:!1},t.prototype.$get=function(){return{getCachePrefix:this.getCachePrefix,exists:this.exists,clear:this.clear,clearAll:this.clearAll,get:this.get,set:this.set,getObject:this.getObject,setObject:this.setObject,getExpiration:this.getExpiration,setExpiration:this.setExpiration,isExpired:this.isExpired}},t}(),angular.module("sbb.cachomatic",[]).constant("moment",moment).provider("Cachomatic",t)}(); -------------------------------------------------------------------------------- /example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Cachomatic Example 10 | 11 | 12 |

Open Console To View Output

13 | 14 | 15 | 16 | 17 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Script to handle all of the generic build tasks for the cachomatic package 3 | * such as linting, coffeescript conversion, etc. 4 | * 5 | * @author Ryan Page 6 | * @see https://github.com/ryanpager/cachomatic 7 | * @version 1.0.0 8 | */ 9 | 10 | // BUILD Dependencies 11 | // ------------------------------------------------------------------ 12 | 13 | var gulp = require('gulp'); 14 | var plumber = require('gulp-plumber'); 15 | var coffee = require('gulp-coffee'); 16 | var coffeelint = require('gulp-coffeelint'); 17 | var concat = require('gulp-concat'); 18 | var uglify = require('gulp-uglify'); 19 | var clean = require('gulp-clean'); 20 | var rename = require('gulp-rename'); 21 | var header = require('gulp-header'); 22 | var pkg = require('./package.json'); 23 | 24 | // BUILD Banner 25 | // ------------------------------------------------------------------ 26 | var banner = [ 27 | '/**', 28 | ' * <%= pkg.description %>', 29 | ' * @author <%= pkg.name %> <<%= pkg.email %>>', 30 | ' * @version v<%= pkg.version %>', 31 | ' * @see <%= pkg.homepage %>', 32 | ' * @license <%= pkg.license %>', 33 | ' */', 34 | '' 35 | ].join('\n'); 36 | 37 | // Tasks 38 | // ------------------------------------------------------------------ 39 | 40 | gulp.task('clean', function() { 41 | gulp.src(['./release/**/*.js'], {read: false}) 42 | .pipe(clean({force: true})); 43 | }); 44 | 45 | gulp.task('lint', function() { 46 | gulp.src('./src/**/*.coffee') 47 | .pipe(coffeelint()) 48 | .pipe(coffeelint.reporter()); 49 | }); 50 | 51 | gulp.task('build-standalone', function() { 52 | gulp.src(['./src/**/*.coffee']) 53 | .pipe(plumber({ 54 | errorHandler: function (error) { 55 | console.log(error.message); 56 | this.emit('end'); 57 | } 58 | })) 59 | .pipe(coffee({ 60 | bare: true 61 | })) 62 | .pipe(header(banner, { pkg : pkg })) 63 | .pipe(concat('cachomatic.standalone.js')) 64 | .pipe(gulp.dest('./release')); 65 | }); 66 | 67 | gulp.task('build-package', function() { 68 | gulp.src([ 69 | './vendor/moment/moment.js', 70 | './release/cachomatic.standalone.js' 71 | ]) 72 | .pipe(concat('cachomatic.js')) 73 | .pipe(gulp.dest('./release')); 74 | }); 75 | 76 | gulp.task('uglify-standalone', function() { 77 | gulp.src('./release/cachomatic.standalone.js') 78 | .pipe(uglify()) 79 | .pipe(header(banner, { pkg : pkg })) 80 | .pipe(rename("cachomatic.standalone.min.js")) 81 | .pipe(gulp.dest('./release')); 82 | }); 83 | 84 | gulp.task('uglify-package', function() { 85 | gulp.src('./release/cachomatic.js') 86 | .pipe(uglify()) 87 | .pipe(rename("cachomatic.min.js")) 88 | .pipe(gulp.dest('./release')); 89 | }); 90 | 91 | // BUILD Task 92 | // ------------------------------------------------------------------ 93 | 94 | gulp.task('build', [ 95 | 'clean', 96 | 'lint', 97 | 'build-standalone', 98 | 'build-package', 99 | 'uglify-standalone', 100 | 'uglify-package' 101 | ]); 102 | 103 | // WATCH Task 104 | // ------------------------------------------------------------------ 105 | 106 | gulp.task('watch', function() { 107 | // Watch and build the coffee script & sass files 108 | gulp.watch('./src', ['build']); 109 | }); 110 | -------------------------------------------------------------------------------- /release/cachomatic.standalone.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cachomatic: An AngularJS Local Storage Module 3 | * @author Ryan Page 4 | * @version v1.0.0 5 | * @see https://github.com/ryanpager/cachomatic 6 | * @license https://github.com/ryanpager/cachomatic/blob/master/LICENSE 7 | */ 8 | var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; 9 | 10 | (function() { 11 | 'use strict'; 12 | var Cachomatic; 13 | Cachomatic = (function() { 14 | Cachomatic.prototype.$inject = ['$windowProvider', 'moment']; 15 | 16 | function Cachomatic($windowProvider, moment) { 17 | this.isExpired = bind(this.isExpired, this); 18 | this.setExpiration = bind(this.setExpiration, this); 19 | this.getExpiration = bind(this.getExpiration, this); 20 | this.setObject = bind(this.setObject, this); 21 | this.getObject = bind(this.getObject, this); 22 | this.set = bind(this.set, this); 23 | this.get = bind(this.get, this); 24 | this.clearAll = bind(this.clearAll, this); 25 | this.clear = bind(this.clear, this); 26 | this.exists = bind(this.exists, this); 27 | this.getCacheKey = bind(this.getCacheKey, this); 28 | this.getCachePrefix = bind(this.getCachePrefix, this); 29 | this.$window = $windowProvider.$get(); 30 | this.moment = moment; 31 | } 32 | 33 | Cachomatic.prototype.cachePrefix = null; 34 | 35 | Cachomatic.prototype.setCachePrefix = function(prefix) { 36 | return this.cachePrefix = prefix; 37 | }; 38 | 39 | Cachomatic.prototype.getCachePrefix = function() { 40 | return this.cachePrefix; 41 | }; 42 | 43 | Cachomatic.prototype.getCacheKey = function(key) { 44 | var prefix; 45 | prefix = this.getCachePrefix(); 46 | if (prefix === null) { 47 | return key; 48 | } else { 49 | return prefix + "-" + key; 50 | } 51 | }; 52 | 53 | Cachomatic.prototype.getExpirationCacheKey = function(key) { 54 | return (this.getCacheKey(key)) + "-expiration"; 55 | }; 56 | 57 | Cachomatic.prototype.exists = function(key) { 58 | if (this.$window.localStorage.getItem(this.getCacheKey(key)) == null) { 59 | return false; 60 | } 61 | return true; 62 | }; 63 | 64 | Cachomatic.prototype.clear = function(key) { 65 | var expiration; 66 | expiration = this.getExpiration(key); 67 | if (expiration != null) { 68 | delete this.$window.localStorage.removeItem(this.getExpirationCacheKey(key)); 69 | } 70 | if (this.exists(key)) { 71 | return delete this.$window.localStorage.removeItem(this.getCacheKey(key)); 72 | } 73 | }; 74 | 75 | Cachomatic.prototype.clearAll = function() { 76 | return this.$window.localStorage.clear(); 77 | }; 78 | 79 | Cachomatic.prototype.get = function(key) { 80 | if (this.isExpired(key)) { 81 | this.clear(key); 82 | return null; 83 | } 84 | if (!this.exists(key)) { 85 | return null; 86 | } 87 | return this.$window.localStorage.getItem(this.getCacheKey(key)); 88 | }; 89 | 90 | Cachomatic.prototype.set = function(key, value, expiration) { 91 | if (expiration == null) { 92 | expiration = null; 93 | } 94 | this.$window.localStorage.setItem(this.getCacheKey(key), value); 95 | if (expiration != null) { 96 | return this.setExpiration(key, expiration); 97 | } 98 | }; 99 | 100 | Cachomatic.prototype.getObject = function(key) { 101 | return JSON.parse(this.get(key)); 102 | }; 103 | 104 | Cachomatic.prototype.setObject = function(key, value, expiration) { 105 | return this.set(key, JSON.stringify(value), expiration); 106 | }; 107 | 108 | Cachomatic.prototype.getExpiration = function(key) { 109 | var expirationCacheKey, value; 110 | expirationCacheKey = this.getExpirationCacheKey(key); 111 | value = this.$window.localStorage.getItem(expirationCacheKey); 112 | if (value) { 113 | return value; 114 | } else { 115 | return null; 116 | } 117 | }; 118 | 119 | Cachomatic.prototype.setExpiration = function(key, expiration) { 120 | var expirationCacheKey; 121 | expirationCacheKey = this.getExpirationCacheKey(key); 122 | return this.$window.localStorage.setItem(expirationCacheKey, this.moment().add(parseInt(expiration), 's').valueOf()); 123 | }; 124 | 125 | Cachomatic.prototype.isExpired = function(key) { 126 | var expiration; 127 | expiration = this.getExpiration(key); 128 | if (!expiration) { 129 | return false; 130 | } 131 | if (this.moment().valueOf() > parseInt(expiration)) { 132 | return true; 133 | } 134 | return false; 135 | }; 136 | 137 | Cachomatic.prototype.$get = function() { 138 | return { 139 | getCachePrefix: this.getCachePrefix, 140 | exists: this.exists, 141 | clear: this.clear, 142 | clearAll: this.clearAll, 143 | get: this.get, 144 | set: this.set, 145 | getObject: this.getObject, 146 | setObject: this.setObject, 147 | getExpiration: this.getExpiration, 148 | setExpiration: this.setExpiration, 149 | isExpired: this.isExpired 150 | }; 151 | }; 152 | 153 | return Cachomatic; 154 | 155 | })(); 156 | return angular.module('sbb.cachomatic', []).constant('moment', moment).provider('Cachomatic', Cachomatic); 157 | })(); 158 | -------------------------------------------------------------------------------- /src/cachomatic.coffee: -------------------------------------------------------------------------------- 1 | # @author Ryan Page 2 | # @see https://github.com/ryanpager/cachomatic 3 | # @version 1.0.0 4 | 5 | (-> 6 | 'use strict' 7 | 8 | # @name Cachomatic 9 | # @description 10 | # This class will handle the caching interface provider for the module. 11 | class Cachomatic 12 | # @type {object} 13 | # @description 14 | # Dependencies to be injected on instantiation. 15 | $inject: ['$windowProvider', 'moment'] 16 | 17 | # @constructor 18 | # @param {!angular.$windowProvider} $windowProvider 19 | # @param {object} moment 20 | constructor: ($windowProvider, moment) -> 21 | @$window = $windowProvider.$get() 22 | @moment = moment 23 | 24 | # @type {string} 25 | # @description 26 | # This will set the cache prefix for all keys. 27 | cachePrefix: null 28 | 29 | # @name setCachePrefix 30 | # @description 31 | # This function will set the cache prefix for all cache keys. 32 | setCachePrefix: (prefix) -> 33 | @cachePrefix = prefix 34 | 35 | # @name getCachePrefix 36 | # @description 37 | # This function will return the key prefix for all cached values. 38 | getCachePrefix: => 39 | return @cachePrefix 40 | 41 | # @name getCacheKey 42 | # @description 43 | # This will return the built cache key used for storing data in the 44 | # cache. 45 | # 46 | # @param {string} key 47 | # 48 | # @return {string} 49 | getCacheKey: (key) => 50 | prefix = @getCachePrefix() 51 | return if prefix is null then key else """#{prefix}-#{key}""" 52 | 53 | # @name getExpirationCacheKey 54 | # @description 55 | # This will return the built expiration cache key used for storing 56 | # data in the cache. 57 | # 58 | # @param {string} key 59 | # 60 | # @return {string} 61 | getExpirationCacheKey: (key) -> 62 | return """#{@getCacheKey(key)}-expiration""" 63 | 64 | # @name exists 65 | # @description 66 | # This function will return a boolean value describing whether or not 67 | # a value is stored in cache at the specific index. 68 | # 69 | # @param {string} key 70 | # 71 | # @return {boolean} 72 | exists: (key) => 73 | unless @$window.localStorage.getItem(@getCacheKey(key))? 74 | return false 75 | 76 | return true 77 | 78 | # @name clear 79 | # @description 80 | # This function will clear a specific index from the cache. 81 | # 82 | # @param {string} key 83 | clear: (key) => 84 | expiration = @getExpiration(key) 85 | if expiration? 86 | delete @$window.localStorage.removeItem(@getExpirationCacheKey(key)) 87 | 88 | if @exists(key) 89 | delete @$window.localStorage.removeItem(@getCacheKey(key)) 90 | 91 | # @name clearAll 92 | # @description 93 | # This function will clear all of the local storage keys and values 94 | # and reset the state back to default. 95 | clearAll: => 96 | @$window.localStorage.clear() 97 | 98 | # @name get 99 | # @description 100 | # This function will retreive a key from the cache at the specific key 101 | # index. 102 | # 103 | # @param {string} get 104 | # 105 | # @return {string|null} 106 | get: (key) => 107 | # If the data at this key is expired, we clear it from the 108 | # cache and return null indicating its bunk data. 109 | if @isExpired(key) 110 | @clear(key) 111 | return null 112 | 113 | unless @exists(key) 114 | return null 115 | 116 | return @$window.localStorage.getItem(@getCacheKey(key)) 117 | 118 | # @name set 119 | # @description 120 | # This function will set a value in the cache, along with its expiration 121 | # if one is set. 122 | # 123 | # @param {string} key 124 | # @param {string} value 125 | # @param {number} expiration 126 | set: (key, value, expiration = null) => 127 | @$window.localStorage.setItem(@getCacheKey(key), value) 128 | if expiration? then @setExpiration(key, expiration) 129 | 130 | # @name getObject 131 | # @description 132 | # This function will retreive an object from the cache at a specific 133 | # key index. 134 | # 135 | # @param {string} key 136 | # 137 | # @return {string|null} 138 | getObject: (key) => 139 | return JSON.parse(@get(key)) 140 | 141 | # @name set 142 | # @description 143 | # This function will set an object value in the cache, along 144 | # with its expiration if one is set. 145 | # 146 | # @param {string} key 147 | # @param {string} value 148 | # @param {number} expiration 149 | setObject: (key, value, expiration) => 150 | @set(key, JSON.stringify(value), expiration) 151 | 152 | # @name getExpiration 153 | # @description 154 | # This function will return the expiration time (epoch) for the key 155 | # associated in the cache. 156 | # 157 | # @param {string} key 158 | # 159 | # @return {number|null} 160 | getExpiration: (key) => 161 | expirationCacheKey = @getExpirationCacheKey(key) 162 | 163 | value = @$window.localStorage.getItem(expirationCacheKey) 164 | if value then return value else return null 165 | 166 | # @name setExpiration 167 | # @description 168 | # This function will set the expiration for a key index in the cache 169 | # based on the passed expiration modifier (in seconds). 170 | # 171 | # @param {string} key 172 | # @param {number} expiration 173 | setExpiration: (key, expiration) => 174 | expirationCacheKey = @getExpirationCacheKey(key) 175 | 176 | @$window.localStorage.setItem( 177 | expirationCacheKey, 178 | @moment().add(parseInt(expiration), 's').valueOf() 179 | ) 180 | 181 | # @name isExpired 182 | # @description 183 | # This function will return a boolean value of whether or not a value 184 | # in the cache at the specific key index is expired. 185 | # 186 | # @param {string} key 187 | # 188 | # @return {boolean} 189 | isExpired: (key) => 190 | expiration = @getExpiration(key) 191 | unless expiration 192 | return false 193 | 194 | if @moment().valueOf() > parseInt(expiration) 195 | return true 196 | 197 | return false 198 | 199 | # @name $get 200 | # @description 201 | # This is a magic method which will allow the service to be returned while 202 | # injected with the correct configuration variables. 203 | $get: -> 204 | return { 205 | getCachePrefix: @getCachePrefix 206 | exists: @exists 207 | clear: @clear 208 | clearAll: @clearAll 209 | get: @get 210 | set: @set 211 | getObject: @getObject 212 | setObject: @setObject 213 | getExpiration: @getExpiration 214 | setExpiration: @setExpiration 215 | isExpired: @isExpired 216 | } 217 | 218 | # @description 219 | # The module declaration for the Cachomatic module. Will also provide the 220 | # constants needed to inject into the module for maximum module testability. 221 | angular 222 | .module 'sbb.cachomatic', [] 223 | .constant 'moment', moment 224 | .provider 'Cachomatic', Cachomatic 225 | )() 226 | -------------------------------------------------------------------------------- /release/cachomatic.min.js: -------------------------------------------------------------------------------- 1 | (function(t){function e(t,e,n){switch(arguments.length){case 2:return null!=t?t:e;case 3:return null!=t?t:null!=e?e:n;default:throw new Error("Implement me")}}function n(t,e){return kt.call(t,e)}function s(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function i(t){Mt.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function a(t,e){var n=!0;return d(function(){return n&&(i(t),n=!1),e.apply(this,arguments)},e)}function r(t,e){pe[t]||(i(e),pe[t]=!0)}function o(t,e){return function(n){return m(t.call(this,n),e)}}function u(t,e){return function(n){return this.localeData().ordinal(t.call(this,n),e)}}function c(){}function l(t,e){e!==!1&&P(t),f(this,t),this._d=new Date(+t._d)}function h(t){var e=b(t),n=e.year||0,s=e.quarter||0,i=e.month||0,a=e.week||0,r=e.day||0,o=e.hour||0,u=e.minute||0,c=e.second||0,l=e.millisecond||0;this._milliseconds=+l+1e3*c+6e4*u+36e5*o,this._days=+r+7*a,this._months=+i+3*s+12*n,this._data={},this._locale=Mt.localeData(),this._bubble()}function d(t,e){for(var s in e)n(e,s)&&(t[s]=e[s]);return n(e,"toString")&&(t.toString=e.toString),n(e,"valueOf")&&(t.valueOf=e.valueOf),t}function f(t,e){var n,s,i;if("undefined"!=typeof e._isAMomentObject&&(t._isAMomentObject=e._isAMomentObject),"undefined"!=typeof e._i&&(t._i=e._i),"undefined"!=typeof e._f&&(t._f=e._f),"undefined"!=typeof e._l&&(t._l=e._l),"undefined"!=typeof e._strict&&(t._strict=e._strict),"undefined"!=typeof e._tzm&&(t._tzm=e._tzm),"undefined"!=typeof e._isUTC&&(t._isUTC=e._isUTC),"undefined"!=typeof e._offset&&(t._offset=e._offset),"undefined"!=typeof e._pf&&(t._pf=e._pf),"undefined"!=typeof e._locale&&(t._locale=e._locale),Ut.length>0)for(n in Ut)s=Ut[n],i=e[s],"undefined"!=typeof i&&(t[s]=i);return t}function _(t){return 0>t?Math.ceil(t):Math.floor(t)}function m(t,e,n){for(var s=""+Math.abs(t),i=t>=0;s.lengths;s++)(n&&t[s]!==e[s]||!n&&S(t[s])!==S(e[s]))&&r++;return r+a}function v(t){if(t){var e=t.toLowerCase().replace(/(.)s$/,"$1");t=ce[t]||le[e]||e}return t}function b(t){var e,s,i={};for(s in t)n(t,s)&&(e=v(s),e&&(i[e]=t[s]));return i}function k(e){var n,s;if(0===e.indexOf("week"))n=7,s="day";else{if(0!==e.indexOf("month"))return;n=12,s="month"}Mt[e]=function(i,a){var r,o,u=Mt._locale[e],c=[];if("number"==typeof i&&(a=i,i=t),o=function(t){var e=Mt().utc().set(s,t);return u.call(Mt._locale,e,i||"")},null!=a)return o(a);for(r=0;n>r;r++)c.push(o(r));return c}}function S(t){var e=+t,n=0;return 0!==e&&isFinite(e)&&(n=e>=0?Math.floor(e):Math.ceil(e)),n}function T(t,e){return new Date(Date.UTC(t,e+1,0)).getUTCDate()}function O(t,e,n){return ot(Mt([t,11,31+e-n]),e,n).week}function x(t){return C(t)?366:365}function C(t){return t%4===0&&t%100!==0||t%400===0}function P(t){var e;t._a&&-2===t._pf.overflow&&(e=t._a[Tt]<0||t._a[Tt]>11?Tt:t._a[Ot]<1||t._a[Ot]>T(t._a[St],t._a[Tt])?Ot:t._a[xt]<0||t._a[xt]>24||24===t._a[xt]&&(0!==t._a[Ct]||0!==t._a[Pt]||0!==t._a[Wt])?xt:t._a[Ct]<0||t._a[Ct]>59?Ct:t._a[Pt]<0||t._a[Pt]>59?Pt:t._a[Wt]<0||t._a[Wt]>999?Wt:-1,t._pf._overflowDayOfYear&&(St>e||e>Ot)&&(e=Ot),t._pf.overflow=e)}function W(e){return null==e._isValid&&(e._isValid=!isNaN(e._d.getTime())&&e._pf.overflow<0&&!e._pf.empty&&!e._pf.invalidMonth&&!e._pf.nullInput&&!e._pf.invalidFormat&&!e._pf.userInvalidated,e._strict&&(e._isValid=e._isValid&&0===e._pf.charsLeftOver&&0===e._pf.unusedTokens.length&&e._pf.bigHour===t)),e._isValid}function G(t){return t?t.toLowerCase().replace("_","-"):t}function U(t){for(var e,n,s,i,a=0;a0;){if(s=F(i.slice(0,e).join("-")))return s;if(n&&n.length>=e&&Y(i,n,!0)>=e-1)break;e--}a++}return null}function F(t){var e=null;if(!Gt[t]&&Ft)try{e=Mt.locale(),require("./locale/"+t),Mt.locale(e)}catch(n){}return Gt[t]}function I(t,e){var n,s;return e._isUTC?(n=e.clone(),s=(Mt.isMoment(t)||w(t)?+t:+Mt(t))-+n,n._d.setTime(+n._d+s),Mt.updateOffset(n,!1),n):Mt(t).local()}function z(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function E(t){var e,n,s=t.match(Lt);for(e=0,n=s.length;n>e;e++)s[e]=me[s[e]]?me[s[e]]:z(s[e]);return function(i){var a="";for(e=0;n>e;e++)a+=s[e]instanceof Function?s[e].call(i,t):s[e];return a}}function L(t,e){return t.isValid()?(e=H(e,t.localeData()),he[e]||(he[e]=E(e)),he[e](t)):t.localeData().invalidDate()}function H(t,e){function n(t){return e.longDateFormat(t)||t}var s=5;for(Ht.lastIndex=0;s>=0&&Ht.test(t);)t=t.replace(Ht,n),Ht.lastIndex=0,s-=1;return t}function A(t,e){var n,s=e._strict;switch(t){case"Q":return Qt;case"DDDD":return Bt;case"YYYY":case"GGGG":case"gggg":return s?te:$t;case"Y":case"G":case"g":return ne;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return s?ee:Zt;case"S":if(s)return Qt;case"SS":if(s)return Xt;case"SSS":if(s)return Bt;case"DDD":return jt;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Kt;case"a":case"A":return e._locale._meridiemParse;case"x":return Jt;case"X":return Rt;case"Z":case"ZZ":return Vt;case"T":return qt;case"SSSS":return Nt;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return s?Xt:At;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return At;case"Do":return s?e._locale._ordinalParse:e._locale._ordinalParseLenient;default:return n=new RegExp(R(J(t.replace("\\","")),"i"))}}function j(t){t=t||"";var e=t.match(Vt)||[],n=e[e.length-1]||[],s=(n+"").match(oe)||["-",0,0],i=+(60*s[1])+S(s[2]);return"+"===s[0]?-i:i}function $(t,e,n){var s,i=n._a;switch(t){case"Q":null!=e&&(i[Tt]=3*(S(e)-1));break;case"M":case"MM":null!=e&&(i[Tt]=S(e)-1);break;case"MMM":case"MMMM":s=n._locale.monthsParse(e,t,n._strict),null!=s?i[Tt]=s:n._pf.invalidMonth=e;break;case"D":case"DD":null!=e&&(i[Ot]=S(e));break;case"Do":null!=e&&(i[Ot]=S(parseInt(e.match(/\d{1,2}/)[0],10)));break;case"DDD":case"DDDD":null!=e&&(n._dayOfYear=S(e));break;case"YY":i[St]=Mt.parseTwoDigitYear(e);break;case"YYYY":case"YYYYY":case"YYYYYY":i[St]=S(e);break;case"a":case"A":n._isPm=n._locale.isPM(e);break;case"h":case"hh":n._pf.bigHour=!0;case"H":case"HH":i[xt]=S(e);break;case"m":case"mm":i[Ct]=S(e);break;case"s":case"ss":i[Pt]=S(e);break;case"S":case"SS":case"SSS":case"SSSS":i[Wt]=S(1e3*("0."+e));break;case"x":n._d=new Date(S(e));break;case"X":n._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":n._useUTC=!0,n._tzm=j(e);break;case"dd":case"ddd":case"dddd":s=n._locale.weekdaysParse(e),null!=s?(n._w=n._w||{},n._w.d=s):n._pf.invalidWeekday=e;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":t=t.substr(0,1);case"gggg":case"GGGG":case"GGGGG":t=t.substr(0,2),e&&(n._w=n._w||{},n._w[t]=S(e));break;case"gg":case"GG":n._w=n._w||{},n._w[t]=Mt.parseTwoDigitYear(e)}}function Z(t){var n,s,i,a,r,o,u;n=t._w,null!=n.GG||null!=n.W||null!=n.E?(r=1,o=4,s=e(n.GG,t._a[St],ot(Mt(),1,4).year),i=e(n.W,1),a=e(n.E,1)):(r=t._locale._week.dow,o=t._locale._week.doy,s=e(n.gg,t._a[St],ot(Mt(),r,o).year),i=e(n.w,1),null!=n.d?(a=n.d,r>a&&++i):a=null!=n.e?n.e+r:r),u=ut(s,i,a,o,r),t._a[St]=u.year,t._dayOfYear=u.dayOfYear}function N(t){var n,s,i,a,r=[];if(!t._d){for(i=V(t),t._w&&null==t._a[Ot]&&null==t._a[Tt]&&Z(t),t._dayOfYear&&(a=e(t._a[St],i[St]),t._dayOfYear>x(a)&&(t._pf._overflowDayOfYear=!0),s=st(a,0,t._dayOfYear),t._a[Tt]=s.getUTCMonth(),t._a[Ot]=s.getUTCDate()),n=0;3>n&&null==t._a[n];++n)t._a[n]=r[n]=i[n];for(;7>n;n++)t._a[n]=r[n]=null==t._a[n]?2===n?1:0:t._a[n];24===t._a[xt]&&0===t._a[Ct]&&0===t._a[Pt]&&0===t._a[Wt]&&(t._nextDay=!0,t._a[xt]=0),t._d=(t._useUTC?st:nt).apply(null,r),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()+t._tzm),t._nextDay&&(t._a[xt]=24)}}function K(t){var e;t._d||(e=b(t._i),t._a=[e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],N(t))}function V(t){var e=new Date;return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function q(e){if(e._f===Mt.ISO_8601)return void X(e);e._a=[],e._pf.empty=!0;var n,s,i,a,r,o=""+e._i,u=o.length,c=0;for(i=H(e._f,e._locale).match(Lt)||[],n=0;n0&&e._pf.unusedInput.push(r),o=o.slice(o.indexOf(s)+s.length),c+=s.length),me[a]?(s?e._pf.empty=!1:e._pf.unusedTokens.push(a),$(a,s,e)):e._strict&&!s&&e._pf.unusedTokens.push(a);e._pf.charsLeftOver=u-c,o.length>0&&e._pf.unusedInput.push(o),e._pf.bigHour===!0&&e._a[xt]<=12&&(e._pf.bigHour=t),e._isPm&&e._a[xt]<12&&(e._a[xt]+=12),e._isPm===!1&&12===e._a[xt]&&(e._a[xt]=0),N(e),P(e)}function J(t){return t.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,n,s,i){return e||n||s||i})}function R(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function Q(t){var e,n,i,a,r;if(0===t._f.length)return t._pf.invalidFormat=!0,void(t._d=new Date(0/0));for(a=0;ar)&&(i=r,n=e));d(t,n||e)}function X(t){var e,n,s=t._i,i=se.exec(s);if(i){for(t._pf.iso=!0,e=0,n=ae.length;n>e;e++)if(ae[e][1].exec(s)){t._f=ae[e][0]+(i[6]||" ");break}for(e=0,n=re.length;n>e;e++)if(re[e][1].exec(s)){t._f+=re[e][0];break}s.match(Vt)&&(t._f+="Z"),q(t)}else t._isValid=!1}function B(t){X(t),t._isValid===!1&&(delete t._isValid,Mt.createFromInputFallback(t))}function tt(t,e){var n,s=[];for(n=0;nt&&o.setFullYear(t),o}function st(t){var e=new Date(Date.UTC.apply(null,arguments));return 1970>t&&e.setUTCFullYear(t),e}function it(t,e){if("string"==typeof t)if(isNaN(t)){if(t=e.weekdaysParse(t),"number"!=typeof t)return null}else t=parseInt(t,10);return t}function at(t,e,n,s,i){return i.relativeTime(e||1,!!n,t,s)}function rt(t,e,n){var s=Mt.duration(t).abs(),i=bt(s.as("s")),a=bt(s.as("m")),r=bt(s.as("h")),o=bt(s.as("d")),u=bt(s.as("M")),c=bt(s.as("y")),l=i0,l[4]=n,at.apply({},l)}function ot(t,e,n){var s,i=n-e,a=n-t.day();return a>i&&(a-=7),i-7>a&&(a+=7),s=Mt(t).add(a,"d"),{week:Math.ceil(s.dayOfYear()/7),year:s.year()}}function ut(t,e,n,s,i){var a,r,o=st(t,0,1).getUTCDay();return o=0===o?7:o,n=null!=n?n:i,a=i-o+(o>s?7:0)-(i>o?7:0),r=7*(e-1)+(n-i)+a+1,{year:r>0?t:t-1,dayOfYear:r>0?r:x(t-1)+r}}function ct(e){var n,s=e._i,i=e._f;return e._locale=e._locale||Mt.localeData(e._l),null===s||i===t&&""===s?Mt.invalid({nullInput:!0}):("string"==typeof s&&(e._i=s=e._locale.preparse(s)),Mt.isMoment(s)?new l(s,!0):(i?D(i)?Q(e):q(e):et(e),n=new l(e),n._nextDay&&(n.add(1,"d"),n._nextDay=t),n))}function lt(t,e){var n,s;if(1===e.length&&D(e[0])&&(e=e[0]),!e.length)return Mt();for(n=e[0],s=1;s=0?"+":"-";return e+m(Math.abs(t),6)},gg:function(){return m(this.weekYear()%100,2)},gggg:function(){return m(this.weekYear(),4)},ggggg:function(){return m(this.weekYear(),5)},GG:function(){return m(this.isoWeekYear()%100,2)},GGGG:function(){return m(this.isoWeekYear(),4)},GGGGG:function(){return m(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return S(this.milliseconds()/100)},SS:function(){return m(S(this.milliseconds()/10),2)},SSS:function(){return m(this.milliseconds(),3)},SSSS:function(){return m(this.milliseconds(),3)},Z:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+m(S(t/60),2)+":"+m(S(t)%60,2)},ZZ:function(){var t=-this.zone(),e="+";return 0>t&&(t=-t,e="-"),e+m(S(t/60),2)+m(S(t)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},x:function(){return this.valueOf()},X:function(){return this.unix()},Q:function(){return this.quarter()}},pe={},ye=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];fe.length;)wt=fe.pop(),me[wt+"o"]=u(me[wt],wt);for(;_e.length;)wt=_e.pop(),me[wt+wt]=o(me[wt],2);me.DDDD=o(me.DDD,3),d(c.prototype,{set:function(t){var e,n;for(n in t)e=t[n],"function"==typeof e?this[n]=e:this["_"+n]=e;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t,e,n){var s,i,a;for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),s=0;12>s;s++){if(i=Mt.utc([2e3,s]),n&&!this._longMonthsParse[s]&&(this._longMonthsParse[s]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[s]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[s]||(a="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[s]=new RegExp(a.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[s].test(t))return s;if(n&&"MMM"===e&&this._shortMonthsParse[s].test(t))return s;if(!n&&this._monthsParse[s].test(t))return s}},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,n,s;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(n=Mt([2e3,1]).day(e),s="^"+this.weekdays(n,"")+"|^"+this.weekdaysShort(n,"")+"|^"+this.weekdaysMin(n,""),this._weekdaysParse[e]=new RegExp(s.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM D, YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,n){return t>11?n?"pm":"PM":n?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e,n){var s=this._calendar[t];return"function"==typeof s?s.apply(e,[n]):s},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,n,s){var i=this._relativeTime[n];return"function"==typeof i?i(t,e,n,s):i.replace(/%d/i,t)},pastFuture:function(t,e){var n=this._relativeTime[t>0?"future":"past"];return"function"==typeof n?n(e):n.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",_ordinalParse:/\d{1,2}/,preparse:function(t){return t},postformat:function(t){return t},week:function(t){return ot(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),Mt=function(e,n,i,a){var r;return"boolean"==typeof i&&(a=i,i=t),r={},r._isAMomentObject=!0,r._i=e,r._f=n,r._l=i,r._strict=a,r._isUTC=!1,r._pf=s(),ct(r)},Mt.suppressDeprecationWarnings=!1,Mt.createFromInputFallback=a("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))}),Mt.min=function(){var t=[].slice.call(arguments,0);return lt("isBefore",t)},Mt.max=function(){var t=[].slice.call(arguments,0);return lt("isAfter",t)},Mt.utc=function(e,n,i,a){var r;return"boolean"==typeof i&&(a=i,i=t),r={},r._isAMomentObject=!0,r._useUTC=!0,r._isUTC=!0,r._l=i,r._i=e,r._f=n,r._strict=a,r._pf=s(),ct(r).utc()},Mt.unix=function(t){return Mt(1e3*t)},Mt.duration=function(t,e){var s,i,a,r,o=t,u=null;return Mt.isDuration(t)?o={ms:t._milliseconds,d:t._days,M:t._months}:"number"==typeof t?(o={},e?o[e]=t:o.milliseconds=t):(u=zt.exec(t))?(s="-"===u[1]?-1:1,o={y:0,d:S(u[Ot])*s,h:S(u[xt])*s,m:S(u[Ct])*s,s:S(u[Pt])*s,ms:S(u[Wt])*s}):(u=Et.exec(t))?(s="-"===u[1]?-1:1,a=function(t){var e=t&&parseFloat(t.replace(",","."));return(isNaN(e)?0:e)*s},o={y:a(u[2]),M:a(u[3]),d:a(u[4]),h:a(u[5]),m:a(u[6]),s:a(u[7]),w:a(u[8])}):"object"==typeof o&&("from"in o||"to"in o)&&(r=y(Mt(o.from),Mt(o.to)),o={},o.ms=r.milliseconds,o.M=r.months),i=new h(o),Mt.isDuration(t)&&n(t,"_locale")&&(i._locale=t._locale),i},Mt.version=Yt,Mt.defaultFormat=ie,Mt.ISO_8601=function(){},Mt.momentProperties=Ut,Mt.updateOffset=function(){},Mt.relativeTimeThreshold=function(e,n){return de[e]===t?!1:n===t?de[e]:(de[e]=n,!0)},Mt.lang=a("moment.lang is deprecated. Use moment.locale instead.",function(t,e){return Mt.locale(t,e)}),Mt.locale=function(t,e){var n;return t&&(n="undefined"!=typeof e?Mt.defineLocale(t,e):Mt.localeData(t),n&&(Mt.duration._locale=Mt._locale=n)),Mt._locale._abbr},Mt.defineLocale=function(t,e){return null!==e?(e.abbr=t,Gt[t]||(Gt[t]=new c),Gt[t].set(e),Mt.locale(t),Gt[t]):(delete Gt[t],null)},Mt.langData=a("moment.langData is deprecated. Use moment.localeData instead.",function(t){return Mt.localeData(t)}),Mt.localeData=function(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return Mt._locale;if(!D(t)){if(e=F(t))return e;t=[t]}return U(t)},Mt.isMoment=function(t){return t instanceof l||null!=t&&n(t,"_isAMomentObject")},Mt.isDuration=function(t){return t instanceof h};for(wt=ye.length-1;wt>=0;--wt)k(ye[wt]);Mt.normalizeUnits=function(t){return v(t)},Mt.invalid=function(t){var e=Mt.utc(0/0);return null!=t?d(e._pf,t):e._pf.userInvalidated=!0,e},Mt.parseZone=function(){return Mt.apply(null,arguments).parseZone()},Mt.parseTwoDigitYear=function(t){return S(t)+(S(t)>68?1900:2e3)},d(Mt.fn=l.prototype,{clone:function(){return Mt(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var t=Mt(this).utc();return 00:!1},parsingFlags:function(){return d({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(t){return this.zone(0,t)},local:function(t){return this._isUTC&&(this.zone(0,t),this._isUTC=!1,t&&this.add(this._dateTzOffset(),"m")),this},format:function(t){var e=L(this,t||Mt.defaultFormat);return this.localeData().postformat(e)},add:g(1,"add"),subtract:g(-1,"subtract"),diff:function(t,e,n){var s,i,a,r=I(t,this),o=6e4*(this.zone()-r.zone());return e=v(e),"year"===e||"month"===e?(s=432e5*(this.daysInMonth()+r.daysInMonth()),i=12*(this.year()-r.year())+(this.month()-r.month()),a=this-Mt(this).startOf("month")-(r-Mt(r).startOf("month")),a-=6e4*(this.zone()-Mt(this).startOf("month").zone()-(r.zone()-Mt(r).startOf("month").zone())),i+=a/s,"year"===e&&(i/=12)):(s=this-r,i="second"===e?s/1e3:"minute"===e?s/6e4:"hour"===e?s/36e5:"day"===e?(s-o)/864e5:"week"===e?(s-o)/6048e5:s),n?i:_(i)},from:function(t,e){return Mt.duration({to:this,from:t}).locale(this.locale()).humanize(!e)},fromNow:function(t){return this.from(Mt(),t)},calendar:function(t){var e=t||Mt(),n=I(e,this).startOf("day"),s=this.diff(n,"days",!0),i=-6>s?"sameElse":-1>s?"lastWeek":0>s?"lastDay":1>s?"sameDay":2>s?"nextDay":7>s?"nextWeek":"sameElse";return this.format(this.localeData().calendar(i,this,Mt(e)))},isLeapYear:function(){return C(this.year())},isDST:function(){return this.zone()+t):(n=Mt.isMoment(t)?+t:+Mt(t),n<+this.clone().startOf(e))},isBefore:function(t,e){var n;return e=v("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=Mt.isMoment(t)?t:Mt(t),+t>+this):(n=Mt.isMoment(t)?+t:+Mt(t),+this.clone().endOf(e)t?this:t}),max:a("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(t){return t=Mt.apply(null,arguments),t>this?this:t}),zone:function(t,e){var n,s=this._offset||0;return null==t?this._isUTC?s:this._dateTzOffset():("string"==typeof t&&(t=j(t)),Math.abs(t)<16&&(t=60*t),!this._isUTC&&e&&(n=this._dateTzOffset()),this._offset=t,this._isUTC=!0,null!=n&&this.subtract(n,"m"),s!==t&&(!e||this._changeInProgress?M(this,Mt.duration(s-t,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,Mt.updateOffset(this,!0),this._changeInProgress=null)),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.zone(this._tzm):"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(t){return t=t?Mt(t).zone():0,(this.zone()-t)%60===0},daysInMonth:function(){return T(this.year(),this.month())},dayOfYear:function(t){var e=bt((Mt(this).startOf("day")-Mt(this).startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},quarter:function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},weekYear:function(t){var e=ot(this,this.localeData()._week.dow,this.localeData()._week.doy).year;return null==t?e:this.add(t-e,"y")},isoWeekYear:function(t){var e=ot(this,1,4).year;return null==t?e:this.add(t-e,"y")},week:function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},isoWeek:function(t){var e=ot(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},weekday:function(t){var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},isoWeeksInYear:function(){return O(this.year(),1,4)},weeksInYear:function(){var t=this.localeData()._week;return O(this.year(),t.dow,t.doy)},get:function(t){return t=v(t),this[t]()},set:function(t,e){return t=v(t),"function"==typeof this[t]&&this[t](e),this},locale:function(e){var n;return e===t?this._locale._abbr:(n=Mt.localeData(e),null!=n&&(this._locale=n),this)},lang:a("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(e){return e===t?this.localeData():this.locale(e)}),localeData:function(){return this._locale},_dateTzOffset:function(){return 15*Math.round(this._d.getTimezoneOffset()/15)}}),Mt.fn.millisecond=Mt.fn.milliseconds=_t("Milliseconds",!1),Mt.fn.second=Mt.fn.seconds=_t("Seconds",!1),Mt.fn.minute=Mt.fn.minutes=_t("Minutes",!1),Mt.fn.hour=Mt.fn.hours=_t("Hours",!0),Mt.fn.date=_t("Date",!0),Mt.fn.dates=a("dates accessor is deprecated. Use date instead.",_t("Date",!0)),Mt.fn.year=_t("FullYear",!0),Mt.fn.years=a("years accessor is deprecated. Use year instead.",_t("FullYear",!0)),Mt.fn.days=Mt.fn.day,Mt.fn.months=Mt.fn.month,Mt.fn.weeks=Mt.fn.week,Mt.fn.isoWeeks=Mt.fn.isoWeek,Mt.fn.quarters=Mt.fn.quarter,Mt.fn.toJSON=Mt.fn.toISOString,d(Mt.duration.fn=h.prototype,{_bubble:function(){var t,e,n,s=this._milliseconds,i=this._days,a=this._months,r=this._data,o=0;r.milliseconds=s%1e3,t=_(s/1e3),r.seconds=t%60,e=_(t/60),r.minutes=e%60,n=_(e/60),r.hours=n%24,i+=_(n/24),o=_(mt(i)),i-=_(pt(o)),a+=_(i/30),i%=30,o+=_(a/12),a%=12,r.days=i,r.months=a,r.years=o},abs:function(){return this._milliseconds=Math.abs(this._milliseconds),this._days=Math.abs(this._days),this._months=Math.abs(this._months),this._data.milliseconds=Math.abs(this._data.milliseconds),this._data.seconds=Math.abs(this._data.seconds),this._data.minutes=Math.abs(this._data.minutes),this._data.hours=Math.abs(this._data.hours),this._data.months=Math.abs(this._data.months),this._data.years=Math.abs(this._data.years),this},weeks:function(){return _(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*S(this._months/12)},humanize:function(t){var e=rt(this,!t,this.localeData());return t&&(e=this.localeData().pastFuture(+this,e)),this.localeData().postformat(e)},add:function(t,e){var n=Mt.duration(t,e);return this._milliseconds+=n._milliseconds,this._days+=n._days,this._months+=n._months,this._bubble(),this},subtract:function(t,e){var n=Mt.duration(t,e);return this._milliseconds-=n._milliseconds,this._days-=n._days,this._months-=n._months,this._bubble(),this},get:function(t){return t=v(t),this[t.toLowerCase()+"s"]()},as:function(t){var e,n;if(t=v(t),"month"===t||"year"===t)return e=this._days+this._milliseconds/864e5,n=this._months+12*mt(e),"month"===t?n:n/12;switch(e=this._days+Math.round(pt(this._months/12)),t){case"week":return e/7+this._milliseconds/6048e5;case"day":return e+this._milliseconds/864e5;case"hour":return 24*e+this._milliseconds/36e5;case"minute":return 24*e*60+this._milliseconds/6e4;case"second": 2 | return 24*e*60*60+this._milliseconds/1e3;case"millisecond":return Math.floor(24*e*60*60*1e3)+this._milliseconds;default:throw new Error("Unknown unit "+t)}},lang:Mt.fn.lang,locale:Mt.fn.locale,toIsoString:a("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",function(){return this.toISOString()}),toISOString:function(){var t=Math.abs(this.years()),e=Math.abs(this.months()),n=Math.abs(this.days()),s=Math.abs(this.hours()),i=Math.abs(this.minutes()),a=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(t?t+"Y":"")+(e?e+"M":"")+(n?n+"D":"")+(s||i||a?"T":"")+(s?s+"H":"")+(i?i+"M":"")+(a?a+"S":""):"P0D"},localeData:function(){return this._locale}}),Mt.duration.fn.toString=Mt.duration.fn.toISOString;for(wt in ue)n(ue,wt)&&yt(wt.toLowerCase());Mt.duration.fn.asMilliseconds=function(){return this.as("ms")},Mt.duration.fn.asSeconds=function(){return this.as("s")},Mt.duration.fn.asMinutes=function(){return this.as("m")},Mt.duration.fn.asHours=function(){return this.as("h")},Mt.duration.fn.asDays=function(){return this.as("d")},Mt.duration.fn.asWeeks=function(){return this.as("weeks")},Mt.duration.fn.asMonths=function(){return this.as("M")},Mt.duration.fn.asYears=function(){return this.as("y")},Mt.locale("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10,n=1===S(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+n}}),Ft?module.exports=Mt:"function"==typeof define&&define.amd?(define("moment",function(t,e,n){return n.config&&n.config()&&n.config().noGlobal===!0&&(vt.moment=Dt),Mt}),gt(!0)):gt()}).call(this);var bind=function(t,e){return function(){return t.apply(e,arguments)}};!function(){"use strict";var t;return t=function(){function t(t,e){this.isExpired=bind(this.isExpired,this),this.setExpiration=bind(this.setExpiration,this),this.getExpiration=bind(this.getExpiration,this),this.setObject=bind(this.setObject,this),this.getObject=bind(this.getObject,this),this.set=bind(this.set,this),this.get=bind(this.get,this),this.clearAll=bind(this.clearAll,this),this.clear=bind(this.clear,this),this.exists=bind(this.exists,this),this.getCacheKey=bind(this.getCacheKey,this),this.getCachePrefix=bind(this.getCachePrefix,this),this.$window=t.$get(),this.moment=e}return t.prototype.$inject=["$windowProvider","moment"],t.prototype.cachePrefix=null,t.prototype.setCachePrefix=function(t){return this.cachePrefix=t},t.prototype.getCachePrefix=function(){return this.cachePrefix},t.prototype.getCacheKey=function(t){var e;return e=this.getCachePrefix(),null===e?t:e+"-"+t},t.prototype.getExpirationCacheKey=function(t){return this.getCacheKey(t)+"-expiration"},t.prototype.exists=function(t){return null==this.$window.localStorage.getItem(this.getCacheKey(t))?!1:!0},t.prototype.clear=function(t){var e;return e=this.getExpiration(t),null!=e&&delete this.$window.localStorage.removeItem(this.getExpirationCacheKey(t)),this.exists(t)?delete this.$window.localStorage.removeItem(this.getCacheKey(t)):void 0},t.prototype.clearAll=function(){return this.$window.localStorage.clear()},t.prototype.get=function(t){return this.isExpired(t)?(this.clear(t),null):this.exists(t)?this.$window.localStorage.getItem(this.getCacheKey(t)):null},t.prototype.set=function(t,e,n){return null==n&&(n=null),this.$window.localStorage.setItem(this.getCacheKey(t),e),null!=n?this.setExpiration(t,n):void 0},t.prototype.getObject=function(t){return JSON.parse(this.get(t))},t.prototype.setObject=function(t,e,n){return this.set(t,JSON.stringify(e),n)},t.prototype.getExpiration=function(t){var e,n;return e=this.getExpirationCacheKey(t),n=this.$window.localStorage.getItem(e),n?n:null},t.prototype.setExpiration=function(t,e){var n;return n=this.getExpirationCacheKey(t),this.$window.localStorage.setItem(n,this.moment().add(parseInt(e),"s").valueOf())},t.prototype.isExpired=function(t){var e;return e=this.getExpiration(t),e&&this.moment().valueOf()>parseInt(e)?!0:!1},t.prototype.$get=function(){return{getCachePrefix:this.getCachePrefix,exists:this.exists,clear:this.clear,clearAll:this.clearAll,get:this.get,set:this.set,getObject:this.getObject,setObject:this.setObject,getExpiration:this.getExpiration,setExpiration:this.setExpiration,isExpired:this.isExpired}},t}(),angular.module("sbb.cachomatic",[]).constant("moment",moment).provider("Cachomatic",t)}(); -------------------------------------------------------------------------------- /release/cachomatic.js: -------------------------------------------------------------------------------- 1 | //! moment.js 2 | //! version : 2.8.4 3 | //! authors : Tim Wood, Iskren Chernev, Moment.js contributors 4 | //! license : MIT 5 | //! momentjs.com 6 | 7 | (function (undefined) { 8 | /************************************ 9 | Constants 10 | ************************************/ 11 | 12 | var moment, 13 | VERSION = '2.8.4', 14 | // the global-scope this is NOT the global object in Node.js 15 | globalScope = typeof global !== 'undefined' ? global : this, 16 | oldGlobalMoment, 17 | round = Math.round, 18 | hasOwnProperty = Object.prototype.hasOwnProperty, 19 | i, 20 | 21 | YEAR = 0, 22 | MONTH = 1, 23 | DATE = 2, 24 | HOUR = 3, 25 | MINUTE = 4, 26 | SECOND = 5, 27 | MILLISECOND = 6, 28 | 29 | // internal storage for locale config files 30 | locales = {}, 31 | 32 | // extra moment internal properties (plugins register props here) 33 | momentProperties = [], 34 | 35 | // check for nodeJS 36 | hasModule = (typeof module !== 'undefined' && module && module.exports), 37 | 38 | // ASP.NET json date format regex 39 | aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, 40 | aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/, 41 | 42 | // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html 43 | // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere 44 | isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/, 45 | 46 | // format tokens 47 | formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|x|X|zz?|ZZ?|.)/g, 48 | localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, 49 | 50 | // parsing token regexes 51 | parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 52 | parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 53 | parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999 54 | parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999 55 | parseTokenDigits = /\d+/, // nonzero number of digits 56 | parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic. 57 | parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z 58 | parseTokenT = /T/i, // T (ISO separator) 59 | parseTokenOffsetMs = /[\+\-]?\d+/, // 1234567890123 60 | parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 61 | 62 | //strict parsing regexes 63 | parseTokenOneDigit = /\d/, // 0 - 9 64 | parseTokenTwoDigits = /\d\d/, // 00 - 99 65 | parseTokenThreeDigits = /\d{3}/, // 000 - 999 66 | parseTokenFourDigits = /\d{4}/, // 0000 - 9999 67 | parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999 68 | parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf 69 | 70 | // iso 8601 regex 71 | // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) 72 | isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/, 73 | 74 | isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', 75 | 76 | isoDates = [ 77 | ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/], 78 | ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/], 79 | ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/], 80 | ['GGGG-[W]WW', /\d{4}-W\d{2}/], 81 | ['YYYY-DDD', /\d{4}-\d{3}/] 82 | ], 83 | 84 | // iso time formats and regexes 85 | isoTimes = [ 86 | ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/], 87 | ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], 88 | ['HH:mm', /(T| )\d\d:\d\d/], 89 | ['HH', /(T| )\d\d/] 90 | ], 91 | 92 | // timezone chunker '+10:00' > ['10', '00'] or '-1530' > ['-15', '30'] 93 | parseTimezoneChunker = /([\+\-]|\d\d)/gi, 94 | 95 | // getter and setter names 96 | proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), 97 | unitMillisecondFactors = { 98 | 'Milliseconds' : 1, 99 | 'Seconds' : 1e3, 100 | 'Minutes' : 6e4, 101 | 'Hours' : 36e5, 102 | 'Days' : 864e5, 103 | 'Months' : 2592e6, 104 | 'Years' : 31536e6 105 | }, 106 | 107 | unitAliases = { 108 | ms : 'millisecond', 109 | s : 'second', 110 | m : 'minute', 111 | h : 'hour', 112 | d : 'day', 113 | D : 'date', 114 | w : 'week', 115 | W : 'isoWeek', 116 | M : 'month', 117 | Q : 'quarter', 118 | y : 'year', 119 | DDD : 'dayOfYear', 120 | e : 'weekday', 121 | E : 'isoWeekday', 122 | gg: 'weekYear', 123 | GG: 'isoWeekYear' 124 | }, 125 | 126 | camelFunctions = { 127 | dayofyear : 'dayOfYear', 128 | isoweekday : 'isoWeekday', 129 | isoweek : 'isoWeek', 130 | weekyear : 'weekYear', 131 | isoweekyear : 'isoWeekYear' 132 | }, 133 | 134 | // format function strings 135 | formatFunctions = {}, 136 | 137 | // default relative time thresholds 138 | relativeTimeThresholds = { 139 | s: 45, // seconds to minute 140 | m: 45, // minutes to hour 141 | h: 22, // hours to day 142 | d: 26, // days to month 143 | M: 11 // months to year 144 | }, 145 | 146 | // tokens to ordinalize and pad 147 | ordinalizeTokens = 'DDD w W M D d'.split(' '), 148 | paddedTokens = 'M D H h m s w W'.split(' '), 149 | 150 | formatTokenFunctions = { 151 | M : function () { 152 | return this.month() + 1; 153 | }, 154 | MMM : function (format) { 155 | return this.localeData().monthsShort(this, format); 156 | }, 157 | MMMM : function (format) { 158 | return this.localeData().months(this, format); 159 | }, 160 | D : function () { 161 | return this.date(); 162 | }, 163 | DDD : function () { 164 | return this.dayOfYear(); 165 | }, 166 | d : function () { 167 | return this.day(); 168 | }, 169 | dd : function (format) { 170 | return this.localeData().weekdaysMin(this, format); 171 | }, 172 | ddd : function (format) { 173 | return this.localeData().weekdaysShort(this, format); 174 | }, 175 | dddd : function (format) { 176 | return this.localeData().weekdays(this, format); 177 | }, 178 | w : function () { 179 | return this.week(); 180 | }, 181 | W : function () { 182 | return this.isoWeek(); 183 | }, 184 | YY : function () { 185 | return leftZeroFill(this.year() % 100, 2); 186 | }, 187 | YYYY : function () { 188 | return leftZeroFill(this.year(), 4); 189 | }, 190 | YYYYY : function () { 191 | return leftZeroFill(this.year(), 5); 192 | }, 193 | YYYYYY : function () { 194 | var y = this.year(), sign = y >= 0 ? '+' : '-'; 195 | return sign + leftZeroFill(Math.abs(y), 6); 196 | }, 197 | gg : function () { 198 | return leftZeroFill(this.weekYear() % 100, 2); 199 | }, 200 | gggg : function () { 201 | return leftZeroFill(this.weekYear(), 4); 202 | }, 203 | ggggg : function () { 204 | return leftZeroFill(this.weekYear(), 5); 205 | }, 206 | GG : function () { 207 | return leftZeroFill(this.isoWeekYear() % 100, 2); 208 | }, 209 | GGGG : function () { 210 | return leftZeroFill(this.isoWeekYear(), 4); 211 | }, 212 | GGGGG : function () { 213 | return leftZeroFill(this.isoWeekYear(), 5); 214 | }, 215 | e : function () { 216 | return this.weekday(); 217 | }, 218 | E : function () { 219 | return this.isoWeekday(); 220 | }, 221 | a : function () { 222 | return this.localeData().meridiem(this.hours(), this.minutes(), true); 223 | }, 224 | A : function () { 225 | return this.localeData().meridiem(this.hours(), this.minutes(), false); 226 | }, 227 | H : function () { 228 | return this.hours(); 229 | }, 230 | h : function () { 231 | return this.hours() % 12 || 12; 232 | }, 233 | m : function () { 234 | return this.minutes(); 235 | }, 236 | s : function () { 237 | return this.seconds(); 238 | }, 239 | S : function () { 240 | return toInt(this.milliseconds() / 100); 241 | }, 242 | SS : function () { 243 | return leftZeroFill(toInt(this.milliseconds() / 10), 2); 244 | }, 245 | SSS : function () { 246 | return leftZeroFill(this.milliseconds(), 3); 247 | }, 248 | SSSS : function () { 249 | return leftZeroFill(this.milliseconds(), 3); 250 | }, 251 | Z : function () { 252 | var a = -this.zone(), 253 | b = '+'; 254 | if (a < 0) { 255 | a = -a; 256 | b = '-'; 257 | } 258 | return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2); 259 | }, 260 | ZZ : function () { 261 | var a = -this.zone(), 262 | b = '+'; 263 | if (a < 0) { 264 | a = -a; 265 | b = '-'; 266 | } 267 | return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2); 268 | }, 269 | z : function () { 270 | return this.zoneAbbr(); 271 | }, 272 | zz : function () { 273 | return this.zoneName(); 274 | }, 275 | x : function () { 276 | return this.valueOf(); 277 | }, 278 | X : function () { 279 | return this.unix(); 280 | }, 281 | Q : function () { 282 | return this.quarter(); 283 | } 284 | }, 285 | 286 | deprecations = {}, 287 | 288 | lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin']; 289 | 290 | // Pick the first defined of two or three arguments. dfl comes from 291 | // default. 292 | function dfl(a, b, c) { 293 | switch (arguments.length) { 294 | case 2: return a != null ? a : b; 295 | case 3: return a != null ? a : b != null ? b : c; 296 | default: throw new Error('Implement me'); 297 | } 298 | } 299 | 300 | function hasOwnProp(a, b) { 301 | return hasOwnProperty.call(a, b); 302 | } 303 | 304 | function defaultParsingFlags() { 305 | // We need to deep clone this object, and es5 standard is not very 306 | // helpful. 307 | return { 308 | empty : false, 309 | unusedTokens : [], 310 | unusedInput : [], 311 | overflow : -2, 312 | charsLeftOver : 0, 313 | nullInput : false, 314 | invalidMonth : null, 315 | invalidFormat : false, 316 | userInvalidated : false, 317 | iso: false 318 | }; 319 | } 320 | 321 | function printMsg(msg) { 322 | if (moment.suppressDeprecationWarnings === false && 323 | typeof console !== 'undefined' && console.warn) { 324 | console.warn('Deprecation warning: ' + msg); 325 | } 326 | } 327 | 328 | function deprecate(msg, fn) { 329 | var firstTime = true; 330 | return extend(function () { 331 | if (firstTime) { 332 | printMsg(msg); 333 | firstTime = false; 334 | } 335 | return fn.apply(this, arguments); 336 | }, fn); 337 | } 338 | 339 | function deprecateSimple(name, msg) { 340 | if (!deprecations[name]) { 341 | printMsg(msg); 342 | deprecations[name] = true; 343 | } 344 | } 345 | 346 | function padToken(func, count) { 347 | return function (a) { 348 | return leftZeroFill(func.call(this, a), count); 349 | }; 350 | } 351 | function ordinalizeToken(func, period) { 352 | return function (a) { 353 | return this.localeData().ordinal(func.call(this, a), period); 354 | }; 355 | } 356 | 357 | while (ordinalizeTokens.length) { 358 | i = ordinalizeTokens.pop(); 359 | formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); 360 | } 361 | while (paddedTokens.length) { 362 | i = paddedTokens.pop(); 363 | formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); 364 | } 365 | formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); 366 | 367 | 368 | /************************************ 369 | Constructors 370 | ************************************/ 371 | 372 | function Locale() { 373 | } 374 | 375 | // Moment prototype object 376 | function Moment(config, skipOverflow) { 377 | if (skipOverflow !== false) { 378 | checkOverflow(config); 379 | } 380 | copyConfig(this, config); 381 | this._d = new Date(+config._d); 382 | } 383 | 384 | // Duration Constructor 385 | function Duration(duration) { 386 | var normalizedInput = normalizeObjectUnits(duration), 387 | years = normalizedInput.year || 0, 388 | quarters = normalizedInput.quarter || 0, 389 | months = normalizedInput.month || 0, 390 | weeks = normalizedInput.week || 0, 391 | days = normalizedInput.day || 0, 392 | hours = normalizedInput.hour || 0, 393 | minutes = normalizedInput.minute || 0, 394 | seconds = normalizedInput.second || 0, 395 | milliseconds = normalizedInput.millisecond || 0; 396 | 397 | // representation for dateAddRemove 398 | this._milliseconds = +milliseconds + 399 | seconds * 1e3 + // 1000 400 | minutes * 6e4 + // 1000 * 60 401 | hours * 36e5; // 1000 * 60 * 60 402 | // Because of dateAddRemove treats 24 hours as different from a 403 | // day when working around DST, we need to store them separately 404 | this._days = +days + 405 | weeks * 7; 406 | // It is impossible translate months into days without knowing 407 | // which months you are are talking about, so we have to store 408 | // it separately. 409 | this._months = +months + 410 | quarters * 3 + 411 | years * 12; 412 | 413 | this._data = {}; 414 | 415 | this._locale = moment.localeData(); 416 | 417 | this._bubble(); 418 | } 419 | 420 | /************************************ 421 | Helpers 422 | ************************************/ 423 | 424 | 425 | function extend(a, b) { 426 | for (var i in b) { 427 | if (hasOwnProp(b, i)) { 428 | a[i] = b[i]; 429 | } 430 | } 431 | 432 | if (hasOwnProp(b, 'toString')) { 433 | a.toString = b.toString; 434 | } 435 | 436 | if (hasOwnProp(b, 'valueOf')) { 437 | a.valueOf = b.valueOf; 438 | } 439 | 440 | return a; 441 | } 442 | 443 | function copyConfig(to, from) { 444 | var i, prop, val; 445 | 446 | if (typeof from._isAMomentObject !== 'undefined') { 447 | to._isAMomentObject = from._isAMomentObject; 448 | } 449 | if (typeof from._i !== 'undefined') { 450 | to._i = from._i; 451 | } 452 | if (typeof from._f !== 'undefined') { 453 | to._f = from._f; 454 | } 455 | if (typeof from._l !== 'undefined') { 456 | to._l = from._l; 457 | } 458 | if (typeof from._strict !== 'undefined') { 459 | to._strict = from._strict; 460 | } 461 | if (typeof from._tzm !== 'undefined') { 462 | to._tzm = from._tzm; 463 | } 464 | if (typeof from._isUTC !== 'undefined') { 465 | to._isUTC = from._isUTC; 466 | } 467 | if (typeof from._offset !== 'undefined') { 468 | to._offset = from._offset; 469 | } 470 | if (typeof from._pf !== 'undefined') { 471 | to._pf = from._pf; 472 | } 473 | if (typeof from._locale !== 'undefined') { 474 | to._locale = from._locale; 475 | } 476 | 477 | if (momentProperties.length > 0) { 478 | for (i in momentProperties) { 479 | prop = momentProperties[i]; 480 | val = from[prop]; 481 | if (typeof val !== 'undefined') { 482 | to[prop] = val; 483 | } 484 | } 485 | } 486 | 487 | return to; 488 | } 489 | 490 | function absRound(number) { 491 | if (number < 0) { 492 | return Math.ceil(number); 493 | } else { 494 | return Math.floor(number); 495 | } 496 | } 497 | 498 | // left zero fill a number 499 | // see http://jsperf.com/left-zero-filling for performance comparison 500 | function leftZeroFill(number, targetLength, forceSign) { 501 | var output = '' + Math.abs(number), 502 | sign = number >= 0; 503 | 504 | while (output.length < targetLength) { 505 | output = '0' + output; 506 | } 507 | return (sign ? (forceSign ? '+' : '') : '-') + output; 508 | } 509 | 510 | function positiveMomentsDifference(base, other) { 511 | var res = {milliseconds: 0, months: 0}; 512 | 513 | res.months = other.month() - base.month() + 514 | (other.year() - base.year()) * 12; 515 | if (base.clone().add(res.months, 'M').isAfter(other)) { 516 | --res.months; 517 | } 518 | 519 | res.milliseconds = +other - +(base.clone().add(res.months, 'M')); 520 | 521 | return res; 522 | } 523 | 524 | function momentsDifference(base, other) { 525 | var res; 526 | other = makeAs(other, base); 527 | if (base.isBefore(other)) { 528 | res = positiveMomentsDifference(base, other); 529 | } else { 530 | res = positiveMomentsDifference(other, base); 531 | res.milliseconds = -res.milliseconds; 532 | res.months = -res.months; 533 | } 534 | 535 | return res; 536 | } 537 | 538 | // TODO: remove 'name' arg after deprecation is removed 539 | function createAdder(direction, name) { 540 | return function (val, period) { 541 | var dur, tmp; 542 | //invert the arguments, but complain about it 543 | if (period !== null && !isNaN(+period)) { 544 | deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); 545 | tmp = val; val = period; period = tmp; 546 | } 547 | 548 | val = typeof val === 'string' ? +val : val; 549 | dur = moment.duration(val, period); 550 | addOrSubtractDurationFromMoment(this, dur, direction); 551 | return this; 552 | }; 553 | } 554 | 555 | function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) { 556 | var milliseconds = duration._milliseconds, 557 | days = duration._days, 558 | months = duration._months; 559 | updateOffset = updateOffset == null ? true : updateOffset; 560 | 561 | if (milliseconds) { 562 | mom._d.setTime(+mom._d + milliseconds * isAdding); 563 | } 564 | if (days) { 565 | rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding); 566 | } 567 | if (months) { 568 | rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding); 569 | } 570 | if (updateOffset) { 571 | moment.updateOffset(mom, days || months); 572 | } 573 | } 574 | 575 | // check if is an array 576 | function isArray(input) { 577 | return Object.prototype.toString.call(input) === '[object Array]'; 578 | } 579 | 580 | function isDate(input) { 581 | return Object.prototype.toString.call(input) === '[object Date]' || 582 | input instanceof Date; 583 | } 584 | 585 | // compare two arrays, return the number of differences 586 | function compareArrays(array1, array2, dontConvert) { 587 | var len = Math.min(array1.length, array2.length), 588 | lengthDiff = Math.abs(array1.length - array2.length), 589 | diffs = 0, 590 | i; 591 | for (i = 0; i < len; i++) { 592 | if ((dontConvert && array1[i] !== array2[i]) || 593 | (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { 594 | diffs++; 595 | } 596 | } 597 | return diffs + lengthDiff; 598 | } 599 | 600 | function normalizeUnits(units) { 601 | if (units) { 602 | var lowered = units.toLowerCase().replace(/(.)s$/, '$1'); 603 | units = unitAliases[units] || camelFunctions[lowered] || lowered; 604 | } 605 | return units; 606 | } 607 | 608 | function normalizeObjectUnits(inputObject) { 609 | var normalizedInput = {}, 610 | normalizedProp, 611 | prop; 612 | 613 | for (prop in inputObject) { 614 | if (hasOwnProp(inputObject, prop)) { 615 | normalizedProp = normalizeUnits(prop); 616 | if (normalizedProp) { 617 | normalizedInput[normalizedProp] = inputObject[prop]; 618 | } 619 | } 620 | } 621 | 622 | return normalizedInput; 623 | } 624 | 625 | function makeList(field) { 626 | var count, setter; 627 | 628 | if (field.indexOf('week') === 0) { 629 | count = 7; 630 | setter = 'day'; 631 | } 632 | else if (field.indexOf('month') === 0) { 633 | count = 12; 634 | setter = 'month'; 635 | } 636 | else { 637 | return; 638 | } 639 | 640 | moment[field] = function (format, index) { 641 | var i, getter, 642 | method = moment._locale[field], 643 | results = []; 644 | 645 | if (typeof format === 'number') { 646 | index = format; 647 | format = undefined; 648 | } 649 | 650 | getter = function (i) { 651 | var m = moment().utc().set(setter, i); 652 | return method.call(moment._locale, m, format || ''); 653 | }; 654 | 655 | if (index != null) { 656 | return getter(index); 657 | } 658 | else { 659 | for (i = 0; i < count; i++) { 660 | results.push(getter(i)); 661 | } 662 | return results; 663 | } 664 | }; 665 | } 666 | 667 | function toInt(argumentForCoercion) { 668 | var coercedNumber = +argumentForCoercion, 669 | value = 0; 670 | 671 | if (coercedNumber !== 0 && isFinite(coercedNumber)) { 672 | if (coercedNumber >= 0) { 673 | value = Math.floor(coercedNumber); 674 | } else { 675 | value = Math.ceil(coercedNumber); 676 | } 677 | } 678 | 679 | return value; 680 | } 681 | 682 | function daysInMonth(year, month) { 683 | return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); 684 | } 685 | 686 | function weeksInYear(year, dow, doy) { 687 | return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week; 688 | } 689 | 690 | function daysInYear(year) { 691 | return isLeapYear(year) ? 366 : 365; 692 | } 693 | 694 | function isLeapYear(year) { 695 | return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; 696 | } 697 | 698 | function checkOverflow(m) { 699 | var overflow; 700 | if (m._a && m._pf.overflow === -2) { 701 | overflow = 702 | m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH : 703 | m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE : 704 | m._a[HOUR] < 0 || m._a[HOUR] > 24 || 705 | (m._a[HOUR] === 24 && (m._a[MINUTE] !== 0 || 706 | m._a[SECOND] !== 0 || 707 | m._a[MILLISECOND] !== 0)) ? HOUR : 708 | m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE : 709 | m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND : 710 | m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND : 711 | -1; 712 | 713 | if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { 714 | overflow = DATE; 715 | } 716 | 717 | m._pf.overflow = overflow; 718 | } 719 | } 720 | 721 | function isValid(m) { 722 | if (m._isValid == null) { 723 | m._isValid = !isNaN(m._d.getTime()) && 724 | m._pf.overflow < 0 && 725 | !m._pf.empty && 726 | !m._pf.invalidMonth && 727 | !m._pf.nullInput && 728 | !m._pf.invalidFormat && 729 | !m._pf.userInvalidated; 730 | 731 | if (m._strict) { 732 | m._isValid = m._isValid && 733 | m._pf.charsLeftOver === 0 && 734 | m._pf.unusedTokens.length === 0 && 735 | m._pf.bigHour === undefined; 736 | } 737 | } 738 | return m._isValid; 739 | } 740 | 741 | function normalizeLocale(key) { 742 | return key ? key.toLowerCase().replace('_', '-') : key; 743 | } 744 | 745 | // pick the locale from the array 746 | // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each 747 | // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root 748 | function chooseLocale(names) { 749 | var i = 0, j, next, locale, split; 750 | 751 | while (i < names.length) { 752 | split = normalizeLocale(names[i]).split('-'); 753 | j = split.length; 754 | next = normalizeLocale(names[i + 1]); 755 | next = next ? next.split('-') : null; 756 | while (j > 0) { 757 | locale = loadLocale(split.slice(0, j).join('-')); 758 | if (locale) { 759 | return locale; 760 | } 761 | if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { 762 | //the next array item is better than a shallower substring of this one 763 | break; 764 | } 765 | j--; 766 | } 767 | i++; 768 | } 769 | return null; 770 | } 771 | 772 | function loadLocale(name) { 773 | var oldLocale = null; 774 | if (!locales[name] && hasModule) { 775 | try { 776 | oldLocale = moment.locale(); 777 | require('./locale/' + name); 778 | // because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales 779 | moment.locale(oldLocale); 780 | } catch (e) { } 781 | } 782 | return locales[name]; 783 | } 784 | 785 | // Return a moment from input, that is local/utc/zone equivalent to model. 786 | function makeAs(input, model) { 787 | var res, diff; 788 | if (model._isUTC) { 789 | res = model.clone(); 790 | diff = (moment.isMoment(input) || isDate(input) ? 791 | +input : +moment(input)) - (+res); 792 | // Use low-level api, because this fn is low-level api. 793 | res._d.setTime(+res._d + diff); 794 | moment.updateOffset(res, false); 795 | return res; 796 | } else { 797 | return moment(input).local(); 798 | } 799 | } 800 | 801 | /************************************ 802 | Locale 803 | ************************************/ 804 | 805 | 806 | extend(Locale.prototype, { 807 | 808 | set : function (config) { 809 | var prop, i; 810 | for (i in config) { 811 | prop = config[i]; 812 | if (typeof prop === 'function') { 813 | this[i] = prop; 814 | } else { 815 | this['_' + i] = prop; 816 | } 817 | } 818 | // Lenient ordinal parsing accepts just a number in addition to 819 | // number + (possibly) stuff coming from _ordinalParseLenient. 820 | this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + /\d{1,2}/.source); 821 | }, 822 | 823 | _months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), 824 | months : function (m) { 825 | return this._months[m.month()]; 826 | }, 827 | 828 | _monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), 829 | monthsShort : function (m) { 830 | return this._monthsShort[m.month()]; 831 | }, 832 | 833 | monthsParse : function (monthName, format, strict) { 834 | var i, mom, regex; 835 | 836 | if (!this._monthsParse) { 837 | this._monthsParse = []; 838 | this._longMonthsParse = []; 839 | this._shortMonthsParse = []; 840 | } 841 | 842 | for (i = 0; i < 12; i++) { 843 | // make the regex if we don't have it already 844 | mom = moment.utc([2000, i]); 845 | if (strict && !this._longMonthsParse[i]) { 846 | this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); 847 | this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); 848 | } 849 | if (!strict && !this._monthsParse[i]) { 850 | regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); 851 | this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); 852 | } 853 | // test the regex 854 | if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { 855 | return i; 856 | } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { 857 | return i; 858 | } else if (!strict && this._monthsParse[i].test(monthName)) { 859 | return i; 860 | } 861 | } 862 | }, 863 | 864 | _weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), 865 | weekdays : function (m) { 866 | return this._weekdays[m.day()]; 867 | }, 868 | 869 | _weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), 870 | weekdaysShort : function (m) { 871 | return this._weekdaysShort[m.day()]; 872 | }, 873 | 874 | _weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), 875 | weekdaysMin : function (m) { 876 | return this._weekdaysMin[m.day()]; 877 | }, 878 | 879 | weekdaysParse : function (weekdayName) { 880 | var i, mom, regex; 881 | 882 | if (!this._weekdaysParse) { 883 | this._weekdaysParse = []; 884 | } 885 | 886 | for (i = 0; i < 7; i++) { 887 | // make the regex if we don't have it already 888 | if (!this._weekdaysParse[i]) { 889 | mom = moment([2000, 1]).day(i); 890 | regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); 891 | this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); 892 | } 893 | // test the regex 894 | if (this._weekdaysParse[i].test(weekdayName)) { 895 | return i; 896 | } 897 | } 898 | }, 899 | 900 | _longDateFormat : { 901 | LTS : 'h:mm:ss A', 902 | LT : 'h:mm A', 903 | L : 'MM/DD/YYYY', 904 | LL : 'MMMM D, YYYY', 905 | LLL : 'MMMM D, YYYY LT', 906 | LLLL : 'dddd, MMMM D, YYYY LT' 907 | }, 908 | longDateFormat : function (key) { 909 | var output = this._longDateFormat[key]; 910 | if (!output && this._longDateFormat[key.toUpperCase()]) { 911 | output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { 912 | return val.slice(1); 913 | }); 914 | this._longDateFormat[key] = output; 915 | } 916 | return output; 917 | }, 918 | 919 | isPM : function (input) { 920 | // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays 921 | // Using charAt should be more compatible. 922 | return ((input + '').toLowerCase().charAt(0) === 'p'); 923 | }, 924 | 925 | _meridiemParse : /[ap]\.?m?\.?/i, 926 | meridiem : function (hours, minutes, isLower) { 927 | if (hours > 11) { 928 | return isLower ? 'pm' : 'PM'; 929 | } else { 930 | return isLower ? 'am' : 'AM'; 931 | } 932 | }, 933 | 934 | _calendar : { 935 | sameDay : '[Today at] LT', 936 | nextDay : '[Tomorrow at] LT', 937 | nextWeek : 'dddd [at] LT', 938 | lastDay : '[Yesterday at] LT', 939 | lastWeek : '[Last] dddd [at] LT', 940 | sameElse : 'L' 941 | }, 942 | calendar : function (key, mom, now) { 943 | var output = this._calendar[key]; 944 | return typeof output === 'function' ? output.apply(mom, [now]) : output; 945 | }, 946 | 947 | _relativeTime : { 948 | future : 'in %s', 949 | past : '%s ago', 950 | s : 'a few seconds', 951 | m : 'a minute', 952 | mm : '%d minutes', 953 | h : 'an hour', 954 | hh : '%d hours', 955 | d : 'a day', 956 | dd : '%d days', 957 | M : 'a month', 958 | MM : '%d months', 959 | y : 'a year', 960 | yy : '%d years' 961 | }, 962 | 963 | relativeTime : function (number, withoutSuffix, string, isFuture) { 964 | var output = this._relativeTime[string]; 965 | return (typeof output === 'function') ? 966 | output(number, withoutSuffix, string, isFuture) : 967 | output.replace(/%d/i, number); 968 | }, 969 | 970 | pastFuture : function (diff, output) { 971 | var format = this._relativeTime[diff > 0 ? 'future' : 'past']; 972 | return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); 973 | }, 974 | 975 | ordinal : function (number) { 976 | return this._ordinal.replace('%d', number); 977 | }, 978 | _ordinal : '%d', 979 | _ordinalParse : /\d{1,2}/, 980 | 981 | preparse : function (string) { 982 | return string; 983 | }, 984 | 985 | postformat : function (string) { 986 | return string; 987 | }, 988 | 989 | week : function (mom) { 990 | return weekOfYear(mom, this._week.dow, this._week.doy).week; 991 | }, 992 | 993 | _week : { 994 | dow : 0, // Sunday is the first day of the week. 995 | doy : 6 // The week that contains Jan 1st is the first week of the year. 996 | }, 997 | 998 | _invalidDate: 'Invalid date', 999 | invalidDate: function () { 1000 | return this._invalidDate; 1001 | } 1002 | }); 1003 | 1004 | /************************************ 1005 | Formatting 1006 | ************************************/ 1007 | 1008 | 1009 | function removeFormattingTokens(input) { 1010 | if (input.match(/\[[\s\S]/)) { 1011 | return input.replace(/^\[|\]$/g, ''); 1012 | } 1013 | return input.replace(/\\/g, ''); 1014 | } 1015 | 1016 | function makeFormatFunction(format) { 1017 | var array = format.match(formattingTokens), i, length; 1018 | 1019 | for (i = 0, length = array.length; i < length; i++) { 1020 | if (formatTokenFunctions[array[i]]) { 1021 | array[i] = formatTokenFunctions[array[i]]; 1022 | } else { 1023 | array[i] = removeFormattingTokens(array[i]); 1024 | } 1025 | } 1026 | 1027 | return function (mom) { 1028 | var output = ''; 1029 | for (i = 0; i < length; i++) { 1030 | output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; 1031 | } 1032 | return output; 1033 | }; 1034 | } 1035 | 1036 | // format date using native date object 1037 | function formatMoment(m, format) { 1038 | if (!m.isValid()) { 1039 | return m.localeData().invalidDate(); 1040 | } 1041 | 1042 | format = expandFormat(format, m.localeData()); 1043 | 1044 | if (!formatFunctions[format]) { 1045 | formatFunctions[format] = makeFormatFunction(format); 1046 | } 1047 | 1048 | return formatFunctions[format](m); 1049 | } 1050 | 1051 | function expandFormat(format, locale) { 1052 | var i = 5; 1053 | 1054 | function replaceLongDateFormatTokens(input) { 1055 | return locale.longDateFormat(input) || input; 1056 | } 1057 | 1058 | localFormattingTokens.lastIndex = 0; 1059 | while (i >= 0 && localFormattingTokens.test(format)) { 1060 | format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); 1061 | localFormattingTokens.lastIndex = 0; 1062 | i -= 1; 1063 | } 1064 | 1065 | return format; 1066 | } 1067 | 1068 | 1069 | /************************************ 1070 | Parsing 1071 | ************************************/ 1072 | 1073 | 1074 | // get the regex to find the next token 1075 | function getParseRegexForToken(token, config) { 1076 | var a, strict = config._strict; 1077 | switch (token) { 1078 | case 'Q': 1079 | return parseTokenOneDigit; 1080 | case 'DDDD': 1081 | return parseTokenThreeDigits; 1082 | case 'YYYY': 1083 | case 'GGGG': 1084 | case 'gggg': 1085 | return strict ? parseTokenFourDigits : parseTokenOneToFourDigits; 1086 | case 'Y': 1087 | case 'G': 1088 | case 'g': 1089 | return parseTokenSignedNumber; 1090 | case 'YYYYYY': 1091 | case 'YYYYY': 1092 | case 'GGGGG': 1093 | case 'ggggg': 1094 | return strict ? parseTokenSixDigits : parseTokenOneToSixDigits; 1095 | case 'S': 1096 | if (strict) { 1097 | return parseTokenOneDigit; 1098 | } 1099 | /* falls through */ 1100 | case 'SS': 1101 | if (strict) { 1102 | return parseTokenTwoDigits; 1103 | } 1104 | /* falls through */ 1105 | case 'SSS': 1106 | if (strict) { 1107 | return parseTokenThreeDigits; 1108 | } 1109 | /* falls through */ 1110 | case 'DDD': 1111 | return parseTokenOneToThreeDigits; 1112 | case 'MMM': 1113 | case 'MMMM': 1114 | case 'dd': 1115 | case 'ddd': 1116 | case 'dddd': 1117 | return parseTokenWord; 1118 | case 'a': 1119 | case 'A': 1120 | return config._locale._meridiemParse; 1121 | case 'x': 1122 | return parseTokenOffsetMs; 1123 | case 'X': 1124 | return parseTokenTimestampMs; 1125 | case 'Z': 1126 | case 'ZZ': 1127 | return parseTokenTimezone; 1128 | case 'T': 1129 | return parseTokenT; 1130 | case 'SSSS': 1131 | return parseTokenDigits; 1132 | case 'MM': 1133 | case 'DD': 1134 | case 'YY': 1135 | case 'GG': 1136 | case 'gg': 1137 | case 'HH': 1138 | case 'hh': 1139 | case 'mm': 1140 | case 'ss': 1141 | case 'ww': 1142 | case 'WW': 1143 | return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits; 1144 | case 'M': 1145 | case 'D': 1146 | case 'd': 1147 | case 'H': 1148 | case 'h': 1149 | case 'm': 1150 | case 's': 1151 | case 'w': 1152 | case 'W': 1153 | case 'e': 1154 | case 'E': 1155 | return parseTokenOneOrTwoDigits; 1156 | case 'Do': 1157 | return strict ? config._locale._ordinalParse : config._locale._ordinalParseLenient; 1158 | default : 1159 | a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i')); 1160 | return a; 1161 | } 1162 | } 1163 | 1164 | function timezoneMinutesFromString(string) { 1165 | string = string || ''; 1166 | var possibleTzMatches = (string.match(parseTokenTimezone) || []), 1167 | tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [], 1168 | parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0], 1169 | minutes = +(parts[1] * 60) + toInt(parts[2]); 1170 | 1171 | return parts[0] === '+' ? -minutes : minutes; 1172 | } 1173 | 1174 | // function to convert string input to date 1175 | function addTimeToArrayFromToken(token, input, config) { 1176 | var a, datePartArray = config._a; 1177 | 1178 | switch (token) { 1179 | // QUARTER 1180 | case 'Q': 1181 | if (input != null) { 1182 | datePartArray[MONTH] = (toInt(input) - 1) * 3; 1183 | } 1184 | break; 1185 | // MONTH 1186 | case 'M' : // fall through to MM 1187 | case 'MM' : 1188 | if (input != null) { 1189 | datePartArray[MONTH] = toInt(input) - 1; 1190 | } 1191 | break; 1192 | case 'MMM' : // fall through to MMMM 1193 | case 'MMMM' : 1194 | a = config._locale.monthsParse(input, token, config._strict); 1195 | // if we didn't find a month name, mark the date as invalid. 1196 | if (a != null) { 1197 | datePartArray[MONTH] = a; 1198 | } else { 1199 | config._pf.invalidMonth = input; 1200 | } 1201 | break; 1202 | // DAY OF MONTH 1203 | case 'D' : // fall through to DD 1204 | case 'DD' : 1205 | if (input != null) { 1206 | datePartArray[DATE] = toInt(input); 1207 | } 1208 | break; 1209 | case 'Do' : 1210 | if (input != null) { 1211 | datePartArray[DATE] = toInt(parseInt( 1212 | input.match(/\d{1,2}/)[0], 10)); 1213 | } 1214 | break; 1215 | // DAY OF YEAR 1216 | case 'DDD' : // fall through to DDDD 1217 | case 'DDDD' : 1218 | if (input != null) { 1219 | config._dayOfYear = toInt(input); 1220 | } 1221 | 1222 | break; 1223 | // YEAR 1224 | case 'YY' : 1225 | datePartArray[YEAR] = moment.parseTwoDigitYear(input); 1226 | break; 1227 | case 'YYYY' : 1228 | case 'YYYYY' : 1229 | case 'YYYYYY' : 1230 | datePartArray[YEAR] = toInt(input); 1231 | break; 1232 | // AM / PM 1233 | case 'a' : // fall through to A 1234 | case 'A' : 1235 | config._isPm = config._locale.isPM(input); 1236 | break; 1237 | // HOUR 1238 | case 'h' : // fall through to hh 1239 | case 'hh' : 1240 | config._pf.bigHour = true; 1241 | /* falls through */ 1242 | case 'H' : // fall through to HH 1243 | case 'HH' : 1244 | datePartArray[HOUR] = toInt(input); 1245 | break; 1246 | // MINUTE 1247 | case 'm' : // fall through to mm 1248 | case 'mm' : 1249 | datePartArray[MINUTE] = toInt(input); 1250 | break; 1251 | // SECOND 1252 | case 's' : // fall through to ss 1253 | case 'ss' : 1254 | datePartArray[SECOND] = toInt(input); 1255 | break; 1256 | // MILLISECOND 1257 | case 'S' : 1258 | case 'SS' : 1259 | case 'SSS' : 1260 | case 'SSSS' : 1261 | datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000); 1262 | break; 1263 | // UNIX OFFSET (MILLISECONDS) 1264 | case 'x': 1265 | config._d = new Date(toInt(input)); 1266 | break; 1267 | // UNIX TIMESTAMP WITH MS 1268 | case 'X': 1269 | config._d = new Date(parseFloat(input) * 1000); 1270 | break; 1271 | // TIMEZONE 1272 | case 'Z' : // fall through to ZZ 1273 | case 'ZZ' : 1274 | config._useUTC = true; 1275 | config._tzm = timezoneMinutesFromString(input); 1276 | break; 1277 | // WEEKDAY - human 1278 | case 'dd': 1279 | case 'ddd': 1280 | case 'dddd': 1281 | a = config._locale.weekdaysParse(input); 1282 | // if we didn't get a weekday name, mark the date as invalid 1283 | if (a != null) { 1284 | config._w = config._w || {}; 1285 | config._w['d'] = a; 1286 | } else { 1287 | config._pf.invalidWeekday = input; 1288 | } 1289 | break; 1290 | // WEEK, WEEK DAY - numeric 1291 | case 'w': 1292 | case 'ww': 1293 | case 'W': 1294 | case 'WW': 1295 | case 'd': 1296 | case 'e': 1297 | case 'E': 1298 | token = token.substr(0, 1); 1299 | /* falls through */ 1300 | case 'gggg': 1301 | case 'GGGG': 1302 | case 'GGGGG': 1303 | token = token.substr(0, 2); 1304 | if (input) { 1305 | config._w = config._w || {}; 1306 | config._w[token] = toInt(input); 1307 | } 1308 | break; 1309 | case 'gg': 1310 | case 'GG': 1311 | config._w = config._w || {}; 1312 | config._w[token] = moment.parseTwoDigitYear(input); 1313 | } 1314 | } 1315 | 1316 | function dayOfYearFromWeekInfo(config) { 1317 | var w, weekYear, week, weekday, dow, doy, temp; 1318 | 1319 | w = config._w; 1320 | if (w.GG != null || w.W != null || w.E != null) { 1321 | dow = 1; 1322 | doy = 4; 1323 | 1324 | // TODO: We need to take the current isoWeekYear, but that depends on 1325 | // how we interpret now (local, utc, fixed offset). So create 1326 | // a now version of current config (take local/utc/offset flags, and 1327 | // create now). 1328 | weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year); 1329 | week = dfl(w.W, 1); 1330 | weekday = dfl(w.E, 1); 1331 | } else { 1332 | dow = config._locale._week.dow; 1333 | doy = config._locale._week.doy; 1334 | 1335 | weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year); 1336 | week = dfl(w.w, 1); 1337 | 1338 | if (w.d != null) { 1339 | // weekday -- low day numbers are considered next week 1340 | weekday = w.d; 1341 | if (weekday < dow) { 1342 | ++week; 1343 | } 1344 | } else if (w.e != null) { 1345 | // local weekday -- counting starts from begining of week 1346 | weekday = w.e + dow; 1347 | } else { 1348 | // default to begining of week 1349 | weekday = dow; 1350 | } 1351 | } 1352 | temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow); 1353 | 1354 | config._a[YEAR] = temp.year; 1355 | config._dayOfYear = temp.dayOfYear; 1356 | } 1357 | 1358 | // convert an array to a date. 1359 | // the array should mirror the parameters below 1360 | // note: all values past the year are optional and will default to the lowest possible value. 1361 | // [year, month, day , hour, minute, second, millisecond] 1362 | function dateFromConfig(config) { 1363 | var i, date, input = [], currentDate, yearToUse; 1364 | 1365 | if (config._d) { 1366 | return; 1367 | } 1368 | 1369 | currentDate = currentDateArray(config); 1370 | 1371 | //compute day of the year from weeks and weekdays 1372 | if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { 1373 | dayOfYearFromWeekInfo(config); 1374 | } 1375 | 1376 | //if the day of the year is set, figure out what it is 1377 | if (config._dayOfYear) { 1378 | yearToUse = dfl(config._a[YEAR], currentDate[YEAR]); 1379 | 1380 | if (config._dayOfYear > daysInYear(yearToUse)) { 1381 | config._pf._overflowDayOfYear = true; 1382 | } 1383 | 1384 | date = makeUTCDate(yearToUse, 0, config._dayOfYear); 1385 | config._a[MONTH] = date.getUTCMonth(); 1386 | config._a[DATE] = date.getUTCDate(); 1387 | } 1388 | 1389 | // Default to current date. 1390 | // * if no year, month, day of month are given, default to today 1391 | // * if day of month is given, default month and year 1392 | // * if month is given, default only year 1393 | // * if year is given, don't default anything 1394 | for (i = 0; i < 3 && config._a[i] == null; ++i) { 1395 | config._a[i] = input[i] = currentDate[i]; 1396 | } 1397 | 1398 | // Zero out whatever was not defaulted, including time 1399 | for (; i < 7; i++) { 1400 | config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; 1401 | } 1402 | 1403 | // Check for 24:00:00.000 1404 | if (config._a[HOUR] === 24 && 1405 | config._a[MINUTE] === 0 && 1406 | config._a[SECOND] === 0 && 1407 | config._a[MILLISECOND] === 0) { 1408 | config._nextDay = true; 1409 | config._a[HOUR] = 0; 1410 | } 1411 | 1412 | config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input); 1413 | // Apply timezone offset from input. The actual zone can be changed 1414 | // with parseZone. 1415 | if (config._tzm != null) { 1416 | config._d.setUTCMinutes(config._d.getUTCMinutes() + config._tzm); 1417 | } 1418 | 1419 | if (config._nextDay) { 1420 | config._a[HOUR] = 24; 1421 | } 1422 | } 1423 | 1424 | function dateFromObject(config) { 1425 | var normalizedInput; 1426 | 1427 | if (config._d) { 1428 | return; 1429 | } 1430 | 1431 | normalizedInput = normalizeObjectUnits(config._i); 1432 | config._a = [ 1433 | normalizedInput.year, 1434 | normalizedInput.month, 1435 | normalizedInput.day || normalizedInput.date, 1436 | normalizedInput.hour, 1437 | normalizedInput.minute, 1438 | normalizedInput.second, 1439 | normalizedInput.millisecond 1440 | ]; 1441 | 1442 | dateFromConfig(config); 1443 | } 1444 | 1445 | function currentDateArray(config) { 1446 | var now = new Date(); 1447 | if (config._useUTC) { 1448 | return [ 1449 | now.getUTCFullYear(), 1450 | now.getUTCMonth(), 1451 | now.getUTCDate() 1452 | ]; 1453 | } else { 1454 | return [now.getFullYear(), now.getMonth(), now.getDate()]; 1455 | } 1456 | } 1457 | 1458 | // date from string and format string 1459 | function makeDateFromStringAndFormat(config) { 1460 | if (config._f === moment.ISO_8601) { 1461 | parseISO(config); 1462 | return; 1463 | } 1464 | 1465 | config._a = []; 1466 | config._pf.empty = true; 1467 | 1468 | // This array is used to make a Date, either with `new Date` or `Date.UTC` 1469 | var string = '' + config._i, 1470 | i, parsedInput, tokens, token, skipped, 1471 | stringLength = string.length, 1472 | totalParsedInputLength = 0; 1473 | 1474 | tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; 1475 | 1476 | for (i = 0; i < tokens.length; i++) { 1477 | token = tokens[i]; 1478 | parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; 1479 | if (parsedInput) { 1480 | skipped = string.substr(0, string.indexOf(parsedInput)); 1481 | if (skipped.length > 0) { 1482 | config._pf.unusedInput.push(skipped); 1483 | } 1484 | string = string.slice(string.indexOf(parsedInput) + parsedInput.length); 1485 | totalParsedInputLength += parsedInput.length; 1486 | } 1487 | // don't parse if it's not a known token 1488 | if (formatTokenFunctions[token]) { 1489 | if (parsedInput) { 1490 | config._pf.empty = false; 1491 | } 1492 | else { 1493 | config._pf.unusedTokens.push(token); 1494 | } 1495 | addTimeToArrayFromToken(token, parsedInput, config); 1496 | } 1497 | else if (config._strict && !parsedInput) { 1498 | config._pf.unusedTokens.push(token); 1499 | } 1500 | } 1501 | 1502 | // add remaining unparsed input length to the string 1503 | config._pf.charsLeftOver = stringLength - totalParsedInputLength; 1504 | if (string.length > 0) { 1505 | config._pf.unusedInput.push(string); 1506 | } 1507 | 1508 | // clear _12h flag if hour is <= 12 1509 | if (config._pf.bigHour === true && config._a[HOUR] <= 12) { 1510 | config._pf.bigHour = undefined; 1511 | } 1512 | // handle am pm 1513 | if (config._isPm && config._a[HOUR] < 12) { 1514 | config._a[HOUR] += 12; 1515 | } 1516 | // if is 12 am, change hours to 0 1517 | if (config._isPm === false && config._a[HOUR] === 12) { 1518 | config._a[HOUR] = 0; 1519 | } 1520 | dateFromConfig(config); 1521 | checkOverflow(config); 1522 | } 1523 | 1524 | function unescapeFormat(s) { 1525 | return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { 1526 | return p1 || p2 || p3 || p4; 1527 | }); 1528 | } 1529 | 1530 | // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript 1531 | function regexpEscape(s) { 1532 | return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); 1533 | } 1534 | 1535 | // date from string and array of format strings 1536 | function makeDateFromStringAndArray(config) { 1537 | var tempConfig, 1538 | bestMoment, 1539 | 1540 | scoreToBeat, 1541 | i, 1542 | currentScore; 1543 | 1544 | if (config._f.length === 0) { 1545 | config._pf.invalidFormat = true; 1546 | config._d = new Date(NaN); 1547 | return; 1548 | } 1549 | 1550 | for (i = 0; i < config._f.length; i++) { 1551 | currentScore = 0; 1552 | tempConfig = copyConfig({}, config); 1553 | if (config._useUTC != null) { 1554 | tempConfig._useUTC = config._useUTC; 1555 | } 1556 | tempConfig._pf = defaultParsingFlags(); 1557 | tempConfig._f = config._f[i]; 1558 | makeDateFromStringAndFormat(tempConfig); 1559 | 1560 | if (!isValid(tempConfig)) { 1561 | continue; 1562 | } 1563 | 1564 | // if there is any input that was not parsed add a penalty for that format 1565 | currentScore += tempConfig._pf.charsLeftOver; 1566 | 1567 | //or tokens 1568 | currentScore += tempConfig._pf.unusedTokens.length * 10; 1569 | 1570 | tempConfig._pf.score = currentScore; 1571 | 1572 | if (scoreToBeat == null || currentScore < scoreToBeat) { 1573 | scoreToBeat = currentScore; 1574 | bestMoment = tempConfig; 1575 | } 1576 | } 1577 | 1578 | extend(config, bestMoment || tempConfig); 1579 | } 1580 | 1581 | // date from iso format 1582 | function parseISO(config) { 1583 | var i, l, 1584 | string = config._i, 1585 | match = isoRegex.exec(string); 1586 | 1587 | if (match) { 1588 | config._pf.iso = true; 1589 | for (i = 0, l = isoDates.length; i < l; i++) { 1590 | if (isoDates[i][1].exec(string)) { 1591 | // match[5] should be 'T' or undefined 1592 | config._f = isoDates[i][0] + (match[6] || ' '); 1593 | break; 1594 | } 1595 | } 1596 | for (i = 0, l = isoTimes.length; i < l; i++) { 1597 | if (isoTimes[i][1].exec(string)) { 1598 | config._f += isoTimes[i][0]; 1599 | break; 1600 | } 1601 | } 1602 | if (string.match(parseTokenTimezone)) { 1603 | config._f += 'Z'; 1604 | } 1605 | makeDateFromStringAndFormat(config); 1606 | } else { 1607 | config._isValid = false; 1608 | } 1609 | } 1610 | 1611 | // date from iso format or fallback 1612 | function makeDateFromString(config) { 1613 | parseISO(config); 1614 | if (config._isValid === false) { 1615 | delete config._isValid; 1616 | moment.createFromInputFallback(config); 1617 | } 1618 | } 1619 | 1620 | function map(arr, fn) { 1621 | var res = [], i; 1622 | for (i = 0; i < arr.length; ++i) { 1623 | res.push(fn(arr[i], i)); 1624 | } 1625 | return res; 1626 | } 1627 | 1628 | function makeDateFromInput(config) { 1629 | var input = config._i, matched; 1630 | if (input === undefined) { 1631 | config._d = new Date(); 1632 | } else if (isDate(input)) { 1633 | config._d = new Date(+input); 1634 | } else if ((matched = aspNetJsonRegex.exec(input)) !== null) { 1635 | config._d = new Date(+matched[1]); 1636 | } else if (typeof input === 'string') { 1637 | makeDateFromString(config); 1638 | } else if (isArray(input)) { 1639 | config._a = map(input.slice(0), function (obj) { 1640 | return parseInt(obj, 10); 1641 | }); 1642 | dateFromConfig(config); 1643 | } else if (typeof(input) === 'object') { 1644 | dateFromObject(config); 1645 | } else if (typeof(input) === 'number') { 1646 | // from milliseconds 1647 | config._d = new Date(input); 1648 | } else { 1649 | moment.createFromInputFallback(config); 1650 | } 1651 | } 1652 | 1653 | function makeDate(y, m, d, h, M, s, ms) { 1654 | //can't just apply() to create a date: 1655 | //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply 1656 | var date = new Date(y, m, d, h, M, s, ms); 1657 | 1658 | //the date constructor doesn't accept years < 1970 1659 | if (y < 1970) { 1660 | date.setFullYear(y); 1661 | } 1662 | return date; 1663 | } 1664 | 1665 | function makeUTCDate(y) { 1666 | var date = new Date(Date.UTC.apply(null, arguments)); 1667 | if (y < 1970) { 1668 | date.setUTCFullYear(y); 1669 | } 1670 | return date; 1671 | } 1672 | 1673 | function parseWeekday(input, locale) { 1674 | if (typeof input === 'string') { 1675 | if (!isNaN(input)) { 1676 | input = parseInt(input, 10); 1677 | } 1678 | else { 1679 | input = locale.weekdaysParse(input); 1680 | if (typeof input !== 'number') { 1681 | return null; 1682 | } 1683 | } 1684 | } 1685 | return input; 1686 | } 1687 | 1688 | /************************************ 1689 | Relative Time 1690 | ************************************/ 1691 | 1692 | 1693 | // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize 1694 | function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { 1695 | return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); 1696 | } 1697 | 1698 | function relativeTime(posNegDuration, withoutSuffix, locale) { 1699 | var duration = moment.duration(posNegDuration).abs(), 1700 | seconds = round(duration.as('s')), 1701 | minutes = round(duration.as('m')), 1702 | hours = round(duration.as('h')), 1703 | days = round(duration.as('d')), 1704 | months = round(duration.as('M')), 1705 | years = round(duration.as('y')), 1706 | 1707 | args = seconds < relativeTimeThresholds.s && ['s', seconds] || 1708 | minutes === 1 && ['m'] || 1709 | minutes < relativeTimeThresholds.m && ['mm', minutes] || 1710 | hours === 1 && ['h'] || 1711 | hours < relativeTimeThresholds.h && ['hh', hours] || 1712 | days === 1 && ['d'] || 1713 | days < relativeTimeThresholds.d && ['dd', days] || 1714 | months === 1 && ['M'] || 1715 | months < relativeTimeThresholds.M && ['MM', months] || 1716 | years === 1 && ['y'] || ['yy', years]; 1717 | 1718 | args[2] = withoutSuffix; 1719 | args[3] = +posNegDuration > 0; 1720 | args[4] = locale; 1721 | return substituteTimeAgo.apply({}, args); 1722 | } 1723 | 1724 | 1725 | /************************************ 1726 | Week of Year 1727 | ************************************/ 1728 | 1729 | 1730 | // firstDayOfWeek 0 = sun, 6 = sat 1731 | // the day of the week that starts the week 1732 | // (usually sunday or monday) 1733 | // firstDayOfWeekOfYear 0 = sun, 6 = sat 1734 | // the first week is the week that contains the first 1735 | // of this day of the week 1736 | // (eg. ISO weeks use thursday (4)) 1737 | function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { 1738 | var end = firstDayOfWeekOfYear - firstDayOfWeek, 1739 | daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), 1740 | adjustedMoment; 1741 | 1742 | 1743 | if (daysToDayOfWeek > end) { 1744 | daysToDayOfWeek -= 7; 1745 | } 1746 | 1747 | if (daysToDayOfWeek < end - 7) { 1748 | daysToDayOfWeek += 7; 1749 | } 1750 | 1751 | adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd'); 1752 | return { 1753 | week: Math.ceil(adjustedMoment.dayOfYear() / 7), 1754 | year: adjustedMoment.year() 1755 | }; 1756 | } 1757 | 1758 | //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday 1759 | function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { 1760 | var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear; 1761 | 1762 | d = d === 0 ? 7 : d; 1763 | weekday = weekday != null ? weekday : firstDayOfWeek; 1764 | daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); 1765 | dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; 1766 | 1767 | return { 1768 | year: dayOfYear > 0 ? year : year - 1, 1769 | dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear 1770 | }; 1771 | } 1772 | 1773 | /************************************ 1774 | Top Level Functions 1775 | ************************************/ 1776 | 1777 | function makeMoment(config) { 1778 | var input = config._i, 1779 | format = config._f, 1780 | res; 1781 | 1782 | config._locale = config._locale || moment.localeData(config._l); 1783 | 1784 | if (input === null || (format === undefined && input === '')) { 1785 | return moment.invalid({nullInput: true}); 1786 | } 1787 | 1788 | if (typeof input === 'string') { 1789 | config._i = input = config._locale.preparse(input); 1790 | } 1791 | 1792 | if (moment.isMoment(input)) { 1793 | return new Moment(input, true); 1794 | } else if (format) { 1795 | if (isArray(format)) { 1796 | makeDateFromStringAndArray(config); 1797 | } else { 1798 | makeDateFromStringAndFormat(config); 1799 | } 1800 | } else { 1801 | makeDateFromInput(config); 1802 | } 1803 | 1804 | res = new Moment(config); 1805 | if (res._nextDay) { 1806 | // Adding is smart enough around DST 1807 | res.add(1, 'd'); 1808 | res._nextDay = undefined; 1809 | } 1810 | 1811 | return res; 1812 | } 1813 | 1814 | moment = function (input, format, locale, strict) { 1815 | var c; 1816 | 1817 | if (typeof(locale) === 'boolean') { 1818 | strict = locale; 1819 | locale = undefined; 1820 | } 1821 | // object construction must be done this way. 1822 | // https://github.com/moment/moment/issues/1423 1823 | c = {}; 1824 | c._isAMomentObject = true; 1825 | c._i = input; 1826 | c._f = format; 1827 | c._l = locale; 1828 | c._strict = strict; 1829 | c._isUTC = false; 1830 | c._pf = defaultParsingFlags(); 1831 | 1832 | return makeMoment(c); 1833 | }; 1834 | 1835 | moment.suppressDeprecationWarnings = false; 1836 | 1837 | moment.createFromInputFallback = deprecate( 1838 | 'moment construction falls back to js Date. This is ' + 1839 | 'discouraged and will be removed in upcoming major ' + 1840 | 'release. Please refer to ' + 1841 | 'https://github.com/moment/moment/issues/1407 for more info.', 1842 | function (config) { 1843 | config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); 1844 | } 1845 | ); 1846 | 1847 | // Pick a moment m from moments so that m[fn](other) is true for all 1848 | // other. This relies on the function fn to be transitive. 1849 | // 1850 | // moments should either be an array of moment objects or an array, whose 1851 | // first element is an array of moment objects. 1852 | function pickBy(fn, moments) { 1853 | var res, i; 1854 | if (moments.length === 1 && isArray(moments[0])) { 1855 | moments = moments[0]; 1856 | } 1857 | if (!moments.length) { 1858 | return moment(); 1859 | } 1860 | res = moments[0]; 1861 | for (i = 1; i < moments.length; ++i) { 1862 | if (moments[i][fn](res)) { 1863 | res = moments[i]; 1864 | } 1865 | } 1866 | return res; 1867 | } 1868 | 1869 | moment.min = function () { 1870 | var args = [].slice.call(arguments, 0); 1871 | 1872 | return pickBy('isBefore', args); 1873 | }; 1874 | 1875 | moment.max = function () { 1876 | var args = [].slice.call(arguments, 0); 1877 | 1878 | return pickBy('isAfter', args); 1879 | }; 1880 | 1881 | // creating with utc 1882 | moment.utc = function (input, format, locale, strict) { 1883 | var c; 1884 | 1885 | if (typeof(locale) === 'boolean') { 1886 | strict = locale; 1887 | locale = undefined; 1888 | } 1889 | // object construction must be done this way. 1890 | // https://github.com/moment/moment/issues/1423 1891 | c = {}; 1892 | c._isAMomentObject = true; 1893 | c._useUTC = true; 1894 | c._isUTC = true; 1895 | c._l = locale; 1896 | c._i = input; 1897 | c._f = format; 1898 | c._strict = strict; 1899 | c._pf = defaultParsingFlags(); 1900 | 1901 | return makeMoment(c).utc(); 1902 | }; 1903 | 1904 | // creating with unix timestamp (in seconds) 1905 | moment.unix = function (input) { 1906 | return moment(input * 1000); 1907 | }; 1908 | 1909 | // duration 1910 | moment.duration = function (input, key) { 1911 | var duration = input, 1912 | // matching against regexp is expensive, do it on demand 1913 | match = null, 1914 | sign, 1915 | ret, 1916 | parseIso, 1917 | diffRes; 1918 | 1919 | if (moment.isDuration(input)) { 1920 | duration = { 1921 | ms: input._milliseconds, 1922 | d: input._days, 1923 | M: input._months 1924 | }; 1925 | } else if (typeof input === 'number') { 1926 | duration = {}; 1927 | if (key) { 1928 | duration[key] = input; 1929 | } else { 1930 | duration.milliseconds = input; 1931 | } 1932 | } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) { 1933 | sign = (match[1] === '-') ? -1 : 1; 1934 | duration = { 1935 | y: 0, 1936 | d: toInt(match[DATE]) * sign, 1937 | h: toInt(match[HOUR]) * sign, 1938 | m: toInt(match[MINUTE]) * sign, 1939 | s: toInt(match[SECOND]) * sign, 1940 | ms: toInt(match[MILLISECOND]) * sign 1941 | }; 1942 | } else if (!!(match = isoDurationRegex.exec(input))) { 1943 | sign = (match[1] === '-') ? -1 : 1; 1944 | parseIso = function (inp) { 1945 | // We'd normally use ~~inp for this, but unfortunately it also 1946 | // converts floats to ints. 1947 | // inp may be undefined, so careful calling replace on it. 1948 | var res = inp && parseFloat(inp.replace(',', '.')); 1949 | // apply sign while we're at it 1950 | return (isNaN(res) ? 0 : res) * sign; 1951 | }; 1952 | duration = { 1953 | y: parseIso(match[2]), 1954 | M: parseIso(match[3]), 1955 | d: parseIso(match[4]), 1956 | h: parseIso(match[5]), 1957 | m: parseIso(match[6]), 1958 | s: parseIso(match[7]), 1959 | w: parseIso(match[8]) 1960 | }; 1961 | } else if (typeof duration === 'object' && 1962 | ('from' in duration || 'to' in duration)) { 1963 | diffRes = momentsDifference(moment(duration.from), moment(duration.to)); 1964 | 1965 | duration = {}; 1966 | duration.ms = diffRes.milliseconds; 1967 | duration.M = diffRes.months; 1968 | } 1969 | 1970 | ret = new Duration(duration); 1971 | 1972 | if (moment.isDuration(input) && hasOwnProp(input, '_locale')) { 1973 | ret._locale = input._locale; 1974 | } 1975 | 1976 | return ret; 1977 | }; 1978 | 1979 | // version number 1980 | moment.version = VERSION; 1981 | 1982 | // default format 1983 | moment.defaultFormat = isoFormat; 1984 | 1985 | // constant that refers to the ISO standard 1986 | moment.ISO_8601 = function () {}; 1987 | 1988 | // Plugins that add properties should also add the key here (null value), 1989 | // so we can properly clone ourselves. 1990 | moment.momentProperties = momentProperties; 1991 | 1992 | // This function will be called whenever a moment is mutated. 1993 | // It is intended to keep the offset in sync with the timezone. 1994 | moment.updateOffset = function () {}; 1995 | 1996 | // This function allows you to set a threshold for relative time strings 1997 | moment.relativeTimeThreshold = function (threshold, limit) { 1998 | if (relativeTimeThresholds[threshold] === undefined) { 1999 | return false; 2000 | } 2001 | if (limit === undefined) { 2002 | return relativeTimeThresholds[threshold]; 2003 | } 2004 | relativeTimeThresholds[threshold] = limit; 2005 | return true; 2006 | }; 2007 | 2008 | moment.lang = deprecate( 2009 | 'moment.lang is deprecated. Use moment.locale instead.', 2010 | function (key, value) { 2011 | return moment.locale(key, value); 2012 | } 2013 | ); 2014 | 2015 | // This function will load locale and then set the global locale. If 2016 | // no arguments are passed in, it will simply return the current global 2017 | // locale key. 2018 | moment.locale = function (key, values) { 2019 | var data; 2020 | if (key) { 2021 | if (typeof(values) !== 'undefined') { 2022 | data = moment.defineLocale(key, values); 2023 | } 2024 | else { 2025 | data = moment.localeData(key); 2026 | } 2027 | 2028 | if (data) { 2029 | moment.duration._locale = moment._locale = data; 2030 | } 2031 | } 2032 | 2033 | return moment._locale._abbr; 2034 | }; 2035 | 2036 | moment.defineLocale = function (name, values) { 2037 | if (values !== null) { 2038 | values.abbr = name; 2039 | if (!locales[name]) { 2040 | locales[name] = new Locale(); 2041 | } 2042 | locales[name].set(values); 2043 | 2044 | // backwards compat for now: also set the locale 2045 | moment.locale(name); 2046 | 2047 | return locales[name]; 2048 | } else { 2049 | // useful for testing 2050 | delete locales[name]; 2051 | return null; 2052 | } 2053 | }; 2054 | 2055 | moment.langData = deprecate( 2056 | 'moment.langData is deprecated. Use moment.localeData instead.', 2057 | function (key) { 2058 | return moment.localeData(key); 2059 | } 2060 | ); 2061 | 2062 | // returns locale data 2063 | moment.localeData = function (key) { 2064 | var locale; 2065 | 2066 | if (key && key._locale && key._locale._abbr) { 2067 | key = key._locale._abbr; 2068 | } 2069 | 2070 | if (!key) { 2071 | return moment._locale; 2072 | } 2073 | 2074 | if (!isArray(key)) { 2075 | //short-circuit everything else 2076 | locale = loadLocale(key); 2077 | if (locale) { 2078 | return locale; 2079 | } 2080 | key = [key]; 2081 | } 2082 | 2083 | return chooseLocale(key); 2084 | }; 2085 | 2086 | // compare moment object 2087 | moment.isMoment = function (obj) { 2088 | return obj instanceof Moment || 2089 | (obj != null && hasOwnProp(obj, '_isAMomentObject')); 2090 | }; 2091 | 2092 | // for typechecking Duration objects 2093 | moment.isDuration = function (obj) { 2094 | return obj instanceof Duration; 2095 | }; 2096 | 2097 | for (i = lists.length - 1; i >= 0; --i) { 2098 | makeList(lists[i]); 2099 | } 2100 | 2101 | moment.normalizeUnits = function (units) { 2102 | return normalizeUnits(units); 2103 | }; 2104 | 2105 | moment.invalid = function (flags) { 2106 | var m = moment.utc(NaN); 2107 | if (flags != null) { 2108 | extend(m._pf, flags); 2109 | } 2110 | else { 2111 | m._pf.userInvalidated = true; 2112 | } 2113 | 2114 | return m; 2115 | }; 2116 | 2117 | moment.parseZone = function () { 2118 | return moment.apply(null, arguments).parseZone(); 2119 | }; 2120 | 2121 | moment.parseTwoDigitYear = function (input) { 2122 | return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); 2123 | }; 2124 | 2125 | /************************************ 2126 | Moment Prototype 2127 | ************************************/ 2128 | 2129 | 2130 | extend(moment.fn = Moment.prototype, { 2131 | 2132 | clone : function () { 2133 | return moment(this); 2134 | }, 2135 | 2136 | valueOf : function () { 2137 | return +this._d + ((this._offset || 0) * 60000); 2138 | }, 2139 | 2140 | unix : function () { 2141 | return Math.floor(+this / 1000); 2142 | }, 2143 | 2144 | toString : function () { 2145 | return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); 2146 | }, 2147 | 2148 | toDate : function () { 2149 | return this._offset ? new Date(+this) : this._d; 2150 | }, 2151 | 2152 | toISOString : function () { 2153 | var m = moment(this).utc(); 2154 | if (0 < m.year() && m.year() <= 9999) { 2155 | if ('function' === typeof Date.prototype.toISOString) { 2156 | // native implementation is ~50x faster, use it when we can 2157 | return this.toDate().toISOString(); 2158 | } else { 2159 | return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); 2160 | } 2161 | } else { 2162 | return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); 2163 | } 2164 | }, 2165 | 2166 | toArray : function () { 2167 | var m = this; 2168 | return [ 2169 | m.year(), 2170 | m.month(), 2171 | m.date(), 2172 | m.hours(), 2173 | m.minutes(), 2174 | m.seconds(), 2175 | m.milliseconds() 2176 | ]; 2177 | }, 2178 | 2179 | isValid : function () { 2180 | return isValid(this); 2181 | }, 2182 | 2183 | isDSTShifted : function () { 2184 | if (this._a) { 2185 | return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; 2186 | } 2187 | 2188 | return false; 2189 | }, 2190 | 2191 | parsingFlags : function () { 2192 | return extend({}, this._pf); 2193 | }, 2194 | 2195 | invalidAt: function () { 2196 | return this._pf.overflow; 2197 | }, 2198 | 2199 | utc : function (keepLocalTime) { 2200 | return this.zone(0, keepLocalTime); 2201 | }, 2202 | 2203 | local : function (keepLocalTime) { 2204 | if (this._isUTC) { 2205 | this.zone(0, keepLocalTime); 2206 | this._isUTC = false; 2207 | 2208 | if (keepLocalTime) { 2209 | this.add(this._dateTzOffset(), 'm'); 2210 | } 2211 | } 2212 | return this; 2213 | }, 2214 | 2215 | format : function (inputString) { 2216 | var output = formatMoment(this, inputString || moment.defaultFormat); 2217 | return this.localeData().postformat(output); 2218 | }, 2219 | 2220 | add : createAdder(1, 'add'), 2221 | 2222 | subtract : createAdder(-1, 'subtract'), 2223 | 2224 | diff : function (input, units, asFloat) { 2225 | var that = makeAs(input, this), 2226 | zoneDiff = (this.zone() - that.zone()) * 6e4, 2227 | diff, output, daysAdjust; 2228 | 2229 | units = normalizeUnits(units); 2230 | 2231 | if (units === 'year' || units === 'month') { 2232 | // average number of days in the months in the given dates 2233 | diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2 2234 | // difference in months 2235 | output = ((this.year() - that.year()) * 12) + (this.month() - that.month()); 2236 | // adjust by taking difference in days, average number of days 2237 | // and dst in the given months. 2238 | daysAdjust = (this - moment(this).startOf('month')) - 2239 | (that - moment(that).startOf('month')); 2240 | // same as above but with zones, to negate all dst 2241 | daysAdjust -= ((this.zone() - moment(this).startOf('month').zone()) - 2242 | (that.zone() - moment(that).startOf('month').zone())) * 6e4; 2243 | output += daysAdjust / diff; 2244 | if (units === 'year') { 2245 | output = output / 12; 2246 | } 2247 | } else { 2248 | diff = (this - that); 2249 | output = units === 'second' ? diff / 1e3 : // 1000 2250 | units === 'minute' ? diff / 6e4 : // 1000 * 60 2251 | units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 2252 | units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst 2253 | units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst 2254 | diff; 2255 | } 2256 | return asFloat ? output : absRound(output); 2257 | }, 2258 | 2259 | from : function (time, withoutSuffix) { 2260 | return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); 2261 | }, 2262 | 2263 | fromNow : function (withoutSuffix) { 2264 | return this.from(moment(), withoutSuffix); 2265 | }, 2266 | 2267 | calendar : function (time) { 2268 | // We want to compare the start of today, vs this. 2269 | // Getting start-of-today depends on whether we're zone'd or not. 2270 | var now = time || moment(), 2271 | sod = makeAs(now, this).startOf('day'), 2272 | diff = this.diff(sod, 'days', true), 2273 | format = diff < -6 ? 'sameElse' : 2274 | diff < -1 ? 'lastWeek' : 2275 | diff < 0 ? 'lastDay' : 2276 | diff < 1 ? 'sameDay' : 2277 | diff < 2 ? 'nextDay' : 2278 | diff < 7 ? 'nextWeek' : 'sameElse'; 2279 | return this.format(this.localeData().calendar(format, this, moment(now))); 2280 | }, 2281 | 2282 | isLeapYear : function () { 2283 | return isLeapYear(this.year()); 2284 | }, 2285 | 2286 | isDST : function () { 2287 | return (this.zone() < this.clone().month(0).zone() || 2288 | this.zone() < this.clone().month(5).zone()); 2289 | }, 2290 | 2291 | day : function (input) { 2292 | var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); 2293 | if (input != null) { 2294 | input = parseWeekday(input, this.localeData()); 2295 | return this.add(input - day, 'd'); 2296 | } else { 2297 | return day; 2298 | } 2299 | }, 2300 | 2301 | month : makeAccessor('Month', true), 2302 | 2303 | startOf : function (units) { 2304 | units = normalizeUnits(units); 2305 | // the following switch intentionally omits break keywords 2306 | // to utilize falling through the cases. 2307 | switch (units) { 2308 | case 'year': 2309 | this.month(0); 2310 | /* falls through */ 2311 | case 'quarter': 2312 | case 'month': 2313 | this.date(1); 2314 | /* falls through */ 2315 | case 'week': 2316 | case 'isoWeek': 2317 | case 'day': 2318 | this.hours(0); 2319 | /* falls through */ 2320 | case 'hour': 2321 | this.minutes(0); 2322 | /* falls through */ 2323 | case 'minute': 2324 | this.seconds(0); 2325 | /* falls through */ 2326 | case 'second': 2327 | this.milliseconds(0); 2328 | /* falls through */ 2329 | } 2330 | 2331 | // weeks are a special case 2332 | if (units === 'week') { 2333 | this.weekday(0); 2334 | } else if (units === 'isoWeek') { 2335 | this.isoWeekday(1); 2336 | } 2337 | 2338 | // quarters are also special 2339 | if (units === 'quarter') { 2340 | this.month(Math.floor(this.month() / 3) * 3); 2341 | } 2342 | 2343 | return this; 2344 | }, 2345 | 2346 | endOf: function (units) { 2347 | units = normalizeUnits(units); 2348 | if (units === undefined || units === 'millisecond') { 2349 | return this; 2350 | } 2351 | return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); 2352 | }, 2353 | 2354 | isAfter: function (input, units) { 2355 | var inputMs; 2356 | units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); 2357 | if (units === 'millisecond') { 2358 | input = moment.isMoment(input) ? input : moment(input); 2359 | return +this > +input; 2360 | } else { 2361 | inputMs = moment.isMoment(input) ? +input : +moment(input); 2362 | return inputMs < +this.clone().startOf(units); 2363 | } 2364 | }, 2365 | 2366 | isBefore: function (input, units) { 2367 | var inputMs; 2368 | units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); 2369 | if (units === 'millisecond') { 2370 | input = moment.isMoment(input) ? input : moment(input); 2371 | return +this < +input; 2372 | } else { 2373 | inputMs = moment.isMoment(input) ? +input : +moment(input); 2374 | return +this.clone().endOf(units) < inputMs; 2375 | } 2376 | }, 2377 | 2378 | isSame: function (input, units) { 2379 | var inputMs; 2380 | units = normalizeUnits(units || 'millisecond'); 2381 | if (units === 'millisecond') { 2382 | input = moment.isMoment(input) ? input : moment(input); 2383 | return +this === +input; 2384 | } else { 2385 | inputMs = +moment(input); 2386 | return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units)); 2387 | } 2388 | }, 2389 | 2390 | min: deprecate( 2391 | 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', 2392 | function (other) { 2393 | other = moment.apply(null, arguments); 2394 | return other < this ? this : other; 2395 | } 2396 | ), 2397 | 2398 | max: deprecate( 2399 | 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', 2400 | function (other) { 2401 | other = moment.apply(null, arguments); 2402 | return other > this ? this : other; 2403 | } 2404 | ), 2405 | 2406 | // keepLocalTime = true means only change the timezone, without 2407 | // affecting the local hour. So 5:31:26 +0300 --[zone(2, true)]--> 2408 | // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist int zone 2409 | // +0200, so we adjust the time as needed, to be valid. 2410 | // 2411 | // Keeping the time actually adds/subtracts (one hour) 2412 | // from the actual represented time. That is why we call updateOffset 2413 | // a second time. In case it wants us to change the offset again 2414 | // _changeInProgress == true case, then we have to adjust, because 2415 | // there is no such time in the given timezone. 2416 | zone : function (input, keepLocalTime) { 2417 | var offset = this._offset || 0, 2418 | localAdjust; 2419 | if (input != null) { 2420 | if (typeof input === 'string') { 2421 | input = timezoneMinutesFromString(input); 2422 | } 2423 | if (Math.abs(input) < 16) { 2424 | input = input * 60; 2425 | } 2426 | if (!this._isUTC && keepLocalTime) { 2427 | localAdjust = this._dateTzOffset(); 2428 | } 2429 | this._offset = input; 2430 | this._isUTC = true; 2431 | if (localAdjust != null) { 2432 | this.subtract(localAdjust, 'm'); 2433 | } 2434 | if (offset !== input) { 2435 | if (!keepLocalTime || this._changeInProgress) { 2436 | addOrSubtractDurationFromMoment(this, 2437 | moment.duration(offset - input, 'm'), 1, false); 2438 | } else if (!this._changeInProgress) { 2439 | this._changeInProgress = true; 2440 | moment.updateOffset(this, true); 2441 | this._changeInProgress = null; 2442 | } 2443 | } 2444 | } else { 2445 | return this._isUTC ? offset : this._dateTzOffset(); 2446 | } 2447 | return this; 2448 | }, 2449 | 2450 | zoneAbbr : function () { 2451 | return this._isUTC ? 'UTC' : ''; 2452 | }, 2453 | 2454 | zoneName : function () { 2455 | return this._isUTC ? 'Coordinated Universal Time' : ''; 2456 | }, 2457 | 2458 | parseZone : function () { 2459 | if (this._tzm) { 2460 | this.zone(this._tzm); 2461 | } else if (typeof this._i === 'string') { 2462 | this.zone(this._i); 2463 | } 2464 | return this; 2465 | }, 2466 | 2467 | hasAlignedHourOffset : function (input) { 2468 | if (!input) { 2469 | input = 0; 2470 | } 2471 | else { 2472 | input = moment(input).zone(); 2473 | } 2474 | 2475 | return (this.zone() - input) % 60 === 0; 2476 | }, 2477 | 2478 | daysInMonth : function () { 2479 | return daysInMonth(this.year(), this.month()); 2480 | }, 2481 | 2482 | dayOfYear : function (input) { 2483 | var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; 2484 | return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); 2485 | }, 2486 | 2487 | quarter : function (input) { 2488 | return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); 2489 | }, 2490 | 2491 | weekYear : function (input) { 2492 | var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year; 2493 | return input == null ? year : this.add((input - year), 'y'); 2494 | }, 2495 | 2496 | isoWeekYear : function (input) { 2497 | var year = weekOfYear(this, 1, 4).year; 2498 | return input == null ? year : this.add((input - year), 'y'); 2499 | }, 2500 | 2501 | week : function (input) { 2502 | var week = this.localeData().week(this); 2503 | return input == null ? week : this.add((input - week) * 7, 'd'); 2504 | }, 2505 | 2506 | isoWeek : function (input) { 2507 | var week = weekOfYear(this, 1, 4).week; 2508 | return input == null ? week : this.add((input - week) * 7, 'd'); 2509 | }, 2510 | 2511 | weekday : function (input) { 2512 | var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; 2513 | return input == null ? weekday : this.add(input - weekday, 'd'); 2514 | }, 2515 | 2516 | isoWeekday : function (input) { 2517 | // behaves the same as moment#day except 2518 | // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) 2519 | // as a setter, sunday should belong to the previous week. 2520 | return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); 2521 | }, 2522 | 2523 | isoWeeksInYear : function () { 2524 | return weeksInYear(this.year(), 1, 4); 2525 | }, 2526 | 2527 | weeksInYear : function () { 2528 | var weekInfo = this.localeData()._week; 2529 | return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); 2530 | }, 2531 | 2532 | get : function (units) { 2533 | units = normalizeUnits(units); 2534 | return this[units](); 2535 | }, 2536 | 2537 | set : function (units, value) { 2538 | units = normalizeUnits(units); 2539 | if (typeof this[units] === 'function') { 2540 | this[units](value); 2541 | } 2542 | return this; 2543 | }, 2544 | 2545 | // If passed a locale key, it will set the locale for this 2546 | // instance. Otherwise, it will return the locale configuration 2547 | // variables for this instance. 2548 | locale : function (key) { 2549 | var newLocaleData; 2550 | 2551 | if (key === undefined) { 2552 | return this._locale._abbr; 2553 | } else { 2554 | newLocaleData = moment.localeData(key); 2555 | if (newLocaleData != null) { 2556 | this._locale = newLocaleData; 2557 | } 2558 | return this; 2559 | } 2560 | }, 2561 | 2562 | lang : deprecate( 2563 | 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', 2564 | function (key) { 2565 | if (key === undefined) { 2566 | return this.localeData(); 2567 | } else { 2568 | return this.locale(key); 2569 | } 2570 | } 2571 | ), 2572 | 2573 | localeData : function () { 2574 | return this._locale; 2575 | }, 2576 | 2577 | _dateTzOffset : function () { 2578 | // On Firefox.24 Date#getTimezoneOffset returns a floating point. 2579 | // https://github.com/moment/moment/pull/1871 2580 | return Math.round(this._d.getTimezoneOffset() / 15) * 15; 2581 | } 2582 | }); 2583 | 2584 | function rawMonthSetter(mom, value) { 2585 | var dayOfMonth; 2586 | 2587 | // TODO: Move this out of here! 2588 | if (typeof value === 'string') { 2589 | value = mom.localeData().monthsParse(value); 2590 | // TODO: Another silent failure? 2591 | if (typeof value !== 'number') { 2592 | return mom; 2593 | } 2594 | } 2595 | 2596 | dayOfMonth = Math.min(mom.date(), 2597 | daysInMonth(mom.year(), value)); 2598 | mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); 2599 | return mom; 2600 | } 2601 | 2602 | function rawGetter(mom, unit) { 2603 | return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); 2604 | } 2605 | 2606 | function rawSetter(mom, unit, value) { 2607 | if (unit === 'Month') { 2608 | return rawMonthSetter(mom, value); 2609 | } else { 2610 | return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); 2611 | } 2612 | } 2613 | 2614 | function makeAccessor(unit, keepTime) { 2615 | return function (value) { 2616 | if (value != null) { 2617 | rawSetter(this, unit, value); 2618 | moment.updateOffset(this, keepTime); 2619 | return this; 2620 | } else { 2621 | return rawGetter(this, unit); 2622 | } 2623 | }; 2624 | } 2625 | 2626 | moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false); 2627 | moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false); 2628 | moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false); 2629 | // Setting the hour should keep the time, because the user explicitly 2630 | // specified which hour he wants. So trying to maintain the same hour (in 2631 | // a new timezone) makes sense. Adding/subtracting hours does not follow 2632 | // this rule. 2633 | moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true); 2634 | // moment.fn.month is defined separately 2635 | moment.fn.date = makeAccessor('Date', true); 2636 | moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true)); 2637 | moment.fn.year = makeAccessor('FullYear', true); 2638 | moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true)); 2639 | 2640 | // add plural methods 2641 | moment.fn.days = moment.fn.day; 2642 | moment.fn.months = moment.fn.month; 2643 | moment.fn.weeks = moment.fn.week; 2644 | moment.fn.isoWeeks = moment.fn.isoWeek; 2645 | moment.fn.quarters = moment.fn.quarter; 2646 | 2647 | // add aliased format methods 2648 | moment.fn.toJSON = moment.fn.toISOString; 2649 | 2650 | /************************************ 2651 | Duration Prototype 2652 | ************************************/ 2653 | 2654 | 2655 | function daysToYears (days) { 2656 | // 400 years have 146097 days (taking into account leap year rules) 2657 | return days * 400 / 146097; 2658 | } 2659 | 2660 | function yearsToDays (years) { 2661 | // years * 365 + absRound(years / 4) - 2662 | // absRound(years / 100) + absRound(years / 400); 2663 | return years * 146097 / 400; 2664 | } 2665 | 2666 | extend(moment.duration.fn = Duration.prototype, { 2667 | 2668 | _bubble : function () { 2669 | var milliseconds = this._milliseconds, 2670 | days = this._days, 2671 | months = this._months, 2672 | data = this._data, 2673 | seconds, minutes, hours, years = 0; 2674 | 2675 | // The following code bubbles up values, see the tests for 2676 | // examples of what that means. 2677 | data.milliseconds = milliseconds % 1000; 2678 | 2679 | seconds = absRound(milliseconds / 1000); 2680 | data.seconds = seconds % 60; 2681 | 2682 | minutes = absRound(seconds / 60); 2683 | data.minutes = minutes % 60; 2684 | 2685 | hours = absRound(minutes / 60); 2686 | data.hours = hours % 24; 2687 | 2688 | days += absRound(hours / 24); 2689 | 2690 | // Accurately convert days to years, assume start from year 0. 2691 | years = absRound(daysToYears(days)); 2692 | days -= absRound(yearsToDays(years)); 2693 | 2694 | // 30 days to a month 2695 | // TODO (iskren): Use anchor date (like 1st Jan) to compute this. 2696 | months += absRound(days / 30); 2697 | days %= 30; 2698 | 2699 | // 12 months -> 1 year 2700 | years += absRound(months / 12); 2701 | months %= 12; 2702 | 2703 | data.days = days; 2704 | data.months = months; 2705 | data.years = years; 2706 | }, 2707 | 2708 | abs : function () { 2709 | this._milliseconds = Math.abs(this._milliseconds); 2710 | this._days = Math.abs(this._days); 2711 | this._months = Math.abs(this._months); 2712 | 2713 | this._data.milliseconds = Math.abs(this._data.milliseconds); 2714 | this._data.seconds = Math.abs(this._data.seconds); 2715 | this._data.minutes = Math.abs(this._data.minutes); 2716 | this._data.hours = Math.abs(this._data.hours); 2717 | this._data.months = Math.abs(this._data.months); 2718 | this._data.years = Math.abs(this._data.years); 2719 | 2720 | return this; 2721 | }, 2722 | 2723 | weeks : function () { 2724 | return absRound(this.days() / 7); 2725 | }, 2726 | 2727 | valueOf : function () { 2728 | return this._milliseconds + 2729 | this._days * 864e5 + 2730 | (this._months % 12) * 2592e6 + 2731 | toInt(this._months / 12) * 31536e6; 2732 | }, 2733 | 2734 | humanize : function (withSuffix) { 2735 | var output = relativeTime(this, !withSuffix, this.localeData()); 2736 | 2737 | if (withSuffix) { 2738 | output = this.localeData().pastFuture(+this, output); 2739 | } 2740 | 2741 | return this.localeData().postformat(output); 2742 | }, 2743 | 2744 | add : function (input, val) { 2745 | // supports only 2.0-style add(1, 's') or add(moment) 2746 | var dur = moment.duration(input, val); 2747 | 2748 | this._milliseconds += dur._milliseconds; 2749 | this._days += dur._days; 2750 | this._months += dur._months; 2751 | 2752 | this._bubble(); 2753 | 2754 | return this; 2755 | }, 2756 | 2757 | subtract : function (input, val) { 2758 | var dur = moment.duration(input, val); 2759 | 2760 | this._milliseconds -= dur._milliseconds; 2761 | this._days -= dur._days; 2762 | this._months -= dur._months; 2763 | 2764 | this._bubble(); 2765 | 2766 | return this; 2767 | }, 2768 | 2769 | get : function (units) { 2770 | units = normalizeUnits(units); 2771 | return this[units.toLowerCase() + 's'](); 2772 | }, 2773 | 2774 | as : function (units) { 2775 | var days, months; 2776 | units = normalizeUnits(units); 2777 | 2778 | if (units === 'month' || units === 'year') { 2779 | days = this._days + this._milliseconds / 864e5; 2780 | months = this._months + daysToYears(days) * 12; 2781 | return units === 'month' ? months : months / 12; 2782 | } else { 2783 | // handle milliseconds separately because of floating point math errors (issue #1867) 2784 | days = this._days + Math.round(yearsToDays(this._months / 12)); 2785 | switch (units) { 2786 | case 'week': return days / 7 + this._milliseconds / 6048e5; 2787 | case 'day': return days + this._milliseconds / 864e5; 2788 | case 'hour': return days * 24 + this._milliseconds / 36e5; 2789 | case 'minute': return days * 24 * 60 + this._milliseconds / 6e4; 2790 | case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000; 2791 | // Math.floor prevents floating point math errors here 2792 | case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds; 2793 | default: throw new Error('Unknown unit ' + units); 2794 | } 2795 | } 2796 | }, 2797 | 2798 | lang : moment.fn.lang, 2799 | locale : moment.fn.locale, 2800 | 2801 | toIsoString : deprecate( 2802 | 'toIsoString() is deprecated. Please use toISOString() instead ' + 2803 | '(notice the capitals)', 2804 | function () { 2805 | return this.toISOString(); 2806 | } 2807 | ), 2808 | 2809 | toISOString : function () { 2810 | // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js 2811 | var years = Math.abs(this.years()), 2812 | months = Math.abs(this.months()), 2813 | days = Math.abs(this.days()), 2814 | hours = Math.abs(this.hours()), 2815 | minutes = Math.abs(this.minutes()), 2816 | seconds = Math.abs(this.seconds() + this.milliseconds() / 1000); 2817 | 2818 | if (!this.asSeconds()) { 2819 | // this is the same as C#'s (Noda) and python (isodate)... 2820 | // but not other JS (goog.date) 2821 | return 'P0D'; 2822 | } 2823 | 2824 | return (this.asSeconds() < 0 ? '-' : '') + 2825 | 'P' + 2826 | (years ? years + 'Y' : '') + 2827 | (months ? months + 'M' : '') + 2828 | (days ? days + 'D' : '') + 2829 | ((hours || minutes || seconds) ? 'T' : '') + 2830 | (hours ? hours + 'H' : '') + 2831 | (minutes ? minutes + 'M' : '') + 2832 | (seconds ? seconds + 'S' : ''); 2833 | }, 2834 | 2835 | localeData : function () { 2836 | return this._locale; 2837 | } 2838 | }); 2839 | 2840 | moment.duration.fn.toString = moment.duration.fn.toISOString; 2841 | 2842 | function makeDurationGetter(name) { 2843 | moment.duration.fn[name] = function () { 2844 | return this._data[name]; 2845 | }; 2846 | } 2847 | 2848 | for (i in unitMillisecondFactors) { 2849 | if (hasOwnProp(unitMillisecondFactors, i)) { 2850 | makeDurationGetter(i.toLowerCase()); 2851 | } 2852 | } 2853 | 2854 | moment.duration.fn.asMilliseconds = function () { 2855 | return this.as('ms'); 2856 | }; 2857 | moment.duration.fn.asSeconds = function () { 2858 | return this.as('s'); 2859 | }; 2860 | moment.duration.fn.asMinutes = function () { 2861 | return this.as('m'); 2862 | }; 2863 | moment.duration.fn.asHours = function () { 2864 | return this.as('h'); 2865 | }; 2866 | moment.duration.fn.asDays = function () { 2867 | return this.as('d'); 2868 | }; 2869 | moment.duration.fn.asWeeks = function () { 2870 | return this.as('weeks'); 2871 | }; 2872 | moment.duration.fn.asMonths = function () { 2873 | return this.as('M'); 2874 | }; 2875 | moment.duration.fn.asYears = function () { 2876 | return this.as('y'); 2877 | }; 2878 | 2879 | /************************************ 2880 | Default Locale 2881 | ************************************/ 2882 | 2883 | 2884 | // Set default locale, other locale will inherit from English. 2885 | moment.locale('en', { 2886 | ordinalParse: /\d{1,2}(th|st|nd|rd)/, 2887 | ordinal : function (number) { 2888 | var b = number % 10, 2889 | output = (toInt(number % 100 / 10) === 1) ? 'th' : 2890 | (b === 1) ? 'st' : 2891 | (b === 2) ? 'nd' : 2892 | (b === 3) ? 'rd' : 'th'; 2893 | return number + output; 2894 | } 2895 | }); 2896 | 2897 | /* EMBED_LOCALES */ 2898 | 2899 | /************************************ 2900 | Exposing Moment 2901 | ************************************/ 2902 | 2903 | function makeGlobal(shouldDeprecate) { 2904 | /*global ender:false */ 2905 | if (typeof ender !== 'undefined') { 2906 | return; 2907 | } 2908 | oldGlobalMoment = globalScope.moment; 2909 | if (shouldDeprecate) { 2910 | globalScope.moment = deprecate( 2911 | 'Accessing Moment through the global scope is ' + 2912 | 'deprecated, and will be removed in an upcoming ' + 2913 | 'release.', 2914 | moment); 2915 | } else { 2916 | globalScope.moment = moment; 2917 | } 2918 | } 2919 | 2920 | // CommonJS module is defined 2921 | if (hasModule) { 2922 | module.exports = moment; 2923 | } else if (typeof define === 'function' && define.amd) { 2924 | define('moment', function (require, exports, module) { 2925 | if (module.config && module.config() && module.config().noGlobal === true) { 2926 | // release the global variable 2927 | globalScope.moment = oldGlobalMoment; 2928 | } 2929 | 2930 | return moment; 2931 | }); 2932 | makeGlobal(true); 2933 | } else { 2934 | makeGlobal(); 2935 | } 2936 | }).call(this); 2937 | 2938 | /** 2939 | * Cachomatic: An AngularJS Local Storage Module 2940 | * @author Ryan Page 2941 | * @version v1.0.0 2942 | * @see https://github.com/ryanpager/cachomatic 2943 | * @license https://github.com/ryanpager/cachomatic/blob/master/LICENSE 2944 | */ 2945 | var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; 2946 | 2947 | (function() { 2948 | 'use strict'; 2949 | var Cachomatic; 2950 | Cachomatic = (function() { 2951 | Cachomatic.prototype.$inject = ['$windowProvider', 'moment']; 2952 | 2953 | function Cachomatic($windowProvider, moment) { 2954 | this.isExpired = bind(this.isExpired, this); 2955 | this.setExpiration = bind(this.setExpiration, this); 2956 | this.getExpiration = bind(this.getExpiration, this); 2957 | this.setObject = bind(this.setObject, this); 2958 | this.getObject = bind(this.getObject, this); 2959 | this.set = bind(this.set, this); 2960 | this.get = bind(this.get, this); 2961 | this.clearAll = bind(this.clearAll, this); 2962 | this.clear = bind(this.clear, this); 2963 | this.exists = bind(this.exists, this); 2964 | this.getCacheKey = bind(this.getCacheKey, this); 2965 | this.getCachePrefix = bind(this.getCachePrefix, this); 2966 | this.$window = $windowProvider.$get(); 2967 | this.moment = moment; 2968 | } 2969 | 2970 | Cachomatic.prototype.cachePrefix = null; 2971 | 2972 | Cachomatic.prototype.setCachePrefix = function(prefix) { 2973 | return this.cachePrefix = prefix; 2974 | }; 2975 | 2976 | Cachomatic.prototype.getCachePrefix = function() { 2977 | return this.cachePrefix; 2978 | }; 2979 | 2980 | Cachomatic.prototype.getCacheKey = function(key) { 2981 | var prefix; 2982 | prefix = this.getCachePrefix(); 2983 | if (prefix === null) { 2984 | return key; 2985 | } else { 2986 | return prefix + "-" + key; 2987 | } 2988 | }; 2989 | 2990 | Cachomatic.prototype.getExpirationCacheKey = function(key) { 2991 | return (this.getCacheKey(key)) + "-expiration"; 2992 | }; 2993 | 2994 | Cachomatic.prototype.exists = function(key) { 2995 | if (this.$window.localStorage.getItem(this.getCacheKey(key)) == null) { 2996 | return false; 2997 | } 2998 | return true; 2999 | }; 3000 | 3001 | Cachomatic.prototype.clear = function(key) { 3002 | var expiration; 3003 | expiration = this.getExpiration(key); 3004 | if (expiration != null) { 3005 | delete this.$window.localStorage.removeItem(this.getExpirationCacheKey(key)); 3006 | } 3007 | if (this.exists(key)) { 3008 | return delete this.$window.localStorage.removeItem(this.getCacheKey(key)); 3009 | } 3010 | }; 3011 | 3012 | Cachomatic.prototype.clearAll = function() { 3013 | return this.$window.localStorage.clear(); 3014 | }; 3015 | 3016 | Cachomatic.prototype.get = function(key) { 3017 | if (this.isExpired(key)) { 3018 | this.clear(key); 3019 | return null; 3020 | } 3021 | if (!this.exists(key)) { 3022 | return null; 3023 | } 3024 | return this.$window.localStorage.getItem(this.getCacheKey(key)); 3025 | }; 3026 | 3027 | Cachomatic.prototype.set = function(key, value, expiration) { 3028 | if (expiration == null) { 3029 | expiration = null; 3030 | } 3031 | this.$window.localStorage.setItem(this.getCacheKey(key), value); 3032 | if (expiration != null) { 3033 | return this.setExpiration(key, expiration); 3034 | } 3035 | }; 3036 | 3037 | Cachomatic.prototype.getObject = function(key) { 3038 | return JSON.parse(this.get(key)); 3039 | }; 3040 | 3041 | Cachomatic.prototype.setObject = function(key, value, expiration) { 3042 | return this.set(key, JSON.stringify(value), expiration); 3043 | }; 3044 | 3045 | Cachomatic.prototype.getExpiration = function(key) { 3046 | var expirationCacheKey, value; 3047 | expirationCacheKey = this.getExpirationCacheKey(key); 3048 | value = this.$window.localStorage.getItem(expirationCacheKey); 3049 | if (value) { 3050 | return value; 3051 | } else { 3052 | return null; 3053 | } 3054 | }; 3055 | 3056 | Cachomatic.prototype.setExpiration = function(key, expiration) { 3057 | var expirationCacheKey; 3058 | expirationCacheKey = this.getExpirationCacheKey(key); 3059 | return this.$window.localStorage.setItem(expirationCacheKey, this.moment().add(parseInt(expiration), 's').valueOf()); 3060 | }; 3061 | 3062 | Cachomatic.prototype.isExpired = function(key) { 3063 | var expiration; 3064 | expiration = this.getExpiration(key); 3065 | if (!expiration) { 3066 | return false; 3067 | } 3068 | if (this.moment().valueOf() > parseInt(expiration)) { 3069 | return true; 3070 | } 3071 | return false; 3072 | }; 3073 | 3074 | Cachomatic.prototype.$get = function() { 3075 | return { 3076 | getCachePrefix: this.getCachePrefix, 3077 | exists: this.exists, 3078 | clear: this.clear, 3079 | clearAll: this.clearAll, 3080 | get: this.get, 3081 | set: this.set, 3082 | getObject: this.getObject, 3083 | setObject: this.setObject, 3084 | getExpiration: this.getExpiration, 3085 | setExpiration: this.setExpiration, 3086 | isExpired: this.isExpired 3087 | }; 3088 | }; 3089 | 3090 | return Cachomatic; 3091 | 3092 | })(); 3093 | return angular.module('sbb.cachomatic', []).constant('moment', moment).provider('Cachomatic', Cachomatic); 3094 | })(); 3095 | --------------------------------------------------------------------------------