1 /* 2 * Loader.js - shared loader implementation 3 * 4 * Copyright © 2015, 2018, JEDLSoft 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 var Path = require("./Path.js"); 21 var ilib = require("./ilib.js"); 22 23 /** 24 * @class 25 * Superclass of the loader classes that contains shared functionality. 26 * 27 * @private 28 * @constructor 29 */ 30 var Loader = function() { 31 // console.log("new Loader instance"); 32 33 this.protocol = "file://"; 34 this.includePath = []; 35 }; 36 37 Loader.prototype = new ilib.Loader(); 38 Loader.prototype.parent = ilib.Loader; 39 Loader.prototype.constructor = Loader; 40 41 Loader.prototype._loadFile = function (pathname, sync, cb) {}; 42 43 Loader.prototype._exists = function(dir, file) { 44 var fullpath = Path.normalize(Path.join(dir, file)); 45 if (this.protocol !== "http://") { 46 var text = this._loadFile(fullpath, true); 47 if (text) { 48 this.includePath.push(dir); 49 } 50 } else { 51 // put the dir on the list now assuming it exists, and check for its availability 52 // later so we can avoid the 404 errors eventually 53 this.includePath.push(dir); 54 this._loadFile(fullpath, false, ilib.bind(this, function(text) { 55 if (!text) { 56 //console.log("Loader._exists: removing " + dir + " from the include path because it doesn't exist."); 57 this.includePath = this.includePath.slice(-1); 58 } 59 })); 60 } 61 }; 62 63 Loader.prototype._loadFileAlongIncludePath = function(includePath, pathname) { 64 for (var i = 0; i < includePath.length; i++) { 65 var manifest = this.manifest[includePath[i]]; 66 if (!manifest || Loader.indexOf(manifest, pathname) > -1) { 67 var filepath = Path.join(includePath[i], pathname); 68 //console.log("Loader._loadFileAlongIncludePath: attempting sync load " + filepath); 69 var text = this._loadFile(filepath, true); 70 if (text) { 71 //console.log("Loader._loadFileAlongIncludePath: succeeded"); 72 return text; 73 } 74 //else { 75 //console.log("Loader._loadFileAlongIncludePath: failed"); 76 //} 77 } 78 //else { 79 //console.log("Loader._loadFileAlongIncludePath: " + pathname + " not in manifest for " + this.includePath[i]); 80 //} 81 } 82 83 //console.log("Loader._loadFileAlongIncludePath: file not found anywhere along the path."); 84 return undefined; 85 }; 86 87 Loader.prototype.loadFiles = function(paths, sync, params, callback) { 88 var includePath = params && params.base ? [params.base].concat(this.includePath) : this.includePath; 89 90 //console.log("Loader loadFiles called"); 91 // make sure we know what we can load 92 if (!paths) { 93 // nothing to load 94 //console.log("nothing to load"); 95 return; 96 } 97 98 //console.log("generic loader: attempting to load these files: " + JSON.stringify(paths) + "\n"); 99 if (sync) { 100 var ret = []; 101 102 // synchronous 103 this._loadManifests(true); 104 105 for (var i = 0; i < paths.length; i++) { 106 var text = this._loadFileAlongIncludePath(includePath, Path.normalize(paths[i])); 107 ret.push(typeof(text) === "string" ? JSON.parse(text) : text); 108 }; 109 110 // only call the callback at the end of the chain of files 111 if (typeof(callback) === 'function') { 112 callback(ret); 113 } 114 115 return ret; 116 } 117 118 // asynchronous 119 this._loadManifests(false, ilib.bind(this, function() { 120 //console.log("Loader.loadFiles: now loading files asynchronously"); 121 var results = []; 122 this._loadFilesAsync(includePath, paths, results, callback); 123 })); 124 }; 125 126 Loader.prototype._loadFilesAsyncAlongIncludePath = function (includes, filename, cb) { 127 var text = undefined; 128 129 if (includes.length > 0) { 130 var root = includes[0]; 131 includes = includes.slice(1); 132 133 var manifest = this.manifest[root]; 134 if (!manifest || Loader.indexOf(manifest, filename) > -1) { 135 var filepath = Path.join(root, filename); 136 this._loadFile(filepath, false, ilib.bind(this, function(t) { 137 //console.log("Loader._loadFilesAsyncAlongIncludePath: loading " + (t ? " success" : " failed")); 138 if (t) { 139 cb(t); 140 } else { 141 this._loadFilesAsyncAlongIncludePath(includes, filename, cb); 142 } 143 })); 144 } else { 145 //console.log("Loader._loadFilesAsyncAlongIncludePath: " + filepath + " not in manifest for " + root); 146 this._loadFilesAsyncAlongIncludePath(includes, filename, cb); 147 } 148 } else { 149 // file not found in any of the include paths 150 cb(); 151 } 152 }; 153 154 Loader.prototype._loadFilesAsync = function (includePath, paths, results, callback) { 155 if (paths.length > 0) { 156 var filename = paths[0]; 157 paths = paths.slice(1); 158 159 //console.log("Loader._loadFilesAsync: attempting to load " + filename + " along the include path."); 160 this._loadFilesAsyncAlongIncludePath(includePath, filename, ilib.bind(this, function (json) { 161 results.push(typeof(json) === "string" ? JSON.parse(json) : json); 162 this._loadFilesAsync(includePath, paths, results, callback); 163 })); 164 } else { 165 // only call the callback at the end of the chain of files 166 if (typeof(callback) === 'function') { 167 callback(results); 168 } 169 } 170 }; 171 172 Loader.prototype._loadManifestFile = function(i, sync, cb) { 173 //console.log("Loader._loadManifestFile: Checking include path " + i + " " + this.includePath[i]); 174 if (i < this.includePath.length) { 175 var filepath = Path.join(this.includePath[i], "ilibmanifest.json"); 176 //console.log("Loader._loadManifestFile: Loading manifest file " + filepath); 177 var text = this._loadFile(filepath, sync, ilib.bind(this, function(text) { 178 if (text) { 179 //console.log("Loader._loadManifestFile: success!"); 180 this.manifest[this.includePath[i]] = (typeof(text) === "string" ? JSON.parse(text) : text).files; 181 } 182 //else console.log("Loader._loadManifestFile: failed..."); 183 this._loadManifestFile(i+1, sync, cb); 184 })); 185 } else { 186 if (typeof(cb) === 'function') { 187 //console.log("Loader._loadManifestFile: now calling callback function"); 188 cb(); 189 } 190 } 191 }; 192 193 Loader.prototype._loadManifests = function(sync, cb) { 194 //console.log("Loader._loadManifests: called " + (sync ? "synchronously" : "asychronously.")); 195 if (!this.manifest) { 196 //console.log("Loader._loadManifests: attempting to find manifests"); 197 this.manifest = {}; 198 if (typeof(sync) !== 'boolean') { 199 sync = true; 200 } 201 202 this._loadManifestFile(0, sync, cb); 203 } else { 204 //console.log("Loader._loadManifests: already loaded"); 205 if (typeof(cb) === 'function') { 206 //console.log("Loader._loadManifests: now calling callback function"); 207 cb(); 208 } 209 } 210 }; 211 212 Loader.prototype.listAvailableFiles = function(sync, cb) { 213 //console.log("generic loader: list available files called"); 214 this._loadManifests(sync, ilib.bind(this, function () { 215 if (typeof(cb) === 'function') { 216 //console.log("generic loader: now calling caller's callback function"); 217 cb(this.manifest); 218 } 219 })); 220 return this.manifest; 221 }; 222 223 Loader.indexOf = function(array, obj) { 224 if (!array || !obj) { 225 return -1; 226 } 227 if (typeof(array.indexOf) === 'function') { 228 return array.indexOf(obj); 229 } else { 230 for (var i = 0; i < array.length; i++) { 231 if (array[i] === obj) { 232 return i; 233 } 234 } 235 return -1; 236 } 237 }; 238 239 Loader.prototype.checkAvailability = function(file) { 240 for (var dir in this.manifest) { 241 if (Loader.indexOf(this.manifest[dir], file) !== -1) { 242 return true; 243 } 244 } 245 246 return false; 247 }; 248 249 Loader.prototype.isAvailable = function(file, sync, cb) { 250 //console.log("Loader.isAvailable: called"); 251 if (typeof(sync) !== 'boolean') { 252 sync = true; 253 } 254 if (sync) { 255 this._loadManifests(sync); 256 return this.checkAvailability(file); 257 } 258 259 this._loadManifests(false, ilib.bind(this, function () { 260 // console.log("generic loader: isAvailable " + path + "? "); 261 if (typeof(cb) === 'function') { 262 cb(this.checkAvailability(file)); 263 } 264 })); 265 }; 266 267 module.exports = Loader;