You are here

class SassParser in Sassy 7.3

Same name and namespace in other branches
  1. 7 phamlp/sass/SassParser.php \SassParser

SassParser class. Parses {@link http://sass-lang.com/ .sass and .sccs} files. @package PHamlP @subpackage Sass

Hierarchy

Expanded class hierarchy of SassParser

File

phpsass/SassParser.php, line 34

View source
class SassParser {

  /**#@+
   * Default option values
   */
  const CACHE = true;
  const CACHE_LOCATION = './sass-cache';
  const CSS_LOCATION = './css';
  const TEMPLATE_LOCATION = './sass-templates';
  const BEGIN_COMMENT = '/';
  const BEGIN_CSS_COMMENT = '/*';
  const END_CSS_COMMENT = '*/';
  const BEGIN_SASS_COMMENT = '//';
  const BEGIN_INTERPOLATION = '#';
  const BEGIN_INTERPOLATION_BLOCK = '#{';
  const BEGIN_BLOCK = '{';
  const END_BLOCK = '}';
  const END_STATEMENT = ';';
  const DOUBLE_QUOTE = '"';
  const SINGLE_QUOTE = "'";

  /**
   * Static holder for last instance of a SassParser
   */
  public static $instance;

  /**
   * @var string the character used for indenting
   * @see indentChars
   * @see indentSpaces
   */
  private $indentChar;

  /**
   * @var array allowable characters for indenting
   */
  private $indentChars = array(
    ' ',
    "\t",
  );

  /**
   * @var integer number of spaces for indentation.
   * Used to calculate {@link Level} if {@link indentChar} is space.
   */
  private $indentSpaces = 2;

  /**
   * @var string source
   */
  private $source;

  /**#@+
   * Option
   */
  private $basepath;

  /**
   * cache:
   * @var boolean Whether parsed Sass files should be cached, allowing greater
   * speed.
   *
   * Defaults to true.
   */
  private $cache;

  /**
   * cache_location:
   * @var string The path where the cached sassc files should be written to.
   *
   * Defaults to './sass-cache'.
   */
  private $cache_location;

  /**
   * css_location:
   * @var string The path where CSS output should be written to.
   *
   * Defaults to './css'.
   */
  private $css_location;

  /**
   * debug_info:
   * @var boolean When true the line number and file where a selector is defined
   * is emitted into the compiled CSS in a format that can be understood by the
   * {@link https://addons.mozilla.org/en-US/firefox/addon/103988/
   * FireSass Firebug extension}.
   * Disabled when using the compressed output style.
   *
   * Defaults to false.
   * @see style
   */
  private $debug_info;

  /**
   * extensions:
   * @var array Sass extensions, e.g. Compass. An associative array of the form
   * $name => $options where $name is the name of the extension and $options
   * is an array of name=>value options pairs.
   */
  protected $extensions;

  /**
   * filename:
   * @var string The filename of the file being rendered.
   * This is used solely for reporting errors.
   */
  protected $filename;

  /**
   * function:
   * @var An array of (function_name => callback) items.
   */
  public static $functions;

  /**
   * line:
   * @var integer The number of the first line of the Sass template. Used for
   * reporting line numbers for errors. This is useful to set if the Sass
   * template is embedded.
   *
   * Defaults to 1.
   */
  private $line;

  /**
   * line_numbers:
   * @var boolean When true the line number and filename where a selector is
   * defined is emitted into the compiled CSS as a comment. Useful for debugging
   * especially when using imports and mixins.
   * Disabled when using the compressed output style or the debug_info option.
   *
   * Defaults to false.
   * @see debug_info
   * @see style
   */
  private $line_numbers;

  /**
   * load_paths:
   * @var array An array of filesystem paths which should be searched for
   * Sass templates imported with the @import directive.
   *
   * Defaults to './sass-templates'.
   */
  private $load_paths;
  private $load_path_functions;

  /**
   * property_syntax:
   * @var string Forces the document to use one syntax for
   * properties. If the correct syntax isn't used, an error is thrown.
   * Value can be:
   * + new - forces the use of a colon or equals sign after the property name.
   * For example   color: #0f3 or width: $main_width.
   * + old -  forces the use of a colon before the property name.
   * For example: :color #0f3 or :width = $main_width.
   *
   * By default, either syntax is valid.
   *
   * Ignored for SCSS files which alaways use the new style.
   */
  private $property_syntax;

  /**
   * quiet:
   * @var boolean When set to true, causes warnings to be disabled.
   * Defaults to false.
   */
  private $quiet;

  /**
   * style:
   * @var string the style of the CSS output.
   * Value can be:
   * + nested - Nested is the default Sass style, because it reflects the
   * structure of the document in much the same way Sass does. Each selector
   * and rule has its own line with indentation is based on how deeply the rule
   * is nested. Nested style is very useful when looking at large CSS files as
   * it allows you to very easily grasp the structure of the file without
   * actually reading anything.
   * + expanded - Expanded is the typical human-made CSS style, with each selector
   * and property taking up one line. Selectors are not indented; properties are
   * indented within the rules.
   * + compact - Each CSS rule takes up only one line, with every property defined
   * on that line. Nested rules are placed with each other while groups of rules
   * are separated by a blank line.
   * + compressed - Compressed has no whitespace except that necessary to separate
   * selectors and properties. It's not meant to be human-readable.
   *
   * Defaults to 'nested'.
   */
  private $style;

  /**
   * syntax:
   * @var string The syntax of the input file.
   * 'sass' for the indented syntax and 'scss' for the CSS-extension syntax.
   *
   * This is set automatically when parsing a file, else defaults to 'sass'.
   */
  private $syntax;

  /**
   * template_location:
   * @var string Path to the root sass template directory for your
   * application.
   */
  private $template_location;

  /**
   * vendor_properties:
   * If enabled a property need only be written in the standard form and vendor
   * specific versions will be added to the style sheet.
   * @var mixed array: vendor properties, merged with the built-in vendor
   * properties, to automatically apply.
   * Boolean true: use built in vendor properties.
   *
   * Defaults to vendor_properties disabled.
   * @see _vendorProperties
   */
  private $vendor_properties = array();

  /**
   * debug:
   * If enabled it causes exceptions to be thrown on errors. This can be
   * useful for tracking down a bug in your sourcefile but will cause a
   * site to break if used in production unless the parser in wrapped in
   * a try/catch structure.
   *
   * Defaults to FALSE
   */
  private $debug = FALSE;

  /**#@-*/

  /**
   * Defines the build-in vendor properties
   * @var array built-in vendor properties
   * @see vendor_properties
   */
  private $_vendorProperties = array(
    'border-radius' => array(
      '-moz-border-radius',
      '-webkit-border-radius',
      '-khtml-border-radius',
    ),
    'border-top-right-radius' => array(
      '-moz-border-radius-topright',
      '-webkit-border-top-right-radius',
      '-khtml-border-top-right-radius',
    ),
    'border-bottom-right-radius' => array(
      '-moz-border-radius-bottomright',
      '-webkit-border-bottom-right-radius',
      '-khtml-border-bottom-right-radius',
    ),
    'border-bottom-left-radius' => array(
      '-moz-border-radius-bottomleft',
      '-webkit-border-bottom-left-radius',
      '-khtml-border-bottom-left-radius',
    ),
    'border-top-left-radius' => array(
      '-moz-border-radius-topleft',
      '-webkit-border-top-left-radius',
      '-khtml-border-top-left-radius',
    ),
    'box-shadow' => array(
      '-moz-box-shadow',
      '-webkit-box-shadow',
    ),
    'box-sizing' => array(
      '-moz-box-sizing',
      '-webkit-box-sizing',
    ),
    'opacity' => array(
      '-moz-opacity',
      '-webkit-opacity',
      '-khtml-opacity',
    ),
  );

  /**
   * Constructor.
   * Sets parser options
   * @param array $options
   * @return SassParser
   */
  public function __construct($options = array()) {
    if (!is_array($options)) {
      if (isset($options['debug']) && $options['debug']) {
        throw new SassException('Options must be an array');
      }
      $options = count((array) $options) ? (array) $options : array();
    }
    if (!empty($options['vendor_properties'])) {
      if ($options['vendor_properties'] === true) {
        $this->vendor_properties = $this->_vendorProperties;
      }
      elseif (is_array($options['vendor_properties'])) {
        $this->vendor_properties = array_merge($this->_vendorProperties, $options['vendor_properties']);
      }
    }
    unset($options['language'], $options['vendor_properties']);
    $basepath = $_SERVER['PHP_SELF'];
    $basepath = substr($basepath, 0, strrpos($basepath, '/') + 1);
    $defaultOptions = array(
      'basepath' => $basepath,
      'cache' => self::CACHE,
      'cache_location' => dirname(__FILE__) . DIRECTORY_SEPARATOR . self::CACHE_LOCATION,
      'css_location' => dirname(__FILE__) . DIRECTORY_SEPARATOR . self::CSS_LOCATION,
      'debug_info' => FALSE,
      'filename' => array(
        'dirname' => '',
        'basename' => '',
      ),
      'functions' => array(),
      'load_paths' => array(),
      'load_path_functions' => array(),
      'line' => 1,
      'line_numbers' => FALSE,
      'style' => SassRenderer::STYLE_NESTED,
      'syntax' => SassFile::SASS,
      'debug' => FALSE,
    );
    $options = array_merge($defaultOptions, $options);
    self::$functions = $options['functions'];
    unset($options['functions']);
    foreach ($options as $name => $value) {
      if (property_exists($this, $name)) {
        $this->{$name} = $value;
      }
    }
    self::$instance = $this;
    $GLOBALS['SassParser_debug'] = $this->debug;
  }

  /**
   * Getter.
   * @param string name of property to get
   * @return mixed return value of getter function
   */
  public function __get($name) {
    $getter = 'get' . ucfirst($name);
    if (method_exists($this, $getter)) {
      return $this
        ->{$getter}();
    }
    if (property_exists($this, $name)) {
      return $this->{$name};
    }
    if ($this->debug) {
      throw new SassException('No getter function for ' . $name);
    }
  }
  public function getBasepath() {
    return $this->basepath;
  }
  public function getCache() {
    return $this->cache;
  }
  public function getCache_location() {
    return $this->cache_location;
  }
  public function getCss_location() {
    return $this->css_location;
  }
  public function getDebug_info() {
    return $this->debug_info;
  }
  public function getFilename() {
    return $this->filename;
  }
  public function getLine() {
    return $this->line;
  }
  public function getSource() {
    return $this->source;
  }
  public function getLine_numbers() {
    return $this->line_numbers;
  }
  public function getFunctions() {
    return self::$functions;
  }
  public function getLoad_paths() {
    return $this->load_paths;
  }
  public function getLoad_path_functions() {
    return $this->load_path_functions;
  }
  public function getProperty_syntax() {
    return $this->property_syntax;
  }
  public function getQuiet() {
    return $this->quiet;
  }
  public function getStyle() {
    return $this->style;
  }
  public function getSyntax() {
    return $this->syntax;
  }
  public function getTemplate_location() {
    return $this->template_location;
  }
  public function getVendor_properties() {
    return $this->vendor_properties;
  }
  public function getDebug() {
    return $this->debug;
  }
  public function getOptions() {
    return array(
      'cache' => $this->cache,
      'cache_location' => $this->cache_location,
      'css_location' => $this->css_location,
      'filename' => $this->filename,
      'functions' => $this->functions,
      'line' => $this->line,
      'line_numbers' => $this->line_numbers,
      'load_paths' => $this->load_paths,
      'load_path_functions' => $this->load_path_functions,
      'property_syntax' => $this->property_syntax,
      'quiet' => $this->quiet,
      'style' => $this->style,
      'syntax' => $this->syntax,
      'template_location' => $this->template_location,
      'vendor_properties' => $this->vendor_properties,
      'debug' => $this->debug,
    );
  }

  /**
   * Parse a sass file or Sass source code and returns the CSS.
   * @param string name of source file or Sass source
   * @return string CSS
   */
  public function toCss($source, $isFile = true) {
    return $this
      ->parse($source, $isFile)
      ->render();
  }

  /**
   * Parse a sass file or Sass source code and
   * returns the document tree that can then be rendered.
   * The file will be searched for in the directories specified by the
   * load_paths option.
   * If caching is enabled a cached version will be used if possible or the
   * compiled version cached if not.
   * @param string name of source file or Sass source
   * @return SassRootNode Root node of document tree
   */
  public function parse($source, $isFile = true) {

    # Richard Lyon - 2011-10-25 - ignore unfound files

    # Richard Lyon - 2011-10-25 - add multiple files to load functions
    if (!$source) {
      return $this
        ->toTree($source);
    }
    if (is_array($source)) {
      $return = array();
      foreach ($source as $source_file) {
        $return = array_merge($return, $this
          ->parse($source_file, TRUE));
      }
      return $return;
    }
    if ($isFile && ($files = SassFile::get_file($source, $this))) {
      $files_source = '';
      foreach ($files as $file) {
        $this->filename = $file;
        $this->syntax = substr($this->filename, -4);
        if ($this->syntax !== SassFile::SASS && $this->syntax !== SassFile::SCSS) {
          if ($this->debug) {
            throw new SassException('Invalid {what}', array(
              '{what}' => 'syntax option',
            ));
          }
          return FALSE;
        }
        if ($this->cache) {
          $cached = SassFile::get_cached_file($this->filename, $this->cache_location);
          if ($cached !== false) {
            return $cached;
          }
        }
        $contents = file_get_contents($this->filename);
        SassFile::$parser = $this;
        SassFile::$path = $this->filename;
        $contents = preg_replace_callback('/url\\(\\s*[\'"]?(?![a-z]+:|\\/+)([^\'")]+)[\'"]?\\s*\\)/i', 'SassFile::resolve_paths', $contents);
        $files_source .= $contents;
        if ($this->cache) {
          SassFile::set_cached_file($tree, $filename, $this->cache_location);
        }
      }
      return $this
        ->toTree($files_source);
    }
    else {
      return $this
        ->toTree($source);
    }
  }

  /**
   * Parse Sass source into a document tree.
   * If the tree is already created return that.
   * @param string Sass source
   * @return SassRootNode the root of this document tree
   */
  private function toTree($source) {
    if ($this->syntax === SassFile::SASS) {
      $source = str_replace(array(
        "\r\n",
        "\n\r",
        "\r",
      ), "\n", $source);
      $this->source = explode("\n", $source);
      $this
        ->setIndentChar();
    }
    else {
      $this->source = $source;
    }
    unset($source);
    $root = new SassRootNode($this);
    $this
      ->buildTree($root);
    return $root;
  }

  /**
   * Builds a parse tree under the parent node.
   * Called recursivly until the source is parsed.
   * @param SassNode the node
   */
  private function buildTree($parent) {
    $node = $this
      ->getNode($parent);
    while (is_object($node) && $node
      ->isChildOf($parent)) {
      $parent
        ->addChild($node);
      $node = $this
        ->buildTree($node);
    }
    return $node;
  }

  /**
   * Creates and returns the next SassNode.
   * The tpye of SassNode depends on the content of the SassToken.
   * @return SassNode a SassNode of the appropriate type. Null when no more
   * source to parse.
   */
  private function getNode($node) {
    $token = $this
      ->getToken();
    if (empty($token)) {
      return null;
    }
    switch (true) {
      case SassDirectiveNode::isa($token):
        return $this
          ->parseDirective($token, $node);
      case SassCommentNode::isa($token):
        return new SassCommentNode($token);
      case SassVariableNode::isa($token):
        return new SassVariableNode($token);
      case SassPropertyNode::isa(array(
        'token' => $token,
        'syntax' => $this->property_syntax,
      )):
        return new SassPropertyNode($token, $this->property_syntax);
      case SassFunctionDefinitionNode::isa($token):
        return new SassFunctionDefinitionNode($token);
      case SassMixinDefinitionNode::isa($token):
        if ($this->syntax === SassFile::SCSS) {
          if ($this->debug) {
            throw new SassException('Mixin definition shortcut not allowed in SCSS', $this);
          }
          return;
        }
        else {
          return new SassMixinDefinitionNode($token);
        }
      case SassMixinNode::isa($token):
        if ($this->syntax === SassFile::SCSS) {
          if ($this->debug) {
            throw new SassException('Mixin include shortcut not allowed in SCSS', $this);
          }
          return;
        }
        else {
          return new SassMixinNode($token);
        }
      default:
        return new SassRuleNode($token);
        break;
    }

    // switch
  }

  /**
   * Returns a token object that contains the next source statement and
   * meta data about it.
   * @return object
   */
  private function getToken() {
    return $this->syntax === SassFile::SASS ? $this
      ->sass2Token() : $this
      ->scss2Token();
  }

  /**
   * Returns an object that contains the next source statement and meta data
   * about it from SASS source.
   * Sass statements are passed over. Statements spanning multiple lines, e.g.
   * CSS comments and selectors, are assembled into a single statement.
   * @return object Statement token. Null if end of source.
   */
  private function sass2Token() {
    $statement = '';

    // source line being tokenised
    $token = null;
    while (is_null($token) && !empty($this->source)) {
      while (empty($statement) && !empty($this->source)) {
        $source = array_shift($this->source);
        $statement = trim($source);
        $this->line++;
      }
      if (empty($statement)) {
        break;
      }
      $level = $this
        ->getLevel($source);

      // Comment statements can span multiple lines
      if ($statement[0] === self::BEGIN_COMMENT) {

        // Consume Sass comments
        if (substr($statement, 0, strlen(self::BEGIN_SASS_COMMENT)) === self::BEGIN_SASS_COMMENT) {
          unset($statement);
          while ($this
            ->getLevel($this->source[0]) > $level) {
            array_shift($this->source);
            $this->line++;
          }
          continue;
        }
        elseif (substr($statement, 0, strlen(self::BEGIN_CSS_COMMENT)) === self::BEGIN_CSS_COMMENT) {
          while ($this
            ->getLevel($this->source[0]) > $level) {
            $statement .= "\n" . ltrim(array_shift($this->source));
            $this->line++;
          }
        }
        else {
          $this->source = $statement;
          if ($this->debug) {
            throw new SassException('Illegal comment type', $this);
          }
        }
      }
      elseif (substr($statement, -1) === SassRuleNode::CONTINUED) {

        // Build the selector statement
        while ($this
          ->getLevel($this->source[0]) === $level) {
          $statement .= ltrim(array_shift($this->source));
          $this->line++;
        }
      }
      $token = (object) array(
        'source' => $statement,
        'level' => $level,
        'filename' => $this->filename,
        'line' => $this->line - 1,
      );
    }
    return $token;
  }

  /**
   * Returns the level of the line.
   * Used for .sass source
   * @param string the source
   * @return integer the level of the source
   * @throws Exception if the source indentation is invalid
   */
  private function getLevel($source) {
    $indent = strlen($source) - strlen(ltrim($source));
    $level = $indent / $this->indentSpaces;
    if (is_float($level)) {
      $level = (int) ceil($level);
    }
    if (!is_int($level) || preg_match("/[^{$this->indentChar}]/", substr($source, 0, $indent))) {
      $this->source = $source;
      if ($this->debug) {
        throw new SassException('Invalid indentation', $this);
      }
      else {
        return 0;
      }
    }
    return $level;
  }

  /**
   * Returns an object that contains the next source statement and meta data
   * about it from SCSS source.
   * @return object Statement token. Null if end of source.
   */
  private function scss2Token() {
    static $srcpos = 0;

    // current position in the source stream
    static $srclen;

    // the length of the source stream
    $statement = '';
    $token = null;
    if (empty($srclen)) {
      $srclen = strlen($this->source);
    }
    while (is_null($token) && $srcpos < strlen($this->source)) {
      $c = $this->source[$srcpos++];
      switch ($c) {
        case self::BEGIN_COMMENT:
          if (substr($this->source, $srcpos - 1, strlen(self::BEGIN_SASS_COMMENT)) === self::BEGIN_SASS_COMMENT) {
            while ($this->source[$srcpos++] !== "\n") {
            }
            $statement .= "\n";
          }
          elseif (substr($this->source, $srcpos - 1, strlen(self::BEGIN_CSS_COMMENT)) === self::BEGIN_CSS_COMMENT) {
            if (ltrim($statement)) {
              if ($this->debug) {
                throw new SassException('Invalid comment', (object) array(
                  'source' => $statement,
                  'filename' => $this->filename,
                  'line' => $this->line,
                ));
              }
            }
            $statement .= $c . $this->source[$srcpos++];
            while (substr($this->source, $srcpos, strlen(self::END_CSS_COMMENT)) !== self::END_CSS_COMMENT) {
              $statement .= $this->source[$srcpos++];
            }
            $srcpos += strlen(self::END_CSS_COMMENT);
            $token = $this
              ->createToken($statement . self::END_CSS_COMMENT);
          }
          else {
            $statement .= $c;
          }
          break;
        case self::DOUBLE_QUOTE:
        case self::SINGLE_QUOTE:
          $statement .= $c;
          while ($this->source[$srcpos] !== $c) {
            $statement .= $this->source[$srcpos++];
          }
          $statement .= $this->source[$srcpos++];
          break;
        case self::BEGIN_INTERPOLATION:
          $statement .= $c;
          if (substr($this->source, $srcpos - 1, strlen(self::BEGIN_INTERPOLATION_BLOCK)) === self::BEGIN_INTERPOLATION_BLOCK) {
            while ($this->source[$srcpos] !== self::END_BLOCK) {
              $statement .= $this->source[$srcpos++];
            }
            $statement .= $this->source[$srcpos++];
          }
          break;
        case self::BEGIN_BLOCK:
        case self::END_BLOCK:
        case self::END_STATEMENT:
          $token = $this
            ->createToken($statement . $c);
          if (is_null($token)) {
            $statement = '';
          }
          break;
        default:
          $statement .= $c;
          break;
      }
    }
    if (is_null($token)) {
      $srclen = $srcpos = 0;
    }
    return $token;
  }

  /**
   * Returns an object that contains the source statement and meta data about
   * it.
   * If the statement is just and end block we update the meta data and return null.
   * @param string source statement
   * @return SassToken
   */
  private function createToken($statement) {
    static $level = 0;
    $this->line += substr_count($statement, "\n");
    $statement = trim($statement);
    if (substr($statement, 0, strlen(self::BEGIN_CSS_COMMENT)) !== self::BEGIN_CSS_COMMENT) {
      $statement = str_replace(array(
        "\n",
        "\r",
      ), '', $statement);
    }
    $last = substr($statement, -1);

    // Trim the statement removing whitespace, end statement (;), begin block ({), and (unless the statement ends in an interpolation block) end block (})
    $statement = rtrim($statement, ' ' . self::BEGIN_BLOCK . self::END_STATEMENT);
    $statement = preg_match('/#\\{.+?\\}$/i', $statement) ? $statement : rtrim($statement, self::END_BLOCK);
    $token = $statement ? (object) array(
      'source' => $statement,
      'level' => $level,
      'filename' => $this->filename,
      'line' => $this->line,
    ) : null;
    $level += $last === self::BEGIN_BLOCK ? 1 : ($last === self::END_BLOCK ? -1 : 0);
    return $token;
  }

  /**
   * Parses a directive
   * @param SassToken token to parse
   * @param SassNode parent node
   * @return SassNode a Sass directive node
   */
  private function parseDirective($token, $parent) {
    switch (SassDirectiveNode::extractDirective($token)) {
      case '@extend':
        return new SassExtendNode($token);
        break;
      case '@function':
        return new SassFunctionDefinitionNode($token);
        break;
      case '@return':
        return new SassReturnNode($token);
        break;
      case '@mixin':
        return new SassMixinDefinitionNode($token);
        break;
      case '@include':
        return new SassMixinNode($token);
        break;
      case '@import':
        if ($this->syntax == SassFile::SASS) {
          $i = 0;
          $source = '';
          while (!empty($this->source) && empty($source)) {
            $source = $this->source[$i++];
          }
          if (!empty($source) && $this
            ->getLevel($source) > $token->level) {
            if ($this->debug) {
              throw new SassException('Nesting not allowed beneath @import directive', $token);
            }
          }
        }
        return new SassImportNode($token);
        break;
      case '@each':
        return new SassEachNode($token);
        break;
      case '@for':
        return new SassForNode($token);
        break;
      case '@if':
        return new SassIfNode($token);
        break;
      case '@else':

        // handles else and else if directives
        return new SassElseNode($token);
        break;
      case '@do':
      case '@while':
        return new SassWhileNode($token);
        break;
      case '@debug':
        return new SassDebugNode($token);
        break;
      case '@warn':
        return new SassDebugNode($token, true);
        break;
      default:
        return new SassDirectiveNode($token);
        break;
    }
  }

  /**
   * Determine the indent character and indent spaces.
   * The first character of the first indented line determines the character.
   * If this is a space the number of spaces determines the indentSpaces; this
   * is always 1 if the indent character is a tab.
   * Only used for .sass files.
   * @throws SassException if the indent is mixed or
   * the indent character can not be determined
   */
  private function setIndentChar() {
    foreach ($this->source as $l => $source) {
      if (!empty($source) && in_array($source[0], $this->indentChars)) {
        $this->indentChar = $source[0];
        for ($i = 0, $len = strlen($source); $i < $len && $source[$i] == $this->indentChar; $i++) {
        }
        if ($i < $len && in_array($source[$i], $this->indentChars)) {
          $this->line = ++$l;
          $this->source = $source;
          if ($this->debug) {
            throw new SassException('Mixed indentation not allowed', $this);
          }
        }
        $this->indentSpaces = $this->indentChar == ' ' ? $i : 1;
        return;
      }
    }

    // foreach
    $this->indentChar = ' ';
    $this->indentSpaces = 2;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
SassParser::$basepath private property
SassParser::$cache private property cache: speed.
SassParser::$cache_location private property cache_location:
SassParser::$css_location private property css_location:
SassParser::$debug private property debug: If enabled it causes exceptions to be thrown on errors. This can be useful for tracking down a bug in your sourcefile but will cause a site to break if used in production unless the parser in wrapped in a try/catch structure.
SassParser::$debug_info private property debug_info: is emitted into the compiled CSS in a format that can be understood by the {@link https://addons.mozilla.org/en-US/firefox/addon/103988/ FireSass Firebug extension}. Disabled when using the compressed output style.
SassParser::$extensions protected property extensions: $name => $options where $name is the name of the extension and $options is an array of name=>value options pairs.
SassParser::$filename protected property filename: This is used solely for reporting errors.
SassParser::$functions public static property function:
SassParser::$indentChar private property
SassParser::$indentChars private property
SassParser::$indentSpaces private property Used to calculate {@link Level} if {@link indentChar} is space.
SassParser::$instance public static property Static holder for last instance of a SassParser
SassParser::$line private property line: reporting line numbers for errors. This is useful to set if the Sass template is embedded.
SassParser::$line_numbers private property line_numbers: defined is emitted into the compiled CSS as a comment. Useful for debugging especially when using imports and mixins. Disabled when using the compressed output style or the debug_info option.
SassParser::$load_paths private property load_paths: Sass templates imported with the @import directive.
SassParser::$load_path_functions private property
SassParser::$property_syntax private property property_syntax: properties. If the correct syntax isn't used, an error is thrown. Value can be: + new - forces the use of a colon or equals sign after the property name. For example color: #0f3 or width: $main_width. + old - forces the use of…
SassParser::$quiet private property quiet: Defaults to false.
SassParser::$source private property
SassParser::$style private property style: Value can be: + nested - Nested is the default Sass style, because it reflects the structure of the document in much the same way Sass does. Each selector and rule has its own line with indentation is based on how deeply the rule is nested.…
SassParser::$syntax private property syntax: 'sass' for the indented syntax and 'scss' for the CSS-extension syntax.
SassParser::$template_location private property template_location: application.
SassParser::$vendor_properties private property vendor_properties: If enabled a property need only be written in the standard form and vendor specific versions will be added to the style sheet. properties, to automatically apply. Boolean true: use built in vendor properties.
SassParser::$_vendorProperties private property Defines the build-in vendor properties
SassParser::BEGIN_BLOCK constant
SassParser::BEGIN_COMMENT constant
SassParser::BEGIN_CSS_COMMENT constant
SassParser::BEGIN_INTERPOLATION constant
SassParser::BEGIN_INTERPOLATION_BLOCK constant
SassParser::BEGIN_SASS_COMMENT constant
SassParser::buildTree private function Builds a parse tree under the parent node. Called recursivly until the source is parsed.
SassParser::CACHE constant
SassParser::CACHE_LOCATION constant
SassParser::createToken private function Returns an object that contains the source statement and meta data about it. If the statement is just and end block we update the meta data and return null.
SassParser::CSS_LOCATION constant
SassParser::DOUBLE_QUOTE constant
SassParser::END_BLOCK constant
SassParser::END_CSS_COMMENT constant
SassParser::END_STATEMENT constant
SassParser::getBasepath public function
SassParser::getCache public function
SassParser::getCache_location public function
SassParser::getCss_location public function
SassParser::getDebug public function
SassParser::getDebug_info public function
SassParser::getFilename public function
SassParser::getFunctions public function
SassParser::getLevel private function Returns the level of the line. Used for .sass source
SassParser::getLine public function
SassParser::getLine_numbers public function
SassParser::getLoad_paths public function
SassParser::getLoad_path_functions public function
SassParser::getNode private function Creates and returns the next SassNode. The tpye of SassNode depends on the content of the SassToken.
SassParser::getOptions public function
SassParser::getProperty_syntax public function
SassParser::getQuiet public function
SassParser::getSource public function
SassParser::getStyle public function
SassParser::getSyntax public function
SassParser::getTemplate_location public function
SassParser::getToken private function Returns a token object that contains the next source statement and meta data about it.
SassParser::getVendor_properties public function
SassParser::parse public function Parse a sass file or Sass source code and returns the document tree that can then be rendered. The file will be searched for in the directories specified by the load_paths option. If caching is enabled a cached version will be used if possible or…
SassParser::parseDirective private function Parses a directive
SassParser::sass2Token private function Returns an object that contains the next source statement and meta data about it from SASS source. Sass statements are passed over. Statements spanning multiple lines, e.g. CSS comments and selectors, are assembled into a single statement.
SassParser::scss2Token private function Returns an object that contains the next source statement and meta data about it from SCSS source.
SassParser::setIndentChar private function Determine the indent character and indent spaces. The first character of the first indented line determines the character. If this is a space the number of spaces determines the indentSpaces; this is always 1 if the indent character is a tab. Only…
SassParser::SINGLE_QUOTE constant
SassParser::TEMPLATE_LOCATION constant
SassParser::toCss public function Parse a sass file or Sass source code and returns the CSS.
SassParser::toTree private function Parse Sass source into a document tree. If the tree is already created return that.
SassParser::__construct public function Constructor. Sets parser options
SassParser::__get public function Getter.