updated core to 7.58, security update
This commit is contained in:
+34
-6
@@ -14,6 +14,8 @@
|
||||
|
||||
Drupal.ajax = Drupal.ajax || {};
|
||||
|
||||
Drupal.settings.urlIsAjaxTrusted = Drupal.settings.urlIsAjaxTrusted || {};
|
||||
|
||||
/**
|
||||
* Attaches the Ajax behavior to each Ajax form element.
|
||||
*/
|
||||
@@ -130,6 +132,11 @@ Drupal.ajax = function (base, element, element_settings) {
|
||||
// 5. /nojs# - Followed by a fragment.
|
||||
// E.g.: path/nojs#myfragment
|
||||
this.url = element_settings.url.replace(/\/nojs(\/|$|\?|&|#)/g, '/ajax$1');
|
||||
// If the 'nojs' version of the URL is trusted, also trust the 'ajax' version.
|
||||
if (Drupal.settings.urlIsAjaxTrusted[element_settings.url]) {
|
||||
Drupal.settings.urlIsAjaxTrusted[this.url] = true;
|
||||
}
|
||||
|
||||
this.wrapper = '#' + element_settings.wrapper;
|
||||
|
||||
// If there isn't a form, jQuery.ajax() will be used instead, allowing us to
|
||||
@@ -155,18 +162,36 @@ Drupal.ajax = function (base, element, element_settings) {
|
||||
ajax.ajaxing = true;
|
||||
return ajax.beforeSend(xmlhttprequest, options);
|
||||
},
|
||||
success: function (response, status) {
|
||||
success: function (response, status, xmlhttprequest) {
|
||||
// Sanity check for browser support (object expected).
|
||||
// When using iFrame uploads, responses must be returned as a string.
|
||||
if (typeof response == 'string') {
|
||||
response = $.parseJSON(response);
|
||||
}
|
||||
|
||||
// Prior to invoking the response's commands, verify that they can be
|
||||
// trusted by checking for a response header. See
|
||||
// ajax_set_verification_header() for details.
|
||||
// - Empty responses are harmless so can bypass verification. This avoids
|
||||
// an alert message for server-generated no-op responses that skip Ajax
|
||||
// rendering.
|
||||
// - Ajax objects with trusted URLs (e.g., ones defined server-side via
|
||||
// #ajax) can bypass header verification. This is especially useful for
|
||||
// Ajax with multipart forms. Because IFRAME transport is used, the
|
||||
// response headers cannot be accessed for verification.
|
||||
if (response !== null && !Drupal.settings.urlIsAjaxTrusted[ajax.url]) {
|
||||
if (xmlhttprequest.getResponseHeader('X-Drupal-Ajax-Token') !== '1') {
|
||||
var customMessage = Drupal.t("The response failed verification so will not be processed.");
|
||||
return ajax.error(xmlhttprequest, ajax.url, customMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return ajax.success(response, status);
|
||||
},
|
||||
complete: function (response, status) {
|
||||
complete: function (xmlhttprequest, status) {
|
||||
ajax.ajaxing = false;
|
||||
if (status == 'error' || status == 'parsererror') {
|
||||
return ajax.error(response, ajax.url);
|
||||
return ajax.error(xmlhttprequest, ajax.url);
|
||||
}
|
||||
},
|
||||
dataType: 'json',
|
||||
@@ -175,6 +200,9 @@ Drupal.ajax = function (base, element, element_settings) {
|
||||
|
||||
// Bind the ajaxSubmit function to the element event.
|
||||
$(ajax.element).bind(element_settings.event, function (event) {
|
||||
if (!Drupal.settings.urlIsAjaxTrusted[ajax.url] && !Drupal.urlIsLocal(ajax.url)) {
|
||||
throw new Error(Drupal.t('The callback URL is not local and not trusted: !url', {'!url': ajax.url}));
|
||||
}
|
||||
return ajax.eventResponse(this, event);
|
||||
});
|
||||
|
||||
@@ -447,8 +475,8 @@ Drupal.ajax.prototype.getEffect = function (response) {
|
||||
/**
|
||||
* Handler for the form redirection error.
|
||||
*/
|
||||
Drupal.ajax.prototype.error = function (response, uri) {
|
||||
alert(Drupal.ajaxError(response, uri));
|
||||
Drupal.ajax.prototype.error = function (xmlhttprequest, uri, customMessage) {
|
||||
Drupal.displayAjaxError(Drupal.ajaxError(xmlhttprequest, uri, customMessage));
|
||||
// Remove the progress element.
|
||||
if (this.progress.element) {
|
||||
$(this.progress.element).remove();
|
||||
@@ -462,7 +490,7 @@ Drupal.ajax.prototype.error = function (response, uri) {
|
||||
$(this.element).removeClass('progress-disabled').removeAttr('disabled');
|
||||
// Reattach behaviors, if they were detached in beforeSerialize().
|
||||
if (this.form) {
|
||||
var settings = response.settings || this.settings || Drupal.settings;
|
||||
var settings = this.settings || Drupal.settings;
|
||||
Drupal.attachBehaviors(this.form, settings);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -114,6 +114,7 @@ Drupal.jsAC.prototype.onkeyup = function (input, e) {
|
||||
*/
|
||||
Drupal.jsAC.prototype.select = function (node) {
|
||||
this.input.value = $(node).data('autocompleteValue');
|
||||
$(this.input).trigger('autocompleteSelect', [node]);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -167,7 +168,7 @@ Drupal.jsAC.prototype.unhighlight = function (node) {
|
||||
Drupal.jsAC.prototype.hidePopup = function (keycode) {
|
||||
// Select item if the right key or mousebutton was pressed.
|
||||
if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
|
||||
this.input.value = $(this.selected).data('autocompleteValue');
|
||||
this.select(this.selected);
|
||||
}
|
||||
// Hide popup.
|
||||
var popup = this.popup;
|
||||
@@ -220,7 +221,7 @@ Drupal.jsAC.prototype.found = function (matches) {
|
||||
for (key in matches) {
|
||||
$('<li></li>')
|
||||
.html($('<div></div>').html(matches[key]))
|
||||
.mousedown(function () { ac.select(this); })
|
||||
.mousedown(function () { ac.hidePopup(this); })
|
||||
.mouseover(function () { ac.highlight(this); })
|
||||
.mouseout(function () { ac.unhighlight(this); })
|
||||
.data('autocompleteValue', key)
|
||||
@@ -270,8 +271,11 @@ Drupal.ACDB.prototype.search = function (searchString) {
|
||||
var db = this;
|
||||
this.searchString = searchString;
|
||||
|
||||
// See if this string needs to be searched for anyway.
|
||||
searchString = searchString.replace(/^\s+|\s+$/, '');
|
||||
// See if this string needs to be searched for anyway. The pattern ../ is
|
||||
// stripped since it may be misinterpreted by the browser.
|
||||
searchString = searchString.replace(/^\s+|\.{2,}\/|\s+$/g, '');
|
||||
// Skip empty search strings, or search strings ending with a comma, since
|
||||
// that is the separator between search terms.
|
||||
if (searchString.length <= 0 ||
|
||||
searchString.charAt(searchString.length - 1) == ',') {
|
||||
return;
|
||||
@@ -306,7 +310,7 @@ Drupal.ACDB.prototype.search = function (searchString) {
|
||||
}
|
||||
},
|
||||
error: function (xmlhttp) {
|
||||
alert(Drupal.ajaxError(xmlhttp, db.uri));
|
||||
Drupal.displayAjaxError(Drupal.ajaxError(xmlhttp, db.uri));
|
||||
}
|
||||
});
|
||||
}, this.delay);
|
||||
|
||||
+200
-19
@@ -27,6 +27,42 @@ $.fn.init = function (selector, context, rootjQuery) {
|
||||
};
|
||||
$.fn.init.prototype = jquery_init.prototype;
|
||||
|
||||
/**
|
||||
* Pre-filter Ajax requests to guard against XSS attacks.
|
||||
*
|
||||
* See https://github.com/jquery/jquery/issues/2432
|
||||
*/
|
||||
if ($.ajaxPrefilter) {
|
||||
// For newer versions of jQuery, use an Ajax prefilter to prevent
|
||||
// auto-executing script tags from untrusted domains. This is similar to the
|
||||
// fix that is built in to jQuery 3.0 and higher.
|
||||
$.ajaxPrefilter(function (s) {
|
||||
if (s.crossDomain) {
|
||||
s.contents.script = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if ($.httpData) {
|
||||
// For the version of jQuery that ships with Drupal core, override
|
||||
// jQuery.httpData to prevent auto-detecting "script" data types from
|
||||
// untrusted domains.
|
||||
var jquery_httpData = $.httpData;
|
||||
$.httpData = function (xhr, type, s) {
|
||||
// @todo Consider backporting code from newer jQuery versions to check for
|
||||
// a cross-domain request here, rather than using Drupal.urlIsLocal() to
|
||||
// block scripts from all URLs that are not on the same site.
|
||||
if (!type && !Drupal.urlIsLocal(s.url)) {
|
||||
var content_type = xhr.getResponseHeader('content-type') || '';
|
||||
if (content_type.indexOf('javascript') >= 0) {
|
||||
// Default to a safe data type.
|
||||
type = 'text';
|
||||
}
|
||||
}
|
||||
return jquery_httpData.call(this, xhr, type, s);
|
||||
};
|
||||
$.httpData.prototype = jquery_httpData.prototype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach all registered behaviors to a page element.
|
||||
*
|
||||
@@ -137,7 +173,7 @@ Drupal.detachBehaviors = function (context, settings, trigger) {
|
||||
*/
|
||||
Drupal.checkPlain = function (str) {
|
||||
var character, regex,
|
||||
replace = { '&': '&', '"': '"', '<': '<', '>': '>' };
|
||||
replace = { '&': '&', "'": ''', '"': '"', '<': '<', '>': '>' };
|
||||
str = String(str);
|
||||
for (character in replace) {
|
||||
if (replace.hasOwnProperty(character)) {
|
||||
@@ -168,23 +204,76 @@ Drupal.checkPlain = function (str) {
|
||||
Drupal.formatString = function(str, args) {
|
||||
// Transform arguments before inserting them.
|
||||
for (var key in args) {
|
||||
switch (key.charAt(0)) {
|
||||
// Escaped only.
|
||||
case '@':
|
||||
args[key] = Drupal.checkPlain(args[key]);
|
||||
break;
|
||||
// Pass-through.
|
||||
case '!':
|
||||
break;
|
||||
// Escaped and placeholder.
|
||||
case '%':
|
||||
default:
|
||||
args[key] = Drupal.theme('placeholder', args[key]);
|
||||
break;
|
||||
if (args.hasOwnProperty(key)) {
|
||||
switch (key.charAt(0)) {
|
||||
// Escaped only.
|
||||
case '@':
|
||||
args[key] = Drupal.checkPlain(args[key]);
|
||||
break;
|
||||
// Pass-through.
|
||||
case '!':
|
||||
break;
|
||||
// Escaped and placeholder.
|
||||
default:
|
||||
args[key] = Drupal.theme('placeholder', args[key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
str = str.replace(key, args[key]);
|
||||
}
|
||||
return str;
|
||||
|
||||
return Drupal.stringReplace(str, args, null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Replace substring.
|
||||
*
|
||||
* The longest keys will be tried first. Once a substring has been replaced,
|
||||
* its new value will not be searched again.
|
||||
*
|
||||
* @param {String} str
|
||||
* A string with placeholders.
|
||||
* @param {Object} args
|
||||
* Key-value pairs.
|
||||
* @param {Array|null} keys
|
||||
* Array of keys from the "args". Internal use only.
|
||||
*
|
||||
* @return {String}
|
||||
* Returns the replaced string.
|
||||
*/
|
||||
Drupal.stringReplace = function (str, args, keys) {
|
||||
if (str.length === 0) {
|
||||
return str;
|
||||
}
|
||||
|
||||
// If the array of keys is not passed then collect the keys from the args.
|
||||
if (!$.isArray(keys)) {
|
||||
keys = [];
|
||||
for (var k in args) {
|
||||
if (args.hasOwnProperty(k)) {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
|
||||
// Order the keys by the character length. The shortest one is the first.
|
||||
keys.sort(function (a, b) { return a.length - b.length; });
|
||||
}
|
||||
|
||||
if (keys.length === 0) {
|
||||
return str;
|
||||
}
|
||||
|
||||
// Take next longest one from the end.
|
||||
var key = keys.pop();
|
||||
var fragments = str.split(key);
|
||||
|
||||
if (keys.length) {
|
||||
for (var i = 0; i < fragments.length; i++) {
|
||||
// Process each fragment with a copy of remaining keys.
|
||||
fragments[i] = Drupal.stringReplace(fragments[i], args, keys.slice(0));
|
||||
}
|
||||
}
|
||||
|
||||
return fragments.join(args[key]);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -251,7 +340,7 @@ Drupal.t = function (str, args, options) {
|
||||
* A translated string.
|
||||
*/
|
||||
Drupal.formatPlural = function (count, singular, plural, args, options) {
|
||||
var args = args || {};
|
||||
args = args || {};
|
||||
args['@count'] = count;
|
||||
// Determine the index of the plural form.
|
||||
var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1);
|
||||
@@ -269,6 +358,72 @@ Drupal.formatPlural = function (count, singular, plural, args, options) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the passed in URL as an absolute URL.
|
||||
*
|
||||
* @param url
|
||||
* The URL string to be normalized to an absolute URL.
|
||||
*
|
||||
* @return
|
||||
* The normalized, absolute URL.
|
||||
*
|
||||
* @see https://github.com/angular/angular.js/blob/v1.4.4/src/ng/urlUtils.js
|
||||
* @see https://grack.com/blog/2009/11/17/absolutizing-url-in-javascript
|
||||
* @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L53
|
||||
*/
|
||||
Drupal.absoluteUrl = function (url) {
|
||||
var urlParsingNode = document.createElement('a');
|
||||
|
||||
// Decode the URL first; this is required by IE <= 6. Decoding non-UTF-8
|
||||
// strings may throw an exception.
|
||||
try {
|
||||
url = decodeURIComponent(url);
|
||||
} catch (e) {}
|
||||
|
||||
urlParsingNode.setAttribute('href', url);
|
||||
|
||||
// IE <= 7 normalizes the URL when assigned to the anchor node similar to
|
||||
// the other browsers.
|
||||
return urlParsingNode.cloneNode(false).href;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the URL is within Drupal's base path.
|
||||
*
|
||||
* @param url
|
||||
* The URL string to be tested.
|
||||
*
|
||||
* @return
|
||||
* Boolean true if local.
|
||||
*
|
||||
* @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L58
|
||||
*/
|
||||
Drupal.urlIsLocal = function (url) {
|
||||
// Always use browser-derived absolute URLs in the comparison, to avoid
|
||||
// attempts to break out of the base path using directory traversal.
|
||||
var absoluteUrl = Drupal.absoluteUrl(url);
|
||||
var protocol = location.protocol;
|
||||
|
||||
// Consider URLs that match this site's base URL but use HTTPS instead of HTTP
|
||||
// as local as well.
|
||||
if (protocol === 'http:' && absoluteUrl.indexOf('https:') === 0) {
|
||||
protocol = 'https:';
|
||||
}
|
||||
var baseUrl = protocol + '//' + location.host + Drupal.settings.basePath.slice(0, -1);
|
||||
|
||||
// Decoding non-UTF-8 strings may throw an exception.
|
||||
try {
|
||||
absoluteUrl = decodeURIComponent(absoluteUrl);
|
||||
} catch (e) {}
|
||||
try {
|
||||
baseUrl = decodeURIComponent(baseUrl);
|
||||
} catch (e) {}
|
||||
|
||||
// The given URL matches the site's base URL, or has a path under the site's
|
||||
// base URL.
|
||||
return absoluteUrl === baseUrl || absoluteUrl.indexOf(baseUrl + '/') === 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate the themed representation of a Drupal object.
|
||||
*
|
||||
@@ -347,10 +502,33 @@ Drupal.getSelection = function (element) {
|
||||
return { 'start': element.selectionStart, 'end': element.selectionEnd };
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a global variable which determines if the window is being unloaded.
|
||||
*
|
||||
* This is primarily used by Drupal.displayAjaxError().
|
||||
*/
|
||||
Drupal.beforeUnloadCalled = false;
|
||||
$(window).bind('beforeunload pagehide', function () {
|
||||
Drupal.beforeUnloadCalled = true;
|
||||
});
|
||||
|
||||
/**
|
||||
* Displays a JavaScript error from an Ajax response when appropriate to do so.
|
||||
*/
|
||||
Drupal.displayAjaxError = function (message) {
|
||||
// Skip displaying the message if the user deliberately aborted (for example,
|
||||
// by reloading the page or navigating to a different page) while the Ajax
|
||||
// request was still ongoing. See, for example, the discussion at
|
||||
// http://stackoverflow.com/questions/699941/handle-ajax-error-when-a-user-clicks-refresh.
|
||||
if (!Drupal.beforeUnloadCalled) {
|
||||
alert(message);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Build an error message from an Ajax response.
|
||||
*/
|
||||
Drupal.ajaxError = function (xmlhttp, uri) {
|
||||
Drupal.ajaxError = function (xmlhttp, uri, customMessage) {
|
||||
var statusCode, statusText, pathText, responseText, readyStateText, message;
|
||||
if (xmlhttp.status) {
|
||||
statusCode = "\n" + Drupal.t("An AJAX HTTP error occurred.") + "\n" + Drupal.t("HTTP Result Code: !status", {'!status': xmlhttp.status});
|
||||
@@ -383,7 +561,10 @@ Drupal.ajaxError = function (xmlhttp, uri) {
|
||||
// We don't need readyState except for status == 0.
|
||||
readyStateText = xmlhttp.status == 0 ? ("\n" + Drupal.t("ReadyState: !readyState", {'!readyState': xmlhttp.readyState})) : "";
|
||||
|
||||
message = statusCode + pathText + statusText + responseText + readyStateText;
|
||||
// Additional message beyond what the xmlhttp object provides.
|
||||
customMessage = customMessage ? ("\n" + Drupal.t("CustomMessage: !customMessage", {'!customMessage': customMessage})) : "";
|
||||
|
||||
message = statusCode + pathText + statusText + customMessage + responseText + readyStateText;
|
||||
return message;
|
||||
};
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 5.3 KiB |
+5
-1
@@ -493,7 +493,11 @@ $(document).bind('state:disabled', function(e) {
|
||||
$(document).bind('state:required', function(e) {
|
||||
if (e.trigger) {
|
||||
if (e.value) {
|
||||
$(e.target).closest('.form-item, .form-wrapper').find('label').append('<span class="form-required">*</span>');
|
||||
var $label = $(e.target).closest('.form-item, .form-wrapper').find('label');
|
||||
// Avoids duplicate required markers on initialization.
|
||||
if (!$label.find('.form-required').length) {
|
||||
$label.append('<span class="form-required">*</span>');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$(e.target).closest('.form-item, .form-wrapper').find('label .form-required').remove();
|
||||
|
||||
+19
-6
@@ -106,8 +106,10 @@ Drupal.tableDrag = function (table, tableSettings) {
|
||||
|
||||
// Add mouse bindings to the document. The self variable is passed along
|
||||
// as event handlers do not have direct access to the tableDrag object.
|
||||
$(document).bind('mousemove', function (event) { return self.dragRow(event, self); });
|
||||
$(document).bind('mouseup', function (event) { return self.dropRow(event, self); });
|
||||
$(document).bind('mousemove pointermove', function (event) { return self.dragRow(event, self); });
|
||||
$(document).bind('mouseup pointerup', function (event) { return self.dropRow(event, self); });
|
||||
$(document).bind('touchmove', function (event) { return self.dragRow(event.originalEvent.touches[0], self); });
|
||||
$(document).bind('touchend', function (event) { return self.dropRow(event.originalEvent.touches[0], self); });
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -274,7 +276,10 @@ Drupal.tableDrag.prototype.makeDraggable = function (item) {
|
||||
});
|
||||
|
||||
// Add the mousedown action for the handle.
|
||||
handle.mousedown(function (event) {
|
||||
handle.bind('mousedown touchstart pointerdown', function (event) {
|
||||
if (event.originalEvent.type == "touchstart") {
|
||||
event = event.originalEvent.touches[0];
|
||||
}
|
||||
// Create a new dragObject recording the event information.
|
||||
self.dragObject = {};
|
||||
self.dragObject.initMouseOffset = self.getMouseOffset(item, event);
|
||||
@@ -500,7 +505,7 @@ Drupal.tableDrag.prototype.dragRow = function (event, self) {
|
||||
if (self.indentEnabled) {
|
||||
var xDiff = self.currentMouseCoords.x - self.dragObject.indentMousePos.x;
|
||||
// Set the number of indentations the mouse has been moved left or right.
|
||||
var indentDiff = Math.round(xDiff / self.indentAmount * self.rtl);
|
||||
var indentDiff = Math.round(xDiff / self.indentAmount);
|
||||
// Indent the row with our estimated diff, which may be further
|
||||
// restricted according to the rows around this row.
|
||||
var indentChange = self.rowObject.indent(indentDiff);
|
||||
@@ -575,12 +580,20 @@ Drupal.tableDrag.prototype.dropRow = function (event, self) {
|
||||
* Get the mouse coordinates from the event (allowing for browser differences).
|
||||
*/
|
||||
Drupal.tableDrag.prototype.mouseCoords = function (event) {
|
||||
// Complete support for pointer events was only introduced to jQuery in
|
||||
// version 1.11.1; between versions 1.7 and 1.11.0 pointer events have the
|
||||
// clientX and clientY properties undefined. In those cases, the properties
|
||||
// must be retrieved from the event.originalEvent object instead.
|
||||
var clientX = event.clientX || event.originalEvent.clientX;
|
||||
var clientY = event.clientY || event.originalEvent.clientY;
|
||||
|
||||
if (event.pageX || event.pageY) {
|
||||
return { x: event.pageX, y: event.pageY };
|
||||
}
|
||||
|
||||
return {
|
||||
x: event.clientX + document.body.scrollLeft - document.body.clientLeft,
|
||||
y: event.clientY + document.body.scrollTop - document.body.clientTop
|
||||
x: clientX + document.body.scrollLeft - document.body.clientLeft,
|
||||
y: clientY + document.body.scrollTop - document.body.clientTop
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+5
-1
@@ -57,10 +57,14 @@ Drupal.tableSelect = function () {
|
||||
// Keep track of the last checked checkbox.
|
||||
lastChecked = e.target;
|
||||
});
|
||||
|
||||
// If all checkboxes are checked on page load, make sure the select-all one
|
||||
// is checked too, otherwise keep unchecked.
|
||||
updateSelectAll((checkboxes.length == $(checkboxes).filter(':checked').length));
|
||||
};
|
||||
|
||||
Drupal.tableSelectRange = function (from, to, state) {
|
||||
// We determine the looping mode based on the the order of from and to.
|
||||
// We determine the looping mode based on the order of from and to.
|
||||
var mode = from.rowIndex > to.rowIndex ? 'previousSibling' : 'nextSibling';
|
||||
|
||||
// Traverse through the sibling nodes.
|
||||
|
||||
@@ -134,6 +134,8 @@ Drupal.verticalTab.prototype = {
|
||||
tabShow: function () {
|
||||
// Display the tab.
|
||||
this.item.show();
|
||||
// Show the vertical tabs.
|
||||
this.item.closest('.vertical-tabs').show();
|
||||
// Update .first marker for items. We need recurse from parent to retain the
|
||||
// actual DOM element order as jQuery implements sortOrder, but not as public
|
||||
// method.
|
||||
@@ -164,6 +166,10 @@ Drupal.verticalTab.prototype = {
|
||||
if ($firstTab.length) {
|
||||
$firstTab.data('verticalTab').focus();
|
||||
}
|
||||
// Hide the vertical tabs (if no tabs remain).
|
||||
else {
|
||||
this.item.closest('.vertical-tabs').hide();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user