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.