1
0
Fork 0

Change the handling of login errors.

Previously FlashMessage was used to provide an error message during login.
This PR changes that flow to not use redirect for this, but renders the HTML and responds using the correct status code where needed. This should solve some issues which were reported in the past.

Thanks to @RealOrangeOne, for initiating this with a PR.

Fixes #2448
Fixes #2712
Closes #2715

Co-authored-by: Jake Howard <git@theorangeone.net>
Dieser Commit ist enthalten in:
BlackDex 2022-09-06 17:14:16 +02:00
Ursprung a62dc102fb
Commit 5a05139efe
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 58C80A2AA6C765E1
6 geänderte Dateien mit 262 neuen und 1205 gelöschten Zeilen

Datei anzeigen

@ -7,8 +7,8 @@ use rocket::serde::json::Json;
use rocket::{ use rocket::{
form::Form, form::Form,
http::{Cookie, CookieJar, SameSite, Status}, http::{Cookie, CookieJar, SameSite, Status},
request::{self, FlashMessage, FromRequest, Outcome, Request}, request::{self, FromRequest, Outcome, Request},
response::{content::RawHtml as Html, Flash, Redirect}, response::{content::RawHtml as Html, Redirect},
Route, Route,
}; };
@ -141,10 +141,24 @@ fn admin_url(referer: Referer) -> String {
} }
} }
#[derive(Responder)]
enum AdminResponse {
#[response(status = 200)]
Ok(ApiResult<Html<String>>),
#[response(status = 401)]
Unauthorized(ApiResult<Html<String>>),
#[response(status = 429)]
TooManyRequests(ApiResult<Html<String>>),
}
#[get("/", rank = 2)] #[get("/", rank = 2)]
fn admin_login(flash: Option<FlashMessage<'_>>) -> ApiResult<Html<String>> { fn admin_login() -> ApiResult<Html<String>> {
render_admin_login(None)
}
fn render_admin_login(msg: Option<&str>) -> ApiResult<Html<String>> {
// If there is an error, show it // If there is an error, show it
let msg = flash.map(|msg| format!("{}: {}", msg.kind(), msg.message())); let msg = msg.map(|msg| format!("Error: {msg}"));
let json = json!({ let json = json!({
"page_content": "admin/login", "page_content": "admin/login",
"version": VERSION, "version": VERSION,
@ -163,22 +177,17 @@ struct LoginForm {
} }
#[post("/", data = "<data>")] #[post("/", data = "<data>")]
fn post_admin_login( fn post_admin_login(data: Form<LoginForm>, cookies: &CookieJar<'_>, ip: ClientIp) -> AdminResponse {
data: Form<LoginForm>,
cookies: &CookieJar<'_>,
ip: ClientIp,
referer: Referer,
) -> Result<Redirect, Flash<Redirect>> {
let data = data.into_inner(); let data = data.into_inner();
if crate::ratelimit::check_limit_admin(&ip.ip).is_err() { if crate::ratelimit::check_limit_admin(&ip.ip).is_err() {
return Err(Flash::error(Redirect::to(admin_url(referer)), "Too many requests, try again later.")); return AdminResponse::TooManyRequests(render_admin_login(Some("Too many requests, try again later.")));
} }
// If the token is invalid, redirect to login page // If the token is invalid, redirect to login page
if !_validate_token(&data.token) { if !_validate_token(&data.token) {
error!("Invalid admin token. IP: {}", ip.ip); error!("Invalid admin token. IP: {}", ip.ip);
Err(Flash::error(Redirect::to(admin_url(referer)), "Invalid admin token, please try again.")) AdminResponse::Unauthorized(render_admin_login(Some("Invalid admin token, please try again.")))
} else { } else {
// If the token received is valid, generate JWT and save it as a cookie // If the token received is valid, generate JWT and save it as a cookie
let claims = generate_admin_claims(); let claims = generate_admin_claims();
@ -192,7 +201,7 @@ fn post_admin_login(
.finish(); .finish();
cookies.add(cookie); cookies.add(cookie);
Ok(Redirect::to(admin_url(referer))) AdminResponse::Ok(render_admin_page())
} }
} }
@ -244,12 +253,16 @@ impl AdminTemplateData {
} }
} }
#[get("/", rank = 1)] fn render_admin_page() -> ApiResult<Html<String>> {
fn admin_page(_token: AdminToken) -> ApiResult<Html<String>> {
let text = AdminTemplateData::new().render()?; let text = AdminTemplateData::new().render()?;
Ok(Html(text)) Ok(Html(text))
} }
#[get("/", rank = 1)]
fn admin_page(_token: AdminToken) -> ApiResult<Html<String>> {
render_admin_page()
}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
struct InviteData { struct InviteData {
@ -303,7 +316,7 @@ async fn test_smtp(data: Json<InviteData>, _token: AdminToken) -> EmptyResult {
#[get("/logout")] #[get("/logout")]
fn logout(cookies: &CookieJar<'_>, referer: Referer) -> Redirect { fn logout(cookies: &CookieJar<'_>, referer: Referer) -> Redirect {
cookies.remove(Cookie::build(COOKIE_NAME, "").path(admin_path()).finish()); cookies.remove(Cookie::build(COOKIE_NAME, "").path(admin_path()).finish());
Redirect::to(admin_url(referer)) Redirect::temporary(admin_url(referer))
} }
#[get("/users")] #[get("/users")]
@ -509,7 +522,6 @@ use cached::proc_macro::cached;
async fn get_release_info(has_http_access: bool, running_within_docker: bool) -> (String, String, String) { async fn get_release_info(has_http_access: bool, running_within_docker: bool) -> (String, String, String) {
// If the HTTP Check failed, do not even attempt to check for new versions since we were not able to connect with github.com anyway. // If the HTTP Check failed, do not even attempt to check for new versions since we were not able to connect with github.com anyway.
if has_http_access { if has_http_access {
info!("Running get_release_info!!");
( (
match get_github_api::<GitRelease>("https://api.github.com/repos/dani-garcia/vaultwarden/releases/latest") match get_github_api::<GitRelease>("https://api.github.com/repos/dani-garcia/vaultwarden/releases/latest")
.await .await

Datei anzeigen

@ -88,8 +88,8 @@ fn static_files(filename: String) -> Result<(ContentType, &'static [u8]), Error>
"identicon.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/identicon.js"))), "identicon.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/identicon.js"))),
"datatables.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/datatables.js"))), "datatables.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/datatables.js"))),
"datatables.css" => Ok((ContentType::CSS, include_bytes!("../static/scripts/datatables.css"))), "datatables.css" => Ok((ContentType::CSS, include_bytes!("../static/scripts/datatables.css"))),
"jquery-3.6.0.slim.js" => { "jquery-3.6.1.slim.js" => {
Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.6.0.slim.js"))) Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.6.1.slim.js")))
} }
_ => err!(format!("Static file not found: {}", filename)), _ => err!(format!("Static file not found: {}", filename)),
} }

Datei-Diff unterdrückt, da er zu groß ist Diff laden

Datei anzeigen

@ -1,5 +1,5 @@
/*! /*!
* jQuery JavaScript Library v3.6.0 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector * jQuery JavaScript Library v3.6.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector
* https://jquery.com/ * https://jquery.com/
* *
* Includes Sizzle.js * Includes Sizzle.js
@ -9,7 +9,7 @@
* Released under the MIT license * Released under the MIT license
* https://jquery.org/license * https://jquery.org/license
* *
* Date: 2021-03-02T17:08Z * Date: 2022-08-26T17:52Z
*/ */
( function( global, factory ) { ( function( global, factory ) {
@ -23,7 +23,7 @@
// (such as Node.js), expose a factory as module.exports. // (such as Node.js), expose a factory as module.exports.
// This accentuates the need for the creation of a real `window`. // This accentuates the need for the creation of a real `window`.
// e.g. var jQuery = require("jquery")(window); // e.g. var jQuery = require("jquery")(window);
// See ticket #14549 for more info. // See ticket trac-14549 for more info.
module.exports = global.document ? module.exports = global.document ?
factory( global, true ) : factory( global, true ) :
function( w ) { function( w ) {
@ -151,7 +151,7 @@ function toType( obj ) {
var var
version = "3.6.0 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector", version = "3.6.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/Tween,-effects/animatedSelector",
// Define a local copy of jQuery // Define a local copy of jQuery
jQuery = function( selector, context ) { jQuery = function( selector, context ) {
@ -3129,8 +3129,8 @@ jQuery.fn.extend( {
var rootjQuery, var rootjQuery,
// A simple way to check for HTML strings // A simple way to check for HTML strings
// Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Prioritize #id over <tag> to avoid XSS via location.hash (trac-9521)
// Strict HTML recognition (#11290: must start with <) // Strict HTML recognition (trac-11290: must start with <)
// Shortcut simple #id case for speed // Shortcut simple #id case for speed
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
@ -4087,7 +4087,7 @@ jQuery.extend( {
isReady: false, isReady: false,
// A counter to track how many items to wait for before // A counter to track how many items to wait for before
// the ready event fires. See #6781 // the ready event fires. See trac-6781
readyWait: 1, readyWait: 1,
// Handle when the DOM is ready // Handle when the DOM is ready
@ -4215,7 +4215,7 @@ function fcamelCase( _all, letter ) {
// Convert dashed to camelCase; used by the css and data modules // Convert dashed to camelCase; used by the css and data modules
// Support: IE <=9 - 11, Edge 12 - 15 // Support: IE <=9 - 11, Edge 12 - 15
// Microsoft forgot to hump their vendor prefix (#9572) // Microsoft forgot to hump their vendor prefix (trac-9572)
function camelCase( string ) { function camelCase( string ) {
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
} }
@ -4251,7 +4251,7 @@ Data.prototype = {
value = {}; value = {};
// We can accept data for non-element nodes in modern browsers, // We can accept data for non-element nodes in modern browsers,
// but we should not, see #8335. // but we should not, see trac-8335.
// Always return an empty object. // Always return an empty object.
if ( acceptData( owner ) ) { if ( acceptData( owner ) ) {
@ -4490,7 +4490,7 @@ jQuery.fn.extend( {
while ( i-- ) { while ( i-- ) {
// Support: IE 11 only // Support: IE 11 only
// The attrs elements can be null (#14894) // The attrs elements can be null (trac-14894)
if ( attrs[ i ] ) { if ( attrs[ i ] ) {
name = attrs[ i ].name; name = attrs[ i ].name;
if ( name.indexOf( "data-" ) === 0 ) { if ( name.indexOf( "data-" ) === 0 ) {
@ -4913,9 +4913,9 @@ var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
input = document.createElement( "input" ); input = document.createElement( "input" );
// Support: Android 4.0 - 4.3 only // Support: Android 4.0 - 4.3 only
// Check state lost if the name is set (#11217) // Check state lost if the name is set (trac-11217)
// Support: Windows Web Apps (WWA) // Support: Windows Web Apps (WWA)
// `name` and `type` must use .setAttribute for WWA (#14901) // `name` and `type` must use .setAttribute for WWA (trac-14901)
input.setAttribute( "type", "radio" ); input.setAttribute( "type", "radio" );
input.setAttribute( "checked", "checked" ); input.setAttribute( "checked", "checked" );
input.setAttribute( "name", "t" ); input.setAttribute( "name", "t" );
@ -4939,7 +4939,7 @@ var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
} )(); } )();
// We have to close these tags to support XHTML (#13200) // We have to close these tags to support XHTML (trac-13200)
var wrapMap = { var wrapMap = {
// XHTML parsers do not magically insert elements in the // XHTML parsers do not magically insert elements in the
@ -4965,7 +4965,7 @@ if ( !support.option ) {
function getAll( context, tag ) { function getAll( context, tag ) {
// Support: IE <=9 - 11 only // Support: IE <=9 - 11 only
// Use typeof to avoid zero-argument method invocation on host objects (#15151) // Use typeof to avoid zero-argument method invocation on host objects (trac-15151)
var ret; var ret;
if ( typeof context.getElementsByTagName !== "undefined" ) { if ( typeof context.getElementsByTagName !== "undefined" ) {
@ -5048,7 +5048,7 @@ function buildFragment( elems, context, scripts, selection, ignored ) {
// Remember the top-level container // Remember the top-level container
tmp = fragment.firstChild; tmp = fragment.firstChild;
// Ensure the created nodes are orphaned (#12392) // Ensure the created nodes are orphaned (trac-12392)
tmp.textContent = ""; tmp.textContent = "";
} }
} }
@ -5469,15 +5469,15 @@ jQuery.event = {
for ( ; cur !== this; cur = cur.parentNode || this ) { for ( ; cur !== this; cur = cur.parentNode || this ) {
// Don't check non-elements (#13208) // Don't check non-elements (trac-13208)
// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) // Don't process clicks on disabled elements (trac-6911, trac-8165, trac-11382, trac-11764)
if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
matchedHandlers = []; matchedHandlers = [];
matchedSelectors = {}; matchedSelectors = {};
for ( i = 0; i < delegateCount; i++ ) { for ( i = 0; i < delegateCount; i++ ) {
handleObj = handlers[ i ]; handleObj = handlers[ i ];
// Don't conflict with Object.prototype properties (#13203) // Don't conflict with Object.prototype properties (trac-13203)
sel = handleObj.selector + " "; sel = handleObj.selector + " ";
if ( matchedSelectors[ sel ] === undefined ) { if ( matchedSelectors[ sel ] === undefined ) {
@ -5731,7 +5731,7 @@ jQuery.Event = function( src, props ) {
// Create target properties // Create target properties
// Support: Safari <=6 - 7 only // Support: Safari <=6 - 7 only
// Target should not be a text node (#504, #13143) // Target should not be a text node (trac-504, trac-13143)
this.target = ( src.target && src.target.nodeType === 3 ) ? this.target = ( src.target && src.target.nodeType === 3 ) ?
src.target.parentNode : src.target.parentNode :
src.target; src.target;
@ -5854,10 +5854,10 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp
return true; return true;
}, },
// Suppress native focus or blur as it's already being fired // Suppress native focus or blur if we're currently inside
// in leverageNative. // a leveraged native-event stack
_default: function() { _default: function( event ) {
return true; return dataPriv.get( event.target, type );
}, },
delegateType: delegateType delegateType: delegateType
@ -5956,7 +5956,8 @@ var
// checked="checked" or checked // checked="checked" or checked
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
rcleanScript = /^\s*<!\[CDATA\[|\]\]>\s*$/g;
// Prefer a tbody over its parent table for containing new rows // Prefer a tbody over its parent table for containing new rows
function manipulationTarget( elem, content ) { function manipulationTarget( elem, content ) {
@ -6070,7 +6071,7 @@ function domManip( collection, args, callback, ignored ) {
// Use the original fragment for the last item // Use the original fragment for the last item
// instead of the first because it can end up // instead of the first because it can end up
// being emptied incorrectly in certain situations (#8070). // being emptied incorrectly in certain situations (trac-8070).
for ( ; i < l; i++ ) { for ( ; i < l; i++ ) {
node = fragment; node = fragment;
@ -6111,6 +6112,12 @@ function domManip( collection, args, callback, ignored ) {
}, doc ); }, doc );
} }
} else { } else {
// Unwrap a CDATA section containing script contents. This shouldn't be
// needed as in XML documents they're already not visible when
// inspecting element contents and in HTML documents they have no
// meaning but we're preserving that logic for backwards compatibility.
// This will be removed completely in 4.0. See gh-4904.
DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc );
} }
} }
@ -6393,9 +6400,12 @@ jQuery.each( {
} ); } );
var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
var rcustomProp = /^--/;
var getStyles = function( elem ) { var getStyles = function( elem ) {
// Support: IE <=11 only, Firefox <=30 (#15098, #14150) // Support: IE <=11 only, Firefox <=30 (trac-15098, trac-14150)
// IE throws on elements created in popups // IE throws on elements created in popups
// FF meanwhile throws on frame elements through "defaultView.getComputedStyle" // FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
var view = elem.ownerDocument.defaultView; var view = elem.ownerDocument.defaultView;
@ -6430,6 +6440,15 @@ var swap = function( elem, options, callback ) {
var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
var whitespace = "[\\x20\\t\\r\\n\\f]";
var rtrimCSS = new RegExp(
"^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$",
"g"
);
( function() { ( function() {
@ -6495,7 +6514,7 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
} }
// Support: IE <=9 - 11 only // Support: IE <=9 - 11 only
// Style of cloned element affects source element cloned (#8908) // Style of cloned element affects source element cloned (trac-8908)
div.style.backgroundClip = "content-box"; div.style.backgroundClip = "content-box";
div.cloneNode( true ).style.backgroundClip = ""; div.cloneNode( true ).style.backgroundClip = "";
support.clearCloneStyle = div.style.backgroundClip === "content-box"; support.clearCloneStyle = div.style.backgroundClip === "content-box";
@ -6575,6 +6594,7 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
function curCSS( elem, name, computed ) { function curCSS( elem, name, computed ) {
var width, minWidth, maxWidth, ret, var width, minWidth, maxWidth, ret,
isCustomProp = rcustomProp.test( name ),
// Support: Firefox 51+ // Support: Firefox 51+
// Retrieving style before computed somehow // Retrieving style before computed somehow
@ -6585,11 +6605,22 @@ function curCSS( elem, name, computed ) {
computed = computed || getStyles( elem ); computed = computed || getStyles( elem );
// getPropertyValue is needed for: // getPropertyValue is needed for:
// .css('filter') (IE 9 only, #12537) // .css('filter') (IE 9 only, trac-12537)
// .css('--customProperty) (#3144) // .css('--customProperty) (gh-3144)
if ( computed ) { if ( computed ) {
ret = computed.getPropertyValue( name ) || computed[ name ]; ret = computed.getPropertyValue( name ) || computed[ name ];
// trim whitespace for custom property (issue gh-4926)
if ( isCustomProp ) {
// rtrim treats U+000D CARRIAGE RETURN and U+000C FORM FEED
// as whitespace while CSS does not, but this is not a problem
// because CSS preprocessing replaces them with U+000A LINE FEED
// (which *is* CSS whitespace)
// https://www.w3.org/TR/css-syntax-3/#input-preprocessing
ret = ret.replace( rtrimCSS, "$1" );
}
if ( ret === "" && !isAttached( elem ) ) { if ( ret === "" && !isAttached( elem ) ) {
ret = jQuery.style( elem, name ); ret = jQuery.style( elem, name );
} }
@ -6685,7 +6716,6 @@ var
// except "table", "table-cell", or "table-caption" // except "table", "table-cell", or "table-caption"
// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
rdisplayswap = /^(none|table(?!-c[ea]).+)/, rdisplayswap = /^(none|table(?!-c[ea]).+)/,
rcustomProp = /^--/,
cssShow = { position: "absolute", visibility: "hidden", display: "block" }, cssShow = { position: "absolute", visibility: "hidden", display: "block" },
cssNormalTransform = { cssNormalTransform = {
letterSpacing: "0", letterSpacing: "0",
@ -6921,15 +6951,15 @@ jQuery.extend( {
if ( value !== undefined ) { if ( value !== undefined ) {
type = typeof value; type = typeof value;
// Convert "+=" or "-=" to relative numbers (#7345) // Convert "+=" or "-=" to relative numbers (trac-7345)
if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
value = adjustCSS( elem, name, ret ); value = adjustCSS( elem, name, ret );
// Fixes bug #9237 // Fixes bug trac-9237
type = "number"; type = "number";
} }
// Make sure that null and NaN values aren't set (#7116) // Make sure that null and NaN values aren't set (trac-7116)
if ( value == null || value !== value ) { if ( value == null || value !== value ) {
return; return;
} }
@ -7149,7 +7179,6 @@ jQuery.fn.extend( {
// Based off of the plugin by Clint Helfers, with permission. // Based off of the plugin by Clint Helfers, with permission.
// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
jQuery.fn.delay = function( time, type ) { jQuery.fn.delay = function( time, type ) {
time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
type = type || "fx"; type = type || "fx";
@ -7374,8 +7403,7 @@ jQuery.extend( {
// Support: IE <=9 - 11 only // Support: IE <=9 - 11 only
// elem.tabIndex doesn't always return the // elem.tabIndex doesn't always return the
// correct value when it hasn't been explicitly set // correct value when it hasn't been explicitly set
// https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ // Use proper attribute retrieval (trac-12072)
// Use proper attribute retrieval(#12072)
var tabindex = jQuery.find.attr( elem, "tabindex" ); var tabindex = jQuery.find.attr( elem, "tabindex" );
if ( tabindex ) { if ( tabindex ) {
@ -7479,8 +7507,7 @@ function classesToArray( value ) {
jQuery.fn.extend( { jQuery.fn.extend( {
addClass: function( value ) { addClass: function( value ) {
var classes, elem, cur, curValue, clazz, j, finalValue, var classNames, cur, curValue, className, i, finalValue;
i = 0;
if ( isFunction( value ) ) { if ( isFunction( value ) ) {
return this.each( function( j ) { return this.each( function( j ) {
@ -7488,36 +7515,35 @@ jQuery.fn.extend( {
} ); } );
} }
classes = classesToArray( value ); classNames = classesToArray( value );
if ( classes.length ) { if ( classNames.length ) {
while ( ( elem = this[ i++ ] ) ) { return this.each( function() {
curValue = getClass( elem ); curValue = getClass( this );
cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
if ( cur ) { if ( cur ) {
j = 0; for ( i = 0; i < classNames.length; i++ ) {
while ( ( clazz = classes[ j++ ] ) ) { className = classNames[ i ];
if ( cur.indexOf( " " + clazz + " " ) < 0 ) { if ( cur.indexOf( " " + className + " " ) < 0 ) {
cur += clazz + " "; cur += className + " ";
} }
} }
// Only assign if different to avoid unneeded rendering. // Only assign if different to avoid unneeded rendering.
finalValue = stripAndCollapse( cur ); finalValue = stripAndCollapse( cur );
if ( curValue !== finalValue ) { if ( curValue !== finalValue ) {
elem.setAttribute( "class", finalValue ); this.setAttribute( "class", finalValue );
} }
} }
} } );
} }
return this; return this;
}, },
removeClass: function( value ) { removeClass: function( value ) {
var classes, elem, cur, curValue, clazz, j, finalValue, var classNames, cur, curValue, className, i, finalValue;
i = 0;
if ( isFunction( value ) ) { if ( isFunction( value ) ) {
return this.each( function( j ) { return this.each( function( j ) {
@ -7529,45 +7555,42 @@ jQuery.fn.extend( {
return this.attr( "class", "" ); return this.attr( "class", "" );
} }
classes = classesToArray( value ); classNames = classesToArray( value );
if ( classes.length ) { if ( classNames.length ) {
while ( ( elem = this[ i++ ] ) ) { return this.each( function() {
curValue = getClass( elem ); curValue = getClass( this );
// This expression is here for better compressibility (see addClass) // This expression is here for better compressibility (see addClass)
cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
if ( cur ) { if ( cur ) {
j = 0; for ( i = 0; i < classNames.length; i++ ) {
while ( ( clazz = classes[ j++ ] ) ) { className = classNames[ i ];
// Remove *all* instances // Remove *all* instances
while ( cur.indexOf( " " + clazz + " " ) > -1 ) { while ( cur.indexOf( " " + className + " " ) > -1 ) {
cur = cur.replace( " " + clazz + " ", " " ); cur = cur.replace( " " + className + " ", " " );
} }
} }
// Only assign if different to avoid unneeded rendering. // Only assign if different to avoid unneeded rendering.
finalValue = stripAndCollapse( cur ); finalValue = stripAndCollapse( cur );
if ( curValue !== finalValue ) { if ( curValue !== finalValue ) {
elem.setAttribute( "class", finalValue ); this.setAttribute( "class", finalValue );
} }
} }
} } );
} }
return this; return this;
}, },
toggleClass: function( value, stateVal ) { toggleClass: function( value, stateVal ) {
var type = typeof value, var classNames, className, i, self,
type = typeof value,
isValidValue = type === "string" || Array.isArray( value ); isValidValue = type === "string" || Array.isArray( value );
if ( typeof stateVal === "boolean" && isValidValue ) {
return stateVal ? this.addClass( value ) : this.removeClass( value );
}
if ( isFunction( value ) ) { if ( isFunction( value ) ) {
return this.each( function( i ) { return this.each( function( i ) {
jQuery( this ).toggleClass( jQuery( this ).toggleClass(
@ -7577,17 +7600,20 @@ jQuery.fn.extend( {
} ); } );
} }
return this.each( function() { if ( typeof stateVal === "boolean" && isValidValue ) {
var className, i, self, classNames; return stateVal ? this.addClass( value ) : this.removeClass( value );
}
classNames = classesToArray( value );
return this.each( function() {
if ( isValidValue ) { if ( isValidValue ) {
// Toggle individual class names // Toggle individual class names
i = 0;
self = jQuery( this ); self = jQuery( this );
classNames = classesToArray( value );
while ( ( className = classNames[ i++ ] ) ) { for ( i = 0; i < classNames.length; i++ ) {
className = classNames[ i ];
// Check each className given, space separated list // Check each className given, space separated list
if ( self.hasClass( className ) ) { if ( self.hasClass( className ) ) {
@ -7721,7 +7747,7 @@ jQuery.extend( {
val : val :
// Support: IE <=10 - 11 only // Support: IE <=10 - 11 only
// option.text throws exceptions (#14686, #14858) // option.text throws exceptions (trac-14686, trac-14858)
// Strip and collapse whitespace // Strip and collapse whitespace
// https://html.spec.whatwg.org/#strip-and-collapse-whitespace // https://html.spec.whatwg.org/#strip-and-collapse-whitespace
stripAndCollapse( jQuery.text( elem ) ); stripAndCollapse( jQuery.text( elem ) );
@ -7748,7 +7774,7 @@ jQuery.extend( {
option = options[ i ]; option = options[ i ];
// Support: IE <=9 only // Support: IE <=9 only
// IE8-9 doesn't update selected after form reset (#2551) // IE8-9 doesn't update selected after form reset (trac-2551)
if ( ( option.selected || i === index ) && if ( ( option.selected || i === index ) &&
// Don't return options that are disabled or in a disabled optgroup // Don't return options that are disabled or in a disabled optgroup
@ -7891,8 +7917,8 @@ jQuery.extend( jQuery.event, {
return; return;
} }
// Determine event propagation path in advance, per W3C events spec (#9951) // Determine event propagation path in advance, per W3C events spec (trac-9951)
// Bubble up to document, then to window; watch for a global ownerDocument var (#9724) // Bubble up to document, then to window; watch for a global ownerDocument var (trac-9724)
if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {
bubbleType = special.delegateType || type; bubbleType = special.delegateType || type;
@ -7944,7 +7970,7 @@ jQuery.extend( jQuery.event, {
acceptData( elem ) ) { acceptData( elem ) ) {
// Call a native DOM method on the target with the same name as the event. // Call a native DOM method on the target with the same name as the event.
// Don't do default actions on window, that's where global variables be (#6170) // Don't do default actions on window, that's where global variables be (trac-6170)
if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {
// Don't re-trigger an onFOO event when we call its FOO() method // Don't re-trigger an onFOO event when we call its FOO() method
@ -8654,7 +8680,9 @@ jQuery.each(
// Support: Android <=4.0 only // Support: Android <=4.0 only
// Make sure we trim BOM and NBSP // Make sure we trim BOM and NBSP
var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; // Require that the "whitespace run" starts from a non-whitespace
// to avoid O(N^2) behavior when the engine would try matching "\s+$" at each space position.
var rtrim = /^[\s\uFEFF\xA0]+|([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/g;
// Bind a function to a context, optionally partially applying any // Bind a function to a context, optionally partially applying any
// arguments. // arguments.
@ -8721,7 +8749,7 @@ jQuery.isNumeric = function( obj ) {
jQuery.trim = function( text ) { jQuery.trim = function( text ) {
return text == null ? return text == null ?
"" : "" :
( text + "" ).replace( rtrim, "" ); ( text + "" ).replace( rtrim, "$1" );
}; };
@ -8769,8 +8797,8 @@ jQuery.noConflict = function( deep ) {
}; };
// Expose jQuery and $ identifiers, even in AMD // Expose jQuery and $ identifiers, even in AMD
// (#7102#comment:10, https://github.com/jquery/jquery/pull/557) // (trac-7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566) // and CommonJS for browser emulators (trac-13566)
if ( typeof noGlobal === "undefined" ) { if ( typeof noGlobal === "undefined" ) {
window.jQuery = window.$ = jQuery; window.jQuery = window.$ = jQuery;
} }

Datei anzeigen

@ -49,7 +49,7 @@
</main> </main>
<link rel="stylesheet" href="{{urlpath}}/vw_static/datatables.css" /> <link rel="stylesheet" href="{{urlpath}}/vw_static/datatables.css" />
<script src="{{urlpath}}/vw_static/jquery-3.6.0.slim.js"></script> <script src="{{urlpath}}/vw_static/jquery-3.6.1.slim.js"></script>
<script src="{{urlpath}}/vw_static/datatables.js"></script> <script src="{{urlpath}}/vw_static/datatables.js"></script>
<script> <script>
'use strict'; 'use strict';

Datei anzeigen

@ -136,7 +136,7 @@
</main> </main>
<link rel="stylesheet" href="{{urlpath}}/vw_static/datatables.css" /> <link rel="stylesheet" href="{{urlpath}}/vw_static/datatables.css" />
<script src="{{urlpath}}/vw_static/jquery-3.6.0.slim.js"></script> <script src="{{urlpath}}/vw_static/jquery-3.6.1.slim.js"></script>
<script src="{{urlpath}}/vw_static/datatables.js"></script> <script src="{{urlpath}}/vw_static/datatables.js"></script>
<script> <script>
'use strict'; 'use strict';