Webform module and config export

This commit is contained in:
Rob Davies 2017-03-16 15:29:07 +00:00
parent 3e6a5cbed2
commit 0e15467384
1040 changed files with 117682 additions and 0 deletions

View file

@ -0,0 +1,41 @@
/**
* @file
* Jquery plugin to find common ancestor.
*
* @see http://stackoverflow.com/questions/3217147/jquery-first-parent-containing-all-children
*/
(function ($) {
'use strict';
jQuery.fn.commonAncestor = function() {
var parents = [];
var minlen = Infinity;
$(this).each(function() {
var curparents = $(this).parents();
parents.push(curparents);
minlen = Math.min(minlen, curparents.length);
});
for (var i in parents) {
parents[i] = parents[i].slice(parents[i].length - minlen);
}
// Iterate until equality is found
for (var i = 0; i < parents[0].length; i++) {
var equal = true;
for (var j in parents) {
if (parents[j][i] != parents[0][i]) {
equal = false;
break;
}
}
if (equal) return $(parents[0][i]);
}
return $([]);
}
})(jQuery);

View file

@ -0,0 +1,61 @@
/**
* @file
* Javascript behaviors for admin pages.
*/
(function ($, Drupal) {
'use strict';
/**
* Filter webform autocomplete handler.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformFilterAutocomplete = {
attach: function (context) {
$('.webform-filter-form input.form-autocomplete', context).once('webform-autocomplete')
.each(function () {
// If input value is an autocomplete match, reset the input to its
// default value.
if (/\(([^)]+)\)$/.test(this.value)) {
this.value = this.defaultValue;
}
// From: http://stackoverflow.com/questions/5366068/jquery-ui-autocomplete-submit-onclick-result
$(this).bind('autocompleteselect', function (event, ui) {
if (ui.item) {
$(this).val(ui.item.value);
this.form.submit();
}
});
});
}
};
/**
* Allow table rows to be hyperlinked.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformTableRowHref = {
attach: function (context) {
// Only attach the click event handler to the entire table and determine
// which row triggers the event.
$('.webform-results__table', context).once('webform-results-table').click(function (event) {
if (event.target.tagName == 'A' || event.target.tagName == 'BUTTON') {
return true;
}
var $tr = $(event.target).parents('tr[data-webform-href]');
if (!$tr.length) {
return true;
}
window.location = $tr.attr('data-webform-href');
return false;
});
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,41 @@
/**
* @file
* Javascript behaviors for AJAX.
*/
(function ($, Drupal) {
'use strict';
/**
* Scroll to top ajax command.
*
* @param {Drupal.Ajax} [ajax]
* A {@link Drupal.ajax} object.
* @param {object} response
* Ajax response.
* @param {string} response.selector
* Selector to use.
*
* @see Drupal.AjaxCommands.prototype.webformScrollTop
*/
Drupal.AjaxCommands.prototype.webformScrollTop = function (ajax, response) {
// Scroll to the top of the view. This will allow users
// to browse newly loaded content after e.g. clicking a pager
// link.
var offset = $(response.selector).offset();
// We can't guarantee that the scrollable object should be
// the body, as the view could be embedded in something
// more complex such as a modal popup. Recurse up the DOM
// and scroll the first element that has a non-zero top.
var scrollTarget = response.selector;
while ($(scrollTarget).scrollTop() === 0 && $(scrollTarget).parent()) {
scrollTarget = $(scrollTarget).parent();
}
// Only scroll upward.
if (offset.top - 10 < $(scrollTarget).scrollTop()) {
$(scrollTarget).animate({scrollTop: (offset.top - 10)}, 500);
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,8 @@
/**
* @file
* This an empty placeholder file that is replaced with custom JavaScript.
*
* @see webform_js_alter()
* @see _webform_asset_alter()
* @see \Drupal\webform\WebformSubmissionForm::form
*/

View file

@ -0,0 +1,44 @@
/**
* @file
* Webform block behaviors.
*/
(function ($, window, Drupal) {
'use strict';
/**
* Provide the summary information for the block settings vertical tabs.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior for the block settings summaries.
*/
Drupal.behaviors.webformBlockSettingsSummary = {
attach: function () {
// The drupalSetSummary method required for this behavior is not available
// on the Blocks administration page, so we need to make sure this
// behavior is processed only if drupalSetSummary is defined.
if (typeof $.fn.drupalSetSummary === 'undefined') {
return;
}
/**
* Create a summary for selected in the provided context.
*
* @param {HTMLDocument|HTMLElement} context
* A context where one would find selected to summarize.
*
* @return {string}
* A string with the summary.
*/
function selectSummary(context) {
return $(context).find("#edit-visibility-webform-webforms option:selected").map(function(){ return this.text }).get().join(", ") || Drupal.t('Not restricted');
}
$('[data-drupal-selector="edit-visibility-webform"]').drupalSetSummary(selectSummary);
}
};
})(jQuery, window, Drupal);

View file

@ -0,0 +1,19 @@
/**
* @file
* Javascript behaviors to fix dialogs.
*/
(function ($, Drupal) {
'use strict';
// @see http://stackoverflow.com/questions/20533487/how-to-ensure-that-ckeditor-has-focus-when-displayed-inside-of-jquery-ui-dialog
var _allowInteraction = $.ui.dialog.prototype._allowInteraction;
$.ui.dialog.prototype._allowInteraction = function (event) {
if ($(event.target).closest('.cke_dialog').length) {
return true;
}
return _allowInteraction.apply(this, arguments);
};
})(jQuery, Drupal);

View file

@ -0,0 +1,35 @@
/**
* @file
* Javascript behaviors for jQuery UI buttons element integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Create jQuery UI buttons element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformButtons = {
attach: function (context) {
$(context).find('.js-webform-buttons [type="radio"]').commonAncestor().once('webform-buttons').each(function () {
var $input = $(this);
// Remove all div and classes around radios and labels.
$input.html($input.find('input[type="radio"], label').removeClass());
// Create buttonset.
$input.buttonset();
// Disable buttonset.
$input.buttonset('option', 'disabled', $input.find('input[type="radio"]:disabled').length);
// Turn buttonset off/on when the input is disabled/enabled.
// @see webform.states.js
$input.on('webform:disabled', function () {
$input.buttonset('option', 'disabled', $input.find('input[type="radio"]:disabled').length);
});
});
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,103 @@
/**
* @file
* Javascript behaviors for CodeMirror integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Initialize CodeMirror editor.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformCodeMirror = {
attach: function (context) {
// Webform CodeMirror editor.
$(context).find('textarea.js-webform-codemirror').once('webform-codemirror').each(function () {
var $input = $(this);
// Open all closed details, so that editor height is correctly calculated.
var $details = $(this).parents('details:not([open])');
$details.attr('open', 'open');
// #59 HTML5 required attribute breaks hack for webform submission.
// https://github.com/marijnh/CodeMirror-old/issues/59
$(this).removeAttr('required');
var editor = CodeMirror.fromTextArea(this, {
mode: $(this).attr('data-webform-codemirror-mode'),
lineNumbers: true,
viewportMargin: Infinity,
readOnly: $(this).prop('readonly') ? true : false,
// Setting for using spaces instead of tabs - https://github.com/codemirror/CodeMirror/issues/988
extraKeys: {
Tab: function (cm) {
var spaces = Array(cm.getOption('indentUnit') + 1).join(' ');
cm.replaceSelection(spaces, 'end', '+element');
}
}
});
// Now, close details.
$details.removeAttr('open');
// Issue #2764443: CodeMirror is not setting submitted value when
// rendered within a webform UI dialog.
editor.on('blur', function (event) {
editor.save();
});
// Update CodeMirror when the textarea's value has changed.
// @see webform.states.js
$input.on('change', function () {
editor.getDoc().setValue($input.val());
});
// Set CodeMirror to be readonly when the textarea is disabled.
// @see webform.states.js
$input.on('webform:disabled', function () {
editor.setOption("readOnly", $input.is(':disabled'));
});
});
// Webform CodeMirror syntax coloring.
$(context).find('.js-webform-codemirror-runmode').once('webform-codemirror-runmode').each(function () {
// Mode Runner - http://codemirror.net/demo/runmode.html
CodeMirror.runMode($(this).addClass('cm-s-default').html(), $(this).attr('data-webform-codemirror-mode'), this);
});
}
};
// Workaround: When a dialog opens we need to reference all CodeMirror
// editors to make sure they are properly initialized and sized.
$(window).on('dialog:aftercreate', function (dialog, $element, settings) {
// Delay refreshing CodeMirror for 10 millisecond while the dialog is
// still being rendered.
// @see http://stackoverflow.com/questions/8349571/codemirror-editor-is-not-loading-content-until-clicked
setTimeout(function () {
$('.CodeMirror').each(function (index, $element) {
var $details = $(this).parents('details:not([open])');
$details.attr('open', 'open');
$element.CodeMirror.refresh();
// Now, close details.
$details.removeAttr('open');
});
}, 10);
});
// On state:visible refresh CodeMirror elements.
$(document).on('state:visible', function (event) {
var $element = $(event.target);
if ($element.hasClass('js-webform-codemirror')) {
$element.parent().find('.CodeMirror').each(function (index, $element) {
$element.CodeMirror.refresh();
});
}
});
})(jQuery, Drupal);

View file

@ -0,0 +1,46 @@
/**
* @file
* Javascript behaviors for color element integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Enhance HTML5 color element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformColor = {
attach: function (context) {
$(context).find('.form-color:not(.form-color-output)').once('webform-color').each(function () {
var $element = $(this);
// Handle browser that don't support the HTML5 color input.
if (Modernizr.inputtypes.color === false) {
// Remove swatch sizes.
$element.removeClass('form-color-small')
.removeClass('form-color-medium')
.removeClass('form-color-large');
}
else {
// Display color input's output to the end user.
var $output = $('<input class="form-color-output ' + $element.attr('class') + ' js-webform-element-mask" data-inputmask-mask="\\#######" />').inputmask();
$output[0].value = $element[0].value;
$element
.after($output)
.css({float: 'left'});
// Sync $element and $output.
$element.on('input', function () {
$output[0].value = $element[0].value;
});
$output.on('input', function () {
$element[0].value = $output[0].value;
});
}
})
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,38 @@
/**
* @file
* Javascript behaviors for jQuery Word and Counter Counter integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Initialize text field and textarea word and character counter.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformCounter = {
attach: function (context) {
$(context).find('.js-webform-counter').once('webform-counter').each(function () {
var options = {
goal: $(this).attr('data-counter-limit'),
msg: $(this).attr('data-counter-message')
};
// Only word type can be defined, otherwise the counter defaults to
// character counting.
if ($(this).attr('data-counter-type') == 'word') {
options.type = 'word';
}
// Set the target to a div that is appended to end of the input's parent container.
options.target = $('<div></div>');
$(this).parent().append(options.target);
$(this).counter(options);
})
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,69 @@
/**
* @file
* Override polyfill for HTML5 date input and provide support for custom date formats.
*/
(function ($, Modernizr, Drupal) {
'use strict';
/**
* Attach datepicker fallback on date elements.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior. Accepts in `settings.date` an object listing
* elements to process, keyed by the HTML ID of the form element containing
* the human-readable value. Each element is an datepicker settings object.
* @prop {Drupal~behaviorDetach} detach
* Detach the behavior destroying datepickers on effected elements.
*/
Drupal.behaviors.date = {
attach: function (context, settings) {
var $context = $(context);
// Skip if date are supported by the browser.
if (Modernizr.inputtypes.date === true) {
return;
}
$context.find('input[data-drupal-date-format]').once('datePicker').each(function () {
var $input = $(this);
var datepickerSettings = {};
var dateFormat = $input.data('drupalDateFormat');
// The date format is saved in PHP style, we need to convert to jQuery
// datepicker.
// @see http://stackoverflow.com/questions/16702398/convert-a-php-date-format-to-a-jqueryui-datepicker-date-format
datepickerSettings.dateFormat = dateFormat
// Year.
.replace('Y', 'yy')
// Month.
.replace('F', 'MM')
.replace('m', 'mm')
.replace('n', 'm')
// Date.
.replace('d', 'dd');
// Add min and max date if set on the input.
if ($input.attr('min')) {
datepickerSettings.minDate = $.datepicker.formatDate(datepickerSettings.dateFormat, $.datepicker.parseDate('yy-mm-dd', $input.attr('min')));
}
if ($input.attr('max')) {
datepickerSettings.maxDate = $.datepicker.formatDate(datepickerSettings.dateFormat, $.datepicker.parseDate('yy-mm-dd', $input.attr('max')));
}
// Format default value.
if ($input.val()) {
$input.val($.datepicker.formatDate(datepickerSettings.dateFormat, $.datepicker.parseDate('yy-mm-dd', $input.val())));
}
$input.datepicker(datepickerSettings);
});
},
detach: function (context, settings, trigger) {
if (trigger === 'unload') {
$(context).find('input[data-drupal-date-format]').findOnce('datePicker').datepicker('destroy');
}
}
};
})(jQuery, Modernizr, Drupal);

View file

@ -0,0 +1,102 @@
/**
* @file
* Javascript behaviors for details element.
*/
(function ($, Drupal) {
'use strict';
/**
* Attach handler to save details open/close state.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformDetailsSave = {
attach: function (context) {
if (!window.localStorage) {
return;
}
// Summary click event handler.
$('details > summary', context).once('webform-details-summary-save').click(function () {
var $details = $(this).parent();
var name = Drupal.webformDetailsSaveGetName($details);
if (!name) {
return;
}
var open = ($details.attr('open') != 'open') ? 1 : 0;
localStorage.setItem(name, open);
});
// Initialize details open state via local storage.
$('details', context).once('webform-details-save').each(function () {
var $details = $(this);
var name = Drupal.webformDetailsSaveGetName($details);
if (!name) {
return;
}
var open = localStorage.getItem(name);
if (open === null) {
return;
}
if (open == 1) {
$details.attr('open', 'open');
}
else {
$details.removeAttr('open');
}
});
}
};
/**
* Get the name used to store the state of details element.
*
* @param $details
* A details element.
*
* @returns string
* The name used to store the state of details element.
*/
Drupal.webformDetailsSaveGetName = function ($details) {
if (!window.localStorage) {
return '';
}
// Any details element not included a webform must have define its own id.
var webformId = $details.attr('data-webform-element-id');
if (webformId) {
return 'Drupal.webform.' + webformId.replace('--', '.');
}
var detailsId = $details.attr('id');
if (!detailsId) {
return '';
}
var $form = $details.parents('form');
if (!$form.length || !$form.attr('id')) {
return '';
}
var formId = $form.attr('id');
if (!formId) {
return '';
}
// ISSUE: When Drupal renders a webform in a modal dialog it appends a unique
// identifier to webform ids and details ids. (ie my-form--FeSFISegTUI)
// WORKAROUND: Remove the unique id that delimited using double dashes.
formId = formId.replace(/--.+?$/, '').replace(/-/g, '_');
detailsId = detailsId.replace(/--.+?$/, '').replace(/-/g, '_');
return 'Drupal.webform.' + formId + '.' + detailsId;
}
})(jQuery, Drupal);

View file

@ -0,0 +1,85 @@
/**
* @file
* Javascript behaviors for details element.
*/
(function ($, Drupal) {
'use strict';
/**
* Attach handler to toggle details open/close state.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformDetailsToggle = {
attach: function (context) {
$('.js-webform-details-toggle', context).once('webform-details-toggle').each(function () {
var $form = $(this);
var $details = $form.find('details');
// Toggle is only useful when there are two or more details elements.
if ($details.length < 2) {
return;
}
// Add toggle state link to first details element.
$details.first().before($('<button type="button" class="link webform-details-toggle-state"></button>')
.attr('title', Drupal.t('Toggle details widget state.'))
.on('click', function (e) {
var open;
if (isFormDetailsOpen($form)) {
$form.find('details').removeAttr('open');
open = 0;
}
else {
$form.find('details').attr('open', 'open');
open = 1;
}
setDetailsToggleLabel($form);
// Set the saved states for all the details elements.
// @see webform.element.details.save.js
if (Drupal.webformDetailsSaveGetName) {
$form.find('details').each(function () {
var name = Drupal.webformDetailsSaveGetName($(this));
if (name) {
localStorage.setItem(name, open);
}
});
}
})
.wrap('<div class="webform-details-toggle-state-wrapper"></div>')
.parent()
);
setDetailsToggleLabel($form);
});
}
};
/**
* Determine if a webform's details are all opened.
*
* @param $form
* A webform.
*
* @returns {boolean}
* TRUE if a webform's details are all opened.
*/
function isFormDetailsOpen($form) {
return ($form.find('details[open]').length == $form.find('details').length)
}
/**
* Set a webform's details toggle state widget label.
*
* @param $form
* A webform.
*/
function setDetailsToggleLabel($form) {
var label = (isFormDetailsOpen($form)) ? Drupal.t('Collapse all') : Drupal.t('Expand all');
$form.find('.webform-details-toggle-state').html(label);
}
})(jQuery, Drupal);

View file

@ -0,0 +1,52 @@
/**
* @file
* Javascript behaviors for HTML editor integration.
*/
(function ($, Drupal, drupalSettings) {
'use strict';
/**
* Initialize HTML Editor.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformHtmlEditor = {
attach: function (context) {
$(context).find('.js-form-type-webform-html-editor textarea').once('webform-html-editor').each(function () {
var allowedContent = drupalSettings['webform']['html_editor']['allowedContent'];
var $textarea = $(this);
CKEDITOR.replace(this.id, {
// Turn off external config and styles.
customConfig: '',
stylesSet: false,
contentsCss: [],
allowedContent: allowedContent,
// Use <br> tags instead of <p> tags.
enterMode: CKEDITOR.ENTER_BR,
shiftEnterMode: CKEDITOR.ENTER_BR,
// Set height, hide the status bar, and remove plugins.
height: '100px',
resize_enabled: false,
removePlugins: 'elementspath,magicline',
// Toolbar settings.
format_tags: 'p;h2;h3;h4;h5;h6',
toolbar: [
{ name: 'styles', items: ['Format', 'Font', 'FontSize' ] },
{ name: 'basicstyles', items: [ 'Bold', 'Italic', 'Subscript', 'Superscript' ] },
{ name: 'insert', items: [ 'SpecialChar' ] },
{ name: 'colors', items: [ 'TextColor', 'BGColor' ] },
{ name: 'paragraph', items: [ 'NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote'] },
{ name: 'links', items: [ 'Link', 'Unlink'] },
{ name: 'tools', items: [ 'Source', '-', 'Maximize' ] }
]
}).on('change', function (evt) {
// Save data onchange since AJAX dialogs don't execute webform.onsubmit.
$textarea.val(evt.editor.getData().trim());
});
})
}
};
})(jQuery, Drupal, drupalSettings);

View file

@ -0,0 +1,21 @@
/**
* @file
* Javascript behaviors for jquery.inputmask integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Initialize input masks.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformElementMask = {
attach: function (context) {
$(context).find('input.js-webform-element-mask').once('webform-element-mask').inputmask();
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,53 @@
/**
* @file
* Javascript behaviors for Geocomplete location integration.
*/
(function ($, Drupal, drupalSettings) {
'use strict';
/**
* Initialize location Geocompletion.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformLocation = {
attach: function (context) {
$(context).find('div.js-webform-location').once('webform-location').each(function () {
var $element = $(this);
var $geocomplete = $element.find('.webform-location-geocomplete').geocomplete({
details: $element,
detailsAttribute: 'data-webform-location-attribute',
types: ['geocode']
});
$geocomplete.on('input', function () {
// Reset attributes on input.
$element.find('[data-webform-location-attribute]').val('');
}).on('blur', function () {
// Make sure to get attributes on blur.
if ($element.find('[data-webform-location-attribute="location"]').val() == '') {
var value = $geocomplete.val();
if (value) {
$geocomplete.geocomplete('find', value);
}
}
});
// If there is default value look up location's attributes, else see if
// the default value should be set to the browser's current geolocation.
var value = $geocomplete.val();
if (value) {
$geocomplete.geocomplete('find', value);
}
else if (navigator.geolocation && $geocomplete.attr('data-webform-location-geolocation')) {
navigator.geolocation.getCurrentPosition(function (position) {
$geocomplete.geocomplete('find', position.coords.latitude + ', ' + position.coords.longitude);
});
}
})
}
};
})(jQuery, Drupal, drupalSettings);

View file

@ -0,0 +1,92 @@
/**
* @file
* Javascript behaviors for message element integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Behavior for handler message close.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformMessageClose = {
attach: function (context) {
$(context).find('.js-webform-message--close').once('webform-message--close').each(function () {
var $element = $(this);
var id = $element.attr('data-message-id');
var storage = $element.attr('data-message-storage');
var effect = $element.attr('data-message-close-effect') || 'hide';
switch (effect) {
case 'slide': effect = 'slideUp'; break;
case 'fade': effect = 'fadeOut'; break;
}
// Check storage status.
if (isClosed($element, storage, id)) {
return;
}
$element.show().find('.js-webform-message__link').on('click', function (event) {
$element[effect]();
setClosed($element, storage, id);
$element.trigger('close');
event.preventDefault();
});
})
}
};
function isClosed($element, storage, id) {
if (!id || !storage) {
return false;
}
switch (storage) {
case 'local':
if (window.localStorage) {
return localStorage.getItem('Drupal.webform.message.' + id) || false;
}
return false;
case 'session':
if (window.sessionStorage) {
return sessionStorage.getItem('Drupal.webform.message.' + id) || false;
}
return false;
default:
return false;
}
}
function setClosed($element, storage, id) {
if (!id || !storage) {
return;
}
switch (storage) {
case 'local':
if (window.localStorage) {
localStorage.setItem('Drupal.webform.message.' + id, true);
}
break;
case 'session':
if (window.sessionStorage) {
sessionStorage.setItem('Drupal.webform.message.' + id, true);
}
break;
case 'user':
case 'state':
$.get($element.find('.js-webform-message__link').attr('href'));
return true;
}
}
})(jQuery, Drupal);

View file

@ -0,0 +1,26 @@
/**
* @file
* Javascript behaviors for message element integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Move show weight to after the table.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformMultiple = {
attach: function (context, settings) {
for (var base in settings.tableDrag) {
var $tableDrag = $(context).find('#' + base);
var $toggleWeight = $tableDrag.parent().find('.tabledrag-toggle-weight');
$toggleWeight.addClass('webform-multiple-tabledrag-toggle-weight');
$tableDrag.after($toggleWeight);
}
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,157 @@
/**
* @file
* Javascript behaviors for other elements.
*/
(function ($, Drupal) {
"use strict";
/**
* Toggle other input (text) field.
*
* @param {boolean} show
* TRUE will display the text field. FALSE with hide and clear the text field.
* @param {object} $element
* The input (text) field to be toggled.
*/
function toggleOther(show, $element) {
var $input = $element.find('input');
if (show) {
// Limit the other inputs width to the parent's container.
$element.width($element.parent().width());
// Display the element.
$element.slideDown();
// Focus and require the input.
$input.focus().prop('required', true);
// Restore the input's value.
var value = $input.data('webform-value');
if (value !== undefined) {
$input.val(value);
$input.get(0).setSelectionRange(0, 0);
}
// Refresh CodeMirror used as other element.
$element.parent().find('.CodeMirror').each(function (index, $element) {
$element.CodeMirror.refresh();
});
}
else {
$element.slideUp();
// Save the input's value.
$input.data('webform-value', $input.val());
// Empty and un-required the input.
$input.val('').prop('required', false);
}
}
/**
* Attach handlers to select other elements.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformSelectOther = {
attach: function (context) {
$(context).find('.js-webform-select-other').once('webform-select-other').each(function () {
var $element = $(this);
var $select = $element.find('select');
var $otherOption = $element.find('option[value="_other_"]');
var $input = $element.find('.js-webform-select-other-input');
if ($otherOption.is(':selected')) {
$input.show().find('input').prop('required', true);
}
$select.on('change', function () {
toggleOther($otherOption.is(':selected'), $input);
});
});
}
};
/**
* Attach handlers to checkboxes other elements.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformCheckboxesOther = {
attach: function (context) {
$(context).find('.js-webform-checkboxes-other').once('webform-checkboxes-other').each(function () {
var $element = $(this);
var $checkbox = $element.find('input[value="_other_"]');
var $input = $element.find('.js-webform-checkboxes-other-input');
if ($checkbox.is(':checked')) {
$input.show().find('input').prop('required', true);
}
$checkbox.on('change', function () {
toggleOther(this.checked, $input);
});
});
}
};
/**
* Attach handlers to radios other elements.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformRadiosOther = {
attach: function (context) {
$(context).find('.js-webform-radios-other').once('webform-radios-other').each(function () {
var $element = $(this);
var $radios = $element.find('input[type="radio"]');
var $input = $element.find('.js-webform-radios-other-input');
if ($radios.filter(':checked').val() === '_other_') {
$input.show().find('input').prop('required', true);
}
$radios.on('change', function () {
toggleOther(($radios.filter(':checked').val() === '_other_'), $input);
});
});
}
};
/**
* Attach handlers to buttons other elements.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformButtonsOther = {
attach: function (context) {
$(context).find('.js-webform-buttons-other').once('webform-buttons-other').each(function () {
var $element = $(this);
var $buttons = $element.find('input[type="radio"]');
var $input = $element.find('.js-webform-buttons-other-input');
if ($buttons.filter(':checked').val() === '_other_') {
$input.show().find('input').prop('required', true);
}
// Note: Initializing buttonset here so that we can set the onchange
// event handler.
// @see Drupal.behaviors.webformButtons
var $container = $(this).find('.form-radios');
// Remove all div and classes around radios and labels.
$container.html($container.find('input[type="radio"], label').removeClass());
// Create buttonset and set onchange handler.
$container.buttonset().change(function () {
toggleOther(($(this).find(':radio:checked').val() === '_other_'), $input);
});
// Disable buttonset.
$container.buttonset('option', 'disabled', $container.find('input[type="radio"]:disabled').length);
// Turn buttonset off/on when the input is disabled/enabled.
// @see webform.states.js
$container.on('webform:disabled', function () {
$container.buttonset('option', 'disabled', $container.find('input[type="radio"]:disabled').length);
});
});
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,58 @@
/**
* @file
* Javascript behaviors for radio buttons.
*
* Fix #states and #required for radios buttons.
*
* @see Issue #2856795: If radio buttons are required but not filled form is nevertheless submitted.
* @see Issue #2856315: Conditional Logic - Requiring Radios in a Fieldset.
* @see Issue #2731991: Setting required on radios marks all options required.
* @see css/webform.form.css
* @see /core/misc/states.js
*/
(function ($, Drupal) {
'use strict';
/**
* Attach handler to add .js-webform-radios-fieldset to radios wrapper fieldset.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformRadios = {
attach: function (context) {
$('.js-webform-radios', context).closest('fieldset.form-composite').addClass('js-webform-radios-fieldset');
}
};
// Make absolutely sure the below event handlers are triggered after
// the /core/misc/states.js event handlers by attaching them after DOM load.
$(function () {
Drupal.behaviors.webformRadios.attach($(document));
function setRequired($target, required) {
if (!$target.hasClass('js-webform-radios-fieldset')) {
return;
}
if (required) {
$target.find('input[type="radio"]').attr({'required': 'required', 'aria-required': 'aria-required'})
$target.find('legend span').addClass('js-form-required form-required');
}
else {
$target.find('input[type="radio"]').removeAttr('required aria-required');
$target.find('legend span').removeClass('js-form-required form-required');
}
}
setRequired($('.form-composite[required="required"]'), true);
$(document).on('state:required', function (e) {
if (e.trigger) {
setRequired($(e.target), e.value);
}
});
});
})(jQuery, Drupal);

View file

@ -0,0 +1,62 @@
/**
* @file
* Javascript behaviors for range element integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Enhance HTML5 range element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformRange = {
attach: function (context) {
$(context).find('.form-range[data-range-output]').once('webform-range').each(function () {
var $element = $(this);
// Handle browser that don't support the HTML5 range input.
if (Modernizr.inputtypes.range === false) {
return;
}
var prefix = $element.attr('data-range-output-prefix');
var suffix = $element.attr('data-range-output-suffix');
// Display range input's output to the end user.
var html = '';
html += '<div class="form-range-output-container">';
html += (prefix ? '<span class="field-prefix">' + prefix + '</span>' : '');
html += '<input type="number" min="' + $element.attr('min') + '" max="' + $element.attr('max') + '" step="' + $element.attr('step') + '" class="form-range-output form-number" />';
html += (suffix ? '<span class="field-suffix">' + suffix + '</span>' : '');
html += '</div>';
var height = parseInt($element.outerHeight()) || 24;
var $outputContainer = $(html);
// Set the container element's line height which will vertically
// align the range widget and the output.
$outputContainer.find('input, span').css({
height: height + 'px',
lineHeight: height + 'px'
});
var $output = $outputContainer.find('input');
$output[0].value = $element[0].value;
$element
.after($outputContainer)
.css({float: 'left'});
// Sync $element and $output.
$element.on('input', function () {
$output[0].value = $element[0].value;
});
$output.on('input', function () {
$element[0].value = $output[0].value;
});
})
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,39 @@
/**
* @file
* Javascript behaviors for RateIt integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Initialize rating element using RateIt.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformRating = {
attach: function (context) {
$(context)
.find('[data-rateit-backingfld]')
.once('webform-rating')
.each(function () {
var $rateit = $(this);
var $input = $($rateit.attr('data-rateit-backingfld'));
// Update the RateIt widget when the input's value has changed.
// @see webform.states.js
$input.on('change', function () {
$rateit.rateit('value', $input.val());
});
// Set RateIt widget to be readonly when the input is disabled.
// @see webform.states.js
$input.on('webform:disabled', function () {
$rateit.rateit('readonly', $input.is(':disabled'));
});
});
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,39 @@
/**
* @file
* Javascript behaviors for roles element integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Enhance roles element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformRoles = {
attach: function (context) {
$(context).find('.js-webform-roles-role[value="authenticated"]').once('webform-roles').each(function () {
var $authenticated = $(this);
var $checkboxes = $authenticated.parents('.form-checkboxes').find('.js-webform-roles-role').filter(function () {
return ($(this).val() != 'anonymous' && $(this).val() != 'authenticated');
});
$authenticated.on('click', function () {
if ($authenticated.is(':checked')) {
$checkboxes.prop('checked', true).attr('disabled', true);
}
else {
$checkboxes.prop('checked', false).removeAttr('disabled');
}
});
if ($authenticated.is(':checked')) {
$checkboxes.prop('checked', true).attr('disabled', true);
}
})
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,26 @@
/**
* @file
* Javascript behaviors for Select2 integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Initialize Select2 support.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformSelect2 = {
attach: function (context) {
$(context)
.find('select.js-webform-select2, .js-webform-select2 select')
.once('webform-select2')
// http://stackoverflow.com/questions/14313001/select2-not-calculating-resolved-width-correctly-if-select-is-hidden
.css('width', '100%')
.select2();
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,83 @@
/**
* @file
* Javascript behaviors for signature pad integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Initialize signature element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformSignature = {
attach: function (context) {
$(context).find('input.js-webform-signature').once('webform-signature').each(function () {
var $input = $(this);
var value = $input.val();
var $wrapper = $input.parent();
var $canvas = $wrapper.find('canvas');
var $button = $wrapper.find('input[type="submit"]');
var canvas = $canvas[0];
// Set height.
$canvas.attr('width', $wrapper.width());
$canvas.attr('height', $wrapper.width() / 3);
$(window).resize(function () {
$canvas.attr('width', $wrapper.width());
$canvas.attr('height', $wrapper.width() / 3);
// Resizing clears the canvas so we need to reset the signature pad.
signaturePad.clear();
var value = $input.val();
if (value) {
signaturePad.fromDataURL(value);
}
});
// Initialize signature canvas.
var signaturePad = new SignaturePad(canvas, {
'onEnd': function () {
$input.val(signaturePad.toDataURL());
}
});
// Set value.
if (value) {
signaturePad.fromDataURL(value);
}
// Set reset handler.
$button.on('click', function () {
signaturePad.clear();
$input.val();
this.blur();
return false;
});
// Input onchange clears signature pad if value is empty.
// @see webform.states.js
$input.on('change', function () {
if (!$input.val()) {
signaturePad.clear();
}
});
// Turn signature pad off/on when the input is disabled/enabled.
// @see webform.states.js
$input.on('webform:disabled', function () {
if ($input.is(':disabled')) {
signaturePad.off();
}
else {
signaturePad.on();
}
});
});
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,50 @@
/**
* @file
* Javascript behaviors for Telephone element.
*/
(function ($, Drupal, drupalSettings) {
'use strict';
/**
* Initialize Telephone international element.
* @see http://intl-tel-input.com/node_modules/intl-tel-input/examples/gen/is-valid-number.html
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformTelephoneInternational = {
attach: function (context) {
$(context).find('input.js-webform-telephone-international').once('webform-telephone-international').each(function () {
var $telephone = $(this);
// Add error message container.
var $error = $('<div class="form-item--error-message">' + Drupal.t('Invalid phone number') + '</div>').hide();
$telephone.closest('.form-item').append($error);
// @todo: Figure out how to lazy load utilsScript (build/js/utils.js).
// @see https://github.com/jackocnr/intl-tel-input#utilities-script
$telephone.intlTelInput({
'nationalMode': false
});
var reset = function() {
$telephone.removeClass('error');
$error.hide();
};
$telephone.blur(function() {
reset();
if ($.trim($telephone.val())) {
if (!$telephone.intlTelInput('isValidNumber')) {
$telephone.addClass('error');
$error.show();
}
}
});
$telephone.on('keyup change', reset);
})
}
};
})(jQuery, Drupal, drupalSettings);

View file

@ -0,0 +1,45 @@
/**
* @file
* Javascript behaviors for Text format integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Enhance text format element.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformTextFormat = {
attach: function (context) {
$(context).find('.js-text-format-wrapper textarea').once('webform-text-format').each(function () {
var $textarea = $(this);
if (!window.CKEDITOR) {
return;
}
// Update the CKEDITOR when the textarea's value has changed.
// @see webform.states.js
$textarea.on('change', function () {
if (CKEDITOR.instances[$textarea.attr('id')]) {
var editor = CKEDITOR.instances[$textarea.attr('id')];
editor.setData($textarea.val());
}
});
// Set CKEDITOR to be readonly when the textarea is disabled.
// @see webform.states.js
$textarea.on('webform:disabled', function () {
if (CKEDITOR.instances[$textarea.attr('id')]) {
var editor = CKEDITOR.instances[$textarea.attr('id')];
editor.setReadOnly($textarea.is(':disabled'));
}
});
});
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,57 @@
/**
* @file
* Javascript behaviors for time integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Attach timepicker fallback on time elements.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior to time elements.
*/
Drupal.behaviors.webformTime = {
attach: function (context, settings) {
var $context = $(context);
// Skip if time inputs are supported by the browser.
if (Modernizr.inputtypes.time === true) {
return;
}
$context.find('input[type="time"]').once('timePicker').each(function () {
var $input = $(this);
var options = {};
if ($input.data('webformTimeFormat')) {
options.timeFormat = $input.data('webformTimeFormat');
}
if ($input.attr('min')) {
options.minTime = $input.attr('min');
}
if ($input.attr('max')) {
options.maxTime = $input.attr('max');
}
// HTML5 time element steps is in seconds but for the timepicker
// fallback it needs to be in minutes.
// Note: The 'datetime' element uses the #date_increment which defaults
// to 1 (second).
// @see \Drupal\Core\Datetime\Element\Datetime::processDatetime
// Only use step if it is greater than 60 seconds.
if ($input.attr('step') && ($input.attr('step') > 60)) {
options.step = Math.round($input.attr('step') / 60);
}
else {
options.step = 1;
}
$input.timepicker(options);
});
}
}
})(jQuery, Drupal);

View file

@ -0,0 +1,56 @@
/**
* @file
* Javascript behaviors for toggle integration.
*/
(function ($, Drupal) {
'use strict';
/**
* Initialize toggle element using Toggles.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformToggle = {
attach: function (context) {
$(context).find('.js-webform-toggle').once('webform-toggle').each(function () {
var $toggle = $(this);
var $wrapper = $toggle.parent();
var $checkbox = $wrapper.find('input[type="checkbox"]');
var $label = $wrapper.find('label');
$toggle.toggles({
checkbox: $checkbox,
on: $checkbox.is(':checked'),
clicker: $label,
text: {
on: $toggle.attr('data-toggle-text-on') || '',
off: $toggle.attr('data-toggle-text-off') || ''
}
});
// If checkbox is disabled then add the .disabled class to the toggle.
if ($checkbox.attr('disabled') || $checkbox.attr('readonly')) {
$toggle.addClass('disabled');
}
// Add .clearfix to the wrapper.
$wrapper.addClass('clearfix')
});
}
};
// Track the disabling of a toggle's checkbox using states.
$(document).on('state:disabled', function (event) {
$('.js-webform-toggle').each(function () {
var $toggle = $(this);
var $wrapper = $toggle.parent();
var $checkbox = $wrapper.find('input[type="checkbox"]');
var isDisabled = ($checkbox.attr('disabled') || $checkbox.attr('readonly'));
(isDisabled) ? $toggle.addClass('disabled') : $toggle.removeClass('disabled');
});
});
})(jQuery, Drupal);

View file

@ -0,0 +1,10 @@
/**
* @file
* Javascript to disable back button.
*/
// From: http://stackoverflow.com/questions/17962130/restrict-user-to-refresh-and-back-forward-in-any-browser
history.pushState({ page: 1 }, "Title 1", "#no-back");
window.onhashchange = function (event) {
window.location.hash = "no-back";
};

View file

@ -0,0 +1,141 @@
/**
* @file
* Javascript behaviors for webforms.
*/
(function ($, Drupal) {
'use strict';
/**
* Autofocus first input.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior for the webform autofocusing.
*/
Drupal.behaviors.webformAutofocus = {
attach: function (context) {
$(context).find('.webform-submission-form.js-webform-autofocus :input:visible:enabled:first').focus();
}
};
/**
* Prevent webform autosubmit on wizard pages.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior for disabling webform autosubmit.
*/
Drupal.behaviors.webformDisableAutoSubmit = {
attach: function (context) {
// @see http://stackoverflow.com/questions/11235622/jquery-disable-form-submit-on-enter
$(context).find('.webform-submission-form.js-webform-disable-autosubmit input').once('webform-disable-autosubmit').on('keyup keypress', function (e) {
var keyCode = e.keyCode || e.which;
if (keyCode === 13) {
e.preventDefault();
return false;
}
});
}
};
/**
* Skip client-side validation when submit button is pressed.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior for the skipping client-side validation.
*/
Drupal.behaviors.webformSubmitNoValidate = {
attach: function (context) {
$(context).find('input:submit.js-webform-novalidate').once('webform-novalidate').on('click', function () {
$(this.form).attr('novalidate', 'novalidate');
});
}
};
/**
* Disable validate when save draft submit button is clicked.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior for the webform draft submit button.
*/
Drupal.behaviors.webformDraft = {
attach: function (context) {
$(context).find('#edit-draft').once('webform-draft').on('click', function () {
$(this.form).attr('novalidate', 'novalidate');
});
}
};
/**
* Filters the webform element list by a text input search string.
*
* The text input will have the selector `input.webform-form-filter-text`.
*
* The target element to do searching in will be in the selector
* `input.webform-form-filter-text[data-element]`
*
* The text source where the text should be found will have the selector
* `.webform-form-filter-text-source`
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior for the webform element filtering.
*/
Drupal.behaviors.webformFilterByText = {
attach: function (context, settings) {
var $input = $('input.webform-form-filter-text').once('webform-form-filter-text');
var $table = $($input.attr('data-element'));
var $filter_rows;
/**
* Filters the webform element list.
*
* @param {jQuery.Event} e
* The jQuery event for the keyup event that triggered the filter.
*/
function filterElementList(e) {
var query = $(e.target).val().toLowerCase();
/**
* Shows or hides the webform element entry based on the query.
*
* @param {number} index
* The index in the loop, as provided by `jQuery.each`
* @param {HTMLElement} label
* The label of the webform.
*/
function toggleEntry(index, label) {
var $label = $(label);
var $row = $label.parent().parent();
var textMatch = $label.text().toLowerCase().indexOf(query) !== -1;
$row.toggle(textMatch);
}
// Filter if the length of the query is at least 2 characters.
if (query.length >= 2) {
$filter_rows.each(toggleEntry);
}
else {
$filter_rows.each(function (index) {
$(this).parent().parent().show();
});
}
}
if ($table.length) {
$filter_rows = $table.find('div.webform-form-filter-text-source');
$input.on('keyup', filterElementList);
}
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,53 @@
/**
* @file
* Javascript behaviors for preventing duplicate webform submissions.
*/
(function ($, Drupal) {
'use strict';
/**
* Submit once.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior for preventing duplicate webform submissions.
*/
Drupal.behaviors.webformSubmitOnce = {
attach: function (context) {
$('.js-webform-submit-once', context).each(function () {
var $form = $(this);
$form.removeAttr('webform-submitted');
$form.find('#edit-actions input[type="submit"]').removeAttr('webform-clicked');
// Track which submit button was clicked.
// @see http://stackoverflow.com/questions/5721724/jquery-how-to-get-which-button-was-clicked-upon-form-submission
$form.find('#edit-actions input[type="submit"]').click(function () {
$form.find('#edit-actions input[type="submit"]').removeAttr('webform-clicked');
$(this).attr('webform-clicked', 'true');
});
$(this).submit(function () {
// Track webform submitted.
if ($form.attr('webform-submitted')) {
return false;
}
$form.attr('webform-submitted', 'true');
// Visually disable all submit buttons.
// Submit buttons can't disabled because their op(eration) must to be posted back to the server.
$form.find('#edit-actions input[type="submit"]').addClass('is-disabled');
// Set the throbber progress indicator.
// @see Drupal.Ajax.prototype.setProgressIndicatorThrobber
var $clickedButton = $form.find('#edit-actions input[type=submit][webform-clicked=true]');
var $progress = $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber">&nbsp;</div></div>');
$clickedButton.after($progress);
});
})
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,75 @@
/**
* @file
* Javascript behaviors for unsaved webforms.
*/
(function ($, Drupal) {
'use strict';
var unsaved = false;
/**
* Unsaved changes.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior for unsaved changes.
*/
Drupal.behaviors.webformUnsaved = {
attach: function (context) {
// Look for the 'data-webform-unsaved' attribute which indicates that the
// multi-step webform has unsaved data.
// @see \Drupal\webform\WebformSubmissionForm::buildForm
if ($('.js-webform-unsaved[data-webform-unsaved]').length) {
unsaved = true;
}
else {
$('.js-webform-unsaved :input:not(input[type=\'submit\'])', context).once('webform-unsaved').on('change keypress', function () {
unsaved = true;
});
}
$('.js-webform-unsaved button, .js-webform-unsaved input[type=\'submit\']', context).once('webform-unsaved').on('click', function () {
unsaved = false;
});
}
};
$(window).on('beforeunload', function () {
if (unsaved) {
return true;
}
});
/*!
* An experimental shim to partially emulate onBeforeUnload on iOS.
* Part of https://github.com/codedance/jquery.AreYouSure/
*
* Copyright (c) 2012-2014, Chris Dance and PaperCut Software http://www.papercut.com/
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Author: chris.dance@papercut.com
* Date: 19th May 2014
*/
$(function () {
if (!navigator.userAgent.toLowerCase().match(/iphone|ipad|ipod|opera/)) {
return;
}
$('a').bind('click', function (evt) {
var href = $(evt.target).closest('a').attr('href');
if (href !== undefined && !(href.match(/^#/) || href.trim() == '')) {
if ($(window).triggerHandler('beforeunload')) {
if (!confirm(Drupal.t('Changes you made may not be saved.') + '\n\n' + Drupal.t('Press OK to leave this page or Cancel to stay.'))) {
return false;
}
}
window.location.href = href;
return false;
}
});
});
})(jQuery, Drupal);

View file

@ -0,0 +1,60 @@
/**
* @file
* Javascript behaviors for help.
*/
(function ($, Drupal) {
'use strict';
/**
* Handles help accordion.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior for help accordion.
*/
Drupal.behaviors.webformHelpAccordion = {
attach: function (context) {
var $widget = $(context).find('.webform-help-accordion');
$widget.once('webform-help-accordion').accordion({
collapsible: true,
heightStyle: "content"
});
var $container = $('h3' + location.hash, $widget);
if ($container.length) {
var active = $widget.find($widget.accordion('option', 'header')).index($container);
$widget.accordion('option', 'active', active);
}
}
};
/**
* Handles disabling help dialog for mobile devices.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior for disabling help dialog for mobile devices.
*/
Drupal.behaviors.webformHelpDialog = {
attach: function (context) {
$(context).find('.button-webform-play').once('webform-help-dialog').on('click', function (event) {
if ($(window).width() < 768) {
event.stopImmediatePropagation();
}
}).each(function () {
// Must make sure that this click event handler is execute first and
// before the AJAX dialog handler.
// @see http://stackoverflow.com/questions/2360655/jquery-event-handlers-always-execute-in-order-they-were-bound-any-way-around-t
var handlers = $._data(this, 'events')['click'];
var handler = handlers.pop();
// Move it at the beginning.
handlers.splice(0, 0, handler);
});
}
};
})(jQuery, Drupal);

View file

@ -0,0 +1,172 @@
/**
* @file
* Javascript behaviors for custom webform #states.
*/
(function ($, Drupal) {
'use strict';
// Make absolutely sure the below event handlers are triggered after
// the /core/misc/states.js event handlers by attaching them after DOM load.
$(function () {
var $document = $(document);
$document.on('state:visible', function (e) {
if (!e.trigger) {
return TRUE;
}
if (!e.value) {
// @see https://www.sitepoint.com/jquery-function-clear-form-data/
$(':input', e.target).andSelf().each(function () {
var $input = $(this);
backupValueAndRequired(this);
clearValueAndRequired(this);
triggerEventHandlers(this);
});
}
else {
$(':input', e.target).andSelf().each(function () {
restoreValueAndRequired(this);
triggerEventHandlers(this);
});
}
});
$document.on('state:disabled', function (e) {
if (e.trigger) {
$(e.target).trigger('webform:disabled')
.find('select, input, textarea').trigger('webform:disabled');
}
});
});
/**
* Trigger an input's event handlers.
*
* @param input
* An input.
*/
function triggerEventHandlers(input) {
var $input = $(input);
var type = input.type;
var tag = input.tagName.toLowerCase(); // Normalize case.
if (type == 'checkbox' || type == 'radio') {
$input
.trigger('change')
.trigger('blur');
}
else if (tag == 'select') {
$input
.trigger('change')
.trigger('blur');
}
else if (type != 'submit' && type != 'button') {
$input
.trigger('input')
.trigger('change')
.trigger('keydown')
.trigger('keyup')
.trigger('blur');
}
}
/**
* Backup an input's current value and required attribute
*
* @param input
* An input.
*/
function backupValueAndRequired(input) {
var $input = $(input);
var type = input.type;
var tag = input.tagName.toLowerCase(); // Normalize case.
// Backup required.
if ($input.prop('required')) {
$input.data('webform-require', true);
}
// Backup value.
if (type == 'checkbox' || type == 'radio') {
$input.data('webform-value', $input.prop('checked'));
}
else if (tag == 'select') {
var values = [];
$input.find('option:selected').each(function (i, option) {
values[i] = option.value;
});
$input.data('webform-value', values);
}
else if (type != 'submit' && type != 'button') {
$input.data('webform-value', input.value);
}
}
/**
* Restore an input's value and required attribute.
*
* @param input
* An input.
*/
function restoreValueAndRequired(input) {
var $input = $(input);
// Restore value.
var value = $input.data('webform-value');
if (typeof value !== 'undefined') {
var type = input.type;
var tag = input.tagName.toLowerCase(); // Normalize case.
if (type == 'checkbox' || type == 'radio') {
$input.prop('checked', value)
}
else if (tag == 'select') {
$.each(value, function (i, option_value) {
$input.find("option[value='" + option_value + "']").prop("selected", true);
});
}
else if (type != 'submit' && type != 'button') {
input.value = value;
}
}
// Restore required.
if ($input.data('webform-required')) {
$input.prop('required', TRUE);
}
}
/**
* Clear an input's value and required attributes.
*
* @param input
* An input.
*/
function clearValueAndRequired(input) {
var $input = $(input);
// Clear value.
var type = input.type;
var tag = input.tagName.toLowerCase(); // Normalize case.
if (type == 'checkbox' || type == 'radio') {
$input.prop('checked', false);
}
else if (tag == 'select') {
if ($input.find('option[value=""]').length) {
$input.val('');
}
else {
input.selectedIndex = -1;
}
}
else if (type != 'submit' && type != 'button') {
input.value = (type == 'color') ? '#000000' : '';
}
// Clear required.
$input.prop('required', false);
}
})(jQuery, Drupal);

View file

@ -0,0 +1,46 @@
/**
* @file
* Javascript behaviors for jQuery UI tooltip integration.
*
* Please Note:
* jQuery UI's tooltip implementation is not very responsive or adaptive.
*
* @see https://www.drupal.org/node/2207383
*/
(function ($, Drupal) {
'use strict';
/**
* Initialize jQuery UI tooltip element support.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformTooltipElement = {
attach: function (context) {
$(context).find('.js-webform-tooltip-element').once('webform-tooltip-element').each(function () {
var $element = $(this);
var $description = $element.children('.description.visually-hidden');
$element.tooltip({
items: ':input',
content: $description.html()
});
});
}
};
/**
* Initialize jQuery UI tooltip link support.
*
* @type {Drupal~behavior}
*/
Drupal.behaviors.webformTooltipLink = {
attach: function (context) {
$(context).find('a.js-webform-tooltip-link').once('webform-tooltip-link').each(function () {
$(this).tooltip();
});
}
};
})(jQuery, Drupal);