/**
* @namespace qui.sections
*/
import $ from '$qui/lib/jquery.module.js'
import Logger from '$qui/lib/logger.module.js'
import {AssertionError} from '$qui/base/errors.js'
import {CancelledError} from '$qui/base/errors.js'
import StockIcon from '$qui/icons/stock-icon.js'
import * as OptionsBar from '$qui/main-ui/options-bar.js'
import * as Navigation from '$qui/navigation.js'
import * as Theme from '$qui/theme.js'
import * as Window from '$qui/window.js'
/**
* @alias qui.sections.BUTTON_TYPE_MENU_BAR
*/
export const BUTTON_TYPE_MENU_BAR = 'menu_bar'
/**
* @alias qui.sections.BUTTON_TYPE_TOP_BAR
*/
export const BUTTON_TYPE_TOP_BAR = 'top_bar'
/**
* @alias qui.sections.BUTTON_TYPE_NONE
*/
export const BUTTON_TYPE_NONE = 'none'
const logger = Logger.get('qui.sections')
let sectionClasses = []
let sectionsList = []
let currentSection = null
let homeSection = null
/**
* An error class used to prevent hiding sections.
* @alias qui.sections.HideCancelled
* @extends qui.base.errors.CancelledError
*/
export class HideCancelled extends CancelledError {
}
function attachSectionEventRelays() {
Window.screenLayoutChangeSignal.connect(function (smallScreen, landscape) {
sectionsList.forEach(function (s) {
s.onScreenLayoutChange(smallScreen, landscape)
})
})
Window.resizeSignal.connect(function (width, height) {
sectionsList.forEach(function (s) {
s.onWindowResize(width, height)
})
})
Window.activeChangeSignal.connect(function (active) {
sectionsList.forEach(function (s) {
s.onWindowActiveChange(active)
})
})
Window.focusChangeSignal.connect(function (focused) {
sectionsList.forEach(function (s) {
s.onWindowFocusedChange(focused)
})
})
OptionsBar.openCloseSignal.connect(function (opened) {
if (!currentSection) {
return
}
currentSection.onOptionsBarOpenClose(opened)
})
}
/**
* @private
* @returns {Promise}
*/
function softReload() {
logger.debug('soft reloading')
/* Reset sections */
if (currentSection) {
let currentPath = Navigation.getCurrentPath()
return reset().then(function () {
return Navigation.navigate({path: currentPath, historyEntry: false})
})
}
return Promise.resolve()
}
/**
* Register a section class.
* @alias qui.sections.register
* @param {typeof qui.sections.Section} sectionClass the section class (see {@link qui.sections.Section})
*/
export function register(sectionClass) {
let index = sectionClasses.indexOf(sectionClass)
if (index >= 0) {
throw new AssertionError('Attempt to register a section class that has already been registered')
}
sectionClasses.push(sectionClass)
let section = sectionClass.getInstance()
section.handleRegister()
sectionsList.push(section)
logger.debug(`registered section "${section.getId()}"`)
if (!homeSection) {
/* Home section is, by default, the first section */
logger.debug(`home section set to "${section.getId()}"`)
homeSection = section
}
}
/**
* Unregister a section class.
* @alias qui.sections.unregister
* @param {typeof qui.sections.Section} sectionClass the section class (see {@link qui.sections.Section})
*/
export function unregister(sectionClass) {
let index = sectionClasses.indexOf(sectionClass)
if (index < 0) {
throw new AssertionError('Attempt to unregister a section class that has not been registered')
}
sectionClasses.splice(index, 1)
let section = sectionsList[index]
section.handleUnregister()
sectionsList.splice(index, 1)
logger.debug(`unregistered section "${section.getId()}"`)
}
/**
* Lookup a section by its identifier.
* @alias qui.sections.get
* @param {String} id the section identifier
* @returns {?qui.sections.Section} the section if found, otherwise `null`
*/
export function get(id) {
return sectionsList.find(section => section.getId() === id) || null
}
/**
* Return the current section.
* @alias qui.sections.getCurrent
* @returns {?qui.sections.Section}
*/
export function getCurrent() {
return currentSection
}
/**
* Return all registered sections, in order.
* @alias qui.sections.all
* @returns {qui.sections.Section[]}
*/
export function all() {
return sectionsList.slice()
}
/**
* Switch to the given section, making it current.
* @alias qui.sections.switchTo
* @param {qui.sections.Section} section the section
* @param {String} [source] optional source to pass to {@link qui.sections.Section#onShow} (defaults to `program`)
* @param {Boolean} [historyEntry] whether to create a new history entry for current context before switching to the
* new section, or not (defaults to `false`)
* @returns {Promise} a promise that resolves as soon as the section is loaded.
*/
export function switchTo(section, source, historyEntry = false) {
/* If requested section is already the current section, do nothing more */
if ((currentSection === section)) {
Navigation.updateHistoryEntry()
return currentSection.whenLoaded()
}
return section.whenPreloaded().then(function () {
let oldSection = currentSection
let hidePromise = Promise.resolve()
if (oldSection) {
hidePromise = oldSection._hide(historyEntry)
}
return hidePromise.then(function () {
let s = section.navigate([])
if (s !== section) {
logger.debug(`section "${section.getId()}" redirected to section "${s.getId()}"`)
return switchTo(s, source)
}
currentSection = section
if (source === undefined) {
source = 'program'
}
logger.debug(`switching to section "${section.getId()}" (source: ${source})`)
}).then(function () {
return section._show(source, oldSection)
}).then(function () {
Navigation.updateHistoryEntry()
})
})
}
/**
* Switch to the home section.
* @alias qui.sections.showHome
* @param {?Boolean} [reset] if `true` will reset the home section to its initial state, recreating its main page
* @returns {Promise} a promise that resolves as soon as the home section is loaded.
*/
export function showHome(reset) {
if (!homeSection) {
throw new AssertionError('No home section')
}
let promise = Promise.resolve()
if (reset) {
promise = homeSection.reset()
}
return promise.then(function () {
return switchTo(homeSection, /* source = */ 'home')
})
}
/**
* Set the home section.
* @alias qui.sections.setHome
* @param {qui.sections.Section} section the new home section
*/
export function setHome(section) {
homeSection = section
}
/**
* Return the home section.
* @alias qui.sections.getHome
* @returns {?qui.sections.Section}
*/
export function getHome() {
return homeSection
}
/**
* Reset all sections.
* @returns {Promise} a promise that is settled as soon as all sections have been reset
*/
export function reset() {
return Promise.all(sectionsList.map(s => s.reset()))
}
export function init() {
attachSectionEventRelays()
/* Prevent unwanted section closing when window is closed */
Window.closeSignal.connect(function () {
return !sectionsList.some(s => !s.canClose())
})
Window.screenLayoutChangeSignal.connect(function (smallScreen, landscape) {
/* Automatically update top icon buttons according to small screen state */
$('div.qui-top-bar > div.qui-top-button.qui-section-button > div.qui-icon').each(function () {
StockIcon.alterElement($(this), {variant: smallScreen ? 'white' : 'interactive'})
})
logger.debug('soft reloading due to screen layout change')
softReload()
})
Theme.changeSignal.connect(function (theme) {
logger.debug('soft reloading due to theme change')
softReload()
})
/* Export some stuff to global scope */
let qui = (window.qui = window.qui || {})
qui.getCurrentSection = getCurrent
}