function()

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());
                }]
            }
        }
    });
}]);