/**
  * Load a new Twig instance that uses the Pattern Loader
  */
 public function __construct($options = array())
 {
     // set-up default vars
     $twigDebug = Config::getOption("twigDebug");
     $twigAutoescape = Config::getOption("twigAutoescape");
     // set-up the loader list
     $loaders = array();
     $filesystemLoaderPaths = array();
     $loaders[] = new Twig_Loader_PatternPartialLoader(Config::getOption("patternSourceDir"), array("patternPaths" => $options["patternPaths"]));
     // see if source/_macros exists
     $macrosPath = Config::getOption("sourceDir") . DIRECTORY_SEPARATOR . "_macros";
     if (is_dir($macrosPath)) {
         $filesystemLoaderPaths[] = $macrosPath;
     }
     // see if source/_layouts exists. if so add it to be searchable
     $layoutsPath = Config::getOption("sourceDir") . DIRECTORY_SEPARATOR . "_layouts";
     if (is_dir($layoutsPath)) {
         $filesystemLoaderPaths[] = $layoutsPath;
     }
     // add source/_patterns subdirectories for Drupal theme template compatibility
     $patternSourceDir = Config::getOption("sourceDir") . DIRECTORY_SEPARATOR . "_patterns";
     $patternObjects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($patternSourceDir), \RecursiveIteratorIterator::SELF_FIRST);
     $patternObjects->setFlags(\FilesystemIterator::SKIP_DOTS);
     // sort the returned objects
     $patternObjects = iterator_to_array($patternObjects);
     ksort($patternObjects);
     foreach ($patternObjects as $name => $object) {
         if ($object->isDir()) {
             $filesystemLoaderPaths[] = $object->getPathname();
         }
     }
     // add the paths to the filesystem loader if the paths existed
     if (count($filesystemLoaderPaths) > 0) {
         $loaders[] = new \Twig_Loader_Filesystem($filesystemLoaderPaths);
     }
     $loaders[] = new \Twig_Loader_String();
     // set-up Twig
     $twigLoader = new \Twig_Loader_Chain($loaders);
     $this->instance = new \Twig_Environment($twigLoader, array("debug" => $twigDebug, "autoescape" => $twigAutoescape));
     // customize Twig
     $this->instance = TwigUtil::loadFilters($this->instance);
     $this->instance = TwigUtil::loadFunctions($this->instance);
     $this->instance = TwigUtil::loadTags($this->instance);
     $this->instance = TwigUtil::loadTests($this->instance);
     $this->instance = TwigUtil::loadDateFormats($this->instance);
     $this->instance = TwigUtil::loadDebug($this->instance);
     $this->instance = TwigUtil::loadMacros($this->instance);
     // add node visitor
     $this->instance->addNodeVisitor(new IncludeNodeVisitor());
 }
Exemplo n.º 2
0
 protected function getAllTestCases()
 {
     $testCases = array();
     if (!is_dir($this->rootFolder)) {
         throw new \InvalidArgumentException("\r\n\nInvalid root test folder specified\r\n\nYou said: " . $this->rootFolder . "\r\n\r\n\nThe root folder is a folder which should have all you test files.\r\n\nFor example, this could be a root folder: '/var/www/myproject/tests'. You should then place all your tests inside that 'root' folder. Those tests may be placed in subdirectories as well.\r\n\r\n\nAlso make sure the given path is correct. If you are on Windows, try to put double quotes around the path name: \r\n\nWill NOT work: /> php.exe TestSuite.phar C:\\web development\\htdocs\\myproject\\tests \r\n\nCorrect way: /> php.exe TestSuite.phar \"C:\\web development\\htdocs\\myproject\\tests\" \r\n\r\n\nPlease try again\r\n\r\n");
     }
     $directory = new \RecursiveDirectoryIterator($this->rootFolder);
     $iterator = new \RecursiveIteratorIterator($directory);
     $iterator->setFlags(\RecursiveDirectoryIterator::SKIP_DOTS);
     $regexIterator = new \RegexIterator($iterator, '/^.+\\.php$/i', \RecursiveRegexIterator::GET_MATCH);
     foreach ($regexIterator as $filepath => $object) {
         if (preg_match('#^(.*?)CF([a-zA-Z0-9]+).php$#', $filepath)) {
             continue;
         }
         require_once $filepath;
         $testCase = basename($filepath, '.php');
         $instance = new $testCase();
         $testMethods = preg_grep('/^Test_/i', get_class_methods($instance));
         $testCases[] = new TestCaseMethods($filepath, $instance, $testMethods);
     }
     return $testCases;
 }
Exemplo n.º 3
0
 /**
  * Move static files from source/ to public/
  */
 protected function moveStatic()
 {
     // set-up the dispatcher
     $dispatcherInstance = Dispatcher::getInstance();
     // note the start of the operation
     $dispatcherInstance->dispatch("generator.moveStaticStart");
     // common values
     $publicDir = Config::getOption("publicDir");
     $sourceDir = Config::getOption("sourceDir");
     $ignoreExt = Config::getOption("ie");
     $ignoreDir = Config::getOption("id");
     // iterate over all of the other files in the source directory
     $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($sourceDir), \RecursiveIteratorIterator::SELF_FIRST);
     // make sure dots are skipped
     $objects->setFlags(\FilesystemIterator::SKIP_DOTS);
     foreach ($objects as $name => $object) {
         // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored
         $fileName = str_replace($sourceDir . DIRECTORY_SEPARATOR, "", $name);
         if ($fileName[0] != "_" && !in_array($object->getExtension(), $ignoreExt) && !in_array($object->getFilename(), $ignoreDir)) {
             // catch directories that have the ignored dir in their path
             $ignored = FileUtil::ignoreDir($fileName);
             // check to see if it's a new directory
             if (!$ignored && $object->isDir() && !is_dir($publicDir . "/" . $fileName)) {
                 mkdir($publicDir . "/" . $fileName);
             }
             // check to see if it's a new file or a file that has changed
             if (!$ignored && $object->isFile() && !file_exists($publicDir . "/" . $fileName)) {
                 FileUtil::moveStaticFile($fileName);
             }
         }
     }
     // note the end of the operation
     $dispatcherInstance->dispatch("generator.moveStaticEnd");
 }
Exemplo n.º 4
0
 /**
  * Delete patterns and user-created directories and files in public/
  */
 protected function cleanPublic()
 {
     // make sure patterns exists before trying to clean it
     if (is_dir($this->pd . "/patterns")) {
         $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->pd . "/patterns/"), \RecursiveIteratorIterator::CHILD_FIRST);
         // make sure dots are skipped
         $objects->setFlags(\FilesystemIterator::SKIP_DOTS);
         // for each file figure out what to do with it
         foreach ($objects as $name => $object) {
             if ($object->isDir()) {
                 // if this is a directory remove it
                 rmdir($name);
             } else {
                 if ($object->isFile() && $object->getFilename() != "README") {
                     // if this is a file remove it
                     unlink($name);
                 }
             }
         }
     }
     // scan source/ & public/ to figure out what directories might need to be cleaned up
     $sourceDirs = glob($this->sd . "/*", GLOB_ONLYDIR);
     $publicDirs = glob($this->pd . "/*", GLOB_ONLYDIR);
     // make sure some directories aren't deleted
     $ignoreDirs = array("styleguide");
     foreach ($ignoreDirs as $ignoreDir) {
         $key = array_search($this->pd . "/" . $ignoreDir, $publicDirs);
         if ($key !== false) {
             unset($publicDirs[$key]);
         }
     }
     // compare source dirs against public. remove those dirs w/ an underscore in source/ from the public/ list
     foreach ($sourceDirs as $sourceDir) {
         $cleanDir = str_replace($this->sd . "/", "", $sourceDir);
         if ($cleanDir[0] == "_") {
             $key = array_search($this->pd . "/" . str_replace("_", "", $cleanDir), $publicDirs);
             if ($key !== false) {
                 unset($publicDirs[$key]);
             }
         }
     }
     // for the remaining dirs in public delete them and their files
     foreach ($publicDirs as $dir) {
         $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir), \RecursiveIteratorIterator::CHILD_FIRST);
         // make sure dots are skipped
         $objects->setFlags(\FilesystemIterator::SKIP_DOTS);
         foreach ($objects as $name => $object) {
             if ($object->isDir()) {
                 rmdir($name);
             } else {
                 if ($object->isFile()) {
                     unlink($name);
                 }
             }
         }
         rmdir($dir);
     }
 }
Exemplo n.º 5
0
 /**
  * Gather all of the information related to the patterns
  */
 public static function gather($options = array())
 {
     // set default vars
     $exportClean = isset($options["exportClean"]) ? $options["exportClean"] : false;
     $exportFiles = isset($options["exportClean"]) ? $options["exportFiles"] : false;
     $dispatcherInstance = Dispatcher::getInstance();
     // cleaning the var for use below, i know this is stupid
     $options = array();
     // dispatch that the data gather has started
     $event = new PatternDataEvent($options);
     $dispatcherInstance->dispatch("patternData.gatherStart", $event);
     // load up the rules for parsing patterns and the directories
     self::loadRules($options);
     // dispatch that the rules are loaded
     $event = new PatternDataEvent($options);
     $dispatcherInstance->dispatch("patternData.rulesLoaded", $event);
     // iterate over the patterns & related data and regenerate the entire site if they've changed
     // seems a little silly to use symfony finder here. not really giving me any power
     $patternObjects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::getOption("patternSourceDir")), \RecursiveIteratorIterator::SELF_FIRST);
     $patternObjects->setFlags(\FilesystemIterator::SKIP_DOTS);
     // sort the returned objects
     $patternObjects = iterator_to_array($patternObjects);
     ksort($patternObjects);
     $patternSourceDir = Config::getOption("patternSourceDir");
     foreach ($patternObjects as $name => $object) {
         $ext = $object->getExtension();
         $isDir = $object->isDir();
         $isFile = $object->isFile();
         $path = str_replace($patternSourceDir . "/", "", $object->getPath());
         $pathName = str_replace($patternSourceDir . "/", "", $object->getPathname());
         $name = $object->getFilename();
         $depth = substr_count($pathName, DIRECTORY_SEPARATOR);
         // iterate over the rules and see if the current file matches one, if so run the rule
         foreach (self::$rules as $rule) {
             if ($rule->test($depth, $ext, $isDir, $isFile, $name)) {
                 $rule->run($depth, $ext, $path, $pathName, $name);
             }
         }
     }
     // dispatch that the data is loaded
     $event = new PatternDataEvent($options);
     $dispatcherInstance->dispatch("patternData.dataLoaded", $event);
     // make sure all of the appropriate pattern data is pumped into $this->d for rendering patterns
     $dataLinkExporter = new DataLinkExporter();
     $dataLinkExporter->run();
     // make sure all of the appropriate pattern data is pumped into $this->d for rendering patterns
     $dataMergeExporter = new DataMergeExporter();
     $dataMergeExporter->run();
     // dispatch that the raw pattern helper is about to start
     $event = new PatternDataEvent($options);
     $dispatcherInstance->dispatch("patternData.rawPatternHelperStart", $event);
     // add the lineage info to PatternData::$store
     $rawPatternHelper = new RawPatternHelper();
     $rawPatternHelper->run();
     // dispatch that the raw pattern helper is ended
     $event = new PatternDataEvent($options);
     $dispatcherInstance->dispatch("patternData.rawPatternHelperEnd", $event);
     // dispatch that the lineage helper is about to start
     $event = new PatternDataEvent($options);
     $dispatcherInstance->dispatch("patternData.lineageHelperStart", $event);
     // add the lineage info to PatternData::$store
     $lineageHelper = new LineageHelper();
     $lineageHelper->run();
     // dispatch that the lineage helper is ended
     $event = new PatternDataEvent($options);
     $dispatcherInstance->dispatch("patternData.lineageHelperEnd", $event);
     // dispatch that the pattern state helper is about to start
     $event = new PatternDataEvent($options);
     $dispatcherInstance->dispatch("patternData.patternStateHelperStart", $event);
     // using the lineage info update the pattern states on PatternData::$store
     $patternStateHelper = new PatternStateHelper();
     $patternStateHelper->run();
     // dispatch that the pattern state helper is ended
     $event = new PatternDataEvent($options);
     $dispatcherInstance->dispatch("patternData.patternStateHelperEnd", $event);
     // set-up code pattern paths
     $ppdExporter = new PatternPathSrcExporter();
     $patternPathSrc = $ppdExporter->run();
     $options = array();
     $options["patternPaths"] = $patternPathSrc;
     // dispatch that the code helper is about to start
     $event = new PatternDataEvent($options);
     $dispatcherInstance->dispatch("patternData.codeHelperStart", $event);
     // render out all of the patterns and store the generated info in PatternData::$store
     $options["exportFiles"] = $exportFiles;
     $options["exportClean"] = $exportClean;
     $patternCodeHelper = new PatternCodeHelper($options);
     $patternCodeHelper->run();
     // dispatch that the pattern code helper is ended
     $event = new PatternDataEvent($options);
     $dispatcherInstance->dispatch("patternData.patternCodeHelperEnd", $event);
     // dispatch that the gather has ended
     $event = new PatternDataEvent($options);
     $dispatcherInstance->dispatch("patternData.gatherEnd", $event);
 }
Exemplo n.º 6
0
 public function watchStarterKit()
 {
     // default vars
     $starterKitPath = $this->starterKitPathPrompt();
     $sourceDir = Config::getOption("sourceDir");
     $fs = new Filesystem();
     $c = false;
     // track that one loop through the pattern file listing has completed
     $o = new \stdClass();
     // create an object to hold the properties
     $cp = new \stdClass();
     // create an object to hold a clone of $o
     $o->patterns = new \stdClass();
     Console::writeLine("watching your starterkit for changes...");
     // run forever
     while (true) {
         // clone the patterns so they can be checked in case something gets deleted
         $cp = clone $o->patterns;
         $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($starterKitPath), \RecursiveIteratorIterator::SELF_FIRST);
         // make sure dots are skipped
         $objects->setFlags(\FilesystemIterator::SKIP_DOTS);
         foreach ($objects as $name => $object) {
             // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored
             $fileName = str_replace($starterKitPath . DIRECTORY_SEPARATOR, "", $name);
             // check to see if it's a new directory
             if ($object->isDir() && !isset($o->{$fileName}) && !is_dir($starterKitPath . "/" . $fileName)) {
                 mkdir($sourceDir . "/" . $fileName);
                 $o->{$fileName} = "dir created";
                 // placeholder
                 Console::writeLine($fileName . "/ directory was created...");
             }
             // check to see if it's a new file or a file that has changed
             if (file_exists($name)) {
                 $mt = $object->getMTime();
                 if ($object->isFile() && !isset($o->{$fileName}) && !file_exists($sourceDir . DIRECTORY_SEPARATOR . $fileName)) {
                     $o->{$fileName} = $mt;
                     $fs->copy($starterKitPath . DIRECTORY_SEPARATOR . $fileName, $sourceDir . DIRECTORY_SEPARATOR . $fileName);
                     Console::writeInfo($fileName . " added...");
                 } else {
                     if ($object->isFile() && isset($o->{$fileName}) && $o->{$fileName} != $mt) {
                         $o->{$fileName} = $mt;
                         $fs->copy($starterKitPath . DIRECTORY_SEPARATOR . $fileName, $sourceDir . DIRECTORY_SEPARATOR . $fileName);
                         Console::writeInfo($fileName . " changed...");
                     } else {
                         if (!isset($o->fileName)) {
                             $o->{$fileName} = $mt;
                         }
                     }
                 }
             } else {
                 unset($o->{$fileName});
             }
         }
         $c = true;
         // taking out the garbage. basically killing mustache after each run.
         if (gc_enabled()) {
             gc_collect_cycles();
         }
         // pause for .05 seconds to give the CPU a rest
         usleep(50000);
     }
 }
Exemplo n.º 7
0
 /**
  * Delete patterns and user-created directories and files in public/
  */
 public static function cleanPublic()
 {
     // set-up the dispatcher
     $dispatcherInstance = Dispatcher::getInstance();
     // dispatch that the data gather has started
     $dispatcherInstance->dispatch("fileUtil.cleanPublicStart");
     // default var
     $patternPublicDir = Config::getOption("patternPublicDir");
     // make sure patterns exists before trying to clean it
     if (is_dir($patternPublicDir)) {
         // symfony finder doesn't support child first and I don't want to do array crap
         $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($patternPublicDir), \RecursiveIteratorIterator::CHILD_FIRST);
         // make sure dots are skipped
         $objects->setFlags(\FilesystemIterator::SKIP_DOTS);
         // for each file figure out what to do with it
         foreach ($objects as $name => $object) {
             if ($object->isDir()) {
                 // if this is a directory remove it
                 rmdir($name);
             } else {
                 if ($object->isFile() && $object->getFilename() != "README") {
                     // if this is a file remove it
                     unlink($name);
                 }
             }
         }
     }
     // scan source/ & public/ to figure out what directories might need to be cleaned up
     $publicDir = Config::getOption("publicDir");
     $sourceDir = Config::getOption("sourceDir");
     $publicDirs = glob($publicDir . DIRECTORY_SEPARATOR . "*", GLOB_ONLYDIR);
     $sourceDirs = glob($sourceDir . DIRECTORY_SEPARATOR . "*", GLOB_ONLYDIR);
     // make sure some directories aren't deleted
     $ignoreDirs = array("styleguide", "patternlab-components");
     foreach ($ignoreDirs as $ignoreDir) {
         $key = array_search($publicDir . DIRECTORY_SEPARATOR . $ignoreDir, $publicDirs);
         if ($key !== false) {
             unset($publicDirs[$key]);
         }
     }
     // compare source dirs against public. remove those dirs w/ an underscore in source/ from the public/ list
     foreach ($sourceDirs as $dir) {
         $cleanDir = str_replace($sourceDir . DIRECTORY_SEPARATOR, "", $dir);
         if ($cleanDir[0] == "_") {
             $key = array_search($publicDir . DIRECTORY_SEPARATOR . str_replace("_", "", $cleanDir), $publicDirs);
             if ($key !== false) {
                 unset($publicDirs[$key]);
             }
         }
     }
     // for the remaining dirs in public delete them and their files
     foreach ($publicDirs as $dir) {
         // symfony finder doesn't support child first and I don't want to do array crap
         $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir), \RecursiveIteratorIterator::CHILD_FIRST);
         // make sure dots are skipped
         $objects->setFlags(\FilesystemIterator::SKIP_DOTS);
         foreach ($objects as $name => $object) {
             if ($object->isDir()) {
                 rmdir($name);
             } else {
                 if ($object->isFile()) {
                     unlink($name);
                 }
             }
         }
         rmdir($dir);
     }
     $dispatcherInstance->dispatch("fileUtil.cleanPublicEnd");
 }
Exemplo n.º 8
0
 /**
  * Run any migrations found in core/migrations that match the approved types
  * @param  {String}      the filename of the migration
  * @param  {String}      the path of the source directory
  * @param  {String}      the path to the destination
  * @param  {Boolean}     moving a single file or a directory
  */
 protected function runMigration($filename, $sourcePath, $destinationPath, $singleFile)
 {
     $filename = str_replace(".json", "", $filename);
     print "   Starting the " . $filename . " migration...\n";
     if ($singleFile) {
         copy($sourcePath . $fileName, $destinationPath . $fileName);
     } else {
         $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($sourcePath), \RecursiveIteratorIterator::SELF_FIRST);
         $objects->setFlags(\FilesystemIterator::SKIP_DOTS);
         foreach ($objects as $object) {
             // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored
             $fileName = str_replace($sourcePath, "", $object->getPathname());
             // check to see if it's a new directory
             if ($object->isDir() && !is_dir($destinationPath . $fileName)) {
                 mkdir($destinationPath . $fileName);
             } else {
                 if ($object->isFile()) {
                     copy($sourcePath . $fileName, $destinationPath . $fileName);
                 }
             }
         }
     }
     print "   Completed the " . $filename . " migration...\n";
 }
Exemplo n.º 9
0
 /**
  * Pulls together a bunch of functions from builder.lib.php in an order that makes sense
  * @param  {Boolean}       decide if CSS should be parsed and saved. performance hog.
  * @param  {Boolean}       decide if static files like CSS and JS should be moved
  */
 public function generate($enableCSS = false, $moveStatic = true, $noCacheBuster = false)
 {
     $timePL = true;
     // track how long it takes to generate a PL site
     if ($timePL) {
         $mtime = microtime();
         $mtime = explode(" ", $mtime);
         $mtime = $mtime[1] + $mtime[0];
         $starttime = $mtime;
     }
     $this->noCacheBuster = $noCacheBuster;
     if ($enableCSS) {
         // enable CSS globally throughout PL
         $this->enableCSS = true;
         // initialize CSS rule saver
         $this->initializeCSSRuleSaver();
         print "CSS generation enabled. This could take a few seconds...\n";
     }
     // gather up all of the data to be used in patterns
     $this->gatherData();
     // gather all of the various pattern info
     $this->gatherPatternInfo();
     // clean the public directory to remove old files
     if ($this->cleanPublic == "true" && $moveStatic) {
         $this->cleanPublic();
     }
     // render out the patterns and move them to public/patterns
     $this->generatePatterns();
     // render out the index and style guide
     $this->generateMainPages();
     // make sure data exists
     if (!is_dir($this->pd . "/data")) {
         mkdir($this->pd . "/data");
     }
     // iterate over the data files and regenerate the entire site if they've changed
     $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->sd . "/_data/"), \RecursiveIteratorIterator::SELF_FIRST);
     // make sure dots are skipped
     $objects->setFlags(\FilesystemIterator::SKIP_DOTS);
     foreach ($objects as $name => $object) {
         $fileName = str_replace($this->sd . "/_data" . DIRECTORY_SEPARATOR, "", $name);
         if ($fileName[0] != "_" && $object->isFile()) {
             $this->moveStaticFile("_data/" . $fileName, "", "_data", "data");
         }
     }
     // move all of the files unless pattern only is set
     if ($moveStatic) {
         // iterate over all of the other files in the source directory
         $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->sd . "/"), \RecursiveIteratorIterator::SELF_FIRST);
         // make sure dots are skipped
         $objects->setFlags(\FilesystemIterator::SKIP_DOTS);
         foreach ($objects as $name => $object) {
             // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored
             $fileName = str_replace($this->sd . DIRECTORY_SEPARATOR, "", $name);
             if ($fileName[0] != "_" && !in_array($object->getExtension(), $this->ie) && !in_array($object->getFilename(), $this->id)) {
                 // catch directories that have the ignored dir in their path
                 $ignoreDir = $this->ignoreDir($fileName);
                 // check to see if it's a new directory
                 if (!$ignoreDir && $object->isDir() && !is_dir($this->pd . "/" . $fileName)) {
                     mkdir($this->pd . "/" . $fileName);
                 }
                 // check to see if it's a new file or a file that has changed
                 if (!$ignoreDir && $object->isFile() && !file_exists($this->pd . "/" . $fileName)) {
                     $this->moveStaticFile($fileName);
                 }
             }
         }
     }
     // update the change time so the auto-reload will fire (doesn't work for the index and style guide)
     $this->updateChangeTime();
     print "your site has been generated...\n";
     // print out how long it took to generate the site
     if ($timePL) {
         $mtime = microtime();
         $mtime = explode(" ", $mtime);
         $mtime = $mtime[1] + $mtime[0];
         $endtime = $mtime;
         $totaltime = $endtime - $starttime;
         $mem = round(memory_get_peak_usage(true) / 1024 / 1024, 2);
         print "site generation took " . $totaltime . " seconds and used " . $mem . "MB of memory...\n";
     }
 }
Exemplo n.º 10
0
 /**
  * Watch the source/ directory for any changes to existing files. Will run forever if given the chance.
  * @param  {Boolean}       decide if the reload server should be turned on
  * @param  {Boolean}       decide if static files like CSS and JS should be moved
  */
 public function watch($reload = false, $moveStatic = true, $noCacheBuster = false)
 {
     // automatically start the auto-refresh tool
     if ($reload) {
         $path = str_replace("lib" . DIRECTORY_SEPARATOR . "PatternLab", "autoReloadServer.php", __DIR__);
         $fp = popen("php " . $path . " -s", "r");
         print "starting page auto-reload...\n";
     }
     $this->noCacheBuster = $noCacheBuster;
     $c = false;
     // track that one loop through the pattern file listing has completed
     $o = new \stdClass();
     // create an object to hold the properties
     $cp = new \stdClass();
     // create an object to hold a clone of $o
     $o->patterns = new \stdClass();
     print "watching your site for changes...\n";
     // run forever
     while (true) {
         // clone the patterns so they can be checked in case something gets deleted
         $cp = clone $o->patterns;
         // iterate over the patterns & related data and regenerate the entire site if they've changed
         $patternObjects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->sd . "/_patterns/"), \RecursiveIteratorIterator::SELF_FIRST);
         // make sure dots are skipped
         $patternObjects->setFlags(\FilesystemIterator::SKIP_DOTS);
         foreach ($patternObjects as $name => $object) {
             // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored
             $fileName = str_replace($this->sd . "/_patterns" . DIRECTORY_SEPARATOR, "", $name);
             $fileNameClean = str_replace(DIRECTORY_SEPARATOR . "_", DIRECTORY_SEPARATOR, $fileName);
             if ($object->isFile() && ($object->getExtension() == "mustache" || $object->getExtension() == "json")) {
                 // make sure this isn't a hidden pattern
                 $patternParts = explode(DIRECTORY_SEPARATOR, $fileName);
                 $pattern = isset($patternParts[2]) ? $patternParts[2] : $patternParts[1];
                 // make sure the pattern still exists in source just in case it's been deleted during the iteration
                 if (file_exists($name)) {
                     $mt = $object->getMTime();
                     if (isset($o->patterns->{$fileName}) && $o->patterns->{$fileName} != $mt) {
                         $o->patterns->{$fileName} = $mt;
                         $this->updateSite($fileName, "changed");
                     } else {
                         if (!isset($o->patterns->{$fileName}) && $c) {
                             $o->patterns->{$fileName} = $mt;
                             $this->updateSite($fileName, "added");
                             if ($object->getExtension() == "mustache") {
                                 $patternSrcPath = str_replace(".mustache", "", $fileName);
                                 $patternDestPath = str_replace("/", "-", $patternSrcPath);
                                 $render = $pattern[0] != "_" ? true : false;
                                 $this->patternPaths[$patternParts[0]][$pattern] = array("patternSrcPath" => $patternSrcPath, "patternDestPath" => $patternDestPath, "render" => $render);
                             }
                         } else {
                             if (!isset($o->patterns->{$fileName})) {
                                 $o->patterns->{$fileName} = $mt;
                             }
                         }
                     }
                     if ($c && isset($o->patterns->{$fileName})) {
                         unset($cp->{$fileName});
                     }
                 } else {
                     // the file was removed during the iteration so remove references to the item
                     unset($o->patterns->{$fileName});
                     unset($cp->{$fileName});
                     unset($this->patternPaths[$patternParts[0]][$pattern]);
                     $this->updateSite($fileName, "removed");
                 }
             }
         }
         // make sure old entries are deleted
         // will throw "pattern not found" errors if an entire directory is removed at once but that shouldn't be a big deal
         if ($c) {
             foreach ($cp as $fileName => $mt) {
                 unset($o->patterns->{$fileName});
                 $patternParts = explode(DIRECTORY_SEPARATOR, $fileName);
                 $pattern = isset($patternParts[2]) ? $patternParts[2] : $patternParts[1];
                 unset($this->patternPaths[$patternParts[0]][$pattern]);
                 $this->updateSite($fileName, "removed");
             }
         }
         // iterate over the data files and regenerate the entire site if they've changed
         $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->sd . "/_data/"), \RecursiveIteratorIterator::SELF_FIRST);
         // make sure dots are skipped
         $objects->setFlags(\FilesystemIterator::SKIP_DOTS);
         foreach ($objects as $name => $object) {
             $fileName = str_replace($this->sd . "/_data" . DIRECTORY_SEPARATOR, "", $name);
             $mt = $object->getMTime();
             if (!isset($o->{$fileName})) {
                 $o->{$fileName} = $mt;
                 if ($fileName[0] != "_" && $object->isFile()) {
                     $this->moveStaticFile("_data/" . $fileName, "", "_data", "data");
                 }
             } else {
                 if ($o->{$fileName} != $mt) {
                     $o->{$fileName} = $mt;
                     $this->updateSite($fileName, "changed");
                     if ($fileName[0] != "_" && $object->isFile()) {
                         $this->moveStaticFile("_data/" . $fileName, "", "_data", "data");
                     }
                 }
             }
         }
         // iterate over all of the other files in the source directory and move them if their modified time has changed
         if ($moveStatic) {
             $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->sd . "/"), \RecursiveIteratorIterator::SELF_FIRST);
             // make sure dots are skipped
             $objects->setFlags(\FilesystemIterator::SKIP_DOTS);
             foreach ($objects as $name => $object) {
                 // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored
                 $fileName = str_replace($this->sd . DIRECTORY_SEPARATOR, "", $name);
                 if ($fileName[0] != "_" && !in_array($object->getExtension(), $this->ie) && !in_array($object->getFilename(), $this->id)) {
                     // catch directories that have the ignored dir in their path
                     $ignoreDir = $this->ignoreDir($fileName);
                     // check to see if it's a new directory
                     if (!$ignoreDir && $object->isDir() && !isset($o->{$fileName}) && !is_dir($this->pd . "/" . $fileName)) {
                         mkdir($this->pd . "/" . $fileName);
                         $o->{$fileName} = "dir created";
                         // placeholder
                         print $fileName . "/ directory was created...\n";
                     }
                     // check to see if it's a new file or a file that has changed
                     if (file_exists($name)) {
                         $mt = $object->getMTime();
                         if (!$ignoreDir && $object->isFile() && !isset($o->{$fileName}) && !file_exists($this->pd . "/" . $fileName)) {
                             $o->{$fileName} = $mt;
                             $this->moveStaticFile($fileName, "added");
                             if ($object->getExtension() == "css") {
                                 $this->updateSite($fileName, "changed", 0);
                                 // make sure the site is updated for MQ reasons
                             }
                         } else {
                             if (!$ignoreDir && $object->isFile() && isset($o->{$fileName}) && $o->{$fileName} != $mt) {
                                 $o->{$fileName} = $mt;
                                 $this->moveStaticFile($fileName, "changed");
                                 if ($object->getExtension() == "css") {
                                     $this->updateSite($fileName, "changed", 0);
                                     // make sure the site is updated for MQ reasons
                                 }
                             } else {
                                 if (!isset($o->fileName)) {
                                     $o->{$fileName} = $mt;
                                 }
                             }
                         }
                     } else {
                         unset($o->{$fileName});
                     }
                 }
             }
         }
         $c = true;
         // taking out the garbage. basically killing mustache after each run.
         unset($this->mpl);
         unset($this->msf);
         unset($this->mv);
         if (gc_enabled()) {
             gc_collect_cycles();
         }
         // output anything the reload server might send our way
         if ($reload) {
             $output = fgets($fp, 100);
             if ($output != "\n") {
                 print $output;
             }
         }
         // pause for .05 seconds to give the CPU a rest
         usleep(50000);
     }
     // close the auto-reload process, this shouldn't do anything
     fclose($fp);
 }