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.