PageSourceSearch

https://ajopa.org/lib/pkp/js/classes/Helper.js

js ajopa.org collected 2026-10-08 19:58:13 UTC 10,732 bytes, 357 lines download raw bytes

1/**
2 * @defgroup js_classes
3 */
4
5/**
6 * @file js/classes/Helper.js
7 *
8 * Copyright (c) 2013-2019 Simon Fraser University
9 * Copyright (c) 2000-2019 John Willinsky
10 * Distributed under the GNU GPL v2. For full terms see the file docs/COPYING.
11 *
12 * @class Helper
13 * @ingroup js_controllers
14 *
15 * @brief PKP helper methods
16 */
17(function($) {
18
19	// Create PKP namespaces.
20	/** @type {Object} */
21	$.pkp = $.pkp || { };
22
23
24	/** @type {Object} */
25	$.pkp.classes = $.pkp.classes || { };
26
27
28	/** @type {Object} */
29	$.pkp.controllers = $.pkp.controllers || { };
30
31
32	/** @type {Object} */
33	$.pkp.controllers.form = $.pkp.controllers.form || {};
34
35
36	/** @type {Object} */
37	$.pkp.plugins = $.pkp.plugins || {};
38
39
40	/** @type {Object} */
41	$.pkp.plugins.blocks = $.pkp.plugins.blocks || {};
42
43
44	/** @type {Object} */
45	$.pkp.plugins.generic = $.pkp.plugins.generic || {};
46
47
48
49	/**
50	 * Helper singleton
51	 * @constructor
52	 *
53	 * @extends $.pkp.classes.ObjectProxy
54	 */
55	$.pkp.classes.Helper = function() {
56		throw new Error('Trying to instantiate the Helper singleton!');
57	};
58
59
60	//
61	// Private class constants
62	//
63	/**
64	 * Characters available for UUID generation.
65	 * @const
66	 * @private
67	 * @type {Array}
68	 */
69	$.pkp.classes.Helper.CHARS_ = ['0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
70		'abcdefghijklmnopqrstuvwxyz'].join('').split('');
71
72
73	//
74	// Public static helper methods
75	//
76	/**
77	 * Generate a random UUID.
78	 *
79	 * Original code thanks to Robert Kieffer <[email protected]>,
80	 * http://www.broofa.com, adapted by PKP.
81	 *
82	 * Copyright (c) 2010 Robert Kieffer
83	 * Copyright (c) 2010-2019 John Willinsky
84	 * Distributed under the GNU GPL v2 and MIT licenses. For full
85	 * terms see the file docs/COPYING.
86	 *
87	 * See discussion of randomness versus uniqueness:
88	 * http://www.broofa.com/2008/09/javascript-uuid-function/
89	 *
90	 * @return {string} an RFC4122v4 compliant UUID.
91	 */
92	$.pkp.classes.Helper.uuid = function() {
93		var chars = $.pkp.classes.Helper.CHARS_, uuid = new Array(36), rnd = 0, r, i;
94		for (i = 0; i < 36; i++) {
95			if (i == 8 || i == 13 || i == 18 || i == 23) {
96				uuid[i] = '-';
97			} else if (i == 14) {
98				uuid[i] = '4';
99			} else {
100				/*jslint bitwise: true*/
101				if (rnd <= 0x02) {
102					rnd = 0x2000000 + (Math.random() * 0x1000000) | 0;
103				}
104				r = rnd & 0xf;
105				rnd = rnd >> 4;
106				uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
107				/*jslint bitwise: false*/
108			}
109		}
110		return uuid.join('');
111	};
112
113
114	/**
115	 * Let one object inherit from another.
116	 *
117	 * Example:
118	 *  $.pkp.classes.Parent = function() {...};
119	 *  $.pkp.classes.Child = function() {...};
120	 *  $.pkp.classes.Helper.inherits($.pkp.classes.Child, $.pkp.classes.Parent);
121	 *
122	 * @param {Function} Child Constructor of the child object.
123	 * @param {Function} Parent Constructor of the parent object.
124	 */
125	$.pkp.classes.Helper.inherits = function(Child, Parent) {
126		// Use an empty temporary object to avoid
127		// calling a potentially costly constructor
128		// on the parent object which also may have
129		// undesired side effects. Also avoids instantiating
130		// a potentially big object.
131		/** @constructor */ var Temp = function() {
132			return this;
133		};
134		Temp.prototype = Parent.prototype;
135
136		// Provide a way to reach the parent's
137		// method implementations even after
138		// overriding them in the child object.
139		Child.parent_ = Parent.prototype;
140
141		// Let the child object inherit from
142		// the parent object.
143		Child.prototype = new Temp();
144
145		// Need to fix the child constructor because
146		// it get's lost when setting the prototype
147		// to an object instance.
148		Child.prototype.constructor = Child;
149
150		// Make sure that we can always call the parent object's
151		// constructor without coupling the child constructor
152		// to it. This should work even when the parent inherits
153		// directly from an Object instance (i.e. the parent's
154		// prototype was set like this: Parent.prototype = {...})
155		// which wipes out the original constructor.
156		if (Parent.prototype.constructor == Object.prototype.constructor) {
157			Parent.prototype.constructor = Parent;
158		}
159	};
160
161
162	/**
163	 * Introduce a central object factory that maintains some
164	 * level of indirection so that we can enrich objects, e.g.
165	 * with aspects, provide different runtime-implementations
166	 * of objects, distinguish between singletons and prototypes
167	 * or even implement dependency injection if we want to.
168	 *
169	 * The standard implementation has a 'convention over
170	 * configuration' approach that assumes that an object's
171	 * name corresponds to the name of a constructor within
172	 * the global jQuery namespace ($).
173	 *
174	 * The factory also helps us to avoid the common pitfall to
175	 * use a constructor without the 'new' keyword.
176	 *
177	 * @param {string} objectName The name of an object.
178	 * @param {Array} args The arguments to be passed
179	 *  into the object's constructor.
180	 * @return {$.pkp.classes.ObjectProxy} the instantiated object.
181	 */
182	$.pkp.classes.Helper.objectFactory = function(objectName, args) {
183		var ObjectConstructor, ObjectProxyInstance, objectInstance;
184
185		// Resolve the object name.
186		ObjectConstructor = $.pkp.classes.Helper.resolveObjectName(objectName);
187
188		// Create a new proxy constructor instance.
189		ObjectProxyInstance = $.pkp.classes.Helper.getObjectProxyInstance();
190
191		// Copy static members over from the object proxy. (This may
192		// overwrite the proxy constructor's prototype in some
193		// browsers but we don't care because we'll replace the prototype
194		// anyway when we inherit.)
195		$.extend(true, ObjectProxyInstance, $.pkp.classes.ObjectProxy);
196
197		// Let the proxy inherit from the proxied object.
198		$.pkp.classes.Helper.inherits(ObjectProxyInstance, ObjectConstructor);
199
200		// Enrich the new proxy constructor prototype with proxy object
201		// prototype members.
202		$.extend(true, ObjectProxyInstance.prototype,
203				$.pkp.classes.ObjectProxy.prototype);
204
205		// Instantiate the proxy with the proxied object.
206		objectInstance = new ObjectProxyInstance(objectName, args);
207		return objectInstance;
208	};
209
210
211	/**
212	 * Resolves the given object name to an object implementation
213	 * (or better to it's constructor).
214	 * @param {string} objectName The object name to resolve.
215	 * @return {Function} The constructor of the object.
216	 */
217	$.pkp.classes.Helper.resolveObjectName = function(objectName) {
218		var objectNameParts, i, functionName, ObjectConstructor;
219
220		// Currently only objects in the $ namespace are
221		// supported.
222		objectNameParts = objectName.split('.');
223		if (objectNameParts.shift() != '$') {
224			throw new Error(['Namespace "', objectNameParts[0], '" for object"',
225				objectName, '" is currently not supported!'].join(''));
226		}
227
228		// Make sure that we actually have a constructor name
229		// (starts with an upper case letter).
230		functionName = objectNameParts[objectNameParts.length - 1];
231		if (functionName.charAt(0).toUpperCase() !== functionName.charAt(0)) {
232			throw new Error(['The name "', objectName, '" does not point to a',
233				'constructor which must always be upper case!'].join(''));
234		}
235
236		// Run through the namespace and identify the constructor.
237		ObjectConstructor = $;
238		for (i in objectNameParts) {
239			ObjectConstructor = ObjectConstructor[objectNameParts[i]];
240			if (ObjectConstructor === undefined) {
241				throw new Error(['Constructor for object "', objectName, '" not found!']
242						.join(''));
243			}
244		}
245
246		// Check that the constructor actually is a function.
247		if (!$.isFunction(ObjectConstructor)) {
248			throw new Error(['The name "', objectName, '" does not point to a',
249				'constructor which must always be a function!'].join());
250		}
251
252		return ObjectConstructor;
253	};
254
255
256	/**
257	 * Create a new instance of a proxy constructor.
258	 *
259	 * NB: We do this in a separate closure to avoid
260	 * memory leaks.
261	 *
262	 * @return {Function} a new proxy instance.
263	 */
264	$.pkp.classes.Helper.getObjectProxyInstance = function() {
265		// Create a new proxy constructor so that proxies
266		// do not interfere with each other.
267		/**
268		 * @constructor
269		 *
270		 * @param {string} objectName The name of the proxied
271		 *  object.
272		 * @param {Array} args The arguments to be passed to
273		 *  the constructor of the proxied object.
274		 */
275		var proxyConstructor = function(objectName, args) {
276			// Set the internal object name.
277			this.objectName_ = objectName;
278
279			// Call the constructor of the proxied object.
280			this.parent.apply(this, args);
281		};
282
283		// Declare properties/methods used in the constructor for the
284		// closure compiler. These will later be overwritten by the
285		// true implementation.
286		/**
287		 * @private
288		 * @type {string} The object name of this object.
289		 */
290		proxyConstructor.objectName_ = '';
291
292		/**
293		 * @param {*=} opt_methodName The name of the method to
294		 *  be found. Do not set when calling this method from a
295		 *  constructor!
296		 * @param {...*} var_args Arguments to be passed to the
297		 *  parent method.
298		 * @return {*} The return value of the parent method.
299		 */
300		/*jslint unparam: true*/
301		proxyConstructor.prototype.parent = function(opt_methodName, var_args) {
302			return this;
303		};
304		/*jslint unparam: false*/
305
306
307		return proxyConstructor;
308	};
309
310
311	/**
312	 * Inject (mix in) an interface into an object.
313	 * @param {Function} Constructor The target object's constructor.
314	 * @param {string} mixinObjectName The object name of interface
315	 *  that can be resolved to an interface implementation by the
316	 *  object factory.
317	 */
318	$.pkp.classes.Helper.injectMixin = function(Constructor, mixinObjectName) {
319		// Retrieve an instance of the mix-in interface implementation.
320		var mixin = $.pkp.classes.Helper.objectFactory(mixinObjectName, []);
321
322		// Inject the mix-in into the target constructor.
323		$.extend(true, Constructor, mixin);
324	};
325
326
327	/**
328	 * A function currying implementation borrowed from Google Closure.
329	 * Additional arguments can be passed, that are partially
330	 * applied to the function.
331	 * @param {Function} fn A function to partially apply.
332	 * @param {Object} context Specifies the object which |this| should
333	 *  point to when the function is run. If the value is null or undefined, it
334	 *  will default to the global object.
335	 * @return {!Function} A partially-applied form of the function bind() was
336	 *  invoked as a method of.
337	 */
338	$.pkp.classes.Helper.curry = function(fn, context) {
339		// Any additional arguments?
340		if (arguments.length > 2) {
341			var boundArgs, newArgs;
342			boundArgs = Array.prototype.slice.call(arguments, 2);
343			return function() {
344				// Prepend the bound arguments to the current arguments.
345				newArgs = Array.prototype.slice.call(arguments);
346				Array.prototype.unshift.apply(newArgs, boundArgs);
347				return fn.apply(context, newArgs);
348			};
349		}
350		return function() {
351			return fn.apply(context, arguments);
352		};
353	};
354
355
356/** @param {jQuery} $ jQuery closure. */
357}(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.