6 * DOM element abstraction.
10 class Element extends Tag {
12 /* Static Properties */
17 * This may be ignored if getTagName() is overridden.
21 public static $tagName = 'div';
24 * Default text direction, used for some layout calculations. Use setDefaultDir() to change.
26 * Currently only per-document directionality is supported.
30 public static $defaultDir = 'ltr';
39 protected $data = null;
42 * Strings of the CSS classes explicitly configured for this element (as opposed to #$classes,
43 * which contains all classes for this element).
47 protected $ownClasses = [];
52 protected $configCallbacks = [];
57 * @param array $config Configuration options
58 * @param string[] $config['classes'] CSS class names to add
59 * @param string $config['id'] HTML id attribute
60 * @param string $config['text'] Text to insert
61 * @param array $config['content'] Content to append (after text), strings
62 * or Element objects. Strings will be HTML-escaped for output, use an
63 * HtmlSnippet instance to prevent that.
64 * @param mixed $config['data'] Element data
66 public function __construct( array $config = [] ) {
68 parent::__construct( $this->getTagName() );
71 if ( isset( $config['infusable'] ) && is_bool( $config['infusable'] ) ) {
72 $this->setInfusable( $config['infusable'] );
74 if ( isset( $config['data'] ) ) {
75 $this->setData( $config['data'] );
77 if ( isset( $config['classes'] ) && is_array( $config['classes'] ) ) {
78 $this->ownClasses = $config['classes'];
79 $this->addClasses( $this->ownClasses );
81 if ( isset( $config['id'] ) ) {
82 $this->setAttributes( [ 'id' => $config['id'] ] );
84 if ( isset( $config['text'] ) ) {
86 $this->appendContent( $config['text'] );
88 if ( isset( $config['content'] ) ) {
89 $this->appendContent( $config['content'] );
94 * Get the HTML tag name.
96 * Override this method to base the result on instance information.
98 * @return string HTML tag name
100 public function getTagName() {
101 return $this::$tagName;
107 * @return mixed Element data
109 public function getData() {
116 * @param mixed $data Element data
119 public function setData( $data ) {
125 * Check if element supports one or more methods.
127 * @param string|string[] $methods Method or list of methods to check
128 * @return bool All methods are supported
130 public function supports( $methods ) {
132 $methods = (array)$methods;
134 foreach ( $methods as $method ) {
135 if ( method_exists( $this, $method ) ) {
141 return count( $methods ) === $support;
145 * Register an additional function to call when building the config. See ::getConfig().
147 * @param callable $func The function. Parameters and return value are the same as ::getConfig().
149 public function registerConfigCallback( callable $func ) {
150 $this->configCallbacks[] = $func;
154 * Add the necessary properties to the given `$config` array to allow
155 * reconstruction of this widget via its constructor.
156 * @param array &$config An array which will be mutated to add the necessary configuration
157 * properties. Unless you are implementing a subclass, you should
158 * always pass a new empty array `[]`.
159 * @return array A configuration array which can be passed to this object's
160 * constructor to recreate it. This is a return value to allow
161 * the safe use of copy-by-value functions like `array_merge` in
162 * the implementation.
164 public function getConfig( &$config ) {
165 // If there are traits, add their config
166 foreach ( $this->configCallbacks as $func ) {
167 call_user_func_array( $func, [ &$config ] );
170 if ( $this->data !== null ) {
171 $config['data'] = $this->data;
173 if ( $this->ownClasses !== [] ) {
174 $config['classes'] = $this->ownClasses;
180 * Create a modified version of the configuration array suitable for
181 * JSON serialization by replacing `Tag` references and
184 * @return array A serialized configuration array.
186 private function getSerializedConfig() {
187 // Ensure that '_' comes first in the output.
188 $config = [ '_' => true ];
189 $config = $this->getConfig( $config );
190 // Post-process config array to turn Tag references into ID references
191 // and HtmlSnippet references into a { html: 'string' } JSON form.
192 $replaceElements = function ( &$item ) {
193 if ( $item instanceof Tag ) {
194 $item->ensureInfusableId();
195 $item = [ 'tag' => $item->getAttribute( 'id' ) ];
196 } elseif ( $item instanceof HtmlSnippet ) {
197 $item = [ 'html' => (string)$item ];
200 array_walk_recursive( $config, $replaceElements );
201 // Set '_' last to ensure that subclasses can't accidentally step on it.
202 $config['_'] = $this->getJavaScriptClassName();
207 * The class name of the JavaScript version of this widget
210 protected function getJavaScriptClassName() {
211 return str_replace( 'OOUI\\', 'OO.ui.', get_class( $this ) );
214 protected function getGeneratedAttributes() {
215 $attributesArray = parent::getGeneratedAttributes();
216 // Add `data-ooui` attribute from serialized config array.
217 if ( $this->infusable ) {
218 $serialized = $this->getSerializedConfig();
219 $attributesArray['data-ooui'] = json_encode( $serialized );
221 return $attributesArray;
225 * Render element into HTML.
227 * @return string HTML serialization
229 public function toString() {
230 Theme::singleton()->updateElementClasses( $this );
231 if ( $this->isInfusable() ) {
232 $this->ensureInfusableId();
234 return parent::toString();
238 * Get the direction of the user interface for a given element.
240 * Currently only per-document directionality is supported.
242 * @param Tag $element Element to check
243 * @return string Text direction, either 'ltr' or 'rtl'
245 public static function getDir( Tag $element ) {
246 return self::$defaultDir;
250 * Set the default direction of the user interface.
252 * @param string $dir Text direction, either 'ltr' or 'rtl'
254 public static function setDefaultDir( $dir ) {
255 self::$defaultDir = $dir === 'rtl' ? 'rtl' : 'ltr';
259 * A helper method to massage an array of HTML attributes into a format that is more likely to
260 * work with an OOjs UI PHP element, camel-casing attribute names and setting values of boolean
261 * ones to true. Intended as a convenience to be used when refactoring legacy systems using HTML
264 * @param array $attrs HTML attributes, e.g. `[ 'disabled' => '', 'accesskey' => 'k' ]`
265 * @return array OOjs UI PHP element config, e.g. `[ 'disabled' => true, 'accessKey' => 'k' ]`
267 public static function configFromHtmlAttributes( array $attrs ) {
275 $attributeToConfig = [
276 'maxlength' => 'maxLength',
277 'readonly' => 'readOnly',
278 'tabindex' => 'tabIndex',
279 'accesskey' => 'accessKey',
282 foreach ( $attrs as $key => $value ) {
283 if ( isset( $booleanAttrs[$key] ) && $value !== false && $value !== null ) {
286 if ( isset( $attributeToConfig[$key] ) ) {
287 $key = $attributeToConfig[$key];
289 $config[$key] = $value;