Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 39 and 400]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Autoprefixer.
  19   *
  20   * This autoprefixer has been developed to satisfy the basic needs of the
  21   * theme Boost when working with Bootstrap 4 alpha. We do not recommend
  22   * that this tool is shared, nor used outside of this theme.
  23   *
  24   * @package    theme_boost
  25   * @copyright  2016 Frédéric Massart - FMCorz.net
  26   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  27   */
  28  
  29  namespace theme_boost;
  30  defined('MOODLE_INTERNAL') || die();
  31  
  32  use Sabberworm\CSS\CSSList\CSSList;
  33  use Sabberworm\CSS\CSSList\Document;
  34  use Sabberworm\CSS\CSSList\KeyFrame;
  35  use Sabberworm\CSS\OutputFormat;
  36  use Sabberworm\CSS\Parser;
  37  use Sabberworm\CSS\Property\AtRule;
  38  use Sabberworm\CSS\Property\Selector;
  39  use Sabberworm\CSS\Rule\Rule;
  40  use Sabberworm\CSS\RuleSet\AtRuleSet;
  41  use Sabberworm\CSS\RuleSet\DeclarationBlock;
  42  use Sabberworm\CSS\RuleSet\RuleSet;
  43  use Sabberworm\CSS\Settings;
  44  use Sabberworm\CSS\Value\CSSFunction;
  45  use Sabberworm\CSS\Value\CSSString;
  46  use Sabberworm\CSS\Value\PrimitiveValue;
  47  use Sabberworm\CSS\Value\RuleValueList;
  48  use Sabberworm\CSS\Value\Size;
  49  use Sabberworm\CSS\Value\ValueList;
  50  
  51  
  52  /**
  53   * Autoprefixer class.
  54   *
  55   * Very basic implementation covering simple needs for Bootstrap 4.
  56   *
  57   * @package    theme_boost
  58   * @copyright  2016 Frédéric Massart - FMCorz.net
  59   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  60   */
  61  class autoprefixer {
  62  
  63      /** @var object The CSS tree. */
  64      protected $tree;
  65  
  66      /** @var string Pseudo classes regex. */
  67      protected $pseudosregex;
  68  
  69      /** @var array At rules prefixes. */
  70      protected static $atrules = [
  71          'keyframes' => ['-webkit-', '-o-']
  72      ];
  73  
  74      /** @var array Pseudo classes prefixes. */
  75      protected static $pseudos = [
  76          '::placeholder' => ['::-webkit-input-placeholder', '::-moz-placeholder', ':-ms-input-placeholder']
  77      ];
  78  
  79      /** @var array Rule properties prefixes. */
  80      protected static $rules = [
  81          'animation' => ['-webkit-'],
  82          'appearance' => ['-webkit-', '-moz-'],
  83          'backface-visibility' => ['-webkit-'],
  84          'box-sizing' => ['-webkit-'],
  85          'box-shadow' => ['-webkit-'],
  86          'background-clip' => ['-webkit-'],
  87          'background-size' => ['-webkit-'],
  88          'box-shadow' => ['-webkit-'],
  89          'column-count' => ['-webkit-', '-moz-'],
  90          'column-gap' => ['-webkit-', '-moz-'],
  91          'perspective' => ['-webkit-'],
  92          'touch-action' => ['-ms-'],
  93          'transform' => ['-webkit-', '-moz-', '-ms-', '-o-'],
  94          'transition' => ['-webkit-', '-o-'],
  95          'transition-timing-function' => ['-webkit-', '-o-'],
  96          'transition-duration' => ['-webkit-', '-o-'],
  97          'transition-property' => ['-webkit-', '-o-'],
  98          'user-select' => ['-webkit-', '-moz-', '-ms-'],
  99      ];
 100  
 101      /**
 102       * Constructor.
 103       *
 104       * @param Document $tree The CSS tree.
 105       */
 106      public function __construct(Document $tree) {
 107          debugging('theme_boost\autoprefixer() is deprecated. Required prefixes for Bootstrap ' .
 108              'are now in theme/boost/scss/moodle/prefixes.scss', DEBUG_DEVELOPER);
 109          $this->tree = $tree;
 110  
 111          $pseudos = array_map(function($pseudo) {
 112              return '(' . preg_quote($pseudo) . ')';
 113          }, array_keys(self::$pseudos));
 114          $this->pseudosregex = '(' . implode('|', $pseudos) . ')';
 115      }
 116  
 117      /**
 118       * Manipulate an array of rules to adapt their values.
 119       *
 120       * @param array $rules The rules.
 121       * @return New array of rules.
 122       */
 123      protected function manipulateRuleValues(array $rules) {
 124          $finalrules = [];
 125  
 126          foreach ($rules as $rule) {
 127              $property = $rule->getRule();
 128              $value = $rule->getValue();
 129  
 130              if ($property === 'position' && $value === 'sticky') {
 131                  $newrule = clone $rule;
 132                  $newrule->setValue('-webkit-sticky');
 133                  $finalrules[] = $newrule;
 134  
 135              } else if ($property === 'background-image' &&
 136                      $value instanceof CSSFunction &&
 137                      $value->getName() === 'linear-gradient') {
 138  
 139                  foreach (['-webkit-', '-o-'] as $prefix) {
 140                      $newfunction = clone $value;
 141                      $newfunction->setName($prefix . $value->getName());
 142                      $newrule = clone $rule;
 143                      $newrule->setValue($newfunction);
 144                      $finalrules[] = $newrule;
 145                  }
 146              }
 147  
 148              $finalrules[] = $rule;
 149          }
 150  
 151          return $finalrules;
 152      }
 153  
 154      /**
 155       * Prefix all the things!
 156       */
 157      public function prefix() {
 158          $this->processBlock($this->tree);
 159      }
 160  
 161      /**
 162       * Process block.
 163       *
 164       * @param object $block A block.
 165       * @param object $parent The parent of the block.
 166       */
 167      protected function processBlock($block) {
 168          foreach ($block->getContents() as $node) {
 169              if ($node instanceof AtRule) {
 170  
 171                  $name = $node->atRuleName();
 172                  if (isset(self::$atrules[$name])) {
 173                      foreach (self::$atrules[$name] as $prefix) {
 174                          $newname = $prefix . $name;
 175                          $newnode = clone $node;
 176  
 177                          if ($node instanceof KeyFrame) {
 178                              $newnode->setVendorKeyFrame($newname);
 179                              $block->insert($newnode, $node);
 180  
 181                          } else {
 182                              debugging('Unhandled atRule prefixing.', DEBUG_DEVELOPER);
 183                          }
 184                      }
 185                  }
 186              }
 187  
 188              if ($node instanceof CSSList) {
 189                  $this->processBlock($node);
 190  
 191              } else if ($node instanceof RuleSet) {
 192                  $this->processDeclaration($node, $block);
 193              }
 194          }
 195      }
 196  
 197      /**
 198       * Process declaration.
 199       *
 200       * @param object $node The declaration block.
 201       * @param object $parent The parent.
 202       */
 203      protected function processDeclaration($node, $parent) {
 204          $rules = [];
 205  
 206          foreach ($node->getRules() as $key => $rule) {
 207              $name = $rule->getRule();
 208              $seen[$name] = true;
 209  
 210              if (!isset(self::$rules[$name])) {
 211                  $rules[] = $rule;
 212                  continue;
 213              }
 214  
 215              foreach (self::$rules[$name] as $prefix) {
 216                  $newname = $prefix . $name;
 217                  if (isset($seen[$newname])) {
 218                      continue;
 219                  }
 220                  $newrule = clone $rule;
 221                  $newrule->setRule($newname);
 222                  $rules[] = $newrule;
 223              }
 224  
 225              $rules[] = $rule;
 226          }
 227  
 228          $node->setRules($this->manipulateRuleValues($rules));
 229  
 230          if ($node instanceof DeclarationBlock) {
 231              $selectors = $node->getSelectors();
 232              foreach ($selectors as $key => $selector) {
 233  
 234                  $matches = [];
 235                  if (preg_match($this->pseudosregex, $selector->getSelector(), $matches)) {
 236  
 237                      $newnode = clone $node;
 238                      foreach (self::$pseudos[$matches[1]] as $newpseudo) {
 239                          $newselector = new Selector(str_replace($matches[1], $newpseudo, $selector->getSelector()));
 240                          $selectors[$key] = $newselector;
 241                          $newnode = clone $node;
 242                          $newnode->setSelectors($selectors);
 243                          $parent->insert($newnode, $node);
 244                      }
 245  
 246                      // We're only expecting one affected pseudo class per block.
 247                      break;
 248                  }
 249              }
 250          }
 251      }
 252  }