UNPKG

@awayjs/core

Version:
485 lines (482 loc) 22.4 kB
import { __extends } from "tslib"; import { ErrorBase } from '../errors/ErrorBase'; import { AssetEvent } from '../events/AssetEvent'; import { URLLoaderEvent } from '../events/URLLoaderEvent'; import { LoaderEvent } from '../events/LoaderEvent'; import { EventDispatcher } from '../events/EventDispatcher'; import { ParserEvent } from '../events/ParserEvent'; import { AssetBase } from './AssetBase'; import { AssetLibraryIterator } from './AssetLibraryIterator'; import { ConflictPrecedence } from './ConflictPrecedence'; import { ConflictStrategy } from './ConflictStrategy'; import { Loader } from './Loader'; /** * AssetLibraryBundle enforces a multiton pattern and is not intended to be * instanced directly. Its purpose is to create a container for 3D data * management, both before and after parsing. If you are interested in creating * multiple library bundles, please use the <code>getInstance()</code> method. */ var AssetLibraryBundle = /** @class */ (function (_super) { __extends(AssetLibraryBundle, _super); /** * Creates a new <code>AssetLibraryBundle</code> object. * * @param me A multiton enforcer for the AssetLibraryBundle ensuring it cannnot be instanced. */ function AssetLibraryBundle() { var _a; var _this = _super.call(this) || this; _this._loaderSessionsGarbage = new Array(); _this._assets = new Array(); //new Vector.<IAsset>; _this._assetDictionary = new Object(); _this._loaderSessions = new Array(); _this.conflictStrategy = ConflictStrategy.IGNORE.create(); _this.conflictPrecedence = ConflictPrecedence.FAVOR_NEW; _this._onAssetRenameDelegate = function (event) { return _this._onAssetRename(event); }; _this._onAssetConflictResolvedDelegate = function (event) { return _this._onAssetConflictResolved(event); }; _this._onLoaderStartDelegate = function (event) { return _this._onLoaderStart(event); }; _this._onLoaderCompleteDelegate = function (event) { return _this._onLoaderComplete(event); }; _this._onTextureSizeErrorDelegate = function (event) { return _this._onTextureSizeError(event); }; _this._onAssetCompleteDelegate = function (event) { return _this._onAssetComplete(event); }; _this._onLoadErrorDelegate = function (event) { return _this._onLoadError(event); }; _this._onParseErrorDelegate = function (event) { return _this._onParseError(event); }; _this._errorDelegateSelector = (_a = {}, _a[URLLoaderEvent.LOAD_ERROR] = _this._onLoadErrorDelegate, _a[ParserEvent.PARSE_ERROR] = _this._onParseErrorDelegate, _a); return _this; } /** * Special addEventListener case for <code>URLLoaderEvent.LOAD_ERROR</code> * and <code>ype == ParserEvent.PARSE_ERROR</code> * * @param type * @param listener */ AssetLibraryBundle.prototype.addEventListener = function (type, listener) { if (type == URLLoaderEvent.LOAD_ERROR || type == ParserEvent.PARSE_ERROR) for (var i = void 0; i < this._loaderSessions.length; i++) this._loaderSessions[i].addEventListener(type, this._errorDelegateSelector[type]); _super.prototype.addEventListener.call(this, type, listener); }; /** * Special removeEventListener case for * <code>URLLoaderEvent.LOAD_ERROR</code> and <code>ype == * ParserEvent.PARSE_ERROR</code> * * @param type * @param listener */ AssetLibraryBundle.prototype.removeEventListener = function (type, listener) { if (type == URLLoaderEvent.LOAD_ERROR || type == ParserEvent.PARSE_ERROR) for (var i = void 0; i < this._loaderSessions.length; i++) this._loaderSessions[i].removeEventListener(type, this._errorDelegateSelector[type]); _super.prototype.removeEventListener.call(this, type, listener); }; /** * Returns an AssetLibraryBundle instance. If no key is given, returns the * default bundle instance (which is similar to using the AssetLibraryBundle * as a singleton.) To keep several separated library bundles, pass a string * key to this method to define which bundle should be returned. This is * referred to as using the AssetLibrary as a multiton. * * @param key Defines which multiton instance should be returned. * @return An instance of the asset library */ AssetLibraryBundle.getInstance = function (key) { if (key === void 0) { key = 'default'; } if (!key) key = 'default'; if (!AssetLibraryBundle._iInstances.hasOwnProperty(key)) AssetLibraryBundle._iInstances[key] = new AssetLibraryBundle(); return AssetLibraryBundle._iInstances[key]; }; /** * */ AssetLibraryBundle.prototype.enableParser = function (parserClass) { Loader.enableParser(parserClass); }; /** * */ AssetLibraryBundle.prototype.enableParsers = function (parserClasses) { Loader.enableParsers(parserClasses); }; Object.defineProperty(AssetLibraryBundle.prototype, "conflictStrategy", { /** * Defines which strategy should be used for resolving naming conflicts, * when two library assets are given the same name. By default, * <code>ConflictStrategy.APPEND_NUM_SUFFIX</code> is used which means that * a numeric suffix is appended to one of the assets. The * <code>conflictPrecedence</code> property defines which of the two * conflicting assets will be renamed. * * @see naming.ConflictStrategy * @see AssetLibrary.conflictPrecedence */ get: function () { return this._strategy; }, set: function (val) { if (!val) throw new ErrorBase('namingStrategy must not be null. To ignore naming, use AssetLibrary.IGNORE'); this._strategy = val.create(); }, enumerable: false, configurable: true }); Object.defineProperty(AssetLibraryBundle.prototype, "conflictPrecedence", { /** * Defines which asset should have precedence when resolving a naming * conflict between two assets of which one has just been renamed by the * user or by a parser. By default <code>ConflictPrecedence.FAVOR_NEW</code> * is used, meaning that the newly renamed asset will keep it's new name * while the older asset gets renamed to not conflict. * * This property is ignored for conflict strategies that do not actually * rename an asset automatically, such as ConflictStrategy.IGNORE and * ConflictStrategy.THROW_ERROR. * * @see away.library.ConflictPrecedence * @see away.library.ConflictStrategy */ get: function () { return this._strategyPreference; }, set: function (val) { this._strategyPreference = val; }, enumerable: false, configurable: true }); /** * Create an AssetLibraryIterator instance that can be used to iterate over * the assets in this asset library instance. The iterator can filter assets * on asset type and/or namespace. A "null" filter value means no filter of * that type is used. * * @param assetTypeFilter Asset type to filter on (from the AssetType enum * class.) Use null to not filter on asset type. * @param namespaceFilter Namespace to filter on. Use null to not filter on * namespace. * @param filterFunc Callback function to use when deciding whether an asset * should be included in the iteration or not. This needs to be a function * that takes a single parameter of type IAsset and returns a boolean where * true means it should be included. * * @see away.library.AssetType */ AssetLibraryBundle.prototype.createIterator = function (assetTypeFilter, namespaceFilter, filterFunc) { if (assetTypeFilter === void 0) { assetTypeFilter = null; } if (namespaceFilter === void 0) { namespaceFilter = null; } if (filterFunc === void 0) { filterFunc = null; } return new AssetLibraryIterator(this._assets, assetTypeFilter, namespaceFilter, filterFunc); }; /** * Loads a file and (optionally) all of its dependencies. * * @param req The URLRequest object containing the URL of the file to be * loaded. * @param context An optional context object providing additional parameters * for loading * @param ns An optional namespace string under which the file is to be * loaded, allowing the differentiation of two resources with identical * assets * @param parser An optional parser object for translating the loaded data * into a usable resource. If not provided, Loader will attempt to * auto-detect the file type. * @return A handle to the retrieved resource. */ AssetLibraryBundle.prototype.load = function (req, context, ns, parser) { if (context === void 0) { context = null; } if (ns === void 0) { ns = null; } if (parser === void 0) { parser = null; } this.getLoader().load(req, context, ns, parser); }; /** * Loads a resource from existing data in memory. * * @param data The data object containing all resource information. * @param context An optional context object providing additional parameters * for loading * @param ns An optional namespace string under which the file is to be * loaded, allowing the differentiation of two resources with identical * assets * @param parser An optional parser object for translating the loaded data * into a usable resource. If not provided, Loader will attempt to * auto-detect the file type. * @return A handle to the retrieved resource. */ AssetLibraryBundle.prototype.loadData = function (data, context, ns, parser) { if (context === void 0) { context = null; } if (ns === void 0) { ns = null; } if (parser === void 0) { parser = null; } this.getLoader().loadData(data, '', context, ns, parser); }; AssetLibraryBundle.prototype.getLoader = function () { var loader = new Loader(); this._loaderSessions.push(loader); loader.addEventListener(LoaderEvent.LOADER_START, this._onLoaderStartDelegate); loader.addEventListener(LoaderEvent.LOADER_COMPLETE, this._onLoaderCompleteDelegate); loader.addEventListener(AssetEvent.TEXTURE_SIZE_ERROR, this._onTextureSizeErrorDelegate); loader.addEventListener(AssetEvent.ASSET_COMPLETE, this._onAssetCompleteDelegate); if (this.hasEventListener(URLLoaderEvent.LOAD_ERROR)) loader.addEventListener(URLLoaderEvent.LOAD_ERROR, this._onLoadErrorDelegate); if (this.hasEventListener(ParserEvent.PARSE_ERROR)) loader.addEventListener(ParserEvent.PARSE_ERROR, this._onParseErrorDelegate); return loader; }; AssetLibraryBundle.prototype.stopLoader = function (loader) { var index = this._loaderSessions.indexOf(loader); if (index == -1) throw new Error('loader is not an active session'); this._loaderSessions.splice(index, 1); this._killLoaderSession(loader); }; /** * */ AssetLibraryBundle.prototype.getAsset = function (name, ns) { if (ns === void 0) { ns = null; } if (this._assetDictDirty) this.rehashAssetDict(); if (ns == null) ns = AssetBase.DEFAULT_NAMESPACE; if (!this._assetDictionary.hasOwnProperty(ns)) return null; return this._assetDictionary[ns][name.toString().toLowerCase()]; }; AssetLibraryBundle.prototype.getAllAssets = function () { return this._assets; }; /** * Adds an asset to the asset library, first making sure that it's name is * unique using the method defined by the <code>conflictStrategy</code> and * <code>conflictPrecedence</code> properties. */ AssetLibraryBundle.prototype.addAsset = function (asset) { // Bail if asset has already been added. if (this._assets.indexOf(asset) >= 0) return; var old = this.getAsset(asset.adaptee.name, asset.adaptee.assetNamespace); var ns = asset.adaptee.assetNamespace || AssetBase.DEFAULT_NAMESPACE; if (old != null) this._strategy.resolveConflict(asset, old, this._assetDictionary[ns], this._strategyPreference); //create unique-id (for now this is used in AwayBuilder only //asset.id = IDUtil.createUID(); // Add it this._assets.push(asset); if (!this._assetDictionary.hasOwnProperty(ns)) this._assetDictionary[ns] = new Object(); this._assetDictionary[ns][asset.adaptee.name.toString().toLowerCase()] = asset; asset.adaptee.addEventListener(AssetEvent.RENAME, this._onAssetRenameDelegate); asset.adaptee.addEventListener(AssetEvent.ASSET_CONFLICT_RESOLVED, this._onAssetConflictResolvedDelegate); }; /** * Removes an asset from the library, and optionally disposes that asset by * calling it's disposeAsset() method (which for most assets is implemented * as a default version of that type's dispose() method. * * @param asset The asset which should be removed from this library. * @param dispose Defines whether the assets should also be disposed. */ AssetLibraryBundle.prototype.removeAsset = function (asset, dispose) { if (dispose === void 0) { dispose = true; } this.removeAssetFromDict(asset); asset.adaptee.removeEventListener(AssetEvent.RENAME, this._onAssetRenameDelegate); asset.adaptee.removeEventListener(AssetEvent.ASSET_CONFLICT_RESOLVED, this._onAssetConflictResolvedDelegate); var idx = this._assets.indexOf(asset); if (idx >= 0) this._assets.splice(idx, 1); if (dispose) asset.dispose(); }; /** * Removes an asset which is specified using name and namespace. * * @param name The name of the asset to be removed. * @param ns The namespace to which the desired asset belongs. * @param dispose Defines whether the assets should also be disposed. * * @see away.library.AssetLibrary.removeAsset() */ AssetLibraryBundle.prototype.removeAssetByName = function (name, ns, dispose) { if (ns === void 0) { ns = null; } if (dispose === void 0) { dispose = true; } var asset = this.getAsset(name, ns); if (asset) this.removeAsset(asset, dispose); return asset; }; /** * Removes all assets from the asset library, optionally disposing them as * they are removed. * * @param dispose Defines whether the assets should also be disposed. */ AssetLibraryBundle.prototype.removeAllAssets = function (dispose) { if (dispose === void 0) { dispose = true; } if (dispose) { var asset = void 0; var len = this._assets.length; for (var c = 0; c < len; c++) { asset = this._assets[c]; asset.dispose(); } } this._assets.length = 0; this.rehashAssetDict(); }; /** * Removes all assets belonging to a particular namespace (null for default) * from the asset library, and optionall disposes them by calling their * disposeAsset() method. * * @param ns The namespace from which all assets should be removed. * @param dispose Defines whether the assets should also be disposed. * * @see away.library.AssetLibrary.removeAsset() */ AssetLibraryBundle.prototype.removeNamespaceAssets = function (ns, dispose) { if (ns === void 0) { ns = null; } if (dispose === void 0) { dispose = true; } var idx = 0; var asset; // Empty the assets vector after having stored a copy of it. // The copy will be filled with all assets which weren't removed. var old_assets = this._assets.concat(); this._assets.length = 0; if (ns == null) ns = AssetBase.DEFAULT_NAMESPACE; var len = old_assets.length; for (var d = 0; d < len; d++) { asset = old_assets[d]; // Remove from dict if in the supplied namespace. If not, // transfer over to the new vector. if (asset.adaptee.assetNamespace == ns) { if (dispose) asset.dispose(); // Remove asset from dictionary, but don't try to auto-remove // the namespace, which will trigger an unnecessarily expensive // test that is not needed since we know that the namespace // will be empty when loop finishes. this.removeAssetFromDict(asset, false); } else { this._assets[idx++] = asset; } } /* for each (asset in old_assets) { // Remove from dict if in the supplied namespace. If not, // transfer over to the new vector. if (asset.assetNamespace == ns) { if (dispose) asset.dispose(); // Remove asset from dictionary, but don't try to auto-remove // the namespace, which will trigger an unnecessarily expensive // test that is not needed since we know that the namespace // will be empty when loop finishes. removeAssetFromDict(asset, false); } else _assets[idx++] = asset; } */ // Remove empty namespace if (this._assetDictionary.hasOwnProperty(ns)) delete this._assetDictionary[ns]; }; AssetLibraryBundle.prototype.removeAssetFromDict = function (asset, autoRemoveEmptyNamespace) { if (autoRemoveEmptyNamespace === void 0) { autoRemoveEmptyNamespace = true; } if (this._assetDictDirty) this.rehashAssetDict(); var assetNamespace = asset.adaptee.assetNamespace; if (this._assetDictionary.hasOwnProperty(assetNamespace)) { if (this._assetDictionary[assetNamespace].hasOwnProperty(asset.adaptee.name.toString().toLowerCase())) delete this._assetDictionary[assetNamespace][asset.adaptee.name.toString().toLowerCase()]; if (autoRemoveEmptyNamespace && !Object.keys(this._assetDictionary[assetNamespace]).length) delete this._assetDictionary[assetNamespace]; } }; AssetLibraryBundle.prototype.stop = function () { var len = this._loaderSessions.length; for (var i = 0; i < len; i++) this._killLoaderSession(this._loaderSessions[i]); this._loaderSessions = new Array(); }; AssetLibraryBundle.prototype.rehashAssetDict = function () { var asset; var assetNamespace; this._assetDictionary = {}; var len = this._assets.length; for (var c = 0; c < len; c++) { asset = this._assets[c]; assetNamespace = asset.adaptee.assetNamespace; if (!this._assetDictionary.hasOwnProperty(assetNamespace)) this._assetDictionary[assetNamespace] = {}; this._assetDictionary[assetNamespace][asset.adaptee.name.toString().toLowerCase()] = asset; } this._assetDictDirty = false; }; /** * Called when a an error occurs during loading. */ AssetLibraryBundle.prototype._onLoadError = function (event) { this.dispatchEvent(event); }; /** * Called when a an error occurs during parsing. */ AssetLibraryBundle.prototype._onParseError = function (event) { this.dispatchEvent(event); }; AssetLibraryBundle.prototype._onAssetComplete = function (event) { this.addAsset(event.asset.adapter); this.dispatchEvent(event); }; AssetLibraryBundle.prototype._onTextureSizeError = function (event) { this.dispatchEvent(event); }; AssetLibraryBundle.prototype._onLoaderStart = function (event) { this.dispatchEvent(event); }; /** * Called when the resource and all of its dependencies was retrieved. */ AssetLibraryBundle.prototype._onLoaderComplete = function (event) { this.stopLoader(event.target); this.dispatchEvent(event); }; AssetLibraryBundle.prototype._killLoaderSession = function (loader) { loader.removeEventListener(LoaderEvent.LOADER_START, this._onLoaderStartDelegate); loader.removeEventListener(LoaderEvent.LOADER_COMPLETE, this._onLoaderCompleteDelegate); loader.removeEventListener(AssetEvent.TEXTURE_SIZE_ERROR, this._onTextureSizeErrorDelegate); loader.removeEventListener(AssetEvent.ASSET_COMPLETE, this._onAssetCompleteDelegate); if (this.hasEventListener(URLLoaderEvent.LOAD_ERROR)) loader.removeEventListener(URLLoaderEvent.LOAD_ERROR, this._onLoadErrorDelegate); if (this.hasEventListener(ParserEvent.PARSE_ERROR)) loader.removeEventListener(ParserEvent.PARSE_ERROR, this._onParseErrorDelegate); loader.stop(); }; AssetLibraryBundle.prototype._onAssetRename = function (event) { var asset = event.target.adapter; // TODO: was ev.currentTarget - watch this var var old = this.getAsset(asset.adaptee.assetNamespace, asset.adaptee.name); if (old != null) { this._strategy.resolveConflict(asset, old, this._assetDictionary[asset.adaptee.assetNamespace], this._strategyPreference); } else { var dict = this._assetDictionary[event.asset.assetNamespace]; if (dict == null) return; dict[event.prevName] = null; dict[event.asset.name.toString().toLowerCase()] = event.asset; } }; AssetLibraryBundle.prototype._onAssetConflictResolved = function (event) { this.dispatchEvent(event.clone()); }; AssetLibraryBundle._iInstances = new Object(); return AssetLibraryBundle; }(EventDispatcher)); export { AssetLibraryBundle };