From a8689ae26beaee6fb6e3ff2f16d5289b0bf9a2a0 Mon Sep 17 00:00:00 2001 From: GarcaMan Date: Wed, 24 Nov 2021 19:41:07 +0000 Subject: [PATCH 001/268] first commit --- cps/static/css/kthoom.css | 14 ++ cps/static/js/kthoom.js | 408 +++++++++++++++++++++++-------------- cps/templates/readcbr.html | 21 +- 3 files changed, 283 insertions(+), 160 deletions(-) diff --git a/cps/static/css/kthoom.css b/cps/static/css/kthoom.css index 9565cd30..2c733e52 100644 --- a/cps/static/css/kthoom.css +++ b/cps/static/css/kthoom.css @@ -149,6 +149,20 @@ body { word-wrap: break-word; } +#mainContent > canvas { + display: block; + margin-left: auto; + margin-right: auto; +} + +.long-strip > .mainImage { + margin-bottom: 4px; +} + +.long-strip > .mainImage:last-child { + margin-bottom: 0px !important; +} + #titlebar { min-height: 25px; height: auto; diff --git a/cps/static/js/kthoom.js b/cps/static/js/kthoom.js index 268fe9ec..92a34d5b 100644 --- a/cps/static/js/kthoom.js +++ b/cps/static/js/kthoom.js @@ -62,6 +62,7 @@ var currentImage = 0; var imageFiles = []; var imageFilenames = []; var totalImages = 0; +var prevScrollPosition = 0; var settings = { hflip: false, @@ -70,8 +71,8 @@ var settings = { fitMode: kthoom.Key.B, theme: "light", direction: 0, // 0 = Left to Right, 1 = Right to Left - nextPage: 0, // 0 = Reset to Top, 1 = Remember Position - scrollbar: 1 // 0 = Hide Scrollbar, 1 = Show Scrollbar + scrollbar: 1, // 0 = Hide Scrollbar, 1 = Show Scrollbar + pageDisplay: 0 // 0 = Single Page, 1 = Long Strip }; kthoom.saveSettings = function() { @@ -178,8 +179,7 @@ function initProgressClick() { }); } -function loadFromArrayBuffer(ab) { - var lastCompletion = 0; +function loadFromArrayBuffer(ab, _callback) { const collator = new Intl.Collator('en', { numeric: true, sensitivity: 'base' }); loadArchiveFormats(['rar', 'zip', 'tar'], function() { // Open the file as an archive @@ -208,9 +208,13 @@ function loadFromArrayBuffer(ab) { "" + "" ); + + drawCanvas(); + setImage(test.dataURI, null); + // display first page if we haven't yet if (imageFiles.length === currentImage + 1) { - updatePage(lastCompletion); + updatePage(); } } else { totalImages--; @@ -225,13 +229,6 @@ function loadFromArrayBuffer(ab) { } function scrollTocToActive() { - // Scroll to the thumbnail in the TOC on page change - $("#tocView").stop().animate({ - scrollTop: $("#tocView a.active").position().top - }, 200); -} - -function updatePage() { $(".page").text((currentImage + 1 ) + "/" + totalImages); // Mark the current page in the TOC @@ -243,22 +240,40 @@ function updatePage() { // Set it to active .addClass("active"); + // Scroll to the thumbnail in the TOC on page change + $("#tocView").stop().animate({ + scrollTop: $("#tocView a.active").position().top + }, 200); +} + +function updatePage() { scrollTocToActive(); + scrollCurrentImageIntoView(); updateProgress(); - - if (imageFiles[currentImage]) { - setImage(imageFiles[currentImage].dataURI); - } else { - setImage("loading"); - } - - $("body").toggleClass("dark-theme", settings.theme === "dark"); - $("#mainContent").toggleClass("disabled-scrollbar", settings.scrollbar === 0); + pageDisplayUpdate(); + setTheme(); kthoom.setSettings(); kthoom.saveSettings(); } +function setTheme() { + $("body").toggleClass("dark-theme", settings.theme === "dark"); + $("#mainContent").toggleClass("disabled-scrollbar", settings.scrollbar === 0); +} + +function pageDisplayUpdate() { + if(settings.pageDisplay === 0) { + $(".mainImage").addClass("hide"); + $(".mainImage").eq(currentImage).removeClass("hide"); + $("#mainContent").removeClass("long-strip"); + } else { + $(".mainImage").removeClass("hide"); + $("#mainContent").addClass("long-strip"); + scrollCurrentImageIntoView(); + } +} + function updateProgress(loadPercentage) { if (settings.direction === 0) { $("#progress .bar-read") @@ -290,100 +305,92 @@ function updateProgress(loadPercentage) { $("#progress .bar-read").css({ width: totalImages === 0 ? 0 : Math.round((currentImage + 1) / totalImages * 100) + "%"}); } -function setImage(url) { - var canvas = $("#mainImage")[0]; - var x = $("#mainImage")[0].getContext("2d"); +function setImage(url, _canvas) { + var canvas = _canvas || $(".mainImage").slice(-1)[0]; // Select the last item on the array if _canvas is null + var x = canvas.getContext("2d"); + $("#mainText").hide(); - if (url === "loading") { - updateScale(true); - canvas.width = innerWidth - 100; - canvas.height = 200; + if (url === "error") { x.fillStyle = "black"; x.textAlign = "center"; x.font = "24px sans-serif"; - x.strokeStyle = "black"; - x.fillText("Loading Page #" + (currentImage + 1), innerWidth / 2, 100); + x.strokeStyle = (settings.theme === "dark") ? "white" : "black"; + x.fillText("Unable to decompress image #" + (currentImage + 1), innerWidth / 2, 100); + + $(".mainImage").slice(-1).addClass("error"); } else { - if (url === "error") { - updateScale(true); - canvas.width = innerWidth - 100; - canvas.height = 200; - x.fillStyle = "black"; - x.textAlign = "center"; - x.font = "24px sans-serif"; - x.strokeStyle = "black"; - x.fillText("Unable to decompress image #" + (currentImage + 1), innerWidth / 2, 100); - } else { - if ($("body").css("scrollHeight") / innerHeight > 1) { - $("body").css("overflowY", "scroll"); - } - - var img = new Image(); - img.onerror = function() { - canvas.width = innerWidth - 100; - canvas.height = 300; - updateScale(true); - x.fillStyle = "black"; - x.font = "50px sans-serif"; - x.strokeStyle = "black"; - x.fillText("Page #" + (currentImage + 1) + " (" + - imageFiles[currentImage].filename + ")", innerWidth / 2, 100); - x.fillStyle = "black"; - x.fillText("Is corrupt or not an image", innerWidth / 2, 200); - - var xhr = new XMLHttpRequest(); - if (/(html|htm)$/.test(imageFiles[currentImage].filename)) { - xhr.open("GET", url, true); - xhr.onload = function() { - $("#mainText").css("display", ""); - $("#mainText").innerHTML(""); - }; - xhr.send(null); - } else if (!/(jpg|jpeg|png|gif|webp)$/.test(imageFiles[currentImage].filename) && imageFiles[currentImage].data.uncompressedSize < 10 * 1024) { - xhr.open("GET", url, true); - xhr.onload = function() { - $("#mainText").css("display", ""); - $("#mainText").innerText(xhr.responseText); - }; - xhr.send(null); - } - }; - img.onload = function() { - var h = img.height, - w = img.width, - sw = w, - sh = h; - settings.rotateTimes = (4 + settings.rotateTimes) % 4; - x.save(); - if (settings.rotateTimes % 2 === 1) { - sh = w; - sw = h; - } - canvas.height = sh; - canvas.width = sw; - x.translate(sw / 2, sh / 2); - x.rotate(Math.PI / 2 * settings.rotateTimes); - x.translate(-w / 2, -h / 2); - if (settings.vflip) { - x.scale(1, -1); - x.translate(0, -h); - } - if (settings.hflip) { - x.scale(-1, 1); - x.translate(-w, 0); - } - canvas.style.display = "none"; - scrollTo(0, 0); - x.drawImage(img, 0, 0); - - updateScale(false); - - canvas.style.display = ""; - $("body").css("overflowY", ""); - x.restore(); - }; - img.src = url; + if ($("body").css("scrollHeight") / innerHeight > 1) { + $("body").css("overflowY", "scroll"); } + + var img = new Image(); + img.onerror = function() { + canvas.width = innerWidth - 100; + canvas.height = 300; + x.fillStyle = "black"; + x.font = "50px sans-serif"; + x.strokeStyle = "black"; + x.fillText("Page #" + (currentImage + 1) + " (" + + imageFiles[currentImage].filename + ")", innerWidth / 2, 100); + x.fillStyle = "black"; + x.fillText("Is corrupt or not an image", innerWidth / 2, 200); + + var xhr = new XMLHttpRequest(); + if (/(html|htm)$/.test(imageFiles[currentImage].filename)) { + xhr.open("GET", url, true); + xhr.onload = function() { + $("#mainText").css("display", ""); + $("#mainText").innerHTML(""); + }; + xhr.send(null); + } else if (!/(jpg|jpeg|png|gif|webp)$/.test(imageFiles[currentImage].filename) && imageFiles[currentImage].data.uncompressedSize < 10 * 1024) { + xhr.open("GET", url, true); + xhr.onload = function() { + $("#mainText").css("display", ""); + $("#mainText").innerText(xhr.responseText); + }; + xhr.send(null); + } + }; + img.onload = function() { + var h = img.height, + w = img.width, + sw = w, + sh = h; + settings.rotateTimes = (4 + settings.rotateTimes) % 4; + x.save(); + if (settings.rotateTimes % 2 === 1) { + sh = w; + sw = h; + } + canvas.height = sh; + canvas.width = sw; + x.translate(sw / 2, sh / 2); + x.rotate(Math.PI / 2 * settings.rotateTimes); + x.translate(-w / 2, -h / 2); + if (settings.vflip) { + x.scale(1, -1); + x.translate(0, -h); + } + if (settings.hflip) { + x.scale(-1, 1); + x.translate(-w, 0); + } + canvas.style.display = "none"; + scrollTo(0, 0); + x.drawImage(img, 0, 0); + + canvas.style.display = ""; + $("body").css("overflowY", ""); + x.restore(); + }; + img.src = url; + } +} + +function reloadImages() { + for(i=0; imageFiles.length; i++) { + setImage(imageFiles[i].dataURI, $(".mainImage")[i]); } } @@ -410,9 +417,6 @@ function showPrevPage() { currentImage++; } else { updatePage(); - if (settings.nextPage === 0) { - $("#mainContent").scrollTop(0); - } } } @@ -423,36 +427,53 @@ function showNextPage() { currentImage--; } else { updatePage(); - if (settings.nextPage === 0) { - $("#mainContent").scrollTop(0); - } } } -function updateScale(clear) { - var mainImageStyle = getElem("mainImage").style; - mainImageStyle.width = ""; - mainImageStyle.height = ""; - mainImageStyle.maxWidth = ""; - mainImageStyle.maxHeight = ""; - var maxheight = innerHeight - 50; - - if (!clear) { - switch (settings.fitMode) { - case kthoom.Key.B: - mainImageStyle.maxWidth = "100%"; - mainImageStyle.maxHeight = maxheight + "px"; - break; - case kthoom.Key.H: - mainImageStyle.height = maxheight + "px"; - break; - case kthoom.Key.W: - mainImageStyle.width = "100%"; - break; - default: - break; - } +function scrollCurrentImageIntoView() { + if(settings.pageDisplay == 0) { + // This will scroll all the way up when Single Page is selected + $("#mainContent").scrollTop(0); + } else { + // This will scroll to the image when Long Strip is selected + $("#mainContent").stop().animate({ + scrollTop: $(".mainImage").eq(currentImage).offset().top + $("#mainContent").scrollTop() - $("#mainContent").offset().top + }, 200); } +} + +function updateScale() { + var canvasArray = $("#mainContent > canvas"); + var maxheight = innerHeight - 50; + + canvasArray.css("width", ""); + canvasArray.css("height", ""); + canvasArray.css("maxWidth", ""); + canvasArray.css("maxHeight", ""); + + if(settings.pageDisplay === 0) { + canvasArray.addClass("hide"); + pageDisplayUpdate(); + } + + switch (settings.fitMode) { + case kthoom.Key.B: + canvasArray.css("maxWidth", "100%"); + canvasArray.css("maxHeight", maxheight + "px"); + break; + case kthoom.Key.H: + canvasArray.css("maxHeight", maxheight + "px"); + break; + case kthoom.Key.W: + canvasArray.css("width", "100%"); + break; + default: + break; + } + + $("#mainContent > canvas.error").css("width", innerWidth - 100); + $("#mainContent > canvas.error").css("height", 200); + $("#mainContent").css({maxHeight: maxheight + 5}); kthoom.setSettings(); kthoom.saveSettings(); @@ -469,6 +490,20 @@ function keyHandler(evt) { if (hasModifier) break; showRightPage(); break; + case kthoom.Key.S: + if (hasModifier) break; + settings.pageDisplay = 0; + pageDisplayUpdate(); + kthoom.setSettings(); + kthoom.saveSettings(); + break; + case kthoom.Key.O: + if (hasModifier) break; + settings.pageDisplay = 1; + pageDisplayUpdate(); + kthoom.setSettings(); + kthoom.saveSettings(); + break; case kthoom.Key.L: if (hasModifier) break; settings.rotateTimes--; @@ -503,22 +538,22 @@ function keyHandler(evt) { case kthoom.Key.W: if (hasModifier) break; settings.fitMode = kthoom.Key.W; - updateScale(false); + updateScale(); break; case kthoom.Key.H: if (hasModifier) break; settings.fitMode = kthoom.Key.H; - updateScale(false); + updateScale(); break; case kthoom.Key.B: if (hasModifier) break; settings.fitMode = kthoom.Key.B; - updateScale(false); + updateScale(); break; case kthoom.Key.N: if (hasModifier) break; settings.fitMode = kthoom.Key.N; - updateScale(false); + updateScale(); break; case kthoom.Key.SPACE: if (evt.shiftKey) { @@ -537,6 +572,43 @@ function keyHandler(evt) { } } +function drawCanvas() { + var maxheight = innerHeight - 50; + var canvasElement = $(""); + var x = canvasElement[0].getContext("2d"); + canvasElement.addClass("mainImage"); + + switch (settings.fitMode) { + case kthoom.Key.B: + canvasElement.css("maxWidth", "100%"); + canvasElement.css("maxHeight", maxheight + "px"); + break; + case kthoom.Key.H: + canvasElement.css("maxHeight", maxheight + "px"); + break; + case kthoom.Key.W: + canvasElement.css("width", "100%"); + break; + default: + break; + } + + if(settings.pageDisplay === 0) { + canvasElement.addClass("hide"); + } + + //Fill with Placeholder text. setImage will override this + canvasElement.width = innerWidth - 100; + canvasElement.height = 200; + x.fillStyle = "black"; + x.textAlign = "center"; + x.font = "24px sans-serif"; + x.strokeStyle = (settings.theme === "dark") ? "white" : "black"; + x.fillText("Loading Page #" + (currentImage + 1), innerWidth / 2, 100); + + $("#mainContent").append(canvasElement); +} + function init(filename) { var request = new XMLHttpRequest(); request.open("GET", filename); @@ -548,16 +620,17 @@ function init(filename) { console.warn(request.statusText, request.responseText); } }); + kthoom.loadSettings(); + setTheme(); + updateScale(); request.send(); initProgressClick(); document.body.className += /AppleWebKit/.test(navigator.userAgent) ? " webkit" : ""; - kthoom.loadSettings(); - updateScale(true); $(document).keydown(keyHandler); $(window).resize(function() { - updateScale(false); + updateScale(); }); // Open TOC menu @@ -588,8 +661,14 @@ function init(filename) { value = /^\d+$/.test(value) ? parseInt(value) : value; settings[this.name] = value; + + if(["hflip", "vflip", "rotateTimes"].includes(this.name)) { + //reloadImages is a slow process when multiple images are involved. Only used when rotating/mirroring + reloadImages(); + } + updatePage(); - updateScale(false); + updateScale(); }); // Close modal @@ -601,9 +680,6 @@ function init(filename) { $("#thumbnails").on("click", "a", function() { currentImage = $(this).data("page") - 1; updatePage(); - if (settings.nextPage === 0) { - $("#mainContent").scrollTop(0); - } }); // Fullscreen mode @@ -633,7 +709,7 @@ function init(filename) { showRightPage(); }, }); - $("#mainImage").click(function(evt) { + $(".mainImage").click(function(evt) { // Firefox does not support offsetX/Y so we have to manually calculate // where the user clicked in the image. var mainContentWidth = $("#mainContent").width(); @@ -668,5 +744,37 @@ function init(filename) { showRightPage(); } }); + + //Scrolling up/down will update current image if a new image is into view (for Long Strip Display) + $("#mainContent").scroll(function(){ + var scroll = $("#mainContent").scrollTop(); + if(settings.pageDisplay === 0) { + // Don't trigger the scroll for Single Page + } else if(scroll > prevScrollPosition) { + //Scroll Down + if(currentImage + 1 < imageFiles.length) { + if(currentImageOffset(currentImage + 1) <= 1) { + currentImage++; + scrollTocToActive(); + updateProgress(); + } + } + } else { + //Scroll Up + if(currentImage - 1 > -1 ) { + if(currentImageOffset(currentImage - 1) >= 0) { + currentImage--; + scrollTocToActive(); + updateProgress(); + } + } + } + + // Update scroll position + prevScrollPosition = scroll; + }); } +function currentImageOffset(imageIndex) { + return $(".mainImage").eq(imageIndex).offset().top - $("#mainContent").position().top +} diff --git a/cps/templates/readcbr.html b/cps/templates/readcbr.html index 411e3fdd..df2e53dd 100644 --- a/cps/templates/readcbr.html +++ b/cps/templates/readcbr.html @@ -73,7 +73,6 @@
-
@@ -91,6 +90,8 @@ ← {{_('Previous Page')}} → {{_('Next Page')}} + S {{_('Single Page Display')}} + O {{_('Long Strip Display')}} B {{_('Scale to Best')}} W {{_('Scale to Width')}} H {{_('Scale to Height')}} @@ -118,6 +119,15 @@ + + {{_('Display')}}: + +
+ + +
+ + {{_('Scale')}}: @@ -157,15 +167,6 @@ - - - {{_('Next Page')}}: - -
- - -
- {{_('Scrollbar')}}: From 7fc04b353bdff5236d79037768e2d676afc9501f Mon Sep 17 00:00:00 2001 From: GarcaMan Date: Wed, 24 Nov 2021 20:22:10 +0000 Subject: [PATCH 002/268] Selecting Position will not scroll the current image up --- cps/static/js/kthoom.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cps/static/js/kthoom.js b/cps/static/js/kthoom.js index 92a34d5b..cfc9cf2d 100644 --- a/cps/static/js/kthoom.js +++ b/cps/static/js/kthoom.js @@ -665,6 +665,9 @@ function init(filename) { if(["hflip", "vflip", "rotateTimes"].includes(this.name)) { //reloadImages is a slow process when multiple images are involved. Only used when rotating/mirroring reloadImages(); + } else if(this.name === "direction") { + // Skips updatePage and updateScale so that the current image doesn't scroll up + return updateProgress(); } updatePage(); From 3f56f0dca75bb6b059289913cab61de740b4cc2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Rodr=C3=ADguez?= <68790233+GarckaMan@users.noreply.github.com> Date: Thu, 25 Nov 2021 01:30:20 -0300 Subject: [PATCH 003/268] Removed parameter that was wrongly added --- cps/static/js/kthoom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/static/js/kthoom.js b/cps/static/js/kthoom.js index cfc9cf2d..6e309c86 100644 --- a/cps/static/js/kthoom.js +++ b/cps/static/js/kthoom.js @@ -179,7 +179,7 @@ function initProgressClick() { }); } -function loadFromArrayBuffer(ab, _callback) { +function loadFromArrayBuffer(ab) { const collator = new Intl.Collator('en', { numeric: true, sensitivity: 'base' }); loadArchiveFormats(['rar', 'zip', 'tar'], function() { // Open the file as an archive From 3ac08a8c0df1f4a70d6b3ca600a9a88fe06ae092 Mon Sep 17 00:00:00 2001 From: GarcaMan Date: Thu, 2 Dec 2021 18:20:14 +0000 Subject: [PATCH 004/268] After toggling fullscreen, focus on main container --- cps/static/js/kthoom.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cps/static/js/kthoom.js b/cps/static/js/kthoom.js index cfc9cf2d..b637fc93 100644 --- a/cps/static/js/kthoom.js +++ b/cps/static/js/kthoom.js @@ -666,7 +666,6 @@ function init(filename) { //reloadImages is a slow process when multiple images are involved. Only used when rotating/mirroring reloadImages(); } else if(this.name === "direction") { - // Skips updatePage and updateScale so that the current image doesn't scroll up return updateProgress(); } @@ -689,6 +688,8 @@ function init(filename) { if (typeof screenfull !== "undefined") { $("#fullscreen").click(function() { screenfull.toggle($("#container")[0]); + // Focus so you can use up/down keys immediately after fullscreen + $("#mainContent").focus(); }); if (screenfull.raw) { From 4eaa9413f9f2ad610dc1335f1f08ff3344cb74f4 Mon Sep 17 00:00:00 2001 From: Evan Peterson <77evan@gmail.com> Date: Mon, 10 Jan 2022 15:15:19 -0500 Subject: [PATCH 005/268] Kobo metadata return correct layout format for fixed layout --- cps/epub.py | 25 +++++++++++++++++++++++++ cps/kobo.py | 3 +++ 2 files changed, 28 insertions(+) diff --git a/cps/epub.py b/cps/epub.py index cbbdcbbd..aae6120b 100644 --- a/cps/epub.py +++ b/cps/epub.py @@ -21,6 +21,7 @@ import zipfile from lxml import etree from . import isoLanguages +from . import config from .helper import split_authors from .constants import BookMeta @@ -39,6 +40,30 @@ def extractCover(zipFile, coverFile, coverpath, tmp_file_name): image.close() return tmp_cover_name +def get_epub_layout(book, book_data): + ns = { + 'n': 'urn:oasis:names:tc:opendocument:xmlns:container', + 'pkg': 'http://www.idpf.org/2007/opf', + } + + file_path = os.path.normpath(os.path.join(config.config_calibre_dir, book.path, book_data.name + "." + book_data.format.lower())) + + epubZip = zipfile.ZipFile(file_path) + + txt = epubZip.read('META-INF/container.xml') + tree = etree.fromstring(txt) + cfname = tree.xpath('n:rootfiles/n:rootfile/@full-path', namespaces=ns)[0] + cf = epubZip.read(cfname) + tree = etree.fromstring(cf) + p = tree.xpath('/pkg:package/pkg:metadata', namespaces=ns)[0] + + layout = p.xpath('pkg:meta[@property="rendition:layout"]/text()', namespaces=ns) + + if len(layout) == 0: + return None + else: + return layout[0] + def get_epub_info(tmp_file_path, original_file_name, original_file_extension): ns = { diff --git a/cps/kobo.py b/cps/kobo.py index ef9a9476..0412ae17 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -49,6 +49,7 @@ import requests from . import config, logger, kobo_auth, db, calibre_db, helper, shelf as shelf_lib, ub, csrf, kobo_sync_status +from .epub import get_epub_layout from .constants import sqlalchemy_version2 from .helper import get_download_link from .services import SyncToken as SyncToken @@ -455,6 +456,8 @@ def get_metadata(book): continue for kobo_format in KOBO_FORMATS[book_data.format]: # log.debug('Id: %s, Format: %s' % (book.id, kobo_format)) + if get_epub_layout(book, book_data) == 'pre-paginated': + kobo_format = 'EPUB3FL' download_urls.append( { "Format": kobo_format, From 0f3f918153fb1c2bf9e3c89bf1af9aeaba5677d8 Mon Sep 17 00:00:00 2001 From: Thore Schillmann Date: Sun, 22 May 2022 17:40:21 +0000 Subject: [PATCH 006/268] multiple authors and publication date in opds feed --- cps/templates/feed.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cps/templates/feed.xml b/cps/templates/feed.xml index 940fb0da..c1e9f43d 100644 --- a/cps/templates/feed.xml +++ b/cps/templates/feed.xml @@ -43,16 +43,17 @@ {{entry.Books.title}} urn:uuid:{{entry.Books.uuid}} {{entry.Books.atom_timestamp}} - {% if entry.Books.authors.__len__() > 0 %} + {% for author in entry.Books.authors %} - {{entry.Books.authors[0].name}} + {{author.name}} - {% endif %} + {% endfor %} {% if entry.Books.publishers.__len__() > 0 %} {{entry.Books.publishers[0].name}} {% endif %} + {{entry.Books.pubdate}} {% for lang in entry.Books.languages %} {{lang.lang_code}} {% endfor %} From bf12542df5cb36e2b266f66f92b31f1b609217a2 Mon Sep 17 00:00:00 2001 From: GarcaMan Date: Sun, 5 Jun 2022 19:56:34 +0000 Subject: [PATCH 007/268] Updated Rotate Left/Right shortcut funtions to update inmediatly Minor fixes --- cps/static/js/kthoom.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cps/static/js/kthoom.js b/cps/static/js/kthoom.js index 1a3bcb89..7a9de8f4 100644 --- a/cps/static/js/kthoom.js +++ b/cps/static/js/kthoom.js @@ -388,8 +388,9 @@ function setImage(url, _canvas) { } } +// reloadImages is a slow process when multiple images are involved. Only used when rotating/mirroring function reloadImages() { - for(i=0; imageFiles.length; i++) { + for(i=0; i < imageFiles.length; i++) { setImage(imageFiles[i].dataURI, $(".mainImage")[i]); } } @@ -511,6 +512,7 @@ function keyHandler(evt) { settings.rotateTimes = 3; } updatePage(); + reloadImages(); break; case kthoom.Key.R: if (hasModifier) break; @@ -519,6 +521,7 @@ function keyHandler(evt) { settings.rotateTimes = 0; } updatePage(); + reloadImages(); break; case kthoom.Key.F: if (hasModifier) break; @@ -534,6 +537,7 @@ function keyHandler(evt) { settings.hflip = true; } updatePage(); + reloadImages(); break; case kthoom.Key.W: if (hasModifier) break; @@ -663,8 +667,7 @@ function init(filename) { settings[this.name] = value; if(["hflip", "vflip", "rotateTimes"].includes(this.name)) { - //reloadImages is a slow process when multiple images are involved. Only used when rotating/mirroring - reloadImages(); + reloadImages(); } else if(this.name === "direction") { return updateProgress(); } @@ -676,6 +679,7 @@ function init(filename) { // Close modal $(".closer, .overlay").click(function() { $(".md-show").removeClass("md-show"); + $("#mainContent").focus(); // focus back on the main container so you use up/down keys without having to click on it }); // TOC thumbnail pagination @@ -688,7 +692,7 @@ function init(filename) { if (typeof screenfull !== "undefined") { $("#fullscreen").click(function() { screenfull.toggle($("#container")[0]); - // Focus so you can use up/down keys immediately after fullscreen + // Focus on main container so you can use up/down keys immediately after fullscreen $("#mainContent").focus(); }); @@ -749,7 +753,7 @@ function init(filename) { } }); - //Scrolling up/down will update current image if a new image is into view (for Long Strip Display) + // Scrolling up/down will update current image if a new image is into view (for Long Strip Display) $("#mainContent").scroll(function(){ var scroll = $("#mainContent").scrollTop(); if(settings.pageDisplay === 0) { From 2816a75c3ec58f8c322f7426a287d64947e607e1 Mon Sep 17 00:00:00 2001 From: Thore Schillmann Date: Thu, 9 Jun 2022 20:35:44 +0000 Subject: [PATCH 008/268] changed datetime format of published tag --- cps/templates/feed.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/templates/feed.xml b/cps/templates/feed.xml index c1e9f43d..d2114157 100644 --- a/cps/templates/feed.xml +++ b/cps/templates/feed.xml @@ -53,7 +53,7 @@ {{entry.Books.publishers[0].name}} {% endif %} - {{entry.Books.pubdate}} + {{entry.Books.pubdate.strftime("%Y-%m-%dT%H:%M:%S+00:00")}} {% for lang in entry.Books.languages %} {{lang.lang_code}} {% endfor %} From 73d48e4ac119e0e735dc0f83369734bd8c8f6460 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Thu, 16 Jun 2022 08:33:39 +0200 Subject: [PATCH 009/268] Frontend for password strength --- cps/__init__.py | 8 +- cps/admin.py | 11 + cps/config_sql.py | 9 +- cps/static/css/style.css | 4 + cps/static/js/libs/pwstrength/i18next.min.js | 1 + .../libs/pwstrength/i18nextHttpBackend.min.js | 1 + cps/static/js/libs/pwstrength/locales/ar.json | 17 + cps/static/js/libs/pwstrength/locales/cs.json | 17 + cps/static/js/libs/pwstrength/locales/de.json | 21 + cps/static/js/libs/pwstrength/locales/el.json | 17 + cps/static/js/libs/pwstrength/locales/en.json | 21 + cps/static/js/libs/pwstrength/locales/eo.json | 17 + cps/static/js/libs/pwstrength/locales/es.json | 17 + cps/static/js/libs/pwstrength/locales/fr.json | 17 + cps/static/js/libs/pwstrength/locales/it.json | 17 + cps/static/js/libs/pwstrength/locales/no.json | 17 + cps/static/js/libs/pwstrength/locales/pl.json | 17 + cps/static/js/libs/pwstrength/locales/pt.json | 17 + cps/static/js/libs/pwstrength/locales/ru.json | 17 + cps/static/js/libs/pwstrength/locales/sk.json | 17 + cps/static/js/libs/pwstrength/locales/th.json | 17 + cps/static/js/libs/pwstrength/locales/tr.json | 17 + .../js/libs/pwstrength/locales/zh-TW.json | 17 + .../libs/pwstrength/pwstrength-bootstrap.js | 1227 +++++++++++++++++ .../pwstrength/pwstrength-bootstrap.min.js | 4 + cps/static/js/main.js | 12 +- cps/static/js/password.js | 58 + cps/templates/config_edit.html | 47 + cps/templates/locales/en/translation.json | 10 + cps/templates/user_edit.html | 6 +- 30 files changed, 1683 insertions(+), 12 deletions(-) create mode 100644 cps/static/js/libs/pwstrength/i18next.min.js create mode 100644 cps/static/js/libs/pwstrength/i18nextHttpBackend.min.js create mode 100644 cps/static/js/libs/pwstrength/locales/ar.json create mode 100644 cps/static/js/libs/pwstrength/locales/cs.json create mode 100644 cps/static/js/libs/pwstrength/locales/de.json create mode 100644 cps/static/js/libs/pwstrength/locales/el.json create mode 100644 cps/static/js/libs/pwstrength/locales/en.json create mode 100644 cps/static/js/libs/pwstrength/locales/eo.json create mode 100644 cps/static/js/libs/pwstrength/locales/es.json create mode 100644 cps/static/js/libs/pwstrength/locales/fr.json create mode 100644 cps/static/js/libs/pwstrength/locales/it.json create mode 100644 cps/static/js/libs/pwstrength/locales/no.json create mode 100644 cps/static/js/libs/pwstrength/locales/pl.json create mode 100644 cps/static/js/libs/pwstrength/locales/pt.json create mode 100644 cps/static/js/libs/pwstrength/locales/ru.json create mode 100644 cps/static/js/libs/pwstrength/locales/sk.json create mode 100644 cps/static/js/libs/pwstrength/locales/th.json create mode 100644 cps/static/js/libs/pwstrength/locales/tr.json create mode 100644 cps/static/js/libs/pwstrength/locales/zh-TW.json create mode 100644 cps/static/js/libs/pwstrength/pwstrength-bootstrap.js create mode 100644 cps/static/js/libs/pwstrength/pwstrength-bootstrap.min.js create mode 100644 cps/static/js/password.js create mode 100644 cps/templates/locales/en/translation.json diff --git a/cps/__init__.py b/cps/__init__.py index 1ba1f778..9b957090 100644 --- a/cps/__init__.py +++ b/cps/__init__.py @@ -98,10 +98,6 @@ updater_thread = Updater() def create_app(): - lm.login_view = 'web.login' - lm.anonymous_user = ub.Anonymous - lm.session_protection = 'strong' - if csrf: csrf.init_app(app) @@ -112,6 +108,10 @@ def create_app(): # pylint: disable=no-member config_sql.load_configuration(config, ub.session, cli_param) + lm.login_view = 'web.login' + lm.anonymous_user = ub.Anonymous + lm.session_protection = 'strong' if config.config_session == 1 else "basic" + db.CalibreDB.update_config(config) db.CalibreDB.setup_db(config.config_calibre_dir, cli_param.settings_path) calibre_db.init_db() diff --git a/cps/admin.py b/cps/admin.py index f222be02..c2b9e500 100755 --- a/cps/admin.py +++ b/cps/admin.py @@ -1765,10 +1765,21 @@ def _configuration_update_helper(): if config.config_login_type == constants.LOGIN_OAUTH: reboot_required |= _configuration_oauth_helper(to_save) + # logfile configuration reboot, message = _configuration_logfile_helper(to_save) if message: return message reboot_required |= reboot + + # security configuration + _config_checkbox(to_save, "config_password_policy") + _config_checkbox(to_save, "config_password_number") + _config_checkbox(to_save, "config_password_lower") + _config_checkbox(to_save, "config_password_upper") + _config_checkbox(to_save, "config_password_special") + _config_int(to_save, "config_password_min_length") + _config_int(to_save, "config_session") + # Rarfile Content configuration _config_string(to_save, "config_rarfile_location") if "config_rarfile_location" in to_save: diff --git a/cps/config_sql.py b/cps/config_sql.py index bba3d650..19184202 100644 --- a/cps/config_sql.py +++ b/cps/config_sql.py @@ -75,7 +75,6 @@ class _Settings(_Base): config_authors_max = Column(Integer, default=0) config_read_column = Column(Integer, default=0) config_title_regex = Column(String, default=r'^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines)\s+') - # config_mature_content_tags = Column(String, default='') config_theme = Column(Integer, default=0) config_log_level = Column(SmallInteger, default=logger.DEFAULT_LOG_LEVEL) @@ -148,6 +147,14 @@ class _Settings(_Base): schedule_generate_series_covers = Column(Boolean, default=False) schedule_reconnect = Column(Boolean, default=False) + config_password_policy = Column(Boolean, default=True) + config_password_min_length = Column(Integer, default=8) + config_password_number = Column(Boolean, default=True) + config_password_lower = Column(Boolean, default=True) + config_password_upper = Column(Boolean, default=True) + config_password_special = Column(Boolean, default=True) + config_session = Column(Integer, default=1) + def __repr__(self): return self.__class__.__name__ diff --git a/cps/static/css/style.css b/cps/static/css/style.css index 6e6b0eae..b44f04ad 100644 --- a/cps/static/css/style.css +++ b/cps/static/css/style.css @@ -433,3 +433,7 @@ div.log { #detailcover:-moz-full-screen { cursor:zoom-out; border: 0; } #detailcover:-ms-fullscreen { cursor:zoom-out; border: 0; } #detailcover:fullscreen { cursor:zoom-out; border: 0; } + +.error-list { + margin-top: 5px; + } diff --git a/cps/static/js/libs/pwstrength/i18next.min.js b/cps/static/js/libs/pwstrength/i18next.min.js new file mode 100644 index 00000000..0b1c1222 --- /dev/null +++ b/cps/static/js/libs/pwstrength/i18next.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).i18next=t()}(this,function(){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{};t(this,e),this.init(n,r)}return r(e,[{key:"init",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.prefix=t.prefix||"i18next:",this.logger=e||p,this.options=t,this.debug=t.debug}},{key:"setDebug",value:function(e){this.debug=e}},{key:"log",value:function(){for(var e=arguments.length,t=new Array(e),n=0;n1?t-1:0),r=1;r-1?e.replace(/###/g,"."):e}function o(){return!e||"string"==typeof e}for(var i="string"!=typeof t?[].concat(t):t.split(".");i.length>1;){if(o())return{};var a=r(i.shift());!e[a]&&n&&(e[a]=new n),e=Object.prototype.hasOwnProperty.call(e,a)?e[a]:{}}return o()?{}:{obj:e,k:r(i.shift())}}function m(e,t,n){var r=y(e,t,Object);r.obj[r.k]=n}function b(e,t){var n=y(e,t),r=n.obj,o=n.k;if(r)return r[o]}function O(e,t,n){var r=b(e,n);return void 0!==r?r:b(t,n)}function k(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}var w={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};function x(e){return"string"==typeof e?e.replace(/[&<>"'\/]/g,function(e){return w[e]}):e}var S="undefined"!=typeof window&&window.navigator&&void 0===window.navigator.userAgentData&&window.navigator.userAgent&&window.navigator.userAgent.indexOf("MSIE")>-1,j=[" ",",","?","!",";"];function P(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function L(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{ns:["translation"],defaultNS:"translation"};return t(this,i),r=n.call(this),S&&h.call(o(r)),r.data=e||{},r.options=a,void 0===r.options.keySeparator&&(r.options.keySeparator="."),void 0===r.options.ignoreJSONStructure&&(r.options.ignoreJSONStructure=!0),r}return r(i,[{key:"addNamespaces",value:function(e){this.options.ns.indexOf(e)<0&&this.options.ns.push(e)}},{key:"removeNamespaces",value:function(e){var t=this.options.ns.indexOf(e);t>-1&&this.options.ns.splice(t,1)}},{key:"getResource",value:function(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=void 0!==r.keySeparator?r.keySeparator:this.options.keySeparator,i=void 0!==r.ignoreJSONStructure?r.ignoreJSONStructure:this.options.ignoreJSONStructure,a=[e,t];n&&"string"!=typeof n&&(a=a.concat(n)),n&&"string"==typeof n&&(a=a.concat(o?n.split(o):n)),e.indexOf(".")>-1&&(a=e.split("."));var s=b(this.data,a);return s||!i||"string"!=typeof n?s:function e(t,n){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:".";if(t){if(t[n])return t[n];for(var o=n.split(r),i=t,a=0;aa+s;)s++,c=i[u=o.slice(a,a+s).join(r)];if(void 0===c)return;if(null===c)return null;if(n.endsWith(u)){if("string"==typeof c)return c;if(u&&"string"==typeof c[u])return c[u]}var l=o.slice(a+s).join(r);return l?e(c,l,r):void 0}i=i[o[a]]}return i}}(this.data&&this.data[e]&&this.data[e][t],n,o)}},{key:"addResource",value:function(e,t,n,r){var o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{silent:!1},i=this.options.keySeparator;void 0===i&&(i=".");var a=[e,t];n&&(a=a.concat(i?n.split(i):n)),e.indexOf(".")>-1&&(r=t,t=(a=e.split("."))[1]),this.addNamespaces(t),m(this.data,a,r),o.silent||this.emit("added",e,t,n,r)}},{key:"addResources",value:function(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{silent:!1};for(var o in n)"string"!=typeof n[o]&&"[object Array]"!==Object.prototype.toString.apply(n[o])||this.addResource(e,t,o,n[o],{silent:!0});r.silent||this.emit("added",e,t,n)}},{key:"addResourceBundle",value:function(e,t,n,r,o){var i=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{silent:!1},a=[e,t];e.indexOf(".")>-1&&(r=n,n=t,t=(a=e.split("."))[1]),this.addNamespaces(t);var s=b(this.data,a)||{};r?function e(t,n,r){for(var o in n)"__proto__"!==o&&"constructor"!==o&&(o in t?"string"==typeof t[o]||t[o]instanceof String||"string"==typeof n[o]||n[o]instanceof String?r&&(t[o]=n[o]):e(t[o],n[o],r):t[o]=n[o]);return t}(s,n,o):s=L(L({},s),n),m(this.data,a,s),i.silent||this.emit("added",e,t,n)}},{key:"removeResourceBundle",value:function(e,t){this.hasResourceBundle(e,t)&&delete this.data[e][t],this.removeNamespaces(t),this.emit("removed",e,t)}},{key:"hasResourceBundle",value:function(e,t){return void 0!==this.getResource(e,t)}},{key:"getResourceBundle",value:function(e,t){return t||(t=this.options.defaultNS),"v1"===this.options.compatibilityAPI?L(L({},{}),this.getResource(e,t)):this.getResource(e,t)}},{key:"getDataByLanguage",value:function(e){return this.data[e]}},{key:"hasLanguageSomeTranslations",value:function(e){var t=this.getDataByLanguage(e);return!!(t&&Object.keys(t)||[]).find(function(e){return t[e]&&Object.keys(t[e]).length>0})}},{key:"toJSON",value:function(){return this.data}}]),i}(),C={processors:{},addPostProcessor:function(e){this.processors[e.name]=e},handle:function(e,t,n,r,o){var i=this;return e.forEach(function(e){i.processors[e]&&(t=i.processors[e].process(t,n,r,o))}),t}};function E(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function D(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{};return t(this,s),n=i.call(this),S&&h.call(o(n)),r=["resourceStore","languageUtils","pluralResolver","interpolator","backendConnector","i18nFormat","utils"],a=e,u=o(n),r.forEach(function(e){a[e]&&(u[e]=a[e])}),n.options=c,void 0===n.options.keySeparator&&(n.options.keySeparator="."),n.logger=g.create("translator"),n}return r(s,[{key:"changeLanguage",value:function(e){e&&(this.language=e)}},{key:"exists",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{interpolation:{}};if(null==e)return!1;var n=this.resolve(e,t);return n&&void 0!==n.res}},{key:"extractFromKey",value:function(e,t){var n=void 0!==t.nsSeparator?t.nsSeparator:this.options.nsSeparator;void 0===n&&(n=":");var r=void 0!==t.keySeparator?t.keySeparator:this.options.keySeparator,o=t.ns||this.options.defaultNS||[],i=n&&e.indexOf(n)>-1,a=!(this.options.userDefinedKeySeparator||t.keySeparator||this.options.userDefinedNsSeparator||t.nsSeparator||function(e,t,n){t=t||"",n=n||"";var r=j.filter(function(e){return t.indexOf(e)<0&&n.indexOf(e)<0});if(0===r.length)return!0;var o=new RegExp("(".concat(r.map(function(e){return"?"===e?"\\?":e}).join("|"),")")),i=!o.test(e);if(!i){var a=e.indexOf(n);a>0&&!o.test(e.substring(0,a))&&(i=!0)}return i}(e,n,r));if(i&&!a){var s=e.match(this.interpolator.nestingRegexp);if(s&&s.length>0)return{key:e,namespaces:o};var u=e.split(n);(n!==r||n===r&&this.options.ns.indexOf(u[0])>-1)&&(o=u.shift()),e=u.join(r)}return"string"==typeof o&&(o=[o]),{key:e,namespaces:o}}},{key:"translate",value:function(t,n,r){var o=this;if("object"!==e(n)&&this.options.overloadTranslationOptionHandler&&(n=this.options.overloadTranslationOptionHandler(arguments)),n||(n={}),null==t)return"";Array.isArray(t)||(t=[String(t)]);var i=void 0!==n.returnDetails?n.returnDetails:this.options.returnDetails,a=void 0!==n.keySeparator?n.keySeparator:this.options.keySeparator,u=this.extractFromKey(t[t.length-1],n),c=u.key,l=u.namespaces,f=l[l.length-1],p=n.lng||this.language,g=n.appendNamespaceToCIMode||this.options.appendNamespaceToCIMode;if(p&&"cimode"===p.toLowerCase()){if(g){var h=n.nsSeparator||this.options.nsSeparator;return i?(d.res="".concat(f).concat(h).concat(c),d):"".concat(f).concat(h).concat(c)}return i?(d.res=c,d):c}var d=this.resolve(t,n),v=d&&d.res,y=d&&d.usedKey||c,m=d&&d.exactUsedKey||c,b=Object.prototype.toString.apply(v),O=void 0!==n.joinArrays?n.joinArrays:this.options.joinArrays,k=!this.i18nFormat||this.i18nFormat.handleAsObject;if(k&&v&&("string"!=typeof v&&"boolean"!=typeof v&&"number"!=typeof v)&&["[object Number]","[object Function]","[object RegExp]"].indexOf(b)<0&&("string"!=typeof O||"[object Array]"!==b)){if(!n.returnObjects&&!this.options.returnObjects){this.options.returnedObjectHandler||this.logger.warn("accessing an object - but returnObjects options is not enabled!");var w=this.options.returnedObjectHandler?this.options.returnedObjectHandler(y,v,D(D({},n),{},{ns:l})):"key '".concat(c," (").concat(this.language,")' returned an object instead of string.");return i?(d.res=w,d):w}if(a){var x="[object Array]"===b,S=x?[]:{},j=x?m:y;for(var P in v)if(Object.prototype.hasOwnProperty.call(v,P)){var L="".concat(j).concat(a).concat(P);S[P]=this.translate(L,D(D({},n),{joinArrays:!1,ns:l})),S[P]===L&&(S[P]=v[P])}v=S}}else if(k&&"string"==typeof O&&"[object Array]"===b)(v=v.join(O))&&(v=this.extendTranslation(v,t,n,r));else{var R=!1,N=!1,C=void 0!==n.count&&"string"!=typeof n.count,E=s.hasDefaultValue(n),F=C?this.pluralResolver.getSuffix(p,n.count,n):"",I=n["defaultValue".concat(F)]||n.defaultValue;!this.isValidLookup(v)&&E&&(R=!0,v=I),this.isValidLookup(v)||(N=!0,v=c);var A=(n.missingKeyNoValueFallbackToKey||this.options.missingKeyNoValueFallbackToKey)&&N?void 0:v,T=E&&I!==v&&this.options.updateMissing;if(N||R||T){if(this.logger.log(T?"updateKey":"missingKey",p,f,c,T?I:v),a){var V=this.resolve(c,D(D({},n),{},{keySeparator:!1}));V&&V.res&&this.logger.warn("Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format.")}var U=[],K=this.languageUtils.getFallbackCodes(this.options.fallbackLng,n.lng||this.language);if("fallback"===this.options.saveMissingTo&&K&&K[0])for(var B=0;B1&&void 0!==arguments[1]?arguments[1]:{};return"string"==typeof e&&(e=[e]),e.forEach(function(e){if(!a.isValidLookup(t)){var u=a.extractFromKey(e,s),c=u.key;n=c;var l=u.namespaces;a.options.fallbackNS&&(l=l.concat(a.options.fallbackNS));var f=void 0!==s.count&&"string"!=typeof s.count,p=f&&!s.ordinal&&0===s.count&&a.pluralResolver.shouldUseIntlApi(),g=void 0!==s.context&&("string"==typeof s.context||"number"==typeof s.context)&&""!==s.context,h=s.lngs?s.lngs:a.languageUtils.toResolveHierarchy(s.lng||a.language,s.fallbackLng);l.forEach(function(e){a.isValidLookup(t)||(i=e,!I["".concat(h[0],"-").concat(e)]&&a.utils&&a.utils.hasLoadedNamespace&&!a.utils.hasLoadedNamespace(i)&&(I["".concat(h[0],"-").concat(e)]=!0,a.logger.warn('key "'.concat(n,'" for languages "').concat(h.join(", "),'" won\'t get resolved as namespace "').concat(i,'" was not yet loaded'),"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!")),h.forEach(function(n){if(!a.isValidLookup(t)){o=n;var i,u=[c];if(a.i18nFormat&&a.i18nFormat.addLookupKeys)a.i18nFormat.addLookupKeys(u,c,n,e,s);else{var l;f&&(l=a.pluralResolver.getSuffix(n,s.count,s));if(f&&(u.push(c+l),p&&u.push(c+"_zero")),g){var h="".concat(c).concat(a.options.contextSeparator).concat(s.context);u.push(h),f&&(u.push(h+l),p&&u.push(h+"_zero"))}}for(;i=u.pop();)a.isValidLookup(t)||(r=i,t=a.getResource(n,e,i,s))}}))})}}),{res:t,usedKey:n,exactUsedKey:r,usedLng:o,usedNS:i}}},{key:"isValidLookup",value:function(e){return!(void 0===e||!this.options.returnNull&&null===e||!this.options.returnEmptyString&&""===e)}},{key:"getResource",value:function(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};return this.i18nFormat&&this.i18nFormat.getResource?this.i18nFormat.getResource(e,t,n,r):this.resourceStore.getResource(e,t,n,r)}}],[{key:"hasDefaultValue",value:function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t)&&"defaultValue"===t.substring(0,"defaultValue".length)&&void 0!==e[t])return!0;return!1}}]),s}();function T(e){return e.charAt(0).toUpperCase()+e.slice(1)}var V=function(){function e(n){t(this,e),this.options=n,this.supportedLngs=this.options.supportedLngs||!1,this.logger=g.create("languageUtils")}return r(e,[{key:"getScriptPartFromCode",value:function(e){if(!e||e.indexOf("-")<0)return null;var t=e.split("-");return 2===t.length?null:(t.pop(),"x"===t[t.length-1].toLowerCase()?null:this.formatLanguageCode(t.join("-")))}},{key:"getLanguagePartFromCode",value:function(e){if(!e||e.indexOf("-")<0)return e;var t=e.split("-");return this.formatLanguageCode(t[0])}},{key:"formatLanguageCode",value:function(e){if("string"==typeof e&&e.indexOf("-")>-1){var t=["hans","hant","latn","cyrl","cans","mong","arab"],n=e.split("-");return this.options.lowerCaseLng?n=n.map(function(e){return e.toLowerCase()}):2===n.length?(n[0]=n[0].toLowerCase(),n[1]=n[1].toUpperCase(),t.indexOf(n[1].toLowerCase())>-1&&(n[1]=T(n[1].toLowerCase()))):3===n.length&&(n[0]=n[0].toLowerCase(),2===n[1].length&&(n[1]=n[1].toUpperCase()),"sgn"!==n[0]&&2===n[2].length&&(n[2]=n[2].toUpperCase()),t.indexOf(n[1].toLowerCase())>-1&&(n[1]=T(n[1].toLowerCase())),t.indexOf(n[2].toLowerCase())>-1&&(n[2]=T(n[2].toLowerCase()))),n.join("-")}return this.options.cleanCode||this.options.lowerCaseLng?e.toLowerCase():e}},{key:"isSupportedCode",value:function(e){return("languageOnly"===this.options.load||this.options.nonExplicitSupportedLngs)&&(e=this.getLanguagePartFromCode(e)),!this.supportedLngs||!this.supportedLngs.length||this.supportedLngs.indexOf(e)>-1}},{key:"getBestMatchFromCodes",value:function(e){var t,n=this;return e?(e.forEach(function(e){if(!t){var r=n.formatLanguageCode(e);n.options.supportedLngs&&!n.isSupportedCode(r)||(t=r)}}),!t&&this.options.supportedLngs&&e.forEach(function(e){if(!t){var r=n.getLanguagePartFromCode(e);if(n.isSupportedCode(r))return t=r;t=n.options.supportedLngs.find(function(e){if(0===e.indexOf(r))return e})}}),t||(t=this.getFallbackCodes(this.options.fallbackLng)[0]),t):null}},{key:"getFallbackCodes",value:function(e,t){if(!e)return[];if("function"==typeof e&&(e=e(t)),"string"==typeof e&&(e=[e]),"[object Array]"===Object.prototype.toString.apply(e))return e;if(!t)return e.default||[];var n=e[t];return n||(n=e[this.getScriptPartFromCode(t)]),n||(n=e[this.formatLanguageCode(t)]),n||(n=e[this.getLanguagePartFromCode(t)]),n||(n=e.default),n||[]}},{key:"toResolveHierarchy",value:function(e,t){var n=this,r=this.getFallbackCodes(t||this.options.fallbackLng||[],e),o=[],i=function(e){e&&(n.isSupportedCode(e)?o.push(e):n.logger.warn("rejecting language code not found in supportedLngs: ".concat(e)))};return"string"==typeof e&&e.indexOf("-")>-1?("languageOnly"!==this.options.load&&i(this.formatLanguageCode(e)),"languageOnly"!==this.options.load&&"currentOnly"!==this.options.load&&i(this.getScriptPartFromCode(e)),"currentOnly"!==this.options.load&&i(this.getLanguagePartFromCode(e))):"string"==typeof e&&i(this.formatLanguageCode(e)),r.forEach(function(e){o.indexOf(e)<0&&i(n.formatLanguageCode(e))}),o}}]),e}(),U=[{lngs:["ach","ak","am","arn","br","fil","gun","ln","mfe","mg","mi","oc","pt","pt-BR","tg","tl","ti","tr","uz","wa"],nr:[1,2],fc:1},{lngs:["af","an","ast","az","bg","bn","ca","da","de","dev","el","en","eo","es","et","eu","fi","fo","fur","fy","gl","gu","ha","hi","hu","hy","ia","it","kk","kn","ku","lb","mai","ml","mn","mr","nah","nap","nb","ne","nl","nn","no","nso","pa","pap","pms","ps","pt-PT","rm","sco","se","si","so","son","sq","sv","sw","ta","te","tk","ur","yo"],nr:[1,2],fc:2},{lngs:["ay","bo","cgg","fa","ht","id","ja","jbo","ka","km","ko","ky","lo","ms","sah","su","th","tt","ug","vi","wo","zh"],nr:[1],fc:3},{lngs:["be","bs","cnr","dz","hr","ru","sr","uk"],nr:[1,2,5],fc:4},{lngs:["ar"],nr:[0,1,2,3,11,100],fc:5},{lngs:["cs","sk"],nr:[1,2,5],fc:6},{lngs:["csb","pl"],nr:[1,2,5],fc:7},{lngs:["cy"],nr:[1,2,3,8],fc:8},{lngs:["fr"],nr:[1,2],fc:9},{lngs:["ga"],nr:[1,2,3,7,11],fc:10},{lngs:["gd"],nr:[1,2,3,20],fc:11},{lngs:["is"],nr:[1,2],fc:12},{lngs:["jv"],nr:[0,1],fc:13},{lngs:["kw"],nr:[1,2,3,4],fc:14},{lngs:["lt"],nr:[1,2,10],fc:15},{lngs:["lv"],nr:[1,2,0],fc:16},{lngs:["mk"],nr:[1,2],fc:17},{lngs:["mnk"],nr:[0,1,2],fc:18},{lngs:["mt"],nr:[1,2,11,20],fc:19},{lngs:["or"],nr:[2,1],fc:2},{lngs:["ro"],nr:[1,2,20],fc:20},{lngs:["sl"],nr:[5,1,2,3],fc:21},{lngs:["he","iw"],nr:[1,2,20,21],fc:22}],K={1:function(e){return Number(e>1)},2:function(e){return Number(1!=e)},3:function(e){return 0},4:function(e){return Number(e%10==1&&e%100!=11?0:e%10>=2&&e%10<=4&&(e%100<10||e%100>=20)?1:2)},5:function(e){return Number(0==e?0:1==e?1:2==e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5)},6:function(e){return Number(1==e?0:e>=2&&e<=4?1:2)},7:function(e){return Number(1==e?0:e%10>=2&&e%10<=4&&(e%100<10||e%100>=20)?1:2)},8:function(e){return Number(1==e?0:2==e?1:8!=e&&11!=e?2:3)},9:function(e){return Number(e>=2)},10:function(e){return Number(1==e?0:2==e?1:e<7?2:e<11?3:4)},11:function(e){return Number(1==e||11==e?0:2==e||12==e?1:e>2&&e<20?2:3)},12:function(e){return Number(e%10!=1||e%100==11)},13:function(e){return Number(0!==e)},14:function(e){return Number(1==e?0:2==e?1:3==e?2:3)},15:function(e){return Number(e%10==1&&e%100!=11?0:e%10>=2&&(e%100<10||e%100>=20)?1:2)},16:function(e){return Number(e%10==1&&e%100!=11?0:0!==e?1:2)},17:function(e){return Number(1==e||e%10==1&&e%100!=11?0:1)},18:function(e){return Number(0==e?0:1==e?1:2)},19:function(e){return Number(1==e?0:0==e||e%100>1&&e%100<11?1:e%100>10&&e%100<20?2:3)},20:function(e){return Number(1==e?0:0==e||e%100>0&&e%100<20?1:2)},21:function(e){return Number(e%100==1?1:e%100==2?2:e%100==3||e%100==4?3:0)},22:function(e){return Number(1==e?0:2==e?1:(e<0||e>10)&&e%10==0?2:3)}},B=["v1","v2","v3"],M={zero:0,one:1,two:2,few:3,many:4,other:5};var H=function(){function e(n){var r,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};t(this,e),this.languageUtils=n,this.options=o,this.logger=g.create("pluralResolver"),this.options.compatibilityJSON&&"v4"!==this.options.compatibilityJSON||"undefined"!=typeof Intl&&Intl.PluralRules||(this.options.compatibilityJSON="v3",this.logger.error("Your environment seems not to be Intl API compatible, use an Intl.PluralRules polyfill. Will fallback to the compatibilityJSON v3 format handling.")),this.rules=(r={},U.forEach(function(e){e.lngs.forEach(function(t){r[t]={numbers:e.nr,plurals:K[e.fc]}})}),r)}return r(e,[{key:"addRule",value:function(e,t){this.rules[e]=t}},{key:"getRule",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this.shouldUseIntlApi())try{return new Intl.PluralRules(e,{type:t.ordinal?"ordinal":"cardinal"})}catch(e){return}return this.rules[e]||this.rules[this.languageUtils.getLanguagePartFromCode(e)]}},{key:"needsPlural",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=this.getRule(e,t);return this.shouldUseIntlApi()?n&&n.resolvedOptions().pluralCategories.length>1:n&&n.numbers.length>1}},{key:"getPluralFormsOfKey",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return this.getSuffixes(e,n).map(function(e){return"".concat(t).concat(e)})}},{key:"getSuffixes",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=this.getRule(e,n);return r?this.shouldUseIntlApi()?r.resolvedOptions().pluralCategories.sort(function(e,t){return M[e]-M[t]}).map(function(e){return"".concat(t.options.prepend).concat(e)}):r.numbers.map(function(r){return t.getSuffix(e,r,n)}):[]}},{key:"getSuffix",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=this.getRule(e,n);return r?this.shouldUseIntlApi()?"".concat(this.options.prepend).concat(r.select(t)):this.getSuffixRetroCompatible(r,t):(this.logger.warn("no plural rule found for: ".concat(e)),"")}},{key:"getSuffixRetroCompatible",value:function(e,t){var n=this,r=e.noAbs?e.plurals(t):e.plurals(Math.abs(t)),o=e.numbers[r];this.options.simplifyPluralSuffix&&2===e.numbers.length&&1===e.numbers[0]&&(2===o?o="plural":1===o&&(o=""));var i=function(){return n.options.prepend&&o.toString()?n.options.prepend+o.toString():o.toString()};return"v1"===this.options.compatibilityJSON?1===o?"":"number"==typeof o?"_plural_".concat(o.toString()):i():"v2"===this.options.compatibilityJSON?i():this.options.simplifyPluralSuffix&&2===e.numbers.length&&1===e.numbers[0]?i():this.options.prepend&&r.toString()?this.options.prepend+r.toString():r.toString()}},{key:"shouldUseIntlApi",value:function(){return!B.includes(this.options.compatibilityJSON)}}]),e}();function z(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function J(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{};t(this,e),this.logger=g.create("interpolator"),this.options=n,this.format=n.interpolation&&n.interpolation.format||function(e){return e},this.init(n)}return r(e,[{key:"init",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};e.interpolation||(e.interpolation={escapeValue:!0});var t=e.interpolation;this.escape=void 0!==t.escape?t.escape:x,this.escapeValue=void 0===t.escapeValue||t.escapeValue,this.useRawValueToEscape=void 0!==t.useRawValueToEscape&&t.useRawValueToEscape,this.prefix=t.prefix?k(t.prefix):t.prefixEscaped||"{{",this.suffix=t.suffix?k(t.suffix):t.suffixEscaped||"}}",this.formatSeparator=t.formatSeparator?t.formatSeparator:t.formatSeparator||",",this.unescapePrefix=t.unescapeSuffix?"":t.unescapePrefix||"-",this.unescapeSuffix=this.unescapePrefix?"":t.unescapeSuffix||"",this.nestingPrefix=t.nestingPrefix?k(t.nestingPrefix):t.nestingPrefixEscaped||k("$t("),this.nestingSuffix=t.nestingSuffix?k(t.nestingSuffix):t.nestingSuffixEscaped||k(")"),this.nestingOptionsSeparator=t.nestingOptionsSeparator?t.nestingOptionsSeparator:t.nestingOptionsSeparator||",",this.maxReplaces=t.maxReplaces?t.maxReplaces:1e3,this.alwaysFormat=void 0!==t.alwaysFormat&&t.alwaysFormat,this.resetRegExp()}},{key:"reset",value:function(){this.options&&this.init(this.options)}},{key:"resetRegExp",value:function(){var e="".concat(this.prefix,"(.+?)").concat(this.suffix);this.regexp=new RegExp(e,"g");var t="".concat(this.prefix).concat(this.unescapePrefix,"(.+?)").concat(this.unescapeSuffix).concat(this.suffix);this.regexpUnescape=new RegExp(t,"g");var n="".concat(this.nestingPrefix,"(.+?)").concat(this.nestingSuffix);this.nestingRegexp=new RegExp(n,"g")}},{key:"interpolate",value:function(e,t,n,r){var o,i,a,s=this,u=this.options&&this.options.interpolation&&this.options.interpolation.defaultVariables||{};function c(e){return e.replace(/\$/g,"$$$$")}var l=function(e){if(e.indexOf(s.formatSeparator)<0){var o=O(t,u,e);return s.alwaysFormat?s.format(o,void 0,n,J(J(J({},r),t),{},{interpolationkey:e})):o}var i=e.split(s.formatSeparator),a=i.shift().trim(),c=i.join(s.formatSeparator).trim();return s.format(O(t,u,a),c,n,J(J(J({},r),t),{},{interpolationkey:a}))};this.resetRegExp();var f=r&&r.missingInterpolationHandler||this.options.missingInterpolationHandler,p=r&&r.interpolation&&void 0!==r.interpolation.skipOnVariables?r.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables;return[{regex:this.regexpUnescape,safeValue:function(e){return c(e)}},{regex:this.regexp,safeValue:function(e){return s.escapeValue?c(s.escape(e)):c(e)}}].forEach(function(t){for(a=0;o=t.regex.exec(e);){var n=o[1].trim();if(void 0===(i=l(n)))if("function"==typeof f){var u=f(e,o,r);i="string"==typeof u?u:""}else if(r&&r.hasOwnProperty(n))i="";else{if(p){i=o[0];continue}s.logger.warn("missed to pass in variable ".concat(n," for interpolating ").concat(e)),i=""}else"string"==typeof i||s.useRawValueToEscape||(i=v(i));var c=t.safeValue(i);if(e=e.replace(o[0],c),p?(t.regex.lastIndex+=i.length,t.regex.lastIndex-=o[0].length):t.regex.lastIndex=0,++a>=s.maxReplaces)break}}),e}},{key:"nest",value:function(e,t){var n,r,o=this,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},a=J({},i);function s(e,t){var n=this.nestingOptionsSeparator;if(e.indexOf(n)<0)return e;var r=e.split(new RegExp("".concat(n,"[ ]*{"))),o="{".concat(r[1]);e=r[0],o=(o=this.interpolate(o,a)).replace(/'/g,'"');try{a=JSON.parse(o),t&&(a=J(J({},t),a))}catch(t){return this.logger.warn("failed parsing options string in nesting for key ".concat(e),t),"".concat(e).concat(n).concat(o)}return delete a.defaultValue,e}for(a.applyPostProcessor=!1,delete a.defaultValue;n=this.nestingRegexp.exec(e);){var u=[],c=!1;if(-1!==n[0].indexOf(this.formatSeparator)&&!/{.*}/.test(n[1])){var l=n[1].split(this.formatSeparator).map(function(e){return e.trim()});n[1]=l.shift(),u=l,c=!0}if((r=t(s.call(this,n[1].trim(),a),a))&&n[0]===e&&"string"!=typeof r)return r;"string"!=typeof r&&(r=v(r)),r||(this.logger.warn("missed to resolve ".concat(n[1]," for nesting ").concat(e)),r=""),c&&(r=u.reduce(function(e,t){return o.format(e,t,i.lng,J(J({},i),{},{interpolationkey:n[1].trim()}))},r.trim())),e=e.replace(n[0],r),this.regexp.lastIndex=0}return e}}]),e}();function q(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&void 0!==arguments[0]?arguments[0]:{};t(this,e),this.logger=g.create("formatter"),this.options=n,this.formats={number:function(e,t,n){return new Intl.NumberFormat(t,n).format(e)},currency:function(e,t,n){return new Intl.NumberFormat(t,Y(Y({},n),{},{style:"currency"})).format(e)},datetime:function(e,t,n){return new Intl.DateTimeFormat(t,Y({},n)).format(e)},relativetime:function(e,t,n){return new Intl.RelativeTimeFormat(t,Y({},n)).format(e,n.range||"day")},list:function(e,t,n){return new Intl.ListFormat(t,Y({},n)).format(e)}},this.init(n)}return r(e,[{key:"init",value:function(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{interpolation:{}}).interpolation;this.formatSeparator=t.formatSeparator?t.formatSeparator:t.formatSeparator||","}},{key:"add",value:function(e,t){this.formats[e.toLowerCase().trim()]=t}},{key:"format",value:function(e,t,n,r){var o=this;return t.split(this.formatSeparator).reduce(function(e,t){var i=function(e){var t=e.toLowerCase().trim(),n={};if(e.indexOf("(")>-1){var r=e.split("(");t=r[0].toLowerCase().trim();var o=r[1].substring(0,r[1].length-1);"currency"===t&&o.indexOf(":")<0?n.currency||(n.currency=o.trim()):"relativetime"===t&&o.indexOf(":")<0?n.range||(n.range=o.trim()):o.split(";").forEach(function(e){if(e){var t=$(e.split(":")),r=t[0],o=t.slice(1).join(":").trim().replace(/^'+|'+$/g,"");n[r.trim()]||(n[r.trim()]=o),"false"===o&&(n[r.trim()]=!1),"true"===o&&(n[r.trim()]=!0),isNaN(o)||(n[r.trim()]=parseInt(o,10))}})}return{formatName:t,formatOptions:n}}(t),a=i.formatName,s=i.formatOptions;if(o.formats[a]){var u=e;try{var c=r&&r.formatParams&&r.formatParams[r.interpolationkey]||{},l=c.locale||c.lng||r.locale||r.lng||n;u=o.formats[a](e,l,Y(Y(Y({},s),r),c))}catch(e){o.logger.warn(e)}return u}return o.logger.warn("there was no format function for ".concat(a)),e},e)}}]),e}();function Q(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function X(e){for(var t=1;t3&&void 0!==arguments[3]?arguments[3]:{};return t(this,i),s=n.call(this),S&&h.call(o(s)),s.backend=e,s.store=r,s.services=a,s.languageUtils=a.languageUtils,s.options=u,s.logger=g.create("backendConnector"),s.waitingReads=[],s.maxParallelReads=u.maxParallelReads||10,s.readingCalls=0,s.state={},s.queue=[],s.backend&&s.backend.init&&s.backend.init(a,u.backend,u),s}return r(i,[{key:"queueLoad",value:function(e,t,n,r){var o=this,i={},a={},s={},u={};return e.forEach(function(e){var r=!0;t.forEach(function(t){var s="".concat(e,"|").concat(t);!n.reload&&o.store.hasResourceBundle(e,t)?o.state[s]=2:o.state[s]<0||(1===o.state[s]?void 0===a[s]&&(a[s]=!0):(o.state[s]=1,r=!1,void 0===a[s]&&(a[s]=!0),void 0===i[s]&&(i[s]=!0),void 0===u[t]&&(u[t]=!0)))}),r||(s[e]=!0)}),(Object.keys(i).length||Object.keys(a).length)&&this.queue.push({pending:a,pendingCount:Object.keys(a).length,loaded:{},errors:[],callback:r}),{toLoad:Object.keys(i),pending:Object.keys(a),toLoadLanguages:Object.keys(s),toLoadNamespaces:Object.keys(u)}}},{key:"loaded",value:function(e,t,n){var r=e.split("|"),o=r[0],i=r[1];t&&this.emit("failedLoading",o,i,t),n&&this.store.addResourceBundle(o,i,n),this.state[e]=t?-1:2;var a={};this.queue.forEach(function(n){var r,s,u,c,l,f;r=n.loaded,s=i,c=y(r,[o],Object),l=c.obj,f=c.k,l[f]=l[f]||[],u&&(l[f]=l[f].concat(s)),u||l[f].push(s),function(e,t){void 0!==e.pending[t]&&(delete e.pending[t],e.pendingCount--)}(n,e),t&&n.errors.push(t),0!==n.pendingCount||n.done||(Object.keys(n.loaded).forEach(function(e){a[e]||(a[e]={});var t=n.loaded[e];t.length&&t.forEach(function(t){void 0===a[e][t]&&(a[e][t]=!0)})}),n.done=!0,n.errors.length?n.callback(n.errors):n.callback())}),this.emit("loaded",a),this.queue=this.queue.filter(function(e){return!e.done})}},{key:"read",value:function(e,t,n){var r=this,o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,i=arguments.length>4&&void 0!==arguments[4]?arguments[4]:350,a=arguments.length>5?arguments[5]:void 0;return e.length?this.readingCalls>=this.maxParallelReads?void this.waitingReads.push({lng:e,ns:t,fcName:n,tried:o,wait:i,callback:a}):(this.readingCalls++,this.backend[n](e,t,function(s,u){if(s&&u&&o<5)setTimeout(function(){r.read.call(r,e,t,n,o+1,2*i,a)},i);else{if(r.readingCalls--,r.waitingReads.length>0){var c=r.waitingReads.shift();r.read(c.lng,c.ns,c.fcName,c.tried,c.wait,c.callback)}a(s,u)}})):a(null,{})}},{key:"prepareLoading",value:function(e,t){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=arguments.length>3?arguments[3]:void 0;if(!this.backend)return this.logger.warn("No backend was added via i18next.use. Will not load resources."),o&&o();"string"==typeof e&&(e=this.languageUtils.toResolveHierarchy(e)),"string"==typeof t&&(t=[t]);var i=this.queueLoad(e,t,r,o);if(!i.toLoad.length)return i.pending.length||o(),null;i.toLoad.forEach(function(e){n.loadOne(e)})}},{key:"load",value:function(e,t,n){this.prepareLoading(e,t,{},n)}},{key:"reload",value:function(e,t,n){this.prepareLoading(e,t,{reload:!0},n)}},{key:"loadOne",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",r=e.split("|"),o=r[0],i=r[1];this.read(o,i,"read",void 0,void 0,function(r,a){r&&t.logger.warn("".concat(n,"loading namespace ").concat(i," for language ").concat(o," failed"),r),!r&&a&&t.logger.log("".concat(n,"loaded namespace ").concat(i," for language ").concat(o),a),t.loaded(e,r,a)})}},{key:"saveMissing",value:function(e,t,n,r,o){var i=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{};this.services.utils&&this.services.utils.hasLoadedNamespace&&!this.services.utils.hasLoadedNamespace(t)?this.logger.warn('did not save key "'.concat(n,'" as the namespace "').concat(t,'" was not yet loaded'),"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!"):null!=n&&""!==n&&(this.backend&&this.backend.create&&this.backend.create(e,t,n,r,null,X(X({},i),{},{isUpdate:o})),e&&e[0]&&this.store.addResource(e[0],t,n,r))}}]),i}();function te(e){return"string"==typeof e.ns&&(e.ns=[e.ns]),"string"==typeof e.fallbackLng&&(e.fallbackLng=[e.fallbackLng]),"string"==typeof e.fallbackNS&&(e.fallbackNS=[e.fallbackNS]),e.supportedLngs&&e.supportedLngs.indexOf("cimode")<0&&(e.supportedLngs=e.supportedLngs.concat(["cimode"])),e}function ne(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function re(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},a=arguments.length>1?arguments[1]:void 0;if(t(this,u),e=i.call(this),S&&h.call(o(e)),e.options=te(r),e.services={},e.logger=g,e.modules={external:[]},n=o(e),Object.getOwnPropertyNames(Object.getPrototypeOf(n)).forEach(function(e){"function"==typeof n[e]&&(n[e]=n[e].bind(n))}),a&&!e.isInitialized&&!r.isClone){if(!e.options.initImmediate)return e.init(r,a),s(e,o(e));setTimeout(function(){e.init(r,a)},0)}return e}return r(u,[{key:"init",value:function(){var t=this,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=arguments.length>1?arguments[1]:void 0;"function"==typeof n&&(r=n,n={}),!n.defaultNS&&n.ns&&("string"==typeof n.ns?n.defaultNS=n.ns:n.ns.indexOf("translation")<0&&(n.defaultNS=n.ns[0]));var o={debug:!1,initImmediate:!0,ns:["translation"],defaultNS:["translation"],fallbackLng:["dev"],fallbackNS:!1,supportedLngs:!1,nonExplicitSupportedLngs:!1,load:"all",preload:!1,simplifyPluralSuffix:!0,keySeparator:".",nsSeparator:":",pluralSeparator:"_",contextSeparator:"_",partialBundledLanguages:!1,saveMissing:!1,updateMissing:!1,saveMissingTo:"fallback",saveMissingPlurals:!0,missingKeyHandler:!1,missingInterpolationHandler:!1,postProcess:!1,postProcessPassResolved:!1,returnNull:!0,returnEmptyString:!0,returnObjects:!1,joinArrays:!1,returnedObjectHandler:!1,parseMissingKeyHandler:!1,appendNamespaceToMissingKey:!1,appendNamespaceToCIMode:!1,overloadTranslationOptionHandler:function(t){var n={};if("object"===e(t[1])&&(n=t[1]),"string"==typeof t[1]&&(n.defaultValue=t[1]),"string"==typeof t[2]&&(n.tDescription=t[2]),"object"===e(t[2])||"object"===e(t[3])){var r=t[3]||t[2];Object.keys(r).forEach(function(e){n[e]=r[e]})}return n},interpolation:{escapeValue:!0,format:function(e,t,n,r){return e},prefix:"{{",suffix:"}}",formatSeparator:",",unescapePrefix:"-",nestingPrefix:"$t(",nestingSuffix:")",nestingOptionsSeparator:",",maxReplaces:1e3,skipOnVariables:!0}};function i(e){return e?"function"==typeof e?new e:e:null}if(this.options=re(re(re({},o),this.options),te(n)),"v1"!==this.options.compatibilityAPI&&(this.options.interpolation=re(re({},o.interpolation),this.options.interpolation)),void 0!==n.keySeparator&&(this.options.userDefinedKeySeparator=n.keySeparator),void 0!==n.nsSeparator&&(this.options.userDefinedNsSeparator=n.nsSeparator),!this.options.isClone){var a;this.modules.logger?g.init(i(this.modules.logger),this.options):g.init(null,this.options),this.modules.formatter?a=this.modules.formatter:"undefined"!=typeof Intl&&(a=G);var s=new V(this.options);this.store=new N(this.options.resources,this.options);var u=this.services;u.logger=g,u.resourceStore=this.store,u.languageUtils=s,u.pluralResolver=new H(s,{prepend:this.options.pluralSeparator,compatibilityJSON:this.options.compatibilityJSON,simplifyPluralSuffix:this.options.simplifyPluralSuffix}),!a||this.options.interpolation.format&&this.options.interpolation.format!==o.interpolation.format||(u.formatter=i(a),u.formatter.init(u,this.options),this.options.interpolation.format=u.formatter.format.bind(u.formatter)),u.interpolator=new _(this.options),u.utils={hasLoadedNamespace:this.hasLoadedNamespace.bind(this)},u.backendConnector=new ee(i(this.modules.backend),u.resourceStore,u,this.options),u.backendConnector.on("*",function(e){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o1?n-1:0),o=1;o0&&"dev"!==c[0]&&(this.options.lng=c[0])}this.services.languageDetector||this.options.lng||this.logger.warn("init: no languageDetector is used and no lng is defined");["getResource","hasResourceBundle","getResourceBundle","getDataByLanguage"].forEach(function(e){t[e]=function(){var n;return(n=t.store)[e].apply(n,arguments)}});["addResource","addResources","addResourceBundle","removeResourceBundle"].forEach(function(e){t[e]=function(){var n;return(n=t.store)[e].apply(n,arguments),t}});var l=d(),f=function(){var e=function(e,n){t.isInitialized&&!t.initializedStoreOnce&&t.logger.warn("init: i18next is already initialized. You should call init just once!"),t.isInitialized=!0,t.options.isClone||t.logger.log("initialized",t.options),t.emit("initialized",t.options),l.resolve(n),r(e,n)};if(t.languages&&"v1"!==t.options.compatibilityAPI&&!t.isInitialized)return e(null,t.t.bind(t));t.changeLanguage(t.options.lng,e)};return this.options.resources||!this.options.initImmediate?f():setTimeout(f,0),l}},{key:"loadResources",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:ie,r="string"==typeof e?e:this.language;if("function"==typeof e&&(n=e),!this.options.resources||this.options.partialBundledLanguages){if(r&&"cimode"===r.toLowerCase())return n();var o=[],i=function(e){e&&t.services.languageUtils.toResolveHierarchy(e).forEach(function(e){o.indexOf(e)<0&&o.push(e)})};if(r)i(r);else this.services.languageUtils.getFallbackCodes(this.options.fallbackLng).forEach(function(e){return i(e)});this.options.preload&&this.options.preload.forEach(function(e){return i(e)}),this.services.backendConnector.load(o,this.options.ns,function(e){e||t.resolvedLanguage||!t.language||t.setResolvedLanguage(t.language),n(e)})}else n(null)}},{key:"reloadResources",value:function(e,t,n){var r=d();return e||(e=this.languages),t||(t=this.options.ns),n||(n=ie),this.services.backendConnector.reload(e,t,function(e){r.resolve(),n(e)}),r}},{key:"use",value:function(e){if(!e)throw new Error("You are passing an undefined module! Please check the object you are passing to i18next.use()");if(!e.type)throw new Error("You are passing a wrong module! Please check the object you are passing to i18next.use()");return"backend"===e.type&&(this.modules.backend=e),("logger"===e.type||e.log&&e.warn&&e.error)&&(this.modules.logger=e),"languageDetector"===e.type&&(this.modules.languageDetector=e),"i18nFormat"===e.type&&(this.modules.i18nFormat=e),"postProcessor"===e.type&&C.addPostProcessor(e),"formatter"===e.type&&(this.modules.formatter=e),"3rdParty"===e.type&&this.modules.external.push(e),this}},{key:"setResolvedLanguage",value:function(e){if(e&&this.languages&&!(["cimode","dev"].indexOf(e)>-1))for(var t=0;t-1)&&this.store.hasLanguageSomeTranslations(n)){this.resolvedLanguage=n;break}}}},{key:"changeLanguage",value:function(e,t){var n=this;this.isLanguageChangingTo=e;var r=d();this.emit("languageChanging",e);var o=function(e){n.language=e,n.languages=n.services.languageUtils.toResolveHierarchy(e),n.resolvedLanguage=void 0,n.setResolvedLanguage(e)},i=function(i){e||i||!n.services.languageDetector||(i=[]);var a="string"==typeof i?i:n.services.languageUtils.getBestMatchFromCodes(i);a&&(n.language||o(a),n.translator.language||n.translator.changeLanguage(a),n.services.languageDetector&&n.services.languageDetector.cacheUserLanguage(a)),n.loadResources(a,function(e){!function(e,i){i?(o(i),n.translator.changeLanguage(i),n.isLanguageChangingTo=void 0,n.emit("languageChanged",i),n.logger.log("languageChanged",i)):n.isLanguageChangingTo=void 0,r.resolve(function(){return n.t.apply(n,arguments)}),t&&t(e,function(){return n.t.apply(n,arguments)})}(e,a)})};return e||!this.services.languageDetector||this.services.languageDetector.async?!e&&this.services.languageDetector&&this.services.languageDetector.async?this.services.languageDetector.detect(i):i(e):i(this.services.languageDetector.detect()),r}},{key:"getFixedT",value:function(t,n,r){var o=this,i=function t(n,i){var a;if("object"!==e(i)){for(var s=arguments.length,u=new Array(s>2?s-2:0),c=2;c1&&void 0!==arguments[1]?arguments[1]:{};if(!this.isInitialized)return this.logger.warn("hasLoadedNamespace: i18next was not initialized",this.languages),!1;if(!this.languages||!this.languages.length)return this.logger.warn("hasLoadedNamespace: i18n.languages were undefined or empty",this.languages),!1;var r=this.resolvedLanguage||this.languages[0],o=!!this.options&&this.options.fallbackLng,i=this.languages[this.languages.length-1];if("cimode"===r.toLowerCase())return!0;var a=function(e,n){var r=t.services.backendConnector.state["".concat(e,"|").concat(n)];return-1===r||2===r};if(n.precheck){var s=n.precheck(this,a);if(void 0!==s)return s}return!!this.hasResourceBundle(r,e)||(!(this.services.backendConnector.backend&&(!this.options.resources||this.options.partialBundledLanguages))||!(!a(r,e)||o&&!a(i,e)))}},{key:"loadNamespaces",value:function(e,t){var n=this,r=d();return this.options.ns?("string"==typeof e&&(e=[e]),e.forEach(function(e){n.options.ns.indexOf(e)<0&&n.options.ns.push(e)}),this.loadResources(function(e){r.resolve(),t&&t(e)}),r):(t&&t(),Promise.resolve())}},{key:"loadLanguages",value:function(e,t){var n=d();"string"==typeof e&&(e=[e]);var r=this.options.preload||[],o=e.filter(function(e){return r.indexOf(e)<0});return o.length?(this.options.preload=r.concat(o),this.loadResources(function(e){n.resolve(),t&&t(e)}),n):(t&&t(),Promise.resolve())}},{key:"dir",value:function(e){if(e||(e=this.resolvedLanguage||(this.languages&&this.languages.length>0?this.languages[0]:this.language)),!e)return"rtl";return["ar","shu","sqr","ssh","xaa","yhd","yud","aao","abh","abv","acm","acq","acw","acx","acy","adf","ads","aeb","aec","afb","ajp","apc","apd","arb","arq","ars","ary","arz","auz","avl","ayh","ayl","ayn","ayp","bbz","pga","he","iw","ps","pbt","pbu","pst","prp","prd","ug","ur","ydd","yds","yih","ji","yi","hbo","men","xmn","fa","jpr","peo","pes","prs","dv","sam","ckb"].indexOf(this.services.languageUtils.getLanguagePartFromCode(e))>-1||e.toLowerCase().indexOf("-arab")>1?"rtl":"ltr"}},{key:"cloneInstance",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:ie,r=re(re(re({},this.options),t),{isClone:!0}),o=new u(r);return["store","services","language"].forEach(function(t){o[t]=e[t]}),o.services=re({},this.services),o.services.utils={hasLoadedNamespace:o.hasLoadedNamespace.bind(o)},o.translator=new A(o.services,o.options),o.translator.on("*",function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0;return new ae(e,t)});var se=ae.createInstance();return se.createInstance=ae.createInstance,se}); diff --git a/cps/static/js/libs/pwstrength/i18nextHttpBackend.min.js b/cps/static/js/libs/pwstrength/i18nextHttpBackend.min.js new file mode 100644 index 00000000..05c56c9d --- /dev/null +++ b/cps/static/js/libs/pwstrength/i18nextHttpBackend.min.js @@ -0,0 +1 @@ +!function(e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).i18nextHttpBackend=e()}(function(){return function o(i,r,a){function s(t,e){if(!r[t]){if(!i[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(u)return u(t,!0);throw(n=new Error("Cannot find module '"+t+"'")).code="MODULE_NOT_FOUND",n}n=r[t]={exports:{}},i[t][0].call(n.exports,function(e){return s(i[t][1][e]||e)},n,n.exports,o,i,r,a)}return r[t].exports}for(var u="function"==typeof require&&require,e=0;e options.common.maxChar) { + return score; + } + return lenScore; + }; + + validation.wordInvalidChar = function(options, word, score) { + if (options.common.invalidCharsRegExp.test(word)) { + return score; + } + return 0; + }; + + validation.wordMinLengthStaticScore = function(options, word, score) { + return word.length < options.common.minChar ? 0 : score; + }; + + validation.wordMaxLengthStaticScore = function(options, word, score) { + return word.length > options.common.maxChar ? 0 : score; + }; + + validation.wordSimilarToUsername = function(options, word, score) { + var username = $(options.common.usernameField).val(); + if ( + username && + word + .toLowerCase() + .match( + username + .replace(/[-[\]/{}()*+=?:.\\^$|!,]/g, '\\$&') + .toLowerCase() + ) + ) { + return score; + } + return 0; + }; + + validation.wordTwoCharacterClasses = function(options, word, score) { + var specialCharRE = new RegExp( + '(.' + options.rules.specialCharClass + ')' + ); + + if ( + word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) || + (word.match(/([a-zA-Z])/) && word.match(/([0-9])/)) || + (word.match(specialCharRE) && word.match(/[a-zA-Z0-9_]/)) + ) { + return score; + } + return; + }; + + validation.wordRepetitions = function(options, word, score) { + if (word.match(/(.)\1\1/)) { + return score; + } + return 0; + }; + + validation.wordSequences = function(options, word, score) { + var found = false, + j; + + if (word.length > 2) { + $.each(rulesEngine.forbiddenSequences, function(idx, seq) { + var sequences; + if (found) { + return; + } + sequences = [ + seq, + seq + .split('') + .reverse() + .join('') + ]; + $.each(sequences, function(ignore, sequence) { + for (j = 0; j < word.length - 2; j += 1) { + // iterate the word trough a sliding window of size 3: + if ( + sequence.indexOf( + word.toLowerCase().substring(j, j + 3) + ) > -1 + ) { + found = true; + } + } + }); + }); + if (found) { + return score; + } + } + return 0; + }; + + validation.wordLowercase = function(options, word, score) { + return word.match(/[a-z]/) && score; + }; + + validation.wordUppercase = function(options, word, score) { + return word.match(/[A-Z]/) && score; + }; + + validation.wordOneNumber = function(options, word, score) { + return word.match(/\d+/) && score; + }; + + validation.wordThreeNumbers = function(options, word, score) { + return word.match(/(.*[0-9].*[0-9].*[0-9])/) && score; + }; + + validation.wordOneSpecialChar = function(options, word, score) { + var specialCharRE = new RegExp(options.rules.specialCharClass); + return word.match(specialCharRE) && score; + }; + + validation.wordTwoSpecialChar = function(options, word, score) { + var twoSpecialCharRE = new RegExp( + '(.*' + + options.rules.specialCharClass + + '.*' + + options.rules.specialCharClass + + ')' + ); + + return word.match(twoSpecialCharRE) && score; + }; + + validation.wordUpperLowerCombo = function(options, word, score) { + return word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) && score; + }; + + validation.wordLetterNumberCombo = function(options, word, score) { + return word.match(/([a-zA-Z])/) && word.match(/([0-9])/) && score; + }; + + validation.wordLetterNumberCharCombo = function(options, word, score) { + var letterNumberCharComboRE = new RegExp( + '([a-zA-Z0-9].*' + + options.rules.specialCharClass + + ')|(' + + options.rules.specialCharClass + + '.*[a-zA-Z0-9])' + ); + + return word.match(letterNumberCharComboRE) && score; + }; + + validation.wordIsACommonPassword = function(options, word, score) { + if ($.inArray(word, options.rules.commonPasswords) >= 0) { + return score; + } + return 0; + }; + + rulesEngine.validation = validation; + + rulesEngine.executeRules = function(options, word) { + var totalScore = 0; + + $.each(options.rules.activated, function(rule, active) { + var score, funct, result, errorMessage; + + if (active) { + score = options.rules.scores[rule]; + funct = rulesEngine.validation[rule]; + + if (typeof funct !== 'function') { + funct = options.rules.extra[rule]; + } + + if (typeof funct === 'function') { + result = funct(options, word, score); + if (result) { + totalScore += result; + } + if (result < 0 || (!$.isNumeric(result) && !result)) { + errorMessage = options.ui.spanError(options, rule); + if (errorMessage.length > 0) { + options.instances.errors.push(errorMessage); + } + } + } + } + }); + + return totalScore; + }; +})(jQuery); + +try { + if (module && module.exports) { + module.exports = rulesEngine; + } +} catch (ignore) { + // Nothing to do +} + +// Source: src/options.js + + + +// eslint-disable-next-line no-implicit-globals +var defaultOptions = {}; + +defaultOptions.common = {}; +defaultOptions.common.minChar = 6; +defaultOptions.common.maxChar = 20; +defaultOptions.common.usernameField = '#username'; +defaultOptions.common.invalidCharsRegExp = new RegExp(/[\s,'"]/); +defaultOptions.common.userInputs = [ + // Selectors for input fields with user input +]; +defaultOptions.common.onLoad = undefined; +defaultOptions.common.onKeyUp = undefined; +defaultOptions.common.onScore = undefined; +defaultOptions.common.zxcvbn = false; +defaultOptions.common.zxcvbnTerms = [ + // List of disrecommended words +]; +defaultOptions.common.events = ['keyup', 'change', 'paste']; +defaultOptions.common.debug = false; + +defaultOptions.rules = {}; +defaultOptions.rules.extra = {}; +defaultOptions.rules.scores = { + wordNotEmail: -100, + wordMinLength: -50, + wordMaxLength: -50, + wordInvalidChar: -100, + wordSimilarToUsername: -100, + wordSequences: -20, + wordTwoCharacterClasses: 2, + wordRepetitions: -25, + wordLowercase: 1, + wordUppercase: 3, + wordOneNumber: 3, + wordThreeNumbers: 5, + wordOneSpecialChar: 3, + wordTwoSpecialChar: 5, + wordUpperLowerCombo: 2, + wordLetterNumberCombo: 2, + wordLetterNumberCharCombo: 2, + wordIsACommonPassword: -100 +}; +defaultOptions.rules.activated = { + wordNotEmail: true, + wordMinLength: true, + wordMaxLength: false, + wordInvalidChar: false, + wordSimilarToUsername: true, + wordSequences: true, + wordTwoCharacterClasses: true, + wordRepetitions: true, + wordLowercase: true, + wordUppercase: true, + wordOneNumber: true, + wordThreeNumbers: true, + wordOneSpecialChar: true, + wordTwoSpecialChar: true, + wordUpperLowerCombo: true, + wordLetterNumberCombo: true, + wordLetterNumberCharCombo: true, + wordIsACommonPassword: true +}; +defaultOptions.rules.raisePower = 1.4; +defaultOptions.rules.specialCharClass = '[!,@,#,$,%,^,&,*,?,_,~]'; +// List taken from https://github.com/danielmiessler/SecLists (MIT License) +defaultOptions.rules.commonPasswords = [ + '123456', + 'password', + '12345678', + 'qwerty', + '123456789', + '12345', + '1234', + '111111', + '1234567', + 'dragon', + '123123', + 'baseball', + 'abc123', + 'football', + 'monkey', + 'letmein', + '696969', + 'shadow', + 'master', + '666666', + 'qwertyuiop', + '123321', + 'mustang', + '1234567890', + 'michael', + '654321', + 'pussy', + 'superman', + '1qaz2wsx', + '7777777', + 'fuckyou', + '121212', + '000000', + 'qazwsx', + '123qwe', + 'killer', + 'trustno1', + 'jordan', + 'jennifer', + 'zxcvbnm', + 'asdfgh', + 'hunter', + 'buster', + 'soccer', + 'harley', + 'batman', + 'andrew', + 'tigger', + 'sunshine', + 'iloveyou', + 'fuckme', + '2000', + 'charlie', + 'robert', + 'thomas', + 'hockey', + 'ranger', + 'daniel', + 'starwars', + 'klaster', + '112233', + 'george', + 'asshole', + 'computer', + 'michelle', + 'jessica', + 'pepper', + '1111', + 'zxcvbn', + '555555', + '11111111', + '131313', + 'freedom', + '777777', + 'pass', + 'fuck', + 'maggie', + '159753', + 'aaaaaa', + 'ginger', + 'princess', + 'joshua', + 'cheese', + 'amanda', + 'summer', + 'love', + 'ashley', + '6969', + 'nicole', + 'chelsea', + 'biteme', + 'matthew', + 'access', + 'yankees', + '987654321', + 'dallas', + 'austin', + 'thunder', + 'taylor', + 'matrix' +]; + +defaultOptions.ui = {}; +defaultOptions.ui.bootstrap2 = false; +defaultOptions.ui.bootstrap3 = false; +defaultOptions.ui.colorClasses = [ + 'danger', + 'danger', + 'danger', + 'warning', + 'warning', + 'success' +]; +defaultOptions.ui.showProgressBar = true; +defaultOptions.ui.progressBarEmptyPercentage = 1; +defaultOptions.ui.progressBarMinWidth = 1; +defaultOptions.ui.progressBarMinPercentage = 1; +defaultOptions.ui.progressExtraCssClasses = ''; +defaultOptions.ui.progressBarExtraCssClasses = ''; +defaultOptions.ui.showPopover = false; +defaultOptions.ui.popoverPlacement = 'bottom'; +defaultOptions.ui.showStatus = false; +defaultOptions.ui.spanError = function(options, key) { + 'use strict'; + var text = options.i18n.t(key); + if (!text) { + return ''; + } + return '' + text + ''; +}; +defaultOptions.ui.popoverError = function(options) { + 'use strict'; + var errors = options.instances.errors, + errorsTitle = options.i18n.t('errorList'), + message = + '
' + + errorsTitle + + '
    '; + + jQuery.each(errors, function(idx, err) { + message += '
  • ' + err + '
  • '; + }); + message += '
'; + return message; +}; +defaultOptions.ui.showVerdicts = true; +defaultOptions.ui.showVerdictsInsideProgressBar = false; +defaultOptions.ui.useVerdictCssClass = false; +defaultOptions.ui.showErrors = false; +defaultOptions.ui.showScore = false; +defaultOptions.ui.container = undefined; +defaultOptions.ui.viewports = { + progress: undefined, + verdict: undefined, + errors: undefined, + score: undefined +}; +defaultOptions.ui.scores = [0, 14, 26, 38, 50]; + +defaultOptions.i18n = {}; +defaultOptions.i18n.t = i18n.t; + +// Source: src/ui.js + +// eslint-disable-next-line no-implicit-globals +var ui = {}; + +(function($) { + 'use strict'; + + var statusClasses = ['error', 'warning', 'success'], + verdictKeys = [ + 'veryWeak', + 'weak', + 'normal', + 'medium', + 'strong', + 'veryStrong' + ]; + + ui.getContainer = function(options, $el) { + var $container; + + $container = $(options.ui.container); + if (!($container && $container.length === 1)) { + $container = $el.parent(); + } + return $container; + }; + + ui.findElement = function($container, viewport, cssSelector) { + if (viewport) { + return $container.find(viewport).find(cssSelector); + } + return $container.find(cssSelector); + }; + + ui.getUIElements = function(options, $el) { + var $container, result; + + if (options.instances.viewports) { + return options.instances.viewports; + } + + $container = ui.getContainer(options, $el); + + result = {}; + result.$progressbar = ui.findElement( + $container, + options.ui.viewports.progress, + 'div.progress' + ); + if (options.ui.showVerdictsInsideProgressBar) { + result.$verdict = result.$progressbar.find('span.password-verdict'); + } + + if (!options.ui.showPopover) { + if (!options.ui.showVerdictsInsideProgressBar) { + result.$verdict = ui.findElement( + $container, + options.ui.viewports.verdict, + 'span.password-verdict' + ); + } + result.$errors = ui.findElement( + $container, + options.ui.viewports.errors, + 'ul.error-list' + ); + } + result.$score = ui.findElement( + $container, + options.ui.viewports.score, + 'span.password-score' + ); + + options.instances.viewports = result; + return result; + }; + + ui.initHelper = function(options, $el, html, viewport) { + var $container = ui.getContainer(options, $el); + if (viewport) { + $container.find(viewport).append(html); + } else { + $(html).insertAfter($el); + } + }; + + ui.initVerdict = function(options, $el) { + ui.initHelper( + options, + $el, + '', + options.ui.viewports.verdict + ); + }; + + ui.initErrorList = function(options, $el) { + ui.initHelper( + options, + $el, + '
    ', + options.ui.viewports.errors + ); + }; + + ui.initScore = function(options, $el) { + ui.initHelper( + options, + $el, + '', + options.ui.viewports.score + ); + }; + + ui.initUI = function(options, $el) { + if (options.ui.showPopover) { + ui.initPopover(options, $el); + } else { + if (options.ui.showErrors) { + ui.initErrorList(options, $el); + } + if ( + options.ui.showVerdicts && + !options.ui.showVerdictsInsideProgressBar + ) { + ui.initVerdict(options, $el); + } + } + if (options.ui.showProgressBar) { + ui.initProgressBar(options, $el); + } + if (options.ui.showScore) { + ui.initScore(options, $el); + } + }; + + ui.updateVerdict = function(options, $el, cssClass, text) { + var $verdict = ui.getUIElements(options, $el).$verdict; + $verdict.removeClass(options.ui.colorClasses.join(' ')); + if (cssClass > -1) { + $verdict.addClass(options.ui.colorClasses[cssClass]); + } + if (options.ui.showVerdictsInsideProgressBar) { + $verdict.css('white-space', 'nowrap'); + } + $verdict.html(text); + }; + + ui.updateErrors = function(options, $el, remove) { + var $errors = ui.getUIElements(options, $el).$errors, + html = ''; + + if (!remove) { + $.each(options.instances.errors, function(idx, err) { + html += '
  • ' + err + '
  • '; + }); + } + $errors.html(html); + }; + + ui.updateScore = function(options, $el, score, remove) { + var $score = ui.getUIElements(options, $el).$score, + html = ''; + + if (!remove) { + html = score.toFixed(2); + } + $score.html(html); + }; + + ui.updateFieldStatus = function(options, $el, cssClass, remove) { + var $target = $el; + + if (options.ui.bootstrap2) { + $target = $el.parents('.control-group').first(); + } else if (options.ui.bootstrap3) { + $target = $el.parents('.form-group').first(); + } + + $.each(statusClasses, function(idx, css) { + css = ui.cssClassesForBS(options, css); + $target.removeClass(css); + }); + + if (remove) { + return; + } + + cssClass = statusClasses[Math.floor(cssClass / 2)]; + cssClass = ui.cssClassesForBS(options, cssClass); + $target.addClass(cssClass); + }; + + ui.cssClassesForBS = function(options, css) { + if (options.ui.bootstrap3) { + css = 'has-' + css; + } else if (!options.ui.bootstrap2) { + // BS4 + if (css === 'error') { + css = 'danger'; + } + css = 'border-' + css; + } + return css; + }; + + ui.getVerdictAndCssClass = function(options, score) { + var level, verdict; + + if (score === undefined) { + return ['', 0]; + } + + if (score <= options.ui.scores[0]) { + level = 0; + } else if (score < options.ui.scores[1]) { + level = 1; + } else if (score < options.ui.scores[2]) { + level = 2; + } else if (score < options.ui.scores[3]) { + level = 3; + } else if (score < options.ui.scores[4]) { + level = 4; + } else { + level = 5; + } + + verdict = verdictKeys[level]; + + return [options.i18n.t(verdict), level]; + }; + + ui.updateUI = function(options, $el, score) { + var cssClass, verdictText, verdictCssClass; + + cssClass = ui.getVerdictAndCssClass(options, score); + verdictText = score === 0 ? '' : cssClass[0]; + cssClass = cssClass[1]; + verdictCssClass = options.ui.useVerdictCssClass ? cssClass : -1; + + if (options.ui.showProgressBar) { + ui.showProgressBar( + options, + $el, + score, + cssClass, + verdictCssClass, + verdictText + ); + } + + if (options.ui.showStatus) { + ui.updateFieldStatus(options, $el, cssClass, score === undefined); + } + + if (options.ui.showPopover) { + ui.updatePopover(options, $el, verdictText, score === undefined); + } else { + if ( + options.ui.showVerdicts && + !options.ui.showVerdictsInsideProgressBar + ) { + ui.updateVerdict(options, $el, verdictCssClass, verdictText); + } + if (options.ui.showErrors) { + ui.updateErrors(options, $el, score === undefined); + } + } + + if (options.ui.showScore) { + ui.updateScore(options, $el, score, score === undefined); + } + }; +})(jQuery); + +// Source: src/ui.progressbar.js + + + +(function($) { + 'use strict'; + + ui.percentage = function(options, score, maximun) { + var result = Math.floor((100 * score) / maximun), + min = options.ui.progressBarMinPercentage; + + result = result <= min ? min : result; + result = result > 100 ? 100 : result; + return result; + }; + + ui.initProgressBar = function(options, $el) { + var $container = ui.getContainer(options, $el), + progressbar = '
    '; + + if (options.ui.showVerdictsInsideProgressBar) { + progressbar += ''; + } + + progressbar += '
    '; + + if (options.ui.viewports.progress) { + $container.find(options.ui.viewports.progress).append(progressbar); + } else { + $(progressbar).insertAfter($el); + } + }; + + ui.showProgressBar = function( + options, + $el, + score, + cssClass, + verdictCssClass, + verdictText + ) { + var barPercentage; + + if (score === undefined) { + barPercentage = options.ui.progressBarEmptyPercentage; + } else { + barPercentage = ui.percentage(options, score, options.ui.scores[4]); + } + ui.updateProgressBar(options, $el, cssClass, barPercentage); + if (options.ui.showVerdictsInsideProgressBar) { + ui.updateVerdict(options, $el, verdictCssClass, verdictText); + } + }; + + ui.updateProgressBar = function(options, $el, cssClass, percentage) { + var $progressbar = ui.getUIElements(options, $el).$progressbar, + $bar = $progressbar.find('.progress-bar'), + cssPrefix = 'progress-'; + + if (options.ui.bootstrap2) { + $bar = $progressbar.find('.bar'); + cssPrefix = ''; + } + + $.each(options.ui.colorClasses, function(idx, value) { + if (options.ui.bootstrap2 || options.ui.bootstrap3) { + $bar.removeClass(cssPrefix + 'bar-' + value); + } else { + $bar.removeClass('bg-' + value); + } + }); + if (options.ui.bootstrap2 || options.ui.bootstrap3) { + $bar.addClass( + cssPrefix + 'bar-' + options.ui.colorClasses[cssClass] + ); + } else { + $bar.addClass('bg-' + options.ui.colorClasses[cssClass]); + } + if (percentage > 0) { + $bar.css('min-width', options.ui.progressBarMinWidth + 'px'); + } else { + $bar.css('min-width', ''); + } + $bar.css('width', percentage + '%'); + }; +})(jQuery); + +// Source: src/ui.popover.js + + + +(function() { + 'use strict'; + + ui.initPopover = function(options, $el) { + try { + $el.popover('destroy'); + } catch (error) { + // Bootstrap 4.2.X onwards + $el.popover('dispose'); + } + $el.popover({ + html: true, + placement: options.ui.popoverPlacement, + trigger: 'manual', + content: ' ' + }); + }; + + ui.updatePopover = function(options, $el, verdictText, remove) { + var popover = $el.data('bs.popover'), + html = '', + hide = true, + bootstrap5 = false, + itsVisible = false; + + if ( + options.ui.showVerdicts && + !options.ui.showVerdictsInsideProgressBar && + verdictText.length > 0 + ) { + html = + '
    ' + + verdictText + + '
    '; + hide = false; + } + if (options.ui.showErrors) { + if (options.instances.errors.length > 0) { + hide = false; + } + html += options.ui.popoverError(options); + } + + if (hide || remove) { + $el.popover('hide'); + return; + } + + if (options.ui.bootstrap2) { + popover = $el.data('popover'); + } else if (!popover) { + // Bootstrap 5 + popover = bootstrap.Popover.getInstance($el[0]); + bootstrap5 = true; + } + + if (bootstrap5) { + itsVisible = $(popover.tip).is(':visible'); + } else { + itsVisible = popover.$arrow && popover.$arrow.parents('body').length > 0; + } + + if (itsVisible) { + if (bootstrap5) { + $(popover.tip).find('.popover-body').html(html); + } else { + $el.find('+ .popover .popover-content').html(html); + } + } else { + // It's hidden + if (options.ui.bootstrap2 || options.ui.bootstrap3) { + popover.options.content = html; + } else if (bootstrap5) { + popover._config.content = html; + } else { + popover.config.content = html; + } + $el.popover('show'); + } + }; +})(); + +// Source: src/methods.js + + + +// eslint-disable-next-line no-implicit-globals +var methods = {}; + +(function($) { + 'use strict'; + var onKeyUp, onPaste, applyToAll; + + onKeyUp = function(event) { + var $el = $(event.target), + options = $el.data('pwstrength-bootstrap'), + word = $el.val(), + userInputs, + verdictText, + verdictLevel, + score; + + if (options === undefined) { + return; + } + + options.instances.errors = []; + if (word.length === 0) { + score = undefined; + } else { + if (options.common.zxcvbn) { + userInputs = []; + $.each( + options.common.userInputs.concat([ + options.common.usernameField + ]), + function(idx, selector) { + var value = $(selector).val(); + if (value) { + userInputs.push(value); + } + } + ); + userInputs = userInputs.concat(options.common.zxcvbnTerms); + score = zxcvbn(word, userInputs).guesses; + score = Math.log(score) * Math.LOG2E; + } else { + score = rulesEngine.executeRules(options, word); + } + if (typeof options.common.onScore === 'function') { + score = options.common.onScore(options, word, score); + } + } + ui.updateUI(options, $el, score); + verdictText = ui.getVerdictAndCssClass(options, score); + verdictLevel = verdictText[1]; + verdictText = verdictText[0]; + + if (options.common.debug) { + console.log(score + ' - ' + verdictText); + } + + if (typeof options.common.onKeyUp === 'function') { + options.common.onKeyUp(event, { + score: score, + verdictText: verdictText, + verdictLevel: verdictLevel + }); + } + }; + + onPaste = function(event) { + // This handler is necessary because the paste event fires before the + // content is actually in the input, so we cannot read its value right + // away. Therefore, the timeouts. + var $el = $(event.target), + word = $el.val(), + tries = 0, + callback; + + callback = function() { + var newWord = $el.val(); + + if (newWord !== word) { + onKeyUp(event); + } else if (tries < 3) { + tries += 1; + setTimeout(callback, 100); + } + }; + + setTimeout(callback, 100); + }; + + methods.init = function(settings) { + this.each(function(idx, el) { + // Make it deep extend (first param) so it extends also the + // rules and other inside objects + var clonedDefaults = $.extend(true, {}, defaultOptions), + localOptions = $.extend(true, clonedDefaults, settings), + $el = $(el); + + localOptions.instances = {}; + $el.data('pwstrength-bootstrap', localOptions); + + $.each(localOptions.common.events, function(ignore, eventName) { + var handler = eventName === 'paste' ? onPaste : onKeyUp; + $el.on(eventName, handler); + }); + + ui.initUI(localOptions, $el); + $el.trigger('keyup'); + + if (typeof localOptions.common.onLoad === 'function') { + localOptions.common.onLoad(); + } + }); + + return this; + }; + + methods.destroy = function() { + this.each(function(idx, el) { + var $el = $(el), + options = $el.data('pwstrength-bootstrap'), + elements = ui.getUIElements(options, $el); + elements.$progressbar.remove(); + elements.$verdict.remove(); + elements.$errors.remove(); + $el.removeData('pwstrength-bootstrap'); + }); + + return this; + }; + + methods.forceUpdate = function() { + this.each(function(idx, el) { + var event = { target: el }; + onKeyUp(event); + }); + + return this; + }; + + methods.addRule = function(name, method, score, active) { + this.each(function(idx, el) { + var options = $(el).data('pwstrength-bootstrap'); + + options.rules.activated[name] = active; + options.rules.scores[name] = score; + options.rules.extra[name] = method; + }); + + return this; + }; + + applyToAll = function(rule, prop, value) { + this.each(function(idx, el) { + $(el).data('pwstrength-bootstrap').rules[prop][rule] = value; + }); + }; + + methods.changeScore = function(rule, score) { + applyToAll.call(this, rule, 'scores', score); + + return this; + }; + + methods.ruleActive = function(rule, active) { + applyToAll.call(this, rule, 'activated', active); + + return this; + }; + + methods.ruleIsMet = function(rule) { + var rulesMetCnt = 0; + + if (rule === 'wordMinLength') { + rule = 'wordMinLengthStaticScore'; + } else if (rule === 'wordMaxLength') { + rule = 'wordMaxLengthStaticScore'; + } + + this.each(function(idx, el) { + var options = $(el).data('pwstrength-bootstrap'), + ruleFunction = rulesEngine.validation[rule], + result; + + if (typeof ruleFunction !== 'function') { + ruleFunction = options.rules.extra[rule]; + } + if (typeof ruleFunction === 'function') { + result = ruleFunction(options, $(el).val(), 1); + if ($.isNumeric(result)) { + rulesMetCnt += result; + } + } + }); + + return rulesMetCnt === this.length; + }; + + $.fn.pwstrength = function(method) { + var result; + + if (methods[method]) { + result = methods[method].apply( + this, + Array.prototype.slice.call(arguments, 1) + ); + } else if (typeof method === 'object' || !method) { + result = methods.init.apply(this, arguments); + } else { + $.error( + 'Method ' + + method + + ' does not exist on jQuery.pwstrength-bootstrap' + ); + } + + return result; + }; +})(jQuery); +}(jQuery)); diff --git a/cps/static/js/libs/pwstrength/pwstrength-bootstrap.min.js b/cps/static/js/libs/pwstrength/pwstrength-bootstrap.min.js new file mode 100644 index 00000000..f850afc9 --- /dev/null +++ b/cps/static/js/libs/pwstrength/pwstrength-bootstrap.min.js @@ -0,0 +1,4 @@ +/* pwstrength-bootstrap 2021-10-29 - GPLv3 & MIT License */ + +!function(s){var t={};!function(o){"use strict";t.fallback={wordMinLength:"Your password is too short",wordMaxLength:"Your password is too long",wordInvalidChar:"Your password contains an invalid character",wordNotEmail:"Do not use your email as your password",wordSimilarToUsername:"Your password cannot contain your username",wordTwoCharacterClasses:"Use different character classes",wordRepetitions:"Too many repetitions",wordSequences:"Your password contains sequences",errorList:"Errors:",veryWeak:"Very Weak",weak:"Weak",normal:"Normal",medium:"Medium",strong:"Strong",veryStrong:"Very Strong"},t.t=function(r){var e="";return(e=o?o.t(r):t.fallback[r])===r?"":e}}(window.i18next);var r,c={};try{!s&&module&&module.exports&&(s=require("jquery"),r=require("jsdom").jsdom,s=s(r().defaultView))}catch(r){}!function(i){"use strict";var r={};c.forbiddenSequences=["0123456789","abcdefghijklmnopqrstuvwxyz","qwertyuiop","asdfghjkl","zxcvbnm","!@#$%^&*()_+"],r.wordNotEmail=function(r,e,o){return e.match(/^([\w!#$%&'*+\-/=?^`{|}~]+\.)*[\w!#$%&'*+\-/=?^`{|}~]+@((((([a-z0-9]{1}[a-z0-9-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(:\d{1,5})?)$/i)?o:0},r.wordMinLength=function(r,e,o){var s=e.length,e=Math.pow(s,r.rules.raisePower);return sr.common.maxChar?o:e},r.wordInvalidChar=function(r,e,o){return r.common.invalidCharsRegExp.test(e)?o:0},r.wordMinLengthStaticScore=function(r,e,o){return e.lengthr.common.maxChar?0:o},r.wordSimilarToUsername=function(r,e,o){r=i(r.common.usernameField).val();return r&&e.toLowerCase().match(r.replace(/[-[\]/{}()*+=?:.\\^$|!,]/g,"\\$&").toLowerCase())?o:0},r.wordTwoCharacterClasses=function(r,e,o){r=new RegExp("(."+r.rules.specialCharClass+")");if(e.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)||e.match(/([a-zA-Z])/)&&e.match(/([0-9])/)||e.match(r)&&e.match(/[a-zA-Z0-9_]/))return o},r.wordRepetitions=function(r,e,o){return e.match(/(.)\1\1/)?o:0},r.wordSequences=function(r,o,e){var s,t=!1;return 2'+e+"":""},u.ui.popoverError=function(r){"use strict";var e=r.instances.errors,o="
    "+r.i18n.t("errorList")+'
      ';return s.each(e,function(r,e){o+="
    • "+e+"
    • "}),o+="
    "},u.ui.showVerdicts=!0,u.ui.showVerdictsInsideProgressBar=!1,u.ui.useVerdictCssClass=!1,u.ui.showErrors=!1,u.ui.showScore=!1,u.ui.container=void 0,u.ui.viewports={progress:void 0,verdict:void 0,errors:void 0,score:void 0},u.ui.scores=[0,14,26,38,50],u.i18n={},u.i18n.t=t.t;var d={};!function(n){"use strict";var a=["error","warning","success"],o=["veryWeak","weak","normal","medium","strong","veryStrong"];d.getContainer=function(r,e){r=n(r.ui.container);return r=!r||1!==r.length?e.parent():r},d.findElement=function(r,e,o){return(e?r.find(e):r).find(o)},d.getUIElements=function(r,e){var o;return r.instances.viewports||(o=d.getContainer(r,e),(e={}).$progressbar=d.findElement(o,r.ui.viewports.progress,"div.progress"),r.ui.showVerdictsInsideProgressBar&&(e.$verdict=e.$progressbar.find("span.password-verdict")),r.ui.showPopover||(r.ui.showVerdictsInsideProgressBar||(e.$verdict=d.findElement(o,r.ui.viewports.verdict,"span.password-verdict")),e.$errors=d.findElement(o,r.ui.viewports.errors,"ul.error-list")),e.$score=d.findElement(o,r.ui.viewports.score,"span.password-score"),r.instances.viewports=e)},d.initHelper=function(r,e,o,s){r=d.getContainer(r,e);s?r.find(s).append(o):n(o).insertAfter(e)},d.initVerdict=function(r,e){d.initHelper(r,e,'',r.ui.viewports.verdict)},d.initErrorList=function(r,e){d.initHelper(r,e,'
      ',r.ui.viewports.errors)},d.initScore=function(r,e){d.initHelper(r,e,'',r.ui.viewports.score)},d.initUI=function(r,e){r.ui.showPopover?d.initPopover(r,e):(r.ui.showErrors&&d.initErrorList(r,e),r.ui.showVerdicts&&!r.ui.showVerdictsInsideProgressBar&&d.initVerdict(r,e)),r.ui.showProgressBar&&d.initProgressBar(r,e),r.ui.showScore&&d.initScore(r,e)},d.updateVerdict=function(r,e,o,s){e=d.getUIElements(r,e).$verdict;e.removeClass(r.ui.colorClasses.join(" ")),-1"+e+""}),e.html(s)},d.updateScore=function(r,e,o,s){r=d.getUIElements(r,e).$score,e="";s||(e=o.toFixed(2)),r.html(e)},d.updateFieldStatus=function(o,r,e,s){var t=r;o.ui.bootstrap2?t=r.parents(".control-group").first():o.ui.bootstrap3&&(t=r.parents(".form-group").first()),n.each(a,function(r,e){e=d.cssClassesForBS(o,e),t.removeClass(e)}),s||(e=a[Math.floor(e/2)],e=d.cssClassesForBS(o,e),t.addClass(e))},d.cssClassesForBS=function(r,e){return r.ui.bootstrap3?e="has-"+e:r.ui.bootstrap2||(e="border-"+(e="error"===e?"danger":e)),e},d.getVerdictAndCssClass=function(r,e){return void 0===e?["",0]:(e=e<=r.ui.scores[0]?0:e
      '+o+"",a=!1),r.ui.showErrors&&(0. + */ +$(document).ready(function() { + i18next.use(i18nextHttpBackend).init({ + lng: $('#password').data("lang"), + debug: false, + fallbackLng: 'en', + backend: { + loadPath: getPath() + "/static/js/libs/pwstrength/locales/{{lng}}.json", + }, + + }, function () { + // Initialized and ready to go + var options = {}; + options.ui = { + bootstrap3: true, + showProgressBar: false, + showErrors: true, + showVerdicts: false, + } + options.rules= { + activated: { + wordNotEmail: false, + wordMinLength: true, + // wordMaxLength: false, + // wordInvalidChar: true, + wordSimilarToUsername: false, + wordSequences: false, + wordTwoCharacterClasses: false, + wordRepetitions: false, + //wordLowercase: true, + //wordUppercase: true, + wordOneNumber: true, + wordThreeNumbers: false, + wordOneSpecialChar: true, + // wordTwoSpecialChar: true, + wordUpperLowerCombo: false, + wordLetterNumberCombo: false, + wordLetterNumberCharCombo: false + } + } + $('#password').pwstrength(options); + }); +}); diff --git a/cps/templates/config_edit.html b/cps/templates/config_edit.html index f61ca9a5..fded8721 100644 --- a/cps/templates/config_edit.html +++ b/cps/templates/config_edit.html @@ -352,6 +352,53 @@ {% endif %}
      +
      + +
      +
      +
      + + +
      + +
      + + +
      +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      +
      +
      diff --git a/cps/templates/locales/en/translation.json b/cps/templates/locales/en/translation.json new file mode 100644 index 00000000..655669ee --- /dev/null +++ b/cps/templates/locales/en/translation.json @@ -0,0 +1,10 @@ +{ + "input": { + "placeholder": "a placeholder" + }, + "nav": { + "home": "Home", + "page1": "Page One", + "page2": "Page Two" + } +} \ No newline at end of file diff --git a/cps/templates/user_edit.html b/cps/templates/user_edit.html index 4aff7ee2..aea2156f 100755 --- a/cps/templates/user_edit.html +++ b/cps/templates/user_edit.html @@ -21,7 +21,7 @@ {% endif %}
      - +
      {% endif %}
      @@ -175,5 +175,9 @@ + + + + {% endblock %} From d6a31e5db8f6982cc7eb7ee01316b6fea6c499ba Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Thu, 16 Jun 2022 10:44:42 +0200 Subject: [PATCH 010/268] config verify password working --- cps/static/js/password.js | 61 ++++++++++++++++++---------------- cps/templates/config_edit.html | 7 ++-- cps/templates/user_edit.html | 2 +- cps/web.py | 2 ++ 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/cps/static/js/password.js b/cps/static/js/password.js index 7ab7e8f3..43a1a0ac 100644 --- a/cps/static/js/password.js +++ b/cps/static/js/password.js @@ -24,35 +24,40 @@ $(document).ready(function() { }, }, function () { - // Initialized and ready to go - var options = {}; - options.ui = { - bootstrap3: true, - showProgressBar: false, - showErrors: true, - showVerdicts: false, - } - options.rules= { - activated: { - wordNotEmail: false, - wordMinLength: true, - // wordMaxLength: false, - // wordInvalidChar: true, - wordSimilarToUsername: false, - wordSequences: false, - wordTwoCharacterClasses: false, - wordRepetitions: false, - //wordLowercase: true, - //wordUppercase: true, - wordOneNumber: true, - wordThreeNumbers: false, - wordOneSpecialChar: true, - // wordTwoSpecialChar: true, - wordUpperLowerCombo: false, - wordLetterNumberCombo: false, - wordLetterNumberCharCombo: false + if ($('#password').data("verify")) { + // Initialized and ready to go + var options = {}; + options.common = { + minChar: $('#password').data("min") } + options.ui = { + bootstrap3: true, + showProgressBar: false, + showErrors: true, + showVerdicts: false, + } + options.rules= { + activated: { + wordNotEmail: false, + wordMinLength: $('#password').data("min") ? true : false, + // wordMaxLength: false, + // wordInvalidChar: true, + wordSimilarToUsername: false, + wordSequences: false, + wordTwoCharacterClasses: false, + wordRepetitions: false, + wordLowercase: $('#password').data("lower") ? true : false, + wordUppercase: $('#password').data("upper") ? true : false, + wordOneNumber: $('#password').data("number") ? true : false, + wordThreeNumbers: false, + wordOneSpecialChar: $('#password').data("special") ? true : false, + // wordTwoSpecialChar: true, + wordUpperLowerCombo: false, + wordLetterNumberCombo: false, + wordLetterNumberCharCombo: false + } + } + $('#password').pwstrength(options); } - $('#password').pwstrength(options); }); }); diff --git a/cps/templates/config_edit.html b/cps/templates/config_edit.html index fded8721..92e38217 100644 --- a/cps/templates/config_edit.html +++ b/cps/templates/config_edit.html @@ -352,6 +352,7 @@ {% endif %}
      +

      @@ -370,9 +371,8 @@

      -
      - +
      @@ -393,13 +393,14 @@
      - +
      +
      diff --git a/cps/templates/user_edit.html b/cps/templates/user_edit.html index aea2156f..fd233d65 100755 --- a/cps/templates/user_edit.html +++ b/cps/templates/user_edit.html @@ -21,7 +21,7 @@ {% endif %}
      - +
      {% endif %}
      diff --git a/cps/web.py b/cps/web.py index dcccbc32..02ac75b1 100755 --- a/cps/web.py +++ b/cps/web.py @@ -1382,6 +1382,7 @@ def change_profile(kobo_support, local_oauth_check, oauth_status, translations, flash(str(ex), category="error") return render_title_template("user_edit.html", content=current_user, + config=config, translations=translations, profile=1, languages=languages, @@ -1433,6 +1434,7 @@ def profile(): profile=1, languages=languages, content=current_user, + config=config, kobo_support=kobo_support, title=_(u"%(name)s's profile", name=current_user.name), page="me", From 04326af2daaf93de87b0498e6b72a1ce804ec38e Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Thu, 16 Jun 2022 11:15:17 +0200 Subject: [PATCH 011/268] password validation working --- cps/admin.py | 2 +- .../libs/pwstrength/pwstrength-bootstrap.js | 6 +---- cps/static/js/password.js | 11 +++++---- cps/templates/config_edit.html | 1 - cps/web.py | 23 ++++++++++++++++--- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/cps/admin.py b/cps/admin.py index c2b9e500..5667fcd3 100755 --- a/cps/admin.py +++ b/cps/admin.py @@ -1778,7 +1778,7 @@ def _configuration_update_helper(): _config_checkbox(to_save, "config_password_upper") _config_checkbox(to_save, "config_password_special") _config_int(to_save, "config_password_min_length") - _config_int(to_save, "config_session") + reboot_required |= _config_int(to_save, "config_session") # Rarfile Content configuration _config_string(to_save, "config_rarfile_location") diff --git a/cps/static/js/libs/pwstrength/pwstrength-bootstrap.js b/cps/static/js/libs/pwstrength/pwstrength-bootstrap.js index e64d1f97..040983b0 100644 --- a/cps/static/js/libs/pwstrength/pwstrength-bootstrap.js +++ b/cps/static/js/libs/pwstrength/pwstrength-bootstrap.js @@ -25,10 +25,6 @@ var i18n = {}; wordTwoCharacterClasses: 'Use different character classes', wordRepetitions: 'Too many repetitions', wordSequences: 'Your password contains sequences', - // wordLowercase: "Use at least one lowercase character", - // wordUppercase: "Use at least one uppercase character", - // wordOneNumber: "Use at least one number", - // wordOneSpecialChar: "Use at least one special character", errorList: 'Errors:', veryWeak: 'Very Weak', weak: 'Weak', @@ -376,7 +372,7 @@ defaultOptions.rules.activated = { wordIsACommonPassword: true }; defaultOptions.rules.raisePower = 1.4; -defaultOptions.rules.specialCharClass = '[!,@,#,$,%,^,&,*,?,_,~]'; +defaultOptions.rules.specialCharClass = "(?=.*?[^A-Za-z\s0-9])"; //'[!,@,#,$,%,^,&,*,?,_,~]'; // List taken from https://github.com/danielmiessler/SecLists (MIT License) defaultOptions.rules.commonPasswords = [ '123456', diff --git a/cps/static/js/password.js b/cps/static/js/password.js index 43a1a0ac..209eea87 100644 --- a/cps/static/js/password.js +++ b/cps/static/js/password.js @@ -37,20 +37,21 @@ $(document).ready(function() { showVerdicts: false, } options.rules= { + specialCharClass: "(?=.*?[^A-Za-z\\s0-9])", activated: { wordNotEmail: false, - wordMinLength: $('#password').data("min") ? true : false, + wordMinLength: $('#password').data("min"), // wordMaxLength: false, // wordInvalidChar: true, wordSimilarToUsername: false, wordSequences: false, wordTwoCharacterClasses: false, wordRepetitions: false, - wordLowercase: $('#password').data("lower") ? true : false, - wordUppercase: $('#password').data("upper") ? true : false, - wordOneNumber: $('#password').data("number") ? true : false, + wordLowercase: $('#password').data("lower") === "True" ? true : false, + wordUppercase: $('#password').data("upper") === "True" ? true : false, + wordOneNumber: $('#password').data("number") === "True" ? true : false, wordThreeNumbers: false, - wordOneSpecialChar: $('#password').data("special") ? true : false, + wordOneSpecialChar: $('#password').data("special") === "True" ? true : false, // wordTwoSpecialChar: true, wordUpperLowerCombo: false, wordLetterNumberCombo: false, diff --git a/cps/templates/config_edit.html b/cps/templates/config_edit.html index 92e38217..22ce2de8 100644 --- a/cps/templates/config_edit.html +++ b/cps/templates/config_edit.html @@ -400,7 +400,6 @@
      -
      diff --git a/cps/web.py b/cps/web.py index 02ac75b1..b02526ce 100755 --- a/cps/web.py +++ b/cps/web.py @@ -23,6 +23,7 @@ import json import mimetypes import chardet # dependency of requests import copy +import re from flask import Blueprint, jsonify from flask import request, redirect, send_from_directory, make_response, flash, abort, url_for @@ -55,6 +56,7 @@ from .kobo_sync_status import remove_synced_book from .render_template import render_title_template from .kobo_sync_status import change_archived_books + feature_support = { 'ldap': bool(services.ldap), 'goodreads': bool(services.goodreads_support), @@ -1355,10 +1357,25 @@ def logout(): def change_profile(kobo_support, local_oauth_check, oauth_status, translations, languages): to_save = request.form.to_dict() current_user.random_books = 0 - if current_user.role_passwd() or current_user.role_admin(): - if to_save.get("password"): - current_user.password = generate_password_hash(to_save.get("password")) try: + if current_user.role_passwd() or current_user.role_admin(): + if to_save.get("password"): + if config.config_password_policy: + verify = "" + if config.config_password_min_length > 0: + verify += "^(?=\S{" + str(config.config_password_min_length) + ",}$)" + if config.config_password_number: + verify += "(?=.*?\d)" + if config.config_password_lower: + verify += "(?=.*?[a-z])" + if config.config_password_upper: + verify += "(?=.*?[A-Z])" + if config.config_password_special: + verify += "(?=.*?[^A-Za-z\s0-9])" + match = re.match(verify, to_save.get("password")) + if not match: + raise Exception(_("Password doesn't comply with password validation rules")) + current_user.password = generate_password_hash(to_save.get("password")) if to_save.get("kindle_mail", current_user.kindle_mail) != current_user.kindle_mail: current_user.kindle_mail = valid_email(to_save.get("kindle_mail")) if to_save.get("email", current_user.email) != current_user.email: From 4ef8c35fb7c5cf8a5b88555c1e60f09a21b8b38e Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Thu, 16 Jun 2022 14:16:00 +0200 Subject: [PATCH 012/268] Bugfies password validation from testrun --- cps/admin.py | 6 +++--- cps/helper.py | 17 +++++++++++++++++ cps/static/js/password.js | 3 ++- cps/templates/config_edit.html | 2 +- cps/web.py | 22 +++------------------- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/cps/admin.py b/cps/admin.py index 5667fcd3..85ea112f 100755 --- a/cps/admin.py +++ b/cps/admin.py @@ -1848,8 +1848,8 @@ def _handle_new_user(to_save, content, languages, translations, kobo_support): content.sidebar_view |= constants.DETAIL_RANDOM content.role = constants.selected_roles(to_save) - content.password = generate_password_hash(to_save["password"]) try: + content.password = generate_password_hash(helper.valid_password(to_save["password"])) if not to_save["name"] or not to_save["email"] or not to_save["password"]: log.info("Missing entries on new user") raise Exception(_(u"Please fill out all fields!")) @@ -1936,8 +1936,8 @@ def _handle_edit_user(to_save, content, languages, translations, kobo_support): log.warning("No admin user remaining, can't remove admin role from {}".format(content.name)) flash(_("No admin user remaining, can't remove admin role"), category="error") return redirect(url_for('admin.admin')) - if to_save.get("password"): - content.password = generate_password_hash(to_save["password"]) + if 'password' in to_save: + content.password = generate_password_hash(helper.valid_password(to_save('password'))) anonymous = content.is_anonymous content.role = constants.selected_roles(to_save) if anonymous: diff --git a/cps/helper.py b/cps/helper.py index d40ffc33..60bc1713 100755 --- a/cps/helper.py +++ b/cps/helper.py @@ -661,6 +661,23 @@ def valid_email(email): raise Exception(_(u"Invalid e-mail address format")) return email +def valid_password(check_password): + if config.config_password_policy: + verify = "" + if config.config_password_min_length > 0: + verify += "^(?=\S{" + str(config.config_password_min_length) + ",}$)" + if config.config_password_number: + verify += "(?=.*?\d)" + if config.config_password_lower: + verify += "(?=.*?[a-z])" + if config.config_password_upper: + verify += "(?=.*?[A-Z])" + if config.config_password_special: + verify += "(?=.*?[^A-Za-z\s0-9])" + match = re.match(verify, check_password) + if not match: + raise Exception(_("Password doesn't comply with password validation rules")) + return check_password # ################################# External interface ################################# diff --git a/cps/static/js/password.js b/cps/static/js/password.js index 209eea87..ecfe65fe 100644 --- a/cps/static/js/password.js +++ b/cps/static/js/password.js @@ -28,7 +28,8 @@ $(document).ready(function() { // Initialized and ready to go var options = {}; options.common = { - minChar: $('#password').data("min") + minChar: $('#password').data("min"), + maxChar: -1 } options.ui = { bootstrap3: true, diff --git a/cps/templates/config_edit.html b/cps/templates/config_edit.html index 22ce2de8..f6ccb5b3 100644 --- a/cps/templates/config_edit.html +++ b/cps/templates/config_edit.html @@ -389,7 +389,7 @@
      - +
      diff --git a/cps/web.py b/cps/web.py index b02526ce..58921bfe 100755 --- a/cps/web.py +++ b/cps/web.py @@ -23,7 +23,6 @@ import json import mimetypes import chardet # dependency of requests import copy -import re from flask import Blueprint, jsonify from flask import request, redirect, send_from_directory, make_response, flash, abort, url_for @@ -47,7 +46,7 @@ from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download from .helper import check_valid_domain, check_email, check_username, \ get_book_cover, get_series_cover_thumbnail, get_download_link, send_mail, generate_random_password, \ send_registration_mail, check_send_to_ereader, check_read_formats, tags_filters, reset_password, valid_email, \ - edit_book_read_status + edit_book_read_status, valid_password from .pagination import Pagination from .redirect import redirect_back from .babel import get_available_locale @@ -1359,23 +1358,8 @@ def change_profile(kobo_support, local_oauth_check, oauth_status, translations, current_user.random_books = 0 try: if current_user.role_passwd() or current_user.role_admin(): - if to_save.get("password"): - if config.config_password_policy: - verify = "" - if config.config_password_min_length > 0: - verify += "^(?=\S{" + str(config.config_password_min_length) + ",}$)" - if config.config_password_number: - verify += "(?=.*?\d)" - if config.config_password_lower: - verify += "(?=.*?[a-z])" - if config.config_password_upper: - verify += "(?=.*?[A-Z])" - if config.config_password_special: - verify += "(?=.*?[^A-Za-z\s0-9])" - match = re.match(verify, to_save.get("password")) - if not match: - raise Exception(_("Password doesn't comply with password validation rules")) - current_user.password = generate_password_hash(to_save.get("password")) + if 'password' in to_save: + current_user.password = generate_password_hash(valid_password(to_save('password'))) if to_save.get("kindle_mail", current_user.kindle_mail) != current_user.kindle_mail: current_user.kindle_mail = valid_email(to_save.get("kindle_mail")) if to_save.get("email", current_user.email) != current_user.email: From 29fd4ae4a276e154f4f934934899d3dc3274d9a1 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Fri, 17 Jun 2022 10:14:33 +0200 Subject: [PATCH 013/268] Bugfixes create users Update Teststatus --- cps/admin.py | 6 +- cps/web.py | 4 +- setup.cfg | 2 +- test/Calibre-Web TestSummary_Linux.html | 11458 +++++++++++++++++----- 4 files changed, 9170 insertions(+), 2300 deletions(-) diff --git a/cps/admin.py b/cps/admin.py index 85ea112f..27d17a1d 100755 --- a/cps/admin.py +++ b/cps/admin.py @@ -1849,10 +1849,10 @@ def _handle_new_user(to_save, content, languages, translations, kobo_support): content.role = constants.selected_roles(to_save) try: - content.password = generate_password_hash(helper.valid_password(to_save["password"])) if not to_save["name"] or not to_save["email"] or not to_save["password"]: log.info("Missing entries on new user") raise Exception(_(u"Please fill out all fields!")) + content.password = generate_password_hash(helper.valid_password(to_save.get("password", ""))) content.email = check_email(to_save["email"]) # Query username, if not existing, change content.name = check_username(to_save["name"]) @@ -1936,8 +1936,6 @@ def _handle_edit_user(to_save, content, languages, translations, kobo_support): log.warning("No admin user remaining, can't remove admin role from {}".format(content.name)) flash(_("No admin user remaining, can't remove admin role"), category="error") return redirect(url_for('admin.admin')) - if 'password' in to_save: - content.password = generate_password_hash(helper.valid_password(to_save('password'))) anonymous = content.is_anonymous content.role = constants.selected_roles(to_save) if anonymous: @@ -1971,6 +1969,8 @@ def _handle_edit_user(to_save, content, languages, translations, kobo_support): if to_save.get("locale"): content.locale = to_save["locale"] try: + if to_save.get('password', "") != "": + content.password = generate_password_hash(helper.valid_password(to_save['password'])) if to_save.get("email", content.email) != content.email: content.email = check_email(to_save["email"]) # Query username, if not existing, change diff --git a/cps/web.py b/cps/web.py index 58921bfe..a623a652 100755 --- a/cps/web.py +++ b/cps/web.py @@ -1358,8 +1358,8 @@ def change_profile(kobo_support, local_oauth_check, oauth_status, translations, current_user.random_books = 0 try: if current_user.role_passwd() or current_user.role_admin(): - if 'password' in to_save: - current_user.password = generate_password_hash(valid_password(to_save('password'))) + if to_save.get('password', "") != "": + current_user.password = generate_password_hash(valid_password(to_save['password'])) if to_save.get("kindle_mail", current_user.kindle_mail) != current_user.kindle_mail: current_user.kindle_mail = valid_email(to_save.get("kindle_mail")) if to_save.get("email", current_user.email) != current_user.email: diff --git a/setup.cfg b/setup.cfg index 92e64f6a..c24dfc97 100644 --- a/setup.cfg +++ b/setup.cfg @@ -97,5 +97,5 @@ comics = natsort>=2.2.0,<8.2.0 comicapi>=2.2.0,<2.3.0 kobo = - jsonschema>=3.2.0,<4.6.0 + jsonschema>=3.2.0,<4.7.0 diff --git a/test/Calibre-Web TestSummary_Linux.html b/test/Calibre-Web TestSummary_Linux.html index 8953c08c..f12ab46f 100644 --- a/test/Calibre-Web TestSummary_Linux.html +++ b/test/Calibre-Web TestSummary_Linux.html @@ -37,20 +37,20 @@
      -

      Start Time: 2022-06-05 22:14:00

      +

      Start Time: 2022-06-16 20:34:19

      -

      Stop Time: 2022-06-06 04:06:43

      +

      Stop Time: 2022-06-17 01:15:22

      -

      Duration: 4h 57 min

      +

      Duration: 3h 51 min

      @@ -467,58 +467,300 @@ TestEbookConvertCalibre - 15 - 11 - 3 - 1 + 30 + 0 + 0 + 30 0 - Detail + Detail - +
      TestEbookConvertCalibre - test_calibre_log
      - PASS - - - - - - -
      TestEbookConvertCalibre - test_convert_deactivate
      - - PASS - - - - - - -
      TestEbookConvertCalibre - test_convert_email
      -
      - FAIL + ERROR
      - @@ -559,37 +867,652 @@ selenium.common.exceptions.NoSuchElementException: Message: Could not locate ele - + -
      TestEbookConvertCalibre - test_convert_only
      +
      TestEbookConvertCalibre - test_convert_email
      + + +
      + ERROR +
      + + + - PASS - + + +
      TestEbookConvertCalibre - test_convert_email
      + + +
      + ERROR +
      + + + + + + + + + + +
      TestEbookConvertCalibre - test_convert_failed_and_email
      + + +
      + ERROR +
      + + + + + + + + + + +
      TestEbookConvertCalibre - test_convert_failed_and_email
      + + +
      + ERROR +
      + + + + + + + + + + +
      TestEbookConvertCalibre - test_convert_only
      + + +
      + ERROR +
      + + + + + + + + + + +
      TestEbookConvertCalibre - test_convert_only
      + + +
      + ERROR +
      + + + + + + + + +
      TestEbookConvertCalibre - test_convert_options
      - FAIL + ERROR
      -