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