ViewSpecManager.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import * as moment from 'moment'
  2. import * as $ from 'jquery'
  3. import { viewHash } from './ViewRegistry'
  4. import { mergeProps, unitsDesc, computeDurationGreatestUnit } from './util'
  5. import { mergeOptions, globalDefaults } from './options'
  6. import { populateInstanceComputableOptions } from './locale'
  7. export default class ViewSpecManager {
  8. _calendar: any // avoid
  9. optionsManager: any
  10. viewSpecCache: any // cache of view definitions (initialized in Calendar.js)
  11. constructor(optionsManager, _calendar) {
  12. this.optionsManager = optionsManager
  13. this._calendar = _calendar
  14. this.clearCache()
  15. }
  16. clearCache() {
  17. this.viewSpecCache = {}
  18. }
  19. // Gets information about how to create a view. Will use a cache.
  20. getViewSpec(viewType) {
  21. let cache = this.viewSpecCache
  22. return cache[viewType] || (cache[viewType] = this.buildViewSpec(viewType))
  23. }
  24. // Given a duration singular unit, like "week" or "day", finds a matching view spec.
  25. // Preference is given to views that have corresponding buttons.
  26. getUnitViewSpec(unit) {
  27. let viewTypes
  28. let i
  29. let spec
  30. if ($.inArray(unit, unitsDesc) != -1) {
  31. // put views that have buttons first. there will be duplicates, but oh well
  32. viewTypes = this._calendar.header.getViewsWithButtons() // TODO: include footer as well?
  33. $.each(viewHash, function(viewType) { // all views
  34. viewTypes.push(viewType)
  35. })
  36. for (i = 0; i < viewTypes.length; i++) {
  37. spec = this.getViewSpec(viewTypes[i])
  38. if (spec) {
  39. if (spec.singleUnit == unit) {
  40. return spec
  41. }
  42. }
  43. }
  44. }
  45. }
  46. // Builds an object with information on how to create a given view
  47. buildViewSpec(requestedViewType) {
  48. let viewOverrides = this.optionsManager.overrides.views || {}
  49. let specChain = [] // for the view. lowest to highest priority
  50. let defaultsChain = [] // for the view. lowest to highest priority
  51. let overridesChain = [] // for the view. lowest to highest priority
  52. let viewType = requestedViewType
  53. let spec // for the view
  54. let overrides // for the view
  55. let durationInput
  56. let duration
  57. let unit
  58. // iterate from the specific view definition to a more general one until we hit an actual View class
  59. while (viewType) {
  60. spec = viewHash[viewType]
  61. overrides = viewOverrides[viewType]
  62. viewType = null // clear. might repopulate for another iteration
  63. if (typeof spec === 'function') { // TODO: deprecate
  64. spec = { 'class': spec }
  65. }
  66. if (spec) {
  67. specChain.unshift(spec)
  68. defaultsChain.unshift(spec.defaults || {})
  69. durationInput = durationInput || spec.duration
  70. viewType = viewType || spec.type
  71. }
  72. if (overrides) {
  73. overridesChain.unshift(overrides) // view-specific option hashes have options at zero-level
  74. durationInput = durationInput || overrides.duration
  75. viewType = viewType || overrides.type
  76. }
  77. }
  78. spec = mergeProps(specChain)
  79. spec.type = requestedViewType
  80. if (!spec['class']) {
  81. return false
  82. }
  83. // fall back to top-level `duration` option
  84. durationInput = durationInput ||
  85. this.optionsManager.dynamicOverrides.duration ||
  86. this.optionsManager.overrides.duration
  87. if (durationInput) {
  88. duration = moment.duration(durationInput)
  89. if (duration.valueOf()) { // valid?
  90. unit = computeDurationGreatestUnit(duration, durationInput)
  91. spec.duration = duration
  92. spec.durationUnit = unit
  93. // view is a single-unit duration, like "week" or "day"
  94. // incorporate options for this. lowest priority
  95. if (duration.as(unit) === 1) {
  96. spec.singleUnit = unit
  97. overridesChain.unshift(viewOverrides[unit] || {})
  98. }
  99. }
  100. }
  101. spec.defaults = mergeOptions(defaultsChain)
  102. spec.overrides = mergeOptions(overridesChain)
  103. this.buildViewSpecOptions(spec)
  104. this.buildViewSpecButtonText(spec, requestedViewType)
  105. return spec
  106. }
  107. // Builds and assigns a view spec's options object from its already-assigned defaults and overrides
  108. buildViewSpecOptions(spec) {
  109. let optionsManager = this.optionsManager
  110. spec.options = mergeOptions([ // lowest to highest priority
  111. globalDefaults,
  112. spec.defaults, // view's defaults (from ViewSubclass.defaults)
  113. optionsManager.dirDefaults,
  114. optionsManager.localeDefaults, // locale and dir take precedence over view's defaults!
  115. optionsManager.overrides, // calendar's overrides (options given to constructor)
  116. spec.overrides, // view's overrides (view-specific options)
  117. optionsManager.dynamicOverrides // dynamically set via setter. highest precedence
  118. ])
  119. populateInstanceComputableOptions(spec.options)
  120. }
  121. // Computes and assigns a view spec's buttonText-related options
  122. buildViewSpecButtonText(spec, requestedViewType) {
  123. let optionsManager = this.optionsManager
  124. // given an options object with a possible `buttonText` hash, lookup the buttonText for the
  125. // requested view, falling back to a generic unit entry like "week" or "day"
  126. function queryButtonText(options) {
  127. let buttonText = options.buttonText || {}
  128. return buttonText[requestedViewType] ||
  129. // view can decide to look up a certain key
  130. (spec.buttonTextKey ? buttonText[spec.buttonTextKey] : null) ||
  131. // a key like "month"
  132. (spec.singleUnit ? buttonText[spec.singleUnit] : null)
  133. }
  134. // highest to lowest priority
  135. spec.buttonTextOverride =
  136. queryButtonText(optionsManager.dynamicOverrides) ||
  137. queryButtonText(optionsManager.overrides) || // constructor-specified buttonText lookup hash takes precedence
  138. spec.overrides.buttonText // `buttonText` for view-specific options is a string
  139. // highest to lowest priority. mirrors buildViewSpecOptions
  140. spec.buttonTextDefault =
  141. queryButtonText(optionsManager.localeDefaults) ||
  142. queryButtonText(optionsManager.dirDefaults) ||
  143. spec.defaults.buttonText || // a single string. from ViewSubclass.defaults
  144. queryButtonText(globalDefaults) ||
  145. (spec.duration ? this._calendar.humanizeDuration(spec.duration) : null) || // like "3 days"
  146. requestedViewType // fall back to given view name
  147. }
  148. }