{"version":3,"file":"37440fd404c8d9f0341e4605.js?3.6.14.7428b935c.1723600572441","mappings":"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","sources":["webpack://mono-repo/./packages/core-asset/src/controller.js","webpack://mono-repo/./build-targets/portal/packages/core-asset/src/controller.js","webpack://mono-repo/./packages/core-category/src/controller.js","webpack://mono-repo/./build-targets/portal/packages/core-category/src/controller.js","webpack://mono-repo/./packages/core-search/src/controller.js","webpack://mono-repo/./build-targets/portal/packages/core-search/src/controller.js","webpack://mono-repo/./packages/core-permissions/src/controller.js"],"sourcesContent":["/**\n * The asset module maintains various views, models & collections of assets for use throughout DAM apps.\n * Required by almost everything.\n *\n * @module asset\n */\n\nimport {appRouter} from '@netx/core-next/instance/router';\nimport {getPreference} from '@netx/core-user-next/utils/getPreference';\nimport {getUserLevel} from '@netx/core-user-next/utils/getUserLevel';\nimport {\n\tcurrentUserMeetsUserLevel,\n\tUserLevel,\n} from '@netx/core-user-next/utils/currentUserMeetsUserLevel';\nimport Netx from '@netx/core/bootstrap';\nimport {URL} from '@netx/core-next/url';\nimport {Menus} from '@netx/core/legacyMenuManager';\n\n// Models\nimport CoreModel from '@netx/core/lib/netx-model';\nimport AssetModel from './models/asset';\nimport AssetRequestModel from '@netx/core-asset/models/assetRequest';\nimport AssetSetModel from './models/assetSet';\nimport AssetViewModel from './models/assetView';\n\n// Singletons\nimport {\n\tattributes as attributesSingleton,\n\tassetAttributes as assetAttributesSingleton,\n\tclipAttributes as clipAttributesSingleton,\n} from '@netx/core-attribute/singletons';\n\nimport {attributeSets as attributeSetsSingleton} from './collections/singleton/attributeSets';\n\n// Collections\nimport AssetAttributeSetsSelectionCollection from './collections/assetAttributeSetsSelection';\nimport AssetRequestsCollection from '@netx/core-asset/collections/assetRequests';\nimport AssetTasksCollection from './collections/assetTasks';\nimport DownloadOptionsCollection from '@netx/core-share/collections/downloadOptions';\nimport MetadataMapsCollection from './collections/metadataMaps';\nimport MiniAssetsCollection from './collections/miniAssets';\n\n// Views\nimport AssetVersionReactivateView from './views/assetVersionReactivate';\nimport AssetConstituentDownloadView_Clip from './views/assetConstituentDownloadView_clip';\nimport AssetDetailView from './views/assetDetailView';\n\nimport AssetConstituentClipModel from './models/assetConstituent_clip';\n\nimport NxVideo from '@netx/core/lib/netx-video';\n\nimport {types as assetSetTypes} from './properties/assetSets';\nimport {checkinErrors} from '@netx/core-upload/properties/errors';\n\n// Asset config `Netx.asset.options`\nimport customConfig from './config';\n\nimport routes from './routes';\n\nimport {things as assetThings} from './properties/asset';\nimport {types as downloadOptionTypes} from '@netx/core-share/properties/downloadOptions';\nimport {attributeHistoryEnabled} from '@netx/core-attribute-history/enablers';\n\nimport AssetRepurposeController from '@netx/core-asset-repurpose/controller';\nimport AssetCustomThumbnailController from '@netx/core-asset-custom-thumbnail/controller';\nimport {convertToRules} from '@netx/core-search-rules/utils/convertToRules';\nimport {createExpirationRule} from '@netx/core-search-rules/rules/expiration/expirationRule';\n\nimport {openDownloadLink} from '@netx/core-next/utils/browser';\nimport {session} from '@netx/core-auth/collections/singleton/session';\nimport {modalViews} from '@netx/core-app/singletons/modalViews';\nimport {appViews} from '@netx/core-app/singletons/appViews';\nimport {fsViews} from '@netx/core-app/singletons/fsViews';\n\nimport {\n\tassetHasClips,\n\tassetHasConstituents_clips,\n\tassetHasConstituents_keyframes,\n\tassetHasConstituents_layers,\n\tassetHasConstituents_pages,\n\tassetHasDetail,\n\tassetHasLinks,\n\tassetHasMetadata,\n\tassetHasPreview,\n\tassetHasProjects,\n\tassetHasReviews,\n\tassetHasVersions,\n\tassetHasViews,\n} from './enablers';\nimport {checksumEnabled} from '@netx/core-asset-next/enablers';\nimport {systemAttributes} from '@netx/core-asset/properties/attributes';\nimport {assetProjectEnabled} from '@netx/core-asset-project/enablers';\n\nimport AssetExpirationController from '@netx/core-asset-expiration/controller';\nimport AssetGalleryController from '@netx/core-asset-gallery/controller';\nimport AssetProjectController from '@netx/core-asset-project/controller';\nimport AssetNextController from '@netx/core-asset-next/controller';\nimport {PDF_VIEWER_EVENT} from '@netx/core-pdf-viewer/actions';\nimport {pendingAssets} from '@netx/core-asset/collections/singleton/pendingAssets';\nimport {assetCursor} from './collections/singleton/assetCursor';\nimport {currentAsset} from './collections/singleton/currentAsset';\nimport {setGlobal} from '@netx/core-next/utils/setGlobal';\nimport {\n\tFindLegacyAction,\n\tRegisterLegacyAction,\n} from '@netx/core/legacyActionManager';\nimport {ASSET_SET_EVENT} from '@netx/core-asset-set/actions';\nimport {\n\tAssetSetType,\n\tdownloadOptionDefaults,\n\tDownloadOptionType,\n} from '@netx/core-asset-set';\n\nconst AssetDetailEnablers = {\n\tassetHasClips,\n\tassetHasConstituents_clips,\n\tassetHasConstituents_keyframes,\n\tassetHasConstituents_layers,\n\tassetHasConstituents_pages,\n\tassetHasDetail,\n\tassetHasLinks,\n\tassetHasMetadata,\n\tassetHasPreview,\n\tassetHasProjects,\n\tassetHasReviews,\n\tassetHasVersions,\n\tassetHasViews,\n};\n\n// SHAREABLE TYPES\n/////////////////////////\n// Define shareable types introduced by this module.\n\n_.extend(Netx.ShareableTypes, {\n\tassets: {\n\t\tlabel: i18n.t('i18n.assets'),\n\t},\n\t'assets PDF': {\n\t\tlabel: i18n.t('i18n.assetContactSheet'),\n\t},\n\t'assets repurposed': {\n\t\tlabel: i18n.t('i18n.repurposedAssets'),\n\t},\n});\n\n// MODULE INITIALIZATION\n/////////////////////////\n// All collection and model instances and event listeners\n// are created when the module is initialized.\n\n/**\n * Init handler\n * @event module:asset#asset:init\n * @param {object} options Normally undefined, but a manual init trigger may override properties of Netx.custom.asset here.\n * @fires module:asset#asset:extend\n * @fires module:asset#asset:initialized\n * @listens Netx.asset.cursor#sync\n */\ndispatcher.once('asset:init', function (options) {\n\t// Check for module options\n\toptions = _.extend({}, customConfig, options);\n\n\t/**\n\t * @property {object} Netx.asset - Used to store custom module options\n\t * @property {Netx.Collections.AssetAttributes} Netx.asset.attributes - The set of metadata objects defined for all assets in the DAM\n\t * @property {array} Netx.asset.bootstrap - Array of promises\n\t * @property {module:asset/models/asset} Netx.asset.current - The asset loaded by the #asset/id route and reflected by assetDetailView\n\t * @property {Netx.Collection.Assets} Netx.asset.cursor - A one-element wide Collection of Assets used to page through the contents of a MiniAssets collection\n\t * @property {number} Netx.asset.cursor.pageSize - Default page size\n\t * @property {number} Netx.asset.cursor.chunkSize - Default chunk size\n\t * @property {Netx.Collections.MiniAssets} Netx.asset.misc - Stores various asset lists not gathered by fetch -- popular assets, recent uploads, etc\n\t * @property {Netx.Collections.PendingAssets} Netx.asset.pending - The set of assets in flux\n\t * @property {object} Netx.asset.presets - The set of asset repurposing presets defined in the DAM\n\t */\n\tvar NS = {};\n\tNS.options = options;\n\n\t// PUBLIC MODELS & COLLECTIONS\n\t/////////////////////////\n\t// Object initializations, etc, go here.\n\n\t// The set of asset metadata objects defined for all assets in the DAM\n\t// Until we can get away from this global namespace\n\t// doing this will maintain the behavior in the rest of the app while allowing\n\t// us to slowly switch over to importing the singleton instead\n\tNS.attributes = attributesSingleton;\n\tNS.assetAttributes = assetAttributesSingleton;\n\tNS.clipAttributes = clipAttributesSingleton;\n\n\t// The set of all MetadataMaps (mappings from asset attributes to embedded metadata fields) defined in the DAM\n\tNS.metadataMaps = new MetadataMapsCollection();\n\t// Stores various asset lists not gathered by fetch -- popular assets, recent uploads, etc\n\tNS.misc = new MiniAssetsCollection([]);\n\t// The set of assets in flux\n\tNS.pending = pendingAssets;\n\n\tNS.tasks = new AssetTasksCollection([])\n\t\t.on('taskComplete', (task) => {\n\t\t\tdispatcher.trigger('asset:task:complete:' + task.id, task);\n\t\t\tdispatcher.trigger('asset:task:complete', task);\n\t\t})\n\t\t// A single asset task has updated\n\t\t.on('taskStatus', (task) => {\n\t\t\tdispatcher.trigger('asset:task:status', task);\n\t\t\tdispatcher.trigger('asset:task:' + task.id + ':status', task);\n\t\t})\n\t\t// All asset tasks have completed\n\t\t.on('complete', (tasks) => {\n\t\t\tdispatcher.trigger('asset:tasks:complete', tasks);\n\t\t});\n\n\tsetGlobal({asset: NS});\n\n\tsetGlobal({currentAsset});\n\n\t// Bootstrapping: collect information from the DAM before we finish initializing\n\t// Collect promises in this array\n\tNS.bootstrap = [\n\t\tAssetNextController.initialize(),\n\t\tAssetGalleryController.initialize(),\n\t\tgetUserLevel() > 0 ? AssetExpirationController.initialize() : true,\n\t\tgetUserLevel() > 0 ? AssetRepurposeController.initialize() : true,\n\t\tgetUserLevel() > 0 ? AssetCustomThumbnailController.initialize() : true,\n\t\tassetProjectEnabled() ? AssetProjectController.initialize() : true,\n\t];\n\n\tif (checksumEnabled()) {\n\t\tsystemAttributes.push({\n\t\t\tcode: 'checksum',\n\t\t\tkey: 'checksum',\n\t\t\tid: -117,\n\t\t\tname: i18n.t('i18n.checksum'),\n\t\t});\n\t}\n\n\t// Until we can get away from this global namespace\n\t// doing this will maintain the behavior in the rest of the app while allowing\n\t// us to slowly switch over to importing the singleton instead\n\tNS.attributeSets = attributeSetsSingleton;\n\t// Sets are becoming necessary for load time things\n\tNS.bootstrap.push(\n\t\tnew Promise((resolve) => {\n\t\t\tattributeSetsSingleton.once('sync', resolve);\n\t\t}),\n\t);\n\n\t// Create a AssetAttributeSetsSelection for each of our layout types\n\tNS.listTypeAttributeSetsSelections = {};\n\t_.each(\n\t\toptions.LIST_TYPE,\n\t\tfunction (listType) {\n\t\t\t// Selected attribute set when in this gallery mode\n\t\t\tthis[listType] = new AssetAttributeSetsSelectionCollection([], {\n\t\t\t\tassetAttributes: NS.assetAttributes,\n\t\t\t\tattributeSets: NS.attributeSets,\n\t\t\t\tuserPrefKey: 'attribute.attributeSets.selected.gallery.' + listType,\n\t\t\t});\n\t\t},\n\t\tNS.listTypeAttributeSetsSelections,\n\t);\n\n\tNS.video = new NxVideo();\n\n\t// Keep variables like `d` contained\n\t// Otherwise we risk polution in the event listeners below\n\t+(function () {\n\t\t// Try to fetch the DAM-wide list of asset attributes\n\t\tvar d = new $.Deferred();\n\t\tNS.bootstrap.push(d.promise());\n\t\tNS.attributes.fetch().then(\n\t\t\tfunction (attrs) {\n\t\t\t\t// got it.\n\t\t\t\td.resolve(attrs);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// If we have a token, it's okay for this to fail.\n\t\t\t\tsession.getToken() ? d.resolve() : d.reject(err);\n\t\t\t},\n\t\t);\n\t})();\n\n\tif (getUserLevel() > 0) {\n\t\tif (\n\t\t\tgetPreference('share.metadata.enabled', true) &&\n\t\t\tcurrentUserMeetsUserLevel(\n\t\t\t\tparseInt(getPreference('share.metadata.userLevel')) || 0,\n\t\t\t)\n\t\t) {\n\t\t\t// Fetch the metadata map list for use in downloads\n\t\t\tNS.bootstrap.push(NS.metadataMaps.fetch());\n\t\t}\n\t}\n\n\t// Asset attributes\n\t// @fires asset:attributes:fetched\n\tNS.attributes.on('sync', function (collection) {\n\t\tif (collection !== this) return;\n\n\t\tdispatcher.trigger('asset:attributes:fetched', this);\n\t});\n\n\t//=================================\n\t//\tEVENT LISTENERS\n\t//=================================\n\n\t/**\n\t * @event module:asset#asset:misc:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'asset:misc:show',\n\t\tfunction (viewOptions) {\n\t\t\tPromise.all([\n\t\t\t\timport(\n\t\t\t\t\t/* webpackChunkName: \"view-assetListView\" */ './views/assetListView'\n\t\t\t\t),\n\t\t\t\timport(\n\t\t\t\t\t/* webpackChunkName: \"view-assetListView\" */ './views/assetListViewTypeManager'\n\t\t\t\t),\n\t\t\t]).then((imported) => {\n\t\t\t\tconst [AssetListView, AssetListViewTypeManagerView] = imported;\n\t\t\t\t// TODO: make own view if we intend to keep\n\t\t\t\tmodalViews.add({\n\t\t\t\t\tviewClass: NS.options.LIST_TYPES_ENABLED\n\t\t\t\t\t\t? AssetListViewTypeManagerView\n\t\t\t\t\t\t: AssetListView,\n\t\t\t\t\tshowActions: ['asset:misc:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * Handler for \"misc\" routes (uploads/recent and assets/popular) which gather\n\t * assets via nonstandard rpc methods.\n\t * @event module:asset#asset:misc\n\t * @param {string} type The specific misc route\n\t * @fires module:asset#asset:misc:show\n\t */\n\tdispatcher.on('asset:misc', function (type) {\n\t\tvar title = i18n.t('i18n.misc');\n\t\tswitch (type) {\n\t\t\tcase 'uploads/recent':\n\t\t\t\tNS.misc.getRecentlyUploadedAssets();\n\t\t\t\ttitle = i18n.t('i18n.recentUploads');\n\t\t\t\tbreak;\n\t\t\tcase 'assets/popular':\n\t\t\t\tNS.misc.getMostViewedAssets();\n\t\t\t\ttitle = i18n.t('i18n.popularAssets');\n\t\t\t\tbreak;\n\t\t}\n\t\t// Show\n\t\tdispatcher.trigger('asset:misc:show', {\n\t\t\tid: 'asset-misc',\n\t\t\tclassName: 'asset-list-wrapper grid scrollable',\n\t\t\tcollection: NS.misc,\n\t\t\tautoSort: false,\n\t\t\theaderTitle: title,\n\t\t});\n\t});\n\n\t/**\n\t * Route to asset\n\t * @event module:asset#asset:route\n\t * @param {number|string|module:asset/models/asset} id An asset id or asset model\n\t */\n\tdispatcher.on('asset:route', function (id, newWindow = false) {\n\t\tif (id instanceof CoreModel) {\n\t\t\tid = id.id;\n\t\t}\n\t\tif (_.isUndefined(pendingAssets.get(id))) {\n\t\t\tif (newWindow) {\n\t\t\t\twindow.open(`${URL.base}#asset/${id}`, '_blank');\n\t\t\t} else {\n\t\t\t\tappRouter.navigate(`asset/${id}`, true);\n\t\t\t}\n\t\t}\n\t});\n\n\t/**\n\t * @event module:asset#asset:detail:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'asset:detail:show',\n\t\tfunction (viewOptions) {\n\t\t\tappViews.add({\n\t\t\t\tviewClass: AssetDetailView,\n\t\t\t\tshowActions: ['asset:detail:show'],\n\t\t\t\tviewOptions: viewOptions,\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * Handles /asset/id routes, optionally with /tab\n\t * @event module:asset#asset:routed\n\t * @param {number|string} id Asset id\n\t * @param {string} tab Tab name\n\t * @fires module:asset#asset:cursor:goto\n\t * @fires module:asset#asset:detail:(tab)\n\t * @fires module:asset#asset:detail:show\n\t * @listens module:asset#asset:current:goto:at\n\t */\n\tdispatcher.on('asset:routed', function (id, tab) {\n\t\t// Show\n\t\tdispatcher.trigger('asset:detail:show', {\n\t\t\tmodel: currentAsset,\n\t\t\tcollection: assetCursor,\n\t\t\ttab: tab,\n\t\t\trouteMatch: new RegExp(/^asset\\/(\\d+)/),\n\t\t});\n\n\t\tvar currentId = assetCursor.length ? currentAsset.id : 0;\n\t\tif (id != currentId) {\n\t\t\tif (tab) {\n\t\t\t\tdispatcher.once('asset:current:goto:at', function () {\n\t\t\t\t\t// trigger additional activities when main fetch finishes\n\t\t\t\t\tdispatcher.trigger('asset:detail:tab:' + tab, id);\n\t\t\t\t});\n\t\t\t}\n\t\t\tdispatcher.trigger('asset:cursor:goto', id);\n\t\t} else {\n\t\t\tif (tab) {\n\t\t\t\t// additional activities\n\t\t\t\tdispatcher.trigger('asset:detail:tab:' + tab, id);\n\t\t\t}\n\t\t}\n\t});\n\n\t/**\n\t * Handle a change in sort order for an assetListView (by re-fetching).\n\t * @event module:asset#asset:sort\n\t * @param {Netx.Collections.Assets} collection Asset collection\n\t */\n\tdispatcher.on('asset:sort', function (collection) {\n\t\tcollection.fetch({reset: true});\n\t});\n\n\t/**\n\t * Helper for routing to audioEditor from asset route\n\t * @event module:asset#asset:audioEditor:routed\n\t * @fires module:audioEditor#audioEditor:routed\n\t */\n\tdispatcher.on('asset:audioEditor:routed', function (id) {\n\t\t// Load module\n\t\tdispatcher.trigger('netx:load:module:audioeditor', {\n\t\t\terror: function (err) {\n\t\t\t\t// Deal with error\n\t\t\t\tconsole.warn(err);\n\t\t\t},\n\t\t\tinitialized: function () {\n\t\t\t\tdispatcher.trigger('audioEditor:routed', id);\n\t\t\t},\n\t\t});\n\t});\n\n\t/**\n\t * Helper for routing to imageEditor from asset route\n\t * @event module:asset#asset:imageEditor:routed\n\t * @fires module:imageEditor#imageEditor:routed\n\t */\n\tdispatcher.on('asset:imageEditor:routed', function (id) {\n\t\t// Load module\n\t\tdispatcher.trigger('netx:load:module:imageeditor', {\n\t\t\terror: function (err) {\n\t\t\t\t// Deal with error\n\t\t\t\tconsole.warn(err);\n\t\t\t},\n\t\t\tinitialized: function () {\n\t\t\t\tdispatcher.trigger('imageEditor:routed', id);\n\t\t\t},\n\t\t});\n\t});\n\n\t//============================================\n\t//\tAsset attribute history\n\t//============================================\n\tif (attributeHistoryEnabled()) {\n\t\t/**\n\t\t * @event module:asset#asset:attributeHistory:search\n\t\t * @param {module:asset/collections/assetAttributeHistory} collection Collection\n\t\t * @param {string} str Search string\n\t\t * @fires module:asset#asset:attributeHistory:search:error\n\t\t * @fires module:asset#asset:attributeHistory:search:success\n\t\t */\n\t\tdispatcher.on('asset:attributeHistory:search', function (collection, str) {\n\t\t\tcollection.search(str).then(\n\t\t\t\tfunction () {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'asset:attributeHistory:search:success',\n\t\t\t\t\t\tcollection,\n\t\t\t\t\t\tstr,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tfunction (error) {\n\t\t\t\t\tdispatcher.trigger('asset:attributeHistory:search:error', error);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t\t/**\n\t\t * @event module:asset#asset:attributeHistory:search:error\n\t\t * @param {string} error Error string\n\t\t * @fires module:app#app:alert\n\t\t */\n\t\tdispatcher.on('asset:attributeHistory:search:error', function (error) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t});\n\n\t\t/**\n\t\t * @event module:asset#asset:attributeHistory:search:clear\n\t\t * @param {module:asset/collections/assetAttributeHistory} collection Collection\n\t\t * @fires module:asset#asset:attributeHistory:search:clear:error\n\t\t * @fires module:asset#asset:attributeHistory:search:clear:success\n\t\t */\n\t\tdispatcher.on('asset:attributeHistory:search:clear', function (collection) {\n\t\t\tcollection.clearSearch().then(\n\t\t\t\tfunction () {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'asset:attributeHistory:search:clear:success',\n\t\t\t\t\t\tcollection,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tfunction (error) {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'asset:attributeHistory:search:clear:error',\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t\t/**\n\t\t * @event module:asset#asset:attributeHistory:search:clear:error\n\t\t * @param {string} error Error string\n\t\t * @fires module:app#app:alert\n\t\t */\n\t\tdispatcher.on(\n\t\t\t'asset:attributeHistory:search:clear:error',\n\t\t\tfunction (error) {\n\t\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t\t},\n\t\t);\n\t\t/**\n\t\t * @event module:asset#asset:attributeHistory:export\n\t\t * @param {object} view attribute history view\n\t\t * @param {object} event jquery\n\t\t * @fires module:share#share:download\n\t\t */\n\t\tdispatcher.on('asset:attributeHistory:export', function (view, event) {\n\t\t\t// \"set\" the asset\n\t\t\tvar assetSet = new AssetSetModel({\n\t\t\t\ttype: assetSetTypes.asset,\n\t\t\t\tids: [view.model.id],\n\t\t\t});\n\t\t\t// make historical download option\n\t\t\tvar downloadOptions = new DownloadOptionsCollection([\n\t\t\t\t{\n\t\t\t\t\ttype: downloadOptionTypes.historical,\n\t\t\t\t},\n\t\t\t]);\n\t\t\t// trigger share\n\t\t\tdispatcher.trigger('share:download', assetSet, downloadOptions, {});\n\t\t});\n\t}\n\t//============================================\n\t//\t!Asset attribute history\n\t//============================================\n\n\t//============================================\n\t//\tAsset attributes\n\t//============================================\n\t/**\n\t * @event module:asset#asset:attributes:submit:confirmed\n\t * @param {object} values Asset attribute data\n\t * @param {Netx.Collections.AssetAttributeSetsSelection} collection Selected attribute sets\n\t * @param {Netx.Collections.AssetAttributeDatum} assetAttributeDatum Asset attribute datum\n\t * @fires module:asset#asset:attributes:submit:error\n\t * @fires module:asset#asset:attributes:submit:success\n\t */\n\tdispatcher.on(\n\t\t'asset:attributes:submit:confirmed',\n\t\tfunction (values, collection, assetAttributeDatum) {\n\t\t\tassetAttributeDatum.parseAndSave(values).then(\n\t\t\t\tfunction (obj) {\n\t\t\t\t\tobj.completed.then(\n\t\t\t\t\t\t(rtn) => {\n\t\t\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t\t\t'asset:attributes:submit:success',\n\t\t\t\t\t\t\t\tcollection,\n\t\t\t\t\t\t\t\tassetAttributeDatum,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t\t\t'asset:attributes:submit:error',\n\t\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t\t\tcollection,\n\t\t\t\t\t\t\t\tassetAttributeDatum,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'asset:attributes:submit:error',\n\t\t\t\t\t\terr,\n\t\t\t\t\t\tcollection,\n\t\t\t\t\t\tassetAttributeDatum,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t);\n\n\t/**\n\t * @event module:asset#asset:attributes:submit:success\n\t * @param {module:asset/models/asset} asset Asset\n\t * @fires module:notification#notification\n\t */\n\tdispatcher.on(\n\t\t'asset:attributes:submit:success',\n\t\tfunction (assetAttributeSetsSelection, assetAttributeDatum) {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\tdescription: i18n.t('i18n.attributesUpdated'),\n\t\t\t\ttype: 'success',\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t//============================================\n\t//\t!Asset attributes\n\t//============================================\n\n\t//============================================\n\t//\tAsset attribute sets\n\t//============================================\n\t/**\n\t * @event module:asset#asset:attributeSet:select:detail\n\t * @param {Netx.Collections.AssetAttributeSetsSelection} attributeSetsSelection Attribute sets selection\n\t * @param {array} selectedIds Generic selected attribute set ids\n\t * @fires module:asset#asset:attributeSet:select:detail:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:attributeSet:select:detail',\n\t\tfunction (attributeSetSelection, selectedIds) {\n\t\t\t// Can override and offer a confirm dialog etc.\n\t\t\tdispatcher.trigger(\n\t\t\t\t'asset:attributeSet:select:detail:confirmed',\n\t\t\t\tattributeSetSelection,\n\t\t\t\tselectedIds,\n\t\t\t);\n\t\t},\n\t);\n\t/**\n\t * @event module:asset#asset:attributeSet:select:detail:confirmed\n\t * @param {Netx.Collections.AssetAttributeSetsSelection} attributeSetsSelection Attribute sets selection\n\t * @param {array} selectedIds Generic selected attribute set ids\n\t */\n\tdispatcher.on(\n\t\t'asset:attributeSet:select:detail:confirmed',\n\t\tfunction (attributeSetSelection, selectedIds) {\n\t\t\tattributeSetSelection.setSelected(selectedIds);\n\t\t},\n\t);\n\t/**\n\t * @event module:asset#asset:attributeSet:select:gallery\n\t * @param {Netx.Collections.AssetAttributeSetsSelection} attributeSetsSelection Attribute sets selection\n\t * @param {string} selectedIds Generic selected attribute set id\n\t * @fires module:asset#asset:attributeSet:select:gallery:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:attributeSet:select:gallery',\n\t\tfunction (attributeSetSelection, selectedId) {\n\t\t\t// Can override and offer a confirm dialog etc.\n\t\t\tdispatcher.trigger(\n\t\t\t\t'asset:attributeSet:select:gallery:confirmed',\n\t\t\t\tattributeSetSelection,\n\t\t\t\tselectedId,\n\t\t\t);\n\t\t},\n\t);\n\t/**\n\t * @event module:asset#asset:attributeSet:select:gallery:confirmed\n\t * @param {Netx.Collections.AssetAttributeSetsSelection} attributeSetsSelection Attribute sets selection\n\t * @param {string} selectedIds Generic selected attribute set id\n\t */\n\tdispatcher.on(\n\t\t'asset:attributeSet:select:gallery:confirmed',\n\t\tfunction (attributeSetSelection, selectedId) {\n\t\t\tattributeSetSelection.setSelected(selectedId);\n\t\t},\n\t);\n\n\t/**\n\t * @event module:asset#asset:attributeSet:select:upload\n\t * @param {Netx.Collections.AssetAttributeSetsSelection} attributeSetsSelection Attribute sets selection\n\t * @param {array} selectedIds Generic selected attribute set ids\n\t * @fires module:asset#asset:attributeSet:select:upload:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:attributeSet:select:upload',\n\t\tfunction (attributeSetSelection, selectedIds) {\n\t\t\t// Can override and offer a confirm dialog etc.\n\t\t\tdispatcher.trigger(\n\t\t\t\t'asset:attributeSet:select:detail:confirmed',\n\t\t\t\tattributeSetSelection,\n\t\t\t\tselectedIds,\n\t\t\t);\n\t\t},\n\t);\n\t/**\n\t * @event module:asset#asset:attributeSet:select:upload:confirmed\n\t * @param {Netx.Collections.AssetAttributeSetsSelection} attributeSetsSelection Attribute sets selection\n\t * @param {array} selectedIds Generic selected attribute set ids\n\t */\n\tdispatcher.on(\n\t\t'asset:attributeSet:select:upload:confirmed',\n\t\tfunction (attributeSetSelection, selectedIds) {\n\t\t\tattributeSetSelection.setSelected(selectedIds);\n\t\t},\n\t);\n\n\t//============================================\n\t//\t!Asset attribute sets\n\t//============================================\n\n\t//============================================\n\t//\tAsset categories\n\t//============================================\n\n\t/**\n\t * Get category objects for given asset and store in provided collection\n\t * Useful for things like AssetEditCategoryView\n\t * @event module:asset#asset:category:add:confirmed\n\t * @param {module:asset/models/asset} model Asset model\n\t * @param {Netx.Collections.Categories} collection Categories collection\n\t * @fires module:asset#asset:categories:get:error\n\t * @fires module:asset#asset:categories:get:success\n\t */\n\tdispatcher.on('asset:categories:get', function (model, collection) {\n\t\tcollection.getCategoriesById(model.get('categories')).then(\n\t\t\tfunction () {\n\t\t\t\t// Success\n\t\t\t\t_.each(collection.models, function (m) {\n\t\t\t\t\tm.set({asset: model}, {silent: true});\n\t\t\t\t});\n\t\t\t\tdispatcher.trigger('asset:categories:get:success', model, collection);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// Error\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'asset:categories:get:error',\n\t\t\t\t\terr,\n\t\t\t\t\tmodel,\n\t\t\t\t\tcollection,\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t});\n\n\t/**\n\t * @event module:asset#asset:category:add:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'asset:category:add:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-assetCategoryEdit\" */ './views/assetCategoryEditView'\n\t\t\t).then((View) => {\n\t\t\t\tmodalViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['asset:category:add:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:category:add\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:asset#asset:category:add:show\n\t * @see module:asset#asset:category:add:confirmed\n\t */\n\tdispatcher.on('asset:category:add', function (model) {\n\t\tvar tree = Netx.category.selectTree;\n\t\ttree.fetch().then(function () {\n\t\t\ttree.disabledCategories = model.get('categories');\n\t\t\ttree.selectedCategory = null;\n\n\t\t\tdispatcher.trigger('asset:category:add:show', {\n\t\t\t\ttree: tree,\n\t\t\t\tmodel: model,\n\t\t\t\teventStr: 'asset:category:add',\n\t\t\t});\n\t\t});\n\t});\n\n\t/**\n\t * @event module:asset#asset:category:add:confirmed\n\t * @param {object} values Asset/Category data\n\t * @param {module:asset/models/asset} model Asset model\n\t */\n\tdispatcher.on('asset:category:add:confirmed', function (values, asset) {\n\t\tvalues = values || {};\n\n\t\tvar catId = parseInt(values.categoryId);\n\n\t\tconst model = asset instanceof AssetModel ? asset : new AssetModel(asset);\n\n\t\tmodel.linkCategory = catId;\n\t\tmodel.addCategory().then(\n\t\t\tfunction () {\n\t\t\t\t// Success\n\t\t\t\tmodel.fetch({duplicateQueryPolicy: 'merge'}).then(function () {\n\t\t\t\t\t// Is current asset, we have a little more work to do before calling this a success\n\t\t\t\t\t$.when(\n\t\t\t\t\t\tmodel.id === currentAsset.id\n\t\t\t\t\t\t\t? currentAsset.fetchCategories()\n\t\t\t\t\t\t\t: true,\n\t\t\t\t\t).then(function () {\n\t\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t\t'asset:category:add:success',\n\t\t\t\t\t\t\tmodel.attributes,\n\t\t\t\t\t\t\tcatId,\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// Error\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'asset:category:add:error',\n\t\t\t\t\terr,\n\t\t\t\t\tmodel.attributes,\n\t\t\t\t\tcatId,\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t});\n\n\t/**\n\t * @event module:asset#asset:category:add:error\n\t * @param {string} error Error string\n\t * @param {module:asset/models/asset} model Asset model\n\t * @param {number} catId Category id\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on('asset:category:add:error', function (error, model, catId) {\n\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t});\n\n\t/**\n\t * @event module:asset#asset:category:add:success\n\t * @param {module:asset/models/asset} model Asset model\n\t * @param {number} catId Category id\n\t */\n\tdispatcher.on('asset:category:add:success', function (model, catId) {\n\t\t//\n\t});\n\n\t/**\n\t * @event module:asset#asset:category:edit:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'asset:category:edit:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-assetCategoryEdit\" */ './views/assetCategoryEditView'\n\t\t\t).then((View) => {\n\t\t\t\tmodalViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['asset:category:edit:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:category:edit\n\t * @param {module:asset/models/asset} model Asset model\n\t * @param {Netx.Models.Category} category Catgegory model\n\t * @fires module:asset#asset:category:edit:show\n\t * @see module:asset#asset:category:edit:confirmed\n\t */\n\tdispatcher.on('asset:category:edit', function (model, category) {\n\t\tvar tree = Netx.category.selectTree;\n\t\ttree.fetch().then(function () {\n\t\t\ttree.disabledCategories = _.without(model.get('categories'), category.id);\n\t\t\ttree.selectedCategory = null;\n\t\t\t//tree.selectedCategory = category.id;\n\t\t\tdispatcher.trigger('asset:category:edit:show', {\n\t\t\t\tmodel: model,\n\t\t\t\ttree: tree,\n\t\t\t\tfromCategory: category,\n\t\t\t});\n\t\t});\n\t});\n\n\t/**\n\t * @event module:asset#asset:category:edit:confirmed\n\t * @param {object} values Category data\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:asset#asset:category:add:confirmed\n\t * @fires module:asset#asset:category:move\n\t */\n\tdispatcher.on('asset:category:edit:confirmed', function (values, asset) {\n\t\t// Vars\n\t\tvar fromCat, toCat;\n\n\t\tasset = asset instanceof AssetModel ? asset.attributes : asset;\n\n\t\tfromCat = parseInt(values.fromCategory);\n\t\ttoCat = parseInt(values.categoryId);\n\n\t\tif (!_.isNumeric(fromCat)) {\n\t\t\tthrow new Error([\"bad category id '\", fromCat, \"'\"].join(''));\n\t\t}\n\t\tif (!_.isNumeric(toCat)) {\n\t\t\tthrow new Error([\"bad category id '\", toCat, \"'\"].join(''));\n\t\t}\n\n\t\t// If we started with category 0, this is an add request\n\t\tif (!fromCat) {\n\t\t\tdispatcher.once('asset:category:add:success', function () {\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'asset:category:edit:success',\n\t\t\t\t\tasset,\n\t\t\t\t\tfromCat,\n\t\t\t\t\ttoCat,\n\t\t\t\t);\n\t\t\t});\n\t\t\tdispatcher.trigger('asset:category:add:confirmed', asset, toCat);\n\t\t} else if (fromCat != toCat) {\n\t\t\tdispatcher.once('asset:category:move:success', function () {\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'asset:category:edit:success',\n\t\t\t\t\tasset,\n\t\t\t\t\tfromCat,\n\t\t\t\t\ttoCat,\n\t\t\t\t);\n\t\t\t});\n\t\t\tdispatcher.trigger('asset:category:move', asset, fromCat, toCat);\n\t\t}\n\t});\n\n\t/**\n\t * move an asset from one category to another\n\t * @event module:asset#asset:category:move\n\t * @param {module:asset/models/asset} model Asset model\n\t * @param {number} fromId From category\n\t * @param {number} toId To category\n\t */\n\tdispatcher.on('asset:category:move', function (asset, fromId, toId) {\n\t\tconst model = asset instanceof AssetModel ? asset : new AssetModel(asset);\n\t\tmodel.moveCategory(parseInt(fromId), parseInt(toId)).then(\n\t\t\tfunction () {\n\t\t\t\t// Success\n\t\t\t\tmodel.fetch({duplicateQueryPolicy: 'merge'}).then(function () {\n\t\t\t\t\t// Is current asset, we have a little more work to do before calling this a success\n\t\t\t\t\t$.when(\n\t\t\t\t\t\tmodel.id === currentAsset.id\n\t\t\t\t\t\t\t? currentAsset.fetchCategories()\n\t\t\t\t\t\t\t: true,\n\t\t\t\t\t).then(function () {\n\t\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t\t'asset:category:move:success',\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\tfromId,\n\t\t\t\t\t\t\ttoId,\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// Error\n\t\t\t\tdispatcher.trigger('asset:category:move:error', err);\n\t\t\t},\n\t\t);\n\t});\n\n\t/**\n\t * @event module:asset#asset:category:move:error\n\t * @param {string} error Error string\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on('asset:category:move:error', function (error) {\n\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t});\n\n\t/**\n\t * @event module:asset#asset:category:move:success\n\t * @param {module:asset/models/asset} model Asset model\n\t */\n\tdispatcher.on('asset:category:move:success', function (model) {\n\t\t//\n\t});\n\n\t/**\n\t * @event module:asset#asset:category:remove\n\t * @param {module:asset/models/asset} model Asset model\n\t * @param {number|string} catId Category id\n\t * @fires module:app#app:confirm\n\t * @fires moduls:asset#asset:category:remove:confirmed\n\t */\n\tdispatcher.on('asset:category:remove', function (model, catId) {\n\t\t// TODO: check that an asset and catId were provided\n\t\tdispatcher.trigger('app:confirm', {\n\t\t\tmessage: i18n.t('i18n.confirmRemovalFromCategory'),\n\t\t\teventStr: 'asset:category:remove',\n\t\t\targs: [model, parseInt(catId)],\n\t\t});\n\t});\n\n\t/**\n\t * @event module:asset#asset:category:remove:confirmed\n\t * @param {module:asset/models/asset} model Asset model\n\t * @param {number|string} catId Category id\n\t * @fires module:app#app:confirm\n\t */\n\tdispatcher.on('asset:category:remove:confirmed', function (asset, catId) {\n\t\tcatId = parseInt(catId);\n\n\t\tconst model = asset instanceof AssetModel ? asset : new AssetModel(asset);\n\n\t\t// Invalid or missing asset model\n\t\tif (!model || !(model instanceof AssetModel)) {\n\t\t\tdispatcher.trigger(\n\t\t\t\t'asset:category:remove:error',\n\t\t\t\tnew Error(i18n.t('i18n.assetWasNotProvided')),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\t// Invalid or missing category id\n\t\telse if (isNaN(catId)) {\n\t\t\tdispatcher.trigger(\n\t\t\t\t'asset:category:remove:error',\n\t\t\t\tnew Error(i18n.t('i18n.validCategoryIdWasNotProvided')),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tmodel.linkCategory = catId;\n\t\tmodel.removeCategory().then(\n\t\t\tfunction () {\n\t\t\t\t// Fetch model to sync data\n\t\t\t\tmodel.fetch({duplicateQueryPolicy: 'merge'}).then(function () {\n\t\t\t\t\t// Is current asset, we have a little more work to do before calling this a success\n\t\t\t\t\t$.when(\n\t\t\t\t\t\tmodel.id === currentAsset.id\n\t\t\t\t\t\t\t? currentAsset.fetchCategories()\n\t\t\t\t\t\t\t: true,\n\t\t\t\t\t).then(function () {\n\t\t\t\t\t\t// Fire success\n\t\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t\t'asset:category:remove:success',\n\t\t\t\t\t\t\tmodel.attributes,\n\t\t\t\t\t\t\tcatId,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tdispatcher.trigger('asset:category:removed', model.id, catId);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// Error\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'asset:category:remove:error',\n\t\t\t\t\terr,\n\t\t\t\t\tmodel.attributes,\n\t\t\t\t\tcatId,\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t});\n\n\t/**\n\t * @event module:asset#asset:category:remove:error\n\t * @param {string} error Error string\n\t * @param {module:asset/models/asset} model Asset model\n\t * @param {number|string} catId Category id\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on('asset:category:remove:error', function (error, asset, catId) {\n\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t});\n\n\t/**\n\t * @event module:asset#asset:category:remove:success\n\t * @param {module:asset/models/asset} model Asset model\n\t * @param {number|string} catId Category id\n\t */\n\tdispatcher.on('asset:category:remove:success', function (asset, catId) {\n\t\t//\n\t});\n\t//============================================\n\t//\t!Asset categories\n\t//============================================\n\n\t//============================================\n\t//\tAsset checkin\n\t//============================================\n\tif (NS.options.CHECKIN_ENABLE) {\n\t\t/**\n\t\t * @event module:asset#asset:checkin\n\t\t * @param {module:asset/models/asset} model Asset model\n\t\t * @fires module:asset#asset:checkin:show\n\t\t */\n\t\tdispatcher.on('asset:checkin', function (model) {\n\t\t\tdispatcher.trigger('upload:asset:checkin', model);\n\t\t});\n\t} // if CHECKIN_ENABLE\n\t//============================================\n\t//\t!Asset checkin\n\t//============================================\n\n\t//============================================\n\t//\tAsset checkout\n\t//============================================\n\t/**\n\t * The asset:checkout handler takes either an asset or an ID as an argument; if neither is provided,\n\t * it operates on Netx.asset.current . Triggers the checkout() method on the asset, then a download\n\t * if DOWNLOAD_ON_CHECKOUT is true.\n\t * @event module:asset#asset:checkout\n\t * @param {module:asset/models/asset} asset Asset\n\t * @fires module:asset#asset:checkout:error\n\t * @fires module:asset#asset:checkout:success\n\t */\n\tdispatcher.on('asset:checkout', function (asset) {\n\t\tif (!asset) {\n\t\t\tdispatcher.trigger(\n\t\t\t\t'asset:checkout:error',\n\t\t\t\tnew Error('Asset was not provided for `asset:checkout`'),\n\t\t\t);\n\t\t}\n\n\t\tasset.checkout().then(\n\t\t\t(batchJob) => {\n\t\t\t\tif (!batchJob) throw new Error('batch job not returned');\n\t\t\t\treturn batchJob.completed.then(() => {\n\t\t\t\t\tif (NS.options.DOWNLOAD_ON_CHECKOUT) {\n\t\t\t\t\t\topenDownloadLink(\n\t\t\t\t\t\t\t`${URL.assetEndpoint}/${asset.id}/original/attachment`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tdispatcher.trigger('asset:checkout:success', asset);\n\t\t\t\t\tdispatcher.trigger('asset:checkedOut', asset.id);\n\t\t\t\t});\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('asset:checkout:error', err, asset);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:asset#asset:checkout:error\n\t * @param {string} error Error string\n\t * @param {module:asset/models/asset} asset Asset\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'asset:checkout:error',\n\t\tfunction (error, asset) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:checkout:success\n\t * @param {module:asset/models/asset} asset Asset\n\t * @fires module:notification#notification\n\t */\n\tdispatcher.on(\n\t\t'asset:checkout:success',\n\t\tfunction (asset) {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\tids: [asset.id],\n\t\t\t\ttitle: i18n.t('i18n.assetUpdated'),\n\t\t\t\tdescription: i18n.t('i18n.checkedOutAsset'),\n\t\t\t\ttype: 'success',\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * Cancel checkout\n\t * @event module:asset#asset:checkout:cancel\n\t * @param {module:asset/models/asset} asset Asset\n\t * @fires module:app#app:confirm\n\t */\n\tdispatcher.on('asset:checkout:cancel', function (asset) {\n\t\tdispatcher.trigger('app:confirm', {\n\t\t\tmessage: i18n.t('i18n.confirmCheckoutCancel'),\n\t\t\teventStr: 'asset:checkout:cancel',\n\t\t\targs: [asset],\n\t\t});\n\t});\n\t/**\n\t * Cancel checkout confirmed\n\t * @event module:asset#asset:checkout:cancel:confirmed\n\t * @param {module:asset/models/asset} asset Asset\n\t * @fires module:asset#asset:checkout:cancel:success\n\t */\n\tdispatcher.on('asset:checkout:cancel:confirmed', function (asset) {\n\t\tasset\n\t\t\t.cancelCheckout()\n\t\t\t.then((batchJob) => {\n\t\t\t\tif (!batchJob) throw new Error('batch job not returned');\n\t\t\t\treturn batchJob.completed.then(() => {\n\t\t\t\t\tdispatcher.trigger('asset:checkout:cancel:success', asset);\n\t\t\t\t\tdispatcher.trigger('asset:checkout:cancelled', asset.id);\n\t\t\t\t});\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\tdispatcher.trigger('asset:checkout:cancel:error', asset);\n\t\t\t});\n\t});\n\t/**\n\t * @event module:asset#asset:checkout:cancel:error\n\t * @param {string} error Error string\n\t * @param {module:asset/models/asset} asset Asset\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'asset:checkout:cancel:error',\n\t\tfunction (error, asset) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t//============================================\n\t//\t!Asset checkout\n\t//============================================\n\n\t//============================================\n\t//\tAsset constituents\n\t//============================================\n\t/**\n\t * Action handler: select a constituent as the new asset thumbnail\n\t * @event module:asset#asset:constituent:choose\n\t * @param {module:asset/models/assetConstituent} model AssetConstituent model\n\t * @fires module:asset#asset:constituent:choose:error\n\t * @fires module:asset#asset:constituent:choose:success\n\t */\n\tdispatcher.on('asset:constituent:choose', function (model) {\n\t\tmodel.setDisplay().then(\n\t\t\tfunction (uuid) {\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'asset:constituent:choose:success',\n\t\t\t\t\tmodel,\n\t\t\t\t\tcurrentAsset.id,\n\t\t\t\t);\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'app:pollBatchJob',\n\t\t\t\t\tuuid,\n\t\t\t\t\t'asset:constituent:choose',\n\t\t\t\t\tfalse,\n\t\t\t\t\t[model, currentAsset.id],\n\t\t\t\t);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('asset:constituent:choose:error', err, model);\n\t\t\t},\n\t\t);\n\t});\n\n\t/**\n\t * @event module:asset#asset:constituent:choose:error\n\t * @param {string} error Error string\n\t * @param {module:asset/models/assetConstitutent} model AssetConstitutent model\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'asset:constituent:choose:error',\n\t\tfunction (error, model) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * UI actions for constituent chosen\n\t * @event module:asset#asset:constituent:choose:success\n\t * @param {module:asset/models/assetConstituent} constituent AssetConstitutent model\n\t * @param {number} assetId Asset id\n\t * @fires module:notification#notification\n\t * @see module:asset#asset:constituent:choose\n\t */\n\tdispatcher.on(\n\t\t'asset:constituent:choose:success',\n\t\tfunction (constituent, assetId) {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\tids: [assetId],\n\t\t\t\ttitle: i18n.t('i18n.assetUpdated'),\n\t\t\t\tdescription: i18n.t('i18n.constituentChosen'),\n\t\t\t\ttype: 'success',\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * Refetch the thumbnail once the asset constituent job has succeeded\n\t * @event module:asset#asset:constituent:choose:succeeded\n\t * @param {module:asset/models/assetConstituent} constituent AssetConstituent model\n\t * @param {number} assetId Asset ID\n\t * @see Netx.Views.AssetDetailView\n\t */\n\tdispatcher.on(\n\t\t'asset:constituent:choose:succeeded',\n\t\tfunction (constituent, assetId) {\n\t\t\t//\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * Delete asset video clip constituent\n\t * @event module:asset#asset:constituent:clip:delete\n\t * @param {module:asset/models/assetConsitutent_clip} clip Constituent (clip)\n\t * @fires module:app#app:confirm:delete\n\t * @see module:asset#asset:constituent:clip:delete:confirmed\n\t */\n\tdispatcher.on('asset:constituent:clip:delete', function (clip) {\n\t\tif (!currentUserMeetsUserLevel(UserLevel.PRODUCER)) return;\n\n\t\tif (!clip) {\n\t\t\tdispatcher.trigger(\n\t\t\t\t'asset:constituent:clip:delete:error',\n\t\t\t\tnew Error(i18n.t('i18n.invalidConstituentClip')),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tdispatcher.trigger('app:confirm:delete', {\n\t\t\tmessage: i18n.t('i18n.confirmConsitutentClipDelete'),\n\t\t\teventStr: 'asset:constituent:clip:delete',\n\t\t\targs: [clip],\n\t\t});\n\t});\n\n\t/**\n\t * Delete asset video clip constituent\n\t * @event module:asset#asset:constituent:clip:delete:confirmed\n\t * @param {module:asset/models/assetConsitutent_clip} clip Constituent (clip) model\n\t * @fires module:asset#asset:constituent:clip:delete:error\n\t * @fires module:asset#asset:constituent:clip:delete:success\n\t * @see module:asset#asset:constituent:clip:delete\n\t */\n\tdispatcher.on('asset:constituent:clip:delete:confirmed', function (clip) {\n\t\tif (!currentUserMeetsUserLevel(UserLevel.PRODUCER)) return;\n\n\t\tclip.destroy({wait: true}).then(\n\t\t\tfunction () {\n\t\t\t\tdispatcher.trigger('asset:constituent:clip:delete:success', clip);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('asset:constituent:clip:delete:error', err, clip);\n\t\t\t},\n\t\t);\n\t});\n\n\t/**\n\t * @event module:asset#asset:constituent:clip:delete:error\n\t * @param {string} error Error string\n\t * @param {module:asset/models/assetConstitutent_clip} clip Consitutent (clip) model\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'asset:constituent:clip:delete:error',\n\t\tfunction (error, clip) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:asset#asset:constituent:clip:delete:success\n\t * @param {module:asset/models/assetConstitutent_clip} clip Consitutent (clip) model\n\t * @fires module:notification#notification\n\t * @see module:asset#asset:constituent:clip:delete\n\t */\n\tdispatcher.on(\n\t\t'asset:constituent:clip:delete:success',\n\t\tfunction (clip) {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\tids: [clip.get('assetId')],\n\t\t\t\ttitle: i18n.t('i18n.constituentClipsUpdated'),\n\t\t\t\tdescription: i18n.t('i18n.constituentClipDeleted'),\n\t\t\t\ttype: 'success',\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:asset#asset:constituent:clip:download:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'asset:constituent:clip:download:show',\n\t\tfunction (viewOptions) {\n\t\t\tmodalViews.add({\n\t\t\t\tviewClass: AssetConstituentDownloadView_Clip,\n\t\t\t\tshowActions: ['asset:constituent:clip:download:show'],\n\t\t\t\tviewOptions: viewOptions,\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:constituent:clip:download\n\t * @param {module:asset/models/assetConstitutent_clip} clip Consitutent (clip) model\n\t * @fires module:asset#asset:constituent:clip:download:show\n\t * @fires module:asset#asset:constituent:clip:download:error\n\t * @see module:asset#asset:constituent:clip:download:confirmed\n\t */\n\tdispatcher.on('asset:constituent:clip:download', function (clip) {\n\t\tvar assetSet = new AssetSetModel({\n\t\t\ttype: assetSetTypes.asset,\n\t\t\tids: [clip.get('assetId')],\n\t\t});\n\n\t\tassetSet.getDownloadOptions().then(\n\t\t\tfunction (opts) {\n\t\t\t\tif (opts.length) {\n\t\t\t\t\tdispatcher.trigger('asset:constituent:clip:download:show', {\n\t\t\t\t\t\tmodel: clip,\n\t\t\t\t\t\tpresets: NS.presets.video ? NS.presets.video.models : [],\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'asset:constituent:clip:download:error',\n\t\t\t\t\t\tnew Error(i18n.t('i18n.constituentClipNoDownloadOptionsAvailable')),\n\t\t\t\t\t\tclip,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('asset:constituent:clip:download:error', err);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:asset#asset:constituent:clip:download:confirmed\n\t * @param {object} data Form data\n\t * @param {module:asset/models/assetConstitutent_clip} clip Consitutent (clip) model\n\t * @fires module:asset#asset:constituent:clip:download:error\n\t * @fires module:asset#asset:constituent:clip:download:success\n\t * @fires module:share#share:assetSet:menu\n\t * @see module:asset#asset:constituent:clip:download\n\t * @todo This is out of line with all other downloads since this is not an asset - this is a contituent of an asset\n\t * Need to get the server to respect the idea of types other than an asset so that we can run this all through the\n\t * standard share workflow. What I have done here is rewrite how share:assetSet:repurpose:manual works\n\t */\n\tdispatcher.on('asset:constituent:clip:download:confirmed', (data, clip) => {\n\t\tconst processIdx = parseInt(data.process);\n\t\tconst process = NS.presets.video.at(processIdx);\n\n\t\tif (!process) {\n\t\t\tdispatcher.trigger(\n\t\t\t\t'asset:constituent:clip:download:error',\n\t\t\t\tnew Error(i18n.t('i18n.invalidProcess')),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst assetSet = {\n\t\t\tids: [clip.get('assetId')],\n\t\t\ttype: assetSetTypes.asset,\n\t\t\trecursive: false,\n\t\t};\n\n\t\tdispatcher.trigger(\n\t\t\tASSET_SET_EVENT.PREPARE,\n\t\t\t[assetSet],\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\t...downloadOptionDefaults,\n\t\t\t\t\tfamily: 'video',\n\t\t\t\t\t// if we set this, the system ticks itself into thinking we are presetProcess once an approval results in a share link\n\t\t\t\t\t// name: `Clip ${clip.get('title')} [${process.get('name')}]`,\n\t\t\t\t\toptions: clip.getManualRepurposeOptions(process),\n\t\t\t\t\ttype: DownloadOptionType.MANUAL_REPURPOSE,\n\t\t\t\t},\n\t\t\t],\n\t\t);\n\n\t\t// dismiss modal\n\t\tdispatcher.trigger(\n\t\t\t'asset:constituent:clip:download:success',\n\t\t\tclip,\n\t\t\tprocess,\n\t\t);\n\t});\n\t/**\n\t * @event module:asset#asset:constituent:clip:download:error\n\t * @param {string} error Error string\n\t * @param {module:asset/models/assetConstitutent_clip} clip Consitutent (clip) model\n\t * @fires module:app#app:alert\n\t * @see module:asset#asset:constituent:clip:download:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:constituent:clip:download:error',\n\t\tfunction (error, clip) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * Edit asset video clips\n\t * @event module:asset#asset:constituent:clip:edit\n\t * @param {number} assetId Asset id\n\t * @param {number} clipId Asset clip id\n\t * @fires module:asset#asset:constituent:clip:edit:error\n\t * @fires module:videoEditor#videoEditor:clips:routed\n\t * @see module:asset#asset:constituent:clip:edit:confirmed\n\t */\n\tdispatcher.on('asset:constituent:clip:edit', function (assetId, clipId) {\n\t\tif (!currentUserMeetsUserLevel(UserLevel.PRODUCER)) return;\n\n\t\tdispatcher.trigger('netx:load:module:videoeditor', {\n\t\t\terror: function (err) {\n\t\t\t\tdispatcher.trigger('asset:constituent:clip:edit:error', err, clipId);\n\t\t\t},\n\t\t\tinitialized: function () {\n\t\t\t\t// TODO: fix to have route in video editor\n\t\t\t\tdispatcher.trigger('videoEditor:clips:routed', assetId, clipId);\n\t\t\t},\n\t\t});\n\t});\n\n\t/**\n\t * @event module:asset#asset:constituent:clip:edit:confirmed\n\t * @param {module:asset/models/asset} asset Asset model\n\t * @param {module:asset/models/assetConstitutent_clip} clip Consitutent (clip)\n\t * @param {object} data Constituent data\n\t * @fires module:asset#asset:constituent:clip:edit:error\n\t * @fires module:asset#asset:constituent:clip:edit:success\n\t * @see module:asset#asset:constituent:clip:edit\n\t */\n\tdispatcher.on(\n\t\t'asset:constituent:clip:edit:confirmed',\n\t\tfunction (asset, clip, data) {\n\t\t\tif (!currentUserMeetsUserLevel(UserLevel.PRODUCER)) return;\n\n\t\t\t// Vars\n\t\t\tvar isNew = false;\n\n\t\t\t// New constituent (clip)\n\t\t\tif (!clip) {\n\t\t\t\tisNew = true;\n\t\t\t\tclip = new AssetConstituentClipModel({\n\t\t\t\t\tassetId: asset.id,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Set data\n\t\t\tclip.set({\n\t\t\t\tattributes: data.attributes,\n\t\t\t\tstartTime: data.range.start,\n\t\t\t\tendTime: data.range.end,\n\t\t\t\ttitle: data.title,\n\t\t\t});\n\n\t\t\t// Save clip\n\t\t\tclip.save().then(\n\t\t\t\tfunction () {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'asset:constituent:clip:edit:success',\n\t\t\t\t\t\tasset,\n\t\t\t\t\t\tclip,\n\t\t\t\t\t\tisNew,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\tdispatcher.trigger('asset:constituent:clip:edit:error', err);\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t);\n\n\t/**\n\t * @event module:asset#asset:constituent:clip:edit:error\n\t * @param {string} error Error string\n\t * @fires module:app#app:alert\n\t * @see module:asset#asset:constituent:clip:edit:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:constituent:clip:edit:error',\n\t\tfunction (error) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:asset#asset:constituent:clip:edit:success\n\t * @param {module:asset/models/asset} asset Asset\n\t * @param {module:asset/models/assetConstituent_clip} clip Constituent\n\t * @param {boolean} isNew New clip\n\t * @fires module:notification#notification\n\t * @see module:asset#asset:constituent:clip:edit:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:constituent:clip:edit:success',\n\t\tfunction (asset, clip, isNew) {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\tids: [asset.id],\n\t\t\t\ttitle: i18n.t('i18n.constituentClips'),\n\t\t\t\tdescription: isNew\n\t\t\t\t\t? i18n.t('i18n.constituentClipCreated')\n\t\t\t\t\t: i18n.t('i18n.constituentClipsUpdated'),\n\t\t\t\ttype: 'success',\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * Create new asset video clips\n\t * @event module:asset#asset:constituent:clip:new\n\t * @param {number} assetId Asset id\n\t * @param {number} clipId Asset clip id\n\t * @fires module:asset#asset:constituent:clip:edit\n\t */\n\tdispatcher.on('asset:constituent:clip:new', function (assetId) {\n\t\tif (!currentUserMeetsUserLevel(UserLevel.PRODUCER)) return;\n\t\t// Maybe one day we will want or need to do something special for new clip requests\n\t\t// For now it is just the same as hitting asset:constituent:clip:edit except without a clipId\n\t\tdispatcher.trigger('asset:constituent:clip:edit', assetId);\n\t});\n\n\t//============================================\n\t//\tAsset constituents\n\t//============================================\n\n\t//============================================\n\t//\tAsset cursor\n\t//============================================\n\tassetCursor.on('nx:asset:cursor:updated', function (cursor, response, opts) {\n\t\tvar asset = cursor.at(0);\n\t\t// If we're routed to an assetDetail tab, and the current asset has changed,\n\t\t// route to the analogous tab for that asset.\n\t\t//\n\t\t// This is a slick piece of code however it causes problems when the\n\t\t// next/prev asset doesn't have whatever \"bits\" have been maintained\n\t\t// Commenting until we have a better solution\n\t\t// 2014.03.17 ~sak~\n\t\t//\n\t\t// assetDetailView deals with that pretty well, I think; also, this is already uncommented ... -m- 3/3/2015\n\t\t//\n\t\tvar hash = window.location.hash,\n\t\t\tbits = hash.match(/#asset\\/\\d+(\\/.*)/);\n\t\tif (bits) {\n\t\t\t// keep whatever trailing bit is currently on the route\n\t\t\t// NOTE: routing to the current route is a no-op.\n\t\t\tappRouter.navigate('asset/' + asset.id + bits[1], {trigger: true});\n\t\t}\n\t\t// DAM-5858\n\t\t// this originally was just an else - but if you had opened the asset detail prior and did a faceted search (anything that causes a sync on cursor)\n\t\t// we were forcing a route to the asset even though no one wanted to go there in the least -jse- 2015/03/07\n\t\telse if (hash.includes('#asset/')) {\n\t\t\tvar curId = hash.split('#asset/')[1];\n\t\t\tif (asset.id !== Number(curId)) {\n\t\t\t\tappRouter.navigate('asset/' + asset.id, {trigger: true});\n\t\t\t}\n\t\t}\n\t});\n\t/*\n\t * When a miniAssetList fetches and it has a view that is visible, that view will trigger this event,\n\t * to say that the next time we enter asset detail view, this is the collection we want to page through.\n\t * @event module:asset#asset:cursor:cacheFetch\n\t * @param {Netx.Collections.Assets} collection Assets collection\n\t */\n\tdispatcher.on('asset:cursor:cacheFetch', function (collection) {\n\t\t// Netx.asset.lastFetch = collection;\n\t\tassetCursor.cacheFetch(collection);\n\t});\n\n\t/**\n\t * make sure that cursor[0] == a specific asset ID,\n\t * while trying to be smart about which selection it was found in.\n\t * @event module:asset#asset:cursor:goto\n\t * @param {number|string} id Asset id\n\t * @param {boolean} forceSimple Simple goto\n\t * @fires module:app#app:alert\n\t * @fires module:asset#asset:cursor:goto\n\t * @fires module:asset#asset:cursor:goto:at\n\t */\n\tdispatcher.on('asset:cursor:goto', function (id, forceSimple) {\n\t\tassetCursor.goto(id, forceSimple).then(\n\t\t\tfunction () {\n\t\t\t\t// If we got no assets back, the asset may have just been deleted, or else something else has gone weird.\n\t\t\t\t// Let's not get into an endless loop over it.\n\t\t\t\tif (assetCursor.length === 0) {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'app:alert',\n\t\t\t\t\t\t'asset #' + id + ' not found',\n\t\t\t\t\t\t'Warning',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// Occasionally, due to a changing set of assets on the cursor, the lastFetch\n\t\t\t\t// will not get the correct ID. Check for that here, and fall back if necessary.\n\t\t\t\telse if (assetCursor.at(0).id != id) {\n\t\t\t\t\tif (!forceSimple) {\n\t\t\t\t\t\t// try again with simple search\n\t\t\t\t\t\tdispatcher.trigger('asset:cursor:goto', id, true);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.warn('DAM couldnt find asset ' + id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Otherwise, cursor has arrived at the correct asset.\n\t\t\t\telse {\n\t\t\t\t\tdispatcher.trigger('asset:cursor:goto:at', id);\n\t\t\t\t}\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// Error\n\t\t\t\tdispatcher.trigger('asset:cursor:goto:at', id);\n\t\t\t},\n\t\t);\n\t});\n\t//============================================\n\t//\t!Asset cursor\n\t//============================================\n\n\t//============================================\n\t//\tAsset custom thumbnail\n\t//============================================\n\t/**\n\t * @event module:asset#asset:customThumbnail:select\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:upload#upload:asset:customThumbnail:select\n\t */\n\tdispatcher.on('asset:customThumbnail:select', function (model) {\n\t\tdispatcher.trigger('upload:asset:customThumbnail:select', model);\n\t});\n\t//============================================\n\t//\tAsset custom thumbnail\n\t//============================================\n\n\t//============================================\n\t//\tAsset detail\n\t//============================================\n\t//\n\t// create actions and events for the tabs in the asset detail view, having the appropriate user levels:\n\t//\n\t+(function () {\n\t\t// Possible outcomes\n\t\t// asset:detail:show:attributeHistory\n\t\t// asset:detail:show:attributes\n\t\t// asset:detail:show:categories\n\t\t// asset:detail:show:detail\n\t\t// asset:detail:show:fileinfo\n\t\t// asset:detail:show:index\n\t\t// asset:detail:show:metadata\n\t\t// asset:detail:show:links\n\t\t// asset:detail:show:clips\n\t\t// asset:detail:show:constituents_keyframes\n\t\t// asset:detail:show:constituents_layers\n\t\t// asset:detail:show:constituents_pages\n\t\t// asset:detail:show:overview\n\t\t// asset:detail:show:permissions\n\t\t// asset:detail:show:preview\n\t\t// asset:detail:show:publicLinks\n\t\t// asset:detail:show:uasge\n\t\t// asset:detail:show:versions\n\t\t// asset:detail:show:views\n\t\t// asset:detail:show:projects\n\t\t// asset:detail:show:reviews\n\t\t// asset:detail:show:smartLabels\n\t\tvar newActions = [];\n\t\t_.each(assetThings, function (thing, tab, all) {\n\t\t\tif (_.isObject(thing)) {\n\t\t\t\tconst _thing = all[tab];\n\t\t\t\tconst namespace = `asset:detail:show:${tab}`;\n\n\t\t\t\tlet enableTest = [];\n\n\t\t\t\tconst _enableTest = _thing.enableTest;\n\t\t\t\tif (_enableTest) {\n\t\t\t\t\tif (_.isArray(_enableTest)) {\n\t\t\t\t\t\tenableTest = enableTest.concat(_enableTest);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tenableTest.push(_enableTest);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Register action\n\t\t\t\tconst action = {\n\t\t\t\t\tevent: namespace,\n\t\t\t\t\tlabel: _thing.label || 'undefined',\n\t\t\t\t\ttestOnSync: currentAsset,\n\t\t\t\t\tuserLevel: _thing.userLevel || 1,\n\t\t\t\t\twrapper(view) {\n\t\t\t\t\t\treturn [currentAsset];\n\t\t\t\t\t},\n\t\t\t\t\tenableTest: enableTest,\n\t\t\t\t\troute(model) {\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\tNS.options.ROUTE || 'asset',\n\t\t\t\t\t\t\t'/',\n\t\t\t\t\t\t\tcurrentAsset.id,\n\t\t\t\t\t\t\t'/',\n\t\t\t\t\t\t\ttab,\n\t\t\t\t\t\t].join('');\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\tif (_thing.matrix) {\n\t\t\t\t\taction.matrix = _thing.matrix;\n\t\t\t\t}\n\t\t\t\tnewActions.push(action);\n\t\t\t}\n\t\t});\n\t\tRegisterLegacyAction(newActions);\n\n\t\t// Enablers for these tabs follow a standard pattern: asset:detail:thing.enableTest == Netx.Enablers.assetHasThing;\n\t\t// We need to intersect since these tabs are customizable in config\n\t\tvar intersect = _.intersection(NS.options.DETAIL_TABS, [\n\t\t\t'views',\n\t\t\t'links',\n\t\t\t'metadata',\n\t\t\t'projects',\n\t\t\t'reviews',\n\t\t\t'clips',\n\t\t\t'constituents_keyframes',\n\t\t\t'constituents_layers',\n\t\t\t'constituents_pages',\n\t\t\t'versions',\n\t\t]);\n\t\t_(intersect).each(function (thing) {\n\t\t\t// What is the point of defining showEmpty in asset detail view if Netx.views.assetDetailView does not exist at this point - and we should not ever have to touch a view like that\n\t\t\t// This is dirty but neccessary for now- can not go digging this all up in a release branch.\n\t\t\tvar showEmpty = false,\n\t\t\t\tucThing = _.ucfirst(thing);\n\n\t\t\tswitch (thing) {\n\t\t\t\tcase 'views':\n\t\t\t\t\tshowEmpty = true;\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!showEmpty) {\n\t\t\t\tFindLegacyAction(`asset:detail:show:${thing}`).enableTest.push(\n\t\t\t\t\tAssetDetailEnablers[`assetHas${ucThing}`],\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t})();\n\n\tif (attributeHistoryEnabled()) {\n\t\t/**\n\t\t * @event module:asset#asset:detail:attributeHistory\n\t\t */\n\t\tdispatcher.on('asset:detail:attributeHistory', function () {\n\t\t\tcurrentAsset.fetchAttributeHistory();\n\t\t});\n\t}\n\n\tif (assetProjectEnabled()) {\n\t\t/**\n\t\t * @event module:asset#asset:detail:show:projects\n\t\t */\n\t\tdispatcher.on('asset:detail:projects', function () {\n\t\t\tcurrentAsset.fetchProjects();\n\t\t});\n\t\t/**\n\t\t * @event module:asset#asset:detail:show:reviews\n\t\t */\n\t\tdispatcher.on('asset:detail:reviews', function () {\n\t\t\tcurrentAsset.fetchReviews();\n\t\t});\n\t}\n\n\t/**\n\t * Calls getForAssets() once the attribute view is shown\n\t * @event module:asset#asset:detail:attributes\n\t */\n\tdispatcher.on('asset:detail:attributes', function () {\n\t\t// Do anything that may be necessary when we show the attributes for the current asset\n\t});\n\n\t/**\n\t * When the permissions pane shows, fetch the current asset's permissions\n\t * @event module:asset#asset:detail:permissions\n\t */\n\tdispatcher.on('asset:detail:permissions', function () {\n\t\tcurrentAsset.fetchPermissions();\n\t});\n\n\t/**\n\t * Load asset versions when the versions tab is shown\n\t * @event module:asset#asset:detail:versions\n\t */\n\tdispatcher.on('asset:detail:versions', function () {\n\t\tcurrentAsset.fetchVersions();\n\t});\n\n\t/**\n\t * showing and managing the usage\n\t * @event module:asset#asset:detail:usage\n\t */\n\tdispatcher.on('asset:detail:usage', function () {\n\t\tcurrentAsset.fetchUsage();\n\t});\n\n\t/**\n\t * showing and managing the index\n\t * @event module:asset#asset:detail:index\n\t */\n\tdispatcher.on('asset:detail:index', function () {\n\t\tcurrentAsset.fetchIndices();\n\t});\n\n\t/**\n\t * showing metadata\n\t * @event module:asset#asset:detail:metadata\n\t */\n\tdispatcher.on('asset:detail:metadata', function () {\n\t\tcurrentAsset.fetchMetadata();\n\t});\n\n\t/**\n\t * When the asset views tab is shown, load views from the server\n\t * @event module:asset#asset:detail:views\n\t */\n\tdispatcher.on('asset:detail:views', function () {\n\t\tcurrentAsset.fetchViews();\n\t});\n\n\t/**\n\t * Fetch every asset detail object for the current asset,\n\t * by triggering the specific events for each section as required.\n\t * @event module:asset#asset:detail:all\n\t */\n\tdispatcher.on('asset:detail:all', function () {\n\t\t// (The enable test is the canonical way to check if this asset has this data.)\n\t\t_.each(NS.options.DETAIL_TABS, function (tab) {\n\t\t\tvar tabTest = Netx.Enablers['assetHas' + _.ucfirst(tab)];\n\t\t\t// if the test doesn't exist,\n\t\t\t// or if it passes,\n\t\t\t// (or if it's 'versions' which expects to be called every time -- TODO: refactor that)\n\t\t\t// trigger an event.\n\t\t\t// TODO: fix - there is not a `Netx.views.assetDetailView`\n\t\t\tif (\n\t\t\t\ttab == 'versions' ||\n\t\t\t\t!_.isFunction(tabTest) ||\n\t\t\t\ttabTest(Netx.views.assetDetailView)\n\t\t\t) {\n\t\t\t\t_.defer(function () {\n\t\t\t\t\t// use _.defer to spread these out a little bit.\n\t\t\t\t\tdispatcher.trigger('asset:detail:' + tab);\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t});\n\n\t/**\n\t * Fetching asset categories, when the categories tab is clicked, fetch the categories\n\t * @event module:asset#asset:detail:categories\n\t */\n\tdispatcher.on('asset:detail:categories', function () {\n\t\tcurrentAsset.fetchCategories();\n\t});\n\n\t// Rather than creating a loop and pointing these all to asset:detail:constituents - I have found it best to just break them out now\n\t// because clips are a different kind of constituent than we have had before and with the new attribute setup it is likely different types\n\t// of constituents may need different treatment in the future\n\t/**\n\t * Fetching asset constituents, when the constituents tab is clicked, fetch the constituents\n\t * @event module:asset#asset:detail:constituents\n\t * @see module:asset#asset:detail:clips\n\t * @see module:asset#asset:detail:constituents_keyframes\n\t * @see module:asset#asset:detail:constituents_layers\n\t * @see module:asset#asset:detail:constituents_pages\n\t */\n\tdispatcher.on('asset:detail:constituents', function () {\n\t\tcurrentAsset.fetchConstituents();\n\t});\n\n\t/**\n\t * @event module:asset#asset:detail:clips\n\t * @fires module:asset#asset:detail:constituents\n\t */\n\tdispatcher.on('asset:detail:clips', function () {\n\t\tdispatcher.trigger('asset:detail:constituents');\n\t});\n\n\t/**\n\t * @event module:asset#asset:detail:constituents_keyframes\n\t * @fires module:asset#asset:detail:constituents\n\t */\n\tdispatcher.on('asset:detail:constituents_keyframes', function () {\n\t\tdispatcher.trigger('asset:detail:constituents');\n\t});\n\n\t/**\n\t * @event module:asset#asset:detail:constituents_layers\n\t * @fires module:asset#asset:detail:constituents\n\t */\n\tdispatcher.on('asset:detail:constituents_layers', function () {\n\t\tdispatcher.trigger('asset:detail:constituents');\n\t});\n\n\t/**\n\t * @event module:asset#asset:detail:constituents_pages\n\t * @fires module:asset#asset:detail:constituents\n\t */\n\tdispatcher.on('asset:detail:constituents_pages', function () {\n\t\tdispatcher.trigger('asset:detail:constituents');\n\t});\n\n\t/**\n\t * fetch the asset links (collection of assets linked to a given asset) when links tab is shown\n\t * @event module:asset#asset:detail:links\n\t */\n\tdispatcher.on('asset:detail:links', function () {\n\t\tcurrentAsset.fetchLinks();\n\t});\n\n\t/**\n\t * @event module:asset#asset:detail:links:toggleHidden\n\t * @param {Netx.Model.AssetModel} assetModel Asset model for the linked asset we are modifying\n\t * @param {boolean} hidden True if linked asset should be hidden from search, false otherwise\n\t * @fires module:asset#asset:detail:links:toggleHidden:success\n\t * @fires module:asset#asset:detail:links:toggleHidden:error\n\t */\n\tdispatcher.on(\n\t\t'asset:detail:links:toggleHidden',\n\t\tfunction (assetModel, hidden) {\n\t\t\t// hidden must be 0 or 1 not true/false\n\t\t\thidden = hidden ? 1 : 0;\n\t\t\t//save\n\t\t\tassetModel.save({hidden: hidden}).then(\n\t\t\t\tfunction () {\n\t\t\t\t\tdispatcher.trigger('asset:detail:links:toggleHidden:success');\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\tdispatcher.trigger('asset:detail:links:toggleHidden:error', err);\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t);\n\t/**\n\t * @event module:asset#asset:detail:links:toggleHidden:success\n\t * @fires module:app#notification\n\t */\n\tdispatcher.on('asset:detail:links:toggleHidden:success', function () {\n\t\tdispatcher.trigger('notification', {\n\t\t\tmodule: 'asset',\n\t\t\ttitle: i18n.t('i18n.relationshipUpdated'),\n\t\t\tdescription: i18n.t('i18n.relationshipVisibiltySet'),\n\t\t\ttype: 'success',\n\t\t});\n\t});\n\t/**\n\t * @event module:asset#asset:detail:links:toggleHidden:error\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on('asset:detail:links:toggleHidden:error', function (error) {\n\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t});\n\n\t/**\n\t * Fetching asset public links, when the public links tab is clicked, fetch the public links if they have not been already\n\t * @event module:asset#asset:detail:publicLinks\n\t */\n\tdispatcher.on('asset:detail:overview', function () {\n\t\tdispatcher.trigger('asset:detail:categories');\n\t\tdispatcher.trigger('asset:detail:links');\n\t\tdispatcher.trigger('asset:detail:views');\n\t\tdispatcher.trigger('asset:detail:versions');\n\t});\n\t//============================================\n\t//\t!Asset detail\n\t//============================================\n\n\t//============================================\n\t//\tAsset docview\n\t//============================================\n\n\t/**\n\t * Show the document viewer on an asset\n\t * @event module:asset#asset:docview\n\t * @param {number|string} id Asset id\n\t * @param {boolean} inMotion Whether or not we were already getting the cursor\n\t * @fires module:asset#asset:docview\n\t * @fires module:asset#asset:cursor:goto\n\t * @fires module:asset#asset:docview:open\n\t */\n\tdispatcher.on('asset:docview', function (id, inMotion) {\n\t\t// Do we have the asset loaded?\n\t\tif (currentAsset.id != id) {\n\t\t\tif (!inMotion) {\n\t\t\t\t// Load it\n\t\t\t\tdispatcher.trigger('asset:cursor:goto', id, false);\n\t\t\t}\n\t\t\t_.defer(function () {\n\t\t\t\t// Keep checking\n\t\t\t\tdispatcher.trigger('asset:docview', id, true);\n\t\t\t});\n\t\t\treturn;\n\t\t} else {\n\t\t\tdispatcher.trigger('asset:docview:open');\n\t\t}\n\t});\n\n\t/**\n\t * Show document viewer (assetDocumentView)\n\t * @event module:asset#asset:docview:open\n\t * @fires module:netx#netx:load:module:doc\n\t * @fires module:doc#doc:open\n\t */\n\tdispatcher.on('asset:docview:open', function () {\n\t\t// TODO: change this route to /#doc/ and let the document module take it from there\n\t\tdispatcher.trigger('netx:load:module:doc', {\n\t\t\terror: function (err) {\n\t\t\t\t// Deal with error\n\t\t\t\tconsole.warn(err);\n\t\t\t},\n\t\t\tinitialized: function () {\n\t\t\t\tdispatcher.trigger(PDF_VIEWER_EVENT.VIEW, {assetId: currentAsset.id});\n\t\t\t},\n\t\t});\n\t});\n\t/**\n\t * launch pdf in new browser tab\n\t * @event module:asset#asset:docview:browser\n\t * @param {object} model - currentAsset model\n\t * @param {object} view - the view\n\t * @param {object} event - the ui event\n\t */\n\tdispatcher.on('asset:docview:browser', function (model, view, event) {\n\t\tconst pdfUrl = `${URL.asset}/${model.id}/original/${model.get('file')}`;\n\t\twindow.open(pdfUrl, '_blank');\n\t});\n\t//============================================\n\t//\t!Asset docview\n\t//============================================\n\n\t//============================================\n\t//\tAsset expiration\n\t//============================================\n\t/**\n\t * @event module:asset#asset:expirations:routed\n\t * @param {number|string} page Page number\n\t * @fires module:asset#asset:expirations\n\t */\n\tdispatcher.on('asset:expirations:routed', function (page) {\n\t\tdispatcher.trigger('asset:expirations', page || 1);\n\t});\n\t/**\n\t * @event module:asset#asset:expirations\n\t * @param {number|string} page Page number\n\t * @fires module:asset#asset:expirations:gallery:show\n\t */\n\tdispatcher.on('asset:expirations', function (page) {\n\t\tdispatcher.trigger('asset:gallery:show', {\n\t\t\tconditions: convertToRules(createExpirationRule()),\n\t\t\tpage: parseInt(page) || 1,\n\t\t});\n\t});\n\n\t/**\n\t * @event module:asset#asset:manage:expire:show\n\t * @param {object} viewOptions View options\n\t * @fires moudle:app#app:showOptions\n\t */\n\tdispatcher.on('asset:manage:expire:show', function (viewOptions) {\n\t\tdispatcher.trigger('app:showOptions', viewOptions);\n\t});\n\t/**\n\t * @event module:asset#asset:manage:expire\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:asset#asset:manage:expire:show\n\t * @fires module:asset#asset:expire\n\t * @see module:asset#asset:expire\n\t * @see module:asset#asset:unexpire\n\t */\n\tdispatcher.on('asset:manage:expire', function (model) {\n\t\t// No sense in giving 1 option - just pass along\n\t\tif (model.get('expirationDate') === 0) {\n\t\t\tdispatcher.trigger('asset:expire', model);\n\t\t\treturn;\n\t\t}\n\t\t// Show\n\t\tdispatcher.trigger('asset:manage:expire:show', {\n\t\t\tmenu: Menus.assetExpirationActions,\n\t\t\tdata: {model: model},\n\t\t});\n\t});\n\t/**\n\t * @event module:asset#asset:expire:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'asset:expire:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-assetSetExpiration\" */ './views/assetSetExpirationView'\n\t\t\t).then((View) => {\n\t\t\t\tmodalViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['asset:expire:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:expire\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:asset#asset:expire:show\n\t * @see module:asset#asset:expire:confirmed\n\t */\n\tdispatcher.on('asset:expire', function (model) {\n\t\tdispatcher.trigger('asset:expire:show', {\n\t\t\tmodel: model,\n\t\t});\n\t});\n\t/**\n\t * @event module:asset#asset:expire:confirmed\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:asset#asset:expire:error\n\t * @fires module:asset#asset:expire:success\n\t * @see module:asset#asset:expire\n\t */\n\tdispatcher.on('asset:expire:confirmed', function (data, model) {\n\t\tif (!data || !data.expirationDate) {\n\t\t\treturn dispatcher.trigger(\n\t\t\t\t'asset:expire:error',\n\t\t\t\ti18n.t('i18n.noExpirationDateProvided'),\n\t\t\t\tmodel,\n\t\t\t);\n\t\t}\n\n\t\tmodel.expire(data.expirationDate).then(\n\t\t\tfunction () {\n\t\t\t\tdispatcher.trigger('asset:expire:success', model, data.expirationDate);\n\t\t\t\tdispatcher.trigger('asset:expiration:set:' + model.id);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('asset:expire:error', err, model);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:asset#asset:expire:error\n\t * @param {string} error Error string\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:app#app:alert\n\t * @see module:asset#asset:expire:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:expire:error',\n\t\tfunction (error, model) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:expire:success\n\t * @param {module:asset/models/asset} model Asset model\n\t * @param {string} date Date string\n\t * @fires module:notification#notification\n\t * @see module:asset#asset:expire:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:expire:success',\n\t\tfunction (model, date) {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\tids: [model.id],\n\t\t\t\ttitle: i18n.t('i18n.assetUpdated'),\n\t\t\t\ttype: 'success',\n\t\t\t\tdescription: i18n.t('i18n.expirationSetFor', {date: date}),\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:asset#asset:unexpire\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:app#app:confirm\n\t * @see module:asset#asset:unexpire:confirmed\n\t */\n\tdispatcher.on('asset:unexpire', function (model) {\n\t\tdispatcher.trigger('app:confirm', {\n\t\t\tmessage: i18n.t('i18n.unexpireConfirm'),\n\t\t\ttitle: i18n.t('i18n.unexpireAsset'),\n\t\t\targs: [model],\n\t\t\teventStr: 'asset:unexpire',\n\t\t});\n\t});\n\t/**\n\t * @event module:asset#asset:unexpire:confirmed\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:asset#asset:unexpire:error\n\t * @fires module:asset#asset:unexpire:success\n\t * @see module:asset#asset:unexpire\n\t */\n\tdispatcher.on('asset:unexpire:confirmed', function (model) {\n\t\tmodel.unexpire().then(\n\t\t\tfunction () {\n\t\t\t\tdispatcher.trigger('asset:unexpire:success', model);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('asset:unexpire:error', err, model);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:asset#asset:unexpire:error\n\t * @param {string} error Error string\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:app#app:alert\n\t * @see module:asset#asset:unexpire:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:unexpire:error',\n\t\tfunction (error, model) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:unexpire:success\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:notification#notification\n\t * @see module:asset#asset:unexpire:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:unexpire:success',\n\t\tfunction (model) {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\tids: [model.id],\n\t\t\t\ttitle: i18n.t('i18n.assetUpdated'),\n\t\t\t\ttype: 'success',\n\t\t\t\tdescription: i18n.t('i18n.unexpired'),\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t//============================================\n\t//\t!Asset expiration\n\t//============================================\n\n\t//============================================\n\t//\tAsset info\n\t//============================================\n\t/**\n\t * @event module:asset#asset:info:edit:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'asset:info:edit:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-assetFileInfo\" */ './views/assetFileInfoView'\n\t\t\t).then((View) => {\n\t\t\t\tmodalViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['asset:info:edit:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:info:edit\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:asset#asset:info:edit:show\n\t * @see module:asset#asset:info:edit:confirmed\n\t */\n\tdispatcher.on('asset:info:edit', function (model) {\n\t\tdispatcher.trigger('asset:info:edit:show', {\n\t\t\tmodel: model,\n\t\t});\n\t});\n\t/**\n\t * @event module:asset#asset:info:edit:confirmed\n\t * @param {object} values Asset info data\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:asset#asset:info:edit:error\n\t * @fires module:asset#asset:info:edit:success\n\t */\n\tdispatcher.on('asset:info:edit:confirmed', function (values, model) {\n\t\tvalues.readOnly = values.readOnly ? 1 : 0;\n\t\tvalues.visible = values.visible ? 1 : 0;\n\t\tmodel.set(values);\n\t\tmodel.save().then(\n\t\t\tfunction () {\n\t\t\t\t// Success\n\t\t\t\tdispatcher.trigger('asset:info:edit:success', model);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// Error\n\t\t\t\tdispatcher.trigger('asset:info:edit:error', err, model);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:asset#asset:info:edit:error\n\t * @param {string} error Error string\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:app#app:alert\n\t * @see module:asset#asset:info:edit:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:info:edit:error',\n\t\tfunction (error, model) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:info:edit:success\n\t * @param {module:asset/models/asset} model Asset model\n\t * @see module:asset#asset:info:edit:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:info:edit:success',\n\t\tfunction (model) {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\ttitle: i18n.t('i18n.assetUpdated'),\n\t\t\t\tdescription: i18n.t('i18n.assetUpdated'),\n\t\t\t\ttype: 'success',\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t//============================================\n\t//\t!Asset info\n\t//============================================\n\n\t//============================================\n\t//\tAsset lock/unlock (read-only)\n\t//============================================\n\t/**\n\t * @event module:asset#asset:lock\n\t * @param {Netx.Model.AssetModel} model Asset model\n\t * @fires module:asset#asset:lock:error\n\t * @fires module:asset#asset:lock:success\n\t */\n\tdispatcher.on('asset:lock', function (model) {\n\t\tmodel.lock().then(\n\t\t\tfunction () {\n\t\t\t\tdispatcher.trigger('asset:lock:success', model);\n\t\t\t\tdispatcher.trigger('asset:locked', model.id);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('asset:lock:error', err, model);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:asset#asset:lock:error\n\t * @param {string} error Error string\n\t * @param {Netx.Model.AssetModel} model Asset model\n\t * @fires module:app#app:alert\n\t * @see module:asset#asset:lock\n\t */\n\tdispatcher.on(\n\t\t'asset:lock:error',\n\t\tfunction (error, model) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:lock:success\n\t * @param {Netx.Model.AssetModel} model Asset model\n\t * @see module:asset#asset:lock\n\t */\n\tdispatcher.on(\n\t\t'asset:lock:success',\n\t\tfunction (model) {\n\t\t\t//\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:asset#asset:unlock\n\t * @param {Netx.Model.AssetModel} model Asset model\n\t * @fires module:asset#asset:unlock:error\n\t * @fires module:asset#asset:unlock:success\n\t */\n\tdispatcher.on('asset:unlock', function (model) {\n\t\tmodel.unlock().then(\n\t\t\tfunction () {\n\t\t\t\tdispatcher.trigger('asset:unlock:success', model);\n\t\t\t\tdispatcher.trigger('asset:unlocked', model.id);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('asset:unlock:error', err, model);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:asset#asset:unlock:error\n\t * @param {string} error Error string\n\t * @param {Netx.Model.AssetModel} model Asset model\n\t * @fires module:app#app:alert\n\t * @see module:asset#asset:unlock\n\t */\n\tdispatcher.on(\n\t\t'asset:unlock:error',\n\t\tfunction (error, model) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:unlock:success\n\t * @param {Netx.Model.AssetModel} model Asset model\n\t * @see module:asset#asset:unlock\n\t */\n\tdispatcher.on(\n\t\t'asset:unlock:success',\n\t\tfunction (model) {\n\t\t\t//\n\t\t},\n\t\twindow,\n\t);\n\t//============================================\n\t//\t!Asset lock/unlock (read-only)\n\t//============================================\n\n\t//============================================\n\t//\tAsset pager\n\t//============================================\n\n\t/**\n\t * Rewind to the first page of a selection\n\t * @event module:asset#asset:pager:firstPage\n\t * @param {Netx.Collections.Assets} collection Asset collection\n\t */\n\tdispatcher.on('asset:pager:firstPage', function (collection) {\n\t\tcollection.getFirstPage();\n\t});\n\n\t/**\n\t * Forward to the last page of a selection\n\t * @event module:asset#asset:pager:lastPage\n\t * @param {Netx.Collections.Assets} collection Asset collection\n\t */\n\tdispatcher.on('asset:pager:lastPage', function (collection) {\n\t\tcollection.getLastPage();\n\t});\n\n\t/**\n\t * Fetch the next page of a selection\n\t * @event module:asset#asset:pager:nextPage\n\t * @param {Netx.Collections.Assets} collection Asset collection\n\t */\n\tdispatcher.on('asset:pager:nextPage', function (collection) {\n\t\tcollection.getNextPage();\n\t});\n\n\t/**\n\t * Fetch the previous page of a selection\n\t * @event module:asset#asset:pager:prevPage\n\t * @param {Netx.Collections.Assets} collection Asset collection\n\t */\n\tdispatcher.on('asset:pager:prevPage', function (collection) {\n\t\tcollection.getPrevPage();\n\t});\n\n\t/**\n\t * Goto a specific page of a selection\n\t * @event module:asset#asset:pager:gotoPage\n\t * @param {Netx.Collections.Assets} collection Asset collection\n\t */\n\tdispatcher.on('asset:pager:gotoPage', function (collection, page) {\n\t\tcollection.gotoPage(page);\n\t});\n\t//============================================\n\t//\t!Asset pager\n\t//============================================\n\n\t//============================================\n\t//\tAsset reimport\n\t//============================================\n\tif (NS.options.REIMPORT_ENABLE) {\n\t\t/**\n\t\t * Action handler: trigger a file uploader for asset re-import\n\t\t * @event module:asset#asset:reimport\n\t\t * @param {module:asset/models/asset} assetModel Asset model\n\t\t * @fires module:upload#upload:asset:reimport\n\t\t */\n\t\tdispatcher.on('asset:reimport', function (assetModel) {\n\t\t\tdispatcher.trigger('upload:asset:reimport', assetModel);\n\t\t});\n\t}\n\t//============================================\n\t//\t!Asset reimport\n\t//============================================\n\n\t//============================================\n\t//\tAsset selection\n\t//============================================\n\t/**\n\t * @event module:asset#asset:selected\n\t * when an asset is selected for a portal logo, watermark or favicon\n\t * the model needs to be fetched\n\t */\n\tdispatcher.on('asset:selected', function (opts) {\n\t\tconst doFetch = opts.fetch;\n\t\tconst newId = opts.newId;\n\t\tlet assetModel = opts.assetModel;\n\n\t\tif (\n\t\t\tnewId &&\n\t\t\tassetModel &&\n\t\t\t(assetModel.id !== newId || assetModel.get('previewUrl').length === 0)\n\t\t) {\n\t\t\tassetModel.set({assetId: newId});\n\n\t\t\t// portal assets still need to be fetched here\n\t\t\tif (doFetch) {\n\t\t\t\tassetModel.fetch().then(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tdispatcher.trigger('asset:selected:success', assetModel, newId);\n\t\t\t\t\t},\n\t\t\t\t\t(error) => {\n\t\t\t\t\t\tdispatcher.trigger('asset:selected:error', error);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} else if (assetModel.id === newId) {\n\t\t\t\t// assetId was successfuly set\n\t\t\t\tdispatcher.trigger('asset:selected:success', assetModel, newId);\n\t\t\t} else {\n\t\t\t\tdispatcher.trigger('asset:selected:error', assetModel, newId);\n\t\t\t}\n\t\t}\n\t});\n\n\t/**\n\t * @event module:asset#asset:selected:error\n\t * @param {string} error Error\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'asset:selected:error',\n\t\tfunction (error) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#system:portal:new:success\n\t * @param {module:asset/models/assetSelect} model Asset select\n\t */\n\tdispatcher.on(\n\t\t'asset:selected:success',\n\t\tfunction (model, id) {\n\t\t\t// do nothing\n\t\t},\n\t\twindow,\n\t);\n\t//============================================\n\t//\t!Asset selection\n\t//============================================\n\n\t//============================================\n\t//\tAsset sharing\n\t//============================================\n\t/**\n\t * Route handler for asset-sharing route:\n\t * Convert an ID to an asset, and punt to the sharing module.\n\t * @event module:asset#asset:share:menu\n\t * @param {number} id Asset id\n\t */\n\tdispatcher.on('asset:share:menu', function (id) {\n\t\tid = parseInt(id);\n\t\tAssetModel.Load(id).then(\n\t\t\tfunction (asset) {\n\t\t\t\tdispatcher.trigger('share:menu', asset);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// does the asset exist, even?\n\t\t\t\tconsole.error(err);\n\t\t\t},\n\t\t);\n\t});\n\t//============================================\n\t//\t!Asset sharing\n\t//============================================\n\n\t//============================================\n\t//\tAsset submit\n\t//============================================\n\t/**\n\t * Asset form submission handler: pack the asset & sync with the server\n\t * @event module:asset#asset:submit:confirmed\n\t * @param {object} values Asset data\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:asset#asset:submit:save\n\t */\n\tdispatcher.on('asset:submit:confirmed', function (values, model) {\n\t\t// Server wants integers and not booleans\n\t\tvalues.readOnly = values.readOnly ? 1 : 0;\n\t\tvalues.visible = values.visible ? 1 : 0;\n\n\t\tmodel.set(values);\n\t\tdispatcher.trigger('asset:submit:save', model);\n\t});\n\t/**\n\t * @event module:asset#asset:submit:save\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:asset#asset:submit:save:error\n\t * @fires module:asset#asset:submit:save:success\n\t */\n\tdispatcher.on('asset:submit:save', function (model) {\n\t\t// now sync the asset\n\t\tif (NS.options.FETCH_ON_SAVE) {\n\t\t\tmodel.save(model.attributes, {silent: true}).then(\n\t\t\t\tfunction (response) {\n\t\t\t\t\t// Success\n\t\t\t\t\tmodel.fetch().then(\n\t\t\t\t\t\tfunction () {\n\t\t\t\t\t\t\t// Success\n\t\t\t\t\t\t\tdispatcher.trigger('asset:submit:success', model);\n\t\t\t\t\t\t\tdispatcher.trigger('asset:submit:save:success', model);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfunction (err) {\n\t\t\t\t\t\t\t// Error\n\t\t\t\t\t\t\tdispatcher.trigger('asset:submit:error', err);\n\t\t\t\t\t\t\tdispatcher.trigger('asset:submit:save:error', err);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\t// Error\n\t\t\t\t\tdispatcher.trigger('asset:submit:error', err);\n\t\t\t\t\tdispatcher.trigger('asset:submit:save:error', err);\n\t\t\t\t},\n\t\t\t);\n\t\t} else {\n\t\t\tmodel.save().then(\n\t\t\t\tfunction () {\n\t\t\t\t\t// Success\n\t\t\t\t\tdispatcher.trigger('asset:submit:success', model);\n\t\t\t\t\tdispatcher.trigger('asset:submit:save:success', model);\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\t// Error\n\t\t\t\t\tdispatcher.trigger('asset:submit:error', err);\n\t\t\t\t\tdispatcher.trigger('asset:submit:save:error', err);\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t});\n\t/**\n\t * @event module:asset#asset:submit:save:error\n\t * @param {string} error Error string\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on('asset:submit:save:error', function (error) {\n\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t});\n\t/**\n\t * @event module:asset#asset:submit:save:success\n\t * @param {module:asset/models/asset} model Asset model\n\t */\n\tdispatcher.on('asset:submit:save:success', function (model) {\n\t\t//\n\t});\n\t//============================================\n\t//\t!Asset submit\n\t//============================================\n\n\t//============================================\n\t//\tAsset tag panel\n\t//============================================\n\t/**\n\t * @event module:asset#asset:tagPanel:grid:show:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'asset:tagPanel:grid:show',\n\t\tfunction (viewOptions = {}) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-tagPanelGrid\" */ './views/tagPanelGridView'\n\t\t\t).then((View) => {\n\t\t\t\tfsViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['asset:tagPanel:grid:show'],\n\t\t\t\t\tviewOptions: {\n\t\t\t\t\t\tisRouted: true,\n\t\t\t\t\t\trouteMatch: /assets\\/grideditor/,\n\t\t\t\t\t\t...viewOptions,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\tdispatcher.on('asset:tagPanel:grid:routed', () => {\n\t\tdispatcher.trigger('asset:tagPanel:grid:show');\n\t});\n\n\t/**\n\t * @event module:asset#asset:tagPanel:grid:clone:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'asset:tagPanel:grid:clone:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-tagPanelGridClone\" */ './views/tagPanelGridCloneView'\n\t\t\t).then((View) => {\n\t\t\t\tfsViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['asset:tagPanel:grid:clone:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:tagPanel:grid:clone\n\t * @fires module:asset#asset:tagPanel:grid:clone:show\n\t */\n\tdispatcher.on('asset:tagPanel:grid:clone', function (data) {\n\t\tdispatcher.trigger('asset:tagPanel:grid:clone:show', data);\n\t});\n\n\t/**\n\t * @event module:asset#asset:tagPanel:grid:preview\n\t * @param {number} assetId Asset id\n\t */\n\tdispatcher.on('asset:tagPanel:grid:preview', function (assetId) {\n\t\tconst model = new AssetModel({assetId: assetId});\n\t\tmodel.fetch();\n\n\t\timport(\n\t\t\t/* webpackChunkName: \"view-assetPreview\" */ './components/AssetPreview/views/asset-preview-dialog'\n\t\t).then((View) => {\n\t\t\t// New asset preview view\n\t\t\tnew View.default({\n\t\t\t\tmodel,\n\t\t\t}).render();\n\t\t});\n\t});\n\t//============================================\n\t//\t!Asset tag panel\n\t//============================================\n\n\t//============================================\n\t//\tAsset tasks\n\t//============================================\n\t/**\n\t * @event module:asset#asset:tasks:poll\n\t * @param {Netx.Collections.AssetTasks} tasks Asset tasks collection\n\t * @fires module:asset#asset:tasks:poll\n\t */\n\tdispatcher.on('asset:tasks:poll', function (tasks) {\n\t\tNS.tasks.trackTasks();\n\t});\n\n\t/**\n\t * Task Polling:\n\t * Set up & begin polling of DAM tasks on assets\n\t * @event module:asset#asset:tasks:track\n\t * @param {array} assets An array of asset models or asset ids\n\t * @fires module:asset#asset:tasks:poll\n\t * @fires module:asset#asset:tasks:complete\n\t * @fires module:asset#asset:tasks:status\n\t */\n\tdispatcher.on('asset:tasks:track', function (assets) {\n\t\tconst tasks = NS.tasks;\n\t\tassets = _.tidyAssetList(assets);\n\t\t_.each(assets, function (assetId) {\n\t\t\t!tasks.get(assetId) && tasks.add({assetId: assetId});\n\t\t});\n\n\t\tdispatcher.trigger('asset:tasks:poll', tasks);\n\t});\n\t//============================================\n\t//\t!Asset tasks\n\t//============================================\n\n\t//============================================\n\t//\tAsset card thumbnail resizing\n\t//============================================\n\t/**\n\t * Save thumbSize to user prefs\n\t * @event asset:thumb:resize\n\t * @param {number} size Desired size\n\t * @fires module:asset#asset:thumb:set\n\t * @fires module:user#user:prefs:set\n\t */\n\tdispatcher.on('asset:thumb:resize', function (size) {\n\t\tdispatcher.trigger('user:prefs:set', 'assetList.thumbSize', parseInt(size));\n\t\t// TODO: I would argue that we should follow the pattern of error/success\n\t\t// `asset:thumb:resize:success`\n\t\t// Rather than this event that kind of sounds like an asset thumb should be being set when this is called (data wise)\n\t\t// and, at least I would expect, that sopme data should be being passed to it.\n\t\tdispatcher.trigger('asset:thumb:set');\n\t});\n\t//============================================\n\t//\tEnd asset card thumbnail resizing\n\t//============================================\n\n\t//============================================\n\t//\tAsset versions\n\t//============================================\n\n\t/**\n\t * Delete an asset version\n\t * @event module:asset#asset:versions:delete\n\t * @param {module:asset/models/assetVersion} model AssetVersion model\n\t * @fires module:app#app:confirm:delete\n\t * @see module:asset#asset:versions:delete:confirmed\n\t */\n\tdispatcher.on('asset:versions:delete', function (model) {\n\t\tdispatcher.trigger('app:confirm:delete', {\n\t\t\tmessage: i18n.t('i18n.deleteVersionConfirm'),\n\t\t\teventStr: 'asset:versions:delete',\n\t\t\targs: [model],\n\t\t});\n\t});\n\n\t/**\n\t * Confirmed deletion of asset version\n\t * @event module:asset#asset:versions:delete:confirmed\n\t * @param {module:asset/models/assetVersion} model AssetVersion model\n\t * @fires module:asset#asset:versions:delete:error\n\t * @fires module:asset#asset:versions:delete:success\n\t */\n\tdispatcher.on('asset:versions:delete:confirmed', function (model) {\n\t\tmodel.destroy({wait: true}).then(\n\t\t\tfunction () {\n\t\t\t\t// Success\n\t\t\t\tdispatcher.trigger('asset:versions:delete:success');\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// Error\n\t\t\t\tdispatcher.trigger('asset:versions:delete:error', err);\n\t\t\t},\n\t\t);\n\t});\n\n\t/**\n\t * @event module:asset#asset:version:error\n\t * @param {string} error Error string\n\t * @fires module:app#app:alert\n\t * @see module:asset#asset:versions:delete:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:versions:delete:error',\n\t\tfunction (error) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:asset#asset:version:success\n\t * @fires module:notification#notification\n\t * @see module:asset#asset:versions:delete:confirmed\n\t */\n\tdispatcher.on(\n\t\t'asset:versions:delete:success',\n\t\tfunction () {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\ttitle: i18n.t('i18n.assetUpdated'),\n\t\t\t\tdescription: i18n.t('i18n.versionDeleted'),\n\t\t\t\ttype: 'success',\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * Download an asset version\n\t * @event module:asset#asset:version:download\n\t * @param {module:asset/models/assetVersion} model AssetVersion model\n\t * @fires module:notification#notification\n\t */\n\tdispatcher.on('asset:version:download', function (versionModel) {\n\t\t// punt to the share controller\n\t\tdispatcher.trigger('share:assetSet:version', versionModel);\n\t});\n\n\t/**\n\t * @event module:asset#asset:versions:reactivate:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on('asset:versions:reactivate:show', function (viewOptions) {\n\t\tmodalViews.add({\n\t\t\tviewClass: AssetVersionReactivateView,\n\t\t\tshowActions: ['asset:versions:reactivate:show'],\n\t\t\tviewOptions: viewOptions,\n\t\t});\n\t});\n\t/**\n\t * Reactivate an asset version\n\t * @event module:asset#asset:versions:reactivate\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:asset#asset:versions:reactivate:show\n\t */\n\tdispatcher.on('asset:versions:reactivate', function (model) {\n\t\tdispatcher.trigger('asset:versions:reactivate:show', {\n\t\t\tmodel: model,\n\t\t});\n\t});\n\n\t/**\n\t * Confirmed reactivation of an asset version\n\t * @event module:asset#asset:versions:reactivate:confirmed\n\t * @param {object} values Reactivation data\n\t * @param {module:asset/models/assetVersion} newVersion AssetVersion model\n\t * @fires module:asset#asset:versions:reactivate:error\n\t * @fires module:asset#asset:versions:reactivate:success\n\t * @see module:asset#asset:versions:reactivate:succeeded\n\t */\n\tdispatcher.on(\n\t\t'asset:versions:reactivate:confirmed',\n\t\tfunction (values, newVersion) {\n\t\t\t//hijack this memoText with value (will get reset when fetch() happens post-reactivate)\n\t\t\tnewVersion.set('memoText', values.memoText);\n\t\t\tnewVersion.reactivateAssetVersion().then(\n\t\t\t\tfunction (uuid) {\n\t\t\t\t\t// The form submission was a success\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'asset:versions:reactivate:success',\n\t\t\t\t\t\tnewVersion,\n\t\t\t\t\t\tuuid,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'asset:versions:reactivate:error',\n\t\t\t\t\t\terr,\n\t\t\t\t\t\tnewVersion,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t);\n\n\t/**\n\t * @event module:asset#asset:versions:reactivate:success\n\t * @param {string} error Error string\n\t * @param {module:asset/models/assetVersion} newVersion Version activated\n\t * @fores module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'asset:versions:reactivate:error',\n\t\tfunction (error, newVersion) {\n\t\t\tdispatcher.trigger(\n\t\t\t\t'app:alert',\n\t\t\t\tcheckinErrors[error.code] ||\n\t\t\t\t\tcheckinErrors['default'] ||\n\t\t\t\t\terror.message ||\n\t\t\t\t\terror,\n\t\t\t\ti18n.t('i18n.error'),\n\t\t\t);\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * Request to activate version was accepted\n\t * @event module:asset#asset:versions:reactivate:success\n\t * @param {module:asset/models/assetVersion} newVersion Version activated\n\t * @param {string} uuid UUID\n\t * @fires module:notification#notification\n\t */\n\tdispatcher.on(\n\t\t'asset:versions:reactivate:success',\n\t\tfunction (newVersion, uuid) {\n\t\t\t// Notify\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\tids: newVersion.get('assetId'),\n\t\t\t\ttitle: i18n.t('i18n.assetUpdating'),\n\t\t\t\tdescription: i18n.t('i18n.reactivatingVersion'),\n\t\t\t\ttype: 'info',\n\t\t\t\tprogressTask: {\n\t\t\t\t\targs: [newVersion],\n\t\t\t\t\teventStr: 'asset:versions:reactivate',\n\t\t\t\t\tuuid: uuid,\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * Version has been reactivated\n\t * @event module:asset#asset:versions:reactivate:succeeded\n\t * @param {module:asset/models/assetVersion} newVersion Version activated\n\t */\n\tdispatcher.on('asset:versions:reactivate:succeeded', function (newVersion) {\n\t\t// Refetch current asset\n\t\tcurrentAsset.fetch().then(function () {\n\t\t\tthis.fetchVersions();\n\t\t});\n\t});\n\n\t//============================================\n\t//\t!Asset versions\n\t//============================================\n\n\t//============================================\n\t//\tAsset views\n\t//============================================\n\n\t/**\n\t * View add\n\t * @event module:asset#asset:views:add\n\t * @fires module:upload#upload:asset:view\n\t */\n\tdispatcher.on('asset:views:add', function () {\n\t\tdispatcher.trigger(\n\t\t\t'upload:asset:view',\n\t\t\tnew AssetViewModel({\n\t\t\t\timageId: currentAsset.id,\n\t\t\t}),\n\t\t);\n\t});\n\t/**\n\t * Delete asset view\n\t * @event module:asset#asset:views:delete\n\t * @param {module:asset/models/assetView} assetView AssetView model\n\t * @fires module:asset#app:confirm:delete\n\t * @see module:asset#asset:views:delete:confirmed\n\t */\n\tdispatcher.on('asset:views:delete', function (assetView) {\n\t\tdispatcher.trigger('app:confirm:delete', {\n\t\t\tmessage: i18n.t('i18n.deleteViewConfirm', {\n\t\t\t\tassetId: assetView.get('imageId'),\n\t\t\t}),\n\t\t\teventStr: 'asset:views:delete',\n\t\t\targs: [assetView],\n\t\t});\n\t});\n\t/**\n\t * View deletion confirmation\n\t * @event module:asset#asset:views:delete:confirmed\n\t * @param {module:asset/models/assetView} assetView AssetView model\n\t * @fires module:asset#asset:views:delete:error\n\t * @fires module:asset#asset:views:delete:success\n\t */\n\tdispatcher.on('asset:views:delete:confirmed', function (assetView) {\n\t\tassetView.destroy({wait: true}).then(\n\t\t\tfunction () {\n\t\t\t\t// Success\n\t\t\t\t// refresh the views list\n\t\t\t\tNS.views.assetId = currentAsset.id;\n\t\t\t\tNS.views.fetch();\n\t\t\t\tdispatcher.trigger('asset:views:delete:success', currentAsset.id);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// Error\n\t\t\t\tdispatcher.trigger('asset:views:delete:error', err);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:asset#asset:views:delete:error\n\t * @param {string} error Error string\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'asset:views:delete:error',\n\t\tfunction (error) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:asset#asset:views:delete:success\n\t * @param {number} assetId Asset id\n\t * @fires module:notification#notification\n\t */\n\tdispatcher.on(\n\t\t'asset:views:delete:success',\n\t\tfunction (assetId) {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\tids: [assetId],\n\t\t\t\ttitle: i18n.t('i18n.assetUpdated'),\n\t\t\t\tdescription: i18n.t('i18n.assetViewsSuccessfullyDeleted'),\n\t\t\t\ttype: 'success',\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * Download the currently selected view\n\t * @event module:asset#asset:views:download\n\t */\n\tdispatcher.on('asset:views:download', function (assetView) {\n\t\tdispatcher.trigger(\n\t\t\tASSET_SET_EVENT.BUILD_DOWNLOAD,\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\ttype: AssetSetType.ASSET,\n\t\t\t\t\tids: [assetView.get('imageId')],\n\t\t\t\t\trecursive: false,\n\t\t\t\t},\n\t\t\t],\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\t...downloadOptionDefaults,\n\t\t\t\t\ttype: DownloadOptionType.ASSET_VIEW,\n\t\t\t\t\tid: assetView.get('viewId'),\n\t\t\t\t\tname: assetView.get('name'),\n\t\t\t\t},\n\t\t\t],\n\t\t);\n\n\t\t// const link = `${URL.assetEndpoint}/${assetView.get('imageId')}/view/${\n\t\t// \tassetView.id\n\t\t// }/view_${assetView.id}.${assetView.get('fileTypeLabel')}?attachment=true`;\n\t\t// openDownloadLink(link);\n\t});\n\t/**\n\t * View edit\n\t * @event module:asset#asset:views:edit\n\t * @param {module:asset/models/assetView} assetView AssetView model\n\t * @fires module:upload#upload:asset:view\n\t */\n\tdispatcher.on('asset:views:edit', function (assetView) {\n\t\tdispatcher.trigger('upload:asset:view', assetView);\n\t});\n\t//============================================\n\t//\t!Asset views\n\t//============================================\n\n\t//============================================\n\t//\tAsset zoom\n\t//============================================\n\t/**\n\t * @event module:asset#asset:zoom:routed\n\t * @param {number|string} id Asset id\n\t * @fires module:asset#asset:zoom\n\t */\n\tdispatcher.on('asset:zoom:routed', function (id) {\n\t\tif (id == currentAsset.id) {\n\t\t\tdispatcher.trigger('asset:zoom', currentAsset);\n\t\t} else {\n\t\t\tAssetModel.Load(id).then(\n\t\t\t\tfunction (asset) {\n\t\t\t\t\t// Success\n\t\t\t\t\tdispatcher.trigger('asset:zoom', asset);\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\t// Error\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t});\n\n\t/**\n\t * Handle a request to display a zoom interface for an asset.\n\t * There are two possible engines for this, so in this handler we\n\t * pick an engine and hand off to one of two engine-specific handlers.\n\t *\n\t * @event module:asset#asset:zoom\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:app#asset:zoom:zoomify\n\t * @fires module:app#asset:zoom:mediarich\n\t */\n\tdispatcher.on('asset:zoom', function (model) {\n\t\t// Which zoom engine to use: MediaRich (aka Equilibrium), or Zoomify?\n\t\tif (\n\t\t\tgetPreference('equilibrium.enabled', true) &&\n\t\t\tgetPreference('equilibrium.zoomEnabled', true)\n\t\t) {\n\t\t\tdispatcher.trigger('asset:zoom:mediarich', model);\n\t\t} else {\n\t\t\tdispatcher.trigger('asset:zoom:zoomify', model);\n\t\t}\n\t});\n\n\t/**\n\t * Handle a zoom request via the Mediarich (aka Equilibrium) engine,\n\t * a Flash app in a seperate window.\n\t * @event module:asset#asset:zoom:mediarich\n\t * @param {module:asset/models/asset} model Asset model\n\t */\n\tdispatcher.on(\n\t\t'asset:zoom:mediarich',\n\t\tfunction (model) {\n\t\t\t// Could take a moment ...\n\t\t\tdispatcher.trigger('app:progress', {\n\t\t\t\tmodel: model,\n\t\t\t\teventStr: 'asset:zoom:mediarich:cache',\n\t\t\t\theaderTitle: i18n.t('i18n.zooming', {defaultValue: 'Zooming ...'}),\n\t\t\t\tmessage: '',\n\t\t\t});\n\n\t\t\tmodel.cacheInMediaRich().then(\n\t\t\t\tfunction () {\n\t\t\t\t\t// Close progress window\n\t\t\t\t\tdispatcher.trigger('asset:zoom:mediarich:cache:success');\n\t\t\t\t\t// Open a view in the fullscreeny appView instance Netx.App.fsViews\n\t\t\t\t\tdispatcher.trigger('asset:zoom:mediarich:show', {\n\t\t\t\t\t\tmodel: model,\n\t\t\t\t\t\trouteMatch: new RegExp(/^asset\\/(\\d+)\\/zoom/),\n\t\t\t\t\t\tisRouted: true,\n\t\t\t\t\t});\n\n\t\t\t\t\treturn;\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\tdispatcher.trigger('asset:zoom:medarich:cache:error', err);\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:asset#asset:zoom:mediarich:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'asset:zoom:mediarich:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-assetMRZoom\" */ './views/assetMRZoomView'\n\t\t\t).then((View) => {\n\t\t\t\tfsViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['asset:zoom:mediarich:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:asset#asset:zoom:zoomify:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'asset:zoom:zoomify:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-assetZoomify\" */ './views/assetZoomifyView'\n\t\t\t).then((View) => {\n\t\t\t\tfsViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['asset:zoom:zoomify:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * Trigger tiling of an asset, and launch the tiled image viewer\n\t * @event module:asset#asset:zoom:zoomify\n\t * @param {module:asset/models/asset} model Asset model\n\t * @fires module:app#app:pollBatchJob\n\t * @fires module:asset#asset:zoom:zoomify:show\n\t * @fires module:asset#asset:zoom:zoomify:error\n\t * @fires module:asset#asset:zoom:zoomify:success\n\t */\n\tdispatcher.on(\n\t\t'asset:zoom:zoomify',\n\t\tfunction (model) {\n\t\t\t// Show\n\t\t\tdispatcher.trigger('asset:zoom:zoomify:show', {\n\t\t\t\tmodel: model,\n\t\t\t\trouteMatch: new RegExp(/^asset\\/(\\d+)\\/zoom/),\n\t\t\t\tisRouted: true,\n\t\t\t});\n\n\t\t\t// ask the server to tile the image\n\t\t\tmodel.makeZoomTiles().then(\n\t\t\t\tfunction (m) {\n\t\t\t\t\t// Success\n\t\t\t\t\tif (m.exists) {\n\t\t\t\t\t\t// tiles are ready\n\t\t\t\t\t\t// HACK: if we're a modal, we might still be showing (fading in with CSS animations.)\n\t\t\t\t\t\t// Or we might not. There's an event 'shown' we can listen for on the view, but no\n\t\t\t\t\t\t// really clean way to know if it's showing yet or not, or already done showing. So i'm inserting a delay here.\n\t\t\t\t\t\t_.delay(function () {\n\t\t\t\t\t\t\tdispatcher.trigger('asset:zoom:zoomify:success', m);\n\t\t\t\t\t\t}, 300); // this 300ms value copied from bootstrap.modal code, where it's hardcoded.\n\t\t\t\t\t} else if (m.progressUUID) {\n\t\t\t\t\t\t// waiting for tiles to finish\n\t\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t\t'app:pollBatchJob',\n\t\t\t\t\t\t\tm.progressUUID,\n\t\t\t\t\t\t\t'asset:zoom:zoomify',\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t\t'asset:zoom:zoomify:error',\n\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t'Zoom unavailable and an invalid progress uuid was returned',\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\tdispatcher.trigger('asset:zoom:zoomify:error', err, model);\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:asset#asset:zoom:zoomify:error\n\t * @param {string} error Error string\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'asset:zoom:zoomify:error',\n\t\tfunction (error) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:asset#asset:zoom:zoomify:success\n\t * @param {module:asset/models/asset} model Asset model\n\t */\n\tdispatcher.on(\n\t\t'asset:zoom:zoomify:success',\n\t\tfunction (model) {\n\t\t\t//\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * UI hander: where to go when the zoom window closes?\n\t * @param module:asset/models/asset model Asset model\n\t */\n\tdispatcher.on(\n\t\t'asset:zoom:zoomify:closed',\n\t\tfunction () {\n\t\t\t// // How did we get here?\n\t\t\t// // If we loaded directly on a zoom,\n\t\t\t// if (Netx.router.history.length == 1 && Netx.router.history[0].match(/zoom$/)) {\n\t\t\t// \t// Can't close the window for security reasons:\n\t\t\t// \t// http://stackoverflow.com/questions/19761241/window-close-and-self-close-do-not-close-the-window-in-chrome\n\t\t\t// \t//\n\t\t\t// \t// Instead, just go home:\n\t\t\t// \tNetx.router.navigate(Netx.custom.HOME_ROUTE || '#', {triger: true}); // for 8.4\n\t\t\t// \t// Netx.router.home(); // easier in 8.5 .\n\t\t\t// \t// Actually, Netx.router.back() would call .home() in this case, so this handler could be a one-liner. TODO: test that.\n\t\t\t// } else {\n\t\t\t// \tNetx.router.back();\n\t\t\t// }\n\t\t},\n\t\twindow,\n\t);\n\n\t//============================================\n\t//\t!Asset zoom\n\t//============================================\n\n\t//=================================\n\t//\t!EVENT LISTENERS\n\t//=================================\n\n\t// END OF INIT:\n\t/////////////////////////\n\t// After the module has run through its init routine,\n\t// stop listening for subsequent init events which would\n\t// cause duplicate listeners and instances to be created.\n\t$.when.apply(this, NS.bootstrap).then(function () {\n\t\tdelete NS.bootstrap;\n\t\tdispatcher.trigger('asset:extend', NS);\n\t\tdispatcher.trigger('asset:initialized', NS);\n\t});\n});\n\nexport default {\n\tinitialize(opts) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tdispatcher.once('asset:initialized', () => {\n\t\t\t\tresolve(routes);\n\t\t\t});\n\t\t\tdispatcher.trigger('asset:init');\n\t\t});\n\t},\n};\n","export {default} from 'netxcore@netx/core-asset/controller';\nimport {currentAsset} from '@netx/core-asset/collections/singleton/currentAsset';\nimport {assetCursor} from '@netx/core-asset/collections/singleton/assetCursor';\n\n// import AssetDetailView from './views/assetDetailView';\nimport AssetQuickzoomView from './views/assetQuickzoomView';\nimport AssetZoomifyView from './views/assetZoomifyView';\nimport AssetModel from './models/asset';\nimport {fsViews} from '@netx/core-app/singletons/fsViews';\n\n//=================================\n//\tProperties\n//=================================\nimport {things as assetThings} from './properties/asset';\nassetThings.links.label = i18n.t('i18n.relationships');\n//=================================\n//\t!Properties\n//=================================\n\n/**\n * Asset Module Extensions\n * @event module:asset#asset:extend\n */\ndispatcher.once('asset:extend', function (NS) {\n\t// Use faceted search\n\t// Filter mode is driven by the property `search.facet.filterMode` - default is quite literally \"DEFAULT\"\n\t// See Netx.Collections.SearchFacets\n\t// NS.expiring.useFacetedSearch(true);\n\n\t//=================================\n\t//\tEVENT LISTENERS\n\t//=================================\n\n\tdispatcher.off('asset:routed');\n\tdispatcher.on('asset:routed', function (id, tab) {\n\t\t// fetch, even if we were just here, DAM-19393\n\t\t// like theme does\n\t\tdispatcher.trigger('asset:cursor:goto', id);\n\n\t\t// THEM-1460\n\t\t$('#wrapper').scrollTop(0);\n\n\t\t// Show\n\t\tdispatcher.trigger('asset:detail:show', {\n\t\t\tmodel: currentAsset,\n\t\t\tcollection: assetCursor,\n\t\t\tdetailTab: tab,\n\t\t\trouteMatch: new RegExp(/^asset\\/(\\d+)/),\n\t\t});\n\t});\n\n\t/**\n\t * @event module:asset#asset:quickzoom:routed\n\t * @param {number|string} id Asset id\n\t * @fires module:asset#asset:zoom\n\t */\n\tdispatcher.on('asset:quickzoom:routed', function (id) {\n\t\tif (id == currentAsset.id) {\n\t\t\tdispatcher.trigger('asset:quickzoom', currentAsset);\n\t\t} else {\n\t\t\tAssetModel.Load(id).then(\n\t\t\t\tfunction (asset) {\n\t\t\t\t\t// Success\n\t\t\t\t\tdispatcher.trigger('asset:quickzoom', asset);\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\t// Error\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t});\n\n\t/**\n\t * @event module:asset#asset:quickzoom\n\t * show em 2000px worth of asset\n\t */\n\tdispatcher.on('asset:quickzoom', function (model) {\n\t\t$('body').addClass('asset-zoom-quickzoom-page');\n\t\t// Show\n\t\tdispatcher.trigger('asset:quickzoom:show', {\n\t\t\tmodel: model,\n\t\t\tid: 'asset-quickzoom',\n\t\t\t// routeMatch: new RegExp(/^asset\\/(\\d+)\\/quickzoom/),\n\t\t\t// isRouted: true,\n\t\t});\n\t});\n\tdispatcher.on(\n\t\t'asset:quickzoom:show',\n\t\tfunction (viewOptions) {\n\t\t\tfsViews.add({\n\t\t\t\tviewClass: AssetQuickzoomView,\n\t\t\t\tshowActions: ['asset:quickzoom:show'],\n\t\t\t\tviewOptions: viewOptions,\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\tdispatcher.on('asset:quickzoom:close', function () {\n\t\t$('body').removeClass('asset-zoom-quickzoom-page');\n\t});\n\t/**\n\t * @event module:asset#asset:zoom:zoomify:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.off('asset:zoom:zoomify:show');\n\tdispatcher.on(\n\t\t'asset:zoom:zoomify:show',\n\t\tfunction (viewOptions) {\n\t\t\t// hack - AssetZoomifyView closes itself if quickzoom is in the room\n\t\t\tfsViews.pop();\n\n\t\t\tfsViews.add({\n\t\t\t\tviewClass: AssetZoomifyView,\n\t\t\t\tshowActions: ['asset:zoom:zoomify:show'],\n\t\t\t\tviewOptions: viewOptions,\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t//=================================\n\t//\t!EVENT LISTENERS\n\t//=================================\n}); // asset:extend\n","/**\n * The category module handles retrieving and displaying\n * category information, category tree, and category management\n *\n * @module category\n */\n\nimport {appRouter} from '@netx/core-next/instance/router';\n\n// Models\nimport CoreModel from '@netx/core/lib/netx-model';\nimport CategoryModel from './models/category';\n\n// Collections\nimport AssetSearchRulesCollection from '@netx/core-search/collections/searchRules_assets';\nimport CategoriesCollection from './collections/categories';\nimport CategoryAssetsCollection from './collections/categoryAssets';\nimport CategoryTreeNodesCollection from './collections/categoryTreeNodes';\nimport RecentCategoriesCollection from './collections/recentCategories';\n\nimport {createCategoryIdRule} from '@netx/core-search-rules/rules/categoryId/categoryIdRule';\nimport {convertToRules} from '@netx/core-search-rules/utils/convertToRules';\n\nimport customConfig from './config';\nimport routes from './routes';\n\nimport _CategoryController from '@netx/core-category-next/controller';\nimport {canAddCategory} from '@netx/core-category-next/enablers';\nimport {modalViews} from '@netx/core-app/singletons/modalViews';\nimport {\n\tcurrentUserMeetsUserLevel,\n\tUserLevel,\n} from '@netx/core-user-next/utils/currentUserMeetsUserLevel';\n\n// MODULE INITIALIZATION\n// =====================\n// All collection and model instances and event listeners\n// are created when the module is initialized.\n\n/**\n * Init handler\n * @event module:cart#category:init\n * @param {object} options Normally undefined, but a manual init trigger may override properties of Netx.custom.category here.\n */\ndispatcher.once('category:init', function (options) {\n\t// Each module should create it's own object that is used to store\n\t// custom options, collection/model/view instances and module properties\n\tconst NS = (Netx.category = {});\n\n\t//=================================\n\t//\tCREATE MODELS AND COLLECTIONS\n\t//=================================\n\t// Add module options to module object and make the options variable available\n\tNS.options = options = _.extend({}, customConfig, options);\n\n\tNS.current = new CategoryModel({}, {collection: NS.tree});\n\tNS.list = new CategoriesCollection([], {\n\t\tcache: NS.tree,\n\t\tcurrentCat: NS.current,\n\t});\n\tNS.recent = new RecentCategoriesCollection();\n\tNS.assets = new CategoryAssetsCollection([], {\n\t\tcategoryId: options.categoryid,\n\t\tdirectory: options.repositorydirectory,\n\t});\n\n\tNS.bootstrap = [_CategoryController.initialize()];\n\n\t//=================================\n\t//\t!CREATE MODELS AND COLLECTIONS\n\t//=================================\n\n\t// !! IMPORTANT !!\n\t//\n\t// DEVNOTE: @spence - Check if there is a user available to the module\n\t// before bootstrapping basic category functionality.\n\t// There are some unique situations where we need category module functionality\n\t// via tokenKey access but without sessionKey access.\n\t//\n\n\t// This check was initially added to support asset request functionality.\n\tif (currentUserMeetsUserLevel(UserLevel.BROWSER)) {\n\t\t// Create category tree collection\n\t\tif (options.tree) {\n\t\t\tNS.tree = new CategoryTreeNodesCollection([], {\n\t\t\t\tcategoryid: NS.options.categoryid,\n\t\t\t});\n\t\t\tNS.tree.fetch();\n\t\t}\n\t}\n\n\t// This event is fired by the category model whenever its id is changed\n\tNS.current.on('change:categoryid', function (category, categoryid, opts) {\n\t\tNS.current.fetch().then(\n\t\t\tfunction (cat) {\n\t\t\t\t// If not top level, trigger the category to open\n\t\t\t\tif (NS.current.id !== NS.options.categoryid) {\n\t\t\t\t\tdispatcher.trigger('category:tree:open', NS.current.id, NS.tree);\n\t\t\t\t}\n\t\t\t\tdispatcher.trigger('category:current:update:success');\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('category:current:update:error', err);\n\t\t\t},\n\t\t);\n\t});\n\n\t// NOTE: UI-specific handlers get the Window object as their context, so they can be easily replaced by custom controllers\n\t// without removing other important listeners.\n\n\t//=================================\n\t// COLLECTION AND MODEL EVENT LISTENERS\n\t//=================================\n\n\t// Sometimes, due to timing we do not get our proper active classes on non top level cats\n\t// I would love to put this in the collection but now we have multiple instances of CategoryTreeNodes with\n\t// varying needs (this is the only tree that cares about route currently) -jse-\n\tif (NS.tree && options.tree) {\n\t\tNS.tree.on(\n\t\t\t'sync',\n\t\t\t_.debounce(function () {\n\t\t\t\t// Vars\n\t\t\t\tvar id = NS.current.id;\n\t\t\t\t// `Top level` is of no concern\n\t\t\t\tif (id > 1) {\n\t\t\t\t\tconst cat = this.get(id);\n\t\t\t\t\tif (cat) {\n\t\t\t\t\t\tcat.trigger('activate');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, 150),\n\t\t);\n\t}\n\n\t// Category current\n\n\t/**\n\t * This event takes a data object as a parameter that\n\t * corresponds to one or more of the catetgory model\n\t * attributes and is used to update the current category state\n\t * @event module:category#category:current:update\n\t * @param {object} data Object corresponding to one or more category attributes\n\t * @see module:category#category:current:update:error\n\t * @see module:category#category:current:update:success\n\t */\n\tdispatcher.on('category:current:update', function (data, page) {\n\t\tif (data.categoryid === NS.current.id) {\n\t\t\tNS.current.trigger('sync', NS.current);\n\t\t} else {\n\t\t\t// Keep in mind almost everytime we are just updating the categoryid\n\t\t\t// See the listener above on `change:categoryid`\n\t\t\tNS.current.set(data);\n\t\t}\n\t});\n\t/**\n\t * @event module:category#category:current:update:error\n\t * @param {string} error Error string\n\t * @fires module:app#app:alert\n\t * @fires module:category#category:route:top\n\t */\n\tdispatcher.on(\n\t\t'category:current:update:error',\n\t\tfunction (error) {\n\t\t\t// Fire off error alert\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t\t// Route to top level\n\t\t\tdispatcher.trigger('category:route:top');\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:category#category:current:update:success\n\t */\n\tdispatcher.on(\n\t\t'category:current:update:success',\n\t\tfunction () {\n\t\t\t//\n\t\t},\n\t\twindow,\n\t);\n\n\t//=================================\n\t// !COLLECTION AND MODEL EVENT LISTENERS\n\t//=================================\n\n\t//=================================\n\t//\tEVENT LISTENERS\n\t//=================================\n\t// Category events that are passed through the\n\t// dispatcher and available to all modules.\n\n\t//============================================\n\t//\tCategory assets\n\t//============================================\n\t/**\n\t * If an asset was deleted, remove it from our collection if it's there.\n\t * @event module:category#asset:deleted\n\t * @param {number} assetid Asset id\n\t */\n\tdispatcher.on('asset:deleted', function (assetId) {\n\t\tassetId = parseInt(assetId);\n\t\tvar out = NS.assets.find(function (a) {\n\t\t\treturn a.get('assetId') === assetId;\n\t\t});\n\t\tif (out) {\n\t\t\tNS.assets.remove(out);\n\t\t}\n\t});\n\n\t/**\n\t * Add all assets in a category to the current cart (this does not clear current cart's assets)\n\t * @event module:category#category:add:to:current:cart\n\t * @param {number} id Category id\n\t * @fires module:cart#cart:addAll\n\t * @see module:cart#cart:addAll:error\n\t * @see module:cart#cart:addAll:success\n\t */\n\tdispatcher.on('category:add:to:current:cart', function (id) {\n\t\tdispatcher.trigger(\n\t\t\t'cart:addAll',\n\t\t\tnew AssetSearchRulesCollection([\n\t\t\t\t{\n\t\t\t\t\telementType: 23, // search type \"category id\"\n\t\t\t\t\telementValue1: parseInt(id), // id of category\n\t\t\t\t\telementSubType1: 2, // stop recurse\n\t\t\t\t},\n\t\t\t]),\n\t\t);\n\t});\n\t//============================================\n\t//\t!Category assets\n\t//============================================\n\n\t//============================================\n\t//\tCategory route\n\t//============================================\n\t/**\n\t * Routes to a categories parent\n\t * @event module:category#category:gotoParent\n\t */\n\tdispatcher.on('category:gotoParent', function () {\n\t\tconst parentId = NS.current.get('parentid');\n\t\tappRouter.navigate(parentId > 1 ? 'category/' + parentId : '/', {\n\t\t\ttrigger: true,\n\t\t});\n\t});\n\t/**\n\t * Handler for the default category route: reroute to the default category id route\n\t * @event module:category#category:route:top\n\t */\n\tdispatcher.on('category:route:top', function () {\n\t\t// set the route:\n\t\tappRouter.navigate('category/' + NS.options.categoryid, {trigger: true});\n\t});\n\t/**\n\t * UI Handler\n\t * Top level tree navigation occured\n\t * @event module:category#category:top:routed\n\t */\n\tdispatcher.on(\n\t\t'category:top:routed',\n\t\tfunction () {\n\t\t\t//\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @route\n\t * @fires module:category#category:routed\n\t */\n\tappRouter.on(\n\t\t'route:category',\n\t\t_.debounce((id, page) => {\n\t\t\t// DAM-16156 - debouncing category routing\n\t\t\t// to prevent our crazy code from looping\n\t\t\tdispatcher.trigger('category:routed', id, page);\n\t\t}, 1),\n\t);\n\t/**\n\t * Main route handler for categories\n\t * @event module:category#category:routed\n\t * @param {number} id Category id\n\t * @param {number} page Page number\n\t * @fires module:category#category:current:update\n\t * @fires module:category#category:tree:open\n\t * @fires module:category#category:gallery:show\n\t * @fires module:category#category:top:routed\n\t */\n\tdispatcher.on('category:routed', function (id, page) {\n\t\tconst opts = NS.options;\n\t\tif (id === opts.categoryid) {\n\t\t\tdispatcher.trigger('category:current:update', {\n\t\t\t\tcategoryid: opts.categoryid,\n\t\t\t\trepositorydirectory: opts.repositorydirectory,\n\t\t\t});\n\t\t\tdispatcher.trigger('category:top:routed');\n\t\t}\n\t\t// Anything but the top level routed\n\t\telse {\n\t\t\tdispatcher.trigger('category:current:update', {categoryid: id}, page);\n\t\t}\n\n\t\t// TODO: move this original code to portal\n\t\tdispatcher.trigger('asset:gallery:show', {\n\t\t\tconditions: convertToRules(createCategoryIdRule({id: parseInt(id)})),\n\t\t\tpage: parseInt(page) || 1,\n\t\t});\n\t});\n\t//============================================\n\t//\t!Category route\n\t//============================================\n\n\t//============================================\n\t//\tCategory add/edit\n\t//============================================\n\t/**\n\t * @event module:category#category:edit:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'category:edit:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-categoryEdit\" */ './views/categoryEditView'\n\t\t\t).then((View) => {\n\t\t\t\tmodalViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['category:edit:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * Launches the category editor\n\t * @event module:category#category:add\n\t * @param {Netx.Models.Category} parentCat Parent category model\n\t * @fires module:category#category:edit\n\t */\n\tdispatcher.on('category:add', function (parentCat, e) {\n\t\t// Event silencing\n\t\tif (e && e.preventDefault) {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t}\n\n\t\t// if no model, add category from main folder tree footer\n\t\tif (!parentCat || !parentCat.constructor?.__isModel__) {\n\t\t\t// if we are routed to a category, assume that as the parent of the new category\n\t\t\tif (window.location.hash.includes('category/' + NS.current.id)) {\n\t\t\t\tparentCat = NS.current;\n\t\t\t}\n\t\t}\n\n\t\tconst catInfo =\n\t\t\tparentCat && canAddCategory(parentCat.attributes)\n\t\t\t\t? {\n\t\t\t\t\t\tparentid: parentCat.id,\n\t\t\t\t\t\tparentCategoryName: parentCat.get('name'),\n\t\t\t\t }\n\t\t\t\t: {\n\t\t\t\t\t\tparentid: 1,\n\t\t\t\t\t\tparentCategoryName: i18n.t('i18n.topLevelCategoryLabel'),\n\t\t\t\t };\n\n\t\tvar model = new CategoryModel(catInfo, NS.current.tree);\n\n\t\tdispatcher.trigger('category:edit', model);\n\t});\n\t/**\n\t * Prepares and calls the category editor if the event parameter is passed\n\t * cageory edit view is displayed if there is no event param the\n\t * category view displays a blank form for adding a new category.\n\t * @event module:category#category:edit\n\t * @param {Netx.Models.Category} model Category model\n\t * @param {event} e jQuery event\n\t * @fires module:category#category:edit:error\n\t * @fires module:category#category:edit:show\n\t * @see module:category#category:edit:confirmed\n\t */\n\tdispatcher.on('category:edit', function (model, e) {\n\t\t// Event silencing\n\t\t// TODO: this is only really necessary because of our tableViews row clicking\n\t\tif (e && e.preventDefault) {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t}\n\n\t\tvar promises = [];\n\t\tif (!model.isNew()) {\n\t\t\tpromises.push(model.getCategoryDetailsData());\n\n\t\t\t// DAM-16146 - If admin user, get selected attribute profile for folder\n\t\t\tif (currentUserMeetsUserLevel(UserLevel.ADMIN)) {\n\t\t\t\tpromises.push(model.getAttributeProfile());\n\t\t\t}\n\t\t}\n\n\t\t$.when.apply($, promises).then(\n\t\t\tfunction () {\n\t\t\t\tdispatcher.trigger('category:edit:show', {\n\t\t\t\t\tmodel: model,\n\t\t\t\t});\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('category:edit:error', err);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:category#category:edit:confirmed\n\t * @param {object} data Category data\n\t * @param {Netx.Models.Category} model Category model\n\t * @fires module:category#category:add:error\n\t * @fires module:category#category:add:success\n\t * @fires module:category#category:children:changed\n\t * @fires module:category#category:children:changed:(parentid)\n\t */\n\tdispatcher.on('category:edit:confirmed', function (data, model) {\n\t\tvar isNew = model.isNew(),\n\t\t\tbeforeParentId;\n\n\t\tif (!isNew) {\n\t\t\t// My parent as of this moment\n\t\t\tbeforeParentId = parseInt(model.get('parentid'));\n\t\t}\n\n\t\t// Set it\n\t\tmodel.parseAndSave(data).then(\n\t\t\tfunction () {\n\t\t\t\tconst parentid = parseInt(model.get('parentid'));\n\t\t\t\t// New category\n\t\t\t\tif (isNew) {\n\t\t\t\t\t// Get it in the list (if applicable)\n\t\t\t\t\tif (parentid === NS.current.id) {\n\t\t\t\t\t\tNS.list.add(model);\n\t\t\t\t\t}\n\t\t\t\t\t// trigger category:children:changed event with the category's parent\n\t\t\t\t\tdispatcher.trigger('category:children:changed', parentid, model);\n\t\t\t\t\tdispatcher.trigger('category:children:changed:' + parentid, model);\n\t\t\t\t\t// For backwards compatability\n\t\t\t\t\tdispatcher.trigger('category:add:success', model);\n\t\t\t\t}\n\t\t\t\t// Exisiting category\n\t\t\t\telse {\n\t\t\t\t\t// Did the category move?\n\t\t\t\t\tif (beforeParentId !== parentid) {\n\t\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t\t'category:moved',\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\tbeforeParentId,\n\t\t\t\t\t\t\tparentid,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t\t'category:children:changed',\n\t\t\t\t\t\t\tbeforeParentId,\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tdispatcher.trigger('category:children:changed', parentid, model);\n\t\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t\t'category:children:changed:' + beforeParentId,\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tdispatcher.trigger('category:children:changed:' + parentid, model);\n\t\t\t\t\t\t// NS.tree.fetchCategory( model.get('parentid') );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// DAM-15698 - Get updated category data\n\t\t\t\tmodel.fetch();\n\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'category:edit:success',\n\t\t\t\t\tmodel,\n\t\t\t\t\tisNew,\n\t\t\t\t\t!isNew && beforeParentId !== parentid,\n\t\t\t\t);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// Error\n\t\t\t\tdispatcher.trigger('category:edit:error', err, model);\n\t\t\t\t// For backwards compatability\n\t\t\t\tdispatcher.trigger('category:add:error', err);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:category#category:edit:error\n\t * @param {Netx.Models.Category} model Category model\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'category:edit:error',\n\t\tfunction (error, model) {\n\t\t\tdispatcher.trigger('app:alert', error);\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:category#category:edit:success\n\t * @param {Netx.Models.Category} model Category model\n\t * @param {boolean} isNew New category\n\t * @param {boolean} newParent Whether the category got a new parent or not\n\t * @fires module:notification#notification\n\t */\n\tdispatcher.on(\n\t\t'category:edit:success',\n\t\tfunction (model, isNew, newParent) {\n\t\t\tif (model instanceof CategoryModel) {\n\t\t\t\tconst name = model.get('name');\n\t\t\t\tdispatcher.trigger('notification', {\n\t\t\t\t\tmodule: 'category',\n\t\t\t\t\ttitle: isNew\n\t\t\t\t\t\t? i18n.t('i18n.categoryAdded')\n\t\t\t\t\t\t: i18n.t('i18n.categoryUpdated'),\n\t\t\t\t\ttype: 'success',\n\t\t\t\t\tdescription: isNew\n\t\t\t\t\t\t? i18n.t('i18n.categorySuccessfullyAdded', {category: name})\n\t\t\t\t\t\t: i18n.t('i18n.categorySuccessfullyUpdated', {\n\t\t\t\t\t\t\t\tcategory: name,\n\t\t\t\t\t\t }),\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\twindow,\n\t);\n\t//============================================\n\t//\t!Category add/edit\n\t//============================================\n\n\t//============================================\n\t//\tCategory delete\n\t//============================================\n\t/**\n\t * Handles a request to delete a category\n\t * @event module:category#category:delete\n\t * @param {Netx.Models.Category} model Category model\n\t * @fires module:app#app:confirm\n\t * @see module:category#category:delete:confirmed\n\t * @see module:category#category:delete:prepare\n\t * @see module:category#category:delete:prepare:error\n\t * @see module:category#category:delete:prepare:success\n\t */\n\tdispatcher.on('category:delete', function (model) {\n\t\t// Notify we are preparing to delete\n\t\tdispatcher.trigger('category:delete:prepare', model);\n\n\t\t// fetch stats for the category:\n\t\tmodel.getDetails().then(\n\t\t\tfunction (data) {\n\t\t\t\t// For clarity's sake:\n\t\t\t\tdata.assetCount = data.fileCount;\n\n\t\t\t\t// The i18n for this message is slightly complicated, because it can contain various mixtures of stats:\n\t\t\t\t// Vars\n\t\t\t\tvar assetCountStr, byteCountStr, message, subCategoryCountStr;\n\n\t\t\t\tmessage = '';\n\t\t\t\tassetCountStr = i18n.t('i18n.nAssets', {\n\t\t\t\t\tcount: data.assetCount,\n\t\t\t\t});\n\t\t\t\tsubCategoryCountStr = i18n.t('i18n.subCategoryCount', {\n\t\t\t\t\tcount: data.subCategoryCount,\n\t\t\t\t});\n\n\t\t\t\t// The DAM translates this one value (but not the others!?!?) to a string:\n\t\t\t\tbyteCountStr = data.byteCount;\n\n\t\t\t\tif (!data.assetCount && !data.subCategoryCount) {\n\t\t\t\t\t// No contents at all.\n\t\t\t\t\tmessage = i18n.t('i18n.confirmDeleteEmpty', {\n\t\t\t\t\t\tname: model.get('name'),\n\t\t\t\t\t});\n\t\t\t\t} else if (!data.assetCount) {\n\t\t\t\t\t// No assets, just subcategories\n\t\t\t\t\tmessage = i18n.t('i18n.confirmDeleteWithoutAssets', {\n\t\t\t\t\t\tname: model.get('name'),\n\t\t\t\t\t\tsubCategoryCount: subCategoryCountStr,\n\t\t\t\t\t});\n\t\t\t\t} else if (!data.subCategoryCount) {\n\t\t\t\t\t// No subcategoriess, just assets\n\t\t\t\t\tmessage = i18n.t('i18n.confirmDeleteWithoutSubCategories', {\n\t\t\t\t\t\tname: model.get('name'),\n\t\t\t\t\t\tassetCount: assetCountStr,\n\t\t\t\t\t\tbyteCount: byteCountStr,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\t// Both assets and categories\n\t\t\t\t\tmessage = i18n.t('i18n.confirmDelete', {\n\t\t\t\t\t\tname: model.get('name'),\n\t\t\t\t\t\tsubCategoryCount: subCategoryCountStr,\n\t\t\t\t\t\tassetCount: assetCountStr,\n\t\t\t\t\t\tbyteCount: byteCountStr,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Preparation was a success\n\t\t\t\tdispatcher.trigger('category:delete:prepare:success', model);\n\n\t\t\t\t// Request confirmation:\n\t\t\t\tdispatcher.trigger('app:confirm:delete', {\n\t\t\t\t\tmessage: message,\n\t\t\t\t\teventStr: 'category:delete',\n\t\t\t\t\targs: [model],\n\t\t\t\t});\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('category:delete:prepare:error', err, model);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * When the category edit form fires category:delete:confirmed,\n\t * destroy the category model & remove it from everywhere.\n\t * and category tree and hides the category view.\n\t * @event module:category#category:delete:confirmed\n\t * @param {Netx.Models.Category} model Category model\n\t * @fires module:category#category:delete:error\n\t * @fires module:category#category:delete:success\n\t * @fires module:category#category:children:changed\n\t * @fires module:category#category:children:changed:(parentid)\n\t */\n\tdispatcher.on('category:delete:confirmed', function (model) {\n\t\t// Check if current category is still a valid one\n\t\tvar currentCatDestroyed = NS.current.id === model.id || NS.current.id === 0;\n\n\t\t// tell the server to destroy the model associated with this view\n\t\tmodel.destroy({wait: true}).then(\n\t\t\tfunction () {\n\t\t\t\t// Success\n\t\t\t\tNS.options.tree && NS.list.remove(model);\n\n\t\t\t\tdispatcher.trigger('category:children:changed', model.get('parentid'));\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'category:children:changed:' + model.get('parentid'),\n\t\t\t\t);\n\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'category:delete:success',\n\t\t\t\t\tmodel,\n\t\t\t\t\tcurrentCatDestroyed,\n\t\t\t\t);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// Error\n\t\t\t\tdispatcher.trigger('category:delete:error', err);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:category#category:delete:error\n\t * @param {string} error Error string\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'category:delete:error',\n\t\tfunction (error) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:category#category:delete:success\n\t * @param {Netx.Models.Category} model Category model\n\t * @param {boolean} currentCatDestroyed Whether or not current category is no more (useful for routing away, etc)\n\t * @fires module:notification#notification\n\t * @fires module:category#category:gotoParent\n\t */\n\tdispatcher.on(\n\t\t'category:delete:success',\n\t\tfunction (model, currentCatDestroyed) {\n\t\t\t// Get category name to use in the notification\n\t\t\tconst name = model.get('name');\n\t\t\t// Is the deleted category an ancestor of the current category?\n\t\t\tconst ancestorOfCurrent =\n\t\t\t\t_.indexOf(NS.current.get('ancestors'), model.id) > -1;\n\n\t\t\tconst currentViewDestroyed = currentCatDestroyed || ancestorOfCurrent;\n\t\t\tif (currentViewDestroyed) {\n\t\t\t\tconst parentId = model.get('ancestors')[0];\n\t\t\t\tif (!parentId || parentId === NS.options.categoryid) {\n\t\t\t\t\tappRouter.home();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tappRouter.navigate(`category/${parentId}`, {\n\t\t\t\t\ttrigger: true,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Dispatch notification\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'category',\n\t\t\t\ttitle: i18n.t('i18n.categoriesUpdated'),\n\t\t\t\tdescription: i18n.t('i18n.categorySuccessfullyDeleted', {\n\t\t\t\t\tcategory: name,\n\t\t\t\t}),\n\t\t\t\ttype: 'success',\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:category#category:delete:prepare\n\t * @param {Netx.Models.Category} model Category model\n\t * @fires module:app#app:progress\n\t * @see module:category#category:delete:prepare:error\n\t * @see module:category#category:delete:prepare:success\n\t */\n\tdispatcher.on('category:delete:prepare', function (model) {\n\t\tdispatcher.trigger('app:progress', {\n\t\t\teventStr: 'category:delete:prepare',\n\t\t\theaderTitle: i18n.t('i18n.preparingCategoryDelete'),\n\t\t\tmessage: i18n.t('i18n.preparingCategoryDeleteMessage'),\n\t\t});\n\t});\n\t/**\n\t * @event module:category#category:delete:prepare:error\n\t * @param {string} error Error string\n\t * @param {Netx.Models.Category} model Category model\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'category:delete:prepare:error',\n\t\tfunction (error, model) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:category#category:delete:prepare:success\n\t * @param {Netx.Models.Category} model Category model\n\t */\n\tdispatcher.on(\n\t\t'category:delete:prepare:success',\n\t\tfunction (model) {\n\t\t\t//\n\t\t},\n\t\twindow,\n\t);\n\t//============================================\n\t//\t!Category delete\n\t//============================================\n\n\t//============================================\n\t//\tCategory import\n\t//============================================\n\t/**\n\t * @event module:category#category:import\n\t * @param {number} catId Category id\n\t */\n\tdispatcher.on('category:import', function (catId) {\n\t\t// If not already routed\n\t\tif (!window.location.hash.includes('upload/import')) {\n\t\t\tappRouter.navigate('upload/import/' + catId, {trigger: true});\n\t\t} else {\n\t\t\tdispatcher.trigger('upload:import', catId);\n\t\t}\n\t});\n\t//============================================\n\t//\t!Category import\n\t//============================================\n\n\t//============================================\n\t//\tCategory recent\n\t//============================================\n\t/**\n\t * @event module:category#category:getRecent:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'category:getRecent:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-labelValueList\" */ '@netx/core-app/views/labelValueListView'\n\t\t\t).then((View) => {\n\t\t\t\tmodalViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['category:getRecent:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * Gets and displays the most recent categories\n\t * @event module:category#category:getRecent\n\t * @fires module:category#category:getRecent:show\n\t */\n\tdispatcher.on('category:getRecent', function () {\n\t\timport(\n\t\t\t/* webpackChunkName: \"view-recentCategory\" */ './views/recentCategoryView'\n\t\t).then((View) => {\n\t\t\tdispatcher.trigger('category:getRecent:show', {\n\t\t\t\tclassName: '',\n\t\t\t\tcollection: Netx.category.recent,\n\t\t\t\theaderTitle: i18n.t('i18n.recentCategories'),\n\t\t\t\tid: 'recent-categories',\n\t\t\t\tchildView: {\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\ttarget: '.list',\n\t\t\t\t\toptions: {\n\t\t\t\t\t\tclassName: 'category',\n\t\t\t\t\t\ttagName: 'li',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t\tNetx.category.recent.fetch();\n\t});\n\t//============================================\n\t//\t!Category recent\n\t//============================================\n\n\t//============================================\n\t//\tCategory searches\n\t//============================================\n\t/**\n\t * Lists all assets recursively within category\n\t * @event module:category#category:listAll\n\t * @param {number} catId Category id\n\t */\n\tdispatcher.on('category:search:listAll', function (catId) {\n\t\t// Support the ability to make this call from a non fetched category (like a route for example)\n\t\tvar cat = new CategoryModel({categoryid: parseInt(catId)});\n\t\tappRouter.navigate(\n\t\t\t'search/advanced/' + cat.searchRules_listAll().toStr(),\n\t\t\ttrue,\n\t\t);\n\t});\n\t//============================================\n\t//\t!Category searches\n\t//============================================\n\n\t//============================================\n\t//\tCategory scope (selection)\n\t//============================================\n\t/**\n\t * @event module:category#category:scope:fetch\n\t * @param {Netx.Collections.Categories} categories Categories\n\t */\n\tdispatcher.on('category:scope:fetch', function (categories) {\n\t\tcategories.getCategoryObjects();\n\t});\n\t/**\n\t * @event module:category#category:scope:toggle:category\n\t * @param {Netx.Collections.Categories} categories Categories\n\t * @param {number} catId Category id\n\t */\n\tdispatcher.on('category:scope:toggle:category', function (categories, catId) {\n\t\tcatId = parseInt(catId);\n\t\tvar category = categories.get(catId);\n\t\tif (category) {\n\t\t\tcategories.remove(catId);\n\t\t} else {\n\t\t\tcategory = new CategoryModel({categoryid: catId});\n\t\t\tcategory.fetch().then(function () {\n\t\t\t\tcategories.unshift(category);\n\t\t\t});\n\t\t}\n\t});\n\t//============================================\n\t//\t!Category scope (selection)\n\t//============================================\n\t/**\n\t * event module:category#category:tree:close\n\t * @param {number} catId Category to open id\n\t * @param {Netx.Collections.CategoryTreeNodes} tree CategoryTreeNodes collection\n\t */\n\tdispatcher.on('category:tree:close', function (catId, tree) {\n\t\tvar category;\n\n\t\t// were we passed a model or an int?\n\t\tif (!_.isNumeric(catId)) {\n\t\t\tcategory = catId;\n\t\t\tcatId = catId.id;\n\t\t\tif (!tree && category.get('tree')) {\n\t\t\t\ttree = category.get('tree');\n\t\t\t}\n\t\t}\n\t\t// Default to main tree\n\t\ttree = tree || NS.tree;\n\n\t\tcategory = category || tree.get(catId);\n\t\tcategory && category.close();\n\t});\n\n\t/**\n\t * Handle a request to pop open a deeply-nested category:\n\t * Load the category, give it a model, and recurse on its parent.\n\t * @event module:category#category:tree:open\n\t * @param {number} catId Category to open id\n\t * @param {Netx.Collections.CategoryTreeNodes} tree CategoryTreeNodes collection\n\t * @param {boolean} parentOnly Fetch everything that is needed but do not open tghe passed categor - just open up its parent and above\n\t * @fires module:category#category:tree:open\n\t */\n\tdispatcher.on('category:tree:open', function (catId, tree, parentOnly) {\n\t\tlet category;\n\n\t\t// were we passed a model or an int?\n\t\tif (catId instanceof CoreModel) {\n\t\t\tcategory = catId;\n\t\t\tcatId = catId.id;\n\t\t\tif (!tree && category.get('tree')) {\n\t\t\t\ttree = category.get('tree');\n\t\t\t}\n\t\t}\n\n\t\tif (!catId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Default if we can't find a tree anyplace:\n\t\ttree = tree || NS.tree;\n\n\t\t// is the category in the tree already?\n\t\tcategory = category || tree.get(catId);\n\t\tif (!category) {\n\t\t\t// We received only a categoryid, and it wasn't in our tree yet.\n\t\t\t// Get it into our tree\n\t\t\ttree.fetchCategory(catId).then(function (cat) {\n\t\t\t\t// retry!\n\t\t\t\tif (cat && cat.id !== NS.options.categoryid) {\n\t\t\t\t\tdispatcher.trigger('category:tree:open', cat.id, tree, parentOnly);\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\tif (parentOnly) {\n\t\t\t\t// if we haven't reached the top, recurse\n\t\t\t\tif (category.id != NS.options.categoryid) {\n\t\t\t\t\tconst parentId = category.get('parentid');\n\t\t\t\t\tlet parentCat;\n\t\t\t\t\t// If we already have the parent category in the tree - do not bother\n\t\t\t\t\t// it will cause unnecessary calls up the tree in most cases\n\t\t\t\t\tif (parentId != NS.options.categoryid) {\n\t\t\t\t\t\tparentCat = tree.get(parentId);\n\t\t\t\t\t\t// We need more data\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!parentCat ||\n\t\t\t\t\t\t\t_.difference(parentCat.get('children'), tree.pluck('categoryid'))\n\t\t\t\t\t\t\t\t.length\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tdispatcher.trigger('category:tree:open', parentId, tree);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// We have all the data we need\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tparentCat.open();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Check that we need to fetch any of its children first\n\t\t\t\tconst childrenNeeded =\n\t\t\t\t\t_.difference(category.get('children'), tree.pluck('categoryid'))\n\t\t\t\t\t\t.length > 0;\n\t\t\t\t// We found the category; cache its children in the tree.\n\t\t\t\t$.when(childrenNeeded ? tree.fetchChildren(catId) : true).then(\n\t\t\t\t\tfunction (children) {\n\t\t\t\t\t\t// This sets the 'open' state on the model, and triggers an 'open' event\n\t\t\t\t\t\tcategory.open();\n\t\t\t\t\t\t// If we haven't reached the top, recurse\n\t\t\t\t\t\tif (category.id != NS.options.categoryid) {\n\t\t\t\t\t\t\tconst parentId = category.get('parentid');\n\t\t\t\t\t\t\tlet parentCat;\n\t\t\t\t\t\t\t// If we already have the parent category in the tree - do not bother\n\t\t\t\t\t\t\t// it will cause unnecessary calls up the tree in most cases\n\t\t\t\t\t\t\tif (parentId != NS.options.categoryid) {\n\t\t\t\t\t\t\t\tparentCat = tree.get(parentId);\n\t\t\t\t\t\t\t\t// We need more data\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t!parentCat ||\n\t\t\t\t\t\t\t\t\t_.difference(\n\t\t\t\t\t\t\t\t\t\tparentCat.get('children'),\n\t\t\t\t\t\t\t\t\t\ttree.pluck('categoryid'),\n\t\t\t\t\t\t\t\t\t).length\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tdispatcher.trigger('category:tree:open', parentId, tree);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// We have all the data we need\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tparentCat.open();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n\n\t/**\n\t * event module:category#category:tree:reset\n\t * @param {Netx.Collections.CategoryTreeNodes} tree CategoryTreeNodes collection\n\t */\n\tdispatcher.on('category:tree:reset', function (tree) {\n\t\tif (tree === Netx.category.tree) {\n\t\t\t// TODO: at the moment there is no way to tell the app \"go to top of category tree\"\n\t\t\t// except to route to the default (blank) route. Someday we may want those to be\n\t\t\t// two different actions ...\n\t\t\tdispatcher.trigger('category:route:top');\n\t\t}\n\t});\n\t/**\n\t * @event module:category#category:tree:search\n\t * @param {string} searchStr Search string\n\t * @param {Netx.Collections.CategoryTreeNodes} tree Tree collection\n\t * @fires module:category#category:tree:search:error\n\t * @fires module:category#category:tree:search:success\n\t */\n\tdispatcher.on('category:tree:search', function (searchStr, tree) {\n\t\ttree = tree || Netx.category.tree;\n\t\t// Do not run if it is the same search string as last time\n\t\tif (searchStr !== tree.searchString) {\n\t\t\ttree.searchCategoryName(searchStr, {duplicateQueryPolicy: 'abort'}).then(\n\t\t\t\tfunction () {\n\t\t\t\t\tdispatcher.trigger('category:tree:search:success', searchStr, tree);\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\tdispatcher.trigger('category:tree:search:error', err, tree);\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t});\n\t/**\n\t * @event module:category#category:tree:search:clear\n\t * @param {Netx.Collections.CategoryTreeNodes} tree Tree collection\n\t */\n\tdispatcher.on('category:tree:search:clear', function (tree) {\n\t\ttree = tree || Netx.category.tree;\n\t\ttree.clearSearch();\n\n\t\t// DAM-20007 - make sure we have siblings for any newly fetched categories\n\t\tdispatcher.trigger('category:tree:open', NS.current.id, NS.tree);\n\t});\n\t/**\n\t * @event module:category#category:tree:search:error\n\t * @param {string} error Error string\n\t * @param {Netx.Collections.CategoryTreeNodes} tree Tree collection\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'category:tree:search:error',\n\t\tfunction (error, tree) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:category#category:tree:search:success\n\t * @param {string} searchStr Search string\n\t * @param {Netx.Collections.CategoryTreeNodes} tree Tree collection\n\t */\n\tdispatcher.on(\n\t\t'category:tree:search:success',\n\t\tfunction (searchStr, tree) {\n\t\t\t//\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:category#category:tree:set:selection\n\t * @param {number} catId Category to open id\n\t * @param {Netx.Collections.CategoryTreeNodes} tree Tree collection\n\t */\n\tdispatcher.on('category:tree:set:selection', function (catId, tree) {\n\t\ttree = tree || Netx.category.tree;\n\t\ttree.selectedCategory = catId;\n\t});\n\t/**\n\t * event module:category#category:tree:toggle\n\t * @param {number} catId Category to open id\n\t * @param {Netx.Collections.CategoryTreeNodes} tree CategoryTreeNodes collection\n\t * @fires module:category#category:tree:close\n\t * @fires module:category#category:tree:open\n\t */\n\tdispatcher.on('category:tree:toggle', function (catId, tree) {\n\t\ttree = tree || Netx.category.tree;\n\n\t\tvar c = tree.get(catId);\n\t\tif (c.isOpen()) {\n\t\t\tdispatcher.trigger('category:tree:close', catId, tree);\n\t\t} else if (c.get('children').length) {\n\t\t\tdispatcher.trigger('category:tree:open', catId, tree);\n\t\t}\n\t});\n\t//============================================\n\t//\t!Category tree\n\t//============================================\n\n\t// END EVENT HANDLERS\n\t// ==================\n\t// After the module has run through it's init routine\n\t// stop listening for subsequent init events which would\n\t// cause duplicate listeners and instances to be created.\n\tPromise.all(NS.bootstrap).then(() => {\n\t\tdelete NS.bootstrap;\n\t\tdispatcher.trigger('category:extend', NS);\n\t\tdispatcher.trigger('category:initialized', NS);\n\t});\n});\n\nexport default {\n\tinitialize(opts) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tdispatcher.once('category:initialized', function () {\n\t\t\t\tresolve(routes);\n\t\t\t});\n\t\t\tdispatcher.trigger('category:init');\n\t\t});\n\t},\n};\n","export {default} from 'netxcore@netx/core-category/controller';\n\nimport {appRouter} from '@netx/core-next/instance/router';\nimport CoreModel from '@netx/core/lib/netx-model';\nimport AssetListView from '@netx/core-asset/views/assetListView';\nimport SearchFacetsView from '@netx/core-search/views/searchFacetsView';\nimport CategoryGalleryView from './views/categoryGalleryView';\nimport CategoryTreeNodesView from '@netx/core-category/views/categoryTreeNodesView';\nimport CategoryBreadCrumbView from '@netx/core-category/views/categoryBreadCrumbView';\nimport CategoryGalleryHeaderView from '@netx/core-category/views/categoryGalleryHeaderView';\nimport CategoryListView from '@netx/core-category/views/categoryListView';\n\nimport cmsConfig from '@netx/core-cms/config';\nimport categoryConfig from '@netx/core-category/config';\nimport {getPreference} from '@netx/core-user-next/utils/getPreference';\nimport {assetCursor} from '@netx/core-asset/collections/singleton/assetCursor';\nimport {appViews} from '@netx/core-app/singletons/appViews';\n\n/**\n * Category Module Extensions\n * @event module:category#category:extend\n * @param {object} NS Namespace object\n */\ndispatcher.once('category:extend', function (NS) {\n\t// Use faceted search\n\t// Filter mode is driven by the property `search.facet.filterMode` - default is quite literally \"DEFAULT\"\n\t// See Netx.Collections.SearchFacets\n\tNS.assets.useFacetedSearch(true);\n\n\t// Sometimes, due to timing we do not get our proper active classes on non top level cats\n\t// I would love to put this in the collection but now we have multiple instances of CategoryTreeNodes with\n\t// varying needs (this is the only tree that cares about route currently) -jse-\n\tif (NS.tree && NS.options.tree) {\n\t\tNS.tree.on(\n\t\t\t'sync',\n\t\t\t_.debounce(function () {\n\t\t\t\t// Vars\n\t\t\t\tvar id = NS.current.id;\n\t\t\t\t// `Top level` is of no concern\n\t\t\t\tif (id > 1) {\n\t\t\t\t\tconst cat = this.get(id);\n\t\t\t\t\tif (cat) {\n\t\t\t\t\t\tcat.trigger('activate');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, 150),\n\t\t);\n\t}\n\n\t// Re-fetch when attributes change\n\tNS.assets.listenTo(\n\t\tdispatcher,\n\t\t'asset:attributes:updated',\n\t\tfunction (attributes) {\n\t\t\tthis.syncedOnce && this.fetch();\n\t\t},\n\t);\n\n\t// Listeners for Backbone specific collection and model events.\n\t// No need to duplicate the event bus that already exists via Backbone.\n\tNS.current.on('sync', function () {\n\t\tthis.getCategoryBreadcrumbs();\n\t\tdispatcher.trigger('category:getAssets');\n\t});\n\n\t/**\n\t * Fetches the assets for a category. Handles setting\n\t * up a selection with the proper sort order, reporsitory\n\t * directory path and startPosition.\n\t * @event module:category#category:getAssets\n\t * @see {@link Netx.Collections.MiniAssets}\n\t */\n\tdispatcher.on('category:getAssets', function () {\n\t\tNS.assets.getCategoryAssets(Netx.category.current.id);\n\t});\n\n\t/**\n\t * @event module:category#category:gallery:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'category:gallery:show',\n\t\tfunction (viewOptions) {\n\t\t\tappViews.add({\n\t\t\t\tviewClass: CategoryGalleryView,\n\t\t\t\tshowActions: ['category:gallery:show'],\n\t\t\t\tviewOptions: viewOptions,\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * Main route handler for categories\n\t * @event module:category#category:routed\n\t * @param {number} id Category id\n\t * @param {number} page Page number\n\t * @fires module:category#category:current:update\n\t * @fires module:category#category:tree:open\n\t * @fires module:category#category:gallery:show\n\t * @fires module:category#category:top:routed\n\t */\n\tdispatcher.off('category:routed');\n\tdispatcher.on('category:routed', function (id, page) {\n\t\tvar catAssets = NS.assets,\n\t\t\thash = window.location.hash,\n\t\t\topts = NS.options;\n\n\t\t// Avoids rerendering if we were just at this route(aka entering and exiting asset detail)\n\t\t// THEM-1244 - this was where the issue was when faceting from page 2,\n\t\t// fixed in src/js/modules/app/models/pager.js\n\t\t// the following could be refactored\n\t\tif (catAssets.lastCategoryRoute !== hash) {\n\t\t\tcatAssets.lastCategoryRoute = hash;\n\t\t\tpage = page || 1;\n\t\t\tid = parseInt(id);\n\n\t\t\t// Top level routed\n\t\t\tif (id === opts.categoryid) {\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'category:current:update',\n\t\t\t\t\t{\n\t\t\t\t\t\tcategoryid: opts.categoryid,\n\t\t\t\t\t\trepositorydirectory: opts.repositorydirectory,\n\t\t\t\t\t},\n\t\t\t\t\tpage,\n\t\t\t\t);\n\t\t\t\t// dispatcher.trigger('category:top:routed');\n\t\t\t}\n\t\t\t// Anything but the top level routed\n\t\t\telse {\n\t\t\t\tdispatcher.trigger('category:current:update', {categoryid: id}, page);\n\t\t\t}\n\t\t} else {\n\t\t\t// search facets get wonky if the category doesn't trigger\n\t\t\tdispatcher.trigger('search:facets:refresh');\n\n\t\t\t// DAM-18538: the asset cursor is likely set to the wrong collection\n\t\t\tassetCursor.cacheFetch(Netx.category.assets);\n\t\t}\n\n\t\tif (!getPreference('assetList.thumbSize')) {\n\t\t\tdispatcher.trigger('asset:thumb:resize', '190');\n\t\t}\n\n\t\tvar categoryViews = {\n\t\t\tbreadcrumbs: {\n\t\t\t\tviewClass: CategoryBreadCrumbView,\n\t\t\t\tmodel: NS.current,\n\t\t\t},\n\t\t\theader: {\n\t\t\t\tviewClass: CategoryGalleryHeaderView,\n\t\t\t\tmodel: NS.current,\n\t\t\t\tcollection: NS.assets,\n\t\t\t},\n\t\t\tlist: !categoryConfig.displayCategoriesAsAssets\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tviewClass: CategoryListView,\n\t\t\t\t\t\tcollection: NS.list,\n\t\t\t\t },\n\t\t\tassets: {\n\t\t\t\tviewClass: AssetListView,\n\t\t\t\tcollection: NS.assets,\n\t\t\t},\n\t\t\tfacets: {\n\t\t\t\tviewClass: SearchFacetsView,\n\t\t\t\tcollection: NS.assets.selection.searchFacets,\n\t\t\t},\n\t\t\ttree: {\n\t\t\t\tviewClass: CategoryTreeNodesView,\n\t\t\t\tcollection: NS.tree.topLevelCategories,\n\t\t\t\ttree: NS.tree,\n\t\t\t},\n\t\t};\n\n\t\tdispatcher.trigger('category:gallery:show', {\n\t\t\tmodel: NS.current,\n\t\t\tcollection: NS.list,\n\t\t\tviews: categoryViews,\n\t\t});\n\n\t\t$('#wrapper').scrollTop(0);\n\t});\n\t//============================================\n\t//\t!Category route\n\t//============================================\n\n\tdispatcher.off('category:gotoParent');\n\tdispatcher.on('category:gotoParent', function () {\n\t\t// navigate up a category if 1. we are not already at the root\n\t\t// and 2. we are inside the root\n\t\t// and 3. 'top level' isn't in any path, incase portal is set there, back button breaks\n\n\t\tif (\n\t\t\tNS.current.id !== NS.options.categoryid &&\n\t\t\t(categoryConfig.categoryName == 'top level' ||\n\t\t\t\tNS.current.get('path').indexOf(categoryConfig.categoryName) > -1)\n\t\t) {\n\t\t\tvar parentId = NS.current.get('parentid');\n\t\t\tappRouter.navigate('category/' + parentId, {trigger: true});\n\t\t} else {\n\t\t\t// otherwise, goto the homepage\n\t\t\tappRouter.navigate('page/' + cmsConfig.homeRoute, {trigger: true});\n\t\t}\n\t});\n\n\t// hide navs on any route\n\tappRouter.on('route', function (path) {\n\t\tvar $body = $('body');\n\t\tif ($body.hasClass('keep-nav-open')) {\n\t\t\t$body.removeClass('keep-nav-open');\n\t\t} else {\n\t\t\t$body.removeClass('category-nav');\n\t\t\tif ($body.hasClass('page-nav')) {\n\t\t\t\tNetx.views.appView.headerView.togglePages();\n\t\t\t}\n\t\t}\n\t});\n\n\t// Category current\n\t// THEM-1387 - override to not getAssets if on detail route\n\t// when refreshing a detail page, cursor gets confused when\n\t// someone elses collection gets cached (mobile tree)\n\tNS.current.off('sync');\n\tNS.current.on('sync', function () {\n\t\tthis.getCategoryBreadcrumbs();\n\t\tif (window.location.hash.indexOf('#asset/') === -1) {\n\t\t\tdispatcher.trigger('category:getAssets');\n\t\t}\n\t});\n\n\t// This event is fired by the category model whenever its id is changed\n\tNS.current.off('change:categoryid');\n\tNS.current.on('change:categoryid', function (category, categoryid, opts) {\n\t\tNS.current.fetch().then(\n\t\t\tfunction (cat) {\n\t\t\t\t// If not top level, trigger the category to open\n\t\t\t\tif (NS.current.id !== NS.options.categoryid) {\n\t\t\t\t\t// dispatcher.trigger('category:tree:open', NS.current.id, NS.tree);\n\t\t\t\t\tvar parentOnly = this.get('children').length ? false : true;\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'category:tree:open',\n\t\t\t\t\t\tNS.current.id,\n\t\t\t\t\t\tNS.tree,\n\t\t\t\t\t\tparentOnly,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tdispatcher.trigger('category:current:update:success', categoryid);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('category:current:update:error', err);\n\t\t\t},\n\t\t);\n\t});\n\n\tdispatcher.on('category:current:update:success', (catId) => {\n\t\tNS.list.fetch();\n\t});\n\n\t/**\n\t * Handle a request to pop open a deeply-nested category:\n\t * Load the category, give it a model, and recurse on its parent.\n\t * @event module:category#category:tree:open\n\t * @param {number} catId Category to open id\n\t * @param {Netx.Collections.CategoryTreeNodes} tree CategoryTreeNodes collection\n\t * @param {boolean} parentOnly Fetch everything that is needed but do not open tghe passed categor - just open up its parent and above\n\t * @fires module:category#category:tree:open\n\t */\n\tdispatcher.on('category:tree:open', function (catId, tree, parentOnly) {\n\t\tlet category;\n\n\t\t// were we passed a model or an int?\n\t\tif (catId instanceof CoreModel) {\n\t\t\tcategory = catId;\n\t\t\tcatId = catId.id;\n\t\t\tif (!tree && category.get('tree')) {\n\t\t\t\ttree = category.get('tree');\n\t\t\t}\n\t\t}\n\n\t\tif (!catId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Default if we can't find a tree anyplace:\n\t\ttree = tree || NS.tree;\n\n\t\t// is the category in the tree already?\n\t\tcategory = category || tree.get(catId);\n\t\tif (!category) {\n\t\t\t// We received only a categoryid, and it wasn't in our tree yet.\n\t\t\t// Get it into our tree\n\t\t\ttree.fetchCategory(catId).then(function (cat) {\n\t\t\t\t// retry!\n\t\t\t\tif (cat && cat.id !== NS.options.categoryid) {\n\t\t\t\t\tdispatcher.trigger('category:tree:open', cat.id, tree, parentOnly);\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\tif (parentOnly) {\n\t\t\t\t// if we haven't reached the top, recurse\n\t\t\t\tif (category.id != NS.options.categoryid) {\n\t\t\t\t\tconst parentId = category.get('parentid');\n\t\t\t\t\tlet parentCat;\n\t\t\t\t\t// If we already have the parent category in the tree - do not bother\n\t\t\t\t\t// it will cause unnecessary calls up the tree in most cases\n\t\t\t\t\tif (parentId != NS.options.categoryid) {\n\t\t\t\t\t\tparentCat = tree.get(parentId);\n\t\t\t\t\t\t// We need more data\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!parentCat ||\n\t\t\t\t\t\t\t_.difference(parentCat.get('children'), tree.pluck('categoryid'))\n\t\t\t\t\t\t\t\t.length\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tdispatcher.trigger('category:tree:open', parentId, tree);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// We have all the data we need\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tparentCat.open();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Check that we need to fetch any of its children first\n\t\t\t\tconst childrenNeeded =\n\t\t\t\t\t_.difference(category.get('children'), tree.pluck('categoryid'))\n\t\t\t\t\t\t.length > 0;\n\t\t\t\t// We found the category; cache its children in the tree.\n\t\t\t\t$.when(childrenNeeded ? tree.fetchChildren(catId) : true).then(\n\t\t\t\t\tfunction (children) {\n\t\t\t\t\t\t// This sets the 'open' state on the model, and triggers an 'open' event\n\t\t\t\t\t\tcategory.open();\n\t\t\t\t\t\t// If we haven't reached the top, recurse\n\t\t\t\t\t\tif (category.id != NS.options.categoryid) {\n\t\t\t\t\t\t\tconst parentId = category.get('parentid');\n\t\t\t\t\t\t\tlet parentCat;\n\t\t\t\t\t\t\t// If we already have the parent category in the tree - do not bother\n\t\t\t\t\t\t\t// it will cause unnecessary calls up the tree in most cases\n\t\t\t\t\t\t\tif (parentId != NS.options.categoryid) {\n\t\t\t\t\t\t\t\tparentCat = tree.get(parentId);\n\t\t\t\t\t\t\t\t// We need more data\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t!parentCat ||\n\t\t\t\t\t\t\t\t\t_.difference(\n\t\t\t\t\t\t\t\t\t\tparentCat.get('children'),\n\t\t\t\t\t\t\t\t\t\ttree.pluck('categoryid'),\n\t\t\t\t\t\t\t\t\t).length\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tdispatcher.trigger('category:tree:open', parentId, tree);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// We have all the data we need\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\tparentCat.open();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t});\n}); // category:extend\n","/**\n * The search module handles custom & simplified asset searches\n *\n * @module search\n */\n\nimport {dispatcher} from '@netx/core-next/instance/dispatcher';\nimport {appRouter} from '@netx/core-next/instance/router';\nimport {isStringValidInteger} from '@netx/core-next/utils/string';\nimport {\n\tsearchModuleStore,\n\tSearchMode,\n} from '@netx/core-search-next/moduleStore';\nimport {getUserLevel} from '@netx/core-user-next/utils/getUserLevel';\n\nimport SavedSearchDispatchListeners from '@netx/core-search/controllers/savedSearchController';\n\n// Collections\nimport AssetSearchRulesCollection from './collections/searchRules_assets';\nimport ClipSearchRulesCollection from './collections/searchRules_clips';\nimport ClipSearchConstituentsCollection from './collections/searchConstituents_clips';\nimport SearchAssetsCollection from './collections/searchAssets';\nimport SearchAttributeHistoryRulesCollection from './collections/searchAttributeHistoryRules';\n\n// Views\nimport SearchGalleryView from './views/searchGalleryView';\nimport SearchConstituentsGalleryView from './views/searchConstituentsGalleryView';\nimport SearchConstituentsGalleryHeaderView from './views/searchConstituentsGalleryHeaderView';\nimport SearchConstituentsListView_clips from './views/searchConstituentsListView_clips';\n\nimport {SearchMatchCriteria} from '@netx/core-search-rules/constants';\nimport {\n\tcreateCategoryIdRule,\n\tCategoryType,\n} from '@netx/core-search-rules/rules/categoryId';\nimport {\n\tcreateKeywordRule,\n\tKeywordType,\n} from '@netx/core-search-rules/rules/keyword';\nimport {\n\tcreateMetadataRule,\n\tMetadataType,\n} from '@netx/core-search-rules/rules/metadata';\nimport {createSavedSearchRule} from '@netx/core-search-rules/rules/savedSearch/savedSearchRule';\nimport {convertRulesToRuleObjects} from '@netx/core-search-rules/utils/convertRulesToRuleObjects';\nimport {convertToRules} from '@netx/core-search-rules/utils/convertToRules';\nimport {decodeBase64Rules} from '@netx/core-search-rules/utils/decodeBase64Rules';\nimport {encodeBase64Rules} from '@netx/core-search-rules/utils/encodeBase64Rules';\nimport {canSearchConstituentClips} from '@netx/core-constituents/enablers';\nimport {assetExpirationEnabled} from '@netx/core-asset-expiration/enablers';\nimport {\n\tcanSearchAttributeHistory,\n\tsimpleAssetIdSearchEnabled,\n} from '@netx/core-search-next';\nimport {SEARCH_RULES_EVENT} from '@netx/core-search-rules/actions';\nimport SearchRulesController from '@netx/core-search-rules/controller';\nimport {searchCriteriaCache} from './cache';\nimport customConfig from './config';\nimport routes from './routes';\n\nimport {\n\tassets as searchAssets,\n\tdateSubTypes1_default,\n\tmodes as searchModes,\n\tsystemAttributes as searchableSystemAttributes,\n} from './properties/search';\n\nimport {isEscapedURI} from '@netx/core/utils/isEscapedURI';\nimport {ids as assetAttributeTypeIds} from '@netx/core-asset/properties/attributes';\nimport {checksumEnabled} from '@netx/core-asset-next/enablers';\n\nimport savedCartsSingleton from './collections/singleton/savedSearch';\nimport {visualSearchEnabled} from '@netx/core-search-visual/enablers';\nimport SearchVisualController from '@netx/core-search-visual/controller';\nimport SavedSearchController from '@netx/core-search-saved/controller';\nimport {assetAttributes} from '@netx/core-attribute/singletons';\nimport {modalViews} from '@netx/core-app/singletons/modalViews';\nimport {escapeURI} from '@netx/core/utils/escapeURI';\nimport {unescapeURI} from '@netx/core/utils/unescapeURI';\nimport {appViews} from '@netx/core-app/singletons/appViews';\n\n//=================================\n//\tSHAREABLE TYPES\n//=================================\n// Define shareable types introduced by this module.\n_.extend(Netx.ShareableTypes, {\n\t'saved search': {\n\t\tlabel: i18n.t('i18n.savedSearch'),\n\t},\n});\n//=================================\n//\t!SHAREABLE TYPES\n//=================================\n\n//=================================\n//\tINIT\n//=================================\n/**\n * @event module:search#search:init\n * @fires module:search#search:extend\n * @fires module:search#search:initialized\n */\ndispatcher.once('search:init', function (options) {\n\t// Instances\n\tvar NS = (Netx.search = {\n\t\trules: {\n\t\t\tassets: new AssetSearchRulesCollection([\n\t\t\t\t{\n\t\t\t\t\telementType: 1,\n\t\t\t\t\telementSubType1: 4,\n\t\t\t\t},\n\t\t\t]),\n\t\t},\n\t\tmode: searchModuleStore.mode,\n\t});\n\tNS.options = options = _.extend({}, customConfig, options);\n\n\t// Search assets collection\n\tNS.assets = new SearchAssetsCollection([]);\n\t// // Re-fetch when attributes change\n\t// NS.assets.listenTo(dispatcher, 'asset:attributes:updated', () => {\n\t// \tthis.syncedOnce && this.fetch();\n\t// });\n\n\tNS.bootstrap = [\n\t\tSavedSearchController.initialize(),\n\t\tSearchRulesController.initialize(),\n\t\tvisualSearchEnabled() ? SearchVisualController.initialize() : true,\n\t];\n\n\t// Fetch the DAM lists of file types & format families into search/properties/search\n\tNS.rules.assets.fetchSearchCriteria();\n\n\t// Saved searches\n\tNS.saved = {\n\t\tall: savedCartsSingleton.allSavedSearches,\n\t\tfavorite: savedCartsSingleton.favoriteSavedSearches,\n\t};\n\n\tif (!visualSearchEnabled()) {\n\t\tdelete searchModes['visual:color'];\n\t\tvar searchOptions = searchAssets.elementTypes.options;\n\t\tvar idx = _.indexOf(searchOptions, _.findWhere(searchOptions, {val: 27}));\n\t\tif (idx) {\n\t\t\tsearchOptions = searchOptions.splice(idx, 1);\n\t\t}\n\t}\n\n\t// Valid user\n\tif (getUserLevel() > 0) {\n\t\t// Fetch saved searches\n\t\tNS.saved.all.fetch();\n\n\t\t// Deal with date search types related to attribute templates\n\t\t// TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO\n\t\t// We need to make a collection for our advanced search rule individual things, populate it with the defined properties\n\t\t// and then that collection can listen for these events and handle this itself - I moved this here now because\n\t\t// I was dealing with some search related stuff and it struck me that attributes collection was far to involved\n\t\t// in module it should know nothing about - attribtues do not depend on search but some things in search depend\n\t\t// on attribute - so this is the best place until we manage the advanced rules in a more backbone friendly manner.\n\t\t// Also, if we do not turn these advanced search options in to collection - how will we know to update advanced search\n\t\t// when options on properties are changed?\n\t\t// This cannot go in SearchRules collection as we create a lot of those - and really, our search rule options are getting\n\t\t// advanced enough (and the template messy enough) that it is about time we think about moving over to models/collections\n\t\t// TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO\n\t\t// https://jira.netx.net/browse/DAM-9364\n\t\t+(function () {\n\t\t\tvar advancedSearchDate = searchAssets.elementSubType1s[4];\n\n\t\t\t// Deal with search types based on user prefs\n\t\t\t// Expiration enabled?\n\t\t\tif (assetExpirationEnabled()) {\n\t\t\t\tsearchAssets.sortFields.expirationDate = i18n.t('i18n.expirationDate');\n\t\t\t}\n\t\t\tif (checksumEnabled()) {\n\t\t\t\tsearchAssets.sortFields.checksum = i18n.t('i18n.checksum');\n\t\t\t\tsearchableSystemAttributes.push('checksum');\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @method\n\t\t\t * @param {Netx.Models.Attribute} model Attribute model\n\t\t\t */\n\t\t\tfunction addToAdvancedSearch_date(model) {\n\t\t\t\t// Must be type date and the attribute must be for an asset (not clips currently)\n\t\t\t\tif (\n\t\t\t\t\tmodel.get('type') !== assetAttributeTypeIds.date ||\n\t\t\t\t\t!model.isForObjectType('asset')\n\t\t\t\t) {\n\t\t\t\t\treturn removeFromAdvancedSearch_date(model);\n\t\t\t\t}\n\t\t\t\t// Vars\n\t\t\t\tif (\n\t\t\t\t\t!_.findWhere(advancedSearchDate.options, {label: model.get('name')})\n\t\t\t\t) {\n\t\t\t\t\tadvancedSearchDate.options.push({\n\t\t\t\t\t\tval: model.get('name'),\n\t\t\t\t\t\tlabel: model.get('name'),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\t/**\n\t\t\t * @method\n\t\t\t * @param {Netx.Models.Attribute} model Attribute model\n\t\t\t */\n\t\t\tfunction removeFromAdvancedSearch_date(model) {\n\t\t\t\t// vars\n\t\t\t\tvar idx = _.indexOf(\n\t\t\t\t\tadvancedSearchDate.options,\n\t\t\t\t\t_.findWhere(advancedSearchDate.options, {label: model.get('name')}),\n\t\t\t\t);\n\t\t\t\tif (idx > -1) {\n\t\t\t\t\tadvancedSearchDate.options.splice(idx, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\t/**\n\t\t\t * @method\n\t\t\t * @param {module:asset/collections/attributes} collection Attributes\n\t\t\t */\n\t\t\tfunction populateFromDateAttributes(collection) {\n\t\t\t\t// Populate the advanced search options for subtype1 date\n\t\t\t\tadvancedSearchDate.options = dateSubTypes1_default.concat(\n\t\t\t\t\t_.compact(\n\t\t\t\t\t\tcollection.map(function (model) {\n\t\t\t\t\t\t\t// Is date and is for asset (no system attributes)\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!model.get('system') &&\n\t\t\t\t\t\t\t\tmodel.get('type') === assetAttributeTypeIds.date &&\n\t\t\t\t\t\t\t\tmodel.isForObjectType('asset')\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tval: model.get('name'),\n\t\t\t\t\t\t\t\t\tlabel: model.get('name'),\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event module:asset#asset:attributes:fetched\n\t\t\t * @param {Netx.Collection.Attributes} collection Attributes collection\n\t\t\t */\n\t\t\tdispatcher.on('asset:attributes:fetched', populateFromDateAttributes);\n\t\t\t/**\n\t\t\t * @event module:asset#asset:attribute:added\n\t\t\t * @param {Netx.Models.Attribute} model Attribute model\n\t\t\t */\n\t\t\tdispatcher.on('asset:attribute:added', addToAdvancedSearch_date);\n\t\t\t/**\n\t\t\t * @event module:asset#asset:attribute:changed\n\t\t\t * @param {Netx.Models.Attribute} model Attribute model\n\t\t\t */\n\t\t\tdispatcher.on('asset:attribute:changed', addToAdvancedSearch_date);\n\t\t\t/**\n\t\t\t * @event module:asset#asset:attribute:removed\n\t\t\t * @param {Netx.Models.Attribute} model Attribute model\n\t\t\t */\n\t\t\tdispatcher.on('asset:attribute:removed', removeFromAdvancedSearch_date);\n\n\t\t\t// Populate from initial data\n\t\t\tpopulateFromDateAttributes(assetAttributes);\n\t\t})();\n\t}\n\n\t//=================================\n\t//\tEVENT LISTENERS\n\t//=================================\n\n\tSavedSearchDispatchListeners();\n\n\t/**\n\t * So SearchWidgetView doesn't need to call upon the router\n\t * @event module:search#search:navigate\n\t * @param {string} type Search path\n\t */\n\tdispatcher.on('search:navigate', (path) => {\n\t\tappRouter.navigate(path, {\n\t\t\ttrigger: true,\n\t\t});\n\t});\n\n\t/**\n\t * @event module:search#search:routed\n\t * @param {string} type Search type\n\t * @param {string} str Search string\n\t * @param {number} page Page number\n\t * @see module:search#search:advanced\n\t * @see module:search#search:basic\n\t * @see module:search#search:exact\n\t * @see module:search#visual:asset\n\t * @see module:search#visual:color\n\t */\n\tdispatcher.on('search:routed', function (type, str, page) {\n\t\tdispatcher.trigger('search:' + type, str, page);\n\t});\n\n\t/**\n\t * if an asset was deleted, remove it from our collection if it's there.\n\t * @event module:search#asset:deleted\n\t * @param {number|string} id Asset id\n\t */\n\tdispatcher.on('asset:deleted', function (id) {\n\t\tid = parseInt(id);\n\t\tvar out = NS.assets.find(function (a) {\n\t\t\treturn a.get('assetId') === id;\n\t\t});\n\t\tif (out) {\n\t\t\tNS.assets.remove(out);\n\t\t}\n\t});\n\n\t/**\n\t * Add all assets in a saved search to the current cart (this does not clear current cart's assets)\n\t * @event module:search#search:saved:add:to:current:cart\n\t * @param {number} id SavedSearch id\n\t * @fires module:cart#cart:addAll\n\t * @see module:cart#cart:addAll:error\n\t * @see module:cart#cart:addAll:success\n\t */\n\tdispatcher.on('search:saved:add:to:current:cart', function (id) {\n\t\tdispatcher.trigger(\n\t\t\t'cart:addAll',\n\t\t\tnew AssetSearchRulesCollection([createSavedSearchRule(parseInt(id))]),\n\t\t);\n\t});\n\n\t/**\n\t * @event module:search#search:advanced:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'search:advanced:show',\n\t\tfunction (viewOptions) {\n\t\t\tdispatcher.trigger(SEARCH_RULES_EVENT.EDIT_SEARCH, {\n\t\t\t\tsearchCriteria: {\n\t\t\t\t\trules: searchCriteriaCache.rules,\n\t\t\t\t\tmatchCriteria: searchCriteriaCache.matchCriteria,\n\t\t\t\t},\n\t\t\t\t...viewOptions,\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:search#search:advanced\n\t * @param {base64} str Advanced search string\n\t * @param {number} page Page number\n\t * @fires module:search#search:advanced:show\n\t * @fires module:search#search:gallery:show\n\t * @fires module:asset#asset:cursor:cacheFetch\n\t */\n\tdispatcher.on('search:advanced', function (str, page) {\n\t\tif (str) {\n\t\t\t// cache the rules for later editing\n\t\t\tconst searchCriteria = decodeBase64Rules(str);\n\n\t\t\tsearchCriteriaCache.update(\n\t\t\t\tconvertRulesToRuleObjects(searchCriteria),\n\t\t\t\tsearchCriteria.matchCriteria,\n\t\t\t);\n\n\t\t\tdispatcher.trigger('asset:gallery:show', {\n\t\t\t\tconditions: searchCriteria,\n\t\t\t\tpage: parseInt(page) || 1,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// with no route argument, just show the search rules view\n\t\tdispatcher.trigger('search:advanced:show', {\n\t\t\trouteMatch: 'search/advanced',\n\t\t\tisRouted: window.location.hash.includes('search/advanced'),\n\t\t});\n\t});\n\n\t/**\n\t * @event module:search#search:attribute:value\n\t * @param {string} attrName Attribute name\n\t * @param {mixed} value Attribute value\n\t */\n\tdispatcher.on('search:attribute:value', function (attrName, value) {\n\t\tif (!Array.isArray(value)) value = [value];\n\n\t\t// create a rule for each value passed\n\t\tconst rules =\n\t\t\tattrName === 'folderPath'\n\t\t\t\t? value.map((val) =>\n\t\t\t\t\t\tcreateCategoryIdRule({id: val, type: CategoryType.ONLY_RECURSIVE}),\n\t\t\t\t )\n\t\t\t\t: value.map((val) =>\n\t\t\t\t\t\tcreateMetadataRule({\n\t\t\t\t\t\t\ttype: MetadataType.MATCH_EXACTLY, // DAM-13419 - use \"AND\" // DAM-13880 - no no, use \"EXACT\"\n\t\t\t\t\t\t\tname: attrName,\n\t\t\t\t\t\t\tvalue: val,\n\t\t\t\t\t\t}),\n\t\t\t\t );\n\n\t\tconst searchStr = encodeBase64Rules({\n\t\t\t...convertToRules(...rules),\n\t\t\tmatchCriteria: SearchMatchCriteria.OR,\n\t\t});\n\n\t\t// send to advanced search\n\t\tappRouter.navigate(`search/advanced/${searchStr}`, true);\n\t});\n\n\tif (canSearchAttributeHistory()) {\n\t\tNS.rules.attributeHistory = new SearchAttributeHistoryRulesCollection([{}]);\n\n\t\t/**\n\t\t * @event module:search#search:attributeHistory:show\n\t\t * @param {object} viewOptions View options\n\t\t */\n\t\tdispatcher.on(\n\t\t\t'search:attributeHistory:show',\n\t\t\tfunction (viewOptions) {\n\t\t\t\t// We are opting to remove all others. The workflow currently doesn't make sense to have an\n\t\t\t\t// advanced search paired with any other views - that is why we are using set instead of add\n\t\t\t\timport(\n\t\t\t\t\t/* webpackChunkName: \"view-searchAttributeHistoryRules\" */ './views/searchAttributeHistoryRulesView'\n\t\t\t\t).then((View) => {\n\t\t\t\t\tmodalViews.add({\n\t\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\t\tshowActions: ['search:attributeHistory:show'],\n\t\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t},\n\t\t\twindow,\n\t\t);\n\n\t\t/**\n\t\t * @event module:search#search:attributeHistory\n\t\t * @param {base64} str Advanced search string\n\t\t * @param {number} page Page number\n\t\t * @fires module:search#search:attribute:history:show\n\t\t */\n\t\tdispatcher.on('search:attributeHistory', function (str, page) {\n\t\t\tconst {hash} = window.location;\n\n\t\t\tif (str) {\n\t\t\t\tdispatcher.trigger('asset:gallery:show', {\n\t\t\t\t\tconditions: decodeBase64Rules(str),\n\t\t\t\t\tpage: parseInt(page) || 1,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// with no route argument, just show the search rules view\n\t\t\tdispatcher.trigger('search:attributeHistory:show', {\n\t\t\t\tcollection: NS.rules.attributeHistory,\n\t\t\t\trouteMatch: 'search/attribute/history',\n\t\t\t\tisRouted: hash.includes('search/attribute/history'),\n\t\t\t\tNS: NS,\n\t\t\t});\n\t\t});\n\n\t\t/**\n\t\t * @event mdule:search#search:attribute:history:submit\n\t\t */\n\t\tdispatcher.on(\n\t\t\t'search:attributeHistory:submit',\n\t\t\tfunction () {\n\t\t\t\tvar str = NS.rules.attributeHistory.toStr();\n\t\t\t\tif (str !== 'e30=') {\n\t\t\t\t\tappRouter.navigate('search/attribute/history/' + str, true);\n\t\t\t\t} else {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'app:alert',\n\t\t\t\t\t\ti18n.t('i18n.searchParametersNotProvided'),\n\t\t\t\t\t\ti18n.t('i18n.error'),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t\twindow,\n\t\t);\n\t}\n\n\t/**\n\t * @event module:search#search:gallery:show\n\t * @param {object} viewOptions View options\n\t * @see module:search#search:advanced\n\t * @see module:search#search:basic\n\t * @see module:search#search:exact\n\t */\n\tdispatcher.on(\n\t\t'search:gallery:show',\n\t\tfunction (viewOptions) {\n\t\t\tappViews.add({\n\t\t\t\tviewClass: SearchGalleryView,\n\t\t\t\tshowActions: ['search:gallery:show'],\n\t\t\t\tviewOptions: viewOptions,\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:search#search:basic\n\t * @param {string} str Search string\n\t * @param {number} page Page number\n\t * @fires module:search#search:gallery:show\n\t * @fires module:asset#asset:cursor:cacheFetch\n\t */\n\tdispatcher.on('search:basic', function (str, page) {\n\t\tstr = (str || '').trim();\n\n\t\tlet escapedStr = str;\n\t\tlet unescapedStr = str;\n\t\t// Already escaped\n\t\tif (isEscapedURI(str)) {\n\t\t\tunescapedStr = unescapeURI(str);\n\t\t} else {\n\t\t\t// Bacnkbone will strip the \"%20\" and put the space back and push it into history\n\t\t\t// which will require two browser back button clicks to get back from\n\t\t\tescapedStr = escapeURI(str).replace(/%20/g, ' ');\n\t\t}\n\n\t\tlet exactMatch = false;\n\t\tlet hash = window.location.hash;\n\t\tconst pageStr = _.isNumeric(page) ? `/page/${page}` : '';\n\t\tconst searchRoute = `search/basic/${escapedStr}${pageStr}`;\n\n\t\t// HTML-1079: If this 'basic' search is enclosed in single or double quotes, it's really an 'exact' search.\n\t\texactMatch = str.match(/^\"(.*)\"$/) || str.match(/^'(.*)'$/);\n\t\tif (exactMatch) {\n\t\t\tdispatcher.trigger('search:exact', exactMatch[1], page);\n\t\t\treturn;\n\t\t}\n\n\t\t// TODO: i18n this 'basic' string ... i18n/translation for route path elements in general.\n\t\tif (hash != `#${searchRoute}`) {\n\t\t\t// No trigger - just fixing the route\n\t\t\tappRouter.navigate(searchRoute);\n\t\t\thash = `#${searchRoute}`;\n\t\t}\n\n\t\t// Trigger search mode\n\t\tsearchModuleStore.setMode(SearchMode.BASIC);\n\n\t\tif (pageStr.length === 0) hash += '/page/1';\n\n\t\tconst ruleItems = [createKeywordRule({keyword: str})];\n\n\t\t// see below, if we match an asset id we will be switching to `OR`\n\t\tlet matchCriteria = SearchMatchCriteria.AND;\n\n\t\t// brought over from search assets collection\n\t\t// DAM-10194 - if simpleAssetIdSearchEnabled is true, AND search is an integer, also search by asset id\n\t\tif (simpleAssetIdSearchEnabled()) {\n\t\t\tif (\n\t\t\t\t// must be a positive number non zero number\n\t\t\t\tisStringValidInteger(str, {\n\t\t\t\t\tmin: 1,\n\t\t\t\t})\n\t\t\t) {\n\t\t\t\t// add search rule for assetId\n\t\t\t\truleItems.push(\n\t\t\t\t\tcreateMetadataRule({\n\t\t\t\t\t\ttype: MetadataType.MATCH_ALL_WILD_CARDS,\n\t\t\t\t\t\tname: 'assetId',\n\t\t\t\t\t\tvalue: str,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tmatchCriteria = SearchMatchCriteria.OR;\n\t\t\t}\n\t\t}\n\n\t\tsearchCriteriaCache.update(ruleItems, matchCriteria);\n\n\t\tdispatcher.trigger('asset:gallery:show', {\n\t\t\tconditions: {\n\t\t\t\t...convertToRules(...ruleItems),\n\t\t\t\tmatchCriteria,\n\t\t\t},\n\t\t\tpage: parseInt(page) || 1,\n\t\t});\n\t});\n\n\t/**\n\t * @event module:search#search:exact\n\t * @param {string} str A complete string (with diacriticals) to search for\n\t * @fires module:search#search:gallery:show\n\t * @fires module:asset#asset:cursor:cacheFetch\n\t */\n\tdispatcher.on('search:exact', function (str, page) {\n\t\tstr = (str || '').trim();\n\n\t\tlet escapedStr = str;\n\t\tlet unescapedStr = str;\n\n\t\t// Already escaped\n\t\tif (isEscapedURI(str)) {\n\t\t\tunescapedStr = unescapeURI(str);\n\t\t} else {\n\t\t\t// Bacnkbone will strip the \"%20\" and put the space back and push it into history\n\t\t\t// which will require two browser back button clicks to get back from\n\t\t\tescapedStr = escapeURI(str).replace(/%20/g, ' ');\n\t\t}\n\n\t\tlet {hash} = window.location;\n\t\tconst pageStr = _.isNumeric(page) ? `/page/${page}` : '';\n\t\t// TODO: i18n this 'basic' string ... i18n/translation for route path elements in general.\n\t\tconst searchRoute = `search/exact/${escapedStr}${pageStr}`;\n\n\t\tif (hash !== `#${searchRoute}`) {\n\t\t\t// No trigger - just fixing the route\n\t\t\tappRouter.navigate(searchRoute);\n\t\t\thash = `#${searchRoute}`;\n\t\t}\n\n\t\tconst searchRule = createKeywordRule({\n\t\t\tkeyword: str,\n\t\t\ttype: KeywordType.MATCH_EXACTLY_INCLUDING_DIACRITICAL_MARKS,\n\t\t});\n\n\t\tsearchCriteriaCache.update([searchRule], SearchMatchCriteria.AND);\n\n\t\t// Trigger search mode\n\t\t// We stil lcall this mode `basic` since there is no other clear indication to the user of the destinction between the two modes\n\t\t// (other than they themselves searching with quotes or no quotes)\n\t\t// TODO: Ultimately I think this should trigger with \"exact\" - but we need to rewrite the code in our search widget view to treat\n\t\t// \"exact\" and \"basic\" as the same option in the search mode dropdown\n\t\tsearchModuleStore.setMode(SearchMode.BASIC);\n\n\t\tdispatcher.trigger('asset:gallery:show', {\n\t\t\tconditions: convertToRules(searchRule),\n\t\t\tpage: parseInt(page) || 1,\n\t\t});\n\t});\n\n\t// Video clips are enabled\n\tif (canSearchConstituentClips()) {\n\t\t// Clip collections\n\t\tNS.clips = new ClipSearchConstituentsCollection([]);\n\t\tNS.rules.clips = new ClipSearchRulesCollection([\n\t\t\t{\n\t\t\t\telementType: 101,\n\t\t\t\telementSubType1: 4,\n\t\t\t},\n\t\t]);\n\n\t\t/**\n\t\t * @event module:search#search:clip:gallery:show\n\t\t * @param {object} viewOptions View options\n\t\t */\n\t\tdispatcher.on(\n\t\t\t'search:clip:gallery:show',\n\t\t\tfunction (viewOptions) {\n\t\t\t\tappViews.add({\n\t\t\t\t\tviewClass: SearchConstituentsGalleryView,\n\t\t\t\t\tshowActions: ['search:clip:gallery:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t},\n\t\t\twindow,\n\t\t);\n\n\t\t/**\n\t\t * @event module:search#search:clip:basic\n\t\t * @param {string} str Search string\n\t\t * @param {number} page Page number\n\t\t * @fires module:search#search:clip:gallery:show\n\t\t * @fires module:asset#asset:cursor:cacheFetch\n\t\t */\n\t\tdispatcher.on('search:clip:basic', function (str, page) {\n\t\t\tstr = (str || '').trim();\n\n\t\t\tvar escapedStr = str,\n\t\t\t\tunescapedStr = str;\n\n\t\t\t// Already escaped\n\t\t\tif (isEscapedURI(str)) {\n\t\t\t\tunescapedStr = unescapeURI(str);\n\t\t\t} else {\n\t\t\t\tescapedStr = escapeURI(str);\n\t\t\t}\n\n\t\t\tvar clips = NS.clips,\n\t\t\t\thash = window.location.hash,\n\t\t\t\tpageStr = _.isNumeric(page) ? '/page/' + page : '',\n\t\t\t\t// TODO: i18n this 'basic' string ... i18n/translation for route path elements in general.\n\t\t\t\tsearchRoute = 'search/clip/basic/' + escapedStr + pageStr;\n\n\t\t\tif (hash != '#' + searchRoute) {\n\t\t\t\t// No trigger - just fixing the route\n\t\t\t\tappRouter.navigate(searchRoute);\n\t\t\t}\n\n\t\t\t// Trigger search mode\n\t\t\tsearchModuleStore.setMode(SearchMode.CLIP);\n\n\t\t\t// If we were just at this search, then don't reload it:\n\t\t\tif (clips.lastSearchRoute != hash) {\n\t\t\t\t// otherwise, carry on with 'clip'\n\t\t\t\tclips.lastSearchRoute = hash;\n\t\t\t\tclips.basic(unescapedStr, page);\n\t\t\t} else {\n\t\t\t\t// Cache the search selection for asset detail use\n\t\t\t\tdispatcher.trigger('asset:cursor:cacheFetch', clips);\n\t\t\t}\n\n\t\t\t// Show\n\t\t\tdispatcher.trigger('search:clip:gallery:show', {\n\t\t\t\tcollection: clips,\n\t\t\t\tviews: {\n\t\t\t\t\theader: {\n\t\t\t\t\t\tviewClass: SearchConstituentsGalleryHeaderView,\n\t\t\t\t\t\tcollection: clips,\n\t\t\t\t\t},\n\t\t\t\t\tassets: {\n\t\t\t\t\t\tviewClass: SearchConstituentsListView_clips,\n\t\t\t\t\t\tcollection: clips,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t\t/**\n\t\t * @event module:search#search:clip:basic:complete\n\t\t * @param {array} data Search data\n\t\t * @see module:search#search:clip:basic\n\t\t */\n\t\tdispatcher.on(\n\t\t\t'search:clip:basic:complete',\n\t\t\tfunction (data) {\n\t\t\t\t// console.log(data);\n\t\t\t},\n\t\t\twindow,\n\t\t);\n\t\t/**\n\t\t * @event module:search#search:clip:basic:error\n\t\t * @param {string} error Error string\n\t\t * @fires module:app#app:alert\n\t\t * @see module:search#search:clip:basic\n\t\t */\n\t\tdispatcher.on(\n\t\t\t'search:clip:basic:error',\n\t\t\tfunction (error) {\n\t\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t\t},\n\t\t\twindow,\n\t\t);\n\t} else {\n\t\t// Not available in the search mode dropdown\n\t\tdelete searchModes.clip;\n\t}\n\n\t// /**\n\t// * @event module:search#search:mode\n\t// * @param {string} modeStr Mode (basic, clip, color, exact)\n\t// * @fires module:search#search:mode:updated\n\t// */\n\t// dispatcher.on('search:mode', function (modeStr) {\n\t// \tvar changed = NS.mode !== modeStr;\n\t// \tif (changed) {\n\t// \t\tNS.mode = modeStr;\n\t// \t\tdispatcher.trigger('search:mode:updated', modeStr);\n\t// \t}\n\t// });\n\n\tdispatcher.on('search:mode:updated', (searchMode) => {\n\t\tNS.mode = searchMode;\n\t});\n\n\t/**\n\t * @event module:search#search:saved:delete\n\t * @param {Netx.Models.SavedSearch} model SavedSearch model\n\t * @fires module:app#app:confirm:delete\n\t * @see module:search#search:saved:delete:confirmed\n\t */\n\tdispatcher.on(\n\t\t'search:saved:delete',\n\t\tfunction (model) {\n\t\t\tdispatcher.trigger('app:confirm:delete', {\n\t\t\t\tmessage: i18n.t('i18n.deleteSavedSearchMessage'),\n\t\t\t\teventStr: 'search:saved:delete',\n\t\t\t\targs: [model],\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:search#search:saved:delete:confirmed\n\t * @param {Netx.Models.SavedSearch} model SavedSearch model\n\t * @fires module:search#search:saved:delete:error\n\t * @fires module:search#search:saved:delete:success\n\t * @see module:search#search:saved:delete\n\t */\n\tdispatcher.on('search:saved:delete:confirmed', function (model) {\n\t\tmodel.destroy({wait: true}).then(\n\t\t\tfunction () {\n\t\t\t\t// If we are currently viewing the deleted saved search - route to landing\n\t\t\t\tif (window.location.href.includes(model.getRoute())) {\n\t\t\t\t\tappRouter.navigate('search/saved', {trigger: true});\n\t\t\t\t}\n\t\t\t\tdispatcher.trigger('search:saved:delete:success', model);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger('search:saved:delete:error', err);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:search#search:saved:delete:error\n\t * @param {string} error Error string\n\t * @fires module:app#app:alert\n\t * @see module:search#search:saved:delete:confirmed\n\t */\n\tdispatcher.on(\n\t\t'search:saved:delete:error',\n\t\tfunction (error) {\n\t\t\tdispatcher.trigger('app:alert', error, 'Danger');\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:search#search:saved:delete:success\n\t * @param {Netx.Models.SavedSearch} model SavedSearch model\n\t * @fires module:notification#notification\n\t * @see module:search#search:saved:delete:confirmed\n\t */\n\tdispatcher.on(\n\t\t'search:saved:delete:success',\n\t\tfunction (model) {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'search',\n\t\t\t\tids: [model.id],\n\t\t\t\ttitle: i18n.t('i18n.savedSearchesUpdated', {\n\t\t\t\t\tdefaultValue: 'Saved searches updated',\n\t\t\t\t}),\n\t\t\t\tdescription: i18n.t('i18n.savedSearchSuccessfullyDeleted', {\n\t\t\t\t\tdefaultValue:\n\t\t\t\t\t\t'The saved search \"__search__\" was successfully deleted',\n\t\t\t\t\tsearch: model.get('title'),\n\t\t\t\t}),\n\t\t\t\ttype: 'success',\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:search#search:saved:landing:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'search:saved:landing:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-savedSearchLanding\" */ './views/savedSearchLandingView'\n\t\t\t).then((View) => {\n\t\t\t\tappViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['search:saved:landing:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:search#search:saved:landing:routed\n\t * @fires module:search#search:saved:landing\n\t */\n\tdispatcher.on('search:saved:landing:routed', function () {\n\t\tdispatcher.trigger('search:saved:landing');\n\t});\n\t/**\n\t * @event module:search#search:saved:landing\n\t * @fires module:search#search:saved:landing:show\n\t */\n\tdispatcher.on('search:saved:landing', function () {\n\t\tdispatcher.trigger('search:saved:landing:show', {\n\t\t\tcollection: NS.saved.all.deepClone(),\n\t\t});\n\t});\n\n\t/**\n\t * @event module:search#search:saved:routed\n\t * @param {number|string} id SavedSearch id\n\t * @fires module:search#search:saved:toGallery\n\t */\n\tdispatcher.on(\n\t\t'search:saved:routed',\n\t\tfunction (id, page) {\n\t\t\t// Ensure page\n\t\t\tpage = page || 1;\n\t\t\t//Netx.views.savedSearchesView.show(); // show searches in sidebar\n\t\t\tdispatcher.trigger('search:saved:toGallery', id, page);\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * Viewing saved searches\n\t * @event module:search#search:saved:toGallery\n\t * @param {number|string} id SavedSearch id\n\t * @fires module:asset#asset:cursor:cacheFetch\n\t */\n\tdispatcher.on(\n\t\t'search:saved:toGallery',\n\t\tfunction (id, page) {\n\t\t\tdispatcher.trigger('asset:gallery:show', {\n\t\t\t\tconditions: convertToRules(createSavedSearchRule(parseInt(id))),\n\t\t\t\tpage: parseInt(page) || 1,\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t//=================================\n\t//\t!EVENT LISTENERS\n\t//=================================\n\n\t/**\n\t * @event module:search#search:facet:option:deselect\n\t * @param {Netx.Models.SarchFacetModel} model SearchFacet\n\t * @fires module:search#search:facet:option:toggle\n\t */\n\tdispatcher.on('search:facet:option:deselect', function (model) {\n\t\tdispatcher.trigger('search:facet:option:toggle', model);\n\t});\n\n\t//=================================\n\t//\t!EVENT LISTENERS\n\t//=================================\n\t// After the module has run through its init routine,\n\t// stop listening for subsequent init events which would\n\t// cause duplicate listeners and instances to be created.\n\t$.when.apply(this, NS.bootstrap).then(function () {\n\t\tdelete NS.bootstrap;\n\t\tdispatcher.trigger('search:extend', NS);\n\t\tdispatcher.trigger('search:initialized', NS);\n\t});\n});\n//=================================\n//\t!INIT\n//=================================\n\nexport default {\n\tinitialize(opts) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tdispatcher.once('search:initialized', () => {\n\t\t\t\tresolve(routes);\n\t\t\t});\n\t\t\tdispatcher.trigger('search:init');\n\t\t});\n\t},\n};\n","export {default} from 'netxcore@netx/core-search/controller';\n\nimport {appRouter} from '@netx/core-next/instance/router';\nimport {\n\tsearchModuleStore,\n\tSearchMode,\n} from '@netx/core-search-next/moduleStore';\n\nimport SearchFacetsView from './views/searchFacetsView';\nimport SearchGalleryHeaderView from './views/searchGalleryHeaderView';\nimport AssetListView from '@netx/core-asset/views/assetListView';\n\nimport {isEscapedURI} from '@netx/core/utils/isEscapedURI';\nimport assetCustomConfig from '@netx/core-asset/config';\nimport AssetListViewTypeManagerView from '@netx/core-asset/views/assetListViewTypeManager';\nimport {escapeURI} from '@netx/core/utils/escapeURI';\nimport {unescapeURI} from '@netx/core/utils/unescapeURI';\n\n/** Search Module Extensions */\ndispatcher.once('search:extend', function (NS) {\n\t// Use faceted search\n\t// Filter mode is driven by the property `search.facet.filterMode` - default is quite literally \"DEFAULT\"\n\t// See Netx.Collections.SearchFacets\n\tNS.assets.useFacetedSearch(true);\n\n\t/**\n\t * @event module:search#search:facet:onOff\n\t * @param {collection} object Assets collection\n\t * @fires module:search#search:facets:on\n\t * @fires module:asset#search:facets:off\n\t */\n\tdispatcher.on('search:facet:onOff', function (collection) {\n\t\t// is this collection relevant to what is going on?\n\t\tif (collection.hasRouteContext && collection.hasRouteContext()) {\n\t\t\tvar hasOption = _.find(\n\t\t\t\tcollection.selection.searchFacets.models,\n\t\t\t\tfunction (facet, idx, all) {\n\t\t\t\t\treturn facet.get('options').length > 0;\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (hasOption) {\n\t\t\t\tdispatcher.trigger('search:facets:on');\n\t\t\t} else {\n\t\t\t\tdispatcher.trigger('search:facets:off');\n\t\t\t}\n\t\t}\n\t\t// else don't worry about it\n\t});\n\n\tdispatcher.on('search:facets:off', function () {\n\t\t// Netx.user.prefs.setUserPreference('showFacets', false);\n\t\t$('body').addClass('facets-off');\n\t});\n\tdispatcher.on('search:facets:on', function () {\n\t\t// Netx.user.prefs.setUserPreference('showFacets', true);\n\t\t$('body').removeClass('facets-off');\n\t});\n\n\t// THEM-1297 - new listener. Instead of triggering the search:exact directly from the search widget,\n\t// and not triggering the route, navigating here, everyone wins.\n\tdispatcher.on('search:typeahead:selected', function (searchStr) {\n\t\tconst escapedStr = escapeURI(searchStr).replace(/%20/g, ' ');\n\t\tappRouter.navigate('search/exact/' + escapedStr, {trigger: true});\n\t});\n\n\t// Old advanced search code from core until portal can join the react gallery\n\tdispatcher.off('search:advanced');\n\tdispatcher.on('search:advanced', function (str, page) {\n\t\tvar hash = window.location.hash;\n\n\t\t// portal does not offer an advanced search modal\n\t\tif (!str) return;\n\n\t\t// Ensure page\n\t\tpage = page || 1;\n\n\t\tvar assets = NS.assets;\n\n\t\t// If we weren't just at this search,\n\t\tif (assets.lastSearchRoute != hash) {\n\t\t\t// decode and perform the search.\n\t\t\tassets.lastSearchRoute = hash;\n\t\t\tassets.advanced(str, page);\n\t\t} else {\n\t\t\t// but if we were, don't reload it:\n\t\t\t// just cache the selection\n\t\t\tdispatcher.trigger('asset:cursor:cacheFetch', assets);\n\t\t}\n\n\t\tdispatcher.trigger('search:gallery:show', {\n\t\t\tcollection: NS.assets,\n\t\t\tNS: NS,\n\t\t\tviews: {\n\t\t\t\tfacets: {\n\t\t\t\t\tviewClass: SearchFacetsView,\n\t\t\t\t\tcollection: NS.assets.selection.searchFacets,\n\t\t\t\t},\n\t\t\t\theader: {\n\t\t\t\t\tviewClass: SearchGalleryHeaderView,\n\t\t\t\t\tcollection: NS.assets,\n\t\t\t\t},\n\t\t\t\tassets: {\n\t\t\t\t\tviewClass: AssetListView,\n\t\t\t\t\tcollection: NS.assets,\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\t});\n\n\t/**\n\t * @event module:search#search:basic\n\t * @param {string} str Search string\n\t * @param {number} page Page number\n\t * @fires module:search#search:gallery:show\n\t * @fires module:asset#asset:cursor:cacheFetch\n\t */\n\tdispatcher.off('search:basic');\n\tdispatcher.on('search:basic', function (str, page) {\n\t\t// overriding to put facets on the bottom\n\n\t\tdispatcher.trigger('search:facets:refresh');\n\n\t\tstr = (str || '').trim();\n\n\t\tvar escapedStr = str,\n\t\t\tunescapedStr = str;\n\n\t\t// Already escaped\n\t\tif (isEscapedURI(str)) {\n\t\t\tunescapedStr = unescapeURI(str);\n\t\t} else {\n\t\t\tescapedStr = escapeURI(str).replace(/%20/g, ' ');\n\t\t}\n\n\t\t// Vars\n\t\tvar assets = NS.assets,\n\t\t\texactMatch = false,\n\t\t\thash = window.location.hash,\n\t\t\tpageStr = _.isNumeric(page) ? '/page/' + page : '',\n\t\t\tsearchRoute = 'search/basic/' + escapedStr + pageStr;\n\n\t\t// HTML-1079: If this 'basic' search is enclosed in single or double quotes, it's really an 'exact' search.\n\t\texactMatch = str.match(/^\"(.*)\"$/) || str.match(/^'(.*)'$/);\n\t\tif (exactMatch) {\n\t\t\tdispatcher.trigger('search:exact', exactMatch[1], page);\n\t\t\treturn;\n\t\t}\n\n\t\t// TODO: i18n this 'basic' string ... i18n/translation for route path elements in general.\n\t\tif (hash != '#' + searchRoute) {\n\t\t\t// No trigger - just fixing the route\n\t\t\tappRouter.navigate(searchRoute);\n\t\t\thash = '#' + searchRoute;\n\t\t}\n\n\t\t// Trigger search mode\n\t\tsearchModuleStore.setMode(SearchMode.BASIC);\n\n\t\tif (pageStr.length === 0) {\n\t\t\thash += '/page/1';\n\t\t}\n\n\t\t// If we were just at this search, then don't reload it:\n\t\tif (assets.lastSearchRoute != hash) {\n\t\t\t// otherwise, carry on with 'basic'\n\t\t\tassets.lastSearchRoute = hash;\n\t\t\tassets.basic(unescapedStr, page);\n\t\t} else {\n\t\t\t// Cache the search selection for asset detail use\n\t\t\tdispatcher.trigger('asset:cursor:cacheFetch', assets);\n\t\t\t// DAM-19563 DAM-19485 - no fetch, make sure facets haven't been turned off\n\t\t\tdispatcher.trigger('search:facet:onOff', assets);\n\t\t}\n\n\t\t// Show\n\t\tdispatcher.trigger('search:gallery:show', {\n\t\t\tcollection: assets,\n\t\t\tviews: {\n\t\t\t\theader: {\n\t\t\t\t\tviewClass: SearchGalleryHeaderView,\n\t\t\t\t\tcollection: assets,\n\t\t\t\t},\n\t\t\t\tassets: {\n\t\t\t\t\tviewClass: AssetListView,\n\t\t\t\t\tcollection: assets,\n\t\t\t\t},\n\t\t\t\tfacets: {\n\t\t\t\t\tviewClass: SearchFacetsView,\n\t\t\t\t\tcollection: assets.selection.searchFacets,\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\t});\n\n\t/**\n\t * @event module:search#search:exact\n\t * @param {string} str A complete string (with diacriticals) to search for\n\t * @fires module:search#search:gallery:show\n\t * @fires module:asset#asset:cursor:cacheFetch\n\t */\n\tdispatcher.off('search:exact');\n\tdispatcher.on('search:exact', function (str, page) {\n\t\tstr = (str || '').trim();\n\n\t\tvar escapedStr = str,\n\t\t\tunescapedStr = str;\n\n\t\t// Already escaped\n\t\tif (isEscapedURI(str)) {\n\t\t\tunescapedStr = unescapeURI(str);\n\t\t} else {\n\t\t\t// Bacnkbone will strip the \"%20\" and put the space back and push it into history\n\t\t\t// which will require two browser back button clicks to get back from\n\t\t\tescapedStr = escapeURI(str).replace(/%20/g, ' ');\n\t\t}\n\n\t\tvar assets = NS.assets,\n\t\t\thash = window.location.hash,\n\t\t\tpageStr = _.isNumeric(page) ? '/page/' + page : '',\n\t\t\t// TODO: i18n this 'basic' string ... i18n/translation for route path elements in general.\n\t\t\tsearchRoute = 'search/exact/' + escapedStr + pageStr;\n\n\t\tif (hash !== '#' + searchRoute) {\n\t\t\t// No trigger - just fixing the route\n\t\t\tappRouter.navigate(searchRoute);\n\t\t\thash = '#' + searchRoute;\n\t\t}\n\n\t\t// Trigger search mode\n\t\t// We stil lcall this mode `basic` since there is no other clear indication to the user of the destinction between the two modes\n\t\t// (other than they themselves searching with quotes or no quotes)\n\t\t// TODO: Ultimately I think this should trigger with \"exact\" - but we need to rewrite the code in our search widget view to treat\n\t\t// \"exact\" and \"basic\" as the same option in the search mode dropdown\n\t\tsearchModuleStore.setMode(SearchMode.BASIC);\n\n\t\t// If we were just at this search, then don't reload it:\n\t\tif (assets.lastSearchRoute != hash) {\n\t\t\t// otherwise, carry on with 'basic'\n\t\t\tassets.lastSearchRoute = hash;\n\t\t\tassets.exact(unescapedStr, page);\n\t\t} else {\n\t\t\t// Cache the search selection for asset detail use\n\t\t\tdispatcher.trigger('asset:cursor:cacheFetch', assets);\n\t\t}\n\n\t\tdispatcher.trigger('search:gallery:show', {\n\t\t\tcollection: assets,\n\t\t\tviews: {\n\t\t\t\tfacets: {\n\t\t\t\t\tviewClass: SearchFacetsView,\n\t\t\t\t\tcollection: assets.selection.searchFacets,\n\t\t\t\t},\n\t\t\t\theader: {\n\t\t\t\t\tviewClass: SearchGalleryHeaderView,\n\t\t\t\t\tcollection: assets,\n\t\t\t\t},\n\t\t\t\tassets: {\n\t\t\t\t\tviewClass: assetCustomConfig.LIST_TYPES_ENABLED\n\t\t\t\t\t\t? AssetListViewTypeManagerView\n\t\t\t\t\t\t: AssetListView,\n\t\t\t\t\tcollection: assets,\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\t});\n\n\t/**\n\t * Viewing saved searches\n\t * @event module:search#search:saved:toGallery\n\t * @param {number|string} id SavedSearch id\n\t * @fires module:asset#asset:cursor:cacheFetch\n\t */\n\tdispatcher.off('search:saved:toGallery');\n\tdispatcher.on(\n\t\t'search:saved:toGallery',\n\t\tfunction (id, page) {\n\t\t\tid = parseInt(id);\n\t\t\t// Load this saved search, unless it's already loaded.\n\t\t\t// TODO: some kind of interface for determining if the current route is already loaded in searchAssets -- for\n\t\t\t// basic, exact, advanced and saved. The mess below only works for saved searches, and we really shouldn't be peeking\n\t\t\t// around inside an object's guts like this.\n\t\t\t//\n\t\t\t// ALSO: we don't ever fetch the saved search object for this search, and perhaps we should do that & give it to the view to work with.\n\t\t\t// Or more generally, create some standard relationship between a Netx.Model that describes a set of assets and the Netx.Collection that\n\t\t\t// fetches/pages those assets.\n\t\t\tvar assets = NS.assets,\n\t\t\t\thash = window.location.hash;\n\t\t\t// If we were just at this search, then don't reload it:\n\t\t\tif (assets.lastSearchRoute != hash) {\n\t\t\t\tassets.lastSearchRoute = hash;\n\t\t\t\tassets.saved(id, page); // fetch saved search.\n\t\t\t} else {\n\t\t\t\t// Cache the search selection for asset detail use\n\t\t\t\tdispatcher.trigger('asset:cursor:cacheFetch', assets);\n\t\t\t}\n\n\t\t\tPromise.all([\n\t\t\t\tassetCustomConfig.LIST_TYPES_ENABLED\n\t\t\t\t\t? import(\n\t\t\t\t\t\t\t/* webpackChunkName: \"view-savedSearchAssetList\" */ '@netx/core-asset/views/assetListViewTypeManager'\n\t\t\t\t\t )\n\t\t\t\t\t: import(\n\t\t\t\t\t\t\t/* webpackChunkName: \"view-savedSearchAssetList\" */ '@netx/core-asset/views/assetListView'\n\t\t\t\t\t ),\n\t\t\t\timport(\n\t\t\t\t\t/* webpackChunkName: \"view-savedSearchAssetList\" */ './views/searchFacetsView'\n\t\t\t\t),\n\t\t\t\timport(\n\t\t\t\t\t/* webpackChunkName: \"view-savedSearchAssetList\" */ './views/searchGalleryHeaderView'\n\t\t\t\t),\n\t\t\t]).then((imports) => {\n\t\t\t\tconst [AssetListView, SearchFacetsView, SearchGalleryHeaderView] =\n\t\t\t\t\timports;\n\n\t\t\t\tdispatcher.trigger('search:gallery:show', {\n\t\t\t\t\tcollection: NS.assets,\n\t\t\t\t\tNS: NS,\n\t\t\t\t\tviews: {\n\t\t\t\t\t\tfacets: {\n\t\t\t\t\t\t\tviewClass: SearchFacetsView.default,\n\t\t\t\t\t\t\tcollection: NS.assets.selection.searchFacets,\n\t\t\t\t\t\t},\n\t\t\t\t\t\theader: {\n\t\t\t\t\t\t\tviewClass: SearchGalleryHeaderView.default,\n\t\t\t\t\t\t\tcollection: NS.assets,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tassets: {\n\t\t\t\t\t\t\tviewClass: AssetListView.default,\n\t\t\t\t\t\t\tcollection: NS.assets,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n}); // search:extend\n","/**\n * Permissions module\n *\n * @module permissions\n */\n\nimport Netx from '@netx/core/bootstrap';\nimport {modalViews} from '@netx/core-app/singletons/modalViews';\n\n// Models\nimport AssetModel from '@netx/core-asset/models/asset';\nimport CategoryModel from '@netx/core-category/models/category';\nimport AssetPermissionModel from './models/assetPermission';\nimport PermissionModel from './models/permission';\n\n// Views\nimport AssetPermissionEditView from './views/assetPermissionEditView';\n\nimport customConfig from './config';\nimport {targetIds as permissionTargetIds} from './properties/assetPermissions';\n\nimport routes from './routes';\n\n// MODULE INITIALIZATION\n// =====================\n// All collection and model instances and event listeners\n// are created when the module is initialized.\n\n/**\n * Init handler\n * @event {@link module:permissions#permissions:init|permissions:init}\n * @param {object} options Normally undefined, but a manual init trigger may override properties of Netx.custom.permissions here.\n */\ndispatcher.once('permissions:init', function (options) {\n\tvar NS = (Netx.permissions = {});\n\t// Check for module options\n\tNS.options = options = _.extend({}, customConfig, options);\n\n\t// PUBLIC MODELS & COLLECTIONS\n\t// ===========================\n\t// Object initializations, etc, go here.\n\n\t// No public models and collections for permissions at this time\n\n\t// BEGIN DISPATCHER EVENT LISTENERS\n\t// ================================\n\t// All other module event listeners are added here, inside the init handler.\n\n\t/**\n\t * @event module:permissions#permissions:edit:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'permissions:edit:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-permissionsEdit\" */ './views/permissionsEditView'\n\t\t\t).then((View) => {\n\t\t\t\tmodalViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['permissions:edit:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:permissions#permissions:edit\n\t * @param {Netx.Model} resource Resource model (attribute, savedCart etc)\n\t * @fires module:permissions#permissions:edit:show\n\t */\n\tdispatcher.on('permissions:edit', function (resource) {\n\t\tresource &&\n\t\t\tresource.getPermissions().then(function () {\n\t\t\t\tdispatcher.trigger('permissions:edit:show', {\n\t\t\t\t\tmodel: resource,\n\t\t\t\t\tcollection: resource.permissions,\n\t\t\t\t});\n\t\t\t});\n\t});\n\n\t/**\n\t * @event module:permissions#permissions:permission:new\n\t * @param {Netx.Collections.Permissions} permissions Permissions collection\n\t * @param {Netx.Model} resource Resource model (attribute, savedCart etc)\n\t */\n\tdispatcher.on('permissions:permission:new', function (permissions, resource) {\n\t\tpermissions && resource && permissions.addPermission();\n\t});\n\n\t/**\n\t * @event module:permissions#permissions:permission:delete\n\t * @param {object} viewOptions View options\n\t * @fires module:permissions#permissions:permission:delete:confirmed\n\t */\n\tdispatcher.on('permissions:permission:delete', function (permission) {\n\t\tdispatcher.trigger('permissions:permission:delete:confirmed', permission);\n\t});\n\t/**\n\t * @event module:permissions#permissions:permission:delete:confirmed\n\t * @param {object} viewOptions View options\n\t * @fires module:permissions#permissions:permission:delete:error\n\t * @fires module:permissions#permissions:permission:delete:success\n\t */\n\tdispatcher.on(\n\t\t'permissions:permission:delete:confirmed',\n\t\tfunction (permission) {\n\t\t\tif (permission.isNew()) {\n\t\t\t\tpermission.collection.remove(permission);\n\t\t\t\tdispatcher.trigger('permissions:permission:delete:success', permission);\n\t\t\t} else {\n\t\t\t\tpermission.destroy({wait: true}).then(\n\t\t\t\t\tfunction () {\n\t\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t\t'permissions:permission:delete:success',\n\t\t\t\t\t\t\tpermission,\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\tfunction (err) {\n\t\t\t\t\t\tdispatcher.trigger('permissions:permission:delete:error', err);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n\t/**\n\t * @event module:permissions#permissions:permission:delete:error\n\t * @param {string} error Error string\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'permissions:permission:delete:error',\n\t\tfunction (error) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:permissions#permissions:permission:delete:success\n\t * @param {Netx.Models.Permission} permission Permission model\n\t */\n\tdispatcher.on(\n\t\t'permissions:permission:delete:success',\n\t\tfunction (permission) {\n\t\t\t//\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:permissions#permissions:permission:edit:cancel\n\t * @param {Netx.Models.Permission} permission Permission model\n\t * @fires module:permission#permissions:permission:edit:cancel:confirmed\n\t */\n\tdispatcher.on('permissions:permission:edit:cancel', function (permission) {\n\t\tdispatcher.trigger(\n\t\t\t'permissions:permission:edit:cancel:confirmed',\n\t\t\tpermission,\n\t\t);\n\t});\n\t/**\n\t * @event module:permissions#permissions:permission:edit:cancel:confirmed\n\t * @param {Netx.Models.Permission} permission Permission model\n\t */\n\tdispatcher.on(\n\t\t'permissions:permission:edit:cancel:confirmed',\n\t\tfunction (permission) {\n\t\t\tif (!permission || !permission.isNew()) return;\n\n\t\t\tpermission.collection && permission.collection.remove(permission);\n\t\t},\n\t);\n\n\t////////////////////////////////////\n\t// Asset permission\n\t////////////////////////////////////\n\t/**\n\t * @event module:permissions#ermissions:asset:delete\n\t * @param {module:permission/models/assetPermission} permission Asset permission\n\t * @fires module:app#app:confirm:delete\n\t */\n\tdispatcher.on('permissions:asset:delete', function (permission, e) {\n\t\tdispatcher.trigger('app:confirm:delete', {\n\t\t\tmessage: i18n.t('i18n.confirmDeletePermissionRecord'),\n\t\t\teventStr: 'permissions:asset:delete',\n\t\t\targs: [permission],\n\t\t});\n\t});\n\t/**\n\t * @event module:permissions#permissions:asset:delete:confirmed\n\t * @param {module:permissions/models/assetPermission} permission Asset permission\n\t * @fires module:permissions#permissions:asset:delete:error\n\t * @fires module:permissions#permissions:asset:delete:success\n\t */\n\tdispatcher.on('permissions:asset:delete:confirmed', function (permission) {\n\t\tpermission.destroy({wait: true}).then(\n\t\t\tfunction () {\n\t\t\t\tdispatcher.trigger('permissions:asset:delete:success', permission);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\t// Error\n\t\t\t\tdispatcher.trigger('permissions:asset:delete:success', err, permission);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:permissions#permissions:asset:delete:error\n\t * @param {string} error Error string\n\t * @param {module:permissions/models/assetPermission} permission Asset permission\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'permissions:asset:delete:error',\n\t\tfunction (error, permission) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:permissions#permissions:asset:delete:success\n\t * @param {module:permissions/models/assetPermission} permission Asset permission\n\t * @fires module:notification#notification\n\t */\n\tdispatcher.on(\n\t\t'permissions:asset:delete:success',\n\t\tfunction (permission) {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\tids: [permission.get('targetId')],\n\t\t\t\ttitle: i18n.t('i18n.permissionsUpdated'),\n\t\t\t\tdescription: i18n.t('i18n.permissionDeleted'),\n\t\t\t\ttype: 'success',\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:permissions#permissions:asset:edit:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'permissions:asset:edit:show',\n\t\tfunction (viewOptions) {\n\t\t\tmodalViews.add({\n\t\t\t\tviewClass: AssetPermissionEditView,\n\t\t\t\tshowActions: ['permissions:asset:edit:show'],\n\t\t\t\tviewOptions: viewOptions,\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:permissions#permissions:asset:edit\n\t * @param {module:permissions/models/assetPermission|number} permission Asset permission or id\n\t * @fires module:permissions#permissions:asset:edit:show\n\t * @see module:permissions#permissions:asset:edit:confirmed\n\t * @todo I am so ugly\n\t */\n\tdispatcher.on('permissions:asset:edit', function (permission) {\n\t\tvar promise;\n\n\t\tif (_.isNumber(permission)) {\n\t\t\tpermission = new PermissionModel({entryId: parseInt(permission)});\n\t\t\tpromise = permission.fetch();\n\t\t}\n\n\t\t$.when(promise || true).then(function () {\n\t\t\t// Try and get a target if enough information is available\n\t\t\tvar target = parseInt(permission.get('target')),\n\t\t\t\ttargetId = parseInt(permission.get('targetId'));\n\n\t\t\tif (targetId) {\n\t\t\t\tvar permissionTarget;\n\t\t\t\tswitch (target) {\n\t\t\t\t\tcase permissionTargetIds.asset:\n\t\t\t\t\t\tpermissionTarget = new AssetModel({assetId: targetId});\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase permissionTargetIds.category:\n\t\t\t\t\t\tpermissionTarget = new CategoryModel({categoryid: targetId});\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tpromise = permissionTarget.fetch();\n\t\t\t}\n\n\t\t\t$.when(promise || true).then(function () {\n\t\t\t\tdispatcher.trigger('permissions:asset:edit:show', {\n\t\t\t\t\tmodel: permission,\n\t\t\t\t\ttarget: permissionTarget,\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t});\n\t/**\n\t * @event module:permissions#permissions:asset:edit:confirmed\n\t * @param {object} values Asset permission data\n\t * @param {module:permissions/models/assetPermission} permission Asset permission\n\t * @fires module:permissions#permissions:asset:edit:error\n\t * @fires module:permissions#permissions:asset:edit:success\n\t */\n\tdispatcher.on(\n\t\t'permissions:asset:edit:confirmed',\n\t\tfunction (values, permission) {\n\t\t\tvar isNew = permission.isNew();\n\t\t\t// Set and save\n\t\t\tpermission.parseAndSave(values).then(\n\t\t\t\tfunction () {\n\t\t\t\t\tpermission.fetch();\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'permissions:asset:edit:success',\n\t\t\t\t\t\tpermission,\n\t\t\t\t\t\tisNew,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\tdispatcher.trigger('permissions:asset:edit:error', err, permission);\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t);\n\t/**\n\t * @event module:permissions#permissions:asset:edit:error\n\t * @param {string} error Error string\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'permissions:asset:edit:error',\n\t\tfunction (error) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:permissions#permissions:asset:edit:success\n\t * @param {module:permissions/models/assetPermission} permission Asset permission\n\t * @param {boolean} isNew New permission\n\t * @fires module:notification#notification\n\t */\n\tdispatcher.on(\n\t\t'permissions:asset:edit:success',\n\t\tfunction (permission, isNew) {\n\t\t\tdispatcher.trigger('notification', {\n\t\t\t\tmodule: 'asset',\n\t\t\t\tids: [permission.get('targetId')],\n\t\t\t\ttitle: i18n.t('i18n.permissionUpdated'),\n\t\t\t\ttype: 'success',\n\t\t\t\tdescription: isNew\n\t\t\t\t\t? i18n.t('i18n.permissionCreatedSuccessfully')\n\t\t\t\t\t: i18n.t('i18n.permissionUpdatedSuccessfully'),\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:permissions#permissions:asset:new\n\t * @param {module:permissions/collections/assetPermissions} permissions Asset permissions\n\t * @param {module:asset/models/asset} asset Asset\n\t * @fires module:permissions#permissions:asset:edit\n\t */\n\tdispatcher.on('permissions:asset:new', function (permissions, asset) {\n\t\tdispatcher.trigger(\n\t\t\t'permissions:asset:edit',\n\t\t\tnew AssetPermissionModel({\n\t\t\t\ttarget: permissionTargetIds.asset,\n\t\t\t\ttargetId: asset && asset.id,\n\t\t\t\ttargetLabel: asset && asset.get('name'),\n\t\t\t}),\n\t\t);\n\t});\n\n\t/**\n\t * @event module:permissions#permissions:asset:reorder\n\t * @param {module:permissions/collections/assetPermissions} permissions Permissions\n\t * @param {module:permissions/models/assetPermission} permission Permission to reposition\n\t * @param {number} position Position\n\t * @fires module:permissions#permissions:asset:reorder:error\n\t * @fires module:permissions#permissions:asset:reorder:success\n\t */\n\tdispatcher.on(\n\t\t'permissions:asset:reorder',\n\t\tfunction (permissions, permission, position) {\n\t\t\tpermissions.setPosition(permission, position).then(\n\t\t\t\tfunction () {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'permissions:asset:reorder:success',\n\t\t\t\t\t\tpermissions,\n\t\t\t\t\t\tpermission,\n\t\t\t\t\t\tposition,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tfunction (error) {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'permissions:asset:reorder:error',\n\t\t\t\t\t\terror,\n\t\t\t\t\t\tpermissions,\n\t\t\t\t\t\tpermission,\n\t\t\t\t\t\tposition,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t);\n\t/**\n\t * @event module:permissions#permissions:asset:reorder:error\n\t * @param {string} error Error\n\t * @param {module:permissions/collections/assetPermissions} permissions Permissions\n\t * @param {module:permissions/models/assetPermission} permission Permission to reposition\n\t * @param {number} position Position\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'permissions:asset:reorder:error',\n\t\tfunction (error, permissions, permission, position) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t////////////////////////////////////\n\t// !Asset permission\n\t////////////////////////////////////\n\n\t////////////////////////////////////\n\t// Participants\n\t////////////////////////////////////\n\t/**\n\t * @event module:permissions#permissions:participant:delete\n\t * @param {module:permissions/models/participant} participant Participant\n\t * @fires module:permissions#permissions:participant:delete:confirmed\n\t */\n\tdispatcher.on('permissions:participant:delete', function (participant) {\n\t\t// DAM-13666 - auto confirm\n\t\t// dispatcher.trigger('app:confirm:delete', {\n\t\t// \tmessage : i18n.t('i18n.confirmParticipantDelete'),\n\t\t// \teventStr : 'permissions:participant:delete',\n\t\t// \targs : [ participant ]\n\t\t// } );\n\t\tdispatcher.trigger('permissions:participant:delete:confirmed', participant);\n\t});\n\t/**\n\t * @event module:permissions#permissions:participant:delete:confirmed\n\t * @param {module:permissions/models/participant} participant Participant\n\t * @fires module:permissions#permissions:participant:delete:error\n\t * @fires module:permissions#permissions:participant:delete:success\n\t */\n\tdispatcher.on(\n\t\t'permissions:participant:delete:confirmed',\n\t\tfunction (participant) {\n\t\t\tparticipant.destroy({wait: true}).then(\n\t\t\t\tfunction () {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'permissions:participant:delete:success',\n\t\t\t\t\t\tparticipant,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'permissions:participant:delete:error',\n\t\t\t\t\t\terr,\n\t\t\t\t\t\tparticipant,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t);\n\t/**\n\t * @event module:permissions#permissions:participant:delete:error\n\t * @param {string} error Error\n\t * @param {module:permissions/models/participant} participant Participant\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'permissions:participant:delete:error',\n\t\tfunction (error, participant) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:permissions#permissions:participant:delete:success\n\t * @param {module:permissions/models/participant} participant Participant\n\t */\n\tdispatcher.on(\n\t\t'permissions:participant:delete:success',\n\t\tfunction (participant) {\n\t\t\t//\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:permissions#permissions:participant:edit:show\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'permissions:participant:edit:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-participantEdit\" */ './views/participantEditView'\n\t\t\t).then((View) => {\n\t\t\t\tmodalViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['permissions:participant:edit:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:permissions#permissions:participant:edit\n\t * @param {module:permissions/models/participant} participant Participant\n\t * @param {module:permissions/collections/participants} participants Participants\n\t * @fires module:permissions#permissions:participant:edit:show\n\t */\n\tdispatcher.on(\n\t\t'permissions:participant:edit',\n\t\tfunction (participant, participants) {\n\t\t\tdispatcher.trigger('permissions:participant:edit:show', {\n\t\t\t\tmodel: participant,\n\t\t\t\tcollection: participants,\n\t\t\t});\n\t\t},\n\t);\n\t/**\n\t * @event module:permissions#permissions:participant:edit:confirmed\n\t * @param {object} data Participant data\n\t * @param {module:permissions/models/participant} participant Participant\n\t * @fires module:permissions#permissions:participant:edit:error\n\t * @fires module:permissions#permissions:participant:edit:success\n\t */\n\tdispatcher.on(\n\t\t'permissions:participant:edit:confirmed',\n\t\tfunction (data, participant) {\n\t\t\tvar isNew = participant.isNew();\n\n\t\t\tparticipant.parseAndSave(data).then(\n\t\t\t\tfunction () {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'permissions:participant:edit:success',\n\t\t\t\t\t\tparticipant,\n\t\t\t\t\t\tisNew,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tfunction (err) {\n\t\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t\t'permissions:participant:edit:error',\n\t\t\t\t\t\terr,\n\t\t\t\t\t\tparticipant,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t);\n\t/**\n\t * @event module:permissions#permissions:participant:edit:error\n\t * @param {string} error Error\n\t * @param {module:permissions/models/participant} participant Participant\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'permissions:participant:edit:error',\n\t\tfunction (error, participant) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:permissions#permissions:participant:edit:success\n\t * @param {module:permissions/models/participant} participant Participant\n\t * @param {boolean} isNew Is new\n\t */\n\tdispatcher.on(\n\t\t'permissions:participant:edit:success',\n\t\tfunction (participant, isNew) {\n\t\t\t//\n\t\t},\n\t\twindow,\n\t);\n\n\t/**\n\t * @event module:permissions#permissions:participant:add\n\t * @param {module:permissions/collections/participants} participants Participants\n\t * @param {object} data Participant data\n\t * @fires module:permissions#permissions:participant:add:error\n\t * @fires module:permissions#permissions:participant:add:success\n\t * @fires module:permissions#permissions:participant:edit:confirmed\n\t */\n\tdispatcher.on('permissions:participant:add', function (participants, data) {\n\t\tvar participant = participants.getParticipant(data.type, data.id);\n\t\tif (participant) {\n\t\t\tdispatcher.trigger(\n\t\t\t\t'permissions:participant:edit:confirmed',\n\t\t\t\tdata,\n\t\t\t\tparticipant,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tparticipants.addParticipant(data).then(\n\t\t\tfunction (participant) {\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'permissions:participant:add:success',\n\t\t\t\t\tparticipants,\n\t\t\t\t\tparticipant,\n\t\t\t\t);\n\t\t\t},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'permissions:participant:add:error',\n\t\t\t\t\terr,\n\t\t\t\t\tparticipants,\n\t\t\t\t\tdata,\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t});\n\t/**\n\t * @event module:permissions#permissions:participant:add:error\n\t * @param {string} error Error\n\t * @param {module:permissions/collections/participants} participants Participants\n\t * @param {object} data Participant data\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'permissions:participant:add:error',\n\t\tfunction (error, participants, data) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:permissions#permissions:participant:add:error\n\t * @param {module:permissions/collections/participants} participants Participants\n\t * @param {module:permissions/models/participant} participant Participant\n\t */\n\tdispatcher.on(\n\t\t'permissions:participant:add:error',\n\t\tfunction (error, participants, participant) {\n\t\t\t//\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:permissions#permissions:participants:toggle\n\t * @param {module:permissions/collections/participants} participants Participants\n\t * @param {object} data Participant data\n\t */\n\tdispatcher.on(\n\t\t'permissions:participants:toggle',\n\t\tfunction (participants, data) {\n\t\t\tvar participant = participants.getParticipant(data.type, data.id);\n\t\t\tparticipant\n\t\t\t\t? dispatcher.trigger('permissions:participant:delete', participant)\n\t\t\t\t: dispatcher.trigger('permissions:participant:add', participants, data);\n\t\t},\n\t);\n\n\t/**\n\t * @event module:permissions#permissions:participants:\n\t * @param {object} viewOptions View options\n\t */\n\tdispatcher.on(\n\t\t'permissions:participants:show',\n\t\tfunction (viewOptions) {\n\t\t\timport(\n\t\t\t\t/* webpackChunkName: \"view-participants\" */ './views/participantsView'\n\t\t\t).then((View) => {\n\t\t\t\tmodalViews.add({\n\t\t\t\t\tviewClass: View.default,\n\t\t\t\t\tshowActions: ['permissions:participants:show'],\n\t\t\t\t\tviewOptions: viewOptions,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t\twindow,\n\t);\n\t/**\n\t * @event module:permissions#permissions:participants\n\t * @param {module:share/models/sharedResource} sharedResource Shared resource\n\t * @fires module:permissions#permissions:participants:show\n\t */\n\tdispatcher.on('permissions:participants', function (sharedResource) {\n\t\tsharedResource.getParticipants().then(\n\t\t\tfunction () {},\n\t\t\tfunction (err) {\n\t\t\t\tdispatcher.trigger(\n\t\t\t\t\t'permissions:participants:error',\n\t\t\t\t\terr,\n\t\t\t\t\tsharedResource,\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t\tdispatcher.trigger('permissions:participants:show', {\n\t\t\tcollection: sharedResource.participants,\n\t\t\tmodel: sharedResource,\n\t\t});\n\t});\n\t/**\n\t * @event module:permissions#permissions:participants:error\n\t * @param {string} error Error\n\t * @param {module:share/models/sharedResource} sharedResource Shared resource\n\t * @fires module:app#app:alert\n\t */\n\tdispatcher.on(\n\t\t'permissions:participants:error',\n\t\tfunction (error, sharedResource) {\n\t\t\tdispatcher.trigger('app:alert', error, i18n.t('i18n.error'));\n\t\t},\n\t);\n\n\t/**\n\t * @event module:permissions#permissions:participants:finished\n\t * @param {module:permissions/collections/participants} participants Participants\n\t * @param {module:share/models/sharedResource} sharedResource Shared resource\n\t * @param {object} data Data\n\t */\n\tdispatcher.on(\n\t\t'permissions:participants:finished',\n\t\tfunction (participants, sharedResource, data) {\n\t\t\tdata = data || {};\n\t\t\tif (data.sendMessage) {\n\t\t\t\tsharedResource.sendMessage(data.message, data.userSelector);\n\t\t\t}\n\n\t\t\tif (_.isFunction(participants.save)) {\n\t\t\t\tparticipants.save();\n\t\t\t}\n\t\t},\n\t);\n\t////////////////////////////////////\n\t// !Participants\n\t////////////////////////////////////\n\n\t// END DISPATCHER EVENT LISTENERS\n\n\t// LAST STEPS:\n\t// non-core extensions may listen on this event to hook into the init process:\n\tdispatcher.trigger('permissions:extend', NS);\n\t// Tell the module loader we're initialized.\n\tdispatcher.trigger('permissions:initialized', NS);\n});\n\nexport default {\n\tinitialize(opts) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tdispatcher.once('permissions:initialized', () => {\n\t\t\t\tresolve(routes);\n\t\t\t});\n\t\t\tdispatcher.trigger('permissions:init');\n\t\t});\n\t},\n};\n"],"names":["AssetDetailEnablers","assetHasClips","assetHasConstituents_clips","assetHasConstituents_keyframes","assetHasConstituents_layers","assetHasConstituents_pages","assetHasDetail","assetHasLinks","assetHasMetadata","assetHasPreview","assetHasProjects","assetHasReviews","assetHasVersions","assetHasViews","_","extend","Netx","assets","label","i18n","t","dispatcher","once","options","customConfig","d","NS","attributes","attributesSingleton","assetAttributes","assetAttributesSingleton","clipAttributes","clipAttributesSingleton","metadataMaps","MetadataMapsCollection","misc","MiniAssetsCollection","pending","pendingAssets","tasks","AssetTasksCollection","on","task","trigger","id","setGlobal","asset","currentAsset","bootstrap","AssetNextController","AssetGalleryController","getUserLevel","AssetExpirationController","AssetRepurposeController","AssetCustomThumbnailController","assetProjectEnabled","AssetProjectController","checksumEnabled","systemAttributes","code","key","name","attributeSets","attributeSetsSingleton","push","Promise","resolve","listTypeAttributeSetsSelections","each","LIST_TYPE","listType","this","AssetAttributeSetsSelectionCollection","userPrefKey","video","NxVideo","$","Deferred","promise","fetch","then","attrs","err","session","reject","getPreference","currentUserMeetsUserLevel","parseInt","collection","viewOptions","all","imported","AssetListView","AssetListViewTypeManagerView","modalViews","viewClass","LIST_TYPES_ENABLED","showActions","window","type","title","getRecentlyUploadedAssets","getMostViewedAssets","className","autoSort","headerTitle","newWindow","CoreModel","isUndefined","open","URL","appRouter","appViews","AssetDetailView","tab","model","assetCursor","routeMatch","RegExp","currentId","reset","error","console","warn","initialized","attributeHistoryEnabled","str","search","clearSearch","view","event","assetSet","AssetSetModel","assetSetTypes","ids","downloadOptions","DownloadOptionsCollection","downloadOptionTypes","values","assetAttributeDatum","parseAndSave","obj","completed","rtn","assetAttributeSetsSelection","module","description","attributeSetSelection","selectedIds","setSelected","selectedId","getCategoriesById","get","models","m","set","silent","View","default","tree","disabledCategories","selectedCategory","eventStr","catId","categoryId","AssetModel","linkCategory","addCategory","duplicateQueryPolicy","when","category","without","fromCategory","fromCat","toCat","isNumeric","Error","join","fromId","toId","moveCategory","message","args","isNaN","removeCategory","CHECKIN_ENABLE","checkout","batchJob","DOWNLOAD_ON_CHECKOUT","openDownloadLink","cancelCheckout","catch","setDisplay","uuid","constituent","assetId","clip","UserLevel","destroy","wait","AssetConstituentDownloadView_Clip","getDownloadOptions","opts","length","presets","data","processIdx","process","at","recursive","ASSET_SET_EVENT","_objectSpread","downloadOptionDefaults","family","getManualRepurposeOptions","DownloadOptionType","clipId","isNew","AssetConstituentClipModel","startTime","range","start","endTime","end","save","cursor","response","hash","location","bits","match","includes","curId","split","Number","forceSimple","newActions","assetThings","thing","isObject","_thing","namespace","enableTest","_enableTest","isArray","concat","action","testOnSync","userLevel","wrapper","route","ROUTE","matrix","RegisterLegacyAction","intersect","intersection","DETAIL_TABS","showEmpty","ucThing","ucfirst","FindLegacyAction","tabTest","isFunction","defer","assetModel","hidden","inMotion","PDF_VIEWER_EVENT","pdfUrl","page","conditions","convertToRules","createExpirationRule","menu","Menus","expirationDate","expire","date","unexpire","readOnly","visible","lock","unlock","getFirstPage","getLastPage","getNextPage","getPrevPage","gotoPage","REIMPORT_ENABLE","doFetch","newId","FETCH_ON_SAVE","fsViews","isRouted","render","trackTasks","tidyAssetList","add","size","versionModel","AssetVersionReactivateView","newVersion","memoText","reactivateAssetVersion","checkinErrors","progressTask","fetchVersions","AssetViewModel","imageId","assetView","views","AssetSetType","defaultValue","cacheInMediaRich","makeZoomTiles","exists","delay","progressUUID","apply","initialize","routes","off","scrollTop","detailTab","addClass","AssetQuickzoomView","removeClass","AssetZoomifyView","current","CategoryModel","list","CategoriesCollection","cache","currentCat","recent","RecentCategoriesCollection","CategoryAssetsCollection","categoryid","directory","repositorydirectory","_CategoryController","CategoryTreeNodesCollection","cat","debounce","out","find","a","remove","AssetSearchRulesCollection","elementType","elementValue1","elementSubType1","parentId","createCategoryIdRule","parentCat","e","_parentCat$constructo","preventDefault","stopPropagation","constructor","__isModel__","catInfo","canAddCategory","parentid","parentCategoryName","promises","getCategoryDetailsData","getAttributeProfile","beforeParentId","newParent","getDetails","assetCountStr","byteCountStr","subCategoryCountStr","assetCount","fileCount","count","subCategoryCount","byteCount","currentCatDestroyed","ancestorOfCurrent","indexOf","childView","target","tagName","searchRules_listAll","toStr","categories","getCategoryObjects","unshift","close","parentOnly","difference","pluck","childrenNeeded","fetchChildren","children","fetchCategory","searchStr","searchString","searchCategoryName","c","isOpen","useFacetedSearch","listenTo","syncedOnce","getCategoryBreadcrumbs","getCategoryAssets","CategoryGalleryView","catAssets","lastCategoryRoute","categoryViews","breadcrumbs","CategoryBreadCrumbView","header","CategoryGalleryHeaderView","categoryConfig","CategoryListView","undefined","facets","SearchFacetsView","selection","searchFacets","CategoryTreeNodesView","topLevelCategories","cmsConfig","path","$body","hasClass","appView","headerView","togglePages","ShareableTypes","rules","mode","searchModuleStore","SearchAssetsCollection","SavedSearchController","SearchRulesController","visualSearchEnabled","SearchVisualController","fetchSearchCriteria","saved","savedCartsSingleton","favorite","searchModes","searchOptions","searchAssets","idx","findWhere","val","splice","advancedSearchDate","addToAdvancedSearch_date","assetAttributeTypeIds","isForObjectType","removeFromAdvancedSearch_date","populateFromDateAttributes","dateSubTypes1_default","compact","map","assetExpirationEnabled","searchableSystemAttributes","SavedSearchDispatchListeners","createSavedSearchRule","SEARCH_RULES_EVENT","searchCriteria","searchCriteriaCache","matchCriteria","decodeBase64Rules","convertRulesToRuleObjects","attrName","value","Array","CategoryType","createMetadataRule","MetadataType","encodeBase64Rules","SearchMatchCriteria","canSearchAttributeHistory","attributeHistory","SearchAttributeHistoryRulesCollection","SearchGalleryView","escapedStr","trim","unescapedStr","isEscapedURI","unescapeURI","escapeURI","replace","exactMatch","pageStr","searchRoute","SearchMode","ruleItems","createKeywordRule","keyword","simpleAssetIdSearchEnabled","isStringValidInteger","min","searchRule","KeywordType","canSearchConstituentClips","clips","ClipSearchConstituentsCollection","ClipSearchRulesCollection","SearchConstituentsGalleryView","lastSearchRoute","basic","SearchConstituentsGalleryHeaderView","SearchConstituentsListView_clips","searchMode","href","getRoute","deepClone","hasRouteContext","facet","advanced","SearchGalleryHeaderView","exact","assetCustomConfig","imports","resource","getPermissions","permissions","addPermission","permission","AssetPermissionEditView","isNumber","PermissionModel","entryId","targetId","permissionTarget","permissionTargetIds","AssetPermissionModel","targetLabel","position","setPosition","participant","participants","getParticipant","addParticipant","sharedResource","getParticipants","sendMessage","userSelector"],"sourceRoot":""}