PageSourceSearch

https://sajbl.org.za/lib/pkp/js/classes/Helper.js

js sajbl.org.za collected 2026-10-02 14:34:50 UTC 10,589 bytes, 349 lines download raw bytes

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