From f6d65e533c62f6deb21342d4901ece24497b433e Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Thu, 4 Jun 2015 07:31:04 +0200 Subject: Update to MediaWiki 1.25.1 --- vendor/oojs/oojs-ui/php/Element.php | 297 +++++++++++++++++ vendor/oojs/oojs-ui/php/ElementMixin.php | 55 ++++ vendor/oojs/oojs-ui/php/Exception.php | 6 + vendor/oojs/oojs-ui/php/HtmlSnippet.php | 36 ++ vendor/oojs/oojs-ui/php/Layout.php | 21 ++ vendor/oojs/oojs-ui/php/Tag.php | 365 +++++++++++++++++++++ vendor/oojs/oojs-ui/php/Theme.php | 58 ++++ vendor/oojs/oojs-ui/php/Widget.php | 71 ++++ vendor/oojs/oojs-ui/php/elements/ButtonElement.php | 102 ++++++ .../oojs/oojs-ui/php/elements/FlaggedElement.php | 133 ++++++++ vendor/oojs/oojs-ui/php/elements/GroupElement.php | 129 ++++++++ vendor/oojs/oojs-ui/php/elements/IconElement.php | 76 +++++ .../oojs/oojs-ui/php/elements/IndicatorElement.php | 78 +++++ vendor/oojs/oojs-ui/php/elements/LabelElement.php | 77 +++++ .../oojs-ui/php/elements/TabIndexedElement.php | 88 +++++ vendor/oojs/oojs-ui/php/elements/TitledElement.php | 74 +++++ vendor/oojs/oojs-ui/php/layouts/FieldLayout.php | 140 ++++++++ vendor/oojs/oojs-ui/php/layouts/FieldsetLayout.php | 32 ++ vendor/oojs/oojs-ui/php/layouts/FormLayout.php | 47 +++ vendor/oojs/oojs-ui/php/layouts/PanelLayout.php | 61 ++++ vendor/oojs/oojs-ui/php/themes/ApexTheme.php | 6 + vendor/oojs/oojs-ui/php/themes/MediaWikiTheme.php | 39 +++ .../oojs/oojs-ui/php/widgets/ButtonGroupWidget.php | 28 ++ .../oojs/oojs-ui/php/widgets/ButtonInputWidget.php | 110 +++++++ vendor/oojs/oojs-ui/php/widgets/ButtonWidget.php | 166 ++++++++++ .../oojs-ui/php/widgets/CheckboxInputWidget.php | 70 ++++ .../oojs-ui/php/widgets/DropdownInputWidget.php | 99 ++++++ vendor/oojs/oojs-ui/php/widgets/IconWidget.php | 34 ++ .../oojs/oojs-ui/php/widgets/IndicatorWidget.php | 32 ++ vendor/oojs/oojs-ui/php/widgets/InputWidget.php | 144 ++++++++ vendor/oojs/oojs-ui/php/widgets/LabelWidget.php | 48 +++ .../oojs/oojs-ui/php/widgets/RadioInputWidget.php | 60 ++++ .../oojs/oojs-ui/php/widgets/TextInputWidget.php | 151 +++++++++ 33 files changed, 2933 insertions(+) create mode 100644 vendor/oojs/oojs-ui/php/Element.php create mode 100644 vendor/oojs/oojs-ui/php/ElementMixin.php create mode 100644 vendor/oojs/oojs-ui/php/Exception.php create mode 100644 vendor/oojs/oojs-ui/php/HtmlSnippet.php create mode 100644 vendor/oojs/oojs-ui/php/Layout.php create mode 100644 vendor/oojs/oojs-ui/php/Tag.php create mode 100644 vendor/oojs/oojs-ui/php/Theme.php create mode 100644 vendor/oojs/oojs-ui/php/Widget.php create mode 100644 vendor/oojs/oojs-ui/php/elements/ButtonElement.php create mode 100644 vendor/oojs/oojs-ui/php/elements/FlaggedElement.php create mode 100644 vendor/oojs/oojs-ui/php/elements/GroupElement.php create mode 100644 vendor/oojs/oojs-ui/php/elements/IconElement.php create mode 100644 vendor/oojs/oojs-ui/php/elements/IndicatorElement.php create mode 100644 vendor/oojs/oojs-ui/php/elements/LabelElement.php create mode 100644 vendor/oojs/oojs-ui/php/elements/TabIndexedElement.php create mode 100644 vendor/oojs/oojs-ui/php/elements/TitledElement.php create mode 100644 vendor/oojs/oojs-ui/php/layouts/FieldLayout.php create mode 100644 vendor/oojs/oojs-ui/php/layouts/FieldsetLayout.php create mode 100644 vendor/oojs/oojs-ui/php/layouts/FormLayout.php create mode 100644 vendor/oojs/oojs-ui/php/layouts/PanelLayout.php create mode 100644 vendor/oojs/oojs-ui/php/themes/ApexTheme.php create mode 100644 vendor/oojs/oojs-ui/php/themes/MediaWikiTheme.php create mode 100644 vendor/oojs/oojs-ui/php/widgets/ButtonGroupWidget.php create mode 100644 vendor/oojs/oojs-ui/php/widgets/ButtonInputWidget.php create mode 100644 vendor/oojs/oojs-ui/php/widgets/ButtonWidget.php create mode 100644 vendor/oojs/oojs-ui/php/widgets/CheckboxInputWidget.php create mode 100644 vendor/oojs/oojs-ui/php/widgets/DropdownInputWidget.php create mode 100644 vendor/oojs/oojs-ui/php/widgets/IconWidget.php create mode 100644 vendor/oojs/oojs-ui/php/widgets/IndicatorWidget.php create mode 100644 vendor/oojs/oojs-ui/php/widgets/InputWidget.php create mode 100644 vendor/oojs/oojs-ui/php/widgets/LabelWidget.php create mode 100644 vendor/oojs/oojs-ui/php/widgets/RadioInputWidget.php create mode 100644 vendor/oojs/oojs-ui/php/widgets/TextInputWidget.php (limited to 'vendor/oojs/oojs-ui/php') 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 @@ +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 @@ +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 @@ +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 @@ +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 @@ +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 . '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 @@ + 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 @@ + 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 @@ +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 @@ +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 @@ +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 @@ +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 @@ +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 @@ +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 @@ +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 @@ +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 @@ + '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 @@ +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 @@ +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 @@ + 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 @@ + 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 @@ +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 @@ +` rather than `