config.js 5.6 KB

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