config.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. import ObservableMixin from './observablemixin.js';
  7. import isObject from './lib/lodash/isObject.js';
  8. import isPlainObject from './lib/lodash/isPlainObject.js';
  9. import utils from './utils.js';
  10. /**
  11. * Handles a configuration dictionary.
  12. *
  13. * @memberOf core
  14. * @mixes core.ObservableMixin
  15. */
  16. export default class Config {
  17. /**
  18. * Creates an instance of the {@link Config} class.
  19. *
  20. * @param {Object} [configurations] The initial configurations to be set.
  21. */
  22. constructor( configurations ) {
  23. if ( configurations ) {
  24. this.set( configurations );
  25. }
  26. }
  27. /**
  28. * Set configuration values.
  29. *
  30. * It accepts both a name/value pair or an object, which properties and values will be used to set
  31. * configurations.
  32. *
  33. * It also accepts setting a "deep configuration" by using dots in the name. For example, `'resize.width'` sets
  34. * the value for the `width` configuration in the `resize` subset.
  35. *
  36. * config.set( 'width', 500 );
  37. * config.set( 'toolbar.collapsed', true );
  38. *
  39. * // Equivalent to:
  40. * config.set( {
  41. * width: 500
  42. * toolbar: {
  43. * collapsed: true
  44. * }
  45. * } );
  46. *
  47. * Passing an object as the value will amend the configuration, not replace it.
  48. *
  49. * config.set( 'toolbar', {
  50. * collapsed: true,
  51. * } );
  52. *
  53. * config.set( 'toolbar', {
  54. * color: 'red',
  55. * } );
  56. *
  57. * config.toolbar.collapsed; // true
  58. * config.toolbar.color; // 'red'
  59. *
  60. * @param {String|Object} name The configuration name or an object from which take properties as
  61. * configuration entries. Configuration names are case-insensitive.
  62. * @param {*} [value=null] The configuration value. Used if a name is passed to nameOrConfigurations.
  63. */
  64. set( name, value ) {
  65. // Just pass the call to the original set() in case of an object. It'll deal with recursing through the
  66. // object and calling set( name, value ) again for each property.
  67. if ( isObject( name ) ) {
  68. ObservableMixin.set.apply( this, arguments );
  69. return;
  70. }
  71. // The target for this configuration is, for now, this object.
  72. let target = this;
  73. // The configuration name should be split into parts if it has dots. E.g: `resize.width`.
  74. const parts = name.toLowerCase().split( '.' );
  75. // Take the name of the configuration out of the parts. E.g. `resize.width` -> `width`
  76. name = parts.pop();
  77. // Retrieves the final target for this configuration recursively.
  78. for ( let i = 0; i < parts.length; i++ ) {
  79. // The target will always be an instance of Config.
  80. if ( !( target[ parts[ i ] ] instanceof Config ) ) {
  81. target.set( parts[ i ], new Config() );
  82. }
  83. target = target[ parts[ i ] ];
  84. }
  85. // Values set as pure objects will be treated as Config subsets.
  86. if ( isPlainObject( value ) ) {
  87. // If the target is an instance of Config (a deep config subset).
  88. if ( target[ name ] instanceof Config ) {
  89. // Amend the target with the value, instead of replacing it.
  90. target[ name ].set( value );
  91. return;
  92. }
  93. value = new Config( value );
  94. }
  95. // Values will never be undefined.
  96. if ( typeof value == 'undefined' ) {
  97. value = null;
  98. }
  99. // Call the original set() on the target.
  100. ObservableMixin.set.call( target, name, value );
  101. }
  102. /**
  103. * Gets the value for a configuration entry.
  104. *
  105. * config.get( 'name' );
  106. *
  107. * Deep configurations can be retrieved by separating each part with a dot.
  108. *
  109. * config.get( 'toolbar.collapsed' );
  110. *
  111. * @param {String} name The configuration name. Configuration names are case-insensitive.
  112. * @returns {*} The configuration value or `undefined` if the configuration entry was not found.
  113. */
  114. get( name ) {
  115. // The target for this configuration is, for now, this object.
  116. let source = this;
  117. // The configuration name should be split into parts if it has dots. E.g. `resize.width` -> [`resize`, `width`]
  118. const parts = name.toLowerCase().split( '.' );
  119. // Take the name of the configuration from the parts. E.g. `resize.width` -> `width`
  120. name = parts.pop();
  121. // Retrieves the source for this configuration recursively.
  122. for ( let i = 0; i < parts.length; i++ ) {
  123. // The target will always be an instance of Config.
  124. if ( !( source[ parts[ i ] ] instanceof Config ) ) {
  125. source = null;
  126. break;
  127. }
  128. source = source[ parts[ i ] ];
  129. }
  130. // Try to retrieve it from the source object.
  131. if ( source && ( typeof source[ name ] != 'undefined' ) ) {
  132. return source[ name ];
  133. }
  134. // If not found, take it from the definition.
  135. if ( this.definition ) {
  136. return this.definition[ name ];
  137. }
  138. }
  139. /**
  140. * Defines the name and default value for configurations. It accepts the same parameters as the
  141. * {@link Config#set set()} method.
  142. *
  143. * On first call, the {@link Config#definition definition} property is created to hold all defined
  144. * configurations.
  145. *
  146. * This method is supposed to be called by plugin developers to setup plugin's configurations. It would be
  147. * rarely used for other needs.
  148. *
  149. * @param {String|Object} name The configuration name or an object from which take properties as
  150. * configuration entries.
  151. * @param {*} [value] The configuration value. Used if a name is passed to nameOrConfigurations. If undefined,
  152. * the configuration is set to `null`.
  153. */
  154. define( name, value ) {
  155. if ( !this.definition ) {
  156. /**
  157. * TODO
  158. *
  159. * @type {Config}
  160. */
  161. this.definition = new Config();
  162. }
  163. this.definition.set( name, value );
  164. }
  165. }
  166. utils.mix( Config, ObservableMixin );