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