1/* 2Copyright The Infusion copyright holders 3See the AUTHORS.md file at the top-level directory of this distribution and at 4https://github.com/fluid-project/infusion/raw/master/AUTHORS.md. 5 6Licensed under the Educational Community License (ECL), Version 2.0 or the New 7BSD license. You may not use this file except in compliance with one these 8Licenses. 9 10You may obtain a copy of the ECL 2.0 License and BSD License at 11https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt 12*/ 13 14var fluid_3_0_0 = fluid_3_0_0 || {}; 15 16(function ($, fluid) { 17 "use strict"; 18 19 /** NOTE: All contents of this file are DEPRECATED and no entry point should be considered a supported API **/ 20 21 fluid.explodeLocalisedName = function (fileName, locale, defaultLocale) { 22 var lastDot = fileName.lastIndexOf("."); 23 if (lastDot === -1 || lastDot === 0) { 24 lastDot = fileName.length; 25 } 26 var baseName = fileName.substring(0, lastDot); 27 var extension = fileName.substring(lastDot); 28 29 var segs = locale.split("_"); 30 31 var exploded = fluid.transform(segs, function (seg, index) { 32 var shortSegs = segs.slice(0, index + 1); 33 return baseName + "_" + shortSegs.join("_") + extension; 34 }); 35 if (defaultLocale) { 36 exploded.unshift(baseName + "_" + defaultLocale + extension); 37 } 38 return exploded; 39 }; 40 41 /** Framework-global caching state for fluid.fetchResources **/ 42 43 var resourceCache = {}; 44 45 var pendingClass = {}; 46 47 /** Accepts a hash of structures with free keys, where each entry has either 48 * href/url or nodeId set - on completion, callback will be called with the populated 49 * structure with fetched resource text in the field "resourceText" for each 50 * entry. Each structure may contain "options" holding raw options to be forwarded 51 * to jQuery.ajax(). 52 */ 53 54 fluid.fetchResources = function (resourceSpecs, callback, options) { 55 var that = { 56 options: fluid.copy(options || {}) 57 }; 58 that.resourceSpecs = resourceSpecs; 59 that.callback = callback; 60 that.operate = function () { 61 fluid.fetchResources.fetchResourcesImpl(that); 62 }; 63 fluid.each(resourceSpecs, function (resourceSpec, key) { 64 resourceSpec.recurseFirer = fluid.makeEventFirer({name: "I/O completion for resource \"" + key + "\""}); 65 resourceSpec.recurseFirer.addListener(that.operate); 66 if (resourceSpec.url && !resourceSpec.href) { 67 resourceSpec.href = resourceSpec.url; 68 } 69 70 // If options.defaultLocale is set, it will replace any 71 // defaultLocale set on an individual resourceSpec 72 if (that.options.defaultLocale) { 73 resourceSpec.defaultLocale = that.options.defaultLocale; 74 } 75 if (!resourceSpec.locale) { 76 resourceSpec.locale = resourceSpec.defaultLocale; 77 } 78 79 }); 80 if (that.options.amalgamateClasses) { 81 fluid.fetchResources.amalgamateClasses(resourceSpecs, that.options.amalgamateClasses, that.operate); 82 } 83 fluid.fetchResources.explodeForLocales(resourceSpecs); 84 that.operate(); 85 return that; 86 }; 87 88 fluid.fetchResources.explodeForLocales = function (resourceSpecs) { 89 fluid.each(resourceSpecs, function (resourceSpec, key) { 90 if (resourceSpec.locale) { 91 var exploded = fluid.explodeLocalisedName(resourceSpec.href, resourceSpec.locale, resourceSpec.defaultLocale); 92 for (var i = 0; i < exploded.length; ++i) { 93 var newKey = key + "$localised-" + i; 94 var newRecord = $.extend(true, {}, resourceSpec, { 95 href: exploded[i], 96 localeExploded: true 97 }); 98 resourceSpecs[newKey] = newRecord; 99 } 100 resourceSpec.localeExploded = exploded.length; 101 } 102 }); 103 return resourceSpecs; 104 }; 105 106 fluid.fetchResources.condenseOneResource = function (resourceSpecs, resourceSpec, key, localeCount) { 107 var localeSpecs = [resourceSpec]; 108 for (var i = 0; i < localeCount; ++i) { 109 var localKey = key + "$localised-" + i; 110 localeSpecs.unshift(resourceSpecs[localKey]); 111 delete resourceSpecs[localKey]; 112 } 113 var lastNonError = fluid.find_if(localeSpecs, function (spec) { 114 return !spec.fetchError; 115 }); 116 if (lastNonError) {
117 resourceSpecs[key] = lastNonError; 118 } 119 }; 120 121 fluid.fetchResources.condenseForLocales = function (resourceSpecs) { 122 fluid.each(resourceSpecs, function (resourceSpec, key) { 123 if (typeof(resourceSpec.localeExploded) === "number") { 124 fluid.fetchResources.condenseOneResource(resourceSpecs, resourceSpec, key, resourceSpec.localeExploded); 125 } 126 }); 127 }; 128 129 fluid.fetchResources.notifyResources = function (that, resourceSpecs, callback) { 130 fluid.fetchResources.condenseForLocales(resourceSpecs); 131 callback(resourceSpecs); 132 }; 133 134 /* 135 * This function is unsupported: It is not really intended for use by implementors. 136 */ 137 // Add "synthetic" elements of *this* resourceSpec list corresponding to any 138 // still pending elements matching the PROLEPTICK CLASS SPECIFICATION supplied 139 fluid.fetchResources.amalgamateClasses = function (specs, classes, operator) { 140 fluid.each(classes, function (clazz) { 141 var pending = pendingClass[clazz]; 142 fluid.each(pending, function (pendingrec, canon) { 143 specs[clazz + "!" + canon] = pendingrec; 144 pendingrec.recurseFirer.addListener(operator); 145 }); 146 }); 147 }; 148 149 /* 150 * This function is unsupported: It is not really intended for use by implementors. 151 */ 152 fluid.fetchResources.timeSuccessCallback = function (resourceSpec) { 153 if (resourceSpec.timeSuccess && resourceSpec.options && resourceSpec.options.success) { 154 var success = resourceSpec.options.success; 155 resourceSpec.options.success = function () { 156 var startTime = new Date(); 157 var ret = success.apply(null, arguments); 158 fluid.log("External callback for URL " + resourceSpec.href + " completed - callback time: " + 159 (new Date().getTime() - startTime.getTime()) + "ms"); 160 return ret; 161 }; 162 } 163 }; 164 165 // TODO: Integrate punch-through from old Engage implementation 166 function canonUrl(url) { 167 return url; 168 } 169 170 fluid.fetchResources.clearResourceCache = function (url) { 171 if (url) { 172 delete resourceCache[canonUrl(url)]; 173 } 174 else { 175 fluid.clear(resourceCache); 176 } 177 }; 178 179 /* 180 * This function is unsupported: It is not really intended for use by implementors. 181 */ 182 fluid.fetchResources.handleCachedRequest = function (resourceSpec, response, fetchError) { 183 var canon = canonUrl(resourceSpec.href); 184 var cached = resourceCache[canon]; 185 if (cached.$$firer$$) { 186 fluid.log("Handling request for " + canon + " from cache"); 187 var fetchClass = resourceSpec.fetchClass; 188 if (fetchClass && pendingClass[fetchClass]) { 189 fluid.log("Clearing pendingClass entry for class " + fetchClass); 190 delete pendingClass[fetchClass][canon]; 191 } 192 var result = {response: response, fetchError: fetchError}; 193 resourceCache[canon] = result; 194 cached.fire(response, fetchError); 195 } 196 }; 197 198 /* 199 * This function is unsupported: It is not really intended for use by implementors. 200 */ 201 fluid.fetchResources.completeRequest = function (thisSpec) { 202 thisSpec.queued = false; 203 thisSpec.completeTime = new Date(); 204 fluid.log("Request to URL " + thisSpec.href + " completed - total elapsed time: " + 205 (thisSpec.completeTime.getTime() - thisSpec.initTime.getTime()) + "ms"); 206 thisSpec.recurseFirer.fire(); 207 }; 208 209 /* 210 * This function is unsupported: It is not really intended for use by implementors. 211 */ 212 fluid.fetchResources.makeResourceCallback = function (thisSpec) { 213 return { 214 success: function (response) { 215 thisSpec.resourceText = response; 216 thisSpec.resourceKey = thisSpec.href; 217 if (thisSpec.forceCache) { 218 fluid.fetchResources.handleCachedRequest(thisSpec, response); 219 } 220 fluid.fetchResources.completeRequest(thisSpec); 221 }, 222 error: function (response, textStatus, errorThrown) { 223 thisSpec.fetchError = { 224 status: response.status, 225 textStatus: response.textStatus, 226 errorThrown: errorThrown 227 }; 228 if (thisSpec.forceCache) { 229 fluid.fetchResources.handleCachedRequest(thisSpec, null, thisSpec.fetchError); 230 } 231 fluid.fetchResources.completeRequest(thisSpec); 232 } 233 234 }; 235 }; 236 237 238 /* 239 * This function is unsupported: It is not really intended for use by implementors. 240 */ 241 fluid.fetchResources.issueCachedRequest = function (resourceSpec, options) { 242 var canon = canonUrl(resourceSpec.href); 243 var cached = resourceCache[canon]; 244 if (!cached) { 245 fluid.log("First request for cached resource with url " + canon); 246 cached = fluid.makeEventFirer({name: "cache notifier for resource URL " + canon}); 247 cached.$$firer$$ = true; 248 resourceCache[canon] = cached; 249 var fetchClass = resourceSpec.fetchClass; 250 if (fetchClass) { 251 if (!pendingClass[fetchClass]) { 252 pendingClass[fetchClass] = {}; 253 } 254 pendingClass[fetchClass][canon] = resourceSpec; 255 } 256 options.cache = false; // TODO: Getting weird "not modified" issues on Firefox 257 $.ajax(options); 258 } 259 else { 260 if (!cached.$$firer$$) { 261 if (cached.response) { 262 options.success(cached.response); 263 } else { 264 options.error(cached.fetchError); 265 } 266 } 267 else { 268 fluid.log("Request for cached resource which is in flight: url " + canon); 269 cached.addListener(function (response, fetchError) { 270 if (response) { 271 options.success(response); 272 } else { 273 options.error(fetchError); 274 } 275 }); 276 } 277 } 278 }; 279 280 /* 281 * This function is unsupported: It is not really intended for use by implementors. 282 */ 283 // Compose callbacks in such a way that the 2nd, marked "external" will be applied 284 // first if it exists, but in all cases, the first, marked internal, will be 285 // CALLED WITHOUT FAIL 286 fluid.fetchResources.composeCallbacks = function (internal, external) { 287 return external ? (internal ? 288 function () { 289 try { 290 external.apply(null, arguments); 291 } 292 catch (e) { 293 fluid.log("Exception applying external fetchResources callback: " + e); 294 } 295 internal.apply(null, arguments);
295 // call the internal callback without fail 296 } : external ) : internal; 297 }; 298 299 // unsupported, NON-API function 300 fluid.fetchResources.composePolicy = function (target, source) { 301 return fluid.fetchResources.composeCallbacks(target, source); 302 }; 303 304 fluid.defaults("fluid.fetchResources.issueRequest", { 305 mergePolicy: { 306 success: fluid.fetchResources.composePolicy, 307 error: fluid.fetchResources.composePolicy, 308 url: "reverse" 309 } 310 }); 311 312 // unsupported, NON-API function 313 fluid.fetchResources.issueRequest = function (resourceSpec, key) { 314 var thisCallback = fluid.fetchResources.makeResourceCallback(resourceSpec); 315 var options = { 316 url: resourceSpec.href, 317 success: thisCallback.success, 318 error: thisCallback.error, 319 dataType: resourceSpec.dataType || "text" 320 }; 321 fluid.fetchResources.timeSuccessCallback(resourceSpec); 322 options = fluid.merge(fluid.defaults("fluid.fetchResources.issueRequest").mergePolicy, 323 options, resourceSpec.options); 324 resourceSpec.queued = true; 325 resourceSpec.initTime = new Date(); 326 fluid.log("Request with key " + key + " queued for " + resourceSpec.href); 327 328 if (resourceSpec.forceCache) { 329 fluid.fetchResources.issueCachedRequest(resourceSpec, options); 330 } 331 else { 332 $.ajax(options); 333 } 334 }; 335 336 337 fluid.fetchResources.fetchResourcesImpl = function (that) { 338 var complete = true; 339 var resourceSpecs = that.resourceSpecs; 340 for (var key in resourceSpecs) { 341 var resourceSpec = resourceSpecs[key]; 342 if (resourceSpec.href && !resourceSpec.completeTime) { 343 if (!resourceSpec.queued) { 344 fluid.fetchResources.issueRequest(resourceSpec, key); 345 } 346 if (resourceSpec.queued) { 347 complete = false; 348 } 349 } 350 else if (resourceSpec.nodeId && !resourceSpec.resourceText) { 351 var node = document.getElementById(resourceSpec.nodeId); 352 // upgrade this to somehow detect whether node is "armoured" somehow 353 // with comment or CDATA wrapping 354 resourceSpec.resourceText = fluid.dom.getElementText(node); 355 resourceSpec.resourceKey = resourceSpec.nodeId; 356 } 357 } 358 if (complete && that.callback && !that.callbackCalled) { 359 that.callbackCalled = true; 360 // Always defer notification in an anti-Zalgo scheme to ease problems like FLUID-6202 361 // In time this will be resolved by i) latched events, ii) global async ginger world 362 setTimeout(function () { 363 fluid.fetchResources.notifyResources(that, resourceSpecs, that.callback); 364 }, 1); 365 } 366 }; 367 368 // TODO: This framework function is a stop-gap before the "ginger world" is capable of 369 // asynchronous instantiation. It currently performs very poor fidelity expansion of a 370 // component's options to discover "resources" only held in the static environment 371 fluid.fetchResources.primeCacheFromResources = function (componentName) { 372 var resources = fluid.defaults(componentName).resources; 373 var expanded = (fluid.expandOptions ? fluid.expandOptions : fluid.identity)(fluid.copy(resources)); 374 fluid.fetchResources(expanded); 375 }; 376 377 /** Utilities invoking requests for expansion **/ 378 fluid.registerNamespace("fluid.expander"); 379 380 /* 381 * This function is unsupported: It is not really intended for use by implementors. 382 */ 383 fluid.expander.makeDefaultFetchOptions = function (successdisposer, failid, options) { 384 return $.extend(true, {dataType: "text"}, options, { 385 success: function (response, environmentdisposer) { 386 var json = JSON.parse(response); 387 environmentdisposer(successdisposer(json)); 388 }, 389 error: function (response, textStatus) { 390 fluid.log("Error fetching " + failid + ": " + textStatus); 391 } 392 }); 393 }; 394 395 /* 396 * This function is unsupported: It is not really intended for use by implementors. 397 */ 398 fluid.expander.makeFetchExpander = function (options) { 399 return { expander: { 400 type: "fluid.expander.deferredFetcher", 401 href: options.url, 402 options: fluid.expander.makeDefaultFetchOptions(options.disposer, options.url, options.options), 403 resourceSpecCollector: "{resourceSpecCollector}", 404 fetchKey: options.fetchKey 405 }}; 406 }; 407 408 fluid.expander.deferredFetcher = function (deliverer, source, expandOptions) { 409 var expander = source.expander; 410 var spec = fluid.copy(expander); 411 // fetch the "global" collector specified in the external environment to receive 412 // this resourceSpec 413 var collector = fluid.expand(expander.resourceSpecCollector, exp
413andOptions); 414 delete spec.type; 415 delete spec.resourceSpecCollector; 416 delete spec.fetchKey; 417 var environmentdisposer = function (disposed) { 418 deliverer(disposed); 419 }; 420 // replace the callback which is there (taking 2 arguments) with one which 421 // directly responds to the request, passing in the result and OUR "disposer" - 422 // which once the user has processed the response (say, parsing JSON and repackaging) 423 // finally deposits it in the place of the expander in the tree to which this reference 424 // has been stored at the point this expander was evaluated. 425 spec.options.success = function (response) { 426 expander.options.success(response, environmentdisposer); 427 }; 428 var key = expander.fetchKey || fluid.allocateGuid(); 429 collector[key] = spec; 430 return fluid.NO_VALUE; 431 }; 432 433 434})(jQuery, fluid_3_0_0);
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.