PageSourceSearch

https://sajce.co.za/lib/pkp/js/controllers/SiteHandler.js

js sajce.co.za collected 2026-10-02 10:28:14 UTC 14,515 bytes, 510 lines download raw bytes

1/**
2 * @defgroup js_controllers
3 */
4/**
5 * @file js/controllers/SiteHandler.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 SiteHandler
12 * @ingroup js_controllers
13 *
14 * @brief Handle the site widget.
15 */
16(function($) {
17
18
19	/**
20	 * @constructor
21	 *
22	 * @extends $.pkp.classes.Handler
23	 *
24	 * @param {jQueryObject} $widgetWrapper An HTML element that this handle will
25	 * be attached to.
26	 * @param {{
27	 *   toggleHelpUrl: string,
28	 *   toggleHelpOffText: string,
29	 *   toggleHelpOnText: string,
30	 *   fetchNotificationUrl: string,
31	 *   requestOptions: Object
32	 *   }} options Handler options.
33	 */
34	$.pkp.controllers.SiteHandler = function($widgetWrapper, options) {
35		this.parent($widgetWrapper, options);
36
37		this.options_ = options;
38		this.unsavedFormElements_ = [];
39
40		$('.go').button();
41
42		this.bind('redirectRequested', this.redirectToUrl);
43		this.bind('notifyUser', this.fetchNotificationHandler_);
44		this.bind('updateHeader', this.updateHeaderHandler_);
45		this.bind('updateSidebar', this.updateSidebarHandler_);
46		this.bind('callWhenClickOutside', this.callWhenClickOutsideHandler_);
47
48		// Listen for grid initialized events so the inline help
49		// can be shown or hidden.
50		this.bind('gridInitialized', this.updateHelpDisplayHandler_);
51
52		// Listen for help toggle events.
53		this.bind('toggleInlineHelp', this.toggleInlineHelpHandler_);
54
55		// Bind the pageUnloadHandler_ method to the DOM so it is
56		// called.
57		$(window).bind('beforeunload', this.pageUnloadHandler_);
58
59		// Avoid IE8 caching ajax results. If it does, widgets like
60		// grids will not refresh correctly.
61		$.ajaxSetup({cache: false});
62
63		this.setMainMaxWidth_();
64
65		// Check if we have notifications to show.
66		if (options.hasSystemNotifications) {
67			this.trigger('notifyUser');
68		}
69
70		// bind event handlers for form status change events.
71		this.bind('formChanged', this.callbackWrapper(
72				this.registerUnsavedFormElement_));
73		this.bind('unregisterChangedForm', this.callbackWrapper(
74				this.unregisterUnsavedFormElement_));
75		this.bind('modalCanceled', this.callbackWrapper(
76				this.unregisterUnsavedFormElement_));
77		this.bind('unregisterAllForms', this.callbackWrapper(
78				this.unregisterAllFormElements_));
79	};
80	$.pkp.classes.Helper.inherits(
81			$.pkp.controllers.SiteHandler, $.pkp.classes.Handler);
82
83
84	//
85	// Private properties
86	//
87	/**
88	 * Site handler options.
89	 * @private
90	 * @type {Object}
91	 */
92	$.pkp.controllers.SiteHandler.prototype.options_ = null;
93
94
95	/**
96	 * A state variable to store the form elements that have unsaved data.
97	 * @private
98	 * @type {Array}
99	 */
100	$.pkp.controllers.SiteHandler.prototype.unsavedFormElements_ = null;
101
102
103	//
104	// Public methods
105	//
106	/**
107	 * Callback that is triggered when the page should redirect.
108	 *
109	 * @param {HTMLElement} sourceElement The element that issued the
110	 *  "redirectRequested" event.
111	 * @param {Event} event The "redirect requested" event.
112	 * @param {string} url The URL to redirect to.
113	 */
114	/*jslint unparam: true*/
115	$.pkp.controllers.SiteHandler.prototype.redirectToUrl =
116			function(sourceElement, event, url) {
117		window.location = url;
118	};
119	/*jslint unparam: false*/
120
121
122	/**
123	 * Handler bound to 'formChanged' events propagated by forms
124	 * that wish to have their form data tracked.
125	 *
126	 * @param {HTMLElement} siteHandlerElement The html element
127	 * attached to this handler.
128	 * @param {HTMLElement} sourceElement The element wishes to
129	 * register.
130	 * @param {Event} event The formChanged event.
131	 * @private
132	 */
133	/*jslint unparam: true*/
134	$.pkp.controllers.SiteHandler.prototype.registerUnsavedFormElement_ =
135			function(siteHandlerElement, sourceElement, event) {
136		var $formElement, formId, index;
137
138		$formElement = $(event.target.lastElementChild);
139		formId = $formElement.attr('id');
140		index = $.inArray(formId, this.unsavedFormElements_);
141		if (index == -1) {
142			this.unsavedFormElements_.push(formId);
143		}
144	};
145	/*jslint unparam: false*/
146
147
148	/**
149	 * Handler bound to 'unregisterChangedForm' events propagated by forms
150	 * that wish to inform that they no longer wish to be tracked as 'unsaved'.
151	 *
152	 * @param {HTMLElement} siteHandlerElement The html element
153	 * attached to this handler.
154	 * @param {HTMLElement} sourceElement The element that wishes to
155	 * unregister.
156	 * @param {Event} event The unregisterChangedForm event.
157	 * @private
158	 */
159	/*jslint unparam: true*/
160	$.pkp.controllers.SiteHandler.prototype.unregisterUnsavedFormElement_ =
161			function(siteHandlerElement, sourceElement, event) {
162		var $formElement, formId, index;
163
164		$formElement = $(event.target.lastElementChild);
165		formId = $formElement.attr('id');
166		index = $.inArray(formId, this.unsavedFormElements_);
167		if (index !== -1) {
168			delete this.unsavedFormElements_[index];
169		}
170	};
171	/*jslint unparam: false*/
172
173
174	/**
175	 * Unregister all unsaved form elements.
176	 * @private
177	 */
178	$.pkp.controllers.SiteHandler.prototype.unregisterAllFormElements_ =
179			function() {
180		this.unsavedFormElements_ = [];
181	};
182
183
184	//
185	// Private methods.
186	//
187	/**
188	 * Respond to a user toggling the display of inline help.
189	 *
190	 * @return {boolean} Always returns false.
191	 * @private
192	 */
193	$.pkp.controllers.SiteHandler.prototype.toggleInlineHelpHandler_ =
194			function() {
195		// persist the change on the server.
196		$.ajax({url: this.options_.toggleHelpUrl});
197
198		this.options_.inlineHelpState = this.options_.inlineHelpState ? 0 : 1;
199		this.updateHelpDisplayHandler_();
200
201		// Stop further event processing
202		return false;
203	};
204
205
206	/**
207	 * Callback to listen to grid initialization events. Used to
208	 * toggle the inline help display on them.
209	 *
210	 * @private
211	 */
212	$.pkp.controllers.SiteHandler.prototype.updateHelpDisplayHandler_ =
213			function() {
214		var $bodyElement, inlineHelpState;
215
216		$bodyElement = this.getHtmlElement();
217		inlineHelpState = this.options_.inlineHelpState;
218		if (inlineHelpState) {
219			// the .css() call removes the CSS applied to the legend intially,
220			// so it is not shown while the page is being loaded.
221			$bodyElement.find('.pkp_grid_description, #legend, .pkp_help').
222					css('visibility', 'visible').show();
223			$bodyElement.find('[id^="toggleHelp"]').html(
224					this.options_.toggleHelpOffText);
225		} else {
226			$bodyElement.find('.pkp_grid_description, #legend, .pkp_help').hide();
227			$bodyElement.find('[id^="toggleHelp"]').html(
228					this.options_.toggleHelpOnText);
229		}
230	};
231
232
233	/**
234	 * Fetch the notification data.
235	 * @param {HTMLElement} sourceElement The element that issued the
236	 *  "fetchNotification" event.
237	 * @param {Event} event The "fetch notification" event.
238	 * @param {Object} jsonData The JSON content representing the
239	 *  notification.
240	 * @private
241	 */
242	/*jslint unparam: true*/
243	$.pkp.controllers.SiteHandler.prototype.fetchNotificationHandler_ =
244			function(sourceElement, event, jsonData) {
245
246		if (jsonData !== undefined) {
247			// This is an event that came from an inplace notification
248			// widget that was not visible because of the scrolling.
249			this.showNotification_(jsonData);
250			return;
251		}
252
253		// Avoid race conditions with in place notifications.
254		$.ajax({
255			url: this.options_.fetchNotificationUrl,
256			data: this.options_.requestOptions,
257			success: this.callbackWrapper(this.showNotificationResponseHandler_),
258			dataType: 'json',
259			async: false
260		});
261	};
262	/*jslint unparam: false*/
263
264
265	/**
266	 * Fetch the header (e.g. on header configuration change).
267	 * @private
268	 */
269	$.pkp.controllers.SiteHandler.prototype.updateHeaderHandler_ =
270			function() {
271		var handler = $.pkp.classes.Handler.getHandler($('#headerContainer'));
272		handler.reload();
273	};
274
275
276	/**
277	 * Fetch the sidebar (e.g. on sidebar configuration change).
278	 * @private
279	 */
280	$.pkp.controllers.SiteHandler.prototype.updateSidebarHandler_ =
281			function() {
282		var handler = $.pkp.classes.Handler.getHandler($('#sidebarContainer'));
283		handler.reload();
284	};
285
286
287	/**
288	 * Binds a click event to this element so we can track if user
289	 * clicked outside the passed element or not.
290	 * @private
291	 * @param {HTMLElement} sourceElement The element that issued the
292	 *  callWhenClickOutside event.
293	 * @param {Event} event The "call when click outside" event.
294	 * @param {{
295	 *   container: jQueryObject,
296	 *   callback: Function,
297	 *   skipWhenVisibleModals: boolean
298	 *   }}
298 eventParams The event parameters.
299	 * - container: a jQuery element to be used to test if user click
300	 * outside of it or not.
301	 * - callback: a callback function in case test is true.
302	 * - skipWhenVisibleModals: boolean flag to tell whether skip the
303	 * callback when modals are visible or not.
304	 */
305	/*jslint unparam: true*/
306	$.pkp.controllers.SiteHandler.prototype.callWhenClickOutsideHandler_ =
307			function(sourceElement, event, eventParams) {
308		if (this.callWhenClickOutsideEventParams_ !== undefined) {
309			throw new Error('Another widget is already using this structure.');
310		}
311
312		this.callWhenClickOutsideEventParams_ = eventParams;
313		setTimeout(this.callbackWrapper(function() {
314			this.bind('mousedown', this.checkOutsideClickHandler_);
315		}), 25);
316	};
317	/*jslint unparam: false*/
318
319
320	/**
321	 * Mouse down event handler, used by the callWhenClickOutside event handler
322	 * to test if user clicked outside an element or not. If true, will
323	 * callback a function. Can optionally avoid the callback
324	 * when a modal widget is loaded.
325	 * @private
326	 * @param {HTMLElement} sourceElement The element that issued the
327	 *  click event.
328	 * @param {Event} event The "mousedown" event.
329	 * @return {?boolean} Event handling status.
330	 */
331	/*jslint unparam: true*/
332	$.pkp.controllers.SiteHandler.prototype.checkOutsideClickHandler_ =
333			function(sourceElement, event) {
334		var $container, callback;
335
336		if (this.callWhenClickOutsideEventParams_ !== undefined) {
337			// Start checking the paramenters.
338			if (this.callWhenClickOutsideEventParams_.container !== undefined) {
339				// Store the container element.
340				$container = this.callWhenClickOutsideEventParams_.container;
341			} else {
342				// Need a container, return.
343				return false;
344			}
345
346			if (this.callWhenClickOutsideEventParams_.callback !== undefined) {
347				// Store the callback.
348				callback = this.callWhenClickOutsideEventParams_.callback;
349			} else {
350				// Need the callback, return.
351				return false;
352			}
353
354			if (this.callWhenClickOutsideEventParams_.skipWhenVisibleModals !==
355					undefined) {
356				if (this.callWhenClickOutsideEventParams_.skipWhenVisibleModals) {
357					if (this.getHtmlElement().find('div.ui-dialog').length > 0) {
358						// Found a modal, return.
359						return false;
360					}
361				}
362			}
363
364			// Do the click origin checking.
365			if ($container.has(event.target).length === 0) {
366				// Unbind this click handler.
367				this.unbind('mousedown', this.checkOutsideClickHandler_);
368
369				// Clean the original event parameters data.
370				this.callWhenClickOutsideEventParams_ = null;
371
372				if (!$container.is(':hidden')) {
373					// Only considered outside if the container is visible.
374					callback();
375				}
376			}
377		}
378
379		return false;
380	};
381	/*jslint unparam: false*/
382
383
384	/**
385	 * Internal callback called upon page unload. If it returns
386	 * anything other than void, a message will be displayed to
387	 * the user.
388	 *
389	 * @private
390	 *
391	 * @return {?string} The warning message string, if needed.
392	 */
393	$.pkp.controllers.SiteHandler.prototype.pageUnloadHandler_ =
394			function() {
395		var handler, unsavedElementCount, element;
396
397		// any registered and then unregistered forms will exist
398		// as properties in the unsavedFormElements_ object. They
399		// will just be undefined.  See if there are any that are
400		// not.
401
402		// we need to get the handler this way since this event is bound
403		// to window, not to SiteHandler.
404		handler = $.pkp.classes.Handler.getHandler($('body'));
405
406		unsavedElementCount = 0;
407		for (element in handler.unsavedFormElements_) {
408			if (element) {
409				unsavedElementCount++;
410			}
411		}
412		if (unsavedElementCount > 0) {
413			return $.pkp.locale.form_dataHasChanged;
414		}
415		return null;
416	};
417
418
419	/**
420	 * Method to determine if a form is currently registered as having
421	 * unsaved changes.
422	 *
423	 * @param {string} id the id of the form to check.
424	 * @return {boolean} true if the form is unsaved.
425	 */
426	$.pkp.controllers.SiteHandler.prototype.isFormUnsaved =
427			function(id) {
428
429		if (this.unsavedFormElements_ !== null &&
430				this.unsavedFormElements_[id] !== undefined) {
431			return true;
432		}
433		return false;
434	};
435
436
437	/**
438	 * Response handler to the notification fetch.
439	 *
440	 * @param {Object} ajaxContext The data returned from the server.
441	 * @param {Object} jsonData A parsed JSON response object.
442	 * @private
443	 */
444	/*jslint unparam: true*/
445	$.pkp.controllers.SiteHandler.prototype.showNotificationResponseHandler_ =
446			function(ajaxContext, jsonData) {
447		this.showNotification_(jsonData);
448	};
449	/*jslint unparam: false*/
450
451
452	//
453	// Private helper method.
454	//
455	/**
456	 * Show the notification content.
457	 *
458	 * @param {Object} jsonData The JSON-encoded notification data.
459	 * @private
460	 */
461	$.pkp.controllers.SiteHandler.prototype.showNotification_ =
462			function(jsonData) {
463		var workingJsonData, notificationsData, levelId, notificationId;
464
465		workingJsonData = this.handleJson(jsonData);
466		if (workingJsonData !== false) {
467			if (workingJsonData.content.general) {
468				notificationsData = workingJsonData.content.general;
469				for (levelId in notificationsData) {
470					for (notificationId in notificationsData[levelId]) {
471						$.pnotify(notificationsData[levelId][notificationId]);
472					}
473				}
474			}
475		}
476	};
477
478
479	/**
480	 * Set the maximum width for the pkp_structure_main div.
481	 * This will prevent content with larger widths (like photos)
482	 * messing up with layout.
483	 * @private
484	 */
485	$.pkp.controllers.SiteHandler.prototype.setMainMaxWidth_ =
486			function() {
487		var $site, structureContentWidth, leftSideBarWidth, rightSideBarWidth,
488				$mainDiv, mainExtraWidth, mainMaxWidth;
489
490		$site = this.getHtmlElement();
491		structureContentWidth = $('.pkp_structure_content', $site).width();
492
493		leftSideBarWidth = $('.pkp_structure_sidebar_left', $site).
494				outerWidth(true);
495		rightSideBarWidth = $('.pkp_structure_sidebar_right', $site).
496				outerWidth(true);
497
498		$mainDiv = $('.pkp_structure_main', $site);
499
500		// Check for padding, margin or border.
501		mainExtraWidth = $mainDiv.outerWidth(true) - $mainDiv.width();
502		mainMaxWidth = structureContentWidth - (
503				leftSideBarWidth + rightSideBarWidth + mainExtraWidth);
504
505		$mainDiv.css('max-width', mainMaxWidth);
506	};
507
508
509/** @param {jQuery} $ jQuery closure. */
510}(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.