in kahuna/public/js/search/index.js [59:316]
function($stateProvider, $urlMatcherFactoryProvider) {
const zeroWidthSpace = /[\u200B]/g;
function removeUtf8SpecialChars(val) {
return angular.isDefined(val) && val.replace(zeroWidthSpace, '');
}
$urlMatcherFactoryProvider.type('Query', {
encode: val => removeUtf8SpecialChars(val),
decode: val => removeUtf8SpecialChars(val),
//call decode value that includes zero-width-space character
is: val => angular.isDefined(val) && !zeroWidthSpace.test(val)
});
$stateProvider.state('search', {
// FIXME [1]: This state should be abstract, but then we can't navigate to
// it, which we need to do to access it's deeper / remembered chile state
url: '/?cropType&customRatio&defaultCropType',
template: searchTemplate,
deepStateRedirect: {
// Inject a transient $stateParams for the results state
// below to pick up and expose
fn: ['$dsr$', function($dsr$) {
const params = angular.extend({}, $dsr$.redirect.params, {
isDeepStateRedirect: true
});
return {state: $dsr$.redirect.state, params};
}]
},
controllerAs: 'ctrl',
controller: [
'$scope', '$window', '$stateParams', 'scrollPosition', 'panels', 'shortcutKeys', 'keyboardShortcut',
'panelService', 'cropSettings', 'mediaApi', 'storage', '$state',
function($scope, $window, $stateParams, scrollPosition, panels, shortcutKeys, keyboardShortcut,
panelService, cropSettings, mediaApi, storage, $state) {
const ctrl = this;
ctrl.canUpload = false;
ctrl.usePermissionsFilter = window._clientConfig.usePermissionsFilter;
ctrl.hasNotifications = window._clientConfig.announcements.length > 0;
mediaApi.canUserUpload().then(canUpload => {
ctrl.canUpload = canUpload;
});
cropSettings.set($stateParams);
ctrl.onLogoClick = () => {
mediaApi.getSession().then(session => {
const showPaid = session.user.permissions.showPaid ? session.user.permissions.showPaid : undefined;
const defaultNonFreeFilter = {
isDefault: true,
isNonFree: showPaid ? showPaid : false
};
storage.setJs("defaultNonFreeFilter", defaultNonFreeFilter, true);
$state.go('search.results', {nonFree: defaultNonFreeFilter.isNonFree});
window.dispatchEvent(new CustomEvent("logoClick", {
detail: {showPaid: defaultNonFreeFilter.isNonFree},
bubbles: true
}));
scrollPosition.resetToTop();
});
};
if ($state.current.name === 'search') {
mediaApi.getSession().then(session => {
storage.setJs('isNonFree', session.user.permissions.showPaid, true);
});
}
ctrl.collectionsPanel = panels.collectionsPanel;
ctrl.metadataPanel = panels.metadataPanel;
panelService.setAndSaveState($scope, 'collections', ctrl.collectionsPanel);
panelService.setAndSaveState($scope, 'metadata', ctrl.metadataPanel);
keyboardShortcut.bindTo($scope).add({
combo: shortcutKeys.get('metadataPanel'),
description: 'Toggle metadata panel',
callback: panels.metadataPanel.toggleHidden
});
keyboardShortcut.bindTo($scope).add({
combo: shortcutKeys.get('collectionsPanel'),
description: 'Toggle collections panel',
callback: panels.collectionsPanel.toggleHidden
});
}],
resolve: {
shortcutKeys: [function() {
// keep the shortcut keys here to stop overriding
return new Map([
['metadataPanel', 'm'],
['collectionsPanel', 'l']
]);
}],
panels: ['panelService', function(panelService) {
const collectionsPanel = panelService.createPanel(true);
const metadataPanel = panelService.createPanel(true);
return { collectionsPanel, metadataPanel };
}]
}
});
const extraQueryParamsNotUsedByGridDirectly = [
'expandPinboard', 'pinboardId', 'pinboardItemId'
];
$stateProvider.state('search.results', {
url: [
'search?{query:Query}',
'ids',
'since',
'nonFree',
'payType',
'uploadedBy',
'until',
'orderBy',
'dateField',
'takenSince',
'takenUntil',
'modifiedSince',
'modifiedUntil',
'hasRightsAcquired',
'hasCrops',
'syndicationStatus',
'persisted',
...extraQueryParamsNotUsedByGridDirectly // otherwise router will drop them
].join('&'),
// Non-URL parameters
params: {
// Routing-level property indicating whether the state has
// been loaded as part of a deep-state redirect. Note that
// this param gets cleared below so it should never reach
// controllers
isDeepStateRedirect: {value: false, type: 'bool'}
},
data: {
title: function(params) {
return params.query ? params.query : 'search';
}
},
resolve: {
// Helper state to determine whether this is just
// reloading a previous search state, or a new search
isReloadingPreviousSearch: ['$stateParams', function($stateParams) {
const isDeepStateRedirect = $stateParams.isDeepStateRedirect;
// *Clear* that transient routing-level flag so we
// *don't pollute the $stateParams
delete $stateParams.isDeepStateRedirect;
return isDeepStateRedirect;
}],
selection: ['orderedSetFactory', function(orderedSetFactory) {
return orderedSetFactory();
}],
// equivalent of `ctrl.imagesAll`
results: ['listFactory', function(listFactory) {
return listFactory();
}],
// equivalent of `ctrl.images`
compactResults$: ['results', function(results) {
return results.items$.
map(items => items.filter(angular.identity)).
shareReplay(1);
}],
// set of selected images resources
selectedImages$: ['selection', 'compactResults$', function(selection, compactResults$) {
return Rx.Observable.combineLatest(
selection.items$,
compactResults$,
(selectedItems, resultsImages) => {
return selectedItems.map(imageUri => {
return resultsImages.find(image => image.uri === imageUri);
});
}
).
distinctUntilChanged(angular.identity, Immutable.is).
shareReplay(1);
}],
selectedCollections: ['$stateParams', function($stateParams) {
const query = $stateParams.query || '';
const collections = getCollectionsFromQuery(query);
return collections;
}]
},
views: {
results: {
template: searchResultsTemplate,
controller: 'SearchResultsCtrl',
controllerAs: 'ctrl'
},
infoPanel: {
template: panelTemplate,
controller: 'GrInfoPanelCtrl',
controllerAs: 'ctrl',
resolve: {
selectedImagesList$: ['selectedImages$', function(selectedImages$) {
return selectedImages$.
map(selectedImages => selectedImages.toList()).
shareReplay(1);
}]
}
},
collectionPanel: {
template: collectionsPanelTemplate,
controller: 'GrCollectionsPanelCtrl',
controllerAs: 'ctrl'
},
multiDrag: {
template: `<div class="multidrag"></div>`,
controller: ['$scope', '$window', '$document', '$element', 'vndMimeTypes',
'selectedImages$',
function($scope, $window, $document, $element, vndMimeTypes,
selectedImages$) {
const windowDrag$ = Rx.DOM.fromEvent($window, 'dragstart');
const dragData$ = windowDrag$.
withLatestFrom(selectedImages$, (event, imagesList) => {
const images = imagesList.toJS();
const dt = event.dataTransfer;
return {images, dt};
});
const sub = dragData$.subscribe(({ images, dt }) => {
if (images.length > 0) {
const doc = $document[0];
const el = $element[0];
//creates an element to use as the drag icon
const dragImage = doc.createElement('div');
dragImage.classList.add('drag-icon');
const imageCount = doc.createElement('span');
imageCount.classList.add('drag-count');
imageCount.innerHTML = images.length;
// we do this as we cannot stringify Resource objects
const imageObjs = images.map(i => ({data: i.data}));
dragImage.appendChild(imageCount);
el.appendChild(dragImage);
dt.setData(
vndMimeTypes.get('gridImagesData'),
JSON.stringify(imageObjs));
dt.setDragImage(dragImage, 0, 0);
}
});
$scope.$on('$destroy', () => sub.dispose());
}]
}
}
});
}]);