View file application/libraries/Scaffold/modules/Paginator/Paginator.php

File size: 5.02Kb
<?php

class Paginator
{

  protected static $_nestedBlockPositions = array();
  protected static $_ignoreSelectorPerPage = false;

  static public function flag()
  {
    Scaffold::flag_set('paginator');
    Scaffold::flag_set(sprintf('pagestart=%d', @$_GET['pageStart']));
    Scaffold::flag_set(sprintf('pageend=%d', @$_GET['pageEnd']));
  }

  static public function output()
  {
    $currentStart = isset($_GET['pageStart']) ? (int) $_GET['pageStart'] : null;
    $currentEnd = isset($_GET['pageEnd']) ? (int) $_GET['pageEnd'] : null;

    // Just output substr
    if( $currentStart != null || $currentEnd != null ) {
      Scaffold::$output = substr(Scaffold::$output, $currentStart, $currentEnd - $currentStart);
      return;
    }

    $selectorsPerPage = 4000;
    $cssLength = strlen(Scaffold::$output);
    $cssSelectors = self::_getSelectorCount();
    $avgLengthPerSelector = (int) round($cssLength / $cssSelectors);
    self::$_nestedBlockPositions = self::_parseNestedBlocks();

    // Segment the file
    $segments = array();
    $currentStart = 0;
    $currentPos = 0;
    $currentCount = 0;
    $i = 0;

    do {
      // Get next pos
      $currentPos += round($selectorsPerPage * $avgLengthPerSelector);
      if( $currentPos > $cssLength ) {
        $currentPos = $cssLength;
      } else {
        // Rewind until it's less than selector count
        $ignoreSelectorCounter = 0;
        do {
          $currentPos = round($currentPos - (5 * $avgLengthPerSelector)) - $ignoreSelectorCounter * 500 * $avgLengthPerSelector; // Fudgesicles
          $currentPos = self::_getPreviousSelectorEnd($currentPos - $cssLength, $currentStart);
          $currentCount = self::_getSelectorCount($currentStart, $currentPos - $currentStart);
          if (self::$_ignoreSelectorPerPage ) {
            $ignoreSelectorCounter++;
          }
        } while( $currentCount > $selectorsPerPage && ($ignoreSelectorCounter < 2) );
      }

      $segments[] = $currentPos;
      $currentStart = $currentPos;
      $i++;
    } while( $currentCount > 0 && $currentPos < $cssLength && $i < 100 );

    // Only do stuff if there is more than one segment
    if( count($segments) > 1 ) {
      array_pop($segments);
      // Generate imports
      $urlInfo = parse_url($_SERVER['REQUEST_URI']);
      $urlQueryArr = array();
      parse_str($urlInfo['query'], $urlQueryArr);
      
      //CDN Work
      $path = true;
      if (file_exists(APPLICATION_PATH_SET .DS. 'general.php')) {
        $generalConfig = include APPLICATION_PATH_SET . DS . 'general.php';
        if(!empty($generalConfig['cdn']['enabled'])) {
          $path = false;
        }
      }

      $importStr = '';
      $lastEnd = -1;
      foreach( $segments as $segmentEnd ) {
        $urlQueryArr['pageStart'] = $lastEnd + 1;
        $urlQueryArr['pageEnd'] = $segmentEnd + 1;
        $url = (empty($path) ? $path : $urlInfo['path']) . '?' . http_build_query($urlQueryArr);
        $importStr .= '@import "' . $url . '";' . "\r\n";
        $lastEnd = $segmentEnd;
      }
      // Truncate CSS, only get last segment
      Scaffold::$output = substr(Scaffold::$output, $lastEnd + 1);
      Scaffold::$output = $importStr . "\r\n" . "\r\n" . Scaffold::$output;
    }
  }

  static protected function _getSelectorCount($start = 0, $length = null)
  {
    if( null === $length ) {
      $length = strlen(Scaffold::$output) - $start;
    }
    return substr_count(Scaffold::$output, '{', $start, $length) +
      substr_count(Scaffold::$output, ',', $start, $length);
  }

  static protected function _getPreviousSelectorEnd($offset = null, $start = null)
  {
    self::$_ignoreSelectorPerPage = false;
    $pos = strrpos(Scaffold::$output, '}', $offset);
    if( false === $pos || $pos < $start ) {
      return false;
    }

    foreach( self::$_nestedBlockPositions as $nestedBlock ) {
      $startMedia = $nestedBlock['start'];
      $endMedia = $nestedBlock['end'];
      if( !($startMedia >= $start && $endMedia > $pos) ) {
        continue;
      }

      $pos = $endMedia;
      // We need to ignore the selector per page because we want to added complete media
      self::$_ignoreSelectorPerPage = true;
      break;
    }

    return $pos;
  }

  static protected function _parseNestedBlocks()
  {
    $nestedBlocks = array();
    $css = Scaffold::$output;
    $nestedSelectors = array('@media', '@-ms-keyframes', '@-moz-keyframes', '@-webkit-keyframes', '@keyframes');
    foreach($nestedSelectors as $nestedSelector) {
      $start = 0;
      while( ($start = strpos($css, $nestedSelector, $start)) !== false ) {
        $pos = strpos($css, '{', $start);
        if( $pos === false ) {
          continue;
        }
        $stack = array();
        array_push($stack, $css[$pos]);
        $pos++;

        while( !empty($stack) ) {
          if( $css[$pos] == '{' ) {
            array_push($stack, '{');
          } elseif( $css[$pos] == '}' ) {
            array_pop($stack);
          }
          $pos++;
        }
        $nestedBlocks[] = array('start' => $start, 'end' => $pos - 1);
        $start = $pos;
      }
    }
    return $nestedBlocks;
  }
}