PageSourceSearch

https://safpj.co.za/lib/pkp/js/controllers/form/FormHandler.js

js safpj.co.za collected 2026-10-02 10:26:15 UTC 12,324 bytes, 459 lines download raw bytes

1/**
2 * @defgroup js_controllers_form
3 */
4/**
5 * @file js/controllers/form/FormHandler.js
6 *
7 * Copyright (c) 2013-2019 Simon Fraser University
8 * Copyright (c) 2000-2019 John Willinsky
9 * Distributed under the GNU GPL v2. For full terms see the file docs/COPYING.
10 *
11 * @class FormHandler
12 * @ingroup js_controllers_form
13 *
14 * @brief Abstract form handler.
15 */
16(function($) {
17
18
19	/**
20	 * @constructor
21	 *
22	 * @extends $.pkp.classes.Handler
23	 *
24	 * @param {jQueryObject} $form the wrapped HTML form element.
25	 * @param {Object} options options to configure the form handler.
26	 */
27	$.pkp.controllers.form.FormHandler = function($form, options) {
28		this.parent($form, options);
29
30		// Check whether we really got a form.
31		if (!$form.is('form')) {
32			throw new Error(['A form handler controller can only be bound',
33				' to an HTML form element!'].join(''));
34		}
35
36		// Transform all form buttons with jQueryUI.
37		if (options.transformButtons !== false) {
38			$('.button', $form).button();
39		}
40
41		// Activate and configure the validation plug-in.
42		if (options.submitHandler) {
43			this.callerSubmitHandler_ = options.submitHandler;
44		}
45
46		// Set the redirect-to URL for the cancel button (if there is one).
47		if (options.cancelRedirectUrl) {
48			this.cancelRedirectUrl_ = options.cancelRedirectUrl;
49		}
50
51		// specific forms may override the form's default behavior
52		// to warn about unsaved changes.
53		if (typeof options.trackFormChanges !== 'undefined') {
54			this.trackFormChanges_ = options.trackFormChanges;
55		}
56
57		// disable submission controls on certain forms.
58		if (options.disableControlsOnSubmit) {
59			this.disableControlsOnSubmit = options.disableControlsOnSubmit;
60		}
61
62		if (options.enableDisablePairs) {
63			this.enableDisablePairs_ = options.enableDisablePairs;
64			this.setupEnableDisablePairs();
65		}
66
67		var validator = $form.validate({
68			onfocusout: false,
69			errorClass: 'error',
70			highlight: function(element, errorClass) {
71				$(element).parent().parent().addClass(errorClass);
72			},
73			unhighlight: function(element, errorClass) {
74				$(element).parent().parent().removeClass(errorClass);
75			},
76			submitHandler: this.callbackWrapper(this.submitHandler_),
77			showErrors: this.callbackWrapper(this.showErrors)
78		});
79
80		// Activate the cancel button (if present).
81		$('#cancelFormButton', $form).click(this.callbackWrapper(this.cancelForm));
82
83		// Activate the reset button (if present).
84		$('#resetFormButton', $form).click(this.callbackWrapper(this.resetForm));
85		$form.find('.showMore, .showLess').bind('click', this.switchViz);
86
87
88		// Initial form validation.
89		if (validator.checkForm()) {
90			this.trigger('formValid');
91		} else {
92			this.trigger('formInvalid');
93		}
94
95		this.initializeTinyMCE_();
96
97		// bind a handler to make sure tinyMCE fields are populated.
98		$('#submitFormButton', $form).click(this.callbackWrapper(
99				this.pushTinyMCEChanges_));
100
101		// bind a handler to handle change events on input fields.
102		$(':input', $form).change(this.callbackWrapper(this.formChange));
103	};
104	$.pkp.classes.Helper.inherits(
105			$.pkp.controllers.form.FormHandler,
106			$.pkp.classes.Handler);
107
108
109	//
110	// Private properties
111	//
112	/**
113	 * If provided, the caller's submit handler, which will be
114	 * triggered to save the form.
115	 * @private
116	 * @type {Function}
117	 */
118	$.pkp.controllers.form.FormHandler.prototype.callerSubmitHandler_ = null;
119
120
121	/**
122	 * If provided, the URL to redirect to when the cancel button is clicked
123	 * @private
124	 * @type {String}
125	 */
126	$.pkp.controllers.form.FormHandler.prototype.cancelRedirectUrl_ = null;
127
128
129	/**
130	 * By default, all FormHandler instances and subclasses track changes to
131	 * form data.
132	 * @private
133	 * @type {boolean}
134	 */
135	$.pkp.controllers.form.FormHandler.prototype.trackFormChanges_ = true;
136
137
138	/**
139	 * Only submit a track event for this form once.
140	 * @type {boolean}
141	 */
142	$.pkp.controllers.form.FormHandler.prototype.formChangesTracked = false;
143
144
145	/**
146	 * If true, the FormHandler will disable the submit button if the form
147	 * successfully validates and is submitted.
148	 * @protected
149	 * @type {boolean}
150	 */
151	$.pkp.controllers.form.FormHandler.prototype.disableControlsOnSubmit = false;
152
153
154	/**
155	 * An object containing items that should enable or disable each other.
156	 * @private
157	 * @type {Object}
158	 */
159	$.pkp.controllers.form.FormHandler.prototype.enableDisablePairs_ = null;
160
161
162	//
163	// Public methods
164	//
165	/**
166	 * Internal callback called whenever the validator has to show form errors.
167	 *
168	 * @param {Object} validator The validator plug-in.
169	 * @param {Object} errorMap An associative list that attributes
170	 *  element names to error messages.
171	 * @param {Array} errorList An array with objects that contains
172	 *  error messages and the corresponding HTMLElements.
173	 */
174	/*jslint unparam: true*/
175	$.pkp.controllers.form.FormHandler.prototype.showErrors =
176			function(validator, errorMap, errorList) {
177
178		// ensure that rich content elements have their
179		// values stored before validation.
180		if (typeof tinyMCE !== 'undefined') {
181			tinyMCE.triggerSave();
182		}
183
184		// Show errors generated by the form change.
185		validator.defaultShowErrors();
186
187		// Emit validation events.
188		if (validator.checkForm()) {
189			// Trigger a "form valid" event.
190			this.trigger('formValid');
191		} else {
192			// Trigger a "form invalid" event.
193			this.trigger('formInvalid');
194			this.enableFormControls();
195		}
196	};
197	/*jslint unparam: false*/
198
199
200	/**
201	 * Internal callback called when a form element changes.
202	 *
203	 * @param {HTMLElement} formElement The form element that generated the event.
204	 * @param {Event} event The formChange event.
205	 */
206	/*jslint unparam: true*/
207	$.pkp.controllers.form.FormHandler.prototype.formChange =
208			function(formElement, event) {
209
210		if (this.trackFormChanges_ && !this.formChangesTracked) {
211			this.trigger('formChanged');
212			this.formChangesTracked = true;
213		}
214	};
215	/*jslint unparam: false*/
216
217
218	//
219	// Protected methods
220	//
221	/**
222	 * Protected method to disable a form's submit control if it is
223	 * desired.
224	 *
225	 * @return {boolean} true.
226	 * @protected
227	 */
228	$.pkp.controllers.form.FormHandler.prototype.disableFormControls =
229			function() {
230
231		// We have made it to submission, disable the form control if
232		// necessary, submit the form.
233		if (this.disableControlsOnSubmit) {
234			this.getHtmlElement().find(':submit').attr('disabled', 'disabled').
235					addClass('ui-state-disabled');
236		}
237		return true;
238	};
239
240
241	/**
242	 * Protected method to reenable a form's submit control if it is
243	 * desired.
244	 *
245	 * @return {boolean} true.
246	 * @protected
247	 */
248	$.pkp.controllers.form.FormHandler.prototype.enableFormControls =
249			function() {
250
251		this.getHtmlElement().find(':submit').removeAttr('disabled').
252				removeClass('ui-state-disabled');
253		return true;
254	};
255
256
257	/**
258	 * Internal callback called to cancel the form.
259	 *
260	 * @param {HTMLElement} cancelButton The cancel button.
261	 * @param {Event} event The event that triggered the
262	 *  cancel button.
263	 * @return {boolean} false.
264	 */
265	/*jslint unparam: true*/
266	$.pkp.controllers.form.FormHandler.prototype.cancelForm =
267			function(cancelButton, event) {
268
269		// Trigger the "form canceled" event and unregister the form.
270		this.formChangesTracked = false;
271		this.trigger('unregisterChangedForm');
272		this.trigger('formCanceled');
273		return false;
274	};
275	/*jslint unparam: false*/
276
277
278	/**
279	 * Internal callback called to reset the form.
280	 *
281	 * @param {HTMLElement} resetButton The reset button.
282	 * @param {Event} event The event that triggered the
283	 *  reset button.
284	 * @return {boolean} false.
285	 */
286	/*jslint unparam: true*/
287	$.pkp.controllers.form.FormHandler.prototype.resetForm =
288			function(resetButton, event) {
289
290		//unregister the form.
291		this.formChangesTracked = false;
292		this.trigger('unregisterChangedForm');
293
294		var $form = this.getHtmlElement();
295		$form.each(function() {
296			this.reset();
297		});
298
299		return false;
300	};
301	/*jslint unparam: false*/
302
303
304	/**
305	 * Internal callback called to submit the form
306	 * without further validation.
307	 *
308	 * @param {Object} validator The validator plug-in.
309	 */
310	$.pkp.controllers.form.FormHandler.prototype.submitFormWithoutValidation =
311			function(validator) {
312
313		// NB: When setting a submitHandler in jQuery's validator
314		// plugin then the submit event will always be canceled and our
315		// return value will be ignored (see the handle() method in the
316		// validator plugin). The only way around this seems to be unsetting
317		// the submit handler before calling the submit method again.
318		validator.settings.submitHandler = null;
319		this.disableFormControls();
320		this.getHtmlElement().submit();
321		this.formChangesTracked = false;
322	};
323
324
325	//
326	// Private Methods
327	//
328	/**
329	 * Initialize TinyMCE instances.
330	 *
331	 * There are instances where TinyMCE is not initialized with the call to
332	 * init(). These occur when content is loaded after the fact (via AJAX).
333	 *
334	 * In these cases, search for richContent fields and initialize them.
335	 *
336	 * @private
337	 */
338	$.pkp.controllers.form.FormHandler.prototype.initializeTinyMCE_ =
339			function() {
340
341		if (typeof tinyMCE !== 'undefined') {
342			var $element, elementId;
343			$element = this.getHtmlElement();
344			elementId = $element.attr('id');
345			setTimeout(function() {
346				// re-select the original element, to prevent closure memory leaks
347				// in (older?) versions of IE.
348				$('#' + elementId).find('.richContent').each(function() {
349					tinyMCE.execCommand('mceAddControl', false,
350							$(this).attr('id').toString());
351				});
352			}, 500);
353		}
354	};
355
356
357	/**
358	 * Internal callback called after form validation to handle form
359	 * submission.
360	 *
361	 * NB: Returning from this method without explicitly submitting
362	 * the form will cancel form submission.
363	 *
364	 * @private
365	 *
366	 * @param {Object} validator The validator plug-in.
367	 * @param {HTMLElement} formElement The wrapped HTML form.
368	 */
369	$.pkp.controllers.form.FormHandler.prototype.submitHandler_ =
370			function(validator, formElement) {
371
372		// Notify any nested formWidgets of the submit action.
373		var formSubmitEvent = new $.Event('formSubmitRequested');
374		$(formElement).find('.formWidget').trigger(formSubmitEvent);
375
376		// If the default behavior was prevented for any reason, stop.
377		if (formSubmitEvent.isDefaultPrevented()) {
378			return;
379		}
380
381		$(formElement).find('.pkp_helpers_progressIndicator').show();
382
383		this.trigger('unregisterChangedForm');
384
385		if (this.callerSubmitHandler_ !== null) {
386			this.formChangesTracked = false;
387			// A form submission handler (e.g. Ajax) was provided. Use it.
388			this.callbackWrapper(this.callerSubmitHandler_).
389					call(validator, formElement);
390		} else {
391			// No form submission handler was provided. Use the usual method.
392			this.submitFormWithoutValidation(validator);
393		}
394	};
395
396
397	/**
398	 * Internal callback called to push TinyMCE changes back to fields
399	 * so they can be validated.
400	 *
401	 * @return {boolean} true.
402	 * @private
403	 */
404	$.pkp.controllers.form.FormHandler.prototype.pushTinyMCEChanges_ =
405			function() {
406		// ensure that rich content elements have their
407		// values stored before validation.
408		if (typeof tinyMCE !== 'undefined') {
409			tinyMCE.triggerSave();
410		}
411		return true;
412	};
413
414
415	/**
416	 * Configures the enable/disable pair bindings between a checkbox
417	 * and some other form element.
418	 *
419	 * @return {boolean} true.
420	 */
421	$.pkp.controllers.form.FormHandler.prototype.setupEnableDisablePairs =
422			function() {
423		var formElement, key;
424
425		formElement = this.getHtmlElement();
426		for (key in this.enableDisablePairs_) {
427			$(formElement).find("[id^='" + key + "']").bind(
428					'click', this.callbackWrapper(this.toggleDependentElement_));
429		}
430		return true;
431	};
432
433
434	/**
435	 * Enables or disables the item which depends on the state of source of the
436	 * Event.
437	 * @param {HTMLElement} sourceElement The element which generated the event.
438	 * @return {boolean} true.
439	 * @private
440	 */
441	$.pkp.controllers.form.FormHandler.prototype.toggleDependentElement_ =
442			function(sourceElement) {
443		var formElement, elementId, targetElement;
444
445		formElement = this.getHtmlElement();
446		elementId = $(sourceElement).attr('id');
447		targetElement = $(formElement).find(
448				"[id^='" + this.enableDisablePairs_[elementId] + "']");
449
450		if ($(sourceElement).is(':checked')) {
451			$(targetElement).attr('disabled', '');
452		} else {
453			$(targetElement).attr('disabled', 'disabled');
454		}
455
456		return true;
457	};
458/** @param {jQuery} $ jQuery closure. */
459}(jQuery));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.