diff --git a/services/web/Gruntfile.coffee b/services/web/Gruntfile.coffee
index 2541d719d9..1fae80dd90 100644
--- a/services/web/Gruntfile.coffee
+++ b/services/web/Gruntfile.coffee
@@ -17,6 +17,8 @@ module.exports = (grunt) ->
grunt.loadNpmTasks 'grunt-contrib-watch'
grunt.loadNpmTasks 'grunt-parallel'
grunt.loadNpmTasks 'grunt-exec'
+ grunt.loadNpmTasks 'grunt-contrib-imagemin'
+ grunt.loadNpmTasks 'grunt-contrib-cssmin'
config =
@@ -27,7 +29,7 @@ module.exports = (grunt) ->
watch:
coffee:
- files: '**/*.coffee'
+ files: 'public/**/*.coffee'
tasks: ['quickcompile:coffee']
options: {}
@@ -44,6 +46,21 @@ module.exports = (grunt) ->
grunt:true
stream:true
+
+ imagemin:
+ dynamic:
+ files: [{
+ expand: true
+ cwd: 'public/img/'
+ src: ['**/*.{png,jpg,gif}']
+ dest: 'public/img/'
+ }]
+ options:
+ interlaced:false
+ optimizationLevel: 7
+
+
+
coffee:
app_dir:
expand: true,
@@ -108,6 +125,11 @@ module.exports = (grunt) ->
files:
"public/stylesheets/style.css": "public/stylesheets/style.less"
+ cssmin:
+ target:
+ files:
+ "public/stylesheets/style.css": "public/stylesheets/style.css"
+
env:
run:
add:
@@ -332,7 +354,7 @@ module.exports = (grunt) ->
grunt.registerTask 'compile:server', 'Compile the server side coffee script', ['clean:app', 'coffee:app', 'coffee:app_dir', 'compile:modules:server']
grunt.registerTask 'compile:client', 'Compile the client side coffee script', ['coffee:client', 'coffee:sharejs', 'wrap_sharejs', "compile:modules:client", 'compile:modules:inject_clientside_includes']
grunt.registerTask 'compile:css', 'Compile the less files to css', ['less']
- grunt.registerTask 'compile:minify', 'Concat and minify the client side js', ['requirejs', "file_append"]
+ grunt.registerTask 'compile:minify', 'Concat and minify the client side js', ['requirejs', "file_append", "cssmin"]
grunt.registerTask 'compile:unit_tests', 'Compile the unit tests', ['clean:unit_tests', 'coffee:unit_tests']
grunt.registerTask 'compile:acceptance_tests', 'Compile the acceptance tests', ['clean:acceptance_tests', 'coffee:acceptance_tests']
grunt.registerTask 'compile:smoke_tests', 'Compile the smoke tests', ['coffee:smoke_tests']
diff --git a/services/web/app/coffee/Features/Compile/CompileController.coffee b/services/web/app/coffee/Features/Compile/CompileController.coffee
index c927e6b666..5f3edd4de5 100755
--- a/services/web/app/coffee/Features/Compile/CompileController.coffee
+++ b/services/web/app/coffee/Features/Compile/CompileController.coffee
@@ -44,11 +44,20 @@ module.exports = CompileController =
}
_compileAsUser: (req, callback) ->
+ # callback with user_id if isolated flag is set on request, undefined otherwise
isolated = req.query?.isolated is "true"
if isolated
- AuthenticationController.getLoggedInUserId req, callback
+ AuthenticationController.getLoggedInUserId req, callback # -> (error, user_id)
else
- callback()
+ callback() # do a per-project compile, not per-user
+
+ _downloadAsUser: (req, callback) ->
+ # callback with user_id if isolated flag or user_id param is set on request, undefined otherwise
+ isolated = req.query?.isolated is "true" or req.params.user_id?
+ if isolated
+ AuthenticationController.getLoggedInUserId req, callback # -> (error, user_id)
+ else
+ callback() # do a per-project compile, not per-user
downloadPdf: (req, res, next = (error) ->)->
Metrics.inc "pdf-downloads"
@@ -82,7 +91,9 @@ module.exports = CompileController =
logger.log project_id:project_id, ip:req.ip, "rate limit hit downloading pdf"
res.send 500
else
- CompileController.proxyToClsi(project_id, "/project/#{project_id}/output/output.pdf", req, res, next)
+ CompileController._downloadAsUser req, (error, user_id) ->
+ url = CompileController._getFileUrl project_id, user_id, req.params.build_id, "output.pdf"
+ CompileController.proxyToClsi(project_id, url, req, res, next)
deleteAuxFiles: (req, res, next) ->
project_id = req.params.Project_id
@@ -92,29 +103,37 @@ module.exports = CompileController =
return next(error) if error?
res.sendStatus(200)
+ # this is only used by templates, so is not called with a user_id
compileAndDownloadPdf: (req, res, next)->
project_id = req.params.project_id
- CompileController._compileAsUser req, (error, user_id) ->
- return next(error) if error?
- CompileManager.compile project_id, user_id, {}, (err)->
- if err?
- logger.err err:err, project_id:project_id, "something went wrong compile and downloading pdf"
- res.sendStatus 500
- url = "/project/#{project_id}/output/output.pdf"
- CompileController.proxyToClsi project_id, url, req, res, next
+ # pass user_id as null, since templates are an "anonymous" compile
+ CompileManager.compile project_id, null, {}, (err)->
+ if err?
+ logger.err err:err, project_id:project_id, "something went wrong compile and downloading pdf"
+ res.sendStatus 500
+ url = "/project/#{project_id}/output/output.pdf"
+ CompileController.proxyToClsi project_id, url, req, res, next
getFileFromClsi: (req, res, next = (error) ->) ->
project_id = req.params.Project_id
- build_id = req.params.build_id
- user_id = req.params.user_id
- if user_id? and build_id?
- url = "/project/#{project_id}/user/#{user_id}/build/#{build_id}/output/#{req.params.file}"
- else if build_id?
- url = "/project/#{project_id}/build/#{build_id}/output/#{req.params.file}"
- else
- url = "/project/#{project_id}/output/#{req.params.file}"
- CompileController.proxyToClsi(project_id, url, req, res, next)
+ CompileController._downloadAsUser req, (error, user_id) ->
+ return next(error) if error?
+ url = CompileController._getFileUrl project_id, user_id, req.params.build_id, req.params.file
+ CompileController.proxyToClsi(project_id, url, req, res, next)
+ # compute a GET file url for a given project, user (optional), build (optional) and file
+ _getFileUrl: (project_id, user_id, build_id, file) ->
+ if user_id? and build_id?
+ url = "/project/#{project_id}/user/#{user_id}/build/#{build_id}/output/#{file}"
+ else if user_id?
+ url = "/project/#{project_id}/user/#{user_id}/output/#{file}"
+ else if build_id?
+ url = "/project/#{project_id}/build/#{build_id}/output/#{file}"
+ else
+ url = "/project/#{project_id}/output/#{file}"
+ return url
+
+ # compute a POST url for a project, user (optional) and action
_getUrl: (project_id, user_id, action) ->
path = "/project/#{project_id}"
path += "/user/#{user_id}" if user_id?
diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee
index 23df462787..7fc890ebd7 100644
--- a/services/web/app/coffee/router.coffee
+++ b/services/web/app/coffee/router.coffee
@@ -106,7 +106,9 @@ module.exports = class Router
webRouter.post '/project/:Project_id/settings/admin', AuthorizationMiddlewear.ensureUserCanAdminProject, ProjectController.updateProjectAdminSettings
webRouter.post '/project/:Project_id/compile', AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.compile
- webRouter.get '/Project/:Project_id/output/output.pdf', AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.downloadPdf
+ # Used by the web download buttons, adds filename header
+ webRouter.get '/project/:Project_id/output/output.pdf', AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.downloadPdf
+ # Used by the pdf viewers
webRouter.get /^\/project\/([^\/]*)\/output\/(.*)$/,
((req, res, next) ->
params =
@@ -236,8 +238,8 @@ module.exports = class Router
res.send("websharelatex is up")
- webRouter.get '/health_check', HealthCheckController.check
- webRouter.get '/health_check/redis', HealthCheckController.checkRedis
+ apiRouter.get '/health_check', HealthCheckController.check
+ apiRouter.get '/health_check/redis', HealthCheckController.checkRedis
apiRouter.get "/status/compiler/:Project_id", AuthorizationMiddlewear.ensureUserCanReadProject, (req, res) ->
sendRes = _.once (statusCode, message)->
diff --git a/services/web/app/views/beta_program/opt_in.jade b/services/web/app/views/beta_program/opt_in.jade
index 40e4c1992b..c6411cb211 100644
--- a/services/web/app/views/beta_program/opt_in.jade
+++ b/services/web/app/views/beta_program/opt_in.jade
@@ -27,7 +27,7 @@ block content
.col-md-12
if user.betaProgram
p #{translate("beta_program_already_participating")}
- form(method="post", action="/beta/opt-out", novalidate)
+ form(id="beta-program-opt-out", method="post", action="/beta/opt-out", novalidate)
.form-group
input(type="hidden", name="_csrf", value=csrfToken)
button.btn.btn-primary(
@@ -37,7 +37,7 @@ block content
.form-group
a(href="/project").btn.btn-info #{translate("back_to_your_projects")}
else
- form(method="post", action="/beta/opt-in", novalidate)
+ form(id="beta-program-opt-in", method="post", action="/beta/opt-in", novalidate)
.form-group
input(type="hidden", name="_csrf", value=csrfToken)
button.btn.btn-primary(
diff --git a/services/web/app/views/layout.jade b/services/web/app/views/layout.jade
index b3d1cf83e3..82b230c976 100644
--- a/services/web/app/views/layout.jade
+++ b/services/web/app/views/layout.jade
@@ -66,7 +66,6 @@ html(itemscope, itemtype='http://schema.org/Product')
block scripts
script(src="#{jsPath}libs/jquery-1.11.1.min.js")
script(src="#{jsPath}libs/angular-1.3.15.min.js")
- include sentry
script.
window.sharelatex = {
siteUrl: '#{settings.siteUrl}',
@@ -141,6 +140,7 @@ html(itemscope, itemtype='http://schema.org/Product')
)
include contact-us-modal
-
+ include sentry
+
diff --git a/services/web/app/views/user/settings.jade b/services/web/app/views/user/settings.jade
index 99698750f9..a23c3660aa 100644
--- a/services/web/app/views/user/settings.jade
+++ b/services/web/app/views/user/settings.jade
@@ -108,7 +108,7 @@ block content
p.small
| #{translate("beta_program_already_participating")}
div
- a(href="/beta/participate") #{translate("manage_beta_program_membership")}
+ a(id="beta-program-participate-link" href="/beta/participate") #{translate("manage_beta_program_membership")}
hr
diff --git a/services/web/package.json b/services/web/package.json
index 19c057c976..3f077d61fd 100644
--- a/services/web/package.json
+++ b/services/web/package.json
@@ -69,17 +69,18 @@
"grunt-bunyan": "0.5.0",
"grunt-contrib-clean": "0.5.0",
"grunt-contrib-coffee": "0.10.0",
+ "grunt-contrib-cssmin": "^1.0.1",
+ "grunt-contrib-imagemin": "^1.0.1",
"grunt-contrib-less": "0.9.0",
"grunt-contrib-requirejs": "0.4.1",
"grunt-contrib-watch": "^1.0.0",
"grunt-env": "0.4.4",
"grunt-exec": "^0.4.7",
- "grunt-parallel": "^0.5.1",
-
"grunt-file-append": "0.0.6",
"grunt-git-rev-parse": "^0.1.4",
"grunt-mocha-test": "0.9.0",
"grunt-newer": "^1.2.0",
+ "grunt-parallel": "^0.5.1",
"grunt-sed": "^0.1.1",
"sandboxed-module": "0.2.0",
"sinon": "",
diff --git a/services/web/public/coffee/filters/formatDate.coffee b/services/web/public/coffee/filters/formatDate.coffee
index 395194456e..fa1734b6be 100644
--- a/services/web/public/coffee/filters/formatDate.coffee
+++ b/services/web/public/coffee/filters/formatDate.coffee
@@ -2,7 +2,7 @@ define [
"base"
"libs/moment-2.9.0"
], (App, moment) ->
- moment.locale "en", calendar:
+ moment.updateLocale "en", calendar:
lastDay : '[Yesterday]'
sameDay : '[Today]'
nextDay : '[Tomorrow]'
diff --git a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
index ed1dee8da2..f503437407 100644
--- a/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
+++ b/services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
@@ -19,6 +19,11 @@ define [
else
$scope.modifierKey = "Ctrl"
+ # utility for making a query string from a hash, could use jquery $.param
+ createQueryString = (args) ->
+ qs_args = ("#{k}=#{v}" for k, v of args)
+ if qs_args.length then "?" + qs_args.join("&") else ""
+
$scope.$on "project:joined", () ->
return if !autoCompile
autoCompile = false
@@ -112,10 +117,14 @@ define [
qs.clsiserverid = response.clsiServerId
ide.clsiServerId = response.clsiServerId
# convert the qs hash into a query string and append it
- qs_args = ("#{k}=#{v}" for k, v of qs)
- $scope.pdf.qs = if qs_args.length then "?" + qs_args.join("&") else ""
+ $scope.pdf.qs = createQueryString qs
$scope.pdf.url += $scope.pdf.qs
- $scope.pdf.downloadUrl = "/Project/#{$scope.project_id}/output/output.pdf" + $scope.pdf.qs
+ # special case for the download url
+ if perUserCompile
+ qs.isolated = true
+ # Save all downloads as files
+ qs.popupDownload = true
+ $scope.pdf.downloadUrl = "/project/#{$scope.project_id}/output/output.pdf" + createQueryString(qs)
fetchLogs(fileByPath['output.log'], fileByPath['output.blg'])
@@ -131,10 +140,12 @@ define [
file.name = "#{file.path.replace(/^output\./, "")} file"
else
file.name = file.path
- if not file.url?
- file.url = "/project/#{project_id}/output/#{file.path}"
+ qs = {}
+ if perUserCompile
+ qs.isolated = true
if response.clsiServerId?
- file.url = file.url + "?clsiserverid=#{response.clsiServerId}"
+ qs.clsiserverid = response.clsiServerId
+ file.url = "/project/#{project_id}/output/#{file.path}" + createQueryString qs
$scope.pdf.outputFiles.push file
diff --git a/services/web/public/img/about/brian_gough.jpg b/services/web/public/img/about/brian_gough.jpg
index bfe3fe6d5c..5b972aecd0 100644
Binary files a/services/web/public/img/about/brian_gough.jpg and b/services/web/public/img/about/brian_gough.jpg differ
diff --git a/services/web/public/img/about/geri.jpg b/services/web/public/img/about/geri.jpg
index abf3af8e81..0de2f9a20e 100644
Binary files a/services/web/public/img/about/geri.jpg and b/services/web/public/img/about/geri.jpg differ
diff --git a/services/web/public/img/about/henry_oswald.jpg b/services/web/public/img/about/henry_oswald.jpg
index d4fd9c739e..234fe214cf 100644
Binary files a/services/web/public/img/about/henry_oswald.jpg and b/services/web/public/img/about/henry_oswald.jpg differ
diff --git a/services/web/public/img/about/james_allen.jpg b/services/web/public/img/about/james_allen.jpg
index 85b8ff0c2b..5a2595c2ed 100644
Binary files a/services/web/public/img/about/james_allen.jpg and b/services/web/public/img/about/james_allen.jpg differ
diff --git a/services/web/public/img/about/kiri_channon.jpg b/services/web/public/img/about/kiri_channon.jpg
index 81f83d2d18..2846989efb 100644
Binary files a/services/web/public/img/about/kiri_channon.jpg and b/services/web/public/img/about/kiri_channon.jpg differ
diff --git a/services/web/public/img/about/michael.jpg b/services/web/public/img/about/michael.jpg
index afb6022a64..a663c704a2 100644
Binary files a/services/web/public/img/about/michael.jpg and b/services/web/public/img/about/michael.jpg differ
diff --git a/services/web/public/img/about/paulo_reis.jpg b/services/web/public/img/about/paulo_reis.jpg
index e928bfb1e8..3ab83a30f0 100644
Binary files a/services/web/public/img/about/paulo_reis.jpg and b/services/web/public/img/about/paulo_reis.jpg differ
diff --git a/services/web/public/img/about/shane_kilkelly.jpg b/services/web/public/img/about/shane_kilkelly.jpg
index b353b2b6ad..081d94aa32 100644
Binary files a/services/web/public/img/about/shane_kilkelly.jpg and b/services/web/public/img/about/shane_kilkelly.jpg differ
diff --git a/services/web/public/img/crests/cambridge.png b/services/web/public/img/crests/cambridge.png
index 515e00a65e..9e4f079b36 100644
Binary files a/services/web/public/img/crests/cambridge.png and b/services/web/public/img/crests/cambridge.png differ
diff --git a/services/web/public/img/crests/durham.png b/services/web/public/img/crests/durham.png
index c3d2ce434f..4f3d7c2762 100644
Binary files a/services/web/public/img/crests/durham.png and b/services/web/public/img/crests/durham.png differ
diff --git a/services/web/public/img/crests/icl.png b/services/web/public/img/crests/icl.png
index 816a782706..7838fca3a1 100644
Binary files a/services/web/public/img/crests/icl.png and b/services/web/public/img/crests/icl.png differ
diff --git a/services/web/public/img/crests/liverpool.jpg b/services/web/public/img/crests/liverpool.jpg
index 80a9925d7c..b61de4d5fb 100644
Binary files a/services/web/public/img/crests/liverpool.jpg and b/services/web/public/img/crests/liverpool.jpg differ
diff --git a/services/web/public/img/crests/mit.gif b/services/web/public/img/crests/mit.gif
index 9300c107d1..845aaa750d 100644
Binary files a/services/web/public/img/crests/mit.gif and b/services/web/public/img/crests/mit.gif differ
diff --git a/services/web/public/img/crests/nasa.png b/services/web/public/img/crests/nasa.png
index 5b3f7de0da..8849968577 100644
Binary files a/services/web/public/img/crests/nasa.png and b/services/web/public/img/crests/nasa.png differ
diff --git a/services/web/public/img/crests/oxford.gif b/services/web/public/img/crests/oxford.gif
index 4fc9b68689..725cc6e0e3 100644
Binary files a/services/web/public/img/crests/oxford.gif and b/services/web/public/img/crests/oxford.gif differ
diff --git a/services/web/public/img/crests/stanford.png b/services/web/public/img/crests/stanford.png
index 23e0f09792..7ecda1f1ef 100644
Binary files a/services/web/public/img/crests/stanford.png and b/services/web/public/img/crests/stanford.png differ
diff --git a/services/web/public/img/crests/tokyo.png b/services/web/public/img/crests/tokyo.png
index 7ace3b4c87..74f5c0057f 100644
Binary files a/services/web/public/img/crests/tokyo.png and b/services/web/public/img/crests/tokyo.png differ
diff --git a/services/web/public/img/crests/yale.png b/services/web/public/img/crests/yale.png
index 53878511a6..6bb48e3499 100644
Binary files a/services/web/public/img/crests/yale.png and b/services/web/public/img/crests/yale.png differ
diff --git a/services/web/public/img/dropbox/document_updated_modal.png b/services/web/public/img/dropbox/document_updated_modal.png
index 7efcdd467a..a8f213f428 100644
Binary files a/services/web/public/img/dropbox/document_updated_modal.png and b/services/web/public/img/dropbox/document_updated_modal.png differ
diff --git a/services/web/public/img/dropbox/dropbox_banner.png b/services/web/public/img/dropbox/dropbox_banner.png
index 64f800f049..0de0ce1f47 100644
Binary files a/services/web/public/img/dropbox/dropbox_banner.png and b/services/web/public/img/dropbox/dropbox_banner.png differ
diff --git a/services/web/public/img/dropbox/dropbox_banner_tall.png b/services/web/public/img/dropbox/dropbox_banner_tall.png
index 5a6ae5286d..0d64cd02c6 100644
Binary files a/services/web/public/img/dropbox/dropbox_banner_tall.png and b/services/web/public/img/dropbox/dropbox_banner_tall.png differ
diff --git a/services/web/public/img/dropbox/dropbox_logo.png b/services/web/public/img/dropbox/dropbox_logo.png
index ec3c56beca..75ed862bef 100644
Binary files a/services/web/public/img/dropbox/dropbox_logo.png and b/services/web/public/img/dropbox/dropbox_logo.png differ
diff --git a/services/web/public/img/dropbox/dropbox_progress_bar.png b/services/web/public/img/dropbox/dropbox_progress_bar.png
index 736faf05c1..0d7d20831f 100644
Binary files a/services/web/public/img/dropbox/dropbox_progress_bar.png and b/services/web/public/img/dropbox/dropbox_progress_bar.png differ
diff --git a/services/web/public/img/dropbox/history_diff.png b/services/web/public/img/dropbox/history_diff.png
index 45ef7dbceb..32451b040a 100644
Binary files a/services/web/public/img/dropbox/history_diff.png and b/services/web/public/img/dropbox/history_diff.png differ
diff --git a/services/web/public/img/dropbox/share_dropbox_folder.png b/services/web/public/img/dropbox/share_dropbox_folder.png
index 1f7acff471..12dbdad029 100644
Binary files a/services/web/public/img/dropbox/share_dropbox_folder.png and b/services/web/public/img/dropbox/share_dropbox_folder.png differ
diff --git a/services/web/public/img/dropbox/simple_logo.png b/services/web/public/img/dropbox/simple_logo.png
index 34d5dcd264..b6ac2e4772 100644
Binary files a/services/web/public/img/dropbox/simple_logo.png and b/services/web/public/img/dropbox/simple_logo.png differ
diff --git a/services/web/public/img/enago.png b/services/web/public/img/enago.png
index 879dd216c0..473ce8bac2 100644
Binary files a/services/web/public/img/enago.png and b/services/web/public/img/enago.png differ
diff --git a/services/web/public/img/faileupload.png b/services/web/public/img/faileupload.png
index baaf090d94..60691e4d3b 100644
Binary files a/services/web/public/img/faileupload.png and b/services/web/public/img/faileupload.png differ
diff --git a/services/web/public/img/favicon@2x.png b/services/web/public/img/favicon@2x.png
index f44949c184..bc7b7aaaaf 100644
Binary files a/services/web/public/img/favicon@2x.png and b/services/web/public/img/favicon@2x.png differ
diff --git a/services/web/public/img/flags/24/en.png b/services/web/public/img/flags/24/en.png
index df8b2a874b..4db6548dc7 100644
Binary files a/services/web/public/img/flags/24/en.png and b/services/web/public/img/flags/24/en.png differ
diff --git a/services/web/public/img/flags/24/pt.png b/services/web/public/img/flags/24/pt.png
index 385203aa99..2c3f5df290 100644
Binary files a/services/web/public/img/flags/24/pt.png and b/services/web/public/img/flags/24/pt.png differ
diff --git a/services/web/public/img/flags/32/en.png b/services/web/public/img/flags/32/en.png
index df8b2a874b..4db6548dc7 100644
Binary files a/services/web/public/img/flags/32/en.png and b/services/web/public/img/flags/32/en.png differ
diff --git a/services/web/public/img/flags/32/ja.png b/services/web/public/img/flags/32/ja.png
index 69cca05ee8..54d0e5ab88 100644
Binary files a/services/web/public/img/flags/32/ja.png and b/services/web/public/img/flags/32/ja.png differ
diff --git a/services/web/public/img/flags/32/ko.png b/services/web/public/img/flags/32/ko.png
index 2a3a7ee7ae..050ebed117 100644
Binary files a/services/web/public/img/flags/32/ko.png and b/services/web/public/img/flags/32/ko.png differ
diff --git a/services/web/public/img/github/octocat.jpg b/services/web/public/img/github/octocat.jpg
index 38e3a18d3b..0d0509656b 100644
Binary files a/services/web/public/img/github/octocat.jpg and b/services/web/public/img/github/octocat.jpg differ
diff --git a/services/web/public/img/lion-128.png b/services/web/public/img/lion-128.png
index 8660218b7b..f945ac9e03 100644
Binary files a/services/web/public/img/lion-128.png and b/services/web/public/img/lion-128.png differ
diff --git a/services/web/public/img/lion-sad-128.png b/services/web/public/img/lion-sad-128.png
index e5e58c42b0..3a0bc7bfb8 100644
Binary files a/services/web/public/img/lion-sad-128.png and b/services/web/public/img/lion-sad-128.png differ
diff --git a/services/web/public/img/logo.png b/services/web/public/img/logo.png
index 3c25641143..440376c052 100644
Binary files a/services/web/public/img/logo.png and b/services/web/public/img/logo.png differ
diff --git a/services/web/public/img/logo@2x.png b/services/web/public/img/logo@2x.png
index cf9969c627..a4ea401456 100644
Binary files a/services/web/public/img/logo@2x.png and b/services/web/public/img/logo@2x.png differ
diff --git a/services/web/public/img/pattern-home.png b/services/web/public/img/pattern-home.png
index 30331a07b9..79746dcbc6 100644
Binary files a/services/web/public/img/pattern-home.png and b/services/web/public/img/pattern-home.png differ
diff --git a/services/web/public/img/references-search/search_example.gif b/services/web/public/img/references-search/search_example.gif
index 7543e71e3d..c028a094b2 100644
Binary files a/services/web/public/img/references-search/search_example.gif and b/services/web/public/img/references-search/search_example.gif differ
diff --git a/services/web/public/img/screen.png b/services/web/public/img/screen.png
index c4542a6999..dcef9cd572 100644
Binary files a/services/web/public/img/screen.png and b/services/web/public/img/screen.png differ
diff --git a/services/web/public/img/screen@2x.png b/services/web/public/img/screen@2x.png
index 512c21a426..7d694223bc 100644
Binary files a/services/web/public/img/screen@2x.png and b/services/web/public/img/screen@2x.png differ
diff --git a/services/web/public/img/social/facebook-32.png b/services/web/public/img/social/facebook-32.png
index 0d8753d8bd..ecf7dcdd64 100644
Binary files a/services/web/public/img/social/facebook-32.png and b/services/web/public/img/social/facebook-32.png differ
diff --git a/services/web/public/img/social/link-32.png b/services/web/public/img/social/link-32.png
index 10d2ed4f47..56fa0de6d3 100644
Binary files a/services/web/public/img/social/link-32.png and b/services/web/public/img/social/link-32.png differ
diff --git a/services/web/public/img/social/mail-32.png b/services/web/public/img/social/mail-32.png
index dc3b9dd3a6..5a2f52dd2b 100644
Binary files a/services/web/public/img/social/mail-32.png and b/services/web/public/img/social/mail-32.png differ
diff --git a/services/web/public/img/spellcheck-underline.png b/services/web/public/img/spellcheck-underline.png
index 9b1f719120..8f22344148 100644
Binary files a/services/web/public/img/spellcheck-underline.png and b/services/web/public/img/spellcheck-underline.png differ
diff --git a/services/web/public/img/spinner.gif b/services/web/public/img/spinner.gif
index 22220a2214..634932d13c 100644
Binary files a/services/web/public/img/spinner.gif and b/services/web/public/img/spinner.gif differ
diff --git a/services/web/public/js/libs/algolia-2.5.2.js b/services/web/public/js/libs/algolia-2.5.2.js
index fead438491..de02c26a9f 100644
--- a/services/web/public/js/libs/algolia-2.5.2.js
+++ b/services/web/public/js/libs/algolia-2.5.2.js
@@ -1,2039 +1,7 @@
-/*
- * Copyright (c) 2013 Algolia
- * http://www.algolia.com/
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
+/*!
+ * algoliasearch 2.5.2
+ * https://github.com/algolia/algoliasearch-client-js
+ * Copyright 2014 Algolia SAS; Licensed MIT
*/
-var ALGOLIA_VERSION = '2.5.2';
-
-/*
- * Copyright (c) 2013 Algolia
- * http://www.algolia.com/
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-/*
- * Algolia Search library initialization
- * @param applicationID the application ID you have in your admin interface
- * @param apiKey a valid API key for the service
- * @param method specify if the protocol used is http or https (http by default to make the first search query faster).
- * You need to use https is you are doing something else than just search queries.
- * @param resolveDNS let you disable first empty query that is launch to warmup the service
- * @param hostsArray (optionnal) the list of hosts that you have received for the service
- */
-var AlgoliaSearch = function(applicationID, apiKey, method, resolveDNS, hostsArray) {
- var self = this;
- this.applicationID = applicationID;
- this.apiKey = apiKey;
-
- if (this._isUndefined(hostsArray)) {
- hostsArray = [applicationID + '-1.algolia.io',
- applicationID + '-2.algolia.io',
- applicationID + '-3.algolia.io'];
- }
- this.hosts = [];
- // Add hosts in random order
- for (var i = 0; i < hostsArray.length; ++i) {
- if (Math.random() > 0.5) {
- this.hosts.reverse();
- }
- if (this._isUndefined(method) || method == null) {
- this.hosts.push(('https:' == document.location.protocol ? 'https' : 'http') + '://' + hostsArray[i]);
- } else if (method === 'https' || method === 'HTTPS') {
- this.hosts.push('https://' + hostsArray[i]);
- } else {
- this.hosts.push('http://' + hostsArray[i]);
- }
- }
- if (Math.random() > 0.5) {
- this.hosts.reverse();
- }
-
- // resolve DNS + check CORS support (JSONP fallback)
- this.jsonp = null;
- this.jsonpWait = 0;
- this._jsonRequest({
- method: 'GET',
- url: '/1/isalive',
- callback: function(success, content) {
- self.jsonp = !success;
- }
- });
- this.extraHeaders = [];
-};
-
-function AlgoliaExplainResults(hit, titleAttribute, otherAttributes) {
-
- function _getHitExplanationForOneAttr_recurse(obj, foundWords) {
- var res = [];
- if (typeof obj === 'object' && 'matchedWords' in obj && 'value' in obj) {
- var match = false;
- for (var j = 0; j < obj.matchedWords.length; ++j) {
- var word = obj.matchedWords[j];
- if (!(word in foundWords)) {
- foundWords[word] = 1;
- match = true;
- }
- }
- if (match) {
- res.push(obj.value);
- }
- } else if (Object.prototype.toString.call(obj) === '[object Array]') {
- for (var i = 0; i < obj.length; ++i) {
- var array = _getHitExplanationForOneAttr_recurse(obj[i], foundWords);
- res = res.concat(array);
- }
- } else if (typeof obj === 'object') {
- for (var prop in obj) {
- if (obj.hasOwnProperty(prop)){
- res = res.concat(_getHitExplanationForOneAttr_recurse(obj[prop], foundWords));
- }
- }
- }
- return res;
- }
-
- function _getHitExplanationForOneAttr(hit, foundWords, attr) {
- var base = hit._highlightResult || hit;
- if (attr.indexOf('.') === -1) {
- if (attr in base) {
- return _getHitExplanationForOneAttr_recurse(base[attr], foundWords);
- }
- return [];
- }
- var array = attr.split('.');
- var obj = base;
- for (var i = 0; i < array.length; ++i) {
- if (Object.prototype.toString.call(obj) === '[object Array]') {
- var res = [];
- for (var j = 0; j < obj.length; ++j) {
- res = res.concat(_getHitExplanationForOneAttr(obj[j], foundWords, array.slice(i).join('.')));
- }
- return res;
- }
- if (array[i] in obj) {
- obj = obj[array[i]];
- } else {
- return [];
- }
- }
- return _getHitExplanationForOneAttr_recurse(obj, foundWords);
- }
-
- var res = {};
- var foundWords = {};
- var title = _getHitExplanationForOneAttr(hit, foundWords, titleAttribute);
- res.title = (title.length > 0) ? title[0] : '';
- res.subtitles = [];
-
- if (typeof otherAttributes !== 'undefined') {
- for (var i = 0; i < otherAttributes.length; ++i) {
- var attr = _getHitExplanationForOneAttr(hit, foundWords, otherAttributes[i]);
- for (var j = 0; j < attr.length; ++j) {
- res.subtitles.push({ attr: otherAttributes[i], value: attr[j] });
- }
- }
- }
- return res;
-}
-
-
-AlgoliaSearch.prototype = {
- /*
- * Delete an index
- *
- * @param indexName the name of index to delete
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer that contains the task ID
- */
- deleteIndex: function(indexName, callback) {
- this._jsonRequest({ method: 'DELETE',
- url: '/1/indexes/' + encodeURIComponent(indexName),
- callback: callback });
- },
- /**
- * Move an existing index.
- * @param srcIndexName the name of index to copy.
- * @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer that contains the task ID
- */
- moveIndex: function(srcIndexName, dstIndexName, callback) {
- var postObj = {operation: 'move', destination: dstIndexName};
- this._jsonRequest({ method: 'POST',
- url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
- body: postObj,
- callback: callback });
-
- },
- /**
- * Copy an existing index.
- * @param srcIndexName the name of index to copy.
- * @param dstIndexName the new index name that will contains a copy of srcIndexName (destination will be overriten if it already exist).
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer that contains the task ID
- */
- copyIndex: function(srcIndexName, dstIndexName, callback) {
- var postObj = {operation: 'copy', destination: dstIndexName};
- this._jsonRequest({ method: 'POST',
- url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
- body: postObj,
- callback: callback });
- },
- /**
- * Return last log entries.
- * @param offset Specify the first entry to retrieve (0-based, 0 is the most recent log entry).
- * @param length Specify the maximum number of entries to retrieve starting at offset. Maximum allowed value: 1000.
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer that contains the task ID
- */
- getLogs: function(callback, offset, length) {
- if (this._isUndefined(offset)) {
- offset = 0;
- }
- if (this._isUndefined(length)) {
- length = 10;
- }
-
- this._jsonRequest({ method: 'GET',
- url: '/1/logs?offset=' + offset + '&length=' + length,
- callback: callback });
- },
- /*
- * List all existing indexes
- *
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer with index list or error description if success is false.
- */
- listIndexes: function(callback) {
- this._jsonRequest({ method: 'GET',
- url: '/1/indexes',
- callback: callback });
- },
-
- /*
- * Get the index object initialized
- *
- * @param indexName the name of index
- * @param callback the result callback with one argument (the Index instance)
- */
- initIndex: function(indexName) {
- return new this.Index(this, indexName);
- },
- /*
- * List all existing user keys with their associated ACLs
- *
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer with user keys list or error description if success is false.
- */
- listUserKeys: function(callback) {
- this._jsonRequest({ method: 'GET',
- url: '/1/keys',
- callback: callback });
- },
- /*
- * Get ACL of a user key
- *
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer with user keys list or error description if success is false.
- */
- getUserKeyACL: function(key, callback) {
- this._jsonRequest({ method: 'GET',
- url: '/1/keys/' + key,
- callback: callback });
- },
- /*
- * Delete an existing user key
- *
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer with user keys list or error description if success is false.
- */
- deleteUserKey: function(key, callback) {
- this._jsonRequest({ method: 'DELETE',
- url: '/1/keys/' + key,
- callback: callback });
- },
- /*
- * Add an existing user key
- *
- * @param acls the list of ACL for this key. Defined by an array of strings that
- * can contains the following values:
- * - search: allow to search (https and http)
- * - addObject: allows to add/update an object in the index (https only)
- * - deleteObject : allows to delete an existing object (https only)
- * - deleteIndex : allows to delete index content (https only)
- * - settings : allows to get index settings (https only)
- * - editSettings : allows to change index settings (https only)
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer with user keys list or error description if success is false.
- */
- addUserKey: function(acls, callback) {
- var aclsObject = {};
- aclsObject.acl = acls;
- this._jsonRequest({ method: 'POST',
- url: '/1/keys',
- body: aclsObject,
- callback: callback });
- },
- /*
- * Add an existing user key
- *
- * @param acls the list of ACL for this key. Defined by an array of strings that
- * can contains the following values:
- * - search: allow to search (https and http)
- * - addObject: allows to add/update an object in the index (https only)
- * - deleteObject : allows to delete an existing object (https only)
- * - deleteIndex : allows to delete index content (https only)
- * - settings : allows to get index settings (https only)
- * - editSettings : allows to change index settings (https only)
- * @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
- * @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
- * @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer with user keys list or error description if success is false.
- */
- addUserKeyWithValidity: function(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery, callback) {
- var indexObj = this;
- var aclsObject = {};
- aclsObject.acl = acls;
- aclsObject.validity = validity;
- aclsObject.maxQueriesPerIPPerHour = maxQueriesPerIPPerHour;
- aclsObject.maxHitsPerQuery = maxHitsPerQuery;
- this._jsonRequest({ method: 'POST',
- url: '/1/indexes/' + indexObj.indexName + '/keys',
- body: aclsObject,
- callback: callback });
- },
-
- /**
- * Set the extra security tagFilters header
- * @param {string|array} tags The list of tags defining the current security filters
- */
- setSecurityTags: function(tags) {
- if (Object.prototype.toString.call(tags) === '[object Array]') {
- var strTags = [];
- for (var i = 0; i < tags.length; ++i) {
- if (Object.prototype.toString.call(tags[i]) === '[object Array]') {
- var oredTags = [];
- for (var j = 0; j < tags[i].length; ++j) {
- oredTags.push(tags[i][j]);
- }
- strTags.push('(' + oredTags.join(',') + ')');
- } else {
- strTags.push(tags[i]);
- }
- }
- tags = strTags.join(',');
- }
- this.tagFilters = tags;
- },
-
- /**
- * Set the extra user token header
- * @param {string} userToken The token identifying a uniq user (used to apply rate limits)
- */
- setUserToken: function(userToken) {
- this.userToken = userToken;
- },
-
- /*
- * Initialize a new batch of search queries
- */
- startQueriesBatch: function() {
- this.batch = [];
- },
- /*
- * Add a search query in the batch
- *
- * @param query the full text query
- * @param args (optional) if set, contains an object with query parameters:
- * - attributes: an array of object attribute names to retrieve
- * (if not set all attributes are retrieve)
- * - attributesToHighlight: an array of object attribute names to highlight
- * (if not set indexed attributes are highlighted)
- * - minWordSizefor1Typo: the minimum number of characters to accept one typo.
- * Defaults to 3.
- * - minWordSizefor2Typos: the minimum number of characters to accept two typos.
- * Defaults to 7.
- * - getRankingInfo: if set, the result hits will contain ranking information in
- * _rankingInfo attribute
- * - page: (pagination parameter) page to retrieve (zero base). Defaults to 0.
- * - hitsPerPage: (pagination parameter) number of hits per page. Defaults to 10.
- */
- addQueryInBatch: function(indexName, query, args) {
- var params = 'query=' + encodeURIComponent(query);
- if (!this._isUndefined(args) && args != null) {
- params = this._getSearchParams(args, params);
- }
- this.batch.push({ indexName: indexName, params: params });
- },
- /*
- * Clear all queries in cache
- */
- clearCache: function() {
- this.cache = {};
- },
- /*
- * Launch the batch of queries using XMLHttpRequest.
- * (Optimized for browser using a POST query to minimize number of OPTIONS queries)
- *
- * @param callback the function that will receive results
- * @param delay (optional) if set, wait for this delay (in ms) and only send the batch if there was no other in the meantime.
- */
- sendQueriesBatch: function(callback, delay) {
- var as = this;
- var params = {requests: [], apiKey: this.apiKey, appID: this.applicationID};
- if (this.userToken) {
- params['X-Algolia-UserToken'] = this.userToken;
- }
- if (this.tagFilters) {
- params['X-Algolia-TagFilters'] = this.tagFilters;
- }
- for (var i = 0; i < as.batch.length; ++i) {
- params.requests.push(as.batch[i]);
- }
- window.clearTimeout(as.onDelayTrigger);
- if (!this._isUndefined(delay) && delay != null && delay > 0) {
- var onDelayTrigger = window.setTimeout( function() {
- as._sendQueriesBatch(params, callback);
- }, delay);
- as.onDelayTrigger = onDelayTrigger;
- } else {
- this._sendQueriesBatch(params, callback);
- }
- },
- /*
- * Index class constructor.
- * You should not use this method directly but use initIndex() function
- */
- Index: function(algoliasearch, indexName) {
- this.indexName = indexName;
- this.as = algoliasearch;
- this.typeAheadArgs = null;
- this.typeAheadValueOption = null;
- },
-
- setExtraHeader: function(key, value) {
- this.extraHeaders.push({ key: key, value: value});
- },
-
- _sendQueriesBatch: function(params, callback) {
- if (this.jsonp == null) {
- var self = this;
- this._waitReady(function() { self._sendQueriesBatch(params, callback); });
- return;
- }
- if (this.jsonp) {
- var jsonpParams = '';
- for (var i = 0; i < params.requests.length; ++i) {
- var q = '/1/indexes/' + encodeURIComponent(params.requests[i].indexName) + '?' + params.requests[i].params;
- jsonpParams += i + '=' + encodeURIComponent(q) + '&';
- }
- this._jsonRequest({ cache: this.cache,
- method: 'GET', jsonp: true,
- url: '/1/indexes/*',
- body: { params: jsonpParams },
- callback: callback });
- } else {
- this._jsonRequest({ cache: this.cache,
- method: 'POST',
- url: '/1/indexes/*/queries',
- body: params,
- callback: callback });
- }
- },
- /*
- * Wrapper that try all hosts to maximize the quality of service
- */
- _jsonRequest: function(opts) {
- var self = this;
- var callback = opts.callback;
- var cache = null;
- var cacheID = opts.url;
- if (!this._isUndefined(opts.body)) {
- cacheID = opts.url + '_body_' + JSON.stringify(opts.body);
- }
- if (!this._isUndefined(opts.cache)) {
- cache = opts.cache;
- if (!this._isUndefined(cache[cacheID])) {
- if (!this._isUndefined(callback)) {
- setTimeout(function () { callback(true, cache[cacheID]); }, 1);
- }
- return;
- }
- }
-
- var impl = function(position) {
- var idx = 0;
- if (!self._isUndefined(position)) {
- idx = position;
- }
- if (self.hosts.length <= idx) {
- if (!self._isUndefined(callback)) {
- callback(false, { message: 'Cannot contact server'});
- }
- return;
- }
- opts.callback = function(retry, success, res, body) {
- if (!success && !self._isUndefined(body)) {
- if (window.console) { console.log('Error: ' + body.message); }
- }
- if (success && !self._isUndefined(opts.cache)) {
- cache[cacheID] = body;
- }
- if (!success && retry && (idx + 1) < self.hosts.length) {
- impl(idx + 1);
- } else {
- if (!self._isUndefined(callback)) {
- callback(success, body);
- }
- }
- };
- opts.hostname = self.hosts[idx];
- self._jsonRequestByHost(opts);
- };
- impl();
- },
-
- _jsonRequestByHost: function(opts) {
- var self = this;
- var url = opts.hostname + opts.url;
-
- if (this.jsonp) {
- if (!opts.jsonp) {
- opts.callback(true, false, null, { 'message': 'Method ' + opts.method + ' ' + url + ' is not supported by JSONP.' });
- return;
- }
- this.jsonpCounter = this.jsonpCounter || 0;
- this.jsonpCounter += 1;
- var cb = 'algoliaJSONP_' + this.jsonpCounter;
- window[cb] = function(data) {
- opts.callback(false, true, null, data);
- try { delete window[cb]; } catch (e) { window[cb] = undefined; }
- };
- var script = document.createElement('script');
- script.type = 'text/javascript';
- script.src = url + '?callback=' + cb + ',' + this.applicationID + ',' + this.apiKey;
- if (opts['X-Algolia-TagFilters']) {
- script.src += '&X-Algolia-TagFilters=' + opts['X-Algolia-TagFilters'];
- }
- if (opts['X-Algolia-UserToken']) {
- script.src += '&X-Algolia-UserToken=' + opts['X-Algolia-UserToken'];
- }
- if (opts.body && opts.body.params) {
- script.src += '&' + opts.body.params;
- }
- var head = document.getElementsByTagName('head')[0];
- script.onerror = function() {
- opts.callback(true, false, null, { 'message': 'Failed to load JSONP script.' });
- head.removeChild(script);
- try { delete window[cb]; } catch (e) { window[cb] = undefined; }
- };
- var done = false;
- script.onload = script.onreadystatechange = function() {
- if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {
- done = true;
- if (typeof window[cb + '_loaded'] === 'undefined') {
- opts.callback(true, false, null, { 'message': 'Failed to load JSONP script.' });
- try { delete window[cb]; } catch (e) { window[cb] = undefined; }
- } else {
- try { delete window[cb + '_loaded']; } catch (e) { window[cb + '_loaded'] = undefined; }
- }
- script.onload = script.onreadystatechange = null; // Handle memory leak in IE
- head.removeChild(script);
- }
- };
- head.appendChild(script);
- } else {
- var body = null;
- if (!this._isUndefined(opts.body)) {
- body = JSON.stringify(opts.body);
- }
- var xmlHttp = window.XMLHttpRequest ? new XMLHttpRequest() : {};
- if ('withCredentials' in xmlHttp) {
- xmlHttp.open(opts.method, url , true);
- xmlHttp.setRequestHeader('X-Algolia-API-Key', this.apiKey);
- xmlHttp.setRequestHeader('X-Algolia-Application-Id', this.applicationID);
- for (var i = 0; i < this.extraHeaders.length; ++i) {
- xmlHttp.setRequestHeader(this.extraHeaders[i].key, this.extraHeaders[i].value);
- }
- if (body != null) {
- xmlHttp.setRequestHeader('Content-type', 'application/json');
- }
- } else if (typeof XDomainRequest != 'undefined') {
- // Handle IE8/IE9
- // XDomainRequest only exists in IE, and is IE's way of making CORS requests.
- xmlHttp = new XDomainRequest();
- xmlHttp.open(opts.method, url);
- } else {
- // very old browser, not supported
- if (window.console) { console.log('Your browser is too old to support CORS requests'); }
- opts.callback(false, false, null, { 'message': 'CORS not supported' });
- return;
- }
- xmlHttp.send(body);
- xmlHttp.onload = function(event) {
- if (!self._isUndefined(event) && event.target != null) {
- var retry = (event.target.status === 0 || event.target.status === 503);
- var success = (event.target.status === 200 || event.target.status === 201);
- opts.callback(retry, success, event.target, event.target.response != null ? JSON.parse(event.target.response) : null);
- } else {
- opts.callback(false, true, event, JSON.parse(xmlHttp.responseText));
- }
- };
- xmlHttp.onerror = function(event) {
- opts.callback(true, false, null, { 'message': 'Could not connect to host', 'error': event } );
- };
- }
- },
-
- /**
- * Wait until JSONP flag has been set to perform the first query
- */
- _waitReady: function(cb) {
- if (this.jsonp == null) {
- this.jsonpWait += 100;
- if (this.jsonpWait > 2000) {
- this.jsonp = true;
- }
- setTimeout(cb, 100);
- }
- },
-
- /*
- * Transform search param object in query string
- */
- _getSearchParams: function(args, params) {
- if (this._isUndefined(args) || args == null) {
- return params;
- }
- for (var key in args) {
- if (key != null && args.hasOwnProperty(key)) {
- params += (params.length === 0) ? '?' : '&';
- params += key + '=' + encodeURIComponent(Object.prototype.toString.call(args[key]) === '[object Array]' ? JSON.stringify(args[key]) : args[key]);
- }
- }
- return params;
- },
- _isUndefined: function(obj) {
- return obj === void 0;
- },
-
- /// internal attributes
- applicationID: null,
- apiKey: null,
- tagFilters: null,
- userToken: null,
- hosts: [],
- cache: {},
- extraHeaders: []
-};
-
-/*
- * Contains all the functions related to one index
- * You should use AlgoliaSearch.initIndex(indexName) to retrieve this object
- */
-AlgoliaSearch.prototype.Index.prototype = {
- /*
- * Clear all queries in cache
- */
- clearCache: function() {
- this.cache = {};
- },
- /*
- * Add an object in this index
- *
- * @param content contains the javascript object to add inside the index
- * @param callback (optional) the result callback with two arguments:
- * success: boolean set to true if the request was successfull
- * content: the server answer that contains 3 elements: createAt, taskId and objectID
- * @param objectID (optional) an objectID you want to attribute to this object
- * (if the attribute already exist the old object will be overwrite)
- */
- addObject: function(content, callback, objectID) {
- var indexObj = this;
- if (this.as._isUndefined(objectID)) {
- this.as._jsonRequest({ method: 'POST',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName),
- body: content,
- callback: callback });
- } else {
- this.as._jsonRequest({ method: 'PUT',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
- body: content,
- callback: callback });
- }
-
- },
- /*
- * Add several objects
- *
- * @param objects contains an array of objects to add
- * @param callback (optional) the result callback with two arguments:
- * success: boolean set to true if the request was successfull
- * content: the server answer that updateAt and taskID
- */
- addObjects: function(objects, callback) {
- var indexObj = this;
- var postObj = {requests:[]};
- for (var i = 0; i < objects.length; ++i) {
- var request = { action: 'addObject',
- body: objects[i] };
- postObj.requests.push(request);
- }
- this.as._jsonRequest({ method: 'POST',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
- body: postObj,
- callback: callback });
- },
- /*
- * Get an object from this index
- *
- * @param objectID the unique identifier of the object to retrieve
- * @param callback (optional) the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the object to retrieve or the error message if a failure occured
- * @param attributes (optional) if set, contains the array of attribute names to retrieve
- */
- getObject: function(objectID, callback, attributes) {
- if (this.as.jsonp == null) {
- var self = this;
- this.as._waitReady(function() { self.getObject(objectID, callback, attributes); });
- return;
- }
- var indexObj = this;
- var params = '';
- if (!this.as._isUndefined(attributes)) {
- params = '?attributes=';
- for (var i = 0; i < attributes.length; ++i) {
- if (i !== 0) {
- params += ',';
- }
- params += attributes[i];
- }
- }
- this.as._jsonRequest({ method: 'GET', jsonp: true,
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID) + params,
- callback: callback });
- },
-
- /*
- * Update partially an object (only update attributes passed in argument)
- *
- * @param partialObject contains the javascript attributes to override, the
- * object must contains an objectID attribute
- * @param callback (optional) the result callback with two arguments:
- * success: boolean set to true if the request was successfull
- * content: the server answer that contains 3 elements: createAt, taskId and objectID
- */
- partialUpdateObject: function(partialObject, callback) {
- var indexObj = this;
- this.as._jsonRequest({ method: 'POST',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(partialObject.objectID) + '/partial',
- body: partialObject,
- callback: callback });
- },
- /*
- * Partially Override the content of several objects
- *
- * @param objects contains an array of objects to update (each object must contains a objectID attribute)
- * @param callback (optional) the result callback with two arguments:
- * success: boolean set to true if the request was successfull
- * content: the server answer that updateAt and taskID
- */
- partialUpdateObjects: function(objects, callback) {
- var indexObj = this;
- var postObj = {requests:[]};
- for (var i = 0; i < objects.length; ++i) {
- var request = { action: 'partialUpdateObject',
- objectID: objects[i].objectID,
- body: objects[i] };
- postObj.requests.push(request);
- }
- this.as._jsonRequest({ method: 'POST',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
- body: postObj,
- callback: callback });
- },
- /*
- * Override the content of object
- *
- * @param object contains the javascript object to save, the object must contains an objectID attribute
- * @param callback (optional) the result callback with two arguments:
- * success: boolean set to true if the request was successfull
- * content: the server answer that updateAt and taskID
- */
- saveObject: function(object, callback) {
- var indexObj = this;
- this.as._jsonRequest({ method: 'PUT',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(object.objectID),
- body: object,
- callback: callback });
- },
- /*
- * Override the content of several objects
- *
- * @param objects contains an array of objects to update (each object must contains a objectID attribute)
- * @param callback (optional) the result callback with two arguments:
- * success: boolean set to true if the request was successfull
- * content: the server answer that updateAt and taskID
- */
- saveObjects: function(objects, callback) {
- var indexObj = this;
- var postObj = {requests:[]};
- for (var i = 0; i < objects.length; ++i) {
- var request = { action: 'updateObject',
- objectID: objects[i].objectID,
- body: objects[i] };
- postObj.requests.push(request);
- }
- this.as._jsonRequest({ method: 'POST',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
- body: postObj,
- callback: callback });
- },
- /*
- * Delete an object from the index
- *
- * @param objectID the unique identifier of object to delete
- * @param callback (optional) the result callback with two arguments:
- * success: boolean set to true if the request was successfull
- * content: the server answer that contains 3 elements: createAt, taskId and objectID
- */
- deleteObject: function(objectID, callback) {
- if (objectID == null || objectID.length === 0) {
- callback(false, { message: 'empty objectID'});
- return;
- }
- var indexObj = this;
- this.as._jsonRequest({ method: 'DELETE',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
- callback: callback });
- },
- /*
- * Search inside the index using XMLHttpRequest request (Using a POST query to
- * minimize number of OPTIONS queries: Cross-Origin Resource Sharing).
- *
- * @param query the full text query
- * @param callback the result callback with two arguments:
- * success: boolean set to true if the request was successfull. If false, the content contains the error.
- * content: the server answer that contains the list of results.
- * @param args (optional) if set, contains an object with query parameters:
- * - page: (integer) Pagination parameter used to select the page to retrieve.
- * Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
- * - hitsPerPage: (integer) Pagination parameter used to select the number of hits per page. Defaults to 20.
- * - attributesToRetrieve: a string that contains the list of object attributes you want to retrieve (let you minimize the answer size).
- * Attributes are separated with a comma (for example "name,address").
- * You can also use a string array encoding (for example ["name","address"]).
- * By default, all attributes are retrieved. You can also use '*' to retrieve all values when an attributesToRetrieve setting is specified for your index.
- * - attributesToHighlight: a string that contains the list of attributes you want to highlight according to the query.
- * Attributes are separated by a comma. You can also use a string array encoding (for example ["name","address"]).
- * If an attribute has no match for the query, the raw value is returned. By default all indexed text attributes are highlighted.
- * You can use `*` if you want to highlight all textual attributes. Numerical attributes are not highlighted.
- * A matchLevel is returned for each highlighted attribute and can contain:
- * - full: if all the query terms were found in the attribute,
- * - partial: if only some of the query terms were found,
- * - none: if none of the query terms were found.
- * - attributesToSnippet: a string that contains the list of attributes to snippet alongside the number of words to return (syntax is `attributeName:nbWords`).
- * Attributes are separated by a comma (Example: attributesToSnippet=name:10,content:10).
- * You can also use a string array encoding (Example: attributesToSnippet: ["name:10","content:10"]). By default no snippet is computed.
- * - minWordSizefor1Typo: the minimum number of characters in a query word to accept one typo in this word. Defaults to 3.
- * - minWordSizefor2Typos: the minimum number of characters in a query word to accept two typos in this word. Defaults to 7.
- * - getRankingInfo: if set to 1, the result hits will contain ranking information in _rankingInfo attribute.
- * - aroundLatLng: search for entries around a given latitude/longitude (specified as two floats separated by a comma).
- * For example aroundLatLng=47.316669,5.016670).
- * You can specify the maximum distance in meters with the aroundRadius parameter (in meters) and the precision for ranking with aroundPrecision
- * (for example if you set aroundPrecision=100, two objects that are distant of less than 100m will be considered as identical for "geo" ranking parameter).
- * At indexing, you should specify geoloc of an object with the _geoloc attribute (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
- * - insideBoundingBox: search entries inside a given area defined by the two extreme points of a rectangle (defined by 4 floats: p1Lat,p1Lng,p2Lat,p2Lng).
- * For example insideBoundingBox=47.3165,4.9665,47.3424,5.0201).
- * At indexing, you should specify geoloc of an object with the _geoloc attribute (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
- * - numericFilters: a string that contains the list of numeric filters you want to apply separated by a comma.
- * The syntax of one filter is `attributeName` followed by `operand` followed by `value`. Supported operands are `<`, `<=`, `=`, `>` and `>=`.
- * You can have multiple conditions on one attribute like for example numericFilters=price>100,price<1000.
- * You can also use a string array encoding (for example numericFilters: ["price>100","price<1000"]).
- * - tagFilters: filter the query by a set of tags. You can AND tags by separating them by commas.
- * To OR tags, you must add parentheses. For example, tags=tag1,(tag2,tag3) means tag1 AND (tag2 OR tag3).
- * You can also use a string array encoding, for example tagFilters: ["tag1",["tag2","tag3"]] means tag1 AND (tag2 OR tag3).
- * At indexing, tags should be added in the _tags** attribute of objects (for example {"_tags":["tag1","tag2"]}).
- * - facetFilters: filter the query by a list of facets.
- * Facets are separated by commas and each facet is encoded as `attributeName:value`.
- * For example: `facetFilters=category:Book,author:John%20Doe`.
- * You can also use a string array encoding (for example `["category:Book","author:John%20Doe"]`).
- * - facets: List of object attributes that you want to use for faceting.
- * Attributes are separated with a comma (for example `"category,author"` ).
- * You can also use a JSON string array encoding (for example ["category","author"]).
- * Only attributes that have been added in **attributesForFaceting** index setting can be used in this parameter.
- * You can also use `*` to perform faceting on all attributes specified in **attributesForFaceting**.
- * - queryType: select how the query words are interpreted, it can be one of the following value:
- * - prefixAll: all query words are interpreted as prefixes,
- * - prefixLast: only the last word is interpreted as a prefix (default behavior),
- * - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
- * - optionalWords: a string that contains the list of words that should be considered as optional when found in the query.
- * The list of words is comma separated.
- * - distinct: If set to 1, enable the distinct feature (disabled by default) if the attributeForDistinct index setting is set.
- * This feature is similar to the SQL "distinct" keyword: when enabled in a query with the distinct=1 parameter,
- * all hits containing a duplicate value for the attributeForDistinct attribute are removed from results.
- * For example, if the chosen attribute is show_name and several hits have the same value for show_name, then only the best
- * one is kept and others are removed.
- * @param delay (optional) if set, wait for this delay (in ms) and only send the query if there was no other in the meantime.
- */
- search: function(query, callback, args, delay) {
- var indexObj = this;
- var params = 'query=' + encodeURIComponent(query);
- if (!this.as._isUndefined(args) && args != null) {
- params = this.as._getSearchParams(args, params);
- }
- window.clearTimeout(indexObj.onDelayTrigger);
- if (!this.as._isUndefined(delay) && delay != null && delay > 0) {
- var onDelayTrigger = window.setTimeout( function() {
- indexObj._search(params, callback);
- }, delay);
- indexObj.onDelayTrigger = onDelayTrigger;
- } else {
- this._search(params, callback);
- }
- },
-
- /*
- * Browse all index content
- *
- * @param page Pagination parameter used to select the page to retrieve.
- * Page is zero-based and defaults to 0. Thus, to retrieve the 10th page you need to set page=9
- * @param hitsPerPage: Pagination parameter used to select the number of hits per page. Defaults to 1000.
- */
- browse: function(page, callback, hitsPerPage) {
- var indexObj = this;
- var params = '?page=' + page;
- if (!_.isUndefined(hitsPerPage)) {
- params += '&hitsPerPage=' + hitsPerPage;
- }
- this.as._jsonRequest({ method: 'GET',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/browse' + params,
- callback: callback });
- },
-
- /*
- * Get a Typeahead.js adapter
- * @param searchParams contains an object with query parameters (see search for details)
- */
- ttAdapter: function(params) {
- var self = this;
- return function(query, cb) {
- self.search(query, function(success, content) {
- if (success) {
- cb(content.hits);
- }
- }, params);
- };
- },
-
- /*
- * Wait the publication of a task on the server.
- * All server task are asynchronous and you can check with this method that the task is published.
- *
- * @param taskID the id of the task returned by server
- * @param callback the result callback with with two arguments:
- * success: boolean set to true if the request was successfull
- * content: the server answer that contains the list of results
- */
- waitTask: function(taskID, callback) {
- var indexObj = this;
- this.as._jsonRequest({ method: 'GET',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/task/' + taskID,
- callback: function(success, body) {
- if (success) {
- if (body.status === 'published') {
- callback(true, body);
- } else {
- setTimeout(function() { indexObj.waitTask(taskID, callback); }, 100);
- }
- } else {
- callback(false, body);
- }
- }});
- },
-
- /*
- * This function deletes the index content. Settings and index specific API keys are kept untouched.
- *
- * @param callback (optional) the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the settings object or the error message if a failure occured
- */
- clearIndex: function(callback) {
- var indexObj = this;
- this.as._jsonRequest({ method: 'POST',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/clear',
- callback: callback });
- },
- /*
- * Get settings of this index
- *
- * @param callback (optional) the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the settings object or the error message if a failure occured
- */
- getSettings: function(callback) {
- var indexObj = this;
- this.as._jsonRequest({ method: 'GET',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
- callback: callback });
- },
-
- /*
- * Set settings for this index
- *
- * @param settigns the settings object that can contains :
- * - minWordSizefor1Typo: (integer) the minimum number of characters to accept one typo (default = 3).
- * - minWordSizefor2Typos: (integer) the minimum number of characters to accept two typos (default = 7).
- * - hitsPerPage: (integer) the number of hits per page (default = 10).
- * - attributesToRetrieve: (array of strings) default list of attributes to retrieve in objects.
- * If set to null, all attributes are retrieved.
- * - attributesToHighlight: (array of strings) default list of attributes to highlight.
- * If set to null, all indexed attributes are highlighted.
- * - attributesToSnippet**: (array of strings) default list of attributes to snippet alongside the number of words to return (syntax is attributeName:nbWords).
- * By default no snippet is computed. If set to null, no snippet is computed.
- * - attributesToIndex: (array of strings) the list of fields you want to index.
- * If set to null, all textual and numerical attributes of your objects are indexed, but you should update it to get optimal results.
- * This parameter has two important uses:
- * - Limit the attributes to index: For example if you store a binary image in base64, you want to store it and be able to
- * retrieve it but you don't want to search in the base64 string.
- * - Control part of the ranking*: (see the ranking parameter for full explanation) Matches in attributes at the beginning of
- * the list will be considered more important than matches in attributes further down the list.
- * In one attribute, matching text at the beginning of the attribute will be considered more important than text after, you can disable
- * this behavior if you add your attribute inside `unordered(AttributeName)`, for example attributesToIndex: ["title", "unordered(text)"].
- * - attributesForFaceting: (array of strings) The list of fields you want to use for faceting.
- * All strings in the attribute selected for faceting are extracted and added as a facet. If set to null, no attribute is used for faceting.
- * - attributeForDistinct: (string) The attribute name used for the Distinct feature. This feature is similar to the SQL "distinct" keyword: when enabled
- * in query with the distinct=1 parameter, all hits containing a duplicate value for this attribute are removed from results.
- * For example, if the chosen attribute is show_name and several hits have the same value for show_name, then only the best one is kept and others are removed.
- * - ranking: (array of strings) controls the way results are sorted.
- * We have six available criteria:
- * - typo: sort according to number of typos,
- * - geo: sort according to decreassing distance when performing a geo-location based search,
- * - proximity: sort according to the proximity of query words in hits,
- * - attribute: sort according to the order of attributes defined by attributesToIndex,
- * - exact:
- * - if the user query contains one word: sort objects having an attribute that is exactly the query word before others.
- * For example if you search for the "V" TV show, you want to find it with the "V" query and avoid to have all popular TV
- * show starting by the v letter before it.
- * - if the user query contains multiple words: sort according to the number of words that matched exactly (and not as a prefix).
- * - custom: sort according to a user defined formula set in **customRanking** attribute.
- * The standard order is ["typo", "geo", "proximity", "attribute", "exact", "custom"]
- * - customRanking: (array of strings) lets you specify part of the ranking.
- * The syntax of this condition is an array of strings containing attributes prefixed by asc (ascending order) or desc (descending order) operator.
- * For example `"customRanking" => ["desc(population)", "asc(name)"]`
- * - queryType: Select how the query words are interpreted, it can be one of the following value:
- * - prefixAll: all query words are interpreted as prefixes,
- * - prefixLast: only the last word is interpreted as a prefix (default behavior),
- * - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
- * - highlightPreTag: (string) Specify the string that is inserted before the highlighted parts in the query result (default to "").
- * - highlightPostTag: (string) Specify the string that is inserted after the highlighted parts in the query result (default to "").
- * - optionalWords: (array of strings) Specify a list of words that should be considered as optional when found in the query.
- * @param callback (optional) the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer or the error message if a failure occured
- */
- setSettings: function(settings, callback) {
- var indexObj = this;
- this.as._jsonRequest({ method: 'PUT',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings',
- body: settings,
- callback: callback });
- },
- /*
- * List all existing user keys associated to this index
- *
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer with user keys list or error description if success is false.
- */
- listUserKeys: function(callback) {
- var indexObj = this;
- this.as._jsonRequest({ method: 'GET',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
- callback: callback });
- },
- /*
- * Get ACL of a user key associated to this index
- *
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer with user keys list or error description if success is false.
- */
- getUserKeyACL: function(key, callback) {
- var indexObj = this;
- this.as._jsonRequest({ method: 'GET',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
- callback: callback });
- },
- /*
- * Delete an existing user key associated to this index
- *
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer with user keys list or error description if success is false.
- */
- deleteUserKey: function(key, callback) {
- var indexObj = this;
- this.as._jsonRequest({ method: 'DELETE',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
- callback: callback });
- },
- /*
- * Add an existing user key associated to this index
- *
- * @param acls the list of ACL for this key. Defined by an array of strings that
- * can contains the following values:
- * - search: allow to search (https and http)
- * - addObject: allows to add/update an object in the index (https only)
- * - deleteObject : allows to delete an existing object (https only)
- * - deleteIndex : allows to delete index content (https only)
- * - settings : allows to get index settings (https only)
- * - editSettings : allows to change index settings (https only)
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer with user keys list or error description if success is false.
- */
- addUserKey: function(acls, callback) {
- var indexObj = this;
- var aclsObject = {};
- aclsObject.acl = acls;
- this.as._jsonRequest({ method: 'POST',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
- body: aclsObject,
- callback: callback });
- },
- /*
- * Add an existing user key associated to this index
- *
- * @param acls the list of ACL for this key. Defined by an array of strings that
- * can contains the following values:
- * - search: allow to search (https and http)
- * - addObject: allows to add/update an object in the index (https only)
- * - deleteObject : allows to delete an existing object (https only)
- * - deleteIndex : allows to delete index content (https only)
- * - settings : allows to get index settings (https only)
- * - editSettings : allows to change index settings (https only)
- * @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
- * @param maxQueriesPerIPPerHour Specify the maximum number of API calls allowed from an IP address per hour.
- * @param maxHitsPerQuery Specify the maximum number of hits this API key can retrieve in one call.
- * @param callback the result callback with two arguments
- * success: boolean set to true if the request was successfull
- * content: the server answer with user keys list or error description if success is false.
- */
- addUserKeyWithValidity: function(acls, validity, maxQueriesPerIPPerHour, maxHitsPerQuery, callback) {
- var indexObj = this;
- var aclsObject = {};
- aclsObject.acl = acls;
- aclsObject.validity = validity;
- aclsObject.maxQueriesPerIPPerHour = maxQueriesPerIPPerHour;
- aclsObject.maxHitsPerQuery = maxHitsPerQuery;
- this.as._jsonRequest({ method: 'POST',
- url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
- body: aclsObject,
- callback: callback });
- },
- ///
- /// Internal methods only after this line
- ///
- _search: function(params, callback) {
- if (this.as.jsonp == null) {
- var self = this;
- this.as._waitReady(function() { self._search(params, callback); });
- return;
- }
- var pObj = {params: params, apiKey: this.as.apiKey, appID: this.as.applicationID};
- if (this.as.tagFilters) {
- pObj['X-Algolia-TagFilters'] = this.as.tagFilters;
- }
- if (this.as.userToken) {
- pObj['X-Algolia-UserToken'] = this.as.userToken;
- }
- if (this.as.jsonp) {
- this.as._jsonRequest({ cache: this.cache,
- method: 'GET', jsonp: true,
- url: '/1/indexes/' + encodeURIComponent(this.indexName),
- body: pObj,
- callback: callback });
- } else {
- this.as._jsonRequest({ cache: this.cache,
- method: 'POST',
- url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/query',
- body: pObj,
- callback: callback });
- }
- },
-
- // internal attributes
- as: null,
- indexName: null,
- cache: {},
- typeAheadArgs: null,
- typeAheadValueOption: null,
- emptyConstructor: function() {}
-};
-
-/*
- * Copyright (c) 2014 Algolia
- * http://www.algolia.com/
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-(function($) {
- var extend = function(out) {
- out = out || {};
- for (var i = 1; i < arguments.length; i++) {
- if (!arguments[i]) {
- continue;
- }
- for (var key in arguments[i]) {
- if (arguments[i].hasOwnProperty(key)) {
- out[key] = arguments[i][key];
- }
- }
- }
- return out;
- };
-
- /**
- * Algolia Search Helper providing faceting and disjunctive faceting
- * @param {AlgoliaSearch} client an AlgoliaSearch client
- * @param {string} index the index name to query
- * @param {hash} options an associative array defining the hitsPerPage, list of facets and list of disjunctive facets
- */
- window.AlgoliaSearchHelper = function(client, index, options) {
- /// Default options
- var defaults = {
- facets: [], // list of facets to compute
- disjunctiveFacets: [], // list of disjunctive facets to compute
- hitsPerPage: 20 // number of hits per page
- };
-
- this.init(client, index, extend({}, defaults, options));
- };
-
- AlgoliaSearchHelper.prototype = {
- /**
- * Initialize a new AlgoliaSearchHelper
- * @param {AlgoliaSearch} client an AlgoliaSearch client
- * @param {string} index the index name to query
- * @param {hash} options an associative array defining the hitsPerPage, list of facets and list of disjunctive facets
- * @return {AlgoliaSearchHelper}
- */
- init: function(client, index, options) {
- this.client = client;
- this.index = index;
- this.options = options;
- this.page = 0;
- this.refinements = {};
- this.disjunctiveRefinements = {};
- },
-
- /**
- * Perform a query
- * @param {string} q the user query
- * @param {function} searchCallback the result callback called with two arguments:
- * success: boolean set to true if the request was successfull
- * content: the query answer with an extra 'disjunctiveFacets' attribute
- */
- search: function(q, searchCallback, searchParams) {
- this.q = q;
- this.searchCallback = searchCallback;
- this.searchParams = searchParams || {};
- this.page = this.page || 0;
- this.refinements = this.refinements || {};
- this.disjunctiveRefinements = this.disjunctiveRefinements || {};
- this._search();
- },
-
- /**
- * Remove all refinements (disjunctive + conjunctive)
- */
- clearRefinements: function() {
- this.disjunctiveRefinements = {};
- this.refinements = {};
- },
-
- /**
- * Ensure a facet refinement exists
- * @param {string} facet the facet to refine
- * @param {string} value the associated value
- */
- addDisjunctiveRefine: function(facet, value) {
- this.disjunctiveRefinements = this.disjunctiveRefinements || {};
- this.disjunctiveRefinements[facet] = this.disjunctiveRefinements[facet] || {};
- this.disjunctiveRefinements[facet][value] = true;
- },
-
- /**
- * Ensure a facet refinement does not exist
- * @param {string} facet the facet to refine
- * @param {string} value the associated value
- */
- removeDisjunctiveRefine: function(facet, value) {
- this.disjunctiveRefinements = this.disjunctiveRefinements || {};
- this.disjunctiveRefinements[facet] = this.disjunctiveRefinements[facet] || {};
- try {
- delete this.disjunctiveRefinements[facet][value];
- } catch (e) {
- this.disjunctiveRefinements[facet][value] = undefined; // IE compat
- }
- },
-
- /**
- * Ensure a facet refinement exists
- * @param {string} facet the facet to refine
- * @param {string} value the associated value
- */
- addRefine: function(facet, value) {
- var refinement = facet + ':' + value;
- this.refinements = this.refinements || {};
- this.refinements[refinement] = true;
- },
-
- /**
- * Ensure a facet refinement does not exist
- * @param {string} facet the facet to refine
- * @param {string} value the associated value
- */
- removeRefine: function(facet, value) {
- var refinement = facet + ':' + value;
- this.refinements = this.refinements || {};
- this.refinements[refinement] = false;
- },
-
- /**
- * Toggle refinement state of a facet
- * @param {string} facet the facet to refine
- * @param {string} value the associated value
- * @return {boolean} true if the facet has been found
- */
- toggleRefine: function(facet, value) {
- for (var i = 0; i < this.options.facets.length; ++i) {
- if (this.options.facets[i] == facet) {
- var refinement = facet + ':' + value;
- this.refinements[refinement] = !this.refinements[refinement];
- this.page = 0;
- this._search();
- return true;
- }
- }
- this.disjunctiveRefinements[facet] = this.disjunctiveRefinements[facet] || {};
- for (var j = 0; j < this.options.disjunctiveFacets.length; ++j) {
- if (this.options.disjunctiveFacets[j] == facet) {
- this.disjunctiveRefinements[facet][value] = !this.disjunctiveRefinements[facet][value];
- this.page = 0;
- this._search();
- return true;
- }
- }
- return false;
- },
-
- /**
- * Check the refinement state of a facet
- * @param {string} facet the facet
- * @param {string} value the associated value
- * @return {boolean} true if refined
- */
- isRefined: function(facet, value) {
- var refinement = facet + ':' + value;
- if (this.refinements[refinement]) {
- return true;
- }
- if (this.disjunctiveRefinements[facet] && this.disjunctiveRefinements[facet][value]) {
- return true;
- }
- return false;
- },
-
- /**
- * Go to next page
- */
- nextPage: function() {
- this._gotoPage(this.page + 1);
- },
-
- /**
- * Go to previous page
- */
- previousPage: function() {
- if (this.page > 0) {
- this._gotoPage(this.page - 1);
- }
- },
-
- /**
- * Goto a page
- * @param {integer} page The page number
- */
- gotoPage: function(page) {
- this._gotoPage(page);
- },
-
- /**
- * Configure the page but do not trigger a reload
- * @param {integer} page The page number
- */
- setPage: function(page) {
- this.page = page;
- },
-
- ///////////// PRIVATE
-
- /**
- * Goto a page
- * @param {integer} page The page number
- */
- _gotoPage: function(page) {
- this.page = page;
- this._search();
- },
-
- /**
- * Perform the underlying queries
- */
- _search: function() {
- this.client.startQueriesBatch();
- this.client.addQueryInBatch(this.index, this.q, this._getHitsSearchParams());
- for (var i = 0; i < this.options.disjunctiveFacets.length; ++i) {
- this.client.addQueryInBatch(this.index, this.q, this._getDisjunctiveFacetSearchParams(this.options.disjunctiveFacets[i]));
- }
- var self = this;
- this.client.sendQueriesBatch(function(success, content) {
- if (!success) {
- self.searchCallback(false, content);
- return;
- }
- var aggregatedAnswer = content.results[0];
- aggregatedAnswer.disjunctiveFacets = {};
- aggregatedAnswer.facetStats = {};
- for (var i = 1; i < content.results.length; ++i) {
- for (var facet in content.results[i].facets) {
- aggregatedAnswer.disjunctiveFacets[facet] = content.results[i].facets[facet];
- if (self.disjunctiveRefinements[facet]) {
- for (var value in self.disjunctiveRefinements[facet]) {
- if (!aggregatedAnswer.disjunctiveFacets[facet][value] && self.disjunctiveRefinements[facet][value]) {
- aggregatedAnswer.disjunctiveFacets[facet][value] = 0;
- }
- }
- }
- }
- for (var stats in content.results[i].facets_stats)
- {
- aggregatedAnswer.facetStats[stats] = content.results[i].facets_stats[stats];
- }
- }
- self.searchCallback(true, aggregatedAnswer);
- });
- },
-
- /**
- * Build search parameters used to fetch hits
- * @return {hash}
- */
- _getHitsSearchParams: function() {
- return extend({}, {
- hitsPerPage: this.options.hitsPerPage,
- page: this.page,
- facets: this.options.facets,
- facetFilters: this._getFacetFilters()
- }, this.searchParams);
- },
-
- /**
- * Build search parameters used to fetch a disjunctive facet
- * @param {string} facet the associated facet name
- * @return {hash}
- */
- _getDisjunctiveFacetSearchParams: function(facet) {
- return extend({}, this.searchParams, {
- hitsPerPage: 1,
- page: 0,
- facets: facet,
- facetFilters: this._getFacetFilters(facet)
- });
- },
-
- /**
- * Build facetFilters parameter based on current refinements
- * @param {string} facet if set, the current disjunctive facet
- * @return {hash}
- */
- _getFacetFilters: function(facet) {
- var facetFilters = [];
- for (var refinement in this.refinements) {
- if (this.refinements[refinement]) {
- facetFilters.push(refinement);
- }
- }
- for (var disjunctiveRefinement in this.disjunctiveRefinements) {
- if (disjunctiveRefinement != facet) {
- var refinements = [];
- for (var value in this.disjunctiveRefinements[disjunctiveRefinement]) {
- if (this.disjunctiveRefinements[disjunctiveRefinement][value]) {
- refinements.push(disjunctiveRefinement + ':' + value);
- }
- }
- if (refinements.length > 0) {
- facetFilters.push(refinements);
- }
- }
- }
- return facetFilters;
- }
- };
-})();
-
-/*
- json2.js
- 2014-02-04
-
- Public Domain.
-
- NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
-
- See http://www.JSON.org/js.html
-
-
- This code should be minified before deployment.
- See http://javascript.crockford.com/jsmin.html
-
- USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
- NOT CONTROL.
-
-
- This file creates a global JSON object containing two methods: stringify
- and parse.
-
- JSON.stringify(value, replacer, space)
- value any JavaScript value, usually an object or array.
-
- replacer an optional parameter that determines how object
- values are stringified for objects. It can be a
- function or an array of strings.
-
- space an optional parameter that specifies the indentation
- of nested structures. If it is omitted, the text will
- be packed without extra whitespace. If it is a number,
- it will specify the number of spaces to indent at each
- level. If it is a string (such as '\t' or ' '),
- it contains the characters used to indent at each level.
-
- This method produces a JSON text from a JavaScript value.
-
- When an object value is found, if the object contains a toJSON
- method, its toJSON method will be called and the result will be
- stringified. A toJSON method does not serialize: it returns the
- value represented by the name/value pair that should be serialized,
- or undefined if nothing should be serialized. The toJSON method
- will be passed the key associated with the value, and this will be
- bound to the value
-
- For example, this would serialize Dates as ISO strings.
-
- Date.prototype.toJSON = function (key) {
- function f(n) {
- // Format integers to have at least two digits.
- return n < 10 ? '0' + n : n;
- }
-
- return this.getUTCFullYear() + '-' +
- f(this.getUTCMonth() + 1) + '-' +
- f(this.getUTCDate()) + 'T' +
- f(this.getUTCHours()) + ':' +
- f(this.getUTCMinutes()) + ':' +
- f(this.getUTCSeconds()) + 'Z';
- };
-
- You can provide an optional replacer method. It will be passed the
- key and value of each member, with this bound to the containing
- object. The value that is returned from your method will be
- serialized. If your method returns undefined, then the member will
- be excluded from the serialization.
-
- If the replacer parameter is an array of strings, then it will be
- used to select the members to be serialized. It filters the results
- such that only members with keys listed in the replacer array are
- stringified.
-
- Values that do not have JSON representations, such as undefined or
- functions, will not be serialized. Such values in objects will be
- dropped; in arrays they will be replaced with null. You can use
- a replacer function to replace those with JSON values.
- JSON.stringify(undefined) returns undefined.
-
- The optional space parameter produces a stringification of the
- value that is filled with line breaks and indentation to make it
- easier to read.
-
- If the space parameter is a non-empty string, then that string will
- be used for indentation. If the space parameter is a number, then
- the indentation will be that many spaces.
-
- Example:
-
- text = JSON.stringify(['e', {pluribus: 'unum'}]);
- // text is '["e",{"pluribus":"unum"}]'
-
-
- text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
- // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
-
- text = JSON.stringify([new Date()], function (key, value) {
- return this[key] instanceof Date ?
- 'Date(' + this[key] + ')' : value;
- });
- // text is '["Date(---current time---)"]'
-
-
- JSON.parse(text, reviver)
- This method parses a JSON text to produce an object or array.
- It can throw a SyntaxError exception.
-
- The optional reviver parameter is a function that can filter and
- transform the results. It receives each of the keys and values,
- and its return value is used instead of the original value.
- If it returns what it received, then the structure is not modified.
- If it returns undefined then the member is deleted.
-
- Example:
-
- // Parse the text. Values that look like ISO date strings will
- // be converted to Date objects.
-
- myData = JSON.parse(text, function (key, value) {
- var a;
- if (typeof value === 'string') {
- a =
-/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
- if (a) {
- return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
- +a[5], +a[6]));
- }
- }
- return value;
- });
-
- myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
- var d;
- if (typeof value === 'string' &&
- value.slice(0, 5) === 'Date(' &&
- value.slice(-1) === ')') {
- d = new Date(value.slice(5, -1));
- if (d) {
- return d;
- }
- }
- return value;
- });
-
-
- This is a reference implementation. You are free to copy, modify, or
- redistribute.
-*/
-
-/*jslint evil: true, regexp: true */
-
-/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
- call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
- getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
- lastIndex, length, parse, prototype, push, replace, slice, stringify,
- test, toJSON, toString, valueOf
-*/
-
-
-// Create a JSON object only if one does not already exist. We create the
-// methods in a closure to avoid creating global variables.
-
-if (typeof JSON !== 'object') {
- JSON = {};
-}
-
-(function () {
- 'use strict';
-
- function f(n) {
- // Format integers to have at least two digits.
- return n < 10 ? '0' + n : n;
- }
-
- if (typeof Date.prototype.toJSON !== 'function') {
-
- Date.prototype.toJSON = function () {
-
- return isFinite(this.valueOf())
- ? this.getUTCFullYear() + '-' +
- f(this.getUTCMonth() + 1) + '-' +
- f(this.getUTCDate()) + 'T' +
- f(this.getUTCHours()) + ':' +
- f(this.getUTCMinutes()) + ':' +
- f(this.getUTCSeconds()) + 'Z'
- : null;
- };
-
- String.prototype.toJSON =
- Number.prototype.toJSON =
- Boolean.prototype.toJSON = function () {
- return this.valueOf();
- };
- }
-
- var cx,
- escapable,
- gap,
- indent,
- meta,
- rep;
-
-
- function quote(string) {
-
-// If the string contains no control characters, no quote characters, and no
-// backslash characters, then we can safely slap some quotes around it.
-// Otherwise we must also replace the offending characters with safe escape
-// sequences.
-
- escapable.lastIndex = 0;
- return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
- var c = meta[a];
- return typeof c === 'string'
- ? c
- : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- }) + '"' : '"' + string + '"';
- }
-
-
- function str(key, holder) {
-
-// Produce a string from holder[key].
-
- var i, // The loop counter.
- k, // The member key.
- v, // The member value.
- length,
- mind = gap,
- partial,
- value = holder[key];
-
-// If the value has a toJSON method, call it to obtain a replacement value.
-
- if (value && typeof value === 'object' &&
- typeof value.toJSON === 'function') {
- value = value.toJSON(key);
- }
-
-// If we were called with a replacer function, then call the replacer to
-// obtain a replacement value.
-
- if (typeof rep === 'function') {
- value = rep.call(holder, key, value);
- }
-
-// What happens next depends on the value's type.
-
- switch (typeof value) {
- case 'string':
- return quote(value);
-
- case 'number':
-
-// JSON numbers must be finite. Encode non-finite numbers as null.
-
- return isFinite(value) ? String(value) : 'null';
-
- case 'boolean':
- case 'null':
-
-// If the value is a boolean or null, convert it to a string. Note:
-// typeof null does not produce 'null'. The case is included here in
-// the remote chance that this gets fixed someday.
-
- return String(value);
-
-// If the type is 'object', we might be dealing with an object or an array or
-// null.
-
- case 'object':
-
-// Due to a specification blunder in ECMAScript, typeof null is 'object',
-// so watch out for that case.
-
- if (!value) {
- return 'null';
- }
-
-// Make an array to hold the partial results of stringifying this object value.
-
- gap += indent;
- partial = [];
-
-// Is the value an array?
-
- if (Object.prototype.toString.apply(value) === '[object Array]') {
-
-// The value is an array. Stringify every element. Use null as a placeholder
-// for non-JSON values.
-
- length = value.length;
- for (i = 0; i < length; i += 1) {
- partial[i] = str(i, value) || 'null';
- }
-
-// Join all of the elements together, separated with commas, and wrap them in
-// brackets.
-
- v = partial.length === 0
- ? '[]'
- : gap
- ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
- : '[' + partial.join(',') + ']';
- gap = mind;
- return v;
- }
-
-// If the replacer is an array, use it to select the members to be stringified.
-
- if (rep && typeof rep === 'object') {
- length = rep.length;
- for (i = 0; i < length; i += 1) {
- if (typeof rep[i] === 'string') {
- k = rep[i];
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- } else {
-
-// Otherwise, iterate through all of the keys in the object.
-
- for (k in value) {
- if (Object.prototype.hasOwnProperty.call(value, k)) {
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- }
-
-// Join all of the member texts together, separated with commas,
-// and wrap them in braces.
-
- v = partial.length === 0
- ? '{}'
- : gap
- ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
- : '{' + partial.join(',') + '}';
- gap = mind;
- return v;
- }
- }
-
-// If the JSON object does not yet have a stringify method, give it one.
-
- if (typeof JSON.stringify !== 'function') {
- escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
- meta = { // table of character substitutions
- '\b': '\\b',
- '\t': '\\t',
- '\n': '\\n',
- '\f': '\\f',
- '\r': '\\r',
- '"' : '\\"',
- '\\': '\\\\'
- };
- JSON.stringify = function (value, replacer, space) {
-
-// The stringify method takes a value and an optional replacer, and an optional
-// space parameter, and returns a JSON text. The replacer can be a function
-// that can replace values, or an array of strings that will select the keys.
-// A default replacer method can be provided. Use of the space parameter can
-// produce text that is more easily readable.
-
- var i;
- gap = '';
- indent = '';
-
-// If the space parameter is a number, make an indent string containing that
-// many spaces.
-
- if (typeof space === 'number') {
- for (i = 0; i < space; i += 1) {
- indent += ' ';
- }
-
-// If the space parameter is a string, it will be used as the indent string.
-
- } else if (typeof space === 'string') {
- indent = space;
- }
-
-// If there is a replacer, it must be a function or an array.
-// Otherwise, throw an error.
-
- rep = replacer;
- if (replacer && typeof replacer !== 'function' &&
- (typeof replacer !== 'object' ||
- typeof replacer.length !== 'number')) {
- throw new Error('JSON.stringify');
- }
-
-// Make a fake root object containing our value under the key of ''.
-// Return the result of stringifying the value.
-
- return str('', {'': value});
- };
- }
-
-
-// If the JSON object does not yet have a parse method, give it one.
-
- if (typeof JSON.parse !== 'function') {
- cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
- JSON.parse = function (text, reviver) {
-
-// The parse method takes a text and an optional reviver function, and returns
-// a JavaScript value if the text is a valid JSON text.
-
- var j;
-
- function walk(holder, key) {
-
-// The walk method is used to recursively walk the resulting structure so
-// that modifications can be made.
-
- var k, v, value = holder[key];
- if (value && typeof value === 'object') {
- for (k in value) {
- if (Object.prototype.hasOwnProperty.call(value, k)) {
- v = walk(value, k);
- if (v !== undefined) {
- value[k] = v;
- } else {
- delete value[k];
- }
- }
- }
- }
- return reviver.call(holder, key, value);
- }
-
-
-// Parsing happens in four stages. In the first stage, we replace certain
-// Unicode characters with escape sequences. JavaScript handles many characters
-// incorrectly, either silently deleting them, or treating them as line endings.
-
- text = String(text);
- cx.lastIndex = 0;
- if (cx.test(text)) {
- text = text.replace(cx, function (a) {
- return '\\u' +
- ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- });
- }
-
-// In the second stage, we run the text against regular expressions that look
-// for non-JSON patterns. We are especially concerned with '()' and 'new'
-// because they can cause invocation, and '=' because it can cause mutation.
-// But just to be safe, we want to reject all unexpected forms.
-
-// We split the second stage into 4 regexp operations in order to work around
-// crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
-// replace all simple value tokens with ']' characters. Third, we delete all
-// open brackets that follow a colon or comma or that begin the text. Finally,
-// we look to see that the remaining characters are only whitespace or ']' or
-// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
-
- if (/^[\],:{}\s]*$/
- .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
- .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
- .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
-
-// In the third stage we use the eval function to compile the text into a
-// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
-// in JavaScript: it can begin a block or an object literal. We wrap the text
-// in parens to eliminate the ambiguity.
-
- j = eval('(' + text + ')');
-
-// In the optional fourth stage, we recursively walk the new structure, passing
-// each name/value pair to a reviver function for possible transformation.
-
- return typeof reviver === 'function'
- ? walk({'': j}, '')
- : j;
- }
-
-// If the text is not JSON parseable, then a SyntaxError is thrown.
-
- throw new SyntaxError('JSON.parse');
- };
- }
-}());
\ No newline at end of file
+function AlgoliaExplainResults(a,b,c){function d(a,b){var c=[];if("object"==typeof a&&"matchedWords"in a&&"value"in a){for(var e=!1,f=0;f0?h[0]:"",f.subtitles=[],"undefined"!=typeof c)for(var i=0;i.5&&this.hosts.reverse(),this.hosts.push(this._isUndefined(c)||null==c?("https:"==document.location.protocol?"https":"http")+"://"+e[g]:"https"===c||"HTTPS"===c?"https://"+e[g]:"http://"+e[g]);Math.random()>.5&&this.hosts.reverse(),this.jsonp=null,this.jsonpWait=0,this._jsonRequest({method:"GET",url:"/1/isalive",callback:function(a){f.jsonp=!a}}),this.extraHeaders=[]};AlgoliaSearch.prototype={deleteIndex:function(a,b){this._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(a),callback:b})},moveIndex:function(a,b,c){var d={operation:"move",destination:b};this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(a)+"/operation",body:d,callback:c})},copyIndex:function(a,b,c){var d={operation:"copy",destination:b};this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(a)+"/operation",body:d,callback:c})},getLogs:function(a,b,c){this._isUndefined(b)&&(b=0),this._isUndefined(c)&&(c=10),this._jsonRequest({method:"GET",url:"/1/logs?offset="+b+"&length="+c,callback:a})},listIndexes:function(a){this._jsonRequest({method:"GET",url:"/1/indexes",callback:a})},initIndex:function(a){return new this.Index(this,a)},listUserKeys:function(a){this._jsonRequest({method:"GET",url:"/1/keys",callback:a})},getUserKeyACL:function(a,b){this._jsonRequest({method:"GET",url:"/1/keys/"+a,callback:b})},deleteUserKey:function(a,b){this._jsonRequest({method:"DELETE",url:"/1/keys/"+a,callback:b})},addUserKey:function(a,b){var c={};c.acl=a,this._jsonRequest({method:"POST",url:"/1/keys",body:c,callback:b})},addUserKeyWithValidity:function(a,b,c,d,e){var f=this,g={};g.acl=a,g.validity=b,g.maxQueriesPerIPPerHour=c,g.maxHitsPerQuery=d,this._jsonRequest({method:"POST",url:"/1/indexes/"+f.indexName+"/keys",body:g,callback:e})},setSecurityTags:function(a){if("[object Array]"===Object.prototype.toString.call(a)){for(var b=[],c=0;c0){var f=window.setTimeout(function(){c._sendQueriesBatch(d,a)},b);c.onDelayTrigger=f}else this._sendQueriesBatch(d,a)},Index:function(a,b){this.indexName=b,this.as=a,this.typeAheadArgs=null,this.typeAheadValueOption=null},setExtraHeader:function(a,b){this.extraHeaders.push({key:a,value:b})},_sendQueriesBatch:function(a,b){if(null==this.jsonp){var c=this;return void this._waitReady(function(){c._sendQueriesBatch(a,b)})}if(this.jsonp){for(var d="",e=0;e2e3&&(this.jsonp=!0),setTimeout(a,100))},_getSearchParams:function(a,b){if(this._isUndefined(a)||null==a)return b;for(var c in a)null!=c&&a.hasOwnProperty(c)&&(b+=0===b.length?"?":"&",b+=c+"="+encodeURIComponent("[object Array]"===Object.prototype.toString.call(a[c])?JSON.stringify(a[c]):a[c]));return b},_isUndefined:function(a){return void 0===a},applicationID:null,apiKey:null,tagFilters:null,userToken:null,hosts:[],cache:{},extraHeaders:[]},AlgoliaSearch.prototype.Index.prototype={clearCache:function(){this.cache={}},addObject:function(a,b,c){var d=this;this.as._jsonRequest(this.as._isUndefined(c)?{method:"POST",url:"/1/indexes/"+encodeURIComponent(d.indexName),body:a,callback:b}:{method:"PUT",url:"/1/indexes/"+encodeURIComponent(d.indexName)+"/"+encodeURIComponent(c),body:a,callback:b})},addObjects:function(a,b){for(var c=this,d={requests:[]},e=0;e0){var g=window.setTimeout(function(){e._search(f,b)},d);e.onDelayTrigger=g}else this._search(f,b)},browse:function(a,b,c){var d=this,e="?page="+a;_.isUndefined(c)||(e+="&hitsPerPage="+c),this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(d.indexName)+"/browse"+e,callback:b})},ttAdapter:function(a){var b=this;return function(c,d){b.search(c,function(a,b){a&&d(b.hits)},a)}},waitTask:function(a,b){var c=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/task/"+a,callback:function(d,e){d?"published"===e.status?b(!0,e):setTimeout(function(){c.waitTask(a,b)},100):b(!1,e)}})},clearIndex:function(a){var b=this;this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/clear",callback:a})},getSettings:function(a){var b=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/settings",callback:a})},setSettings:function(a,b){var c=this;this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/settings",body:a,callback:b})},listUserKeys:function(a){var b=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(b.indexName)+"/keys",callback:a})},getUserKeyACL:function(a,b){var c=this;this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys/"+a,callback:b})},deleteUserKey:function(a,b){var c=this;this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys/"+a,callback:b})},addUserKey:function(a,b){var c=this,d={};d.acl=a,this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(c.indexName)+"/keys",body:d,callback:b})},addUserKeyWithValidity:function(a,b,c,d,e){var f=this,g={};g.acl=a,g.validity=b,g.maxQueriesPerIPPerHour=c,g.maxHitsPerQuery=d,this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(f.indexName)+"/keys",body:g,callback:e})},_search:function(a,b){if(null==this.as.jsonp){var c=this;return void this.as._waitReady(function(){c._search(a,b)})}var d={params:a,apiKey:this.as.apiKey,appID:this.as.applicationID};this.as.tagFilters&&(d["X-Algolia-TagFilters"]=this.as.tagFilters),this.as.userToken&&(d["X-Algolia-UserToken"]=this.as.userToken),this.as._jsonRequest(this.as.jsonp?{cache:this.cache,method:"GET",jsonp:!0,url:"/1/indexes/"+encodeURIComponent(this.indexName),body:d,callback:b}:{cache:this.cache,method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/query",body:d,callback:b})},as:null,indexName:null,cache:{},typeAheadArgs:null,typeAheadValueOption:null,emptyConstructor:function(){}},function(){var a=function(a){a=a||{};for(var b=1;b0&&this._gotoPage(this.page-1)},gotoPage:function(a){this._gotoPage(a)},setPage:function(a){this.page=a},_gotoPage:function(a){this.page=a,this._search()},_search:function(){this.client.startQueriesBatch(),this.client.addQueryInBatch(this.index,this.q,this._getHitsSearchParams());for(var a=0;a0&&b.push(e)}return b}}}(),"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(a){return 10>a?"0"+a:a}function quote(a){return escapable.lastIndex=0,escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return"string"==typeof b?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,g,h=gap,i=b[a];switch(i&&"object"==typeof i&&"function"==typeof i.toJSON&&(i=i.toJSON(a)),"function"==typeof rep&&(i=rep.call(b,a,i)),typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";if(gap+=indent,g=[],"[object Array]"===Object.prototype.toString.apply(i)){for(f=i.length,c=0;f>c;c+=1)g[c]=str(c,i)||"null";return e=0===g.length?"[]":gap?"[\n"+gap+g.join(",\n"+gap)+"\n"+h+"]":"["+g.join(",")+"]",gap=h,e}if(rep&&"object"==typeof rep)for(f=rep.length,c=0;f>c;c+=1)"string"==typeof rep[c]&&(d=rep[c],e=str(d,i),e&&g.push(quote(d)+(gap?": ":":")+e));else for(d in i)Object.prototype.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&g.push(quote(d)+(gap?": ":":")+e));return e=0===g.length?"{}":gap?"{\n"+gap+g.join(",\n"+gap)+"\n"+h+"}":"{"+g.join(",")+"}",gap=h,e}}"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var cx,escapable,gap,indent,meta,rep;"function"!=typeof JSON.stringify&&(escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(a,b,c){var d;if(gap="",indent="","number"==typeof c)for(d=0;c>d;d+=1)indent+=" ";else"string"==typeof c&&(indent=c);if(rep=b,b&&"function"!=typeof b&&("object"!=typeof b||"number"!=typeof b.length))throw new Error("JSON.stringify");return str("",{"":a})}),"function"!=typeof JSON.parse&&(cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&"object"==typeof e)for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&(d=walk(e,c),void 0!==d?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;if(text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})),/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}();
\ No newline at end of file
diff --git a/services/web/public/js/libs/moment-2.9.0.js b/services/web/public/js/libs/moment-2.9.0.js
index d98958d75a..d301ddbbe8 100755
--- a/services/web/public/js/libs/moment-2.9.0.js
+++ b/services/web/public/js/libs/moment-2.9.0.js
@@ -1,3043 +1,7 @@
//! moment.js
-//! version : 2.9.0
+//! version : 2.13.0
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
//! license : MIT
//! momentjs.com
-
-(function (undefined) {
- /************************************
- Constants
- ************************************/
-
- var moment,
- VERSION = '2.9.0',
- // the global-scope this is NOT the global object in Node.js
- globalScope = (typeof global !== 'undefined' && (typeof window === 'undefined' || window === global.window)) ? global : this,
- oldGlobalMoment,
- round = Math.round,
- hasOwnProperty = Object.prototype.hasOwnProperty,
- i,
-
- YEAR = 0,
- MONTH = 1,
- DATE = 2,
- HOUR = 3,
- MINUTE = 4,
- SECOND = 5,
- MILLISECOND = 6,
-
- // internal storage for locale config files
- locales = {},
-
- // extra moment internal properties (plugins register props here)
- momentProperties = [],
-
- // check for nodeJS
- hasModule = (typeof module !== 'undefined' && module && module.exports),
-
- // ASP.NET json date format regex
- aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
- aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,
-
- // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
- // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
- isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,
-
- // format tokens
- 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,
- localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,
-
- // parsing token regexes
- parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
- parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
- parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999
- parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
- parseTokenDigits = /\d+/, // nonzero number of digits
- 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.
- parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z
- parseTokenT = /T/i, // T (ISO separator)
- parseTokenOffsetMs = /[\+\-]?\d+/, // 1234567890123
- parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
-
- //strict parsing regexes
- parseTokenOneDigit = /\d/, // 0 - 9
- parseTokenTwoDigits = /\d\d/, // 00 - 99
- parseTokenThreeDigits = /\d{3}/, // 000 - 999
- parseTokenFourDigits = /\d{4}/, // 0000 - 9999
- parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999
- parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf
-
- // iso 8601 regex
- // 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)
- 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)?)?$/,
-
- isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
-
- isoDates = [
- ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/],
- ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/],
- ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/],
- ['GGGG-[W]WW', /\d{4}-W\d{2}/],
- ['YYYY-DDD', /\d{4}-\d{3}/]
- ],
-
- // iso time formats and regexes
- isoTimes = [
- ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/],
- ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
- ['HH:mm', /(T| )\d\d:\d\d/],
- ['HH', /(T| )\d\d/]
- ],
-
- // timezone chunker '+10:00' > ['10', '00'] or '-1530' > ['-', '15', '30']
- parseTimezoneChunker = /([\+\-]|\d\d)/gi,
-
- // getter and setter names
- proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
- unitMillisecondFactors = {
- 'Milliseconds' : 1,
- 'Seconds' : 1e3,
- 'Minutes' : 6e4,
- 'Hours' : 36e5,
- 'Days' : 864e5,
- 'Months' : 2592e6,
- 'Years' : 31536e6
- },
-
- unitAliases = {
- ms : 'millisecond',
- s : 'second',
- m : 'minute',
- h : 'hour',
- d : 'day',
- D : 'date',
- w : 'week',
- W : 'isoWeek',
- M : 'month',
- Q : 'quarter',
- y : 'year',
- DDD : 'dayOfYear',
- e : 'weekday',
- E : 'isoWeekday',
- gg: 'weekYear',
- GG: 'isoWeekYear'
- },
-
- camelFunctions = {
- dayofyear : 'dayOfYear',
- isoweekday : 'isoWeekday',
- isoweek : 'isoWeek',
- weekyear : 'weekYear',
- isoweekyear : 'isoWeekYear'
- },
-
- // format function strings
- formatFunctions = {},
-
- // default relative time thresholds
- relativeTimeThresholds = {
- s: 45, // seconds to minute
- m: 45, // minutes to hour
- h: 22, // hours to day
- d: 26, // days to month
- M: 11 // months to year
- },
-
- // tokens to ordinalize and pad
- ordinalizeTokens = 'DDD w W M D d'.split(' '),
- paddedTokens = 'M D H h m s w W'.split(' '),
-
- formatTokenFunctions = {
- M : function () {
- return this.month() + 1;
- },
- MMM : function (format) {
- return this.localeData().monthsShort(this, format);
- },
- MMMM : function (format) {
- return this.localeData().months(this, format);
- },
- D : function () {
- return this.date();
- },
- DDD : function () {
- return this.dayOfYear();
- },
- d : function () {
- return this.day();
- },
- dd : function (format) {
- return this.localeData().weekdaysMin(this, format);
- },
- ddd : function (format) {
- return this.localeData().weekdaysShort(this, format);
- },
- dddd : function (format) {
- return this.localeData().weekdays(this, format);
- },
- w : function () {
- return this.week();
- },
- W : function () {
- return this.isoWeek();
- },
- YY : function () {
- return leftZeroFill(this.year() % 100, 2);
- },
- YYYY : function () {
- return leftZeroFill(this.year(), 4);
- },
- YYYYY : function () {
- return leftZeroFill(this.year(), 5);
- },
- YYYYYY : function () {
- var y = this.year(), sign = y >= 0 ? '+' : '-';
- return sign + leftZeroFill(Math.abs(y), 6);
- },
- gg : function () {
- return leftZeroFill(this.weekYear() % 100, 2);
- },
- gggg : function () {
- return leftZeroFill(this.weekYear(), 4);
- },
- ggggg : function () {
- return leftZeroFill(this.weekYear(), 5);
- },
- GG : function () {
- return leftZeroFill(this.isoWeekYear() % 100, 2);
- },
- GGGG : function () {
- return leftZeroFill(this.isoWeekYear(), 4);
- },
- GGGGG : function () {
- return leftZeroFill(this.isoWeekYear(), 5);
- },
- e : function () {
- return this.weekday();
- },
- E : function () {
- return this.isoWeekday();
- },
- a : function () {
- return this.localeData().meridiem(this.hours(), this.minutes(), true);
- },
- A : function () {
- return this.localeData().meridiem(this.hours(), this.minutes(), false);
- },
- 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 toInt(this.milliseconds() / 100);
- },
- SS : function () {
- return leftZeroFill(toInt(this.milliseconds() / 10), 2);
- },
- SSS : function () {
- return leftZeroFill(this.milliseconds(), 3);
- },
- SSSS : function () {
- return leftZeroFill(this.milliseconds(), 3);
- },
- Z : function () {
- var a = this.utcOffset(),
- b = '+';
- if (a < 0) {
- a = -a;
- b = '-';
- }
- return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2);
- },
- ZZ : function () {
- var a = this.utcOffset(),
- b = '+';
- if (a < 0) {
- a = -a;
- b = '-';
- }
- return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 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();
- }
- },
-
- deprecations = {},
-
- lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'],
-
- updateInProgress = false;
-
- // Pick the first defined of two or three arguments. dfl comes from
- // default.
- function dfl(a, b, c) {
- switch (arguments.length) {
- case 2: return a != null ? a : b;
- case 3: return a != null ? a : b != null ? b : c;
- default: throw new Error('Implement me');
- }
- }
-
- function hasOwnProp(a, b) {
- return hasOwnProperty.call(a, b);
- }
-
- function defaultParsingFlags() {
- // We need to deep clone this object, and es5 standard is not very
- // helpful.
- return {
- empty : false,
- unusedTokens : [],
- unusedInput : [],
- overflow : -2,
- charsLeftOver : 0,
- nullInput : false,
- invalidMonth : null,
- invalidFormat : false,
- userInvalidated : false,
- iso: false
- };
- }
-
- function printMsg(msg) {
- if (moment.suppressDeprecationWarnings === false &&
- typeof console !== 'undefined' && console.warn) {
- console.warn('Deprecation warning: ' + msg);
- }
- }
-
- function deprecate(msg, fn) {
- var firstTime = true;
- return extend(function () {
- if (firstTime) {
- printMsg(msg);
- firstTime = false;
- }
- return fn.apply(this, arguments);
- }, fn);
- }
-
- function deprecateSimple(name, msg) {
- if (!deprecations[name]) {
- printMsg(msg);
- deprecations[name] = true;
- }
- }
-
- function padToken(func, count) {
- return function (a) {
- return leftZeroFill(func.call(this, a), count);
- };
- }
- function ordinalizeToken(func, period) {
- return function (a) {
- return this.localeData().ordinal(func.call(this, a), period);
- };
- }
-
- function monthDiff(a, b) {
- // difference in months
- var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()),
- // b is in (anchor - 1 month, anchor + 1 month)
- anchor = a.clone().add(wholeMonthDiff, 'months'),
- anchor2, adjust;
-
- if (b - anchor < 0) {
- anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
- // linear across the month
- adjust = (b - anchor) / (anchor - anchor2);
- } else {
- anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
- // linear across the month
- adjust = (b - anchor) / (anchor2 - anchor);
- }
-
- return -(wholeMonthDiff + adjust);
- }
-
- while (ordinalizeTokens.length) {
- i = ordinalizeTokens.pop();
- formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i);
- }
- while (paddedTokens.length) {
- i = paddedTokens.pop();
- formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2);
- }
- formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3);
-
-
- function meridiemFixWrap(locale, hour, meridiem) {
- var isPm;
-
- if (meridiem == null) {
- // nothing to do
- return hour;
- }
- if (locale.meridiemHour != null) {
- return locale.meridiemHour(hour, meridiem);
- } else if (locale.isPM != null) {
- // Fallback
- isPm = locale.isPM(meridiem);
- if (isPm && hour < 12) {
- hour += 12;
- }
- if (!isPm && hour === 12) {
- hour = 0;
- }
- return hour;
- } else {
- // thie is not supposed to happen
- return hour;
- }
- }
-
- /************************************
- Constructors
- ************************************/
-
- function Locale() {
- }
-
- // Moment prototype object
- function Moment(config, skipOverflow) {
- if (skipOverflow !== false) {
- checkOverflow(config);
- }
- copyConfig(this, config);
- this._d = new Date(+config._d);
- // Prevent infinite loop in case updateOffset creates new moment
- // objects.
- if (updateInProgress === false) {
- updateInProgress = true;
- moment.updateOffset(this);
- updateInProgress = false;
- }
- }
-
- // Duration Constructor
- function Duration(duration) {
- var normalizedInput = normalizeObjectUnits(duration),
- years = normalizedInput.year || 0,
- quarters = normalizedInput.quarter || 0,
- months = normalizedInput.month || 0,
- weeks = normalizedInput.week || 0,
- days = normalizedInput.day || 0,
- hours = normalizedInput.hour || 0,
- minutes = normalizedInput.minute || 0,
- seconds = normalizedInput.second || 0,
- milliseconds = normalizedInput.millisecond || 0;
-
- // representation for dateAddRemove
- this._milliseconds = +milliseconds +
- seconds * 1e3 + // 1000
- minutes * 6e4 + // 1000 * 60
- hours * 36e5; // 1000 * 60 * 60
- // Because of dateAddRemove treats 24 hours as different from a
- // day when working around DST, we need to store them separately
- this._days = +days +
- weeks * 7;
- // It is impossible translate months into days without knowing
- // which months you are are talking about, so we have to store
- // it separately.
- this._months = +months +
- quarters * 3 +
- years * 12;
-
- this._data = {};
-
- this._locale = moment.localeData();
-
- this._bubble();
- }
-
- /************************************
- Helpers
- ************************************/
-
-
- function extend(a, b) {
- for (var i in b) {
- if (hasOwnProp(b, i)) {
- a[i] = b[i];
- }
- }
-
- if (hasOwnProp(b, 'toString')) {
- a.toString = b.toString;
- }
-
- if (hasOwnProp(b, 'valueOf')) {
- a.valueOf = b.valueOf;
- }
-
- return a;
- }
-
- function copyConfig(to, from) {
- var i, prop, val;
-
- if (typeof from._isAMomentObject !== 'undefined') {
- to._isAMomentObject = from._isAMomentObject;
- }
- if (typeof from._i !== 'undefined') {
- to._i = from._i;
- }
- if (typeof from._f !== 'undefined') {
- to._f = from._f;
- }
- if (typeof from._l !== 'undefined') {
- to._l = from._l;
- }
- if (typeof from._strict !== 'undefined') {
- to._strict = from._strict;
- }
- if (typeof from._tzm !== 'undefined') {
- to._tzm = from._tzm;
- }
- if (typeof from._isUTC !== 'undefined') {
- to._isUTC = from._isUTC;
- }
- if (typeof from._offset !== 'undefined') {
- to._offset = from._offset;
- }
- if (typeof from._pf !== 'undefined') {
- to._pf = from._pf;
- }
- if (typeof from._locale !== 'undefined') {
- to._locale = from._locale;
- }
-
- if (momentProperties.length > 0) {
- for (i in momentProperties) {
- prop = momentProperties[i];
- val = from[prop];
- if (typeof val !== 'undefined') {
- to[prop] = val;
- }
- }
- }
-
- return to;
- }
-
- function absRound(number) {
- if (number < 0) {
- return Math.ceil(number);
- } else {
- return Math.floor(number);
- }
- }
-
- // left zero fill a number
- // see http://jsperf.com/left-zero-filling for performance comparison
- function leftZeroFill(number, targetLength, forceSign) {
- var output = '' + Math.abs(number),
- sign = number >= 0;
-
- while (output.length < targetLength) {
- output = '0' + output;
- }
- return (sign ? (forceSign ? '+' : '') : '-') + output;
- }
-
- function positiveMomentsDifference(base, other) {
- var res = {milliseconds: 0, months: 0};
-
- res.months = other.month() - base.month() +
- (other.year() - base.year()) * 12;
- if (base.clone().add(res.months, 'M').isAfter(other)) {
- --res.months;
- }
-
- res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
-
- return res;
- }
-
- function momentsDifference(base, other) {
- var res;
- other = makeAs(other, base);
- if (base.isBefore(other)) {
- res = positiveMomentsDifference(base, other);
- } else {
- res = positiveMomentsDifference(other, base);
- res.milliseconds = -res.milliseconds;
- res.months = -res.months;
- }
-
- return res;
- }
-
- // TODO: remove 'name' arg after deprecation is removed
- function createAdder(direction, name) {
- return function (val, period) {
- var dur, tmp;
- //invert the arguments, but complain about it
- if (period !== null && !isNaN(+period)) {
- deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).');
- tmp = val; val = period; period = tmp;
- }
-
- val = typeof val === 'string' ? +val : val;
- dur = moment.duration(val, period);
- addOrSubtractDurationFromMoment(this, dur, direction);
- return this;
- };
- }
-
- function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) {
- var milliseconds = duration._milliseconds,
- days = duration._days,
- months = duration._months;
- updateOffset = updateOffset == null ? true : updateOffset;
-
- if (milliseconds) {
- mom._d.setTime(+mom._d + milliseconds * isAdding);
- }
- if (days) {
- rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding);
- }
- if (months) {
- rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding);
- }
- if (updateOffset) {
- moment.updateOffset(mom, days || months);
- }
- }
-
- // check if is an array
- function isArray(input) {
- return Object.prototype.toString.call(input) === '[object Array]';
- }
-
- function isDate(input) {
- return Object.prototype.toString.call(input) === '[object Date]' ||
- input instanceof Date;
- }
-
- // compare two arrays, return the number of differences
- function compareArrays(array1, array2, dontConvert) {
- var len = Math.min(array1.length, array2.length),
- lengthDiff = Math.abs(array1.length - array2.length),
- diffs = 0,
- i;
- for (i = 0; i < len; i++) {
- if ((dontConvert && array1[i] !== array2[i]) ||
- (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
- diffs++;
- }
- }
- return diffs + lengthDiff;
- }
-
- function normalizeUnits(units) {
- if (units) {
- var lowered = units.toLowerCase().replace(/(.)s$/, '$1');
- units = unitAliases[units] || camelFunctions[lowered] || lowered;
- }
- return units;
- }
-
- function normalizeObjectUnits(inputObject) {
- var normalizedInput = {},
- normalizedProp,
- prop;
-
- for (prop in inputObject) {
- if (hasOwnProp(inputObject, prop)) {
- normalizedProp = normalizeUnits(prop);
- if (normalizedProp) {
- normalizedInput[normalizedProp] = inputObject[prop];
- }
- }
- }
-
- return normalizedInput;
- }
-
- function makeList(field) {
- var count, setter;
-
- if (field.indexOf('week') === 0) {
- count = 7;
- setter = 'day';
- }
- else if (field.indexOf('month') === 0) {
- count = 12;
- setter = 'month';
- }
- else {
- return;
- }
-
- moment[field] = function (format, index) {
- var i, getter,
- method = moment._locale[field],
- results = [];
-
- if (typeof format === 'number') {
- index = format;
- format = undefined;
- }
-
- getter = function (i) {
- var m = moment().utc().set(setter, i);
- return method.call(moment._locale, m, format || '');
- };
-
- if (index != null) {
- return getter(index);
- }
- else {
- for (i = 0; i < count; i++) {
- results.push(getter(i));
- }
- return results;
- }
- };
- }
-
- function toInt(argumentForCoercion) {
- var coercedNumber = +argumentForCoercion,
- value = 0;
-
- if (coercedNumber !== 0 && isFinite(coercedNumber)) {
- if (coercedNumber >= 0) {
- value = Math.floor(coercedNumber);
- } else {
- value = Math.ceil(coercedNumber);
- }
- }
-
- return value;
- }
-
- function daysInMonth(year, month) {
- return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
- }
-
- function weeksInYear(year, dow, doy) {
- return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week;
- }
-
- function daysInYear(year) {
- return isLeapYear(year) ? 366 : 365;
- }
-
- function isLeapYear(year) {
- return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
- }
-
- function checkOverflow(m) {
- var overflow;
- if (m._a && m._pf.overflow === -2) {
- overflow =
- m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH :
- m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE :
- m._a[HOUR] < 0 || m._a[HOUR] > 24 ||
- (m._a[HOUR] === 24 && (m._a[MINUTE] !== 0 ||
- m._a[SECOND] !== 0 ||
- m._a[MILLISECOND] !== 0)) ? HOUR :
- m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE :
- m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND :
- m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND :
- -1;
-
- if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
- overflow = DATE;
- }
-
- m._pf.overflow = overflow;
- }
- }
-
- function isValid(m) {
- if (m._isValid == null) {
- m._isValid = !isNaN(m._d.getTime()) &&
- m._pf.overflow < 0 &&
- !m._pf.empty &&
- !m._pf.invalidMonth &&
- !m._pf.nullInput &&
- !m._pf.invalidFormat &&
- !m._pf.userInvalidated;
-
- if (m._strict) {
- m._isValid = m._isValid &&
- m._pf.charsLeftOver === 0 &&
- m._pf.unusedTokens.length === 0 &&
- m._pf.bigHour === undefined;
- }
- }
- return m._isValid;
- }
-
- function normalizeLocale(key) {
- return key ? key.toLowerCase().replace('_', '-') : key;
- }
-
- // pick the locale from the array
- // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
- // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
- function chooseLocale(names) {
- var i = 0, j, next, locale, split;
-
- while (i < names.length) {
- split = normalizeLocale(names[i]).split('-');
- j = split.length;
- next = normalizeLocale(names[i + 1]);
- next = next ? next.split('-') : null;
- while (j > 0) {
- locale = loadLocale(split.slice(0, j).join('-'));
- if (locale) {
- return locale;
- }
- if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
- //the next array item is better than a shallower substring of this one
- break;
- }
- j--;
- }
- i++;
- }
- return null;
- }
-
- function loadLocale(name) {
- var oldLocale = null;
- if (!locales[name] && hasModule) {
- try {
- oldLocale = moment.locale();
- require('./locale/' + name);
- // because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales
- moment.locale(oldLocale);
- } catch (e) { }
- }
- return locales[name];
- }
-
- // Return a moment from input, that is local/utc/utcOffset equivalent to
- // model.
- function makeAs(input, model) {
- var res, diff;
- if (model._isUTC) {
- res = model.clone();
- diff = (moment.isMoment(input) || isDate(input) ?
- +input : +moment(input)) - (+res);
- // Use low-level api, because this fn is low-level api.
- res._d.setTime(+res._d + diff);
- moment.updateOffset(res, false);
- return res;
- } else {
- return moment(input).local();
- }
- }
-
- /************************************
- Locale
- ************************************/
-
-
- extend(Locale.prototype, {
-
- set : function (config) {
- var prop, i;
- for (i in config) {
- prop = config[i];
- if (typeof prop === 'function') {
- this[i] = prop;
- } else {
- this['_' + i] = prop;
- }
- }
- // Lenient ordinal parsing accepts just a number in addition to
- // number + (possibly) stuff coming from _ordinalParseLenient.
- 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 (m) {
- return this._months[m.month()];
- },
-
- _monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
- monthsShort : function (m) {
- return this._monthsShort[m.month()];
- },
-
- monthsParse : function (monthName, format, strict) {
- var i, mom, regex;
-
- if (!this._monthsParse) {
- this._monthsParse = [];
- this._longMonthsParse = [];
- this._shortMonthsParse = [];
- }
-
- for (i = 0; i < 12; i++) {
- // make the regex if we don't have it already
- mom = moment.utc([2000, i]);
- if (strict && !this._longMonthsParse[i]) {
- this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
- this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
- }
- if (!strict && !this._monthsParse[i]) {
- regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
- this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
- }
- // test the regex
- if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
- return i;
- } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
- return i;
- } else if (!strict && this._monthsParse[i].test(monthName)) {
- return i;
- }
- }
- },
-
- _weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
- weekdays : function (m) {
- return this._weekdays[m.day()];
- },
-
- _weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
- weekdaysShort : function (m) {
- return this._weekdaysShort[m.day()];
- },
-
- _weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
- weekdaysMin : function (m) {
- return this._weekdaysMin[m.day()];
- },
-
- weekdaysParse : function (weekdayName) {
- var i, mom, regex;
-
- if (!this._weekdaysParse) {
- this._weekdaysParse = [];
- }
-
- for (i = 0; i < 7; i++) {
- // make the regex if we don't have it already
- if (!this._weekdaysParse[i]) {
- mom = moment([2000, 1]).day(i);
- regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
- this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
- }
- // test the regex
- if (this._weekdaysParse[i].test(weekdayName)) {
- return i;
- }
- }
- },
-
- _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 (key) {
- var output = this._longDateFormat[key];
- if (!output && this._longDateFormat[key.toUpperCase()]) {
- output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {
- return val.slice(1);
- });
- this._longDateFormat[key] = output;
- }
- return output;
- },
-
- isPM : function (input) {
- // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
- // Using charAt should be more compatible.
- return ((input + '').toLowerCase().charAt(0) === 'p');
- },
-
- _meridiemParse : /[ap]\.?m?\.?/i,
- meridiem : function (hours, minutes, isLower) {
- if (hours > 11) {
- return isLower ? 'pm' : 'PM';
- } else {
- return isLower ? '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 (key, mom, now) {
- var output = this._calendar[key];
- return typeof output === 'function' ? output.apply(mom, [now]) : output;
- },
-
- _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 (number, withoutSuffix, string, isFuture) {
- var output = this._relativeTime[string];
- return (typeof output === 'function') ?
- output(number, withoutSuffix, string, isFuture) :
- output.replace(/%d/i, number);
- },
-
- pastFuture : function (diff, output) {
- var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
- return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
- },
-
- ordinal : function (number) {
- return this._ordinal.replace('%d', number);
- },
- _ordinal : '%d',
- _ordinalParse : /\d{1,2}/,
-
- preparse : function (string) {
- return string;
- },
-
- postformat : function (string) {
- return string;
- },
-
- week : function (mom) {
- return weekOfYear(mom, this._week.dow, this._week.doy).week;
- },
-
- _week : {
- dow : 0, // Sunday is the first day of the week.
- doy : 6 // The week that contains Jan 1st is the first week of the year.
- },
-
- firstDayOfWeek : function () {
- return this._week.dow;
- },
-
- firstDayOfYear : function () {
- return this._week.doy;
- },
-
- _invalidDate: 'Invalid date',
- invalidDate: function () {
- return this._invalidDate;
- }
- });
-
- /************************************
- Formatting
- ************************************/
-
-
- function removeFormattingTokens(input) {
- if (input.match(/\[[\s\S]/)) {
- return input.replace(/^\[|\]$/g, '');
- }
- return input.replace(/\\/g, '');
- }
-
- function makeFormatFunction(format) {
- var array = format.match(formattingTokens), i, length;
-
- for (i = 0, length = array.length; i < length; i++) {
- if (formatTokenFunctions[array[i]]) {
- array[i] = formatTokenFunctions[array[i]];
- } else {
- array[i] = removeFormattingTokens(array[i]);
- }
- }
-
- return function (mom) {
- var output = '';
- for (i = 0; i < length; i++) {
- output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
- }
- return output;
- };
- }
-
- // format date using native date object
- function formatMoment(m, format) {
- if (!m.isValid()) {
- return m.localeData().invalidDate();
- }
-
- format = expandFormat(format, m.localeData());
-
- if (!formatFunctions[format]) {
- formatFunctions[format] = makeFormatFunction(format);
- }
-
- return formatFunctions[format](m);
- }
-
- function expandFormat(format, locale) {
- var i = 5;
-
- function replaceLongDateFormatTokens(input) {
- return locale.longDateFormat(input) || input;
- }
-
- localFormattingTokens.lastIndex = 0;
- while (i >= 0 && localFormattingTokens.test(format)) {
- format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
- localFormattingTokens.lastIndex = 0;
- i -= 1;
- }
-
- return format;
- }
-
-
- /************************************
- Parsing
- ************************************/
-
-
- // get the regex to find the next token
- function getParseRegexForToken(token, config) {
- var a, strict = config._strict;
- switch (token) {
- case 'Q':
- return parseTokenOneDigit;
- case 'DDDD':
- return parseTokenThreeDigits;
- case 'YYYY':
- case 'GGGG':
- case 'gggg':
- return strict ? parseTokenFourDigits : parseTokenOneToFourDigits;
- case 'Y':
- case 'G':
- case 'g':
- return parseTokenSignedNumber;
- case 'YYYYYY':
- case 'YYYYY':
- case 'GGGGG':
- case 'ggggg':
- return strict ? parseTokenSixDigits : parseTokenOneToSixDigits;
- case 'S':
- if (strict) {
- return parseTokenOneDigit;
- }
- /* falls through */
- case 'SS':
- if (strict) {
- return parseTokenTwoDigits;
- }
- /* falls through */
- case 'SSS':
- if (strict) {
- return parseTokenThreeDigits;
- }
- /* falls through */
- case 'DDD':
- return parseTokenOneToThreeDigits;
- case 'MMM':
- case 'MMMM':
- case 'dd':
- case 'ddd':
- case 'dddd':
- return parseTokenWord;
- case 'a':
- case 'A':
- return config._locale._meridiemParse;
- case 'x':
- return parseTokenOffsetMs;
- case 'X':
- return parseTokenTimestampMs;
- case 'Z':
- case 'ZZ':
- return parseTokenTimezone;
- case 'T':
- return parseTokenT;
- case 'SSSS':
- return parseTokenDigits;
- case 'MM':
- case 'DD':
- case 'YY':
- case 'GG':
- case 'gg':
- case 'HH':
- case 'hh':
- case 'mm':
- case 'ss':
- case 'ww':
- case 'WW':
- return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits;
- case 'M':
- case 'D':
- case 'd':
- case 'H':
- case 'h':
- case 'm':
- case 's':
- case 'w':
- case 'W':
- case 'e':
- case 'E':
- return parseTokenOneOrTwoDigits;
- case 'Do':
- return strict ? config._locale._ordinalParse : config._locale._ordinalParseLenient;
- default :
- a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i'));
- return a;
- }
- }
-
- function utcOffsetFromString(string) {
- string = string || '';
- var possibleTzMatches = (string.match(parseTokenTimezone) || []),
- tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [],
- parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0],
- minutes = +(parts[1] * 60) + toInt(parts[2]);
-
- return parts[0] === '+' ? minutes : -minutes;
- }
-
- // function to convert string input to date
- function addTimeToArrayFromToken(token, input, config) {
- var a, datePartArray = config._a;
-
- switch (token) {
- // QUARTER
- case 'Q':
- if (input != null) {
- datePartArray[MONTH] = (toInt(input) - 1) * 3;
- }
- break;
- // MONTH
- case 'M' : // fall through to MM
- case 'MM' :
- if (input != null) {
- datePartArray[MONTH] = toInt(input) - 1;
- }
- break;
- case 'MMM' : // fall through to MMMM
- case 'MMMM' :
- a = config._locale.monthsParse(input, token, config._strict);
- // if we didn't find a month name, mark the date as invalid.
- if (a != null) {
- datePartArray[MONTH] = a;
- } else {
- config._pf.invalidMonth = input;
- }
- break;
- // DAY OF MONTH
- case 'D' : // fall through to DD
- case 'DD' :
- if (input != null) {
- datePartArray[DATE] = toInt(input);
- }
- break;
- case 'Do' :
- if (input != null) {
- datePartArray[DATE] = toInt(parseInt(
- input.match(/\d{1,2}/)[0], 10));
- }
- break;
- // DAY OF YEAR
- case 'DDD' : // fall through to DDDD
- case 'DDDD' :
- if (input != null) {
- config._dayOfYear = toInt(input);
- }
-
- break;
- // YEAR
- case 'YY' :
- datePartArray[YEAR] = moment.parseTwoDigitYear(input);
- break;
- case 'YYYY' :
- case 'YYYYY' :
- case 'YYYYYY' :
- datePartArray[YEAR] = toInt(input);
- break;
- // AM / PM
- case 'a' : // fall through to A
- case 'A' :
- config._meridiem = input;
- // config._isPm = config._locale.isPM(input);
- break;
- // HOUR
- case 'h' : // fall through to hh
- case 'hh' :
- config._pf.bigHour = true;
- /* falls through */
- case 'H' : // fall through to HH
- case 'HH' :
- datePartArray[HOUR] = toInt(input);
- break;
- // MINUTE
- case 'm' : // fall through to mm
- case 'mm' :
- datePartArray[MINUTE] = toInt(input);
- break;
- // SECOND
- case 's' : // fall through to ss
- case 'ss' :
- datePartArray[SECOND] = toInt(input);
- break;
- // MILLISECOND
- case 'S' :
- case 'SS' :
- case 'SSS' :
- case 'SSSS' :
- datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000);
- break;
- // UNIX OFFSET (MILLISECONDS)
- case 'x':
- config._d = new Date(toInt(input));
- break;
- // UNIX TIMESTAMP WITH MS
- case 'X':
- config._d = new Date(parseFloat(input) * 1000);
- break;
- // TIMEZONE
- case 'Z' : // fall through to ZZ
- case 'ZZ' :
- config._useUTC = true;
- config._tzm = utcOffsetFromString(input);
- break;
- // WEEKDAY - human
- case 'dd':
- case 'ddd':
- case 'dddd':
- a = config._locale.weekdaysParse(input);
- // if we didn't get a weekday name, mark the date as invalid
- if (a != null) {
- config._w = config._w || {};
- config._w['d'] = a;
- } else {
- config._pf.invalidWeekday = input;
- }
- break;
- // WEEK, WEEK DAY - numeric
- case 'w':
- case 'ww':
- case 'W':
- case 'WW':
- case 'd':
- case 'e':
- case 'E':
- token = token.substr(0, 1);
- /* falls through */
- case 'gggg':
- case 'GGGG':
- case 'GGGGG':
- token = token.substr(0, 2);
- if (input) {
- config._w = config._w || {};
- config._w[token] = toInt(input);
- }
- break;
- case 'gg':
- case 'GG':
- config._w = config._w || {};
- config._w[token] = moment.parseTwoDigitYear(input);
- }
- }
-
- function dayOfYearFromWeekInfo(config) {
- var w, weekYear, week, weekday, dow, doy, temp;
-
- w = config._w;
- if (w.GG != null || w.W != null || w.E != null) {
- dow = 1;
- doy = 4;
-
- // TODO: We need to take the current isoWeekYear, but that depends on
- // how we interpret now (local, utc, fixed offset). So create
- // a now version of current config (take local/utc/offset flags, and
- // create now).
- weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year);
- week = dfl(w.W, 1);
- weekday = dfl(w.E, 1);
- } else {
- dow = config._locale._week.dow;
- doy = config._locale._week.doy;
-
- weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year);
- week = dfl(w.w, 1);
-
- if (w.d != null) {
- // weekday -- low day numbers are considered next week
- weekday = w.d;
- if (weekday < dow) {
- ++week;
- }
- } else if (w.e != null) {
- // local weekday -- counting starts from begining of week
- weekday = w.e + dow;
- } else {
- // default to begining of week
- weekday = dow;
- }
- }
- temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow);
-
- config._a[YEAR] = temp.year;
- config._dayOfYear = temp.dayOfYear;
- }
-
- // convert an array to a date.
- // the array should mirror the parameters below
- // note: all values past the year are optional and will default to the lowest possible value.
- // [year, month, day , hour, minute, second, millisecond]
- function dateFromConfig(config) {
- var i, date, input = [], currentDate, yearToUse;
-
- if (config._d) {
- return;
- }
-
- currentDate = currentDateArray(config);
-
- //compute day of the year from weeks and weekdays
- if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
- dayOfYearFromWeekInfo(config);
- }
-
- //if the day of the year is set, figure out what it is
- if (config._dayOfYear) {
- yearToUse = dfl(config._a[YEAR], currentDate[YEAR]);
-
- if (config._dayOfYear > daysInYear(yearToUse)) {
- config._pf._overflowDayOfYear = true;
- }
-
- date = makeUTCDate(yearToUse, 0, config._dayOfYear);
- config._a[MONTH] = date.getUTCMonth();
- config._a[DATE] = date.getUTCDate();
- }
-
- // Default to current date.
- // * if no year, month, day of month are given, default to today
- // * if day of month is given, default month and year
- // * if month is given, default only year
- // * if year is given, don't default anything
- for (i = 0; i < 3 && config._a[i] == null; ++i) {
- config._a[i] = input[i] = currentDate[i];
- }
-
- // Zero out whatever was not defaulted, including time
- for (; i < 7; i++) {
- config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
- }
-
- // Check for 24:00:00.000
- if (config._a[HOUR] === 24 &&
- config._a[MINUTE] === 0 &&
- config._a[SECOND] === 0 &&
- config._a[MILLISECOND] === 0) {
- config._nextDay = true;
- config._a[HOUR] = 0;
- }
-
- config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input);
- // Apply timezone offset from input. The actual utcOffset can be changed
- // with parseZone.
- if (config._tzm != null) {
- config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
- }
-
- if (config._nextDay) {
- config._a[HOUR] = 24;
- }
- }
-
- function dateFromObject(config) {
- var normalizedInput;
-
- if (config._d) {
- return;
- }
-
- normalizedInput = normalizeObjectUnits(config._i);
- config._a = [
- normalizedInput.year,
- normalizedInput.month,
- normalizedInput.day || normalizedInput.date,
- normalizedInput.hour,
- normalizedInput.minute,
- normalizedInput.second,
- normalizedInput.millisecond
- ];
-
- dateFromConfig(config);
- }
-
- function currentDateArray(config) {
- var now = new Date();
- if (config._useUTC) {
- return [
- now.getUTCFullYear(),
- now.getUTCMonth(),
- now.getUTCDate()
- ];
- } else {
- return [now.getFullYear(), now.getMonth(), now.getDate()];
- }
- }
-
- // date from string and format string
- function makeDateFromStringAndFormat(config) {
- if (config._f === moment.ISO_8601) {
- parseISO(config);
- return;
- }
-
- config._a = [];
- config._pf.empty = true;
-
- // This array is used to make a Date, either with `new Date` or `Date.UTC`
- var string = '' + config._i,
- i, parsedInput, tokens, token, skipped,
- stringLength = string.length,
- totalParsedInputLength = 0;
-
- tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
-
- for (i = 0; i < tokens.length; i++) {
- token = tokens[i];
- parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
- if (parsedInput) {
- skipped = string.substr(0, string.indexOf(parsedInput));
- if (skipped.length > 0) {
- config._pf.unusedInput.push(skipped);
- }
- string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
- totalParsedInputLength += parsedInput.length;
- }
- // don't parse if it's not a known token
- if (formatTokenFunctions[token]) {
- if (parsedInput) {
- config._pf.empty = false;
- }
- else {
- config._pf.unusedTokens.push(token);
- }
- addTimeToArrayFromToken(token, parsedInput, config);
- }
- else if (config._strict && !parsedInput) {
- config._pf.unusedTokens.push(token);
- }
- }
-
- // add remaining unparsed input length to the string
- config._pf.charsLeftOver = stringLength - totalParsedInputLength;
- if (string.length > 0) {
- config._pf.unusedInput.push(string);
- }
-
- // clear _12h flag if hour is <= 12
- if (config._pf.bigHour === true && config._a[HOUR] <= 12) {
- config._pf.bigHour = undefined;
- }
- // handle meridiem
- config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR],
- config._meridiem);
- dateFromConfig(config);
- checkOverflow(config);
- }
-
- function unescapeFormat(s) {
- return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
- return p1 || p2 || p3 || p4;
- });
- }
-
- // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
- function regexpEscape(s) {
- return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
- }
-
- // date from string and array of format strings
- function makeDateFromStringAndArray(config) {
- var tempConfig,
- bestMoment,
-
- scoreToBeat,
- i,
- currentScore;
-
- if (config._f.length === 0) {
- config._pf.invalidFormat = true;
- config._d = new Date(NaN);
- return;
- }
-
- for (i = 0; i < config._f.length; i++) {
- currentScore = 0;
- tempConfig = copyConfig({}, config);
- if (config._useUTC != null) {
- tempConfig._useUTC = config._useUTC;
- }
- tempConfig._pf = defaultParsingFlags();
- tempConfig._f = config._f[i];
- makeDateFromStringAndFormat(tempConfig);
-
- if (!isValid(tempConfig)) {
- continue;
- }
-
- // if there is any input that was not parsed add a penalty for that format
- currentScore += tempConfig._pf.charsLeftOver;
-
- //or tokens
- currentScore += tempConfig._pf.unusedTokens.length * 10;
-
- tempConfig._pf.score = currentScore;
-
- if (scoreToBeat == null || currentScore < scoreToBeat) {
- scoreToBeat = currentScore;
- bestMoment = tempConfig;
- }
- }
-
- extend(config, bestMoment || tempConfig);
- }
-
- // date from iso format
- function parseISO(config) {
- var i, l,
- string = config._i,
- match = isoRegex.exec(string);
-
- if (match) {
- config._pf.iso = true;
- for (i = 0, l = isoDates.length; i < l; i++) {
- if (isoDates[i][1].exec(string)) {
- // match[5] should be 'T' or undefined
- config._f = isoDates[i][0] + (match[6] || ' ');
- break;
- }
- }
- for (i = 0, l = isoTimes.length; i < l; i++) {
- if (isoTimes[i][1].exec(string)) {
- config._f += isoTimes[i][0];
- break;
- }
- }
- if (string.match(parseTokenTimezone)) {
- config._f += 'Z';
- }
- makeDateFromStringAndFormat(config);
- } else {
- config._isValid = false;
- }
- }
-
- // date from iso format or fallback
- function makeDateFromString(config) {
- parseISO(config);
- if (config._isValid === false) {
- delete config._isValid;
- moment.createFromInputFallback(config);
- }
- }
-
- function map(arr, fn) {
- var res = [], i;
- for (i = 0; i < arr.length; ++i) {
- res.push(fn(arr[i], i));
- }
- return res;
- }
-
- function makeDateFromInput(config) {
- var input = config._i, matched;
- if (input === undefined) {
- config._d = new Date();
- } else if (isDate(input)) {
- config._d = new Date(+input);
- } else if ((matched = aspNetJsonRegex.exec(input)) !== null) {
- config._d = new Date(+matched[1]);
- } else if (typeof input === 'string') {
- makeDateFromString(config);
- } else if (isArray(input)) {
- config._a = map(input.slice(0), function (obj) {
- return parseInt(obj, 10);
- });
- dateFromConfig(config);
- } else if (typeof(input) === 'object') {
- dateFromObject(config);
- } else if (typeof(input) === 'number') {
- // from milliseconds
- config._d = new Date(input);
- } else {
- moment.createFromInputFallback(config);
- }
- }
-
- function makeDate(y, m, d, h, M, s, ms) {
- //can't just apply() to create a date:
- //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
- var date = new Date(y, m, d, h, M, s, ms);
-
- //the date constructor doesn't accept years < 1970
- if (y < 1970) {
- date.setFullYear(y);
- }
- return date;
- }
-
- function makeUTCDate(y) {
- var date = new Date(Date.UTC.apply(null, arguments));
- if (y < 1970) {
- date.setUTCFullYear(y);
- }
- return date;
- }
-
- function parseWeekday(input, locale) {
- if (typeof input === 'string') {
- if (!isNaN(input)) {
- input = parseInt(input, 10);
- }
- else {
- input = locale.weekdaysParse(input);
- if (typeof input !== 'number') {
- return null;
- }
- }
- }
- return input;
- }
-
- /************************************
- Relative Time
- ************************************/
-
-
- // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
- function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
- return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
- }
-
- function relativeTime(posNegDuration, withoutSuffix, locale) {
- var duration = moment.duration(posNegDuration).abs(),
- seconds = round(duration.as('s')),
- minutes = round(duration.as('m')),
- hours = round(duration.as('h')),
- days = round(duration.as('d')),
- months = round(duration.as('M')),
- years = round(duration.as('y')),
-
- args = seconds < relativeTimeThresholds.s && ['s', seconds] ||
- minutes === 1 && ['m'] ||
- minutes < relativeTimeThresholds.m && ['mm', minutes] ||
- hours === 1 && ['h'] ||
- hours < relativeTimeThresholds.h && ['hh', hours] ||
- days === 1 && ['d'] ||
- days < relativeTimeThresholds.d && ['dd', days] ||
- months === 1 && ['M'] ||
- months < relativeTimeThresholds.M && ['MM', months] ||
- years === 1 && ['y'] || ['yy', years];
-
- args[2] = withoutSuffix;
- args[3] = +posNegDuration > 0;
- args[4] = locale;
- return substituteTimeAgo.apply({}, args);
- }
-
-
- /************************************
- Week of Year
- ************************************/
-
-
- // firstDayOfWeek 0 = sun, 6 = sat
- // the day of the week that starts the week
- // (usually sunday or monday)
- // firstDayOfWeekOfYear 0 = sun, 6 = sat
- // the first week is the week that contains the first
- // of this day of the week
- // (eg. ISO weeks use thursday (4))
- function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
- var end = firstDayOfWeekOfYear - firstDayOfWeek,
- daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(),
- adjustedMoment;
-
-
- if (daysToDayOfWeek > end) {
- daysToDayOfWeek -= 7;
- }
-
- if (daysToDayOfWeek < end - 7) {
- daysToDayOfWeek += 7;
- }
-
- adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd');
- return {
- week: Math.ceil(adjustedMoment.dayOfYear() / 7),
- year: adjustedMoment.year()
- };
- }
-
- //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
- function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) {
- var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear;
-
- d = d === 0 ? 7 : d;
- weekday = weekday != null ? weekday : firstDayOfWeek;
- daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0);
- dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1;
-
- return {
- year: dayOfYear > 0 ? year : year - 1,
- dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear
- };
- }
-
- /************************************
- Top Level Functions
- ************************************/
-
- function makeMoment(config) {
- var input = config._i,
- format = config._f,
- res;
-
- config._locale = config._locale || moment.localeData(config._l);
-
- if (input === null || (format === undefined && input === '')) {
- return moment.invalid({nullInput: true});
- }
-
- if (typeof input === 'string') {
- config._i = input = config._locale.preparse(input);
- }
-
- if (moment.isMoment(input)) {
- return new Moment(input, true);
- } else if (format) {
- if (isArray(format)) {
- makeDateFromStringAndArray(config);
- } else {
- makeDateFromStringAndFormat(config);
- }
- } else {
- makeDateFromInput(config);
- }
-
- res = new Moment(config);
- if (res._nextDay) {
- // Adding is smart enough around DST
- res.add(1, 'd');
- res._nextDay = undefined;
- }
-
- return res;
- }
-
- moment = function (input, format, locale, strict) {
- var c;
-
- if (typeof(locale) === 'boolean') {
- strict = locale;
- locale = undefined;
- }
- // object construction must be done this way.
- // https://github.com/moment/moment/issues/1423
- c = {};
- c._isAMomentObject = true;
- c._i = input;
- c._f = format;
- c._l = locale;
- c._strict = strict;
- c._isUTC = false;
- c._pf = defaultParsingFlags();
-
- return makeMoment(c);
- };
-
- moment.suppressDeprecationWarnings = false;
-
- moment.createFromInputFallback = deprecate(
- '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 (config) {
- config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
- }
- );
-
- // Pick a moment m from moments so that m[fn](other) is true for all
- // other. This relies on the function fn to be transitive.
- //
- // moments should either be an array of moment objects or an array, whose
- // first element is an array of moment objects.
- function pickBy(fn, moments) {
- var res, i;
- if (moments.length === 1 && isArray(moments[0])) {
- moments = moments[0];
- }
- if (!moments.length) {
- return moment();
- }
- res = moments[0];
- for (i = 1; i < moments.length; ++i) {
- if (moments[i][fn](res)) {
- res = moments[i];
- }
- }
- return res;
- }
-
- moment.min = function () {
- var args = [].slice.call(arguments, 0);
-
- return pickBy('isBefore', args);
- };
-
- moment.max = function () {
- var args = [].slice.call(arguments, 0);
-
- return pickBy('isAfter', args);
- };
-
- // creating with utc
- moment.utc = function (input, format, locale, strict) {
- var c;
-
- if (typeof(locale) === 'boolean') {
- strict = locale;
- locale = undefined;
- }
- // object construction must be done this way.
- // https://github.com/moment/moment/issues/1423
- c = {};
- c._isAMomentObject = true;
- c._useUTC = true;
- c._isUTC = true;
- c._l = locale;
- c._i = input;
- c._f = format;
- c._strict = strict;
- c._pf = defaultParsingFlags();
-
- return makeMoment(c).utc();
- };
-
- // creating with unix timestamp (in seconds)
- moment.unix = function (input) {
- return moment(input * 1000);
- };
-
- // duration
- moment.duration = function (input, key) {
- var duration = input,
- // matching against regexp is expensive, do it on demand
- match = null,
- sign,
- ret,
- parseIso,
- diffRes;
-
- if (moment.isDuration(input)) {
- duration = {
- ms: input._milliseconds,
- d: input._days,
- M: input._months
- };
- } else if (typeof input === 'number') {
- duration = {};
- if (key) {
- duration[key] = input;
- } else {
- duration.milliseconds = input;
- }
- } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) {
- sign = (match[1] === '-') ? -1 : 1;
- duration = {
- y: 0,
- d: toInt(match[DATE]) * sign,
- h: toInt(match[HOUR]) * sign,
- m: toInt(match[MINUTE]) * sign,
- s: toInt(match[SECOND]) * sign,
- ms: toInt(match[MILLISECOND]) * sign
- };
- } else if (!!(match = isoDurationRegex.exec(input))) {
- sign = (match[1] === '-') ? -1 : 1;
- parseIso = function (inp) {
- // We'd normally use ~~inp for this, but unfortunately it also
- // converts floats to ints.
- // inp may be undefined, so careful calling replace on it.
- var res = inp && parseFloat(inp.replace(',', '.'));
- // apply sign while we're at it
- return (isNaN(res) ? 0 : res) * sign;
- };
- duration = {
- y: parseIso(match[2]),
- M: parseIso(match[3]),
- d: parseIso(match[4]),
- h: parseIso(match[5]),
- m: parseIso(match[6]),
- s: parseIso(match[7]),
- w: parseIso(match[8])
- };
- } else if (duration == null) {// checks for null or undefined
- duration = {};
- } else if (typeof duration === 'object' &&
- ('from' in duration || 'to' in duration)) {
- diffRes = momentsDifference(moment(duration.from), moment(duration.to));
-
- duration = {};
- duration.ms = diffRes.milliseconds;
- duration.M = diffRes.months;
- }
-
- ret = new Duration(duration);
-
- if (moment.isDuration(input) && hasOwnProp(input, '_locale')) {
- ret._locale = input._locale;
- }
-
- return ret;
- };
-
- // version number
- moment.version = VERSION;
-
- // default format
- moment.defaultFormat = isoFormat;
-
- // constant that refers to the ISO standard
- moment.ISO_8601 = function () {};
-
- // Plugins that add properties should also add the key here (null value),
- // so we can properly clone ourselves.
- moment.momentProperties = momentProperties;
-
- // This function will be called whenever a moment is mutated.
- // It is intended to keep the offset in sync with the timezone.
- moment.updateOffset = function () {};
-
- // This function allows you to set a threshold for relative time strings
- moment.relativeTimeThreshold = function (threshold, limit) {
- if (relativeTimeThresholds[threshold] === undefined) {
- return false;
- }
- if (limit === undefined) {
- return relativeTimeThresholds[threshold];
- }
- relativeTimeThresholds[threshold] = limit;
- return true;
- };
-
- moment.lang = deprecate(
- 'moment.lang is deprecated. Use moment.locale instead.',
- function (key, value) {
- return moment.locale(key, value);
- }
- );
-
- // This function will load locale and then set the global locale. If
- // no arguments are passed in, it will simply return the current global
- // locale key.
- moment.locale = function (key, values) {
- var data;
- if (key) {
- if (typeof(values) !== 'undefined') {
- data = moment.defineLocale(key, values);
- }
- else {
- data = moment.localeData(key);
- }
-
- if (data) {
- moment.duration._locale = moment._locale = data;
- }
- }
-
- return moment._locale._abbr;
- };
-
- moment.defineLocale = function (name, values) {
- if (values !== null) {
- values.abbr = name;
- if (!locales[name]) {
- locales[name] = new Locale();
- }
- locales[name].set(values);
-
- // backwards compat for now: also set the locale
- moment.locale(name);
-
- return locales[name];
- } else {
- // useful for testing
- delete locales[name];
- return null;
- }
- };
-
- moment.langData = deprecate(
- 'moment.langData is deprecated. Use moment.localeData instead.',
- function (key) {
- return moment.localeData(key);
- }
- );
-
- // returns locale data
- moment.localeData = function (key) {
- var locale;
-
- if (key && key._locale && key._locale._abbr) {
- key = key._locale._abbr;
- }
-
- if (!key) {
- return moment._locale;
- }
-
- if (!isArray(key)) {
- //short-circuit everything else
- locale = loadLocale(key);
- if (locale) {
- return locale;
- }
- key = [key];
- }
-
- return chooseLocale(key);
- };
-
- // compare moment object
- moment.isMoment = function (obj) {
- return obj instanceof Moment ||
- (obj != null && hasOwnProp(obj, '_isAMomentObject'));
- };
-
- // for typechecking Duration objects
- moment.isDuration = function (obj) {
- return obj instanceof Duration;
- };
-
- for (i = lists.length - 1; i >= 0; --i) {
- makeList(lists[i]);
- }
-
- moment.normalizeUnits = function (units) {
- return normalizeUnits(units);
- };
-
- moment.invalid = function (flags) {
- var m = moment.utc(NaN);
- if (flags != null) {
- extend(m._pf, flags);
- }
- else {
- m._pf.userInvalidated = true;
- }
-
- return m;
- };
-
- moment.parseZone = function () {
- return moment.apply(null, arguments).parseZone();
- };
-
- moment.parseTwoDigitYear = function (input) {
- return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
- };
-
- moment.isDate = isDate;
-
- /************************************
- Moment Prototype
- ************************************/
-
-
- extend(moment.fn = Moment.prototype, {
-
- clone : function () {
- return moment(this);
- },
-
- valueOf : function () {
- return +this._d - ((this._offset || 0) * 60000);
- },
-
- unix : function () {
- return Math.floor(+this / 1000);
- },
-
- 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 m = moment(this).utc();
- if (0 < m.year() && m.year() <= 9999) {
- if ('function' === typeof Date.prototype.toISOString) {
- // native implementation is ~50x faster, use it when we can
- return this.toDate().toISOString();
- } else {
- return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
- }
- } else {
- return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
- }
- },
-
- toArray : function () {
- var m = this;
- return [
- m.year(),
- m.month(),
- m.date(),
- m.hours(),
- m.minutes(),
- m.seconds(),
- m.milliseconds()
- ];
- },
-
- isValid : function () {
- return isValid(this);
- },
-
- isDSTShifted : function () {
- if (this._a) {
- return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0;
- }
-
- return false;
- },
-
- parsingFlags : function () {
- return extend({}, this._pf);
- },
-
- invalidAt: function () {
- return this._pf.overflow;
- },
-
- utc : function (keepLocalTime) {
- return this.utcOffset(0, keepLocalTime);
- },
-
- local : function (keepLocalTime) {
- if (this._isUTC) {
- this.utcOffset(0, keepLocalTime);
- this._isUTC = false;
-
- if (keepLocalTime) {
- this.subtract(this._dateUtcOffset(), 'm');
- }
- }
- return this;
- },
-
- format : function (inputString) {
- var output = formatMoment(this, inputString || moment.defaultFormat);
- return this.localeData().postformat(output);
- },
-
- add : createAdder(1, 'add'),
-
- subtract : createAdder(-1, 'subtract'),
-
- diff : function (input, units, asFloat) {
- var that = makeAs(input, this),
- zoneDiff = (that.utcOffset() - this.utcOffset()) * 6e4,
- anchor, diff, output, daysAdjust;
-
- units = normalizeUnits(units);
-
- if (units === 'year' || units === 'month' || units === 'quarter') {
- output = monthDiff(this, that);
- if (units === 'quarter') {
- output = output / 3;
- } else if (units === 'year') {
- output = output / 12;
- }
- } else {
- diff = this - that;
- output = units === 'second' ? diff / 1e3 : // 1000
- units === 'minute' ? diff / 6e4 : // 1000 * 60
- units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
- units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
- units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
- diff;
- }
- return asFloat ? output : absRound(output);
- },
-
- from : function (time, withoutSuffix) {
- return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
- },
-
- fromNow : function (withoutSuffix) {
- return this.from(moment(), withoutSuffix);
- },
-
- calendar : function (time) {
- // We want to compare the start of today, vs this.
- // Getting start-of-today depends on whether we're locat/utc/offset
- // or not.
- var now = time || moment(),
- sod = makeAs(now, this).startOf('day'),
- diff = this.diff(sod, 'days', true),
- format = diff < -6 ? 'sameElse' :
- diff < -1 ? 'lastWeek' :
- diff < 0 ? 'lastDay' :
- diff < 1 ? 'sameDay' :
- diff < 2 ? 'nextDay' :
- diff < 7 ? 'nextWeek' : 'sameElse';
- return this.format(this.localeData().calendar(format, this, moment(now)));
- },
-
- isLeapYear : function () {
- return isLeapYear(this.year());
- },
-
- isDST : function () {
- return (this.utcOffset() > this.clone().month(0).utcOffset() ||
- this.utcOffset() > this.clone().month(5).utcOffset());
- },
-
- day : function (input) {
- var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
- if (input != null) {
- input = parseWeekday(input, this.localeData());
- return this.add(input - day, 'd');
- } else {
- return day;
- }
- },
-
- month : makeAccessor('Month', true),
-
- startOf : function (units) {
- units = normalizeUnits(units);
- // the following switch intentionally omits break keywords
- // to utilize falling through the cases.
- switch (units) {
- case 'year':
- this.month(0);
- /* falls through */
- case 'quarter':
- case 'month':
- this.date(1);
- /* falls through */
- case 'week':
- case 'isoWeek':
- case 'day':
- this.hours(0);
- /* falls through */
- case 'hour':
- this.minutes(0);
- /* falls through */
- case 'minute':
- this.seconds(0);
- /* falls through */
- case 'second':
- this.milliseconds(0);
- /* falls through */
- }
-
- // weeks are a special case
- if (units === 'week') {
- this.weekday(0);
- } else if (units === 'isoWeek') {
- this.isoWeekday(1);
- }
-
- // quarters are also special
- if (units === 'quarter') {
- this.month(Math.floor(this.month() / 3) * 3);
- }
-
- return this;
- },
-
- endOf: function (units) {
- units = normalizeUnits(units);
- if (units === undefined || units === 'millisecond') {
- return this;
- }
- return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
- },
-
- isAfter: function (input, units) {
- var inputMs;
- units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond');
- if (units === 'millisecond') {
- input = moment.isMoment(input) ? input : moment(input);
- return +this > +input;
- } else {
- inputMs = moment.isMoment(input) ? +input : +moment(input);
- return inputMs < +this.clone().startOf(units);
- }
- },
-
- isBefore: function (input, units) {
- var inputMs;
- units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond');
- if (units === 'millisecond') {
- input = moment.isMoment(input) ? input : moment(input);
- return +this < +input;
- } else {
- inputMs = moment.isMoment(input) ? +input : +moment(input);
- return +this.clone().endOf(units) < inputMs;
- }
- },
-
- isBetween: function (from, to, units) {
- return this.isAfter(from, units) && this.isBefore(to, units);
- },
-
- isSame: function (input, units) {
- var inputMs;
- units = normalizeUnits(units || 'millisecond');
- if (units === 'millisecond') {
- input = moment.isMoment(input) ? input : moment(input);
- return +this === +input;
- } else {
- inputMs = +moment(input);
- return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units));
- }
- },
-
- min: deprecate(
- 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548',
- function (other) {
- other = moment.apply(null, arguments);
- return other < this ? this : other;
- }
- ),
-
- max: deprecate(
- 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548',
- function (other) {
- other = moment.apply(null, arguments);
- return other > this ? this : other;
- }
- ),
-
- zone : deprecate(
- 'moment().zone is deprecated, use moment().utcOffset instead. ' +
- 'https://github.com/moment/moment/issues/1779',
- function (input, keepLocalTime) {
- if (input != null) {
- if (typeof input !== 'string') {
- input = -input;
- }
-
- this.utcOffset(input, keepLocalTime);
-
- return this;
- } else {
- return -this.utcOffset();
- }
- }
- ),
-
- // keepLocalTime = true means only change the timezone, without
- // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
- // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
- // +0200, so we adjust the time as needed, to be valid.
- //
- // Keeping the time actually adds/subtracts (one hour)
- // from the actual represented time. That is why we call updateOffset
- // a second time. In case it wants us to change the offset again
- // _changeInProgress == true case, then we have to adjust, because
- // there is no such time in the given timezone.
- utcOffset : function (input, keepLocalTime) {
- var offset = this._offset || 0,
- localAdjust;
- if (input != null) {
- if (typeof input === 'string') {
- input = utcOffsetFromString(input);
- }
- if (Math.abs(input) < 16) {
- input = input * 60;
- }
- if (!this._isUTC && keepLocalTime) {
- localAdjust = this._dateUtcOffset();
- }
- this._offset = input;
- this._isUTC = true;
- if (localAdjust != null) {
- this.add(localAdjust, 'm');
- }
- if (offset !== input) {
- if (!keepLocalTime || this._changeInProgress) {
- addOrSubtractDurationFromMoment(this,
- moment.duration(input - offset, 'm'), 1, false);
- } else if (!this._changeInProgress) {
- this._changeInProgress = true;
- moment.updateOffset(this, true);
- this._changeInProgress = null;
- }
- }
-
- return this;
- } else {
- return this._isUTC ? offset : this._dateUtcOffset();
- }
- },
-
- isLocal : function () {
- return !this._isUTC;
- },
-
- isUtcOffset : function () {
- return this._isUTC;
- },
-
- isUtc : function () {
- return this._isUTC && this._offset === 0;
- },
-
- zoneAbbr : function () {
- return this._isUTC ? 'UTC' : '';
- },
-
- zoneName : function () {
- return this._isUTC ? 'Coordinated Universal Time' : '';
- },
-
- parseZone : function () {
- if (this._tzm) {
- this.utcOffset(this._tzm);
- } else if (typeof this._i === 'string') {
- this.utcOffset(utcOffsetFromString(this._i));
- }
- return this;
- },
-
- hasAlignedHourOffset : function (input) {
- if (!input) {
- input = 0;
- }
- else {
- input = moment(input).utcOffset();
- }
-
- return (this.utcOffset() - input) % 60 === 0;
- },
-
- daysInMonth : function () {
- return daysInMonth(this.year(), this.month());
- },
-
- dayOfYear : function (input) {
- var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1;
- return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
- },
-
- quarter : function (input) {
- return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
- },
-
- weekYear : function (input) {
- var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year;
- return input == null ? year : this.add((input - year), 'y');
- },
-
- isoWeekYear : function (input) {
- var year = weekOfYear(this, 1, 4).year;
- return input == null ? year : this.add((input - year), 'y');
- },
-
- week : function (input) {
- var week = this.localeData().week(this);
- return input == null ? week : this.add((input - week) * 7, 'd');
- },
-
- isoWeek : function (input) {
- var week = weekOfYear(this, 1, 4).week;
- return input == null ? week : this.add((input - week) * 7, 'd');
- },
-
- weekday : function (input) {
- var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
- return input == null ? weekday : this.add(input - weekday, 'd');
- },
-
- isoWeekday : function (input) {
- // behaves the same as moment#day except
- // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
- // as a setter, sunday should belong to the previous week.
- return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);
- },
-
- isoWeeksInYear : function () {
- return weeksInYear(this.year(), 1, 4);
- },
-
- weeksInYear : function () {
- var weekInfo = this.localeData()._week;
- return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
- },
-
- get : function (units) {
- units = normalizeUnits(units);
- return this[units]();
- },
-
- set : function (units, value) {
- var unit;
- if (typeof units === 'object') {
- for (unit in units) {
- this.set(unit, units[unit]);
- }
- }
- else {
- units = normalizeUnits(units);
- if (typeof this[units] === 'function') {
- this[units](value);
- }
- }
- return this;
- },
-
- // If passed a locale key, it will set the locale for this
- // instance. Otherwise, it will return the locale configuration
- // variables for this instance.
- locale : function (key) {
- var newLocaleData;
-
- if (key === undefined) {
- return this._locale._abbr;
- } else {
- newLocaleData = moment.localeData(key);
- if (newLocaleData != null) {
- this._locale = newLocaleData;
- }
- return this;
- }
- },
-
- lang : deprecate(
- 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
- function (key) {
- if (key === undefined) {
- return this.localeData();
- } else {
- return this.locale(key);
- }
- }
- ),
-
- localeData : function () {
- return this._locale;
- },
-
- _dateUtcOffset : function () {
- // On Firefox.24 Date#getTimezoneOffset returns a floating point.
- // https://github.com/moment/moment/pull/1871
- return -Math.round(this._d.getTimezoneOffset() / 15) * 15;
- }
-
- });
-
- function rawMonthSetter(mom, value) {
- var dayOfMonth;
-
- // TODO: Move this out of here!
- if (typeof value === 'string') {
- value = mom.localeData().monthsParse(value);
- // TODO: Another silent failure?
- if (typeof value !== 'number') {
- return mom;
- }
- }
-
- dayOfMonth = Math.min(mom.date(),
- daysInMonth(mom.year(), value));
- mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
- return mom;
- }
-
- function rawGetter(mom, unit) {
- return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]();
- }
-
- function rawSetter(mom, unit, value) {
- if (unit === 'Month') {
- return rawMonthSetter(mom, value);
- } else {
- return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
- }
- }
-
- function makeAccessor(unit, keepTime) {
- return function (value) {
- if (value != null) {
- rawSetter(this, unit, value);
- moment.updateOffset(this, keepTime);
- return this;
- } else {
- return rawGetter(this, unit);
- }
- };
- }
-
- moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false);
- moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false);
- moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false);
- // Setting the hour should keep the time, because the user explicitly
- // specified which hour he wants. So trying to maintain the same hour (in
- // a new timezone) makes sense. Adding/subtracting hours does not follow
- // this rule.
- moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true);
- // moment.fn.month is defined separately
- moment.fn.date = makeAccessor('Date', true);
- moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true));
- moment.fn.year = makeAccessor('FullYear', true);
- moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true));
-
- // add plural methods
- moment.fn.days = moment.fn.day;
- moment.fn.months = moment.fn.month;
- moment.fn.weeks = moment.fn.week;
- moment.fn.isoWeeks = moment.fn.isoWeek;
- moment.fn.quarters = moment.fn.quarter;
-
- // add aliased format methods
- moment.fn.toJSON = moment.fn.toISOString;
-
- // alias isUtc for dev-friendliness
- moment.fn.isUTC = moment.fn.isUtc;
-
- /************************************
- Duration Prototype
- ************************************/
-
-
- function daysToYears (days) {
- // 400 years have 146097 days (taking into account leap year rules)
- return days * 400 / 146097;
- }
-
- function yearsToDays (years) {
- // years * 365 + absRound(years / 4) -
- // absRound(years / 100) + absRound(years / 400);
- return years * 146097 / 400;
- }
-
- extend(moment.duration.fn = Duration.prototype, {
-
- _bubble : function () {
- var milliseconds = this._milliseconds,
- days = this._days,
- months = this._months,
- data = this._data,
- seconds, minutes, hours, years = 0;
-
- // The following code bubbles up values, see the tests for
- // examples of what that means.
- data.milliseconds = milliseconds % 1000;
-
- seconds = absRound(milliseconds / 1000);
- data.seconds = seconds % 60;
-
- minutes = absRound(seconds / 60);
- data.minutes = minutes % 60;
-
- hours = absRound(minutes / 60);
- data.hours = hours % 24;
-
- days += absRound(hours / 24);
-
- // Accurately convert days to years, assume start from year 0.
- years = absRound(daysToYears(days));
- days -= absRound(yearsToDays(years));
-
- // 30 days to a month
- // TODO (iskren): Use anchor date (like 1st Jan) to compute this.
- months += absRound(days / 30);
- days %= 30;
-
- // 12 months -> 1 year
- years += absRound(months / 12);
- months %= 12;
-
- data.days = days;
- data.months = months;
- data.years = years;
- },
-
- abs : function () {
- 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);
-
- return this;
- },
-
- weeks : function () {
- return absRound(this.days() / 7);
- },
-
- valueOf : function () {
- return this._milliseconds +
- this._days * 864e5 +
- (this._months % 12) * 2592e6 +
- toInt(this._months / 12) * 31536e6;
- },
-
- humanize : function (withSuffix) {
- var output = relativeTime(this, !withSuffix, this.localeData());
-
- if (withSuffix) {
- output = this.localeData().pastFuture(+this, output);
- }
-
- return this.localeData().postformat(output);
- },
-
- add : function (input, val) {
- // supports only 2.0-style add(1, 's') or add(moment)
- var dur = moment.duration(input, val);
-
- this._milliseconds += dur._milliseconds;
- this._days += dur._days;
- this._months += dur._months;
-
- this._bubble();
-
- return this;
- },
-
- subtract : function (input, val) {
- var dur = moment.duration(input, val);
-
- this._milliseconds -= dur._milliseconds;
- this._days -= dur._days;
- this._months -= dur._months;
-
- this._bubble();
-
- return this;
- },
-
- get : function (units) {
- units = normalizeUnits(units);
- return this[units.toLowerCase() + 's']();
- },
-
- as : function (units) {
- var days, months;
- units = normalizeUnits(units);
-
- if (units === 'month' || units === 'year') {
- days = this._days + this._milliseconds / 864e5;
- months = this._months + daysToYears(days) * 12;
- return units === 'month' ? months : months / 12;
- } else {
- // handle milliseconds separately because of floating point math errors (issue #1867)
- days = this._days + Math.round(yearsToDays(this._months / 12));
- switch (units) {
- case 'week': return days / 7 + this._milliseconds / 6048e5;
- case 'day': return days + this._milliseconds / 864e5;
- case 'hour': return days * 24 + this._milliseconds / 36e5;
- case 'minute': return days * 24 * 60 + this._milliseconds / 6e4;
- case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000;
- // Math.floor prevents floating point math errors here
- case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds;
- default: throw new Error('Unknown unit ' + units);
- }
- }
- },
-
- lang : moment.fn.lang,
- locale : moment.fn.locale,
-
- toIsoString : deprecate(
- 'toIsoString() is deprecated. Please use toISOString() instead ' +
- '(notice the capitals)',
- function () {
- return this.toISOString();
- }
- ),
-
- toISOString : function () {
- // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
- var years = Math.abs(this.years()),
- months = Math.abs(this.months()),
- days = Math.abs(this.days()),
- hours = Math.abs(this.hours()),
- minutes = Math.abs(this.minutes()),
- seconds = Math.abs(this.seconds() + this.milliseconds() / 1000);
-
- if (!this.asSeconds()) {
- // this is the same as C#'s (Noda) and python (isodate)...
- // but not other JS (goog.date)
- return 'P0D';
- }
-
- return (this.asSeconds() < 0 ? '-' : '') +
- 'P' +
- (years ? years + 'Y' : '') +
- (months ? months + 'M' : '') +
- (days ? days + 'D' : '') +
- ((hours || minutes || seconds) ? 'T' : '') +
- (hours ? hours + 'H' : '') +
- (minutes ? minutes + 'M' : '') +
- (seconds ? seconds + 'S' : '');
- },
-
- localeData : function () {
- return this._locale;
- },
-
- toJSON : function () {
- return this.toISOString();
- }
- });
-
- moment.duration.fn.toString = moment.duration.fn.toISOString;
-
- function makeDurationGetter(name) {
- moment.duration.fn[name] = function () {
- return this._data[name];
- };
- }
-
- for (i in unitMillisecondFactors) {
- if (hasOwnProp(unitMillisecondFactors, i)) {
- makeDurationGetter(i.toLowerCase());
- }
- }
-
- moment.duration.fn.asMilliseconds = function () {
- return this.as('ms');
- };
- moment.duration.fn.asSeconds = function () {
- return this.as('s');
- };
- moment.duration.fn.asMinutes = function () {
- return this.as('m');
- };
- moment.duration.fn.asHours = function () {
- return this.as('h');
- };
- moment.duration.fn.asDays = function () {
- return this.as('d');
- };
- moment.duration.fn.asWeeks = function () {
- return this.as('weeks');
- };
- moment.duration.fn.asMonths = function () {
- return this.as('M');
- };
- moment.duration.fn.asYears = function () {
- return this.as('y');
- };
-
- /************************************
- Default Locale
- ************************************/
-
-
- // Set default locale, other locale will inherit from English.
- moment.locale('en', {
- ordinalParse: /\d{1,2}(th|st|nd|rd)/,
- ordinal : function (number) {
- var b = number % 10,
- output = (toInt(number % 100 / 10) === 1) ? 'th' :
- (b === 1) ? 'st' :
- (b === 2) ? 'nd' :
- (b === 3) ? 'rd' : 'th';
- return number + output;
- }
- });
-
- /* EMBED_LOCALES */
-
- /************************************
- Exposing Moment
- ************************************/
-
- function makeGlobal(shouldDeprecate) {
- /*global ender:false */
- if (typeof ender !== 'undefined') {
- return;
- }
- oldGlobalMoment = globalScope.moment;
- if (shouldDeprecate) {
- globalScope.moment = deprecate(
- 'Accessing Moment through the global scope is ' +
- 'deprecated, and will be removed in an upcoming ' +
- 'release.',
- moment);
- } else {
- globalScope.moment = moment;
- }
- }
-
- // CommonJS module is defined
- if (hasModule) {
- module.exports = moment;
- } else if (typeof define === 'function' && define.amd) {
- define(function (require, exports, module) {
- if (module.config && module.config() && module.config().noGlobal === true) {
- // release the global variable
- globalScope.moment = oldGlobalMoment;
- }
-
- return moment;
- });
- makeGlobal(true);
- } else {
- makeGlobal();
- }
-}).call(this);
\ No newline at end of file
+!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return fd.apply(null,arguments)}function b(a){fd=a}function c(a){return a instanceof Array||"[object Array]"===Object.prototype.toString.call(a)}function d(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function e(a,b){var c,d=[];for(c=0;c0)for(c in hd)d=hd[c],e=b[d],m(e)||(a[d]=e);return a}function o(b){n(this,b),this._d=new Date(null!=b._d?b._d.getTime():NaN),id===!1&&(id=!0,a.updateOffset(this),id=!1)}function p(a){return a instanceof o||null!=a&&null!=a._isAMomentObject}function q(a){return 0>a?Math.ceil(a):Math.floor(a)}function r(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=q(b)),c}function s(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;e>d;d++)(c&&a[d]!==b[d]||!c&&r(a[d])!==r(b[d]))&&g++;return g+f}function t(b){a.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+b)}function u(b,c){var d=!0;return g(function(){return null!=a.deprecationHandler&&a.deprecationHandler(null,b),d&&(t(b+"\nArguments: "+Array.prototype.slice.call(arguments).join(", ")+"\n"+(new Error).stack),d=!1),c.apply(this,arguments)},c)}function v(b,c){null!=a.deprecationHandler&&a.deprecationHandler(b,c),jd[b]||(t(c),jd[b]=!0)}function w(a){return a instanceof Function||"[object Function]"===Object.prototype.toString.call(a)}function x(a){return"[object Object]"===Object.prototype.toString.call(a)}function y(a){var b,c;for(c in a)b=a[c],w(b)?this[c]=b:this["_"+c]=b;this._config=a,this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function z(a,b){var c,d=g({},a);for(c in b)f(b,c)&&(x(a[c])&&x(b[c])?(d[c]={},g(d[c],a[c]),g(d[c],b[c])):null!=b[c]?d[c]=b[c]:delete d[c]);return d}function A(a){null!=a&&this.set(a)}function B(a){return a?a.toLowerCase().replace("_","-"):a}function C(a){for(var b,c,d,e,f=0;f0;){if(d=D(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&s(e,c,!0)>=b-1)break;b--}f++}return null}function D(a){var b=null;if(!nd[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=ld._abbr,require("./locale/"+a),E(b)}catch(c){}return nd[a]}function E(a,b){var c;return a&&(c=m(b)?H(a):F(a,b),c&&(ld=c)),ld._abbr}function F(a,b){return null!==b?(b.abbr=a,null!=nd[a]?(v("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale"),b=z(nd[a]._config,b)):null!=b.parentLocale&&(null!=nd[b.parentLocale]?b=z(nd[b.parentLocale]._config,b):v("parentLocaleUndefined","specified parentLocale is not defined yet")),nd[a]=new A(b),E(a),nd[a]):(delete nd[a],null)}function G(a,b){if(null!=b){var c;null!=nd[a]&&(b=z(nd[a]._config,b)),c=new A(b),c.parentLocale=nd[a],nd[a]=c,E(a)}else null!=nd[a]&&(null!=nd[a].parentLocale?nd[a]=nd[a].parentLocale:null!=nd[a]&&delete nd[a]);return nd[a]}function H(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return ld;if(!c(a)){if(b=D(a))return b;a=[a]}return C(a)}function I(){return kd(nd)}function J(a,b){var c=a.toLowerCase();od[c]=od[c+"s"]=od[b]=a}function K(a){return"string"==typeof a?od[a]||od[a.toLowerCase()]:void 0}function L(a){var b,c,d={};for(c in a)f(a,c)&&(b=K(c),b&&(d[b]=a[c]));return d}function M(b,c){return function(d){return null!=d?(O(this,b,d),a.updateOffset(this,c),this):N(this,b)}}function N(a,b){return a.isValid()?a._d["get"+(a._isUTC?"UTC":"")+b]():NaN}function O(a,b,c){a.isValid()&&a._d["set"+(a._isUTC?"UTC":"")+b](c)}function P(a,b){var c;if("object"==typeof a)for(c in a)this.set(c,a[c]);else if(a=K(a),w(this[a]))return this[a](b);return this}function Q(a,b,c){var d=""+Math.abs(a),e=b-d.length,f=a>=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d}function R(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(sd[a]=e),b&&(sd[b[0]]=function(){return Q(e.apply(this,arguments),b[1],b[2])}),c&&(sd[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function S(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function T(a){var b,c,d=a.match(pd);for(b=0,c=d.length;c>b;b++)sd[d[b]]?d[b]=sd[d[b]]:d[b]=S(d[b]);return function(b){var e,f="";for(e=0;c>e;e++)f+=d[e]instanceof Function?d[e].call(b,a):d[e];return f}}function U(a,b){return a.isValid()?(b=V(b,a.localeData()),rd[b]=rd[b]||T(b),rd[b](a)):a.localeData().invalidDate()}function V(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(qd.lastIndex=0;d>=0&&qd.test(a);)a=a.replace(qd,c),qd.lastIndex=0,d-=1;return a}function W(a,b,c){Kd[a]=w(b)?b:function(a,d){return a&&c?c:b}}function X(a,b){return f(Kd,a)?Kd[a](b._strict,b._locale):new RegExp(Y(a))}function Y(a){return Z(a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}))}function Z(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function $(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),"number"==typeof b&&(d=function(a,c){c[b]=r(a)}),c=0;cd;++d)f=h([2e3,d]),this._shortMonthsParse[d]=this.monthsShort(f,"").toLocaleLowerCase(),this._longMonthsParse[d]=this.months(f,"").toLocaleLowerCase();return c?"MMM"===b?(e=md.call(this._shortMonthsParse,g),-1!==e?e:null):(e=md.call(this._longMonthsParse,g),-1!==e?e:null):"MMM"===b?(e=md.call(this._shortMonthsParse,g),-1!==e?e:(e=md.call(this._longMonthsParse,g),-1!==e?e:null)):(e=md.call(this._longMonthsParse,g),-1!==e?e:(e=md.call(this._shortMonthsParse,g),-1!==e?e:null))}function fa(a,b,c){var d,e,f;if(this._monthsParseExact)return ea.call(this,a,b,c);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),d=0;12>d;d++){if(e=h([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}}function ga(a,b){var c;if(!a.isValid())return a;if("string"==typeof b)if(/^\d+$/.test(b))b=r(b);else if(b=a.localeData().monthsParse(b),"number"!=typeof b)return a;return c=Math.min(a.date(),ba(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a}function ha(b){return null!=b?(ga(this,b),a.updateOffset(this,!0),this):N(this,"Month")}function ia(){return ba(this.year(),this.month())}function ja(a){return this._monthsParseExact?(f(this,"_monthsRegex")||la.call(this),a?this._monthsShortStrictRegex:this._monthsShortRegex):this._monthsShortStrictRegex&&a?this._monthsShortStrictRegex:this._monthsShortRegex}function ka(a){return this._monthsParseExact?(f(this,"_monthsRegex")||la.call(this),a?this._monthsStrictRegex:this._monthsRegex):this._monthsStrictRegex&&a?this._monthsStrictRegex:this._monthsRegex}function la(){function a(a,b){return b.length-a.length}var b,c,d=[],e=[],f=[];for(b=0;12>b;b++)c=h([2e3,b]),d.push(this.monthsShort(c,"")),e.push(this.months(c,"")),f.push(this.months(c,"")),f.push(this.monthsShort(c,""));for(d.sort(a),e.sort(a),f.sort(a),b=0;12>b;b++)d[b]=Z(d[b]),e[b]=Z(e[b]),f[b]=Z(f[b]);this._monthsRegex=new RegExp("^("+f.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+e.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+d.join("|")+")","i")}function ma(a){var b,c=a._a;return c&&-2===j(a).overflow&&(b=c[Nd]<0||c[Nd]>11?Nd:c[Od]<1||c[Od]>ba(c[Md],c[Nd])?Od:c[Pd]<0||c[Pd]>24||24===c[Pd]&&(0!==c[Qd]||0!==c[Rd]||0!==c[Sd])?Pd:c[Qd]<0||c[Qd]>59?Qd:c[Rd]<0||c[Rd]>59?Rd:c[Sd]<0||c[Sd]>999?Sd:-1,j(a)._overflowDayOfYear&&(Md>b||b>Od)&&(b=Od),j(a)._overflowWeeks&&-1===b&&(b=Td),j(a)._overflowWeekday&&-1===b&&(b=Ud),j(a).overflow=b),a}function na(a){var b,c,d,e,f,g,h=a._i,i=$d.exec(h)||_d.exec(h);if(i){for(j(a).iso=!0,b=0,c=be.length;c>b;b++)if(be[b][1].exec(i[1])){e=be[b][0],d=be[b][2]!==!1;break}if(null==e)return void(a._isValid=!1);if(i[3]){for(b=0,c=ce.length;c>b;b++)if(ce[b][1].exec(i[3])){f=(i[2]||" ")+ce[b][0];break}if(null==f)return void(a._isValid=!1)}if(!d&&null!=f)return void(a._isValid=!1);if(i[4]){if(!ae.exec(i[4]))return void(a._isValid=!1);g="Z"}a._f=e+(f||"")+(g||""),Ca(a)}else a._isValid=!1}function oa(b){var c=de.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(na(b),void(b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b))))}function pa(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 100>a&&a>=0&&isFinite(h.getFullYear())&&h.setFullYear(a),h}function qa(a){var b=new Date(Date.UTC.apply(null,arguments));return 100>a&&a>=0&&isFinite(b.getUTCFullYear())&&b.setUTCFullYear(a),b}function ra(a){return sa(a)?366:365}function sa(a){return a%4===0&&a%100!==0||a%400===0}function ta(){return sa(this.year())}function ua(a,b,c){var d=7+b-c,e=(7+qa(a,0,d).getUTCDay()-b)%7;return-e+d-1}function va(a,b,c,d,e){var f,g,h=(7+c-d)%7,i=ua(a,d,e),j=1+7*(b-1)+h+i;return 0>=j?(f=a-1,g=ra(f)+j):j>ra(a)?(f=a+1,g=j-ra(a)):(f=a,g=j),{year:f,dayOfYear:g}}function wa(a,b,c){var d,e,f=ua(a.year(),b,c),g=Math.floor((a.dayOfYear()-f-1)/7)+1;return 1>g?(e=a.year()-1,d=g+xa(e,b,c)):g>xa(a.year(),b,c)?(d=g-xa(a.year(),b,c),e=a.year()+1):(e=a.year(),d=g),{week:d,year:e}}function xa(a,b,c){var d=ua(a,b,c),e=ua(a+1,b,c);return(ra(a)-d+e)/7}function ya(a,b,c){return null!=a?a:null!=b?b:c}function za(b){var c=new Date(a.now());return b._useUTC?[c.getUTCFullYear(),c.getUTCMonth(),c.getUTCDate()]:[c.getFullYear(),c.getMonth(),c.getDate()]}function Aa(a){var b,c,d,e,f=[];if(!a._d){for(d=za(a),a._w&&null==a._a[Od]&&null==a._a[Nd]&&Ba(a),a._dayOfYear&&(e=ya(a._a[Md],d[Md]),a._dayOfYear>ra(e)&&(j(a)._overflowDayOfYear=!0),c=qa(e,0,a._dayOfYear),a._a[Nd]=c.getUTCMonth(),a._a[Od]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=f[b]=d[b];for(;7>b;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b];24===a._a[Pd]&&0===a._a[Qd]&&0===a._a[Rd]&&0===a._a[Sd]&&(a._nextDay=!0,a._a[Pd]=0),a._d=(a._useUTC?qa:pa).apply(null,f),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[Pd]=24)}}function Ba(a){var b,c,d,e,f,g,h,i;b=a._w,null!=b.GG||null!=b.W||null!=b.E?(f=1,g=4,c=ya(b.GG,a._a[Md],wa(Ka(),1,4).year),d=ya(b.W,1),e=ya(b.E,1),(1>e||e>7)&&(i=!0)):(f=a._locale._week.dow,g=a._locale._week.doy,c=ya(b.gg,a._a[Md],wa(Ka(),f,g).year),d=ya(b.w,1),null!=b.d?(e=b.d,(0>e||e>6)&&(i=!0)):null!=b.e?(e=b.e+f,(b.e<0||b.e>6)&&(i=!0)):e=f),1>d||d>xa(c,f,g)?j(a)._overflowWeeks=!0:null!=i?j(a)._overflowWeekday=!0:(h=va(c,d,e,f,g),a._a[Md]=h.year,a._dayOfYear=h.dayOfYear)}function Ca(b){if(b._f===a.ISO_8601)return void na(b);b._a=[],j(b).empty=!0;var c,d,e,f,g,h=""+b._i,i=h.length,k=0;for(e=V(b._f,b._locale).match(pd)||[],c=0;c0&&j(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),k+=d.length),sd[f]?(d?j(b).empty=!1:j(b).unusedTokens.push(f),aa(f,d,b)):b._strict&&!d&&j(b).unusedTokens.push(f);j(b).charsLeftOver=i-k,h.length>0&&j(b).unusedInput.push(h),j(b).bigHour===!0&&b._a[Pd]<=12&&b._a[Pd]>0&&(j(b).bigHour=void 0),j(b).parsedDateParts=b._a.slice(0),j(b).meridiem=b._meridiem,b._a[Pd]=Da(b._locale,b._a[Pd],b._meridiem),Aa(b),ma(b)}function Da(a,b,c){var d;return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&12>b&&(b+=12),d||12!==b||(b=0),b):b}function Ea(a){var b,c,d,e,f;if(0===a._f.length)return j(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;ef)&&(d=f,c=b));g(a,c||b)}function Fa(a){if(!a._d){var b=L(a._i);a._a=e([b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],function(a){return a&&parseInt(a,10)}),Aa(a)}}function Ga(a){var b=new o(ma(Ha(a)));return b._nextDay&&(b.add(1,"d"),b._nextDay=void 0),b}function Ha(a){var b=a._i,e=a._f;return a._locale=a._locale||H(a._l),null===b||void 0===e&&""===b?l({nullInput:!0}):("string"==typeof b&&(a._i=b=a._locale.preparse(b)),p(b)?new o(ma(b)):(c(e)?Ea(a):e?Ca(a):d(b)?a._d=b:Ia(a),k(a)||(a._d=null),a))}function Ia(b){var f=b._i;void 0===f?b._d=new Date(a.now()):d(f)?b._d=new Date(f.valueOf()):"string"==typeof f?oa(b):c(f)?(b._a=e(f.slice(0),function(a){return parseInt(a,10)}),Aa(b)):"object"==typeof f?Fa(b):"number"==typeof f?b._d=new Date(f):a.createFromInputFallback(b)}function Ja(a,b,c,d,e){var f={};return"boolean"==typeof c&&(d=c,c=void 0),f._isAMomentObject=!0,f._useUTC=f._isUTC=e,f._l=c,f._i=a,f._f=b,f._strict=d,Ga(f)}function Ka(a,b,c,d){return Ja(a,b,c,d,!1)}function La(a,b){var d,e;if(1===b.length&&c(b[0])&&(b=b[0]),!b.length)return Ka();for(d=b[0],e=1;ea&&(a=-a,c="-"),c+Q(~~(a/60),2)+b+Q(~~a%60,2)})}function Ra(a,b){var c=(b||"").match(a)||[],d=c[c.length-1]||[],e=(d+"").match(ie)||["-",0,0],f=+(60*e[1])+r(e[2]);return"+"===e[0]?f:-f}function Sa(b,c){var e,f;return c._isUTC?(e=c.clone(),f=(p(b)||d(b)?b.valueOf():Ka(b).valueOf())-e.valueOf(),e._d.setTime(e._d.valueOf()+f),a.updateOffset(e,!1),e):Ka(b).local()}function Ta(a){return 15*-Math.round(a._d.getTimezoneOffset()/15)}function Ua(b,c){var d,e=this._offset||0;return this.isValid()?null!=b?("string"==typeof b?b=Ra(Hd,b):Math.abs(b)<16&&(b=60*b),!this._isUTC&&c&&(d=Ta(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?jb(this,db(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?e:Ta(this):null!=b?this:NaN}function Va(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Wa(a){return this.utcOffset(0,a)}function Xa(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Ta(this),"m")),this}function Ya(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(Ra(Gd,this._i)),this}function Za(a){return this.isValid()?(a=a?Ka(a).utcOffset():0,(this.utcOffset()-a)%60===0):!1}function $a(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function _a(){if(!m(this._isDSTShifted))return this._isDSTShifted;var a={};if(n(a,this),a=Ha(a),a._a){var b=a._isUTC?h(a._a):Ka(a._a);this._isDSTShifted=this.isValid()&&s(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function ab(){return this.isValid()?!this._isUTC:!1}function bb(){return this.isValid()?this._isUTC:!1}function cb(){return this.isValid()?this._isUTC&&0===this._offset:!1}function db(a,b){var c,d,e,g=a,h=null;return Pa(a)?g={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(g={},b?g[b]=a:g.milliseconds=a):(h=je.exec(a))?(c="-"===h[1]?-1:1,g={y:0,d:r(h[Od])*c,h:r(h[Pd])*c,m:r(h[Qd])*c,s:r(h[Rd])*c,ms:r(h[Sd])*c}):(h=ke.exec(a))?(c="-"===h[1]?-1:1,g={y:eb(h[2],c),M:eb(h[3],c),w:eb(h[4],c),d:eb(h[5],c),h:eb(h[6],c),m:eb(h[7],c),s:eb(h[8],c)}):null==g?g={}:"object"==typeof g&&("from"in g||"to"in g)&&(e=gb(Ka(g.from),Ka(g.to)),g={},g.ms=e.milliseconds,g.M=e.months),d=new Oa(g),Pa(a)&&f(a,"_locale")&&(d._locale=a._locale),d}function eb(a,b){var c=a&&parseFloat(a.replace(",","."));return(isNaN(c)?0:c)*b}function fb(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function gb(a,b){var c;return a.isValid()&&b.isValid()?(b=Sa(b,a),a.isBefore(b)?c=fb(a,b):(c=fb(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c):{milliseconds:0,months:0}}function hb(a){return 0>a?-1*Math.round(-1*a):Math.round(a)}function ib(a,b){return function(c,d){var e,f;return null===d||isNaN(+d)||(v(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period)."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=db(c,d),jb(this,e,a),this}}function jb(b,c,d,e){var f=c._milliseconds,g=hb(c._days),h=hb(c._months);b.isValid()&&(e=null==e?!0:e,f&&b._d.setTime(b._d.valueOf()+f*d),g&&O(b,"Date",N(b,"Date")+g*d),h&&ga(b,N(b,"Month")+h*d),e&&a.updateOffset(b,g||h))}function kb(a,b){var c=a||Ka(),d=Sa(c,this).startOf("day"),e=this.diff(d,"days",!0),f=-6>e?"sameElse":-1>e?"lastWeek":0>e?"lastDay":1>e?"sameDay":2>e?"nextDay":7>e?"nextWeek":"sameElse",g=b&&(w(b[f])?b[f]():b[f]);return this.format(g||this.localeData().calendar(f,this,Ka(c)))}function lb(){return new o(this)}function mb(a,b){var c=p(a)?a:Ka(a);return this.isValid()&&c.isValid()?(b=K(m(b)?"millisecond":b),"millisecond"===b?this.valueOf()>c.valueOf():c.valueOf()b-f?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)||0}function ub(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function vb(){var a=this.clone().utc();return 0f&&(b=f),Vb.call(this,a,b,c,d,e))}function Vb(a,b,c,d,e){var f=va(a,b,c,d,e),g=qa(f.year,0,f.dayOfYear);return this.year(g.getUTCFullYear()),this.month(g.getUTCMonth()),this.date(g.getUTCDate()),this}function Wb(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)}function Xb(a){return wa(a,this._week.dow,this._week.doy).week}function Yb(){return this._week.dow}function Zb(){return this._week.doy}function $b(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function _b(a){var b=wa(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")}function ac(a,b){return"string"!=typeof a?a:isNaN(a)?(a=b.weekdaysParse(a),"number"==typeof a?a:null):parseInt(a,10)}function bc(a,b){return c(this._weekdays)?this._weekdays[a.day()]:this._weekdays[this._weekdays.isFormat.test(b)?"format":"standalone"][a.day()]}function cc(a){return this._weekdaysShort[a.day()]}function dc(a){return this._weekdaysMin[a.day()]}function ec(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],d=0;7>d;++d)f=h([2e3,1]).day(d),this._minWeekdaysParse[d]=this.weekdaysMin(f,"").toLocaleLowerCase(),this._shortWeekdaysParse[d]=this.weekdaysShort(f,"").toLocaleLowerCase(),this._weekdaysParse[d]=this.weekdays(f,"").toLocaleLowerCase();return c?"dddd"===b?(e=md.call(this._weekdaysParse,g),-1!==e?e:null):"ddd"===b?(e=md.call(this._shortWeekdaysParse,g),-1!==e?e:null):(e=md.call(this._minWeekdaysParse,g),-1!==e?e:null):"dddd"===b?(e=md.call(this._weekdaysParse,g),-1!==e?e:(e=md.call(this._shortWeekdaysParse,g),-1!==e?e:(e=md.call(this._minWeekdaysParse,g),-1!==e?e:null))):"ddd"===b?(e=md.call(this._shortWeekdaysParse,g),-1!==e?e:(e=md.call(this._weekdaysParse,g),-1!==e?e:(e=md.call(this._minWeekdaysParse,g),-1!==e?e:null))):(e=md.call(this._minWeekdaysParse,g),-1!==e?e:(e=md.call(this._weekdaysParse,g),-1!==e?e:(e=md.call(this._shortWeekdaysParse,g),-1!==e?e:null)))}function fc(a,b,c){var d,e,f;if(this._weekdaysParseExact)return ec.call(this,a,b,c);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),d=0;7>d;d++){if(e=h([2e3,1]).day(d),c&&!this._fullWeekdaysParse[d]&&(this._fullWeekdaysParse[d]=new RegExp("^"+this.weekdays(e,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[d]=new RegExp("^"+this.weekdaysShort(e,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[d]=new RegExp("^"+this.weekdaysMin(e,"").replace(".",".?")+"$","i")),this._weekdaysParse[d]||(f="^"+this.weekdays(e,"")+"|^"+this.weekdaysShort(e,"")+"|^"+this.weekdaysMin(e,""),this._weekdaysParse[d]=new RegExp(f.replace(".",""),"i")),c&&"dddd"===b&&this._fullWeekdaysParse[d].test(a))return d;if(c&&"ddd"===b&&this._shortWeekdaysParse[d].test(a))return d;if(c&&"dd"===b&&this._minWeekdaysParse[d].test(a))return d;if(!c&&this._weekdaysParse[d].test(a))return d}}function gc(a){if(!this.isValid())return null!=a?this:NaN;var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=ac(a,this.localeData()),this.add(a-b,"d")):b}function hc(a){if(!this.isValid())return null!=a?this:NaN;var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function ic(a){return this.isValid()?null==a?this.day()||7:this.day(this.day()%7?a:a-7):null!=a?this:NaN}function jc(a){return this._weekdaysParseExact?(f(this,"_weekdaysRegex")||mc.call(this),a?this._weekdaysStrictRegex:this._weekdaysRegex):this._weekdaysStrictRegex&&a?this._weekdaysStrictRegex:this._weekdaysRegex}function kc(a){return this._weekdaysParseExact?(f(this,"_weekdaysRegex")||mc.call(this),a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):this._weekdaysShortStrictRegex&&a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex}function lc(a){return this._weekdaysParseExact?(f(this,"_weekdaysRegex")||mc.call(this),a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):this._weekdaysMinStrictRegex&&a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex}function mc(){function a(a,b){return b.length-a.length}var b,c,d,e,f,g=[],i=[],j=[],k=[];for(b=0;7>b;b++)c=h([2e3,1]).day(b),d=this.weekdaysMin(c,""),e=this.weekdaysShort(c,""),f=this.weekdays(c,""),g.push(d),i.push(e),j.push(f),k.push(d),k.push(e),k.push(f);for(g.sort(a),i.sort(a),j.sort(a),k.sort(a),b=0;7>b;b++)i[b]=Z(i[b]),j[b]=Z(j[b]),k[b]=Z(k[b]);this._weekdaysRegex=new RegExp("^("+k.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+j.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+g.join("|")+")","i")}function nc(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function oc(){return this.hours()%12||12}function pc(){return this.hours()||24}function qc(a,b){R(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})}function rc(a,b){return b._meridiemParse}function sc(a){return"p"===(a+"").toLowerCase().charAt(0)}function tc(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function uc(a,b){b[Sd]=r(1e3*("0."+a))}function vc(){return this._isUTC?"UTC":""}function wc(){return this._isUTC?"Coordinated Universal Time":""}function xc(a){return Ka(1e3*a)}function yc(){return Ka.apply(null,arguments).parseZone()}function zc(a,b,c){var d=this._calendar[a];return w(d)?d.call(b,c):d}function Ac(a){var b=this._longDateFormat[a],c=this._longDateFormat[a.toUpperCase()];return b||!c?b:(this._longDateFormat[a]=c.replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a])}function Bc(){return this._invalidDate}function Cc(a){return this._ordinal.replace("%d",a)}function Dc(a){return a}function Ec(a,b,c,d){var e=this._relativeTime[c];return w(e)?e(a,b,c,d):e.replace(/%d/i,a)}function Fc(a,b){var c=this._relativeTime[a>0?"future":"past"];return w(c)?c(b):c.replace(/%s/i,b)}function Gc(a,b,c,d){var e=H(),f=h().set(d,b);return e[c](f,a)}function Hc(a,b,c){if("number"==typeof a&&(b=a,a=void 0),a=a||"",null!=b)return Gc(a,b,c,"month");var d,e=[];for(d=0;12>d;d++)e[d]=Gc(a,d,c,"month");return e}function Ic(a,b,c,d){"boolean"==typeof a?("number"==typeof b&&(c=b,b=void 0),b=b||""):(b=a,c=b,a=!1,"number"==typeof b&&(c=b,b=void 0),b=b||"");var e=H(),f=a?e._week.dow:0;if(null!=c)return Gc(b,(c+f)%7,d,"day");var g,h=[];for(g=0;7>g;g++)h[g]=Gc(b,(g+f)%7,d,"day");return h}function Jc(a,b){return Hc(a,b,"months")}function Kc(a,b){return Hc(a,b,"monthsShort")}function Lc(a,b,c){return Ic(a,b,c,"weekdays")}function Mc(a,b,c){return Ic(a,b,c,"weekdaysShort")}function Nc(a,b,c){return Ic(a,b,c,"weekdaysMin")}function Oc(){var a=this._data;return this._milliseconds=Le(this._milliseconds),this._days=Le(this._days),this._months=Le(this._months),a.milliseconds=Le(a.milliseconds),a.seconds=Le(a.seconds),a.minutes=Le(a.minutes),a.hours=Le(a.hours),a.months=Le(a.months),a.years=Le(a.years),this}function Pc(a,b,c,d){var e=db(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()}function Qc(a,b){return Pc(this,a,b,1)}function Rc(a,b){return Pc(this,a,b,-1)}function Sc(a){return 0>a?Math.floor(a):Math.ceil(a)}function Tc(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data;return f>=0&&g>=0&&h>=0||0>=f&&0>=g&&0>=h||(f+=864e5*Sc(Vc(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=q(f/1e3),i.seconds=a%60,b=q(a/60),i.minutes=b%60,c=q(b/60),i.hours=c%24,g+=q(c/24),e=q(Uc(g)),h+=e,g-=Sc(Vc(e)),d=q(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function Uc(a){return 4800*a/146097}function Vc(a){return 146097*a/4800}function Wc(a){var b,c,d=this._milliseconds;if(a=K(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+Uc(b),"month"===a?c:c/12;switch(b=this._days+Math.round(Vc(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3;case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}}function Xc(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*r(this._months/12)}function Yc(a){return function(){return this.as(a)}}function Zc(a){
+return a=K(a),this[a+"s"]()}function $c(a){return function(){return this._data[a]}}function _c(){return q(this.days()/7)}function ad(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function bd(a,b,c){var d=db(a).abs(),e=_e(d.as("s")),f=_e(d.as("m")),g=_e(d.as("h")),h=_e(d.as("d")),i=_e(d.as("M")),j=_e(d.as("y")),k=e=f&&["m"]||f=g&&["h"]||g=h&&["d"]||h=i&&["M"]||i=j&&["y"]||["yy",j];return k[2]=b,k[3]=+a>0,k[4]=c,ad.apply(null,k)}function cd(a,b){return void 0===af[a]?!1:void 0===b?af[a]:(af[a]=b,!0)}function dd(a){var b=this.localeData(),c=bd(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function ed(){var a,b,c,d=bf(this._milliseconds)/1e3,e=bf(this._days),f=bf(this._months);a=q(d/60),b=q(a/60),d%=60,a%=60,c=q(f/12),f%=12;var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(0>m?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"}var fd,gd;gd=Array.prototype.some?Array.prototype.some:function(a){for(var b=Object(this),c=b.length>>>0,d=0;c>d;d++)if(d in b&&a.call(this,b[d],d,b))return!0;return!1};var hd=a.momentProperties=[],id=!1,jd={};a.suppressDeprecationWarnings=!1,a.deprecationHandler=null;var kd;kd=Object.keys?Object.keys:function(a){var b,c=[];for(b in a)f(a,b)&&c.push(b);return c};var ld,md,nd={},od={},pd=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,qd=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,rd={},sd={},td=/\d/,ud=/\d\d/,vd=/\d{3}/,wd=/\d{4}/,xd=/[+-]?\d{6}/,yd=/\d\d?/,zd=/\d\d\d\d?/,Ad=/\d\d\d\d\d\d?/,Bd=/\d{1,3}/,Cd=/\d{1,4}/,Dd=/[+-]?\d{1,6}/,Ed=/\d+/,Fd=/[+-]?\d+/,Gd=/Z|[+-]\d\d:?\d\d/gi,Hd=/Z|[+-]\d\d(?::?\d\d)?/gi,Id=/[+-]?\d+(\.\d{1,3})?/,Jd=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,Kd={},Ld={},Md=0,Nd=1,Od=2,Pd=3,Qd=4,Rd=5,Sd=6,Td=7,Ud=8;md=Array.prototype.indexOf?Array.prototype.indexOf:function(a){var b;for(b=0;b=a?""+a:"+"+a}),R(0,["YY",2],0,function(){return this.year()%100}),R(0,["YYYY",4],0,"year"),R(0,["YYYYY",5],0,"year"),R(0,["YYYYYY",6,!0],0,"year"),J("year","y"),W("Y",Fd),W("YY",yd,ud),W("YYYY",Cd,wd),W("YYYYY",Dd,xd),W("YYYYYY",Dd,xd),$(["YYYYY","YYYYYY"],Md),$("YYYY",function(b,c){c[Md]=2===b.length?a.parseTwoDigitYear(b):r(b)}),$("YY",function(b,c){c[Md]=a.parseTwoDigitYear(b)}),$("Y",function(a,b){b[Md]=parseInt(a,10)}),a.parseTwoDigitYear=function(a){return r(a)+(r(a)>68?1900:2e3)};var ee=M("FullYear",!0);a.ISO_8601=function(){};var fe=u("moment().min is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(){var a=Ka.apply(null,arguments);return this.isValid()&&a.isValid()?this>a?this:a:l()}),ge=u("moment().max is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(){var a=Ka.apply(null,arguments);return this.isValid()&&a.isValid()?a>this?this:a:l()}),he=function(){return Date.now?Date.now():+new Date};Qa("Z",":"),Qa("ZZ",""),W("Z",Hd),W("ZZ",Hd),$(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=Ra(Hd,a)});var ie=/([\+\-]|\d\d)/gi;a.updateOffset=function(){};var je=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?\d*)?$/,ke=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;db.fn=Oa.prototype;var le=ib(1,"add"),me=ib(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var ne=u("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)});R(0,["gg",2],0,function(){return this.weekYear()%100}),R(0,["GG",2],0,function(){return this.isoWeekYear()%100}),Pb("gggg","weekYear"),Pb("ggggg","weekYear"),Pb("GGGG","isoWeekYear"),Pb("GGGGG","isoWeekYear"),J("weekYear","gg"),J("isoWeekYear","GG"),W("G",Fd),W("g",Fd),W("GG",yd,ud),W("gg",yd,ud),W("GGGG",Cd,wd),W("gggg",Cd,wd),W("GGGGG",Dd,xd),W("ggggg",Dd,xd),_(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=r(a)}),_(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}),R("Q",0,"Qo","quarter"),J("quarter","Q"),W("Q",td),$("Q",function(a,b){b[Nd]=3*(r(a)-1)}),R("w",["ww",2],"wo","week"),R("W",["WW",2],"Wo","isoWeek"),J("week","w"),J("isoWeek","W"),W("w",yd),W("ww",yd,ud),W("W",yd),W("WW",yd,ud),_(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=r(a)});var oe={dow:0,doy:6};R("D",["DD",2],"Do","date"),J("date","D"),W("D",yd),W("DD",yd,ud),W("Do",function(a,b){return a?b._ordinalParse:b._ordinalParseLenient}),$(["D","DD"],Od),$("Do",function(a,b){b[Od]=r(a.match(yd)[0],10)});var pe=M("Date",!0);R("d",0,"do","day"),R("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),R("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),R("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),R("e",0,0,"weekday"),R("E",0,0,"isoWeekday"),J("day","d"),J("weekday","e"),J("isoWeekday","E"),W("d",yd),W("e",yd),W("E",yd),W("dd",function(a,b){return b.weekdaysMinRegex(a)}),W("ddd",function(a,b){return b.weekdaysShortRegex(a)}),W("dddd",function(a,b){return b.weekdaysRegex(a)}),_(["dd","ddd","dddd"],function(a,b,c,d){var e=c._locale.weekdaysParse(a,d,c._strict);null!=e?b.d=e:j(c).invalidWeekday=a}),_(["d","e","E"],function(a,b,c,d){b[d]=r(a)});var qe="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),re="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),se="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),te=Jd,ue=Jd,ve=Jd;R("DDD",["DDDD",3],"DDDo","dayOfYear"),J("dayOfYear","DDD"),W("DDD",Bd),W("DDDD",vd),$(["DDD","DDDD"],function(a,b,c){c._dayOfYear=r(a)}),R("H",["HH",2],0,"hour"),R("h",["hh",2],0,oc),R("k",["kk",2],0,pc),R("hmm",0,0,function(){return""+oc.apply(this)+Q(this.minutes(),2)}),R("hmmss",0,0,function(){return""+oc.apply(this)+Q(this.minutes(),2)+Q(this.seconds(),2)}),R("Hmm",0,0,function(){return""+this.hours()+Q(this.minutes(),2)}),R("Hmmss",0,0,function(){return""+this.hours()+Q(this.minutes(),2)+Q(this.seconds(),2)}),qc("a",!0),qc("A",!1),J("hour","h"),W("a",rc),W("A",rc),W("H",yd),W("h",yd),W("HH",yd,ud),W("hh",yd,ud),W("hmm",zd),W("hmmss",Ad),W("Hmm",zd),W("Hmmss",Ad),$(["H","HH"],Pd),$(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),$(["h","hh"],function(a,b,c){b[Pd]=r(a),j(c).bigHour=!0}),$("hmm",function(a,b,c){var d=a.length-2;b[Pd]=r(a.substr(0,d)),b[Qd]=r(a.substr(d)),j(c).bigHour=!0}),$("hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[Pd]=r(a.substr(0,d)),b[Qd]=r(a.substr(d,2)),b[Rd]=r(a.substr(e)),j(c).bigHour=!0}),$("Hmm",function(a,b,c){var d=a.length-2;b[Pd]=r(a.substr(0,d)),b[Qd]=r(a.substr(d))}),$("Hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[Pd]=r(a.substr(0,d)),b[Qd]=r(a.substr(d,2)),b[Rd]=r(a.substr(e))});var we=/[ap]\.?m?\.?/i,xe=M("Hours",!0);R("m",["mm",2],0,"minute"),J("minute","m"),W("m",yd),W("mm",yd,ud),$(["m","mm"],Qd);var ye=M("Minutes",!1);R("s",["ss",2],0,"second"),J("second","s"),W("s",yd),W("ss",yd,ud),$(["s","ss"],Rd);var ze=M("Seconds",!1);R("S",0,0,function(){return~~(this.millisecond()/100)}),R(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),R(0,["SSS",3],0,"millisecond"),R(0,["SSSS",4],0,function(){return 10*this.millisecond()}),R(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),R(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),R(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),R(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),R(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),J("millisecond","ms"),W("S",Bd,td),W("SS",Bd,ud),W("SSS",Bd,vd);var Ae;for(Ae="SSSS";Ae.length<=9;Ae+="S")W(Ae,Ed);for(Ae="S";Ae.length<=9;Ae+="S")$(Ae,uc);var Be=M("Milliseconds",!1);R("z",0,0,"zoneAbbr"),R("zz",0,0,"zoneName");var Ce=o.prototype;Ce.add=le,Ce.calendar=kb,Ce.clone=lb,Ce.diff=sb,Ce.endOf=Eb,Ce.format=wb,Ce.from=xb,Ce.fromNow=yb,Ce.to=zb,Ce.toNow=Ab,Ce.get=P,Ce.invalidAt=Nb,Ce.isAfter=mb,Ce.isBefore=nb,Ce.isBetween=ob,Ce.isSame=pb,Ce.isSameOrAfter=qb,Ce.isSameOrBefore=rb,Ce.isValid=Lb,Ce.lang=ne,Ce.locale=Bb,Ce.localeData=Cb,Ce.max=ge,Ce.min=fe,Ce.parsingFlags=Mb,Ce.set=P,Ce.startOf=Db,Ce.subtract=me,Ce.toArray=Ib,Ce.toObject=Jb,Ce.toDate=Hb,Ce.toISOString=vb,Ce.toJSON=Kb,Ce.toString=ub,Ce.unix=Gb,Ce.valueOf=Fb,Ce.creationData=Ob,Ce.year=ee,Ce.isLeapYear=ta,Ce.weekYear=Qb,Ce.isoWeekYear=Rb,Ce.quarter=Ce.quarters=Wb,Ce.month=ha,Ce.daysInMonth=ia,Ce.week=Ce.weeks=$b,Ce.isoWeek=Ce.isoWeeks=_b,Ce.weeksInYear=Tb,Ce.isoWeeksInYear=Sb,Ce.date=pe,Ce.day=Ce.days=gc,Ce.weekday=hc,Ce.isoWeekday=ic,Ce.dayOfYear=nc,Ce.hour=Ce.hours=xe,Ce.minute=Ce.minutes=ye,Ce.second=Ce.seconds=ze,Ce.millisecond=Ce.milliseconds=Be,Ce.utcOffset=Ua,Ce.utc=Wa,Ce.local=Xa,Ce.parseZone=Ya,Ce.hasAlignedHourOffset=Za,Ce.isDST=$a,Ce.isDSTShifted=_a,Ce.isLocal=ab,Ce.isUtcOffset=bb,Ce.isUtc=cb,Ce.isUTC=cb,Ce.zoneAbbr=vc,Ce.zoneName=wc,Ce.dates=u("dates accessor is deprecated. Use date instead.",pe),Ce.months=u("months accessor is deprecated. Use month instead",ha),Ce.years=u("years accessor is deprecated. Use year instead",ee),Ce.zone=u("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",Va);var De=Ce,Ee={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},Fe={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},Ge="Invalid date",He="%d",Ie=/\d{1,2}/,Je={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"},Ke=A.prototype;Ke._calendar=Ee,Ke.calendar=zc,Ke._longDateFormat=Fe,Ke.longDateFormat=Ac,Ke._invalidDate=Ge,Ke.invalidDate=Bc,Ke._ordinal=He,Ke.ordinal=Cc,Ke._ordinalParse=Ie,Ke.preparse=Dc,Ke.postformat=Dc,Ke._relativeTime=Je,Ke.relativeTime=Ec,Ke.pastFuture=Fc,Ke.set=y,Ke.months=ca,Ke._months=Wd,Ke.monthsShort=da,Ke._monthsShort=Xd,Ke.monthsParse=fa,Ke._monthsRegex=Zd,Ke.monthsRegex=ka,Ke._monthsShortRegex=Yd,Ke.monthsShortRegex=ja,Ke.week=Xb,Ke._week=oe,Ke.firstDayOfYear=Zb,Ke.firstDayOfWeek=Yb,Ke.weekdays=bc,Ke._weekdays=qe,Ke.weekdaysMin=dc,Ke._weekdaysMin=se,Ke.weekdaysShort=cc,Ke._weekdaysShort=re,Ke.weekdaysParse=fc,Ke._weekdaysRegex=te,Ke.weekdaysRegex=jc,Ke._weekdaysShortRegex=ue,Ke.weekdaysShortRegex=kc,Ke._weekdaysMinRegex=ve,Ke.weekdaysMinRegex=lc,Ke.isPM=sc,Ke._meridiemParse=we,Ke.meridiem=tc,E("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===r(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),a.lang=u("moment.lang is deprecated. Use moment.locale instead.",E),a.langData=u("moment.langData is deprecated. Use moment.localeData instead.",H);var Le=Math.abs,Me=Yc("ms"),Ne=Yc("s"),Oe=Yc("m"),Pe=Yc("h"),Qe=Yc("d"),Re=Yc("w"),Se=Yc("M"),Te=Yc("y"),Ue=$c("milliseconds"),Ve=$c("seconds"),We=$c("minutes"),Xe=$c("hours"),Ye=$c("days"),Ze=$c("months"),$e=$c("years"),_e=Math.round,af={s:45,m:45,h:22,d:26,M:11},bf=Math.abs,cf=Oa.prototype;cf.abs=Oc,cf.add=Qc,cf.subtract=Rc,cf.as=Wc,cf.asMilliseconds=Me,cf.asSeconds=Ne,cf.asMinutes=Oe,cf.asHours=Pe,cf.asDays=Qe,cf.asWeeks=Re,cf.asMonths=Se,cf.asYears=Te,cf.valueOf=Xc,cf._bubble=Tc,cf.get=Zc,cf.milliseconds=Ue,cf.seconds=Ve,cf.minutes=We,cf.hours=Xe,cf.days=Ye,cf.weeks=_c,cf.months=Ze,cf.years=$e,cf.humanize=dd,cf.toISOString=ed,cf.toString=ed,cf.toJSON=ed,cf.locale=Bb,cf.localeData=Cb,cf.toIsoString=u("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",ed),cf.lang=ne,R("X",0,0,"unix"),R("x",0,0,"valueOf"),W("x",Fd),W("X",Id),$("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),$("x",function(a,b,c){c._d=new Date(r(a))}),a.version="2.13.0",b(Ka),a.fn=De,a.min=Ma,a.max=Na,a.now=he,a.utc=h,a.unix=xc,a.months=Jc,a.isDate=d,a.locale=E,a.invalid=l,a.duration=db,a.isMoment=p,a.weekdays=Lc,a.parseZone=yc,a.localeData=H,a.isDuration=Pa,a.monthsShort=Kc,a.weekdaysMin=Nc,a.defineLocale=F,a.updateLocale=G,a.locales=I,a.weekdaysShort=Mc,a.normalizeUnits=K,a.relativeTimeThreshold=cd,a.prototype=De;var df=a;return df});
\ No newline at end of file