diff options
Diffstat (limited to 'vendor/oojs/oojs-ui/php/Element.php')
-rw-r--r-- | vendor/oojs/oojs-ui/php/Element.php | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/vendor/oojs/oojs-ui/php/Element.php b/vendor/oojs/oojs-ui/php/Element.php new file mode 100644 index 00000000..eaa8c825 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/Element.php @@ -0,0 +1,297 @@ +<?php + +namespace OOUI; + +/** + * DOM element abstraction. + * + * @abstract + */ +class Element extends Tag { + + /* Static properties */ + + /** + * HTML tag name. + * + * This may be ignored if getTagName() is overridden. + * + * @var string + */ + public static $tagName = 'div'; + + /** + * Default text direction, used for some layout calculations. Use setDefaultDir() to change. + * + * Currently only per-document directionality is supported. + * + * @var string + */ + public static $defaultDir = 'ltr'; + + /* Members */ + + /** + * Element data. + * + * @var mixed + */ + protected $data = null; + + /** + * Mixins. + * + * @var array List mixed in objects. + */ + protected $mixins = array(); + + /* Methods */ + + /** + * @param array $config Configuration options + * @param string[] $config['classes'] CSS class names to add + * @param string $config['id'] HTML id attribute + * @param string $config['text'] Text to insert + * @param array $config['content'] Content to append (after text), strings + * or Element objects. Strings will be HTML-escaped for output, use an + * HtmlSnippet instance to prevent that. + * @param mixed $config['data'] Element data + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $this->getTagName() ); + + // Initialization + if ( isset( $config['infusable'] ) && is_bool( $config['infusable'] ) ) { + $this->setInfusable( $config['infusable'] ); + } + if ( isset( $config['data'] ) ) { + $this->setData( $config['data'] ); + } + if ( isset( $config['classes'] ) && is_array( $config['classes'] ) ) { + $this->addClasses( $config['classes'] ); + } + if ( isset( $config['id'] ) ) { + $this->setAttributes( array( 'id' => $config['id'] ) ); + } + if ( isset( $config['text'] ) ) { + // JS compatibility + $this->appendContent( $config['text'] ); + } + if ( isset( $config['content'] ) ) { + $this->appendContent( $config['content'] ); + } + } + + /** + * Call a mixed-in method. + * + * This makes the methods of a mixin accessible through the element being mixed into. + * + * Triggers an error if the method is not found, as normal. + * + * @param string $method Method name + * @param array $arguments Method arguments + * @return mixed Result of method call + */ + public function __call( $method, $arguments ) { + // Search mixins for methods + foreach ( $this->mixins as $mixin ) { + if ( method_exists( $mixin, $method ) ) { + return call_user_func_array( array( $mixin, $method ), $arguments ); + } + } + // Fail normally + trigger_error( + 'Call to undefined method ' . __CLASS__ . '::' . $method . '()', + E_USER_ERROR + ); + } + + /** + * Get a mixed-in target property. + * + * This makes the target of a mixin accessible through the element being mixed into. + * + * The target's property name is statically configured by the mixin class. + * + * Triggers a notice if the property is not found, as normal. + * + * @param string $name Property name + * @return Tag|null Target property or null if not found + */ + public function __get( $name ) { + // Search mixins for methods + foreach ( $this->mixins as $mixin ) { + if ( isset( $mixin::$targetPropertyName ) && $mixin::$targetPropertyName === $name ) { + return $mixin->target; + } + } + // Fail normally + trigger_error( 'Undefined property: ' . $name, E_USER_NOTICE ); + return null; + } + + /** + * Get the HTML tag name. + * + * Override this method to base the result on instance information. + * + * @return string HTML tag name + */ + public function getTagName() { + return $this::$tagName; + } + + /** + * Get element data. + * + * @return mixed Element data + */ + public function getData() { + return $this->data; + } + + /** + * Set element data. + * + * @param mixed $data Element data + * @chainable + */ + public function setData( $data ) { + $this->data = $data; + return $this; + } + + /** + * Check if element supports one or more methods. + * + * @param string|string[] $methods Method or list of methods to check + * @return boolean All methods are supported + */ + public function supports( $methods ) { + $support = 0; + $methods = (array)$methods; + + foreach ( $methods as $method ) { + if ( method_exists( $this, $method ) ) { + $support++; + continue; + } + + // Search mixins for methods + foreach ( $this->mixins as $mixin ) { + if ( method_exists( $mixin, $method ) ) { + $support++; + break; + } + } + } + + return count( $methods ) === $support; + } + + /** + * Mixin a class. + * + * @param ElementMixin $mixin Mixin object + */ + public function mixin( ElementMixin $mixin ) { + $this->mixins[] = $mixin; + } + + /** + * Add the necessary properties to the given `$config` array to allow + * reconstruction of this widget via its constructor. + * @param array &$config + * An array which will be mutated to add the necessary configuration + * properties. Unless you are implementing a subclass, you should + * always pass a new empty `array()`. + * @return array + * A configuration array which can be passed to this object's + * constructor to recreate it. This is a return value to allow + * the safe use of copy-by-value functions like `array_merge` in + * the implementation. + */ + public function getConfig( &$config ) { + foreach ( $this->mixins as $mixin ) { + $config = $mixin->getConfig( $config ); + } + if ( $this->data !== null ) { + $config['data'] = $this->data; + } + return $config; + } + + /** + * Create a modified version of the configuration array suitable for + * JSON serialization by replacing `Tag` references and + * `HtmlSnippet`s. + * + * @return array + * A serialized configuration array. + */ + private function getSerializedConfig() { + // Ensure that '_' comes first in the output. + $config = array( '_' => true ); + $config = $this->getConfig( $config ); + // Post-process config array to turn Tag references into ID references + // and HtmlSnippet references into a { html: 'string' } JSON form. + $replaceElements = function( &$item ) { + if ( $item instanceof Tag ) { + $item->ensureInfusableId(); + $item = array( 'tag' => $item->getAttribute( 'id' ) ); + } elseif ( $item instanceof HtmlSnippet ) { + $item = array( 'html' => (string) $item ); + } + }; + array_walk_recursive( $config, $replaceElements ); + // Set '_' last to ensure that subclasses can't accidentally step on it. + $config['_'] = preg_replace( '/^OOUI\\\\/', '', get_class( $this ) ); + return $config; + } + + protected function getGeneratedAttributes() { + $attributesArray = parent::getGeneratedAttributes(); + // Add `data-ooui` attribute from serialized config array. + if ( $this->infusable ) { + $serialized = $this->getSerializedConfig(); + $attributesArray['data-ooui'] = json_encode( $serialized ); + } + return $attributesArray; + } + + /** + * Render element into HTML. + * + * @return string HTML serialization + */ + public function toString() { + Theme::singleton()->updateElementClasses( $this ); + if ( $this->isInfusable() ) { + $this->ensureInfusableId(); + } + return parent::toString(); + } + + /** + * Get the direction of the user interface for a given element. + * + * Currently only per-document directionality is supported. + * + * @param Tag $element Element to check + * @return string Text direction, either 'ltr' or 'rtl' + */ + public static function getDir( Tag $element ) { + return self::$defaultDir; + } + + /** + * Set the default direction of the user interface. + * + * @return string Text direction, either 'ltr' or 'rtl' + */ + public static function setDefaultDir( $dir ) { + self::$defaultDir = $dir === 'rtl' ? 'rtl' : 'ltr'; + } +} |