@awayjs/core
Version:
AwayJS core classes
485 lines (482 loc) • 22.4 kB
JavaScript
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 };