(function ($) { Drupal.behaviors.textarea = { attach: function (context, settings) { $('.form-textarea-wrapper.resizable', context).once('textarea', function () { var staticOffset = null; var textarea = $(this).addClass('resizable-textarea').find('textarea'); var grippie = $('
').mousedown(startDrag); grippie.insertAfter(textarea); function startDrag(e) { staticOffset = textarea.height() - e.pageY; textarea.css('opacity', 0.25); $(document).mousemove(performDrag).mouseup(endDrag); return false; } function performDrag(e) { textarea.height(Math.max(32, staticOffset + e.pageY) + 'px'); return false; } function endDrag(e) { $(document).unbind('mousemove', performDrag).unbind('mouseup', endDrag); textarea.css('opacity', 1); } }); } }; })(jQuery); ; (function ($) { /** * A progressbar object. Initialized with the given id. Must be inserted into * the DOM afterwards through progressBar.element. * * method is the function which will perform the HTTP request to get the * progress bar state. Either "GET" or "POST". * * e.g. pb = new progressBar('myProgressBar'); * some_element.appendChild(pb.element); */ Drupal.progressBar = function (id, updateCallback, method, errorCallback) { var pb = this; this.id = id; this.method = method || 'GET'; this.updateCallback = updateCallback; this.errorCallback = errorCallback; // The WAI-ARIA setting aria-live="polite" will announce changes after users // have completed their current activity and not interrupt the screen reader. this.element = $('').attr('id', id); this.element.html(' ' + '' + ' '); }; /** * Set the percentage and status message for the progressbar. */ Drupal.progressBar.prototype.setProgress = function (percentage, message) { if (percentage >= 0 && percentage <= 100) { $('div.filled', this.element).css('width', percentage + '%'); $('div.percentage', this.element).html(percentage + '%'); } $('div.message', this.element).html(message); if (this.updateCallback) { this.updateCallback(percentage, message, this); } }; /** * Start monitoring progress via Ajax. */ Drupal.progressBar.prototype.startMonitoring = function (uri, delay) { this.delay = delay; this.uri = uri; this.sendPing(); }; /** * Stop monitoring progress via Ajax. */ Drupal.progressBar.prototype.stopMonitoring = function () { clearTimeout(this.timer); // This allows monitoring to be stopped from within the callback. this.uri = null; }; /** * Request progress data from server. */ Drupal.progressBar.prototype.sendPing = function () { if (this.timer) { clearTimeout(this.timer); } if (this.uri) { var pb = this; // When doing a post request, you need non-null data. Otherwise a // HTTP 411 or HTTP 406 (with Apache mod_security) error may result. $.ajax({ type: this.method, url: this.uri, data: '', dataType: 'json', success: function (progress) { // Display errors. if (progress.status == 0) { pb.displayError(progress.data); return; } // Update display. pb.setProgress(progress.percentage, progress.message); // Schedule next timer. pb.timer = setTimeout(function () { pb.sendPing(); }, pb.delay); }, error: function (xmlhttp) { pb.displayError(Drupal.ajaxError(xmlhttp, pb.uri)); } }); } }; /** * Display errors on the page. */ Drupal.progressBar.prototype.displayError = function (string) { var error = $('').html(string); $(this.element).before(error).hide(); if (this.errorCallback) { this.errorCallback(this); } }; })(jQuery); ; /** * JavaScript behaviors for the front-end display of webforms. */ (function ($) { Drupal.behaviors.webform = Drupal.behaviors.webform || {}; Drupal.behaviors.webform.attach = function(context) { // Calendar datepicker behavior. Drupal.webform.datepicker(context); // Conditional logic. if (Drupal.settings.webform && Drupal.settings.webform.conditionals) { Drupal.webform.conditional(context); } }; Drupal.webform = Drupal.webform || {}; Drupal.webform.datepicker = function(context) { $('div.webform-datepicker').each(function() { var $webformDatepicker = $(this); var $calendar = $webformDatepicker.find('input.webform-calendar'); // Ensure the page we're on actually contains a datepicker. if ($calendar.length == 0) { return; } var startDate = $calendar[0].className.replace(/.*webform-calendar-start-(\d{4}-\d{2}-\d{2}).*/, '$1').split('-'); var endDate = $calendar[0].className.replace(/.*webform-calendar-end-(\d{4}-\d{2}-\d{2}).*/, '$1').split('-'); var firstDay = $calendar[0].className.replace(/.*webform-calendar-day-(\d).*/, '$1'); // Convert date strings into actual Date objects. startDate = new Date(startDate[0], startDate[1] - 1, startDate[2]); endDate = new Date(endDate[0], endDate[1] - 1, endDate[2]); // Ensure that start comes before end for datepicker. if (startDate > endDate) { var laterDate = startDate; startDate = endDate; endDate = laterDate; } var startYear = startDate.getFullYear(); var endYear = endDate.getFullYear(); // Set up the jQuery datepicker element. $calendar.datepicker({ dateFormat: 'yy-mm-dd', yearRange: startYear + ':' + endYear, firstDay: parseInt(firstDay), minDate: startDate, maxDate: endDate, onSelect: function(dateText, inst) { var date = dateText.split('-'); $webformDatepicker.find('select.year, input.year').val(+date[0]).trigger('change'); $webformDatepicker.find('select.month').val(+date[1]).trigger('change'); $webformDatepicker.find('select.day').val(+date[2]).trigger('change'); }, beforeShow: function(input, inst) { // Get the select list values. var year = $webformDatepicker.find('select.year, input.year').val(); var month = $webformDatepicker.find('select.month').val(); var day = $webformDatepicker.find('select.day').val(); // If empty, default to the current year/month/day in the popup. var today = new Date(); year = year ? year : today.getFullYear(); month = month ? month : today.getMonth() + 1; day = day ? day : today.getDate(); // Make sure that the default year fits in the available options. year = (year < startYear || year > endYear) ? startYear : year; // jQuery UI Datepicker will read the input field and base its date off // of that, even though in our case the input field is a button. $(input).val(year + '-' + month + '-' + day); } }); // Prevent the calendar button from submitting the form. $calendar.click(function(event) { $(this).focus(); event.preventDefault(); }); }); }; Drupal.webform.conditional = function(context) { // Add the bindings to each webform on the page. $.each(Drupal.settings.webform.conditionals, function(formKey, settings) { var $form = $('.' + formKey + ':not(.webform-conditional-processed)'); $form.each(function(index, currentForm) { var $currentForm = $(currentForm); $currentForm.addClass('webform-conditional-processed'); $currentForm.bind('change', { 'settings': settings }, Drupal.webform.conditionalCheck); // Trigger all the elements that cause conditionals on this form. $.each(Drupal.settings.webform.conditionals[formKey]['sourceMap'], function(elementKey) { $currentForm.find('.' + elementKey).find('input,select,textarea').filter(':first').trigger('change'); }); }) }); }; /** * Event handler to respond to field changes in a form. * * This event is bound to the entire form, not individual fields. */ Drupal.webform.conditionalCheck = function(e) { var $triggerElement = $(e.target).closest('.webform-component'); var $form = $triggerElement.closest('form'); var triggerElementKey = $triggerElement.attr('class').match(/webform-component--[^ ]+/)[0]; var settings = e.data.settings; if (settings.sourceMap[triggerElementKey]) { $.each(settings.sourceMap[triggerElementKey], function(n, rgid) { var ruleGroup = settings.ruleGroups[rgid]; // Perform the comparison callback and build the results for this group. var conditionalResult = true; var conditionalResults = []; $.each(ruleGroup['rules'], function(m, rule) { var elementKey = rule['source']; var element = $form.find('.' + elementKey)[0]; var existingValue = settings.values[elementKey] ? settings.values[elementKey] : null; conditionalResults.push(window['Drupal']['webform'][rule.callback](element, existingValue, rule['value'] )); }); // Filter out false values. var filteredResults = []; for (var i = 0; i < conditionalResults.length; i++) { if (conditionalResults[i]) { filteredResults.push(conditionalResults[i]); } } // Calculate the and/or result. if (ruleGroup['andor'] === 'or') { conditionalResult = filteredResults.length > 0; } else { conditionalResult = filteredResults.length === conditionalResults.length; } // Flip the result of the action is to hide. var showComponent; if (ruleGroup['action'] == 'hide') { showComponent = !conditionalResult; } else { showComponent = conditionalResult; } var $target = $form.find('.' + ruleGroup['target']); var $targetElements; if (showComponent) { $targetElements = $target.find('.webform-conditional-disabled').removeClass('webform-conditional-disabled'); $.fn.prop ? $targetElements.prop('disabled', false) : $targetElements.removeAttr('disabled'); $target.show(); } else { $targetElements = $target.find(':input').addClass('webform-conditional-disabled'); $.fn.prop ? $targetElements.prop('disabled', true) : $targetElements.attr('disabled', true); $target.hide(); } }); } }; Drupal.webform.conditionalOperatorStringEqual = function(element, existingValue, ruleValue) { var returnValue = false; var currentValue = Drupal.webform.stringValue(element, existingValue); $.each(currentValue, function(n, value) { if (value.toLowerCase() === ruleValue.toLowerCase()) { returnValue = true; return false; // break. } }); return returnValue; }; Drupal.webform.conditionalOperatorStringNotEqual = function(element, existingValue, ruleValue) { var found = false; var currentValue = Drupal.webform.stringValue(element, existingValue); $.each(currentValue, function(n, value) { if (value.toLowerCase() === ruleValue.toLowerCase()) { found = true; } }); return !found; }; Drupal.webform.conditionalOperatorStringContains = function(element, existingValue, ruleValue) { var returnValue = false; var currentValue = Drupal.webform.stringValue(element, existingValue); $.each(currentValue, function(n, value) { if (value.toLowerCase().indexOf(ruleValue.toLowerCase()) > -1) { returnValue = true; return false; // break. } }); return returnValue; }; Drupal.webform.conditionalOperatorStringDoesNotContain = function(element, existingValue, ruleValue) { var found = false; var currentValue = Drupal.webform.stringValue(element, existingValue); $.each(currentValue, function(n, value) { if (value.toLowerCase().indexOf(ruleValue.toLowerCase()) > -1) { found = true; } }); return !found; }; Drupal.webform.conditionalOperatorStringBeginsWith = function(element, existingValue, ruleValue) { var returnValue = false; var currentValue = Drupal.webform.stringValue(element, existingValue); $.each(currentValue, function(n, value) { if (value.toLowerCase().indexOf(ruleValue.toLowerCase()) === 0) { returnValue = true; return false; // break. } }); return returnValue; }; Drupal.webform.conditionalOperatorStringEndsWith = function(element, existingValue, ruleValue) { var returnValue = false; var currentValue = Drupal.webform.stringValue(element, existingValue); $.each(currentValue, function(n, value) { if (value.toLowerCase().lastIndexOf(ruleValue.toLowerCase()) === value.length - ruleValue.length) { returnValue = true; return false; // break. } }); return returnValue; }; Drupal.webform.conditionalOperatorStringEmpty = function(element, existingValue, ruleValue) { var currentValue = Drupal.webform.stringValue(element, existingValue); var returnValue = true; $.each(currentValue, function(n, value) { if (value !== '') { returnValue = false; return false; // break. } }); return returnValue; }; Drupal.webform.conditionalOperatorStringNotEmpty = function(element, existingValue, ruleValue) { return !Drupal.webform.conditionalOperatorStringEmpty(element, existingValue, ruleValue); }; Drupal.webform.conditionalOperatorNumericEqual = function(element, existingValue, ruleValue) { // See float comparison: http://php.net/manual/en/language.types.float.php var currentValue = Drupal.webform.stringValue(element, existingValue); var epsilon = 0.000001; // An empty string does not match any number. return currentValue[0] === '' ? false : (Math.abs(parseFloat(currentValue[0]) - parseFloat(ruleValue)) < epsilon); }; Drupal.webform.conditionalOperatorNumericNotEqual = function(element, existingValue, ruleValue) { // See float comparison: http://php.net/manual/en/language.types.float.php var currentValue = Drupal.webform.stringValue(element, existingValue); var epsilon = 0.000001; // An empty string does not match any number. return currentValue[0] === '' ? true : (Math.abs(parseFloat(currentValue[0]) - parseFloat(ruleValue)) >= epsilon); }; Drupal.webform.conditionalOperatorNumericGreaterThan = function(element, existingValue, ruleValue) { var currentValue = Drupal.webform.stringValue(element, existingValue); return parseFloat(currentValue[0]) > parseFloat(ruleValue); }; Drupal.webform.conditionalOperatorNumericLessThan = function(element, existingValue, ruleValue) { var currentValue = Drupal.webform.stringValue(element, existingValue); return parseFloat(currentValue[0]) < parseFloat(ruleValue); }; Drupal.webform.conditionalOperatorDateEqual = function(element, existingValue, ruleValue) { var currentValue = Drupal.webform.timeValue(element, existingValue); return currentValue === ruleValue; }; Drupal.webform.conditionalOperatorDateBefore = function(element, existingValue, ruleValue) { var currentValue = Drupal.webform.dateValue(element, existingValue); return (currentValue !== false) && currentValue < ruleValue; }; Drupal.webform.conditionalOperatorDateAfter = function(element, existingValue, ruleValue) { var currentValue = Drupal.webform.dateValue(element, existingValue); return (currentValue !== false) && currentValue > ruleValue; }; Drupal.webform.conditionalOperatorTimeEqual = function(element, existingValue, ruleValue) { var currentValue = Drupal.webform.timeValue(element, existingValue); return currentValue === ruleValue; }; Drupal.webform.conditionalOperatorTimeBefore = function(element, existingValue, ruleValue) { // Date and time operators intentionally exclusive for "before". var currentValue = Drupal.webform.timeValue(element, existingValue); return (currentValue !== false) && (currentValue < ruleValue); }; Drupal.webform.conditionalOperatorTimeAfter = function(element, existingValue, ruleValue) { // Date and time operators intentionally inclusive for "after". var currentValue = Drupal.webform.timeValue(element, existingValue); return (currentValue !== false) && (currentValue >= ruleValue); }; /** * Utility function to get a string value from a select/radios/text/etc. field. */ Drupal.webform.stringValue = function(element, existingValue) { var value = []; if (element) { // Checkboxes and radios. $(element).find('input[type=checkbox]:checked,input[type=radio]:checked').each(function() { value.push(this.value); }); // Select lists. if (!value.length) { var selectValue = $(element).find('select').val(); if (selectValue) { value.push(selectValue); } } // Simple text fields. This check is done last so that the select list in // select-or-other fields comes before the "other" text field. if (!value.length) { $(element).find('input:not([type=checkbox],[type=radio]),textarea').each(function() { value.push(this.value); }); } } else if (existingValue) { value = existingValue; } return value; }; /** * Utility function to calculate a millisecond timestamp from a time field. */ Drupal.webform.dateValue = function(element, existingValue) { if (element) { var day = $(element).find('[name*=day]').val(); var month = $(element).find('[name*=month]').val(); var year = $(element).find('[name*=year]').val(); // Months are 0 indexed in JavaScript. if (month) { month--; } return (year !== '' && month !== '' && day !== '') ? Date.UTC(year, month, day) : false; } else { var existingValue = existingValue.length ? existingValue[0].split('-') : existingValue; return existingValue.length ? Date.UTC(existingValue[0], existingValue[1], existingValue[2]) : false; } }; /** * Utility function to calculate a millisecond timestamp from a time field. */ Drupal.webform.timeValue = function(element, existingValue) { if (element) { var hour = $(element).find('[name*=hour]').val(); var minute = $(element).find('[name*=minute]').val(); var ampm = $(element).find('[name*=ampm]:checked').val(); // Convert to integers if set. hour = (hour === '') ? hour : parseInt(hour); minute = (minute === '') ? minute : parseInt(minute); if (hour !== '') { hour = (hour < 12 && ampm == 'pm') ? hour + 12 : hour; hour = (hour === 12 && ampm == 'am') ? 0 : hour; } return (hour !== '' && minute !== '') ? Date.UTC(1970, 0, 1, hour, minute) : false; } else { var existingValue = existingValue.length ? existingValue[0].split(':') : existingValue; return existingValue.length ? Date.UTC(1970, 0, 1, existingValue[0], existingValue[1]) : false; } }; })(jQuery); ; /** * @file * Provides JavaScript additions to the managed file field type. * * This file provides progress bar support (if available), popup windows for * file previews, and disabling of other file fields during Ajax uploads (which * prevents separate file fields from accidentally uploading files). */ (function ($) { /** * Attach behaviors to managed file element upload fields. */ Drupal.behaviors.fileValidateAutoAttach = { attach: function (context, settings) { if (settings.file && settings.file.elements) { $.each(settings.file.elements, function(selector) { var extensions = settings.file.elements[selector]; $(selector, context).bind('change', {extensions: extensions}, Drupal.file.validateExtension); }); } }, detach: function (context, settings) { if (settings.file && settings.file.elements) { $.each(settings.file.elements, function(selector) { $(selector, context).unbind('change', Drupal.file.validateExtension); }); } } }; /** * Attach behaviors to the file upload and remove buttons. */ Drupal.behaviors.fileButtons = { attach: function (context) { $('input.form-submit', context).bind('mousedown', Drupal.file.disableFields); $('div.form-managed-file input.form-submit', context).bind('mousedown', Drupal.file.progressBar); }, detach: function (context) { $('input.form-submit', context).unbind('mousedown', Drupal.file.disableFields); $('div.form-managed-file input.form-submit', context).unbind('mousedown', Drupal.file.progressBar); } }; /** * Attach behaviors to links within managed file elements. */ Drupal.behaviors.filePreviewLinks = { attach: function (context) { $('div.form-managed-file .file a, .file-widget .file a', context).bind('click',Drupal.file.openInNewWindow); }, detach: function (context){ $('div.form-managed-file .file a, .file-widget .file a', context).unbind('click', Drupal.file.openInNewWindow); } }; /** * File upload utility functions. */ Drupal.file = Drupal.file || { /** * Client-side file input validation of file extensions. */ validateExtension: function (event) { // Remove any previous errors. $('.file-upload-js-error').remove(); // Add client side validation for the input[type=file]. var extensionPattern = event.data.extensions.replace(/,\s*/g, '|'); if (extensionPattern.length > 1 && this.value.length > 0) { var acceptableMatch = new RegExp('\\.(' + extensionPattern + ')$', 'gi'); if (!acceptableMatch.test(this.value)) { var error = Drupal.t("The selected file %filename cannot be uploaded. Only files with the following extensions are allowed: %extensions.", { // According to the specifications of HTML5, a file upload control // should not reveal the real local path to the file that a user // has selected. Some web browsers implement this restriction by // replacing the local path with "C:\fakepath\", which can cause // confusion by leaving the user thinking perhaps Drupal could not // find the file because it messed up the file path. To avoid this // confusion, therefore, we strip out the bogus fakepath string. '%filename': this.value.replace('C:\\fakepath\\', ''), '%extensions': extensionPattern.replace(/\|/g, ', ') }); $(this).closest('div.form-managed-file').prepend(' '); this.value = ''; return false; } } }, /** * Prevent file uploads when using buttons not intended to upload. */ disableFields: function (event){ var clickedButton = this; // Only disable upload fields for Ajax buttons. if (!$(clickedButton).hasClass('ajax-processed')) { return; } // Check if we're working with an "Upload" button. var $enabledFields = []; if ($(this).closest('div.form-managed-file').length > 0) { $enabledFields = $(this).closest('div.form-managed-file').find('input.form-file'); } // Temporarily disable upload fields other than the one we're currently // working with. Filter out fields that are already disabled so that they // do not get enabled when we re-enable these fields at the end of behavior // processing. Re-enable in a setTimeout set to a relatively short amount // of time (1 second). All the other mousedown handlers (like Drupal's Ajax // behaviors) are excuted before any timeout functions are called, so we // don't have to worry about the fields being re-enabled too soon. // @todo If the previous sentence is true, why not set the timeout to 0? var $fieldsToTemporarilyDisable = $('div.form-managed-file input.form-file').not($enabledFields).not(':disabled'); $fieldsToTemporarilyDisable.attr('disabled', 'disabled'); setTimeout(function (){ $fieldsToTemporarilyDisable.attr('disabled', false); }, 1000); }, /** * Add progress bar support if possible. */ progressBar: function (event) { var clickedButton = this; var $progressId = $(clickedButton).closest('div.form-managed-file').find('input.file-progress'); if ($progressId.length) { var originalName = $progressId.attr('name'); // Replace the name with the required identifier. $progressId.attr('name', originalName.match(/APC_UPLOAD_PROGRESS|UPLOAD_IDENTIFIER/)[0]); // Restore the original name after the upload begins. setTimeout(function () { $progressId.attr('name', originalName); }, 1000); } // Show the progress bar if the upload takes longer than half a second. setTimeout(function () { $(clickedButton).closest('div.form-managed-file').find('div.ajax-progress-bar').slideDown(); }, 500); }, /** * Open links to files within forms in a new window. */ openInNewWindow: function (event) { $(this).attr('target', '_blank'); window.open(this.href, 'filePreview', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=500,height=550'); return false; } }; })(jQuery); ;