]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blobdiff - vendor/wikimedia/css-sanitizer/src/Objects/CSSObjectList.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / vendor / wikimedia / css-sanitizer / src / Objects / CSSObjectList.php
diff --git a/vendor/wikimedia/css-sanitizer/src/Objects/CSSObjectList.php b/vendor/wikimedia/css-sanitizer/src/Objects/CSSObjectList.php
new file mode 100644 (file)
index 0000000..42ecac5
--- /dev/null
@@ -0,0 +1,243 @@
+<?php
+/**
+ * @file
+ * @license https://opensource.org/licenses/Apache-2.0 Apache-2.0
+ */
+
+namespace Wikimedia\CSS\Objects;
+
+use Wikimedia\CSS\Util;
+
+/**
+ * Represent a list of CSS objects
+ */
+class CSSObjectList implements \Countable, \SeekableIterator, \ArrayAccess, CSSObject {
+
+       /** @var string The specific class of object contained */
+       protected static $objectType;
+
+       /** @var CSSObject[] The objects contained */
+       protected $objects;
+
+       /** @var int */
+       protected $offset = 0;
+
+       /**
+        * Additional validation for objects
+        * @param CSSObject[] $objects
+        */
+       protected static function testObjects( array $objects ) {
+       }
+
+       /**
+        * @param CSSObject[] $objects
+        */
+       public function __construct( array $objects = [] ) {
+               Util::assertAllInstanceOf( $objects, static::$objectType, static::class );
+               static::testObjects( $objects );
+               $this->objects = array_values( $objects );
+       }
+
+       /**
+        * Insert one or more objects into the list
+        * @param CSSObject|CSSObject[]|CSSObjectList $objects An object to add, or an array of objects.
+        * @param int $index Insert the objects at this index. If omitted, the
+        *  objects are added at the end.
+        */
+       public function add( $objects, $index = null ) {
+               if ( $objects instanceof static ) {
+                       $objects = $objects->objects;
+               } elseif ( is_array( $objects ) ) {
+                       Util::assertAllInstanceOf( $objects, static::$objectType, static::class );
+                       $objects = array_values( $objects );
+                       static::testObjects( $objects );
+               } else {
+                       if ( !$objects instanceof static::$objectType ) {
+                               throw new \InvalidArgumentException(
+                                       static::class . ' may only contain instances of ' . static::$objectType . '.'
+                               );
+                       }
+                       $objects = [ $objects ];
+                       static::testObjects( $objects );
+               }
+
+               if ( $index === null ) {
+                       $index = count( $this->objects );
+               } elseif ( $index < 0 || $index > count( $this->objects ) ) {
+                       throw new \OutOfBoundsException( 'Index is out of range.' );
+               }
+
+               array_splice( $this->objects, $index, 0, $objects );
+               if ( $this->offset > $index ) {
+                       $this->offset += count( $objects );
+               }
+       }
+
+       /**
+        * Remove an object from the list
+        * @param int $index
+        * @return CSSObject The removed object
+        */
+       public function remove( $index ) {
+               if ( $index < 0 || $index >= count( $this->objects ) ) {
+                       throw new \OutOfBoundsException( 'Index is out of range.' );
+               }
+               $ret = $this->objects[$index];
+               array_splice( $this->objects, $index, 1 );
+
+               // This works most sanely with foreach() and removing the current index
+               if ( $this->offset >= $index ) {
+                       $this->offset--;
+               }
+
+               return $ret;
+       }
+
+       /**
+        * Extract a slice of the list
+        * @param int $offset
+        * @param int|null $length
+        * @return CSSObject[] The objects in the slice
+        */
+       public function slice( $offset, $length = null ) {
+               return array_slice( $this->objects, $offset, $length );
+       }
+
+       /**
+        * Clear the list
+        */
+       public function clear() {
+               $this->objects = [];
+               $this->offset = 0;
+       }
+
+       // \Countable interface
+
+       public function count() {
+               return count( $this->objects );
+       }
+
+       // \SeekableIterator interface
+
+       public function seek( $offset ) {
+               if ( $offset < 0 || $offset >= count( $this->objects ) ) {
+                       throw new \OutOfBoundsException( 'Offset is out of range.' );
+               }
+               $this->offset = $offset;
+       }
+
+       public function current() {
+               return isset( $this->objects[$this->offset] ) ? $this->objects[$this->offset] : null;
+       }
+
+       public function key() {
+               return $this->offset;
+       }
+
+       public function next() {
+               $this->offset++;
+       }
+
+       public function rewind() {
+               $this->offset = 0;
+       }
+
+       public function valid() {
+               return isset( $this->objects[$this->offset] );
+       }
+
+       // \ArrayAccess interface
+
+       public function offsetExists( $offset ) {
+               return isset( $this->objects[$offset] );
+       }
+
+       public function offsetGet( $offset ) {
+               if ( !is_numeric( $offset ) || (float)(int)$offset !== (float)$offset ) {
+                       throw new \InvalidArgumentException( 'Offset must be an integer.' );
+               }
+               if ( $offset < 0 || $offset > count( $this->objects ) ) {
+                       throw new \OutOfBoundsException( 'Offset is out of range.' );
+               }
+               return $this->objects[$offset];
+       }
+
+       public function offsetSet( $offset, $value ) {
+               if ( !$value instanceof static::$objectType ) {
+                       throw new \InvalidArgumentException(
+                               static::class . ' may only contain instances of ' . static::$objectType . '.'
+                       );
+               }
+               static::testObjects( [ $value ] );
+               if ( !is_numeric( $offset ) || (float)(int)$offset !== (float)$offset ) {
+                       throw new \InvalidArgumentException( 'Offset must be an integer.' );
+               }
+               if ( $offset < 0 || $offset > count( $this->objects ) ) {
+                       throw new \OutOfBoundsException( 'Offset is out of range.' );
+               }
+               $this->objects[$offset] = $value;
+       }
+
+       public function offsetUnset( $offset ) {
+               if ( isset( $this->objects[$offset] ) && $offset !== count( $this->objects ) - 1 ) {
+                       throw new \OutOfBoundsException( 'Cannot leave holes in the list.' );
+               }
+               unset( $this->objects[$offset] );
+       }
+
+       // CSSObject interface
+
+       public function getPosition() {
+               $ret = null;
+               foreach ( $this->objects as $obj ) {
+                       $pos = $obj->getPosition();
+                       if ( $pos[0] >= 0 && (
+                               !$ret || $pos[0] < $ret[0] || $pos[0] === $ret[0] && $pos[1] < $ret[1]
+                       ) ) {
+                               $ret = $pos;
+                       }
+               }
+               return $ret ?: [ -1, -1 ];
+       }
+
+       /**
+        * Return the tokens to use to separate list items
+        * @param CSSObject $left
+        * @param CSSObject|null $right
+        * @return Token[]
+        */
+       protected function getSeparator( CSSObject $left, CSSObject $right = null ) {
+               return [];
+       }
+
+       /**
+        * @param string $function Function to call, toTokenArray() or toComponentValueArray()
+        */
+       private function toTokenOrCVArray( $function ) {
+               $ret = [];
+               $l = count( $this->objects );
+               for ( $i = 0; $i < $l; $i++ ) {
+                       // Manually looping and appending turns out to be noticably faster than array_merge.
+                       foreach ( $this->objects[$i]->$function() as $v ) {
+                               $ret[] = $v;
+                       }
+                       $sep = $this->getSeparator( $this->objects[$i], $i + 1 < $l ? $this->objects[$i + 1] : null );
+                       foreach ( $sep as $v ) {
+                               $ret[] = $v;
+                       }
+               }
+               return $ret;
+       }
+
+       public function toTokenArray() {
+               return $this->toTokenOrCVArray( __FUNCTION__ );
+       }
+
+       public function toComponentValueArray() {
+               return $this->toTokenOrCVArray( __FUNCTION__ );
+       }
+
+       public function __toString() {
+               return Util::stringify( $this );
+       }
+}