diff options
author | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-05-01 15:30:02 -0400 |
---|---|---|
committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-05-01 15:30:02 -0400 |
commit | 1de335ad3f395ca6861085393ba366a9e3fb4a0d (patch) | |
tree | f1fdd326034e05177596851be6a7127615d81498 /vendor/oojs/oojs-ui/php | |
parent | 9c75fa8ff6d4d38ef552c00fef5969fb154765e8 (diff) | |
parent | f6d65e533c62f6deb21342d4901ece24497b433e (diff) |
Merge commit 'f6d65'
# Conflicts:
# skins/ArchLinux/ArchLinux.php
Diffstat (limited to 'vendor/oojs/oojs-ui/php')
33 files changed, 2933 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'; + } +} diff --git a/vendor/oojs/oojs-ui/php/ElementMixin.php b/vendor/oojs/oojs-ui/php/ElementMixin.php new file mode 100644 index 00000000..7f54076e --- /dev/null +++ b/vendor/oojs/oojs-ui/php/ElementMixin.php @@ -0,0 +1,55 @@ +<?php + +namespace OOUI; + +class ElementMixin { + + /* Properties */ + + /** + * Tag being targeted. + * + * @var Tag + */ + public $target = null; + + /** + * Element being mixed into. + * + * @var Element + */ + protected $element = null; + + /** + * Property name for accessing the target on the element. + * + * @var string + */ + public static $targetPropertyName = ''; + + /* Methods */ + + /** + * Create element. + * + * @param Element $element Element being mixed into + * @param Tag $tag Tag being targeted + * @param array $config Configuration options + */ + public function __construct( Element $element, Tag $target, array $config = array() ) { + $this->element = $element; + $this->target = $target; + } + + /** + * Add properties to the given `$config` array to allow reconstruction + * of this widget via its constructor. This method is meant to be + * overridden by subclasses of `ElementMixin`. + * + * @return array A configuration array. + * @see Element::getConfig() + */ + public function getConfig( &$config ) { + return $config; + } +} diff --git a/vendor/oojs/oojs-ui/php/Exception.php b/vendor/oojs/oojs-ui/php/Exception.php new file mode 100644 index 00000000..2f5ba1b4 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/Exception.php @@ -0,0 +1,6 @@ +<?php + +namespace OOUI; + +class Exception extends \Exception { +} diff --git a/vendor/oojs/oojs-ui/php/HtmlSnippet.php b/vendor/oojs/oojs-ui/php/HtmlSnippet.php new file mode 100644 index 00000000..e0889fca --- /dev/null +++ b/vendor/oojs/oojs-ui/php/HtmlSnippet.php @@ -0,0 +1,36 @@ +<?php + +namespace OOUI; + +/** + * Wraps a HTML snippet for use with Tag::appendContent() and Tag::prependContent(). + */ +class HtmlSnippet { + + /* Members */ + + /** + * HTML snippet this instance represents. + * + * @var string + */ + protected $content; + + /* Methods */ + + /** + * @param string $content + */ + public function __construct( $content ) { + $this->content = $content; + } + + /** + * Render into HTML. + * + * @return string Unchanged HTML snippet + */ + public function __toString() { + return $this->content; + } +} diff --git a/vendor/oojs/oojs-ui/php/Layout.php b/vendor/oojs/oojs-ui/php/Layout.php new file mode 100644 index 00000000..7a4e58d0 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/Layout.php @@ -0,0 +1,21 @@ +<?php + +namespace OOUI; + +/** + * Container for elements. + * + * @abstract + */ +class Layout extends Element { + /** + * @param array $config Configuration options + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $config ); + + // Initialization + $this->addClasses( array( 'oo-ui-layout' ) ); + } +} diff --git a/vendor/oojs/oojs-ui/php/Tag.php b/vendor/oojs/oojs-ui/php/Tag.php new file mode 100644 index 00000000..da8c2bfa --- /dev/null +++ b/vendor/oojs/oojs-ui/php/Tag.php @@ -0,0 +1,365 @@ +<?php + +namespace OOUI; + +class Tag { + + /* Members */ + + /** + * Tag name for this instance. + * + * @var string HTML tag name + */ + protected $tag = ''; + + /** + * Attributes. + * + * @var array HTML attributes + */ + protected $attributes = array(); + + /** + * Classes. + * + * @var array CSS classes + */ + protected $classes = array(); + + /** + * Content. + * + * @var array Content text and elements + */ + protected $content = array(); + + /** + * Group. + * + * @var GroupElement|null Group element is in + */ + protected $elementGroup = null; + + /** + * Infusion support. + * + * @var boolean Whether to serialize tag/element/widget state for client-side use. + */ + protected $infusable = false; + + /* Methods */ + + /** + * Create element. + * + * @param string $tag HTML tag name + */ + public function __construct( $tag = 'div' ) { + $this->tag = $tag; + } + + /** + * Check for CSS class. + * + * @param string $name CSS class name + * @return boolean + */ + public function hasClass( $class ) { + return in_array( $class, $this->classes ); + } + + /** + * Add CSS classes. + * + * @param array $classes List of classes to add + * @chainable + */ + public function addClasses( array $classes ) { + $this->classes = array_merge( $this->classes, $classes ); + return $this; + } + + /** + * Remove CSS classes. + * + * @param array $classes List of classes to remove + * @chainable + */ + public function removeClasses( array $classes ) { + $this->classes = array_diff( $this->classes, $classes ); + return $this; + } + + /** + * Toggle CSS classes. + * + * @param array $classes List of classes to add + * @param boolean $toggle Add classes + * @chainable + */ + public function toggleClasses( array $classes, $toggle = null ) { + if ( $toggle === null ) { + $this->classes = array_diff( + array_merge( $this->classes, $classes ), + array_intersect( $this->classes, $classes ) + ); + } elseif ( $toggle ) { + $this->classes = array_merge( $this->classes, $classes ); + } else { + $this->classes = array_diff( $this->classes, $classes ); + } + return $this; + } + + /** + * Get HTML attribute value. + * + * @param string $key HTML attribute name + * @return string|null + */ + public function getAttribute( $key ) { + return isset( $this->attributes[$key] ) ? $this->attributes[$key] : null; + } + + /** + * Add HTML attributes. + * + * @param array $attributes List of attribute key/value pairs to add + * @chainable + */ + public function setAttributes( array $attributes ) { + foreach ( $attributes as $key => $value ) { + $this->attributes[$key] = $value; + } + return $this; + } + + /** + * Set value of input element ('value' attribute for most, element content for textarea). + * + * @param string $value Value to set + * @chainable + */ + public function setValue( $value ) { + if ( strtolower( $this->tag ) === 'textarea' ) { + $this->clearContent(); + $this->appendContent( $value ); + } else { + $this->setAttributes( array( 'value' => $value ) ); + } + return $this; + } + + /** + * Remove HTML attributes. + * + * @param array $keys List of attribute keys to remove + * @chainable + */ + public function removeAttributes( array $keys ) { + foreach ( $keys as $key ) { + unset( $this->attributes[$key] ); + } + return $this; + } + + /** + * Add content to the end. + * + * Accepts variadic arguments (the $content argument can be repeated any number of times). + * + * @param string|Tag|HtmlSnippet $content Content to append. Strings will be HTML-escaped + * for output, use a HtmlSnippet instance to prevent that. + * @chainable + */ + public function appendContent( /* $content... */ ) { + $contents = func_get_args(); + $this->content = array_merge( $this->content, $contents ); + return $this; + } + + /** + * Add content to the beginning. + * + * Accepts variadic arguments (the $content argument can be repeated any number of times). + * + * @param string|Tag|HtmlSnippet $content Content to prepend. Strings will be HTML-escaped + * for output, use a HtmlSnippet instance to prevent that. + * @chainable + */ + public function prependContent( /* $content... */ ) { + $contents = func_get_args(); + array_splice( $this->content, 0, 0, $contents ); + return $this; + } + + /** + * Remove all content. + * + * @chainable + */ + public function clearContent() { + $this->content = array(); + return $this; + } + + /** + * Get group element is in. + * + * @return GroupElement|null Group element, null if none + */ + public function getElementGroup() { + return $this->elementGroup; + } + + /** + * Set group element is in. + * + * @param GroupElement|null $group Group element, null if none + * @chainable + */ + public function setElementGroup( $group ) { + $this->elementGroup = $group; + return $this; + } + + /** + * Enable widget for client-side infusion. + * + * @param boolean $infusable True to allow tag/element/widget to be referenced client-side. + * @chainable + */ + public function setInfusable( $infusable ) { + $this->infusable = $infusable; + return $this; + } + + /** + * Get client-side infusability. + * + * @return boolean If this tag/element/widget can be referenced client-side. + */ + public function isInfusable() { + return $this->infusable; + } + + private static $id_cnt = 0; + /** + * Ensure that this given Tag is infusable and has a unique `id` + * attribute. + * @chainable + */ + public function ensureInfusableId() { + $this->setInfusable( true ); + if ( $this->getAttribute( 'id' ) === null ) { + $this->setAttributes( array( 'id' => "ooui-" . ( self::$id_cnt++ ) ) ); + } + return $this; + } + + /** + * Return an augmented `attributes` array, including synthetic attributes + * which are created from other properties (like the `classes` array) + * but which shouldn't be retained in the user-visible `attributes`. + * @return array An attributes array. + */ + protected function getGeneratedAttributes() { + // Copy attributes, add `class` attribute from `$this->classes` array. + $attributesArray = $this->attributes; + if ( $this->classes ) { + $attributesArray['class'] = implode( ' ', array_unique( $this->classes ) ); + } + if ( $this->infusable ) { + // Indicate that this is "just" a tag (not a widget) + $attributesArray['data-ooui'] = json_encode( array( '_' => 'Tag' ) ); + } + return $attributesArray; + } + + /** + * Render element into HTML. + * + * @return string HTML serialization + */ + public function toString() { + $attributes = ''; + foreach ( $this->getGeneratedAttributes() as $key => $value ) { + if ( !preg_match( '/^[0-9a-zA-Z-]+$/', $key ) ) { + throw new Exception( 'Attribute name must consist of only ASCII letters, numbers and dash' ); + } + + if ( $key === 'href' || $key === 'action' ) { + // Make it impossible to point a link or a form to a 'javascript:' URL. There's no good way + // to blacklist them because of very lax parsing, so instead we whitelist known-good + // protocols (and also accept protocol-less and protocol-relative links). There are no good + // reasons to ever use 'javascript:' URLs anyway. + $protocolWhitelist = array( + // Sourced from MediaWiki's $wgUrlProtocols + 'bitcoin', 'ftp', 'ftps', 'geo', 'git', 'gopher', 'http', 'https', 'irc', 'ircs', + 'magnet', 'mailto', 'mms', 'news', 'nntp', 'redis', 'sftp', 'sip', 'sips', 'sms', 'ssh', + 'svn', 'tel', 'telnet', 'urn', 'worldwind', 'xmpp', + ); + + // Protocol-relative URLs are handled really badly by parse_url() + if ( substr( $value, 0, 2 ) === '//' ) { + $url = "http:$value"; + } else { + $url = $value; + } + // Must suppress warnings when the value is not a valid URL. parse_url() returns false then. + // @codingStandardsIgnoreStart + $scheme = @parse_url( $url, PHP_URL_SCHEME ); + // @codingStandardsIgnoreEnd + + if ( !( $scheme === null || in_array( strtolower( $scheme ), $protocolWhitelist ) ) ) { + throw new Exception( "Potentially unsafe '$key' attribute value. " . + "Scheme: '$scheme'; value: '$value'." ); + } + } + + // Use single-quotes around the attribute value in HTML, because + // some of the values might be JSON strings + // 1. Encode both single and double quotes (and other special chars) + $value = htmlspecialchars( $value, ENT_QUOTES ); + // 2. Decode double quotes, for readability. + $value = str_replace( '"', '"', $value ); + // 3. Wrap attribute value in single quotes in the HTML. + $attributes .= ' ' . $key . "='" . $value . "'"; + } + + // Content + $content = ''; + foreach ( $this->content as $part ) { + if ( is_string( $part ) ) { + $content .= htmlspecialchars( $part ); + } elseif ( $part instanceof Tag || $part instanceof HtmlSnippet ) { + $content .= (string)$part; + } + } + + if ( !preg_match( '/^[0-9a-zA-Z]+$/', $this->tag ) ) { + throw new Exception( 'Tag name must consist of only ASCII letters and numbers' ); + } + + // Tag + return '<' . $this->tag . $attributes . '>' . $content . '</' . $this->tag . '>'; + } + + /** + * Magic method implementation. + * + * PHP doesn't allow __toString to throw exceptions and will trigger a fatal error if it does. + * This is a wrapper around the real toString() to convert them to errors instead. + * + * @return string + */ + public function __toString() { + try { + return $this->toString(); + } catch ( Exception $ex ) { + trigger_error( (string)$ex, E_USER_ERROR ); + return ''; + } + } +} diff --git a/vendor/oojs/oojs-ui/php/Theme.php b/vendor/oojs/oojs-ui/php/Theme.php new file mode 100644 index 00000000..d36b6d82 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/Theme.php @@ -0,0 +1,58 @@ +<?php + +namespace OOUI; + +/** + * Theme logic. + * + * @abstract + */ +class Theme { + + /* Members */ + + private static $singleton; + + /* Static Methods */ + + public static function setSingleton( Theme $theme ) { + self::$singleton = $theme; + } + + public static function singleton() { + if ( !self::$singleton ) { + throw new Exception( __METHOD__ . ' was called with no singleton theme set.' ); + } + + return self::$singleton; + } + + /** + * Get a list of classes to be applied to a widget. + * + * The 'on' and 'off' lists combined MUST contain keys for all classes the theme adds or removes, + * otherwise state transitions will not work properly. + * + * @param Element $element Element for which to get classes + * @return array Categorized class names with `on` and `off` lists + */ + public function getElementClasses( Element $element ) { + return array( 'on' => array(), 'off' => array() ); + } + + /** + * Update CSS classes provided by the theme. + * + * For elements with theme logic hooks, this should be called any time there's a state change. + * + * @param Element $element Element for which to update classes + * @return array Categorized class names with `on` and `off` lists + */ + public function updateElementClasses( Element $element ) { + $classes = $this->getElementClasses( $element ); + + $element + ->removeClasses( $classes['off'] ) + ->addClasses( $classes['on'] ); + } +} diff --git a/vendor/oojs/oojs-ui/php/Widget.php b/vendor/oojs/oojs-ui/php/Widget.php new file mode 100644 index 00000000..04152aa5 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/Widget.php @@ -0,0 +1,71 @@ +<?php + +namespace OOUI; + +/** + * User interface control. + * + * @abstract + */ +class Widget extends Element { + + /* Properties */ + + /** + * Disabled. + * + * @var boolean Widget is disabled + */ + protected $disabled = false; + + /* Methods */ + + /** + * @param array $config Configuration options + * @param boolean $config['disabled'] Disable (default: false) + */ + public function __construct( array $config = array() ) { + // Initialize config + $config = array_merge( array( 'disabled' => false ), $config ); + + // Parent constructor + parent::__construct( $config ); + + // Initialization + $this->addClasses( array( 'oo-ui-widget' ) ); + $this->setDisabled( $config['disabled'] ); + } + + /** + * Check if the widget is disabled. + * + * @return boolean Button is disabled + */ + public function isDisabled() { + return $this->disabled; + } + + /** + * Set the disabled state of the widget. + * + * This should probably change the widgets' appearance and prevent it from being used. + * + * @param boolean $disabled Disable widget + * @chainable + */ + public function setDisabled( $disabled ) { + $this->disabled = !!$disabled; + $this->toggleClasses( array( 'oo-ui-widget-disabled' ), $this->disabled ); + $this->toggleClasses( array( 'oo-ui-widget-enabled' ), !$this->disabled ); + $this->setAttributes( array( 'aria-disabled' => $this->disabled ? 'true' : 'false' ) ); + + return $this; + } + + public function getConfig( &$config ) { + if ( $this->disabled ) { + $config['disabled'] = $this->disabled; + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/elements/ButtonElement.php b/vendor/oojs/oojs-ui/php/elements/ButtonElement.php new file mode 100644 index 00000000..f9acf2d8 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/elements/ButtonElement.php @@ -0,0 +1,102 @@ +<?php + +namespace OOUI; + +/** + * Element with a button. + * + * Buttons are used for controls which can be clicked. They can be configured to use tab indexing + * and access keys for accessibility purposes. + * + * @abstract + */ +class ButtonElement extends ElementMixin { + /** + * Button is framed. + * + * @var boolean + */ + protected $framed = false; + + /** + * Button's access key. + * + * @var string + */ + protected $accessKey = null; + + public static $targetPropertyName = 'button'; + + /** + * @param Element $element Element being mixed into + * @param array $config Configuration options + * @param boolean $config['framed'] Render button with a frame (default: true) + * @param string $config['accessKey'] Button's access key + */ + public function __construct( Element $element, array $config = array() ) { + // Parent constructor + $target = isset( $config['button'] ) ? $config['button'] : new Tag( 'a' ); + parent::__construct( $element, $target, $config ); + + // Initialization + $this->element->addClasses( array( 'oo-ui-buttonElement' ) ); + $this->target->addClasses( array( 'oo-ui-buttonElement-button' ) ); + $this->toggleFramed( isset( $config['framed'] ) ? $config['framed'] : true ); + $this->setAccessKey( isset( $config['accessKey'] ) ? $config['accessKey'] : null ); + $this->target->setAttributes( array( + 'role' => 'button', + ) ); + } + + /** + * Toggle frame. + * + * @param boolean $framed Make button framed, omit to toggle + * @chainable + */ + public function toggleFramed( $framed = null ) { + $this->framed = $framed !== null ? !!$framed : !$this->framed; + $this->element->toggleClasses( array( 'oo-ui-buttonElement-framed' ), $this->framed ); + $this->element->toggleClasses( array( 'oo-ui-buttonElement-frameless' ), !$this->framed ); + } + + /** + * Check if button has a frame. + * + * @return boolean Button is framed + */ + public function isFramed() { + return $this->framed; + } + + /** + * Set access key. + * + * @param string $accessKey Button's access key, use empty string to remove + * @chainable + */ + public function setAccessKey( $accessKey ) { + $accessKey = is_string( $accessKey ) && strlen( $accessKey ) ? $accessKey : null; + + if ( $this->accessKey !== $accessKey ) { + if ( $accessKey !== null ) { + $this->target->setAttributes( array( 'accesskey' => $accessKey ) ); + } else { + $this->target->removeAttributes( array( 'accesskey' ) ); + } + $this->accessKey = $accessKey; + } + + return $this; + } + + public function getConfig( &$config ) { + if ( $this->framed !== true ) { + $config['framed'] = $this->framed; + } + if ( $this->accessKey !== null ) { + $config['accessKey'] = $this->accessKey; + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/elements/FlaggedElement.php b/vendor/oojs/oojs-ui/php/elements/FlaggedElement.php new file mode 100644 index 00000000..bd5dc80d --- /dev/null +++ b/vendor/oojs/oojs-ui/php/elements/FlaggedElement.php @@ -0,0 +1,133 @@ +<?php + +namespace OOUI; + +/** + * Element with named flags that can be added, removed, listed and checked. + * + * A flag, when set, adds a CSS class on the `$element` by combining `oo-ui-flaggedElement-` with + * the flag name. Flags are primarily useful for styling. + * + * @abstract + */ +class FlaggedElement extends ElementMixin { + /** + * Flags. + * + * @var string + */ + protected $flags = array(); + + public static $targetPropertyName = 'flagged'; + + /** + * @param Element $element Element being mixed into + * @param array $config Configuration options + * @param string|string[] $config['flags'] Flags describing importance and functionality, e.g. + * 'primary', 'safe', 'progressive', 'destructive' or 'constructive' + */ + public function __construct( Element $element, array $config = array() ) { + // Parent constructor + $target = isset( $config['flagged'] ) ? $config['flagged'] : $element; + parent::__construct( $element, $target, $config ); + + // Initialization + $this->setFlags( isset( $config['flags'] ) ? $config['flags'] : null ); + } + + /** + * Check if a flag is set. + * + * @param string $flag Name of flag + * @return boolean Has flag + */ + public function hasFlag( $flag ) { + return isset( $this->flags[$flag] ); + } + + /** + * Get the names of all flags set. + * + * @return string[] Flag names + */ + public function getFlags() { + return array_keys( $this->flags ); + } + + /** + * Clear all flags. + * + * @chainable + */ + public function clearFlags() { + $remove = array(); + $classPrefix = 'oo-ui-flaggedElement-'; + + foreach ( $this->flags as $flag ) { + $remove[] = $classPrefix . $flag; + } + + $this->target->removeClasses( $remove ); + $this->flags = array(); + + return $this; + } + + /** + * Add one or more flags. + * + * @param string|array $flags One or more flags to add, or an array keyed by flag name + * containing boolean set/remove instructions. + * @chainable + */ + public function setFlags( $flags ) { + $add = array(); + $remove = array(); + $classPrefix = 'oo-ui-flaggedElement-'; + + if ( is_string( $flags ) ) { + // Set + if ( !isset( $this->flags[$flags] ) ) { + $this->flags[$flags] = true; + $add[] = $classPrefix . $flags; + } + } elseif ( is_array( $flags ) ) { + foreach ( $flags as $key => $value ) { + if ( is_numeric( $key ) ) { + // Set + if ( !isset( $this->flags[$value] ) ) { + $this->flags[$value] = true; + $add[] = $classPrefix . $value; + } + } else { + if ( $value ) { + // Set + if ( !isset( $this->flags[$key] ) ) { + $this->flags[$key] = true; + $add[] = $classPrefix . $key; + } + } else { + // Remove + if ( isset( $this->flags[$key] ) ) { + unset( $this->flags[$key] ); + $remove[] = $classPrefix . $key; + } + } + } + } + } + + $this->target + ->addClasses( $add ) + ->removeClasses( $remove ); + + return $this; + } + + public function getConfig( &$config ) { + if ( !empty( $this->flags ) ) { + $config['flags'] = $this->getFlags(); + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/elements/GroupElement.php b/vendor/oojs/oojs-ui/php/elements/GroupElement.php new file mode 100644 index 00000000..93d3c7a1 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/elements/GroupElement.php @@ -0,0 +1,129 @@ +<?php + +namespace OOUI; + +/** + * Element containing a sequence of child elements. + * + * @abstract + */ +class GroupElement extends ElementMixin { + /** + * List of items in the group. + * + * @var Element[] + */ + protected $items = array(); + + public static $targetPropertyName = 'group'; + + /** + * @param Element $element Element being mixed into + * @param array $config Configuration options + */ + public function __construct( Element $element, array $config = array() ) { + // Parent constructor + $target = isset( $config['group'] ) ? $config['group'] : new Tag( 'div' ); + parent::__construct( $element, $target, $config ); + } + + /** + * Check if there are no items. + * + * @return boolean Group is empty + */ + public function isEmpty() { + return !count( $this->items ); + } + + /** + * Get items. + * + * @return Element[] Items + */ + public function getItems() { + return $this->items; + } + + /** + * Add items. + * + * Adding an existing item will move it. + * + * @param Element[] $items Items + * @param number $index Index to insert items at + * @chainable + */ + public function addItems( array $items, $index = null ) { + foreach ( $items as $item ) { + // Check if item exists then remove it first, effectively "moving" it + $currentIndex = array_search( $item, $this->items ); + if ( $currentIndex !== false ) { + $this->removeItems( array( $item ) ); + // Adjust index to compensate for removal + if ( $currentIndex < $index ) { + $index--; + } + } + // Add the item + $item->setElementGroup( $this ); + } + + if ( $index === null || $index < 0 || $index >= count( $this->items ) ) { + $this->items = array_merge( $this->items, $items ); + } else { + array_splice( $this->items, $index, 0, $items ); + } + + // Update actual target element contents to reflect our list + $this->target->clearContent(); + call_user_func_array( array( $this->target, 'appendContent' ), $this->items ); + + return $this; + } + + /** + * Remove items. + * + * @param Element[] $items Items to remove + * @chainable + */ + public function removeItems( $items ) { + foreach ( $items as $item ) { + $index = array_search( $item, $this->items ); + if ( $index !== false ) { + $item->setElementGroup( null ); + array_splice( $this->items, $index, 1 ); + } + } + + // Update actual target element contents to reflect our list + $this->target->clearContent(); + call_user_func_array( array( $this->target, 'appendContent' ), $this->items ); + + return $this; + } + + /** + * Clear all items. + * + * Items will be detached, not removed, so they can be used later. + * + * @chainable + */ + public function clearItems() { + foreach ( $this->items as $item ) { + $item->setElementGroup( null ); + } + + $this->items = array(); + $this->target->clearContent(); + + return $this; + } + + public function getConfig( &$config ) { + $config['items'] = $this->items; + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/elements/IconElement.php b/vendor/oojs/oojs-ui/php/elements/IconElement.php new file mode 100644 index 00000000..b6d27376 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/elements/IconElement.php @@ -0,0 +1,76 @@ +<?php + +namespace OOUI; + +/** + * Element containing an icon. + * + * Icons are graphics, about the size of normal text. They can be used to aid the user in locating + * a control or convey information in a more space efficient way. Icons should rarely be used + * without labels; such as in a toolbar where space is at a premium or within a context where the + * meaning is very clear to the user. + * + * @abstract + */ +class IconElement extends ElementMixin { + /** + * Symbolic icon name. + * + * @var string + */ + protected $icon = null; + + public static $targetPropertyName = 'icon'; + + /** + * @param Element $element Element being mixed into + * @param array $config Configuration options + * @param string $config['icon'] Symbolic icon name + */ + public function __construct( Element $element, array $config = array() ) { + // Parent constructor + // FIXME 'iconElement' is a very stupid way to call '$icon' + $target = isset( $config['iconElement'] ) ? $config['iconElement'] : new Tag( 'span' ); + parent::__construct( $element, $target, $config ); + + // Initialization + $this->target->addClasses( array( 'oo-ui-iconElement-icon' ) ); + $this->setIcon( isset( $config['icon'] ) ? $config['icon'] : null ); + } + + /** + * Set icon name. + * + * @param string|null $icon Symbolic icon name + * @chainable + */ + public function setIcon( $icon = null ) { + if ( $this->icon !== null ) { + $this->target->removeClasses( array( 'oo-ui-icon-' . $this->icon ) ); + } + if ( $icon !== null ) { + $this->target->addClasses( array( 'oo-ui-icon-' . $icon ) ); + } + + $this->icon = $icon; + $this->element->toggleClasses( array( 'oo-ui-iconElement' ), (bool)$this->icon ); + + return $this; + } + + /** + * Get icon name. + * + * @return string Icon name + */ + public function getIcon() { + return $this->icon; + } + + public function getConfig( &$config ) { + if ( $this->icon !== null ) { + $config['icon'] = $this->icon; + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/elements/IndicatorElement.php b/vendor/oojs/oojs-ui/php/elements/IndicatorElement.php new file mode 100644 index 00000000..56238b6c --- /dev/null +++ b/vendor/oojs/oojs-ui/php/elements/IndicatorElement.php @@ -0,0 +1,78 @@ +<?php + +namespace OOUI; + +/** + * Element containing an indicator. + * + * Indicators are graphics, smaller than normal text. They can be used to describe unique status or + * behavior. Indicators should only be used in exceptional cases; such as a button that opens a menu + * instead of performing an action directly, or an item in a list which has errors that need to be + * resolved. + * + * @abstract + */ +class IndicatorElement extends ElementMixin { + /** + * Symbolic indicator name + * + * @var string|null + */ + protected $indicator = null; + + public static $targetPropertyName = 'indicator'; + + /** + * @param Element $element Element being mixed into + * @param array $config Configuration options + * @param string $config['indicator'] Symbolic indicator name + */ + public function __construct( Element $element, array $config = array() ) { + // Parent constructor + // FIXME 'indicatorElement' is a very stupid way to call '$indicator' + $target = isset( $config['indicatorElement'] ) + ? $config['indicatorElement'] + : new Tag( 'span' ); + parent::__construct( $element, $target, $config ); + + // Initialization + $this->target->addClasses( array( 'oo-ui-indicatorElement-indicator' ) ); + $this->setIndicator( isset( $config['indicator'] ) ? $config['indicator'] : null ); + } + + /** + * Set indicator name. + * + * @param string|null $indicator Symbolic name of indicator to use or null for no indicator + * @chainable + */ + public function setIndicator( $indicator = null ) { + if ( $this->indicator !== null ) { + $this->target->removeClasses( array( 'oo-ui-indicator-' . $this->indicator ) ); + } + if ( $indicator !== null ) { + $this->target->addClasses( array( 'oo-ui-indicator-' . $indicator ) ); + } + + $this->indicator = $indicator; + $this->element->toggleClasses( array( 'oo-ui-indicatorElement' ), (bool)$this->indicator ); + + return $this; + } + + /** + * Get indicator name. + * + * @return string Symbolic name of indicator + */ + public function getIndicator() { + return $this->indicator; + } + + public function getConfig( &$config ) { + if ( $this->indicator !== null ) { + $config['indicator'] = $this->indicator; + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/elements/LabelElement.php b/vendor/oojs/oojs-ui/php/elements/LabelElement.php new file mode 100644 index 00000000..d5cf7bee --- /dev/null +++ b/vendor/oojs/oojs-ui/php/elements/LabelElement.php @@ -0,0 +1,77 @@ +<?php + +namespace OOUI; + +/** + * Element containing a label. + * + * @abstract + */ +class LabelElement extends ElementMixin { + /** + * Label value. + * + * @var string|HtmlSnippet|null + */ + protected $label = null; + + public static $targetPropertyName = 'label'; + + /** + * @param Element $element Element being mixed into + * @param array $config Configuration options + * @param string|HtmlSnippet $config['label'] Label text + */ + public function __construct( Element $element, array $config = array() ) { + // Parent constructor + // FIXME 'labelElement' is a very stupid way to call '$label' + $target = isset( $config['labelElement'] ) ? $config['labelElement'] : new Tag( 'span' ); + parent::__construct( $element, $target, $config ); + + // Initialization + $this->target->addClasses( array( 'oo-ui-labelElement-label' ) ); + $this->setLabel( isset( $config['label'] ) ? $config['label'] : null ); + } + + /** + * Set the label. + * + * An empty string will result in the label being hidden. A string containing only whitespace will + * be converted to a single ` `. + * + * @param string|HtmlSnippet|null $label Label text + * @chainable + */ + public function setLabel( $label ) { + $this->label = $label; + + $this->target->clearContent(); + if ( $this->label !== null ) { + if ( is_string( $this->label ) && $this->label !== '' && trim( $this->label ) === '' ) { + $this->target->appendContent( new HtmlSnippet( ' ' ) ); + } else { + $this->target->appendContent( $label ); + } + } + + $this->element->toggleClasses( array( 'oo-ui-labelElement' ), !!$this->label ); + + return $this; + } + + /** + * Get the label. + * + * @return string|HtmlSnippet|null Label text + */ + public function getLabel() { + return $this->label; + } + + public function getConfig( &$config ) { + if ( $this->label !== null ) { + $config['label'] = $this->label; + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/elements/TabIndexedElement.php b/vendor/oojs/oojs-ui/php/elements/TabIndexedElement.php new file mode 100644 index 00000000..223b5371 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/elements/TabIndexedElement.php @@ -0,0 +1,88 @@ +<?php + +namespace OOUI; + +/** + * Element supporting "sequential focus navigation" using the 'tabindex' attribute. + * + * @abstract + */ +class TabIndexedElement extends ElementMixin { + /** + * Tab index value. + * + * @var number|null + */ + protected $tabIndex = null; + + public static $targetPropertyName = 'tabIndexed'; + + /** + * @param Element $element Element being mixed into + * @param array $config Configuration options + * @param number|null $config['tabIndex'] Tab index value. Use 0 to use default ordering, use -1 to + * prevent tab focusing, use null to suppress the `tabindex` attribute. (default: 0) + */ + public function __construct( Element $element, array $config = array() ) { + // Parent constructor + $target = isset( $config['tabIndexed'] ) ? $config['tabIndexed'] : $element; + parent::__construct( $element, $target, $config ); + + // Initialization + $this->setTabIndex( isset( $config['tabIndex'] ) ? $config['tabIndex'] : 0 ); + } + + /** + * Set tab index value. + * + * @param number|null $tabIndex Tab index value or null for no tab index + * @chainable + */ + public function setTabIndex( $tabIndex ) { + $tabIndex = is_numeric( $tabIndex ) ? $tabIndex : null; + + if ( $this->tabIndex !== $tabIndex ) { + $this->tabIndex = $tabIndex; + $this->updateTabIndex(); + } + + return $this; + } + + /** + * Update the tabIndex attribute, in case of changes to tabIndex or disabled + * state. + * + * @chainable + */ + public function updateTabIndex() { + $disabled = $this->element->isDisabled(); + if ( $this->tabIndex !== null ) { + $this->target->setAttributes( array( + // Do not index over disabled elements + 'tabindex' => $disabled ? -1 : $this->tabIndex, + // ChromeVox and NVDA do not seem to inherit this from parent elements + 'aria-disabled' => ( $disabled ? 'true' : 'false' ) + ) ); + } else { + $this->target->removeAttributes( array( 'tabindex', 'aria-disabled' ) ); + } + return $this; + } + + /** + * Get tab index value. + * + * @return number|null Tab index value + */ + public function getTabIndex() { + return $this->tabIndex; + } + + public function getConfig( &$config ) { + if ( $this->tabIndex !== 0 ) { + $config['tabIndex'] = $this->tabIndex; + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/elements/TitledElement.php b/vendor/oojs/oojs-ui/php/elements/TitledElement.php new file mode 100644 index 00000000..5f1317c4 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/elements/TitledElement.php @@ -0,0 +1,74 @@ +<?php + +namespace OOUI; + +/** + * Element with a title. + * + * Titles are rendered by the browser and are made visible when hovering the element. Titles are + * not visible on touch devices. + * + * @abstract + */ +class TitledElement extends ElementMixin { + /** + * Title text. + * + * @var string + */ + protected $title = null; + + public static $targetPropertyName = 'titled'; + + /** + * @param Element $element Element being mixed into + * @param array $config Configuration options + * @param string $config['title'] Title. If not provided, the static property 'title' is used. + */ + public function __construct( Element $element, array $config = array() ) { + // Parent constructor + $target = isset( $config['titled'] ) ? $config['titled'] : $element; + parent::__construct( $element, $target, $config ); + + // Initialization + $this->setTitle( + isset( $config['title'] ) ? $config['title'] : + ( isset( $element::$title ) ? $element::$title : null ) + ); + } + + /** + * Set title. + * + * @param string|null $title Title text or null for no title + * @chainable + */ + public function setTitle( $title ) { + if ( $this->title !== $title ) { + $this->title = $title; + if ( $title !== null ) { + $this->target->setAttributes( array( 'title' => $title ) ); + } else { + $this->target->removeAttributes( array( 'title' ) ); + } + } + + return $this; + } + + /** + * Get title. + * + * @return string Title string + */ + public function getTitle() { + return $this->title; + } + + public function getConfig( &$config ) { + if ( $this->title !== null ) { + $config['title'] = $this->title; + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/layouts/FieldLayout.php b/vendor/oojs/oojs-ui/php/layouts/FieldLayout.php new file mode 100644 index 00000000..ef0d4c6c --- /dev/null +++ b/vendor/oojs/oojs-ui/php/layouts/FieldLayout.php @@ -0,0 +1,140 @@ +<?php + +namespace OOUI; + +/** + * Layout made of a field and optional label. + * + * Available label alignment modes include: + * - left: Label is before the field and aligned away from it, best for when the user will be + * scanning for a specific label in a form with many fields + * - right: Label is before the field and aligned toward it, best for forms the user is very + * familiar with and will tab through field checking quickly to verify which field they are in + * - top: Label is before the field and above it, best for when the user will need to fill out all + * fields from top to bottom in a form with few fields + * - inline: Label is after the field and aligned toward it, best for small boolean fields like + * checkboxes or radio buttons + */ +class FieldLayout extends Layout { + + /** + * Alignment. + * + * @var string + */ + protected $align; + + /** + * Field widget to be laid out. + * + * @var Widget + */ + protected $fieldWidget; + + private $field, $body, $help; + + /** + * @param Widget $fieldWidget Field widget + * @param array $config Configuration options + * @param string $config['align'] Alignment mode, either 'left', 'right', 'top' or 'inline' + * (default: 'left') + * @param string $config['help'] Explanatory text shown as a '?' icon. + */ + public function __construct( $fieldWidget, array $config = array() ) { + // Allow passing positional parameters inside the config array + if ( is_array( $fieldWidget ) && isset( $fieldWidget['fieldWidget'] ) ) { + $config = $fieldWidget; + $fieldWidget = $config['fieldWidget']; + } + + $hasInputWidget = $fieldWidget instanceof InputWidget; + + // Config initialization + $config = array_merge( array( 'align' => 'left' ), $config ); + + // Parent constructor + parent::__construct( $config ); + + // Properties + $this->fieldWidget = $fieldWidget; + $this->field = new Tag( 'div' ); + $this->body = new Tag( $hasInputWidget ? 'label' : 'div' ); + if ( isset( $config['help'] ) ) { + $this->help = new ButtonWidget( array( + 'classes' => array( 'oo-ui-fieldLayout-help' ), + 'framed' => false, + 'icon' => 'info', + 'title' => $config['help'], + ) ); + } else { + $this->help = ''; + } + + // Mixins + $this->mixin( new LabelElement( $this, $config ) ); + + // Initialization + $this + ->addClasses( array( 'oo-ui-fieldLayout' ) ) + ->appendContent( $this->help, $this->body ); + $this->body->addClasses( array( 'oo-ui-fieldLayout-body' ) ); + $this->field + ->addClasses( array( 'oo-ui-fieldLayout-field' ) ) + ->toggleClasses( array( 'oo-ui-fieldLayout-disable' ), $this->fieldWidget->isDisabled() ) + ->appendContent( $this->fieldWidget ); + + $this->setAlignment( $config['align'] ); + } + + /** + * Get the field. + * + * @return Widget Field widget + */ + public function getField() { + return $this->fieldWidget; + } + + /** + * Set the field alignment mode. + * + * @param string $value Alignment mode, either 'left', 'right', 'top' or 'inline' + * @chainable + */ + protected function setAlignment( $value ) { + if ( $value !== $this->align ) { + // Default to 'left' + if ( !in_array( $value, array( 'left', 'right', 'top', 'inline' ) ) ) { + $value = 'left'; + } + // Reorder elements + $this->body->clearContent(); + if ( $value === 'inline' ) { + $this->body->appendContent( $this->field, $this->label ); + } else { + $this->body->appendContent( $this->label, $this->field ); + } + // Set classes. The following classes can be used here: + // * oo-ui-fieldLayout-align-left + // * oo-ui-fieldLayout-align-right + // * oo-ui-fieldLayout-align-top + // * oo-ui-fieldLayout-align-inline + if ( $this->align ) { + $this->removeClasses( array( 'oo-ui-fieldLayout-align-' . $this->align ) ); + } + $this->addClasses( array( 'oo-ui-fieldLayout-align-' . $value ) ); + $this->align = $value; + } + + return $this; + } + + public function getConfig( &$config ) { + $config['fieldWidget'] = $this->fieldWidget; + $config['align'] = $this->align; + if ( $this->help !== '' ) { + $config['help'] = $this->help->getTitle(); + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/layouts/FieldsetLayout.php b/vendor/oojs/oojs-ui/php/layouts/FieldsetLayout.php new file mode 100644 index 00000000..f9faa353 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/layouts/FieldsetLayout.php @@ -0,0 +1,32 @@ +<?php + +namespace OOUI; + +/** + * Layout made of a fieldset and optional legend. + * + * Just add FieldLayout items. + */ +class FieldsetLayout extends Layout { + /** + * @param array $config Configuration options + * @param FieldLayout[] $config['items'] Items to add + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $config ); + + // Mixins + $this->mixin( new IconElement( $this, $config ) ); + $this->mixin( new LabelElement( $this, $config ) ); + $this->mixin( new GroupElement( $this, $config ) ); + + // Initialization + $this + ->addClasses( array( 'oo-ui-fieldsetLayout' ) ) + ->prependContent( $this->icon, $this->label, $this->group ); + if ( isset( $config['items'] ) ) { + $this->addItems( $config['items'] ); + } + } +} diff --git a/vendor/oojs/oojs-ui/php/layouts/FormLayout.php b/vendor/oojs/oojs-ui/php/layouts/FormLayout.php new file mode 100644 index 00000000..ebeb89de --- /dev/null +++ b/vendor/oojs/oojs-ui/php/layouts/FormLayout.php @@ -0,0 +1,47 @@ +<?php + +namespace OOUI; + +/** + * Layout with an HTML form. + */ +class FormLayout extends Layout { + + /* Static properties */ + + public static $tagName = 'form'; + + /** + * @param array $config Configuration options + * @param string $config['method'] HTML form `method` attribute + * @param string $config['action'] HTML form `action` attribute + * @param string $config['enctype'] HTML form `enctype` attribute + * @param FieldsetLayout[] $config['items'] Items to add + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $config ); + + // Mixins + $this->mixin( new GroupElement( $this, array_merge( $config, array( 'group' => $this ) ) ) ); + + // Initialization + $attributeWhitelist = array( 'method', 'action', 'enctype' ); + $this + ->addClasses( array( 'oo-ui-formLayout' ) ) + ->setAttributes( array_intersect_key( $config, array_flip( $attributeWhitelist ) ) ); + if ( isset( $config['items'] ) ) { + $this->addItems( $config['items'] ); + } + } + + public function getConfig( &$config ) { + foreach ( array( 'method', 'action', 'enctype' ) as $attr ) { + $value = $this->getAttribute( $attr ); + if ( $value !== null ) { + $config[$attr] = $value; + } + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/layouts/PanelLayout.php b/vendor/oojs/oojs-ui/php/layouts/PanelLayout.php new file mode 100644 index 00000000..64931374 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/layouts/PanelLayout.php @@ -0,0 +1,61 @@ +<?php + +namespace OOUI; + +/** + * Layout that expands to cover the entire area of its parent, with optional scrolling and padding. + */ +class PanelLayout extends Layout { + /** + * @param array $config Configuration options + * @param boolean $config['scrollable'] Allow vertical scrolling (default: false) + * @param boolean $config['padded'] Pad the content from the edges (default: false) + * @param boolean $config['expanded'] Expand size to fill the entire parent element + * (default: true) + * @param boolean $config['framed'] Wrap in a frame to visually separate from outside content + * (default: false) + */ + public function __construct( array $config = array() ) { + // Config initialization + $config = array_merge( array( + 'scrollable' => false, + 'padded' => false, + 'expanded' => true, + 'framed' => false, + ), $config ); + + // Parent constructor + parent::__construct( $config ); + + // Initialization + $this->addClasses( array( 'oo-ui-panelLayout' ) ); + if ( $config['scrollable'] ) { + $this->addClasses( array( 'oo-ui-panelLayout-scrollable' ) ); + } + if ( $config['padded'] ) { + $this->addClasses( array( 'oo-ui-panelLayout-padded' ) ); + } + if ( $config['expanded'] ) { + $this->addClasses( array( 'oo-ui-panelLayout-expanded' ) ); + } + if ( $config['framed'] ) { + $this->addClasses( array( 'oo-ui-panelLayout-framed' ) ); + } + } + public function getConfig( &$config ) { + if ( $this->hasClass( 'oo-ui-panelLayout-scrollable' ) ) { + $config['scrollable'] = true; + } + if ( $this->hasClass( 'oo-ui-panelLayout-padded' ) ) { + $config['padded'] = true; + } + if ( !$this->hasClass( 'oo-ui-panelLayout-expanded' ) ) { + $config['expanded'] = false; + } + if ( $this->hasClass( 'oo-ui-panelLayout-framed' ) ) { + $config['framed'] = true; + } + $config['content'] = $this->content; + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/themes/ApexTheme.php b/vendor/oojs/oojs-ui/php/themes/ApexTheme.php new file mode 100644 index 00000000..e812736e --- /dev/null +++ b/vendor/oojs/oojs-ui/php/themes/ApexTheme.php @@ -0,0 +1,6 @@ +<?php + +namespace OOUI; + +class ApexTheme extends Theme { +} diff --git a/vendor/oojs/oojs-ui/php/themes/MediaWikiTheme.php b/vendor/oojs/oojs-ui/php/themes/MediaWikiTheme.php new file mode 100644 index 00000000..86e4c353 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/themes/MediaWikiTheme.php @@ -0,0 +1,39 @@ +<?php + +namespace OOUI; + +class MediaWikiTheme extends Theme { + + /* Methods */ + + public function getElementClasses( Element $element ) { + $variants = array( + 'warning' => false, + 'invert' => false, + 'progressive' => false, + 'constructive' => false, + 'destructive' => false + ); + + // Parent method + $classes = parent::getElementClasses( $element ); + + if ( $element->supports( array( 'hasFlag' ) ) ) { + $isFramed = $element->supports( array( 'isFramed' ) ) && $element->isFramed(); + if ( $isFramed && ( $element->isDisabled() || $element->hasFlag( 'primary' ) ) ) { + $variants['invert'] = true; + } else { + $variants['progressive'] = $element->hasFlag( 'progressive' ); + $variants['constructive'] = $element->hasFlag( 'constructive' ); + $variants['destructive'] = $element->hasFlag( 'destructive' ); + $variants['warning'] = $element->hasFlag( 'warning' ); + } + } + + foreach ( $variants as $variant => $toggle ) { + $classes[$toggle ? 'on' : 'off'][] = 'oo-ui-image-' . $variant; + } + + return $classes; + } +} diff --git a/vendor/oojs/oojs-ui/php/widgets/ButtonGroupWidget.php b/vendor/oojs/oojs-ui/php/widgets/ButtonGroupWidget.php new file mode 100644 index 00000000..79d3aaa1 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/widgets/ButtonGroupWidget.php @@ -0,0 +1,28 @@ +<?php + +namespace OOUI; + +/** + * Group widget for multiple related buttons. + * + * Use together with ButtonWidget. + */ +class ButtonGroupWidget extends Widget { + /** + * @param array $config Configuration options + * @param ButtonWidget[] $config['items'] Buttons to add + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $config ); + + // Mixins + $this->mixin( new GroupElement( $this, array_merge( $config, array( 'group' => $this ) ) ) ); + + // Initialization + $this->addClasses( array( 'oo-ui-buttonGroupWidget' ) ); + if ( isset( $config['items'] ) ) { + $this->addItems( $config['items'] ); + } + } +} diff --git a/vendor/oojs/oojs-ui/php/widgets/ButtonInputWidget.php b/vendor/oojs/oojs-ui/php/widgets/ButtonInputWidget.php new file mode 100644 index 00000000..b3bcb63b --- /dev/null +++ b/vendor/oojs/oojs-ui/php/widgets/ButtonInputWidget.php @@ -0,0 +1,110 @@ +<?php + +namespace OOUI; + +/** + * A button that is an input widget. Intended to be used within a FormLayout. + */ +class ButtonInputWidget extends InputWidget { + /* Properties */ + + /** + * Whether to use `<input/>` rather than `<button/>`. + * + * @var boolean + */ + protected $useInputTag; + + private $labelElementMixin; + + /** + * @param array $config Configuration options + * @param string $config['type'] HTML tag `type` attribute, may be 'button', 'submit' or 'reset' + * (default: 'button') + * @param boolean $config['useInputTag'] Whether to use `<input/>` rather than `<button/>`. Only + * useful if you need IE 6 support in a form with multiple buttons. If you use this option, + * icons and indicators will not be displayed, it won't be possible to have a non-plaintext + * label, and it won't be possible to set a value (which will internally become identical to the + * label). (default: false) + */ + public function __construct( array $config = array() ) { + // Configuration initialization + $config = array_merge( array( 'type' => 'button', 'useInputTag' => false ), $config ); + + // Properties (must be set before parent constructor, which calls setValue()) + $this->useInputTag = $config['useInputTag']; + + // Parent constructor + parent::__construct( $config ); + + // Mixins + $this->mixin( new ButtonElement( $this, + array_merge( $config, array( 'button' => $this->input ) ) ) ); + $this->mixin( new IconElement( $this, $config ) ); + $this->mixin( new IndicatorElement( $this, $config ) ); + // HACK: We need to have access to the mixin to override the setLabel() method + $this->mixin( $this->labelElementMixin = new LabelElement( $this, $config ) ); + $this->mixin( new TitledElement( $this, + array_merge( $config, array( 'titled' => $this->input ) ) ) ); + + // Initialization + if ( !$config['useInputTag'] ) { + $this->input->appendContent( $this->icon, $this->label, $this->indicator ); + } + + // HACK: This is done in LabelElement mixin, but doesn't call our overridden method because of + // how we implement mixins. Switching to traits will fix that. + $this->setLabel( isset( $config['label'] ) ? $config['label'] : null ); + + $this->addClasses( array( 'oo-ui-buttonInputWidget' ) ); + } + + protected function getInputElement( $config ) { + $input = new Tag( $config['useInputTag'] ? 'input' : 'button' ); + $input->setAttributes( array( 'type' => $config['type'] ) ); + return $input; + } + + /** + * Set label value. + * + * Overridden to support setting the 'value' of `<input/>` elements. + * + * @param string|null $label Label text + * @chainable + */ + public function setLabel( $label ) { + $this->labelElementMixin->setLabel( $label ); + + if ( $this->useInputTag ) { + // Discard non-plaintext labels + $label = is_string( $label ) ? $label : ''; + $this->input->setValue( $label ); + } + + return $this; + } + + /** + * Set the value of the input. + * + * Overridden to disable for `<input/>` elements, which have value identical to the label. + * + * @param string $value New value + * @chainable + */ + public function setValue( $value ) { + if ( !$this->useInputTag ) { + parent::setValue( $value ); + } + return $this; + } + + public function getConfig( &$config ) { + if ( $this->useInputTag ) { + $config['useInputTag'] = true; + } + $config['type'] = $this->input->getAttribute( 'type' ); + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/widgets/ButtonWidget.php b/vendor/oojs/oojs-ui/php/widgets/ButtonWidget.php new file mode 100644 index 00000000..f26608b1 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/widgets/ButtonWidget.php @@ -0,0 +1,166 @@ +<?php + +namespace OOUI; + +/** + * Generic widget for buttons. + */ +class ButtonWidget extends Widget { + + /** + * Hyperlink to visit when clicked. + * + * @var string + */ + protected $href = null; + + /** + * Target to open hyperlink in. + * + * @var string + */ + protected $target = null; + + /** + * Search engine traversal hint. + * + * True if search engines should avoid following this hyperlink. + * + * @var boolean + */ + protected $noFollow = true; + + /** + * @param array $config Configuration options + * @param string $config['href'] Hyperlink to visit when clicked + * @param string $config['target'] Target to open hyperlink in + * @param boolean $config['noFollow'] Search engine traversal hint (default: true) + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $config ); + + // Mixins + $this->mixin( new ButtonElement( $this, $config ) ); + $this->mixin( new IconElement( $this, $config ) ); + $this->mixin( new IndicatorElement( $this, $config ) ); + $this->mixin( new LabelElement( $this, $config ) ); + $this->mixin( new TitledElement( $this, + array_merge( $config, array( 'titled' => $this->button ) ) ) ); + $this->mixin( new FlaggedElement( $this, $config ) ); + $this->mixin( new TabIndexedElement( $this, + array_merge( $config, array( 'tabIndexed' => $this->button ) ) ) ); + + // Initialization + $this->button->appendContent( $this->icon, $this->label, $this->indicator ); + $this + ->addClasses( array( 'oo-ui-buttonWidget' ) ) + ->appendContent( $this->button ); + + $this->setHref( isset( $config['href'] ) ? $config['href'] : null ); + $this->setTarget( isset( $config['target'] ) ? $config['target'] : null ); + $this->setNoFollow( isset( $config['noFollow'] ) ? $config['noFollow'] : true ); + } + + /** + * Get hyperlink location. + * + * @return string Hyperlink location + */ + public function getHref() { + return $this->href; + } + + /** + * Get hyperlink target. + * + * @return string Hyperlink target + */ + public function getTarget() { + return $this->target; + } + + /** + * Get search engine traversal hint. + * + * @return boolean Whether search engines should avoid traversing this hyperlink + */ + public function getNoFollow() { + return $this->noFollow; + } + + /** + * Set hyperlink location. + * + * @param string|null $href Hyperlink location, null to remove + */ + public function setHref( $href ) { + $this->href = is_string( $href ) ? $href : null; + + $this->updateHref(); + + return $this; + } + + /** + * Update the href attribute, in case of changes to href or disabled + * state. + * + * @chainable + */ + public function updateHref() { + if ( $this->href !== null && !$this->isDisabled() ) { + $this->button->setAttributes( array( 'href' => $this->href ) ); + } else { + $this->button->removeAttributes( array( 'href' ) ); + } + return $this; + } + + /** + * Set hyperlink target. + * + * @param string|null $target Hyperlink target, null to remove + */ + public function setTarget( $target ) { + $this->target = is_string( $target ) ? $target : null; + + if ( $this->target !== null ) { + $this->button->setAttributes( array( 'target' => $target ) ); + } else { + $this->button->removeAttributes( array( 'target' ) ); + } + + return $this; + } + + /** + * Set search engine traversal hint. + * + * @param boolean $noFollow True if search engines should avoid traversing this hyperlink + */ + public function setNoFollow( $noFollow ) { + $this->noFollow = is_bool( $noFollow ) ? $noFollow : true; + + if ( $this->noFollow ) { + $this->button->setAttributes( array( 'rel' => 'nofollow' ) ); + } else { + $this->button->removeAttributes( array( 'rel' ) ); + } + + return $this; + } + + public function getConfig( &$config ) { + if ( $this->href !== null ) { + $config['href'] = $this->href; + } + if ( $this->target !== null ) { + $config['target'] = $this->target; + } + if ( $this->noFollow !== true ) { + $config['noFollow'] = $this->noFollow; + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/widgets/CheckboxInputWidget.php b/vendor/oojs/oojs-ui/php/widgets/CheckboxInputWidget.php new file mode 100644 index 00000000..bda09c66 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/widgets/CheckboxInputWidget.php @@ -0,0 +1,70 @@ +<?php + +namespace OOUI; + +/** + * Checkbox input widget. + */ +class CheckboxInputWidget extends InputWidget { + + /* Properties */ + + /** + * Whether the checkbox is selected. + * + * @var boolean + */ + protected $selected; + + /** + * @param array $config Configuration options + * @param boolean $config['selected'] Whether the checkbox is initially selected + * (default: false) + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $config ); + + // Initialization + $this->addClasses( array( 'oo-ui-checkboxInputWidget' ) ); + $this->setSelected( isset( $config['selected'] ) ? $config['selected'] : false ); + } + + protected function getInputElement( $config ) { + $input = new Tag( 'input' ); + $input->setAttributes( array( 'type' => 'checkbox' ) ); + return $input; + } + + /** + * Set selection state of this checkbox. + * + * @param boolean $state Whether the checkbox is selected + * @chainable + */ + public function setSelected( $state ) { + $this->selected = (bool)$state; + if ( $this->selected ) { + $this->input->setAttributes( array( 'checked' => 'checked' ) ); + } else { + $this->input->removeAttributes( array( 'checked' ) ); + } + return $this; + } + + /** + * Check if this checkbox is selected. + * + * @return boolean Checkbox is selected + */ + public function isSelected() { + return $this->selected; + } + + public function getConfig( &$config ) { + if ( $this->selected ) { + $config['selected'] = $this->selected; + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/widgets/DropdownInputWidget.php b/vendor/oojs/oojs-ui/php/widgets/DropdownInputWidget.php new file mode 100644 index 00000000..ae541a66 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/widgets/DropdownInputWidget.php @@ -0,0 +1,99 @@ +<?php + +namespace OOUI; + +/** + * Dropdown input widget, wrapping a `<select>` element. Intended to be used within a + * OO.ui.FormLayout. + */ +class DropdownInputWidget extends InputWidget { + + /** + * HTML `<option>` tags for this widget. + * @var Tag[] + */ + protected $options = array(); + + /** + * @param array $config Configuration options + * @param array[] $config['options'] Array of menu options in the format + * `array( 'data' => …, 'label' => … )` + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $config ); + + // Initialization + $this->setOptions( isset( $config['options'] ) ? $config['options'] : array() ); + $this->addClasses( array( 'oo-ui-dropdownInputWidget' ) ); + } + + protected function getInputElement( $config ) { + return new Tag( 'select' ); + } + + public function setValue( $value ) { + $this->value = $this->cleanUpValue( $value ); + foreach ( $this->options as &$opt ) { + if ( $opt->getAttribute( 'value' ) === $this->value ) { + $opt->setAttributes( array( 'selected' => 'selected' ) ); + } else { + $opt->removeAttributes( array( 'selected' ) ); + } + } + return $this; + } + + + /** + * Set the options available for this input. + * + * @param array[] $options Array of menu options in the format + * `array( 'data' => …, 'label' => … )` + * @chainable + */ + public function setOptions( $options ) { + $value = $this->getValue(); + $isValueAvailable = false; + $this->options = array(); + + // Rebuild the dropdown menu + $this->input->clearContent(); + foreach ( $options as $opt ) { + $option = new Tag( 'option' ); + $option->setAttributes( array( 'value' => $opt['data'] ) ); + $option->appendContent( isset( $opt['label'] ) ? $opt['label'] : $opt['data'] ); + + if ( $value === $opt['data'] ) { + $isValueAvailable = true; + } + + $this->options[] = $option; + $this->input->appendContent( $option ); + } + + // Restore the previous value, or reset to something sensible + if ( $isValueAvailable ) { + // Previous value is still available + $this->setValue( $value ); + } else { + // No longer valid, reset + if ( count( $options ) ) { + $this->setValue( $options[0]['data'] ); + } + } + + return $this; + } + + public function getConfig( &$config ) { + $o = array(); + foreach ( $this->options as $option ) { + $label = $option->content[0]; + $data = $option->getAttribute( 'value' ); + $o[] = array( 'data' => $data, 'label' => $label ); + } + $config['options'] = $o; + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/widgets/IconWidget.php b/vendor/oojs/oojs-ui/php/widgets/IconWidget.php new file mode 100644 index 00000000..f8273f37 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/widgets/IconWidget.php @@ -0,0 +1,34 @@ +<?php + +namespace OOUI; + +/** + * Icon widget. + * + * See IconElement for more information. + */ +class IconWidget extends Widget { + + /* Static properties */ + + public static $tagName = 'span'; + + /** + * @param array $config Configuration options + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $config ); + + // Mixins + $this->mixin( new IconElement( $this, + array_merge( $config, array( 'iconElement' => $this ) ) ) ); + $this->mixin( new TitledElement( $this, + array_merge( $config, array( 'titled' => $this ) ) ) ); + $this->mixin( new FlaggedElement( $this, + array_merge( $config, array( 'flagged' => $this ) ) ) ); + + // Initialization + $this->addClasses( array( 'oo-ui-iconWidget' ) ); + } +} diff --git a/vendor/oojs/oojs-ui/php/widgets/IndicatorWidget.php b/vendor/oojs/oojs-ui/php/widgets/IndicatorWidget.php new file mode 100644 index 00000000..01f2055d --- /dev/null +++ b/vendor/oojs/oojs-ui/php/widgets/IndicatorWidget.php @@ -0,0 +1,32 @@ +<?php + +namespace OOUI; + +/** + * Indicator widget. + * + * See IndicatorElement for more information. + */ +class IndicatorWidget extends Widget { + + /* Static properties */ + + public static $tagName = 'span'; + + /** + * @param array $config Configuration options + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $config ); + + // Mixins + $this->mixin( new IndicatorElement( $this, + array_merge( $config, array( 'indicatorElement' => $this ) ) ) ); + $this->mixin( new TitledElement( $this, + array_merge( $config, array( 'titled' => $this ) ) ) ); + + // Initialization + $this->addClasses( array( 'oo-ui-indicatorWidget' ) ); + } +} diff --git a/vendor/oojs/oojs-ui/php/widgets/InputWidget.php b/vendor/oojs/oojs-ui/php/widgets/InputWidget.php new file mode 100644 index 00000000..234d3145 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/widgets/InputWidget.php @@ -0,0 +1,144 @@ +<?php + +namespace OOUI; + +/** + * Base class for input widgets. + * + * @abstract + */ +class InputWidget extends Widget { + + /* Properties */ + + /** + * Input element. + * + * @var Tag + */ + protected $input; + + /** + * Input value. + * + * @var string + */ + protected $value = ''; + + /** + * @param array $config Configuration options + * @param string $config['name'] HTML input name (default: '') + * @param string $config['value'] Input value (default: '') + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $config ); + + // Properties + $this->input = $this->getInputElement( $config ); + + // Mixins + $this->mixin( new FlaggedElement( $this, + array_merge( $config, array( 'flagged' => $this ) ) ) ); + $this->mixin( new TabIndexedElement( $this, + array_merge( $config, array( 'tabIndexed' => $this->input ) ) ) ); + + // Initialization + if ( isset( $config['name'] ) ) { + $this->input->setAttributes( array( 'name' => $config['name'] ) ); + } + if ( $this->isDisabled() ) { + $this->input->setAttributes( array( 'disabled' => 'disabled' ) ); + } + $this + ->addClasses( array( 'oo-ui-inputWidget' ) ) + ->appendContent( $this->input ); + $this->appendContent( new Tag( 'span' ) ); + $this->setValue( isset( $config['value'] ) ? $config['value'] : null ); + } + + /** + * Get input element. + * + * @param array $config Configuration options + * @return Tag Input element + */ + protected function getInputElement( $config ) { + return new Tag( 'input' ); + } + + /** + * Get the value of the input. + * + * @return string Input value + */ + public function getValue() { + return $this->value; + } + + /** + * Sets the direction of the current input, either RTL or LTR + * + * @param boolean $isRTL + */ + public function setRTL( $isRTL ) { + if ( $isRTL ) { + $this->input->removeClasses( array( 'oo-ui-ltr' ) ); + $this->input->addClasses( array( 'oo-ui-rtl' ) ); + } else { + $this->input->removeClasses( array( 'oo-ui-rtl' ) ); + $this->input->addClasses( array( 'oo-ui-ltr' ) ); + } + } + + /** + * Set the value of the input. + * + * @param string $value New value + * @chainable + */ + public function setValue( $value ) { + $this->value = $this->cleanUpValue( $value ); + $this->input->setValue( $this->value ); + return $this; + } + + /** + * Clean up incoming value. + * + * Ensures value is a string, and converts null to empty string. + * + * @param string $value Original value + * @return string Cleaned up value + */ + protected function cleanUpValue( $value ) { + if ( $value === null ) { + return ''; + } else { + return (string)$value; + } + } + + public function setDisabled( $state ) { + parent::setDisabled( $state ); + if ( isset( $this->input ) ) { + if ( $this->isDisabled() ) { + $this->input->setAttributes( array( 'disabled' => 'disabled' ) ); + } else { + $this->input->removeAttributes( array( 'disabled' ) ); + } + } + return $this; + } + + public function getConfig( &$config ) { + $name = $this->input->getAttribute( 'name' ); + if ( $name !== null ) { + $config['name'] = $name; + } + if ( $this->value !== '' ) { + $config['value'] = $this->value; + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/widgets/LabelWidget.php b/vendor/oojs/oojs-ui/php/widgets/LabelWidget.php new file mode 100644 index 00000000..b59a5f25 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/widgets/LabelWidget.php @@ -0,0 +1,48 @@ +<?php + +namespace OOUI; + +/** + * Label widget. + */ +class LabelWidget extends Widget { + + /* Static properties */ + + public static $tagName = 'span'; + + /* Properties */ + + /** + * Associated input element. + * + * @var InputWidget|null + */ + protected $input; + + /** + * @param array $config Configuration options + * @param InputWidget $config['input'] Input widget this label is for + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $config ); + + // Mixins + $this->mixin( new LabelElement( $this, + array_merge( $config, array( 'labelElement' => $this ) ) ) ); + + // Properties + $this->input = isset( $config['input'] ) ? $config['input'] : null; + + // Initialization + $this->addClasses( array( 'oo-ui-labelWidget' ) ); + } + + public function getConfig( &$config ) { + if ( $this->input !== null ) { + $config['input'] = $this->input; + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/widgets/RadioInputWidget.php b/vendor/oojs/oojs-ui/php/widgets/RadioInputWidget.php new file mode 100644 index 00000000..26da29d0 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/widgets/RadioInputWidget.php @@ -0,0 +1,60 @@ +<?php + +namespace OOUI; + +/** + * Radio input widget. + */ +class RadioInputWidget extends InputWidget { + + /** + * @param array $config Configuration options + * @param boolean $config['selected'] Whether the radio button is initially selected + * (default: false) + */ + public function __construct( array $config = array() ) { + // Parent constructor + parent::__construct( $config ); + + // Initialization + $this->addClasses( array( 'oo-ui-radioInputWidget' ) ); + $this->setSelected( isset( $config['selected'] ) ? $config['selected'] : false ); + } + + protected function getInputElement( $config ) { + $input = new Tag( 'input' ); + $input->setAttributes( array( 'type' => 'radio' ) ); + return $input; + } + + /** + * Set selection state of this radio button. + * + * @param boolean $state Whether the button is selected + */ + public function setSelected( $state ) { + // RadioInputWidget doesn't track its state. + if ( $state ) { + $this->input->setAttributes( array( 'checked' => 'checked' ) ); + } else { + $this->input->removeAttributes( array( 'checked' ) ); + } + return $this; + } + + /** + * Check if this radio button is selected. + * + * @return boolean Radio is selected + */ + public function isSelected() { + return $this->input->getAttribute( 'checked' ) === 'checked'; + } + + public function getConfig( &$config ) { + if ( $this->isSelected() ) { + $config['selected'] = true; + } + return parent::getConfig( $config ); + } +} diff --git a/vendor/oojs/oojs-ui/php/widgets/TextInputWidget.php b/vendor/oojs/oojs-ui/php/widgets/TextInputWidget.php new file mode 100644 index 00000000..a5f31f74 --- /dev/null +++ b/vendor/oojs/oojs-ui/php/widgets/TextInputWidget.php @@ -0,0 +1,151 @@ +<?php + +namespace OOUI; + +/** + * Input widget with a text field. + */ +class TextInputWidget extends InputWidget { + + /* Properties */ + + /** + * Prevent changes. + * + * @var boolean + */ + protected $readOnly = false; + + /** + * Allow multiple lines of text. + * + * @var boolean + */ + protected $multiline = false; + + /** + * @param array $config Configuration options + * @param string $config['type'] HTML tag `type` attribute (default: 'text') + * @param string $config['placeholder'] Placeholder text + * @param boolean $config['autofocus'] Ask the browser to focus this widget, using the 'autofocus' + * HTML attribute (default: false) + * @param boolean $config['readOnly'] Prevent changes (default: false) + * @param number $config['maxLength'] Maximum allowed number of characters to input + * @param boolean $config['multiline'] Allow multiple lines of text (default: false) + * @param boolean $config['required'] Mark the field as required (default: false) + */ + public function __construct( array $config = array() ) { + // Config initialization + $config = array_merge( array( + 'readOnly' => false, + 'autofocus' => false, + 'required' => false, + ), $config ); + + // Parent constructor + parent::__construct( $config ); + + // Properties + $this->multiline = isset( $config['multiline'] ) ? (bool)$config['multiline'] : false; + + // Mixins + $this->mixin( new IconElement( $this, $config ) ); + $this->mixin( new IndicatorElement( $this, $config ) ); + + // Initialization + $this + ->addClasses( array( 'oo-ui-textInputWidget' ) ) + ->appendContent( $this->icon, $this->indicator ); + $this->setReadOnly( $config['readOnly'] ); + if ( isset( $config['placeholder'] ) ) { + $this->input->setAttributes( array( 'placeholder' => $config['placeholder'] ) ); + } + if ( isset( $config['maxLength'] ) ) { + $this->input->setAttributes( array( 'maxlength' => $config['maxLength'] ) ); + } + if ( $config['autofocus'] ) { + $this->input->setAttributes( array( 'autofocus' => 'autofocus' ) ); + } + if ( $config['required'] ) { + $this->input->setAttributes( array( 'required' => 'required', 'aria-required' => 'true' ) ); + } + } + + /** + * Check if the widget is read-only. + * + * @return boolean + */ + public function isReadOnly() { + return $this->readOnly; + } + + /** + * Set the read-only state of the widget. This should probably change the widget's appearance and + * prevent it from being used. + * + * @param boolean $state Make input read-only + * @chainable + */ + public function setReadOnly( $state ) { + $this->readOnly = (bool)$state; + if ( $this->readOnly ) { + $this->input->setAttributes( array( 'readonly' => 'readonly' ) ); + } else { + $this->input->removeAttributes( array( 'readonly' ) ); + } + return $this; + } + + protected function getInputElement( $config ) { + if ( isset( $config['multiline'] ) && $config['multiline'] ) { + return new Tag( 'textarea' ); + } else { + $type = isset( $config['type'] ) ? $config['type'] : 'text'; + $input = new Tag( 'input' ); + $input->setAttributes( array( 'type' => $type ) ); + return $input; + } + } + + /** + * Check if input supports multiple lines. + * + * @return boolean + */ + public function isMultiline() { + return (bool)$this->multiline; + } + + public function getConfig( &$config ) { + if ( $this->isMultiline() ) { + $config['multiline'] = true; + } else { + $type = $this->input->getAttribute( 'type' ); + if ( $type !== 'text' ) { + $config['type'] = $type; + } + } + if ( $this->isReadOnly() ) { + $config['readOnly'] = true; + } + $placeholder = $this->input->getAttribute( 'placeholder' ); + if ( $placeholder !== null ) { + $config['placeholder'] = $placeholder; + } + $maxlength = $this->input->getAttribute( 'maxlength' ); + if ( $maxlength !== null ) { + $config['maxLength'] = $maxlength; + } + $autofocus = $this->input->getAttribute( 'autofocus' ); + if ( $autofocus !== null ) { + $config['autofocus'] = true; + } + $required = $this->input->getAttribute( 'required' ); + $ariarequired = $this->input->getAttribute( 'aria-required' ); + if ( ( $required !== null ) || ( $ariarequired !== null ) ) { + $config['required'] = true; + } + return parent::getConfig( $config ); + } +} |