UNPKG

qiprofile

Version:

Quantitative Imaging Profile ( QiPr) web application

268 lines (249 loc) 11.2 kB
define ['angular', 'lodash', 'underscore.string', 'uirouter', 'resources', 'subject', 'session', 'scan', 'registration'], (ng, _, _s) -> routes = ng.module( 'qiprofile.routes', ['ui.router', 'qiprofile.resources', 'qiprofile.subject', 'qiprofile.session', 'qiprofile.scan', 'qiprofile.registration'] ) routes.config [ '$stateProvider', '$urlMatcherFactoryProvider', '$locationProvider', ($stateProvider, $urlMatcherFactoryProvider, $locationProvider) -> # Match optional trailing slash. Cf. # https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions\ # #how-to-make-a-trailing-slash-optional-for-all-routes $urlMatcherFactoryProvider.strictMode(false) $stateProvider # The top-level state. This abstract state is the ancestor # of all other states. It holds the url prefix. There are # two ui-views in the index.html file, main and goHome. This # top-level state formats the goHome navigation button. The # main ui-view is formatted by child states. Since the main # ui-view is in the root context, it is referenced by the # child state as main@. .state 'quip', url: '/quip?project' abstract: true resolve: # The project is shared by all states. # TODO - determine the default in a property. project: ($stateParams) -> $stateParams.project or 'QIN' views: # The home button. home: templateUrl: '/partials/home-btn.html' controller: 'HomeCtrl' # The help button. help: templateUrl: '/partials/help-btn.html' controller: 'HelpCtrl' # The home landing page. .state 'quip.home', url: '' resolve: Resources: 'Resources' collections: (Resources, project) -> # The selection criterion is the project name. condition = project: project # Delegate to the ImagingCollection resource. Resources.ImagingCollection.query(condition) views: 'main@': templateUrl: '/partials/collection-list.html' controller: 'CollectionListCtrl' # The subject list page. .state 'quip.subjects', url: '/subjects' resolve: Subject: 'Subject' subjects: (Subject, project) -> # The selection criterion is the project name. condition = project: project # The id is always fetched. In addition, the # project/collection/number secondary key is fetched. # No other fields are fetched. fields = ['project', 'collection', 'number'] # Delegate to the resource. Subject.query(condition, fields) collections: ($state, subjects) -> # The sorted collection names, with duplicates removed. _.chain(subjects).map('collection').uniq().value().sort() views: 'main@': templateUrl: '/partials/subject-list.html' controller: 'SubjectListCtrl' # The collection abstract state. .state 'quip.collection', abstract: true url: '/:collection' resolve: collection: ($stateParams) -> _s.capitalize($stateParams.collection) # The Collection Detail state. .state 'quip.collection.detail', url: '' resolve: Subject: 'Subject' # TODO - rename charting to subjects. charting: (Subject, project, collection) -> # The selection criterion is the project and collection. condition = {project: project, collection: collection} # The id is always fetched. In addition, the # project/collection/number secondary key and # encounters are fetched. No other field is fetched. fields = ['project', 'collection', 'number', 'encounters'] # Delegate to the resource. Subject.query(condition, fields) views: 'main@': templateUrl: '/partials/collection-detail.html' controller: 'CollectionDetailCtrl' # The subject state. .state 'quip.collection.subject', url: '/subject/{subject:[0-9]+}?id' resolve: subject: ($stateParams, Subject, project, collection) -> # The search condition object determnined as follows: # * If the subject id is available, then the search # condition is the {id} primary key. # * Otherwise, the search condition is the # {project, collection, number} secondary key. condition = if $stateParams.id? id: $stateParams.id else # Return the secondary key. project: project collection: collection number: parseInt($stateParams.subject) # Fetch the subject. Subject.find(condition) views: 'main@': templateUrl: '/partials/subject-detail.html' controller: 'SubjectDetailCtrl' # The session state. # The session state parameter is the session number. .state 'quip.collection.subject.session', url: '/session/{session:[0-9]+}' resolve: session: (subject, $stateParams, Session) -> number = parseInt($stateParams.session) if number >= subject.sessions.length throw new ReferenceError( "Subject #{ subject.number } Session #{ number } was" + "not found" ) # Grab the subject session embedded object. session = subject.sessions[number - 1] # If the session has a scans array, then the detail was already # fetched and we are done. Otherwise, fetch the detail. if session.scans? session else # The session must have a detail foreign key. if not session.detail? throw new ReferenceError( "Subject #{ subject.number } Session #{ session.number }" + " does not have detail" ) # Fetch the session detail. Session.detail(session) views: 'main@': templateUrl: '/partials/session-detail.html' controller: 'SessionDetailCtrl' # The scan state. # The scan state parameter is the scan number. .state 'quip.collection.subject.session.scan', abstract: true url: '/scan/:scan' resolve: scan: (session, $stateParams, Scan) -> number = parseInt($stateParams.scan) Scan.find(session, number) # The scan volume state. # The volume state parameter is the volume number. .state 'quip.collection.subject.session.scan.volume', abstract: true url: '?volume' resolve: imageSequence: (scan) -> scan volume: ($stateParams) -> if $stateParams.volume? then parseInt($stateParams.volume) # TODO - uncomment to enable XTK. # # The scan 3D volume detail state. # .state 'quip.collection.subject.session.scan.volume.detail', # url: '' # views: # 'main@': # templateUrl: '/partials/volume-display.html' # controller: 'VolumeDisplayCtrl' # The Scan Detail state. .state 'quip.collection.subject.session.scan.volume.slice', url: '?slice' resolve: slice: ($stateParams) -> if $stateParams.slice? then parseInt($stateParams.slice) timeSeries: (subject, session, scan) -> if not scan.timeSeries? throw new ReferenceError( " #{ scan.title } does not have a time series" ) # Resolve to the time series. scan.timeSeries views: 'main@': templateUrl: '/partials/slice-display.html' controller: 'SliceDisplayCtrl' # The registration state. # The registration state parameter is the registration # resource name. .state 'quip.collection.subject.session.scan.registration', abstract: true url: '/registration/:registration' resolve: registration: (scan, $stateParams, Registration) -> Registration.find(scan, $stateParams.registration) # The Registration Detail state. .state 'quip.collection.subject.session.scan.registration.volume', abstract: true url: '?volume' resolve: imageSequence: (registration) -> registration volume: -> if $stateParams.volume? then parseInt($stateParams.volume) # # TODO - uncomment to enable XTK. # # The registration 3D volume detail state. # .state 'quip.collection.subject.session.scan.registration.volume.detail', # url: '' # views: # 'main@': # templateUrl: '/partials/volume-display.html' # controller: 'VolumeDisplayCtrl' # The registration slice detail state. .state 'quip.collection.subject.session.scan.registration.volume.slice', url: '?slice' resolve: slice: ($stateParams) -> if $stateParams.slice? then parseInt($stateParams.slice) timeSeries: (registration) -> timeSeries = registration.timeSeries if not timeSeries? throw new ReferenceError( "#{ registration.title } does not have a time series" ) # TODO - see scan TODO if timeSeries.image.isLoaded() timeSeries else # TODO - defer the promise resolution to the rendering? timeSeries.image.load().then -> timeSeries views: 'main@': templateUrl: '/partials/slice-display.html' controller: 'SliceDisplayCtrl' # HTML5 mode enables the back button to work properly. $locationProvider.html5Mode(enabled: true, requireBase: false) ]