├── .editorconfig ├── LICENSE ├── README.md ├── composer.json ├── config └── config.yml.default └── src └── PatternLab ├── Annotations.php ├── Builder.php ├── Config.php ├── Console.php ├── Console ├── Command.php ├── Commands │ ├── ConfigCommand.php │ ├── ExportCommand.php │ ├── GenerateCommand.php │ ├── HelpCommand.php │ ├── ServerCommand.php │ ├── StarterKitCommand.php │ ├── VersionCommand.php │ └── WatchCommand.php ├── Event.php ├── ProcessSpawner.php └── ProcessSpawnerEvent.php ├── Data.php ├── Dispatcher.php ├── Fetch.php ├── FileUtil.php ├── Generator.php ├── InstallerUtil.php ├── JSON.php ├── Listener.php ├── Migrator.php ├── Parsers └── Documentation.php ├── PatternData.php ├── PatternData ├── Event.php ├── Exporter.php ├── Exporters │ ├── DataLinkExporter.php │ ├── DataMergeExporter.php │ ├── NavItemsExporter.php │ ├── PatternPartialsExporter.php │ ├── PatternPathDestsExporter.php │ ├── PatternPathSrcExporter.php │ └── ViewAllPathsExporter.php ├── Helper.php ├── Helpers │ ├── LineageHelper.php │ ├── PatternCodeHelper.php │ ├── PatternStateHelper.php │ ├── Plugins │ │ └── CSSRuleSaverHelperPlugin.php │ └── RawPatternHelper.php ├── Rule.php └── Rules │ ├── DocumentationRule.php │ ├── PatternInfoListItemsRule.php │ ├── PatternInfoRule.php │ ├── PatternRule.php │ ├── PatternSubtypeRule.php │ ├── PatternTypeRule.php │ └── PseudoPatternRule.php ├── PatternEngine.php ├── PatternEngine ├── Loader.php ├── Rule.php └── Util.php ├── Saying.php ├── Template.php ├── Timer.php ├── Util.php ├── Watcher.php └── Zippy ├── UnpackAdapter.php └── UnpackFileStrategy.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = tab 6 | trim_trailing_whitespace = false 7 | end_of_line = lf 8 | insert_final_newline = true 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Brad Frost, http://bradfrostweb.com & Dave Olsen, http://dmolsen.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![license](https://img.shields.io/github/license/pattern-lab/patternlab-php-core.svg) 2 | [![Gitter](https://img.shields.io/gitter/room/pattern-lab/php.svg)](https://gitter.im/pattern-lab/php) 3 | 4 | # Pattern Lab Core 5 | 6 | This repository contains the core functionality for Pattern Lab. Pattern Lab Core is designed to be included as a dependency within Editions. Turn it up. 7 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pattern-lab/core", 3 | "description": "The core functionality for Pattern Lab.", 4 | "keywords": ["pattern lab", "styleguide", "style guide", "atomic", "atomic design"], 5 | "homepage": "http://patternlab.io", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Evan Lovely", 10 | "homepage": "http://evanlovely.com", 11 | "role": "Lead Developer" 12 | }, 13 | { 14 | "name": "Salem Ghoweri", 15 | "homepage": "https://github.com/sghoweri", 16 | "role": "Developer" 17 | }, 18 | { 19 | "name": "Dave Olsen", 20 | "email": "dmolsen@gmail.com", 21 | "homepage": "http://dmolsen.com", 22 | "role": "Former Lead Developer" 23 | }, 24 | { 25 | "name": "Brad Frost", 26 | "homepage": "http://bradfrostweb.com", 27 | "role": "Creator" 28 | } 29 | ], 30 | "support": { 31 | "issues": "https://github.com/pattern-lab/patternlab-php-core/issues", 32 | "wiki": "http://patternlab.io/docs/", 33 | "source": "https://github.com/pattern-lab/patternlab-php-core/releases" 34 | }, 35 | "autoload": { 36 | "psr-0": { 37 | "PatternLab": "src/" 38 | } 39 | }, 40 | "require": { 41 | "php": ">=5.4", 42 | "alchemy/zippy": "^0.3", 43 | "doctrine/collections": "1.4.0", 44 | "kevinlebrun/colors.php": "^1.0", 45 | "michelf/php-markdown": "^1.6", 46 | "seld/jsonlint": "^1.0", 47 | "shudrum/array-finder": "^1.0", 48 | "symfony/event-dispatcher": "^3.0", 49 | "symfony/filesystem": "^3.0", 50 | "symfony/finder": "^3.0", 51 | "symfony/process": "^3.1", 52 | "symfony/yaml": "^3.0" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /config/config.yml.default: -------------------------------------------------------------------------------- 1 | ## Configuration Options for Pattern Lab 2 | 3 | # pattern lab version 4 | v: "2.6.0" 5 | 6 | # what to do when included config var conflicts, q for question, a for always override, n for never override 7 | overrideConfig: "q" 8 | 9 | # file extensions to ignore when building or watching the source dir, separate with a comma 10 | ie: 11 | - "DS_Store" 12 | - "less" 13 | - "scss" 14 | 15 | # directories and files to ignore when building or watching the source dir, separate with a comma 16 | id: 17 | - "scss" 18 | - ".svn" 19 | - ".sass-cache" 20 | 21 | # whether the public directory should be cleaned when generating your site 22 | cleanPublic: "true" 23 | 24 | # the order of pattern states, css class names 25 | patternStates: 26 | - "inprogress" 27 | - "inreview" 28 | - "complete" 29 | 30 | # the pattern types that shouldn't be included in the style guide, useful if you nest pages/templates 31 | styleGuideExcludes: "" 32 | 33 | # should the cache buster be on, set to false to set the cacheBuster value to 0 34 | cacheBusterOn: "true" 35 | 36 | # project directories 37 | exportDir: "export" 38 | publicDir: "public" 39 | sourceDir: "source" 40 | 41 | # defaultPattern to show on load 42 | defaultPattern: "all" 43 | 44 | # show pattern info by default on the "view all" views 45 | defaultShowPatternInfo: false 46 | -------------------------------------------------------------------------------- /src/PatternLab/Annotations.php: -------------------------------------------------------------------------------- 1 | dispatch("annotations.gatherStart"); 50 | 51 | // set-up the comments store 52 | self::$store["comments"] = array(); 53 | 54 | // create the annotations dir if it doesn't exist 55 | if (!is_dir($annotationsDir)) { 56 | mkdir($annotationsDir); 57 | } 58 | 59 | // find the markdown-based annotations 60 | $finder = new Finder(); 61 | $finder->files()->name("*.md")->in($annotationsDir); 62 | $finder->sortByName(); 63 | 64 | foreach ($finder as $name => $file) { 65 | 66 | $data = array(); 67 | $data[0] = array(); 68 | 69 | $text = file_get_contents($file->getPathname()); 70 | 71 | $matches = (strpos($text,PHP_EOL."~*~".PHP_EOL) !== false) ? explode(PHP_EOL."~*~".PHP_EOL,$text) : array($text); 72 | 73 | foreach ($matches as $match) { 74 | 75 | list($yaml,$markdown) = Documentation::parse($match); 76 | 77 | if (isset($yaml["el"]) || isset($yaml["selector"])) { 78 | $data[0]["el"] = (isset($yaml["el"])) ? $yaml["el"] : $yaml["selector"]; 79 | } else { 80 | $data[0]["el"] = "#someimpossibleselector"; 81 | } 82 | $data[0]["title"] = isset($yaml["title"]) ? $yaml["title"] : ""; 83 | $data[0]["comment"] = $markdown; 84 | 85 | self::$store["comments"] = array_merge(self::$store["comments"],$data); 86 | 87 | } 88 | 89 | } 90 | 91 | // read in the old style annotations.js, modify the data and generate JSON array to merge 92 | $data = array(); 93 | $oldStyleAnnotationsPath = $annotationsDir.DIRECTORY_SEPARATOR."annotations.js"; 94 | if (file_exists($oldStyleAnnotationsPath)) { 95 | $text = trim(file_get_contents($oldStyleAnnotationsPath)); 96 | $text = str_replace("var comments = ","",$text); 97 | if ($text[strlen($text)-1] == ";") { 98 | $text = rtrim($text,";"); 99 | } 100 | $data = json_decode($text,true); 101 | if ($jsonErrorMessage = JSON::hasError()) { 102 | JSON::lastErrorMsg(Console::getHumanReadablePath($oldStyleAnnotationsPath),$jsonErrorMessage,$data); 103 | } 104 | } 105 | 106 | // merge in any data from the old file if the json decode was successful 107 | if (is_array($data) && isset($data["comments"])) { 108 | self::$store["comments"] = array_merge(self::$store["comments"],$data["comments"]); 109 | } 110 | 111 | $dispatcherInstance->dispatch("annotations.gatherEnd"); 112 | 113 | } 114 | 115 | /** 116 | * Get the data in the store 117 | */ 118 | public static function get() { 119 | return self::$store; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/PatternLab/Console/Command.php: -------------------------------------------------------------------------------- 1 | pathPHP = Console::getPathPHP(); 28 | $this->pathConsole = Console::getPathConsole(); 29 | 30 | } 31 | 32 | /** 33 | * See if a particular command matches the one passed via the command line 34 | * @param {String} the command to check 35 | * 36 | * @return {Boolean} the result of the test 37 | */ 38 | public function test($command) { 39 | return ($command == str_replace(":","",$this->command)); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/PatternLab/Console/Commands/ConfigCommand.php: -------------------------------------------------------------------------------- 1 | command = "config"; 29 | 30 | Console::setCommand($this->command,"Configure Pattern Lab","The --config command allows for the review and update of existing Pattern Lab config options.","c"); 31 | Console::setCommandOption($this->command,"get:","Get the value for a specific config option.","To get a configuration option:","","configOption"); 32 | Console::setCommandOption($this->command,"list","List the current config options.","To list the current configuration:"); 33 | Console::setCommandOption($this->command,"set:","Set the value for a specific config option.","To set a configuration option:","","configOption=\"configValue\""); 34 | 35 | } 36 | 37 | public function run() { 38 | 39 | if (Console::findCommandOption("list")) { 40 | 41 | $this->listOptions(); 42 | 43 | } else if (Console::findCommandOption("get")) { 44 | 45 | $this->getOption(); 46 | 47 | } else if (Console::findCommandOption("set")) { 48 | 49 | $this->setOption(); 50 | 51 | } else { 52 | 53 | Console::writeHelpCommand($this->command); 54 | 55 | } 56 | 57 | } 58 | 59 | /** 60 | * Get the given option and return its value 61 | */ 62 | private function getOption() { 63 | 64 | // figure out which option was passed 65 | $searchOption = Console::findCommandOptionValue("get"); 66 | $optionValue = Config::getOption($searchOption); 67 | 68 | // write it out 69 | if (!$optionValue) { 70 | Console::writeError("the --get value you provided, ".$searchOption.", does not exists in the config..."); 71 | } else { 72 | $optionValue = (is_array($optionValue)) ? implode(", ",$optionValue) : $optionValue; 73 | $optionValue = (!$optionValue) ? "false" : $optionValue; 74 | Console::writeInfo($searchOption.": ".$optionValue.""); 75 | } 76 | 77 | } 78 | 79 | /** 80 | * List out of the options available in the config 81 | */ 82 | private function listOptions() { 83 | 84 | // get all of the options 85 | $options = Config::getOptions(); 86 | 87 | // sort 'em alphabetically 88 | ksort($options); 89 | 90 | // find length of longest option 91 | $this->lengthLong = 0; 92 | foreach ($options as $optionName => $optionValue) { 93 | $this->lengthLong = (strlen($optionName) > $this->lengthLong) ? strlen($optionName) : $this->lengthLong; 94 | } 95 | 96 | $this->writeOutOptions($options); 97 | 98 | } 99 | 100 | /** 101 | * Set the given option to the given value 102 | */ 103 | protected function setOption() { 104 | 105 | // find the value that was passed 106 | $updateOption = Console::findCommandOptionValue("set"); 107 | $updateOptionBits = explode("=",$updateOption); 108 | if (count($updateOptionBits) == 1) { 109 | Console::writeError("the --set value should look like optionName=\"optionValue\". nothing was updated..."); 110 | } 111 | 112 | // set the name and value that were passed 113 | $updateName = $updateOptionBits[0]; 114 | $updateValue = (($updateOptionBits[1][0] == "\"") || ($updateOptionBits[1][0] == "'")) ? substr($updateOptionBits[1],1,strlen($updateOptionBits[1])-1) : $updateOptionBits[1]; 115 | 116 | // make sure the option being updated already exists 117 | $currentValue = Config::getOption($updateName); 118 | 119 | if (!$currentValue) { 120 | Console::writeError("the --set option you provided, ".$updateName.", does not exists in the config. nothing will be updated..."); 121 | } else { 122 | Config::updateConfigOption($updateName,$updateValue); 123 | Console::writeInfo("config option updated..."); 124 | } 125 | 126 | } 127 | 128 | /** 129 | * Write out the given options. Check to see if it's a nested sequential or associative array 130 | * @param {Mixed} the options to check and write out 131 | * @param {String} copy to be added to the beginning of the option if nested 132 | */ 133 | private function writeOutOptions($options, $pre = "") { 134 | 135 | foreach ($options as $optionName => $optionValue) { 136 | 137 | if (is_array($optionValue) && (count($optionValue) > 0) && !isset($optionValue[0])) { 138 | 139 | $this->writeOutOptions($optionValue, $optionName."."); 140 | 141 | } else { 142 | 143 | $optionValue = (is_array($optionValue) && isset($optionValue[0])) ? implode(", ",$optionValue) : $optionValue; 144 | $optionValue = (!$optionValue) ? "false" : $optionValue; 145 | $spacer = Console::getSpacer($this->lengthLong,strlen($pre.$optionName)); 146 | Console::writeLine("".$pre.$optionName.":".$spacer.$optionValue); 147 | 148 | } 149 | 150 | } 151 | 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/PatternLab/Console/Commands/ExportCommand.php: -------------------------------------------------------------------------------- 1 | command = "export"; 30 | 31 | Console::setCommand($this->command,"Export Pattern Lab patterns & assets","The export command generates your patterns without Pattern Lab's CSS & JS, copies static assets from public/, and puts all of it in export/.","e"); 32 | Console::setCommandOption($this->command,"clean","Don't add any header or footer mark-up to the exported patterns.","To generate clean versions of your patterns:"); 33 | 34 | } 35 | 36 | public function run() { 37 | 38 | // set-up required vars 39 | $options = array(); 40 | $options["exportFiles"] = true; 41 | $options["exportClean"] = Console::findCommandOption("clean"); 42 | $options["moveStatic"] = false; 43 | 44 | FileUtil::cleanExport(); 45 | 46 | $g = new Generator(); 47 | $g->generate($options); 48 | 49 | FileUtil::exportStatic(); 50 | 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/PatternLab/Console/Commands/GenerateCommand.php: -------------------------------------------------------------------------------- 1 | command = "generate"; 27 | 28 | Console::setCommand($this->command,"Generate Pattern Lab","The generate command generates an entire site a single time. By default it removes old content in public/, compiles the patterns and moves content from source/ into public/","g"); 29 | Console::setCommandOption($this->command,"patternsonly","Generate only the patterns. Does NOT clean public/.","To generate only the patterns:","p"); 30 | Console::setCommandOption($this->command,"nocache","Set the cacheBuster value to 0.","To turn off the cacheBuster:","n"); 31 | 32 | } 33 | 34 | public function run() { 35 | 36 | // set-up required vars 37 | $options = array(); 38 | $options["moveStatic"] = (Console::findCommandOption("p|patternsonly")) ? false : true; 39 | $options["noCacheBuster"] = Console::findCommandOption("n|nocache"); 40 | 41 | $g = new Generator(); 42 | $g->generate($options); 43 | 44 | $s = new Saying(); 45 | $s->say(); 46 | 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/PatternLab/Console/Commands/HelpCommand.php: -------------------------------------------------------------------------------- 1 | command = "help:"; 24 | 25 | Console::setCommand($this->command,"Print the help for a given command","The help command prints out the help for a given flag. Just use -h with another command and it will tell you all of the options.","h:"); 26 | Console::setCommandSample($this->command,"To get help for a particular command:",""); 27 | 28 | } 29 | 30 | public function run() { 31 | 32 | if ($helpCommand = Console::findCommandValue("h|help")) { 33 | $helpCommand = str_replace("-","",$helpCommand); 34 | if ($commandFound = Console::findCommandLong($helpCommand)) { 35 | Console::writeHelpCommand($commandFound); 36 | } else { 37 | Console::writeHelp(); 38 | } 39 | } else { 40 | Console::writeHelp(); 41 | } 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/PatternLab/Console/Commands/ServerCommand.php: -------------------------------------------------------------------------------- 1 | command = "server"; 28 | 29 | Console::setCommand($this->command,"Start the PHP-based server","The server command will start PHP's web server for you.","s"); 30 | Console::setCommandOption($this->command,"host:","Provide a custom hostname. Default value is localhost.","To use a custom hostname and the default port:","",""); 31 | Console::setCommandOption($this->command,"port:","Provide a custom port. Default value is 8080.","To use a custom port and the default hostname:","",""); 32 | Console::setCommandOption($this->command,"quiet","Turn on quiet mode for the server.","To turn on quiet mode:"); 33 | Console::setCommandOption($this->command,"with-watch","Start watching ./source when starting the server. Takes the same arguments as --watch.","To turn on with-watch mode:"); 34 | Console::setCommandSample($this->command,"To provide both a custom hostname and port:","--host --port "); 35 | 36 | } 37 | 38 | public function run() { 39 | 40 | if (version_compare(phpversion(), '5.4.0', '<')) { 41 | 42 | Console::writeWarning("you must have PHP 5.4.0 or greater to use this feature. you are using PHP ".phpversion()."..."); 43 | 44 | } else { 45 | 46 | // set-up defaults 47 | $publicDir = Config::getOption("publicDir"); 48 | $coreDir = Config::getOption("coreDir"); 49 | 50 | $host = Console::findCommandOptionValue("host"); 51 | $host = $host ? $host : "localhost"; 52 | 53 | $port = Console::findCommandOptionValue("port"); 54 | $host = $port ? $host.":".$port : $host.":8080"; 55 | 56 | $quiet = Console::findCommandOption("quiet"); 57 | 58 | // set-up the base command 59 | $command = $this->pathPHP." -S ".$host." ".$coreDir."/server/router.php"; 60 | $commands = array(); 61 | $commands[] = array("command" => $command, "cwd" => $publicDir, "timeout" => null, "idle" => 1800); 62 | 63 | // get the watch command info 64 | if (Console::findCommandOption("with-watch")) { 65 | $watchCommand = new WatchCommand; 66 | $commands[] = array("command" => $watchCommand->build()." --no-procs", "timeout" => null, "idle" => 1800); 67 | } 68 | 69 | Console::writeInfo("server started on http://".$host." - use ctrl+c to exit..."); 70 | 71 | $processSpawner = new ProcessSpawner; 72 | $processSpawner->spawn($commands, $quiet); 73 | 74 | } 75 | 76 | } 77 | 78 | public function build() { 79 | 80 | $command = $this->pathPHP." ".$this->pathConsole." --".$this->command; 81 | 82 | $host = Console::findCommandOptionValue("host"); 83 | $port = Console::findCommandOptionValue("port"); 84 | 85 | if ($host) { 86 | $command .= " --host ".$host; 87 | } 88 | if ($port) { 89 | $command .= " --port ".$port; 90 | } 91 | 92 | return $command; 93 | 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/PatternLab/Console/Commands/StarterKitCommand.php: -------------------------------------------------------------------------------- 1 | command = "starterkit"; 28 | 29 | Console::setCommand($this->command,"Initialize or fetch a specific StarterKit","The StarterKit command downloads StarterKits.","k"); 30 | Console::setCommandOption($this->command,"init","Initialize with a blank StarterKit based on the active PatternEngine.","To initialize your project with a base StarterKit:","i"); 31 | Console::setCommandOption($this->command,"install:","Fetch a specific StarterKit from GitHub.","To fetch a StarterKit from GitHub:","j:",""); 32 | Console::setCommandOption($this->command,"suggestions","Show suggested StarterKits for this Edition. Offer install prompt.","To show suggested StarterKits for this Edition:"); 33 | 34 | } 35 | 36 | public function run() { 37 | 38 | // find the value given to the command 39 | $init = Console::findCommandOption("i|init"); 40 | $starterkit = Console::findCommandOptionValue("f|install"); 41 | $suggestions = Console::findCommandOption("suggestions"); 42 | 43 | if ($suggestions) { 44 | 45 | $this->starterKitSuggestions(); 46 | 47 | } else if ($init || $starterkit) { 48 | 49 | $this->starterKitInstall($starterkit, $init); 50 | 51 | } else { 52 | 53 | Console::writeHelpCommand($this->command); 54 | 55 | } 56 | 57 | } 58 | 59 | protected function starterKitInstall($starterkit, $init) { 60 | 61 | // set-up the base starterkit 62 | if ($init) { 63 | $patternEngine = Config::getOption("patternExtension"); 64 | $starterkit = "pattern-lab/starterkit-".$patternEngine."-base"; 65 | } 66 | 67 | // download the starterkit 68 | $f = new Fetch(); 69 | $f->fetchStarterKit($starterkit); 70 | 71 | } 72 | 73 | protected function starterKitSuggestions() { 74 | 75 | Console::writeLine(""); 76 | 77 | $composerPath = Config::getOption("baseDir")."/composer.json"; 78 | if (file_exists($composerPath)) { 79 | 80 | $json = file_get_contents($composerPath); 81 | $data = json_decode($json,true); 82 | if ($jsonErrorMessage = JSON::hasError()) { 83 | JSON::lastErrorMsg(Console::getHumanReadablePath($oldStyleAnnotationsPath),$jsonErrorMessage,$data); 84 | } 85 | 86 | if (isset($data["extra"]) && isset($data["extra"]["patternlab"]) && isset($data["extra"]["patternlab"]["starterKitSuggestions"])) { 87 | 88 | $starterKitSuggestions = $data["extra"]["patternlab"]["starterKitSuggestions"]; 89 | 90 | Console::writeInfo("suggested starterkits that work with this edition:", false, true); 91 | foreach ($starterKitSuggestions as $i => $suggestion) { 92 | $num = $i + 1; 93 | Console::writeLine($num.": ".$suggestion, true); 94 | } 95 | 96 | // hack around installer util feature in Console::promptInput 97 | InstallerUtil::$isInteractive = true; 98 | 99 | // prompt for input on the suggestions 100 | Console::writeLine(""); 101 | 102 | $prompt = "choose an option or hit return to cancel:"; 103 | $options = "(ex. 1)"; 104 | $input = Console::promptInput($prompt,$options,"1"); 105 | $result = (int)$input - 1; 106 | 107 | if (isset($starterKitSuggestions[$result])) { 108 | 109 | Console::writeLine(""); 110 | $f = new Fetch(); 111 | $result = $f->fetchStarterKit($starterKitSuggestions[$result]); 112 | 113 | } 114 | 115 | } else { 116 | 117 | Console::writeWarning("this edition has no starterkits to suggested...", false, true); 118 | 119 | } 120 | 121 | } else { 122 | 123 | Console::writeError("can't find composer.json to get suggestions...", false, true); 124 | 125 | } 126 | 127 | 128 | 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/PatternLab/Console/Commands/VersionCommand.php: -------------------------------------------------------------------------------- 1 | command = "version"; 25 | 26 | Console::setCommand($this->command,"Print the version number","The version command prints out the current version of Pattern Lab.","v"); 27 | 28 | } 29 | 30 | public function run() { 31 | 32 | Console::writeInfo("you're running v".Config::getOption("v")." of the PHP version of Pattern Lab..."); 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/PatternLab/Console/Commands/WatchCommand.php: -------------------------------------------------------------------------------- 1 | command = "watch"; 28 | 29 | Console::setCommand($this->command,"Watch for changes and regenerate","The watch command builds Pattern Lab, watches for changes in source/ and regenerates Pattern Lab when there are any.","w"); 30 | Console::setCommandOption($this->command,"patternsonly","Watches only the patterns. Does NOT clean public/.","To watch and generate only the patterns:","p"); 31 | Console::setCommandOption($this->command,"nocache","Set the cacheBuster value to 0.","To watch and turn off the cache buster:","n"); 32 | Console::setCommandOption($this->command,"sk","Watch for changes to the StarterKit and copy to source/. The --sk flag should only be used if one is actively developing a StarterKit.","To watch for changes to the StarterKit:"); 33 | Console::setCommandOption($this->command,"no-procs","Disable plug-in related processes. For use with --server --with-watch.","To disable plug-in related processes:"); 34 | Console::setCommandSample($this->command,"To watch only patterns and turn off the cache buster:","--patternsonly --nocache"); 35 | 36 | } 37 | 38 | public function run() { 39 | 40 | // set-up required vars 41 | $options = array(); 42 | $options["moveStatic"] = (Console::findCommandOption("p|patternsonly")) ? false : true; 43 | $options["noCacheBuster"] = Console::findCommandOption("n|nocache"); 44 | 45 | // see if the starterKit flag was passed so you know what dir to watch 46 | if (Console::findCommandOption("sk")) { 47 | 48 | // load the starterkit watcher 49 | $w = new Watcher(); 50 | $w->watchStarterKit(); 51 | 52 | } else { 53 | 54 | if (Console::findCommandOption("no-procs")) { 55 | 56 | // don't have to worry about loading processes so launch watcher 57 | 58 | // load the generator 59 | $g = new Generator(); 60 | $g->generate($options); 61 | 62 | // load the watcher 63 | $w = new Watcher(); 64 | $w->watch($options); 65 | 66 | } else { 67 | 68 | // a vanilla --watch command needs to have a --no-procs version built 69 | // so we don't get caught in while() loops. re-request the console command 70 | $commands = array(); 71 | $commands[] = array("command" => $this->build()." --no-procs", "timeout" => null, "idle" => 600); 72 | 73 | Console::writeInfo("spawning the watch process..."); 74 | 75 | $process = new ProcessSpawner; 76 | $process->spawn($commands); 77 | 78 | } 79 | 80 | } 81 | 82 | } 83 | 84 | public function build() { 85 | 86 | $command = $this->pathPHP." ".$this->pathConsole." --".$this->command; 87 | 88 | if (Console::findCommandOption("p|patternsonly")) { 89 | $command .= " --patternsonly"; 90 | } 91 | if (Console::findCommandOption("n|nocache")) { 92 | $command .= " --nocache"; 93 | } 94 | 95 | return $command; 96 | 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/PatternLab/Console/Event.php: -------------------------------------------------------------------------------- 1 | options = $options; 23 | } 24 | 25 | public function getOptions() { 26 | return $this->options; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/PatternLab/Console/ProcessSpawner.php: -------------------------------------------------------------------------------- 1 | dispatch('processSpawner.getPluginProcesses',$event); 39 | $this->pluginProcesses = $event->getPluginProcesses(); 40 | 41 | } 42 | 43 | /** 44 | * Spawn the passed commands and those collected from plugins 45 | * @param {Array} a list of commands to spawn 46 | * @param {Boolean} if this should be run in quiet mode 47 | */ 48 | public function spawn($commands = array(), $quiet = false) { 49 | 50 | // set-up a default 51 | $processes = array(); 52 | 53 | // add the default processes sent to the spawner 54 | if (!empty($commands)) { 55 | foreach ($commands as $command) { 56 | $processes[] = $this->buildProcess($command); 57 | } 58 | } 59 | 60 | // add the processes sent to the spawner from plugins 61 | foreach ($this->pluginProcesses as $pluginProcess) { 62 | $processes[] = $this->buildProcess($pluginProcess); 63 | } 64 | 65 | // if there are processes to spawn do so 66 | if (!empty($processes)) { 67 | 68 | // start the processes 69 | foreach ($processes as $process) { 70 | $process["process"]->start(); 71 | } 72 | 73 | // check on them and produce output 74 | while (true) { 75 | foreach ($processes as $process) { 76 | try { 77 | if ($process["process"]->isRunning()) { 78 | $process["process"]->checkTimeout(); 79 | if (!$quiet && $process["output"]) { 80 | print $process["process"]->getIncrementalOutput(); 81 | $cmd = $process["process"]->getCommandLine(); 82 | if (strpos($cmd,"router.php") != (strlen($cmd) - 10)) { 83 | print $process["process"]->getIncrementalErrorOutput(); 84 | } 85 | } 86 | } 87 | } catch (ProcessTimedOutException $e) { 88 | if ($e->isGeneralTimeout()) { 89 | Console::writeError("pattern lab processes should never time out. yours did..."); 90 | } else if ($e->isIdleTimeout()) { 91 | Console::writeError("pattern lab processes automatically time out if their is no command line output in 30 minutes..."); 92 | } 93 | } 94 | } 95 | usleep(100000); 96 | } 97 | } 98 | 99 | } 100 | 101 | /** 102 | * Build the process from the given commandOptions 103 | * @param {Array} the options from which to build the process 104 | */ 105 | protected function buildProcess($commandOptions) { 106 | 107 | if (is_string($commandOptions)) { 108 | 109 | $process = new Process(escapeshellcmd((string) $commandOptions)); 110 | return array("process" => $process, "output" => true); 111 | 112 | } else if (is_array($commandOptions)) { 113 | 114 | $commandline = escapeshellcmd((string) $commandOptions["command"]); 115 | $cwd = isset($commandOptions["cwd"]) ? $commandOptions["cwd"] : null; 116 | $env = isset($commandOptions["env"]) ? $commandOptions["env"] : null; 117 | $input = isset($commandOptions["input"]) ? $commandOptions["input"] : null; 118 | $timeout = isset($commandOptions["timeout"]) ? $commandOptions["timeout"] : null; 119 | $options = isset($commandOptions["options"]) ? $commandOptions["options"] : array(); 120 | $idle = isset($commandOptions["idle"]) ? $commandOptions["idle"] : null; 121 | $output = isset($commandOptions["output"]) ? $commandOptions["output"] : true; 122 | 123 | $process = new Process($commandline, $cwd, $env, $input, $timeout, $options); 124 | 125 | // double-check idle 126 | if (!empty($idle)) { 127 | $process->setIdleTimeout($idle); 128 | } 129 | 130 | return array("process" => $process, "output" => $output); 131 | 132 | } 133 | 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/PatternLab/Console/ProcessSpawnerEvent.php: -------------------------------------------------------------------------------- 1 | pluginProcesses = array(); 23 | } 24 | 25 | public function addPluginProcesses($processes = array()) { 26 | if (!empty($processes)) { 27 | $this->pluginProcesses = array_merge($this->pluginProcesses, $processes); 28 | } 29 | } 30 | 31 | public function getPluginProcesses() { 32 | return $this->pluginProcesses; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/PatternLab/Data.php: -------------------------------------------------------------------------------- 1 | $v) { 62 | if (is_array($v)) { 63 | $array[$k] = self::recursiveWalk($v); 64 | } else { 65 | $array[$k] = self::compareReplaceListVars($v); 66 | } 67 | } 68 | return $array; 69 | } 70 | 71 | /** 72 | * Set-up the recursive walk so that the data can be properly saved 73 | */ 74 | public static function compareReplaceListVarsInit() { 75 | 76 | self::$store = self::recursiveWalk(self::$store); 77 | 78 | } 79 | 80 | /** 81 | * Gather data from any JSON and YAML files in source/_data 82 | * 83 | * Reserved attributes: 84 | * - Data::$store["listItems"] : listItems from listitems.json, duplicated into separate arrays for Data::$store["listItems"]["one"], Data::$store["listItems"]["two"]... etc. 85 | * - Data::$store["link"] : the links to each pattern 86 | * - Data::$store["cacheBuster"] : the cache buster value to be appended to URLs 87 | * - Data::$store["patternSpecific"] : holds attributes from the pattern-specific data files 88 | * 89 | * @return {Array} populates Data::$store 90 | */ 91 | public static function gather($options = array()) { 92 | 93 | // set-up the dispatcher 94 | $dispatcherInstance = Dispatcher::getInstance(); 95 | 96 | // dispatch that the data gather has started 97 | $dispatcherInstance->dispatch("data.gatherStart"); 98 | 99 | // default vars 100 | $found = false; 101 | $dataJSON = array(); 102 | $dataYAML = array(); 103 | $listItemsJSON = array(); 104 | $listItemsYAML = array(); 105 | $sourceDir = Config::getOption("sourceDir"); 106 | 107 | // iterate over all of the other files in the source directory 108 | if (!is_dir($sourceDir."/_data/")) { 109 | Console::writeWarning("_data/ doesn't exist so you won't have dynamic data..."); 110 | mkdir($sourceDir."/_data/"); 111 | } 112 | 113 | // find the markdown-based annotations 114 | $finder = new Finder(); 115 | $finder->files()->in($sourceDir."/_data/"); 116 | $finder->sortByName(); 117 | 118 | foreach ($finder as $name => $file) { 119 | 120 | $ext = $file->getExtension(); 121 | $data = array(); 122 | $fileName = $file->getFilename(); 123 | $hidden = ($fileName[0] == "_"); 124 | $isListItems = strpos($fileName,"listitems"); 125 | $pathName = $file->getPathname(); 126 | $pathNameClean = str_replace($sourceDir."/","",$pathName); 127 | 128 | if (!$hidden && (($ext == "json") || ($ext == "yaml") || ($ext == "yml"))) { 129 | 130 | if ($isListItems === false) { 131 | 132 | if ($ext == "json") { 133 | 134 | $file = file_get_contents($pathName); 135 | $data = json_decode($file,true); 136 | if ($jsonErrorMessage = JSON::hasError()) { 137 | JSON::lastErrorMsg($pathNameClean,$jsonErrorMessage,$data); 138 | } 139 | 140 | } else if (($ext == "yaml") || ($ext == "yml")) { 141 | 142 | $file = file_get_contents($pathName); 143 | 144 | try { 145 | $data = YAML::parse($file); 146 | } catch (ParseException $e) { 147 | printf("unable to parse ".$pathNameClean.": %s..\n", $e->getMessage()); 148 | } 149 | 150 | // single line of text won't throw a YAML error. returns as string 151 | if (gettype($data) == "string") { 152 | $data = array(); 153 | } 154 | 155 | } 156 | 157 | if (is_array($data)) { 158 | self::$store = array_replace_recursive(self::$store,$data); 159 | } 160 | 161 | } else if ($isListItems !== false) { 162 | 163 | $data = ($ext == "json") ? self::getListItems("_data/".$fileName) : self::getListItems("_data/".$fileName,"yaml"); 164 | 165 | if (!isset(self::$store["listItems"])) { 166 | self::$store["listItems"] = array(); 167 | } 168 | 169 | self::$store["listItems"] = array_replace_recursive(self::$store["listItems"],$data); 170 | 171 | } 172 | 173 | } 174 | 175 | } 176 | 177 | if (is_array(self::$store)) { 178 | foreach (self::$reservedKeys as $reservedKey) { 179 | if (array_key_exists($reservedKey,self::$store)) { 180 | Console::writeWarning("\"".$reservedKey."\" is a reserved key in Pattern Lab. the data using that key will be overwritten. please choose a new key..."); 181 | } 182 | } 183 | } 184 | 185 | self::$store["cacheBuster"] = Config::getOption("cacheBuster"); 186 | self::$store["link"] = array(); 187 | self::$store["patternSpecific"] = array(); 188 | 189 | $dispatcherInstance->dispatch("data.gatherEnd"); 190 | 191 | } 192 | 193 | /** 194 | * Grab a copy of the $store 195 | * 196 | * @return {Array} the store 197 | */ 198 | public static function get() { 199 | return self::$store; 200 | } 201 | 202 | 203 | 204 | /** 205 | * Generate the listItems array 206 | * @param {String} the filename for the pattern to be parsed 207 | * @param {String} the extension so that a flag switch can be used to parse data 208 | * 209 | * @return {Array} the final set of list items 210 | */ 211 | public static function getListItems($filepath,$ext = "json") { 212 | 213 | // set-up the dispatcher 214 | $dispatcherInstance = Dispatcher::getInstance(); 215 | 216 | // dispatch that the data gather has started 217 | $dispatcherInstance->dispatch("data.getListItemsStart"); 218 | 219 | // default vars 220 | $sourceDir = Config::getOption("sourceDir"); 221 | $listItems = array(); 222 | $listItemsData = array(); 223 | 224 | // add list item data, makes 'listItems' a reserved word 225 | if (file_exists($sourceDir."/".$filepath)) { 226 | 227 | $file = file_get_contents($sourceDir."/".$filepath); 228 | 229 | if ($ext == "json") { 230 | $listItemsData = json_decode($file, true); 231 | if ($jsonErrorMessage = JSON::hasError()) { 232 | JSON::lastErrorMsg($filepath,$jsonErrorMessage,$listItems); 233 | } 234 | } else { 235 | 236 | try { 237 | $listItemsData = YAML::parse($file); 238 | } catch (ParseException $e) { 239 | printf("unable to parse ".$pathNameClean.": %s..\n", $e->getMessage()); 240 | } 241 | 242 | // single line of text won't throw a YAML error. returns as string 243 | if (gettype($listItemsData) == "string") { 244 | $listItemsData = array(); 245 | } 246 | 247 | } 248 | 249 | 250 | $numbers = array("one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); 251 | 252 | $i = 0; 253 | $k = 1; 254 | $c = count($listItemsData)+1; 255 | 256 | while ($k < $c) { 257 | 258 | shuffle($listItemsData); 259 | $itemsArray = array(); 260 | 261 | while ($i < $k) { 262 | $itemsArray[] = $listItemsData[$i]; 263 | $i++; 264 | } 265 | 266 | $listItems[$numbers[$k-1]] = $itemsArray; 267 | 268 | $i = 0; 269 | $k++; 270 | 271 | } 272 | 273 | } 274 | 275 | $dispatcherInstance->dispatch("data.getListItemsEnd"); 276 | 277 | return $listItems; 278 | 279 | } 280 | 281 | /** 282 | * Get an option for the data store 283 | * @param {String} a string in dot notation dictating where the option is in the data structure 284 | * 285 | * @return {Array} the store 286 | */ 287 | public static function getOption($key = "") { 288 | if (!empty($key)) { 289 | $arrayFinder = new ArrayFinder(self::$store); 290 | return $arrayFinder->get($key); 291 | } 292 | return false; 293 | } 294 | 295 | /** 296 | * Get the final data array specifically for a pattern 297 | * @param {String} the filename for the pattern to be parsed 298 | * @param {Array} any extra data that should be added to the pattern specific data that's being returned 299 | * 300 | * @return {Array} the final set of list items 301 | */ 302 | public static function getPatternSpecificData($patternPartial,$extraData = array()) { 303 | 304 | // if there is pattern-specific data make sure to override the default in $this->d 305 | $d = self::get(); 306 | 307 | if (isset($d["patternSpecific"]) && array_key_exists($patternPartial,$d["patternSpecific"])) { 308 | 309 | if (!empty($d["patternSpecific"][$patternPartial]["data"])) { 310 | $d = array_replace_recursive($d, $d["patternSpecific"][$patternPartial]["data"]); 311 | } 312 | 313 | if (!empty($d["patternSpecific"][$patternPartial]["listItems"])) { 314 | 315 | $numbers = array("one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); 316 | 317 | $k = 0; 318 | $c = count($d["patternSpecific"][$patternPartial]["listItems"]); 319 | 320 | while ($k < $c) { 321 | $section = $numbers[$k]; 322 | $d["listItems"][$section] = array_replace_recursive( $d["listItems"][$section], $d["patternSpecific"][$patternPartial]["listItems"][$section]); 323 | $k++; 324 | } 325 | 326 | } 327 | 328 | } 329 | 330 | if (!empty($extraData)) { 331 | $d = array_replace_recursive($d, $extraData); 332 | } 333 | 334 | unset($d["patternSpecific"]); 335 | 336 | return $d; 337 | 338 | } 339 | 340 | /** 341 | * Initialize a pattern specific data store under the patternSpecific option 342 | * @param {String} the pattern to create an array for 343 | */ 344 | public static function initPattern($optionName) { 345 | 346 | if (!isset(self::$store["patternSpecific"])) { 347 | self::$store["patternSpecific"] = array(); 348 | } 349 | 350 | if ((!isset(self::$store["patternSpecific"][$optionName])) || (!is_array(self::$store["patternSpecific"][$optionName]))) { 351 | self::$store["patternSpecific"][$optionName] = array(); 352 | } 353 | 354 | } 355 | 356 | /** 357 | * Print out the data var. For debugging purposes 358 | * 359 | * @return {String} the formatted version of the d object 360 | */ 361 | public static function printData() { 362 | print_r(self::$store); 363 | } 364 | 365 | /** 366 | * Replace the data store 367 | * @param {Array} the new store 368 | */ 369 | public static function replaceStore($store) { 370 | self::$store = $store; 371 | } 372 | 373 | /** 374 | * Set an option for the data store 375 | * @param {String} a string in dot notation dictating where the option is in the data structure 376 | * @param {Mixed} the value for the key 377 | * 378 | * @return {Array} the store 379 | */ 380 | public static function setOption($key = "", $value = "") { 381 | 382 | if (empty($key)) { 383 | return false; 384 | } 385 | 386 | $arrayFinder = new ArrayFinder(self::$store); 387 | $arrayFinder->set($key, $value); 388 | self::$store = $arrayFinder->get(); 389 | 390 | } 391 | 392 | /** 393 | * Set an option on a sub element of the data array 394 | * @param {String} name of the option 395 | * @param {String} value for the option 396 | */ 397 | public static function setOptionLink($optionName,$optionValue) { 398 | 399 | if (!isset(self::$store["link"])) { 400 | self::$store["link"] = array(); 401 | } 402 | 403 | self::$store["link"][$optionName] = $optionValue; 404 | 405 | } 406 | 407 | /** 408 | * Set the pattern data option 409 | * @param {String} name of the pattern 410 | * @param {String} value for the pattern's data attribute 411 | */ 412 | public static function setPatternData($optionName,$optionValue) { 413 | 414 | if (isset(self::$store["patternSpecific"][$optionName])) { 415 | self::$store["patternSpecific"][$optionName]["data"] = $optionValue; 416 | return true; 417 | } 418 | 419 | return false; 420 | 421 | } 422 | 423 | /** 424 | * Set the pattern listitems option 425 | * @param {String} name of the pattern 426 | * @param {String} value for the pattern's listItem attribute 427 | */ 428 | public static function setPatternListItems($optionName,$optionValue) { 429 | 430 | if (isset(self::$store["patternSpecific"][$optionName])) { 431 | self::$store["patternSpecific"][$optionName]["listItems"] = $optionValue; 432 | return true; 433 | } 434 | 435 | return false; 436 | 437 | } 438 | 439 | } 440 | -------------------------------------------------------------------------------- /src/PatternLab/Dispatcher.php: -------------------------------------------------------------------------------- 1 | getListeners(); 63 | foreach ($listeners as $event => $eventProps) { 64 | $eventPriority = (isset($eventProps["priority"])) ? $eventProps["priority"] : 0; 65 | self::$instance->addListener($event, array($listener, $eventProps["callable"]), $eventPriority); 66 | } 67 | } 68 | 69 | } 70 | 71 | } 72 | 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/PatternLab/Fetch.php: -------------------------------------------------------------------------------- 1 | getPackageInfo($starterkit); 42 | 43 | // set default attributes 44 | $sourceDir = Config::getOption("sourceDir"); 45 | $tempDir = sys_get_temp_dir().DIRECTORY_SEPARATOR."pl-sk"; 46 | $tempDirSK = $tempDir.DIRECTORY_SEPARATOR."pl-sk-archive"; 47 | $tempDirDist = $tempDirSK.DIRECTORY_SEPARATOR.$repo."-".$tag.DIRECTORY_SEPARATOR."dist"; 48 | $tempComposerFile = $tempDirSK.DIRECTORY_SEPARATOR.$repo."-".$tag.DIRECTORY_SEPARATOR."composer.json"; 49 | 50 | //get the path to the GH repo and validate it 51 | $tarballUrl = "https://github.com/".$org."/".$repo."/archive/".$tag.".tar.gz"; 52 | 53 | Console::writeInfo("downloading the starterkit. this may take a few minutes..."); 54 | 55 | // try to download the given package 56 | if (!$package = @file_get_contents($tarballUrl)) { 57 | $error = error_get_last(); 58 | Console::writeError("the starterkit wasn't downloaded because:\n\n ".$error["message"]); 59 | } 60 | 61 | // Create temp directory if doesn't exist 62 | $fs = new Filesystem(); 63 | try { 64 | $fs->mkdir($tempDir, 0775); 65 | } catch (IOExceptionInterface $e) { 66 | Console::writeError("Error creating temporary directory at " . $e->getPath()); 67 | } 68 | 69 | // write the package to the temp directory 70 | $tempFile = tempnam($tempDir, "pl-sk-archive.tar.gz"); 71 | file_put_contents($tempFile, $package); 72 | 73 | Console::writeInfo("finished downloading the starterkit..."); 74 | 75 | // make sure the temp dir exists before copying into it 76 | if (!is_dir($tempDirSK)) { 77 | mkdir($tempDirSK); 78 | } 79 | 80 | // extract, if the zip is supposed to be unpacked do that (e.g. stripdir) 81 | try { 82 | $zippy = Zippy::load(); 83 | $zippy->addStrategy(new UnpackFileStrategy()); 84 | $zippy->getAdapterFor('tar.gz')->open($tempFile)->extract($tempDirSK); 85 | } catch(\RuntimeException $e) { 86 | Console::writeError("failed to extract the starterkit. easiest solution is to manually download it and copy ./dist./source/..."); 87 | } 88 | 89 | if (!is_dir($tempDirDist)) { 90 | // try without repo dir 91 | $tempDirDist = $tempDirSK.DIRECTORY_SEPARATOR."dist"; 92 | } 93 | // thrown an error if temp/dist/ doesn't exist 94 | if (!is_dir($tempDirDist)) { 95 | Console::writeError("the starterkit needs to contain a dist/ directory before it can be installed..."); 96 | } 97 | 98 | // check for composer.json. if it exists use it for determining things. otherwise just mirror dist/ to source/ 99 | if (file_exists($tempComposerFile)) { 100 | 101 | $tempComposerJSON = json_decode(file_get_contents($tempComposerFile), true); 102 | 103 | // see if it has a patternlab section that might define the files to move 104 | if (isset($tempComposerJSON["extra"]) && isset($tempComposerJSON["extra"]["patternlab"])) { 105 | Console::writeInfo("installing the starterkit..."); 106 | InstallerUtil::parseComposerExtraList($tempComposerJSON["extra"]["patternlab"], $starterkit, $tempDirDist); 107 | Console::writeInfo("installed the starterkit..."); 108 | } else { 109 | $this->mirrorDist($sourceDir, $tempDirDist); 110 | } 111 | 112 | } else { 113 | 114 | $this->mirrorDist($sourceDir, $tempDirDist); 115 | 116 | } 117 | 118 | // remove the temp files 119 | Console::writeInfo("cleaning up the temp files..."); 120 | $fs = new Filesystem(); 121 | $fs->remove($tempFile); 122 | $fs->remove($tempDirSK); 123 | 124 | Console::writeInfo("the starterkit installation is complete..."); 125 | 126 | return true; 127 | 128 | } 129 | 130 | /** 131 | * Break up the package path 132 | * @param {String} path of the GitHub repo 133 | * 134 | * @return {Array} the parts of the package path 135 | */ 136 | protected function getPackageInfo($package) { 137 | 138 | $org = ""; 139 | $repo = ""; 140 | $tag = "master"; 141 | 142 | if (strpos($package, "#") !== false) { 143 | list($package,$tag) = explode("#",$package); 144 | } 145 | 146 | if (strpos($package, "/") !== false) { 147 | list($org,$repo) = explode("/",$package); 148 | } else { 149 | Console::writeError("please provide a real path to a package..."); 150 | } 151 | 152 | return array($org,$repo,$tag); 153 | 154 | } 155 | 156 | /** 157 | * Force mirror the dist/ folder to source/ 158 | * @param {String} path to the source directory 159 | * @param {String} path to the temp dist directory 160 | */ 161 | protected function mirrorDist($sourceDir, $tempDirDist) { 162 | 163 | // set default vars 164 | $fsOptions = array(); 165 | $emptyDir = true; 166 | $validFiles = array("README",".gitkeep",".DS_Store","styleguide","patternlab-components"); 167 | 168 | // see if the source directory is empty 169 | if (is_dir($sourceDir)) { 170 | $objects = new \DirectoryIterator($sourceDir); 171 | foreach ($objects as $object) { 172 | if (!$object->isDot() && !in_array($object->getFilename(),$validFiles)) { 173 | $emptyDir = false; 174 | break; 175 | } 176 | } 177 | } 178 | 179 | // if source directory isn't empty ask if it's ok to nuke what's there 180 | if (!$emptyDir) { 181 | 182 | $prompt = "a starterkit is already installed. merge the new files with it or replace it?"; 183 | $options = "M/r"; 184 | $input = Console::promptInput($prompt,$options,"M"); 185 | $fsOptions = ($input == "r") ? array("delete" => true, "override" => true) : array("delete" => false, "override" => false); 186 | 187 | } 188 | 189 | // mirror dist to source 190 | Console::writeInfo("installing the starterkit files..."); 191 | $fs = new Filesystem(); 192 | $fs->mirror($tempDirDist, $sourceDir, null, $fsOptions); 193 | Console::writeInfo("starterkit files have been installed..."); 194 | 195 | } 196 | 197 | } 198 | -------------------------------------------------------------------------------- /src/PatternLab/FileUtil.php: -------------------------------------------------------------------------------- 1 | ".Console::getHumanReadablePath($configPath)." by adding '".$configOption."=some/path'. sorry, stopping pattern lab... :("); 34 | } else if (!is_dir($path)) { 35 | Console::writeWarning("i can't seem to find the directory ".Console::getHumanReadablePath($path)."..."); 36 | self::makeDir($path); 37 | Console::writeWarning("i created ".Console::getHumanReadablePath($path)." just in case. you can edit this in ".Console::getHumanReadablePath($configPath)." by editing ".$configOption."..."); 38 | } 39 | 40 | } 41 | 42 | /** 43 | * Make a directory 44 | * @param {String} directory to be made 45 | */ 46 | public static function makeDir($dir) { 47 | $fs = new Filesystem(); 48 | try { 49 | $fs->mkdir($dir); 50 | } catch (IOExceptionInterface $e) { 51 | Console::writeError("an error occurred while creating your directory at ".$e->getPath()."..."); 52 | } 53 | unset($fs); 54 | } 55 | 56 | /** 57 | * Copies a file from the given source path to the given public path. 58 | * THIS IS NOT FOR PATTERNS 59 | * @param {String} the source file 60 | * @param {String} the public file 61 | */ 62 | protected static function moveFile($s,$p) { 63 | 64 | // default vars 65 | $sourceDir = Config::getOption("sourceDir"); 66 | $publicDir = Config::getOption("publicDir"); 67 | 68 | $fs = new Filesystem(); 69 | $fs->copy($sourceDir.DIRECTORY_SEPARATOR.$s,$publicDir.DIRECTORY_SEPARATOR.$p); 70 | 71 | } 72 | 73 | /** 74 | * Moves static files that aren't directly related to Pattern Lab 75 | * @param {String} file name to be moved 76 | * @param {String} copy for the message to be printed out 77 | * @param {String} part of the file name to be found for replacement 78 | * @param {String} the replacement 79 | */ 80 | public static function moveStaticFile($fileName,$copy = "", $find = "", $replace = "") { 81 | self::moveFile($fileName,str_replace($find, $replace, $fileName)); 82 | Util::updateChangeTime(); 83 | if ($copy != "") { 84 | Console::writeInfo($fileName." ".$copy."..."); 85 | } 86 | } 87 | 88 | /** 89 | * Check to see if a given filename is in a directory that should be ignored 90 | * @param {String} file name to be checked 91 | * 92 | * @return {Boolean} whether the directory should be ignored 93 | */ 94 | public static function ignoreDir($fileName) { 95 | $id = Config::getOption("id"); 96 | foreach ($id as $dir) { 97 | $pos = strpos(DIRECTORY_SEPARATOR.$fileName,DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR); 98 | if ($pos !== false) { 99 | return true; 100 | } 101 | } 102 | return false; 103 | } 104 | 105 | /** 106 | * Taken from Composer: https://github.com/composer/composer/blob/master/src/Composer/Util/Filesystem.php 107 | * 108 | * Normalize a path. This replaces backslashes with slashes, removes ending 109 | * slash and collapses redundant separators and up-level references. 110 | * 111 | * @param string $path Path to the file or directory 112 | * @return string 113 | */ 114 | public static function normalizePath($path) { 115 | $parts = array(); 116 | $path = strtr($path, '\\', '/'); 117 | $prefix = ''; 118 | $absolute = false; 119 | 120 | if (preg_match('{^([0-9a-z]+:(?://(?:[a-z]:)?)?)}i', $path, $match)) { 121 | $prefix = $match[1]; 122 | $path = substr($path, strlen($prefix)); 123 | } 124 | 125 | if (substr($path, 0, 1) === '/') { 126 | $absolute = true; 127 | $path = substr($path, 1); 128 | } 129 | 130 | $up = false; 131 | foreach (explode('/', $path) as $chunk) { 132 | if ('..' === $chunk && ($absolute || $up)) { 133 | array_pop($parts); 134 | $up = !(empty($parts) || '..' === end($parts)); 135 | } elseif ('.' !== $chunk && '' !== $chunk) { 136 | $parts[] = $chunk; 137 | $up = '..' !== $chunk; 138 | } 139 | } 140 | 141 | return $prefix.($absolute ? '/' : '').implode('/', $parts); 142 | 143 | } 144 | 145 | /** 146 | * Delete everything in export/ 147 | */ 148 | public static function cleanExport() { 149 | 150 | // default var 151 | $exportDir = Config::getOption("exportDir"); 152 | 153 | if (is_dir($exportDir)) { 154 | 155 | $files = scandir($exportDir); 156 | foreach ($files as $file) { 157 | if (($file == "..") || ($file == ".")) { 158 | array_shift($files); 159 | } else { 160 | $key = array_keys($files,$file); 161 | $files[$key[0]] = $exportDir.DIRECTORY_SEPARATOR.$file; 162 | } 163 | } 164 | 165 | $fs = new Filesystem(); 166 | $fs->remove($files); 167 | 168 | } 169 | 170 | } 171 | 172 | /** 173 | * Delete patterns and user-created directories and files in public/ 174 | */ 175 | public static function cleanPublic() { 176 | 177 | // set-up the dispatcher 178 | $dispatcherInstance = Dispatcher::getInstance(); 179 | 180 | // dispatch that the data gather has started 181 | $dispatcherInstance->dispatch("fileUtil.cleanPublicStart"); 182 | 183 | // default var 184 | $patternPublicDir = Config::getOption("patternPublicDir"); 185 | 186 | // make sure patterns exists before trying to clean it 187 | if (is_dir($patternPublicDir)) { 188 | 189 | // symfony finder doesn't support child first and I don't want to do array crap 190 | $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($patternPublicDir), \RecursiveIteratorIterator::CHILD_FIRST); 191 | 192 | // make sure dots are skipped 193 | $objects->setFlags(\FilesystemIterator::SKIP_DOTS); 194 | 195 | // for each file figure out what to do with it 196 | foreach ($objects as $name => $object) { 197 | 198 | if ($object->isDir()) { 199 | // if this is a directory remove it 200 | rmdir($name); 201 | } else if ($object->isFile() && ($object->getFilename() != "README")) { 202 | // if this is a file remove it 203 | unlink($name); 204 | } 205 | 206 | } 207 | 208 | } 209 | 210 | // scan source/ & public/ to figure out what directories might need to be cleaned up 211 | $publicDir = Config::getOption("publicDir"); 212 | $sourceDir = Config::getOption("sourceDir"); 213 | $publicDirs = glob($publicDir.DIRECTORY_SEPARATOR."*",GLOB_ONLYDIR); 214 | $sourceDirs = glob($sourceDir.DIRECTORY_SEPARATOR."*",GLOB_ONLYDIR); 215 | 216 | // make sure some directories aren't deleted 217 | $ignoreDirs = array("styleguide","patternlab-components"); 218 | foreach ($ignoreDirs as $ignoreDir) { 219 | $key = array_search($publicDir.DIRECTORY_SEPARATOR.$ignoreDir,$publicDirs); 220 | if ($key !== false){ 221 | unset($publicDirs[$key]); 222 | } 223 | } 224 | 225 | // compare source dirs against public. remove those dirs w/ an underscore in source/ from the public/ list 226 | foreach ($sourceDirs as $dir) { 227 | $cleanDir = str_replace($sourceDir.DIRECTORY_SEPARATOR,"",$dir); 228 | if ($cleanDir[0] == "_") { 229 | $key = array_search($publicDir.DIRECTORY_SEPARATOR.str_replace("_","",$cleanDir),$publicDirs); 230 | if ($key !== false){ 231 | unset($publicDirs[$key]); 232 | } 233 | } 234 | } 235 | 236 | // for the remaining dirs in public delete them and their files 237 | foreach ($publicDirs as $dir) { 238 | 239 | // symfony finder doesn't support child first and I don't want to do array crap 240 | $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir), \RecursiveIteratorIterator::CHILD_FIRST); 241 | 242 | // make sure dots are skipped 243 | $objects->setFlags(\FilesystemIterator::SKIP_DOTS); 244 | 245 | foreach($objects as $name => $object) { 246 | 247 | if ($object->isDir()) { 248 | rmdir($name); 249 | } else if ($object->isFile()) { 250 | unlink($name); 251 | } 252 | 253 | } 254 | 255 | rmdir($dir); 256 | 257 | } 258 | 259 | $dispatcherInstance->dispatch("fileUtil.cleanPublicEnd"); 260 | 261 | } 262 | 263 | /** 264 | * moves user-generated static files from public/ to export/ 265 | */ 266 | public static function exportStatic() { 267 | 268 | // default vars 269 | $exportDir = Config::getOption("exportDir"); 270 | $publicDir = Config::getOption("publicDir"); 271 | $sourceDir = Config::getOption("sourceDir"); 272 | 273 | if (!is_dir($exportDir)) { 274 | mkdir($exportDir); 275 | } 276 | 277 | if (is_dir($publicDir)) { 278 | 279 | // decide which files in public should def. be ignored 280 | $ignore = array("annotations","data","patterns","styleguide","index.html","latest-change.txt",".DS_Store",".svn","README"); 281 | 282 | $files = scandir($publicDir); 283 | foreach ($files as $key => $file) { 284 | if (($file == "..") || ($file == ".")) { 285 | unset($files[$key]); 286 | } else if (in_array($file,$ignore)) { 287 | unset($files[$key]); 288 | } else if (is_dir($publicDir.DIRECTORY_SEPARATOR.$file) && !is_dir($sourceDir.DIRECTORY_SEPARATOR.$file)) { 289 | unset($files[$key]); 290 | } else if (is_file($publicDir.DIRECTORY_SEPARATOR.$file) && !is_file($sourceDir.DIRECTORY_SEPARATOR.$file)) { 291 | unset($files[$key]); 292 | } 293 | } 294 | 295 | $fs = new Filesystem(); 296 | foreach ($files as $file) { 297 | if (is_dir($publicDir.DIRECTORY_SEPARATOR.$file)) { 298 | $fs->mirror($publicDir.DIRECTORY_SEPARATOR.$file,$exportDir.DIRECTORY_SEPARATOR.$file); 299 | } else if (is_file($publicDir.DIRECTORY_SEPARATOR.$file)) { 300 | $fs->copy($publicDir.DIRECTORY_SEPARATOR.$file,$exportDir.DIRECTORY_SEPARATOR.$file); 301 | } 302 | } 303 | 304 | } 305 | 306 | } 307 | 308 | } 309 | -------------------------------------------------------------------------------- /src/PatternLab/Generator.php: -------------------------------------------------------------------------------- 1 | generateIndex(); 81 | $this->generateStyleguide(); 82 | $this->generateViewAllPages(); 83 | 84 | // render out the patterns and move them to public/patterns 85 | $options = array(); 86 | $options["exportFiles"] = $exportFiles; 87 | $this->generatePatterns($options); 88 | 89 | // render the annotations as a js file 90 | $this->generateAnnotations(); 91 | 92 | // move all of the files unless pattern only is set 93 | if ($moveStatic) { 94 | $this->moveStatic(); 95 | } 96 | 97 | // update the change time so the auto-reload will fire (doesn't work for the index and style guide) 98 | Util::updateChangeTime(); 99 | 100 | if ($watchVerbose && $watchMessage) { 101 | Console::writeLine($watchMessage); 102 | } else { 103 | Console::writeLine("your site has been generated..."); 104 | Timer::stop(); 105 | } 106 | 107 | } 108 | 109 | /** 110 | * Move static files from source/ to public/ 111 | */ 112 | protected function moveStatic() { 113 | 114 | // set-up the dispatcher 115 | $dispatcherInstance = Dispatcher::getInstance(); 116 | 117 | // note the start of the operation 118 | $dispatcherInstance->dispatch("generator.moveStaticStart"); 119 | 120 | // common values 121 | $publicDir = Config::getOption("publicDir"); 122 | $sourceDir = Config::getOption("sourceDir"); 123 | $ignoreExt = Config::getOption("ie"); 124 | $ignoreDir = Config::getOption("id"); 125 | 126 | // iterate over all of the other files in the source directory 127 | $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($sourceDir), \RecursiveIteratorIterator::SELF_FIRST); 128 | 129 | // make sure dots are skipped 130 | $objects->setFlags(\FilesystemIterator::SKIP_DOTS); 131 | 132 | foreach($objects as $name => $object) { 133 | 134 | // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored 135 | $fileName = str_replace($sourceDir.DIRECTORY_SEPARATOR,"",$name); 136 | 137 | if (($fileName[0] != "_") && (!in_array($object->getExtension(),$ignoreExt)) && (!in_array($object->getFilename(),$ignoreDir))) { 138 | 139 | // catch directories that have the ignored dir in their path 140 | $ignored = FileUtil::ignoreDir($fileName); 141 | 142 | // check to see if it's a new directory 143 | if (!$ignored && $object->isDir() && !is_dir($publicDir."/".$fileName)) { 144 | mkdir($publicDir."/".$fileName); 145 | } 146 | 147 | // check to see if it's a new file or a file that has changed 148 | if (!$ignored && $object->isFile() && (!file_exists($publicDir."/".$fileName))) { 149 | FileUtil::moveStaticFile($fileName); 150 | } 151 | 152 | } 153 | 154 | } 155 | 156 | // note the end of the operation 157 | $dispatcherInstance->dispatch("generator.moveStaticEnd"); 158 | 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /src/PatternLab/JSON.php: -------------------------------------------------------------------------------- 1 | false, 23 | JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', 24 | JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch', 25 | JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', 26 | JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', 27 | JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded' 28 | ); 29 | 30 | /** 31 | * Returns the last error message when building a JSON file. Mimics json_last_error_msg() from PHP 5.5 32 | * @param {String} the file that generated the error 33 | */ 34 | public static function hasError() { 35 | $error = json_last_error(); 36 | $errorMessage = array_key_exists($error, self::$errors) ? self::$errors[$error] : "Unknown error ({$error})"; 37 | return $errorMessage; 38 | } 39 | 40 | /** 41 | * Returns the last error message when building a JSON file. Mimics json_last_error_msg() from PHP 5.5 42 | * @param {String} the file that generated the error 43 | */ 44 | public static function lastErrorMsg($file,$message,$data) { 45 | Console::writeLine(PHP_EOL."The JSON file, ".$file.", wasn't loaded. The error: ".$message.""); 46 | if ($message == "Syntax error, malformed JSON") { 47 | Console::writeLine(""); 48 | $parser = new JsonLint\JsonParser(); 49 | $error = $parser->lint($data); 50 | Console::writeError($error->getMessage(), false, true); 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/PatternLab/Listener.php: -------------------------------------------------------------------------------- 1 | listeners[$eventName] = array("callable" => $callable, "priority" => $priority); 31 | 32 | } 33 | 34 | /** 35 | * Get the list of listeners 36 | */ 37 | public function getListeners() { 38 | return $this->listeners; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/PatternLab/Migrator.php: -------------------------------------------------------------------------------- 1 | getFilename(); 46 | if (!$migration->isDot() && $migration->isFile() && ($filename[0] != "_")) { 47 | $migrationsValid[] = $filename; 48 | } 49 | } 50 | 51 | asort($migrationsValid); 52 | 53 | foreach ($migrationsValid as $filename) { 54 | 55 | $basePath = __DIR__."/../../../"; 56 | $migrationData = json_decode(file_get_contents(__DIR__."/../../migrations/".$filename)); 57 | $checkType = $migrationData->checkType; 58 | $sourcePath = ($checkType == "fileExists") ? $basePath.$migrationData->sourcePath : $basePath.$migrationData->sourcePath.DIRECTORY_SEPARATOR; 59 | $destinationPath = ($checkType == "fileExists") ? $basePath.$migrationData->destinationPath : $basePath.$migrationData->destinationPath.DIRECTORY_SEPARATOR; 60 | 61 | if ($checkType == "dirEmpty") { 62 | 63 | $emptyDir = true; 64 | $objects = new \DirectoryIterator($destinationPath); 65 | foreach ($objects as $object) { 66 | if (!$object->isDot() && ($object->getFilename() != "README") && ($object->getFilename() != ".DS_Store")) { 67 | $emptyDir = false; 68 | } 69 | } 70 | 71 | if ($emptyDir) { 72 | $this->runMigration($filename, $sourcePath, $destinationPath, false); 73 | } 74 | 75 | } else if ($checkType == "dirExists") { 76 | 77 | if (!is_dir($destinationPath)) { 78 | mkdir($destinationPath); 79 | } 80 | 81 | } else if ($checkType == "fileExists") { 82 | 83 | if (!file_exists($destinationPath)) { 84 | $this->runMigration($filename, $sourcePath, $destinationPath, true); 85 | } 86 | 87 | } else if (($checkType == "versionDiffDir") && $diffVersion) { 88 | 89 | // make sure the destination path exists 90 | if (!is_dir($destinationPath)) { 91 | mkdir($destinationPath); 92 | } 93 | 94 | $this->runMigration($filename, $sourcePath, $destinationPath, false); 95 | 96 | } else if (($checkType == "versionDiffFile") && $diffVersion) { 97 | 98 | $this->runMigration($filename, $sourcePath, $destinationPath, true); 99 | 100 | } else { 101 | 102 | print "pattern Lab doesn't recognize a checkType of ".$checkType.". The migrator class is pretty thin at the moment.\n"; 103 | exit; 104 | 105 | } 106 | 107 | } 108 | 109 | } 110 | 111 | /** 112 | * Run any migrations found in core/migrations that match the approved types 113 | * @param {String} the filename of the migration 114 | * @param {String} the path of the source directory 115 | * @param {String} the path to the destination 116 | * @param {Boolean} moving a single file or a directory 117 | */ 118 | protected function runMigration($filename, $sourcePath, $destinationPath, $singleFile) { 119 | 120 | $filename = str_replace(".json","",$filename); 121 | print "starting the ".$filename." migration...\n"; 122 | 123 | if ($singleFile) { 124 | 125 | copy($sourcePath.$fileName,$destinationPath.$fileName); 126 | 127 | } else { 128 | 129 | if (strpos($sourcePath,"pattern-lab/") !== false) { 130 | 131 | $sourcePath = str_replace(__DIR__."/../../../","",rtrim($sourcePath,"/")); 132 | $f = new Fetch(); 133 | $f->fetch("starterkit",$sourcePath); 134 | 135 | } else { 136 | 137 | $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($sourcePath), \RecursiveIteratorIterator::SELF_FIRST); 138 | $objects->setFlags(\FilesystemIterator::SKIP_DOTS); 139 | 140 | foreach ($objects as $object) { 141 | 142 | // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored 143 | $fileName = str_replace($sourcePath,"",$object->getPathname()); 144 | 145 | // check to see if it's a new directory 146 | if ($object->isDir() && !is_dir($destinationPath.$fileName)) { 147 | mkdir($destinationPath.$fileName); 148 | } else if ($object->isFile()) { 149 | copy($sourcePath.$fileName,$destinationPath.$fileName); 150 | } 151 | 152 | } 153 | } 154 | 155 | } 156 | 157 | print "completed the ".$filename." migration...\n"; 158 | 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /src/PatternLab/Parsers/Documentation.php: -------------------------------------------------------------------------------- 1 | getMessage()); 47 | } 48 | 49 | // single line of text won't throw a YAML error. returns as string 50 | if (gettype($yaml) == "string") { 51 | $yaml = array(); 52 | } 53 | 54 | return $yaml; 55 | 56 | } 57 | 58 | /** 59 | * Return only the relevant YAML from the given text 60 | * @param {String} the text to be parsed 61 | * 62 | * @return {Array} the parsed content 63 | */ 64 | public static function getYAML($text) { 65 | list($yaml,$markdown) = self::parse($text); 66 | return $yaml; 67 | } 68 | 69 | /** 70 | * Return only the relevant converted markdown from the given text 71 | * @param {String} the text to be parsed 72 | * 73 | * @return {Array} the parsed content 74 | */ 75 | public static function getMarkdown($text) { 76 | list($yaml,$markdown) = self::parse($text); 77 | return $markdown; 78 | } 79 | 80 | /** 81 | * Find and convert YAML and markdown in Pattern Lab documention files 82 | * @param {String} the text to be chunked for YAML and markdown 83 | * 84 | * @return {Array} array containing both the YAML and converted markdown 85 | */ 86 | public static function parse($text) { 87 | 88 | self::setLineEndings(); 89 | 90 | // set-up defaults 91 | $yaml = array(); 92 | $markdown = ""; 93 | 94 | // read in the content 95 | // based on: https://github.com/mnapoli/FrontYAML/blob/master/src/Parser.php 96 | $lines = explode(PHP_EOL, $text); 97 | 98 | if (count($lines) <= 1) { 99 | $markdown = self::convertMarkdown($text); 100 | return array($yaml,$markdown); 101 | } 102 | 103 | if (rtrim($lines[0]) !== '---') { 104 | $markdown = self::convertMarkdown($text); 105 | return array($yaml,$markdown); 106 | } 107 | 108 | $head = array(); 109 | unset($lines[0]); 110 | $i = 1; 111 | foreach ($lines as $line) { 112 | if ($line === '---') { 113 | break; 114 | } 115 | $head[] = $line; 116 | $i++; 117 | } 118 | 119 | $head = implode(PHP_EOL, $head); 120 | $body = implode(PHP_EOL, array_slice($lines, $i)); 121 | 122 | $yaml = self::convertYAML($head); 123 | $markdown = self::convertMarkdown($body); 124 | 125 | return array($yaml,$markdown); 126 | 127 | } 128 | 129 | /** 130 | * Set the proper line endings so the text can be parsed properly 131 | */ 132 | protected static function setLineEndings() { 133 | if (!self::$lineEndingsSet) { 134 | ini_set("auto_detect_line_endings", true); 135 | self::$lineEndingsSet = true; 136 | } 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Event.php: -------------------------------------------------------------------------------- 1 | options = $options; 24 | } 25 | 26 | public function getOptions() { 27 | return $this->options; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Exporter.php: -------------------------------------------------------------------------------- 1 | store = PatternData::get(); 28 | 29 | } 30 | 31 | public function run() { 32 | 33 | foreach ($this->store as $patternStoreKey => $patternStoreData) { 34 | 35 | switch ($patternStoreData["category"]) { 36 | // atoms - view all 37 | case "patternType": 38 | if (isset($patternStoreData["pathDash"])) { 39 | $value = "../../patterns/" . $patternStoreData["pathDash"] . "/index.html"; 40 | Data::setOptionLink("viewall-" . $patternStoreData["nameDash"] . "-all", $value); 41 | } 42 | break; 43 | 44 | // atoms/forms - view all 45 | case "patternSubtype": 46 | if (isset($patternStoreData["pathDash"])) { 47 | $value = "../../patterns/" . $patternStoreData["pathDash"] . "/index.html"; 48 | Data::setOptionLink($patternStoreData["partial"], $value); 49 | } 50 | break; 51 | 52 | // atoms/forms/select.mustache 53 | case "pattern": 54 | if (isset($patternStoreData["pathDash"])) { 55 | $value = "../../patterns/" . $patternStoreData["pathDash"] . "/" . $patternStoreData["pathDash"] . ".html"; 56 | Data::setOptionLink($patternStoreKey, $value); 57 | } 58 | break; 59 | } 60 | 61 | } 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Exporters/DataMergeExporter.php: -------------------------------------------------------------------------------- 1 | store = PatternData::get(); 29 | 30 | } 31 | 32 | public function run() { 33 | 34 | foreach ($this->store as $patternStoreKey => $patternStoreData) { 35 | 36 | if ($patternStoreData["category"] == "pattern") { 37 | 38 | if (isset($patternStoreData["data"]) || isset($patternStoreData["listItems"])) { 39 | Data::initPattern($patternStoreKey); 40 | } 41 | 42 | if (isset($patternStoreData["data"])) { 43 | Data::setPatternData($patternStoreKey, $patternStoreData["data"]); 44 | } 45 | 46 | if (isset($patternStoreData["listItems"])) { 47 | Data::setPatternListItems($patternStoreKey, $patternStoreData["listItems"]); 48 | } 49 | 50 | } 51 | 52 | } 53 | 54 | // walk across the data and change link.pattern-partial to real source 55 | Data::compareReplaceListVarsInit(); 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Exporters/NavItemsExporter.php: -------------------------------------------------------------------------------- 1 | store = PatternData::get(); 29 | $this->styleGuideExcludes = Config::getOption("styleGuideExcludes"); 30 | 31 | } 32 | 33 | public function run() { 34 | 35 | $bi = 0; 36 | $ni = 0; 37 | $patternSubtypeSet = false; 38 | $patternType = ""; 39 | $patternTypeDash = ""; 40 | $suffixRendered = Config::getOption("outputFileSuffixes.rendered"); 41 | 42 | $navItems = array(); 43 | $navItems["patternTypes"] = array(); 44 | 45 | // iterate over the different categories and add them to the navigation 46 | foreach ($this->store as $patternStoreKey => $patternStoreData) { 47 | 48 | if ($patternStoreData["category"] == "patternType") { 49 | 50 | $bi = (count($navItems["patternTypes"]) == 0) ? 0 : $bi + 1; 51 | 52 | // add a new patternType to the nav 53 | $navItems["patternTypes"][$bi] = array("patternTypeLC" => strtolower($patternStoreData["nameClean"]), 54 | "patternTypeUC" => ucwords($patternStoreData["nameClean"]), 55 | "patternType" => $patternStoreData["name"], 56 | "patternTypeDash" => $patternStoreData["nameDash"], 57 | "patternTypeItems" => array(), 58 | "patternItems" => array()); 59 | 60 | // starting a new set of pattern types. it might not have any pattern subtypes 61 | $patternSubtypeSet = false; 62 | $patternType = $patternStoreData["name"]; 63 | $patternTypeDash = $patternStoreData["nameDash"]; 64 | 65 | } else if ($patternStoreData["category"] == "patternSubtype") { 66 | 67 | $ni = (!$patternSubtypeSet) ? 0 : $ni + 1; 68 | 69 | // add a new patternSubtype to the nav 70 | $navItems["patternTypes"][$bi]["patternTypeItems"][$ni] = array("patternSubtypeLC" => strtolower($patternStoreData["nameClean"]), 71 | "patternSubtypeUC" => ucwords($patternStoreData["nameClean"]), 72 | "patternSubtype" => $patternStoreData["name"], 73 | "patternSubtypeDash" => $patternStoreData["nameDash"], 74 | "patternSubtypeItems" => array()); 75 | 76 | // starting a new set of pattern types. it might not have any pattern subtypes 77 | $patternSubtype = $patternStoreData["name"]; 78 | $patternSubtypeDash = $patternStoreData["nameDash"]; 79 | $patternSubtypeSet = true; 80 | 81 | } else if ($patternStoreData["category"] == "pattern") { 82 | 83 | if (isset($patternStoreData["hidden"]) && !$patternStoreData["hidden"]) { 84 | 85 | // set-up the info for the nav 86 | $patternInfo = array("patternPath" => $patternStoreData["pathDash"]."/".$patternStoreData["pathDash"].$suffixRendered.".html", 87 | "patternSrcPath" => $patternStoreData["pathName"], 88 | "patternName" => ucwords($patternStoreData["nameClean"]), 89 | "patternState" => $patternStoreData["state"], 90 | "patternPartial" => $patternStoreData["partial"]); 91 | 92 | // add to the nav 93 | if ($patternStoreData["depth"] == 1) { 94 | $navItems["patternTypes"][$bi]["patternItems"][] = $patternInfo; 95 | } else { 96 | $navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][] = $patternInfo; 97 | } 98 | 99 | } 100 | 101 | } 102 | 103 | } 104 | 105 | // review each subtype. add a view all link or remove the subtype as necessary 106 | foreach ($navItems["patternTypes"] as $patternTypeKey => $patternTypeValues) { 107 | 108 | $reset = false; 109 | $patternType = $patternTypeValues["patternType"]; 110 | $patternTypeDash = $patternTypeValues["patternTypeDash"]; 111 | 112 | if (!in_array($patternType,$this->styleGuideExcludes)) { 113 | 114 | foreach ($patternTypeValues["patternTypeItems"] as $patternSubtypeKey => $patternSubtypeValues) { 115 | 116 | // if there are no sub-items in a section remove it 117 | if (empty($patternSubtypeValues["patternSubtypeItems"])) { 118 | 119 | unset($navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]); 120 | $reset = true; 121 | 122 | } else { 123 | 124 | $patternSubtype = $patternSubtypeValues["patternSubtype"]; 125 | $patternSubtypeDash = $patternSubtypeValues["patternSubtypeDash"]; 126 | $subItemsCount = count($patternSubtypeValues["patternSubtypeItems"]); 127 | 128 | // add a view all link 129 | $navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]["patternSubtypeItems"][$subItemsCount] = array( 130 | "patternPath" => $patternType."-".$patternSubtype."/index.html", 131 | "patternName" => "View All", 132 | "patternType" => $patternType, 133 | "patternSubtype" => $patternSubtype, 134 | "patternPartial" => "viewall-".$patternTypeDash."-".$patternSubtypeDash); 135 | 136 | } 137 | 138 | } 139 | 140 | } 141 | 142 | if ($reset) { 143 | $navItems["patternTypes"][$patternTypeKey]["patternTypeItems"] = array_values($navItems["patternTypes"][$patternTypeKey]["patternTypeItems"]); 144 | $reset = false; 145 | } 146 | 147 | // add an overall view all link to the menus with sub-menus 148 | if (!empty($navItems["patternTypes"][$patternTypeKey]["patternTypeItems"])) { 149 | 150 | $navItems["patternTypes"][$patternTypeKey]["patternItems"][] = array("patternPath" => $patternType."/index.html", 151 | "patternName" => "View All", 152 | "patternType" => $patternType, 153 | "patternSubtype" => "all", 154 | "patternPartial" => "viewall-".$patternTypeDash."-all"); 155 | 156 | } 157 | 158 | } 159 | 160 | return $navItems; 161 | 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Exporters/PatternPartialsExporter.php: -------------------------------------------------------------------------------- 1 | store = PatternData::get(); 30 | $this->cacheBuster = Data::getOption("cacheBuster"); 31 | $this->styleGuideExcludes = Config::getOption("styleGuideExcludes"); 32 | 33 | } 34 | 35 | /** 36 | * Compare the search and ignore props against the name. 37 | * Can use && or || in the comparison 38 | * @param {String} the type of the pattern that should be used in the view all 39 | * @param {String} the subtype of the pattern that be used in the view all 40 | * 41 | * @return {Array} the list of partials 42 | */ 43 | public function run($type = "", $subtype = "") { 44 | 45 | // default vars 46 | $patternPartials = array(); 47 | $suffixRendered = Config::getOption("outputFileSuffixes.rendered"); 48 | 49 | foreach ($this->store as $patternStoreKey => $patternStoreData) { 50 | 51 | // Docs for patternTypes (i.e. `atoms.md`), don't have these rules and need them to pass below conditionals 52 | if ( 53 | !isset($patternStoreData['depth']) 54 | && !isset($patternStoreData['hidden']) 55 | && !isset($patternStoreData['noviewall']) 56 | ) { 57 | $patternStoreData["hidden"] = false; 58 | $patternStoreData["noviewall"] = false; 59 | $patternStoreData["depth"] = 0; 60 | } 61 | $canShow = isset($patternStoreData["hidden"]) && (!$patternStoreData["hidden"]) && (!$patternStoreData["noviewall"]); 62 | 63 | if (($patternStoreData["category"] == "pattern") && $canShow && ($patternStoreData["depth"] > 1) && (!in_array($patternStoreData["type"],$this->styleGuideExcludes))) { 64 | 65 | if ((($patternStoreData["type"] == $type) && empty($subtype)) || (empty($type) && empty($subtype)) || (($patternStoreData["type"] == $type) && ($patternStoreData["subtype"] == $subtype))) { 66 | 67 | $patternPartialData = array(); 68 | $patternPartialData["patternName"] = $patternStoreData["nameClean"]; 69 | $patternPartialData["patternLink"] = $patternStoreData["pathDash"]."/".$patternStoreData["pathDash"].$suffixRendered.".html"; 70 | $patternPartialData["patternPartial"] = $patternStoreData["partial"]; 71 | $patternPartialData["patternPartialCode"] = $patternStoreData["code"]; 72 | $patternPartialData["patternState"] = $patternStoreData["state"]; 73 | 74 | $patternPartialData["patternLineageExists"] = isset($patternStoreData["lineages"]); 75 | $patternPartialData["patternLineages"] = isset($patternStoreData["lineages"]) ? $patternStoreData["lineages"] : array(); 76 | $patternPartialData["patternLineageRExists"] = isset($patternStoreData["lineagesR"]); 77 | $patternPartialData["patternLineagesR"] = isset($patternStoreData["lineagesR"]) ? $patternStoreData["lineagesR"] : array(); 78 | $patternPartialData["patternLineageEExists"] = (isset($patternStoreData["lineages"]) || isset($patternStoreData["lineagesR"])); 79 | 80 | $patternPartialData["patternDescExists"] = isset($patternStoreData["desc"]); 81 | $patternPartialData["patternDesc"] = isset($patternStoreData["desc"]) ? $patternStoreData["desc"] : ""; 82 | 83 | $patternPartialData["patternDescAdditions"] = isset($patternStoreData["partialViewDescAdditions"]) ? $patternStoreData["partialViewDescAdditions"] : array(); 84 | $patternPartialData["patternExampleAdditions"] = isset($patternStoreData["partialViewExampleAdditions"]) ? $patternStoreData["partialViewExampleAdditions"] : array(); 85 | 86 | // add the pattern data so it can be exported 87 | $patternData = array(); 88 | $patternData["lineage"] = isset($patternStoreData["lineages"]) ? $patternStoreData["lineages"] : array(); 89 | $patternData["lineageR"] = isset($patternStoreData["lineagesR"]) ? $patternStoreData["lineagesR"] : array(); 90 | $patternData["patternBreadcrumb"] = $patternStoreData["breadcrumb"]; 91 | $patternData["patternDesc"] = (isset($patternStoreData["desc"])) ? $patternStoreData["desc"] : ""; 92 | $patternData["patternExtension"] = Config::getOption("patternExtension"); 93 | $patternData["patternName"] = $patternStoreData["nameClean"]; 94 | $patternData["patternPartial"] = $patternStoreData["partial"]; 95 | $patternData["patternState"] = $patternStoreData["state"]; 96 | $patternPartialData["patternData"] = json_encode($patternData); 97 | 98 | $patternPartials[] = $patternPartialData; 99 | 100 | } 101 | 102 | } else if (($patternStoreData["category"] == "patternSubtype") && (!in_array($patternStoreData["type"],$this->styleGuideExcludes))) { 103 | 104 | if ((($patternStoreData["type"] == $type) && empty($subtype)) || (empty($type) && empty($subtype)) || (($patternStoreData["type"] == $type) && ($patternStoreData["name"] == $subtype))) { 105 | 106 | $patternPartialData = array(); 107 | $patternPartialData["patternName"] = $patternStoreData["nameClean"]; 108 | $patternPartialData["patternLink"] = $patternStoreData["pathDash"]."/index.html"; 109 | $patternPartialData["patternPartial"] = $patternStoreData["partial"]; 110 | $patternPartialData["patternSectionSubtype"] = true; 111 | $patternPartialData["patternDesc"] = isset($patternStoreData["desc"]) ? $patternStoreData["desc"] : ""; 112 | 113 | $patternPartials[] = $patternPartialData; 114 | 115 | } 116 | 117 | } else if ( 118 | ($patternStoreData["category"] == "pattern") 119 | && $canShow 120 | && (isset($patternStoreData["full"]) 121 | && ($type === $patternStoreData["full"] || $type === "")) 122 | && ($subtype === "") 123 | ) { 124 | // This is for `patternType` docs. Given this structure: 125 | // - _patterns/ 126 | // - atoms/ 127 | // - forms/ 128 | // - atoms.md 129 | // This will take the contents of `atoms.md` and place at top of "Atoms > View All" 130 | 131 | $patternPartialData = array(); 132 | // Getting the name from md's `title: My Name` works here, as does the link, but it doesn't make sense to link to the view you are already on. Plus you can just do the title in the MD doc. Keeping here for now in case it's wanted later. 133 | // $patternPartialData["patternName"] = isset($patternStoreData["nameClean"]) ? $patternStoreData["nameClean"] : ''; 134 | // $patternPartialData["patternLink"] = $patternStoreData["full"] . "/index.html"; 135 | 136 | $patternPartialData["patternSectionSubtype"] = true; 137 | $patternPartialData["patternDesc"] = isset($patternStoreData["desc"]) ? $patternStoreData["desc"] : ""; 138 | 139 | $patternPartials[] = $patternPartialData; 140 | } 141 | 142 | } 143 | 144 | return array("partials" => $patternPartials, "cacheBuster" => $this->cacheBuster); 145 | 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Exporters/PatternPathDestsExporter.php: -------------------------------------------------------------------------------- 1 | store = PatternData::get(); 28 | 29 | } 30 | 31 | public function run() { 32 | 33 | $patternPathDests = array(); 34 | 35 | foreach ($this->store as $patternStoreKey => $patternStoreData) { 36 | 37 | if (($patternStoreData["category"] == "pattern") && isset($patternStoreData["hidden"]) && !$patternStoreData["hidden"]) { 38 | 39 | $nameDash = $patternStoreData["nameDash"]; 40 | $typeDash = $patternStoreData["typeDash"]; 41 | 42 | if (!isset($patternPathDests[$typeDash])) { 43 | $patternPathDests[$typeDash] = array(); 44 | } 45 | 46 | $patternPathDests[$typeDash][$nameDash] = $patternStoreData["pathDash"]; 47 | 48 | } 49 | 50 | } 51 | 52 | return $patternPathDests; 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Exporters/PatternPathSrcExporter.php: -------------------------------------------------------------------------------- 1 | store = PatternData::get(); 29 | 30 | } 31 | 32 | public function run() { 33 | 34 | $patternPathDests = array(); 35 | 36 | foreach ($this->store as $patternStoreKey => $patternStoreData) { 37 | 38 | if (($patternStoreData["category"] == "pattern") && isset($patternStoreData["nameDash"])) { 39 | 40 | $nameDash = $patternStoreData["nameDash"]; 41 | $typeDash = $patternStoreData["typeDash"]; 42 | 43 | if (!isset($patternPathDests[$typeDash])) { 44 | $patternPathDests[$typeDash] = array(); 45 | } 46 | 47 | $patternPathDests[$typeDash][$nameDash] = (isset($patternStoreData["pseudo"])) ? $patternStoreData["pathOrig"] : $patternStoreData["pathName"]; 48 | 49 | } 50 | 51 | } 52 | 53 | return $patternPathDests; 54 | 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php: -------------------------------------------------------------------------------- 1 | styleGuideExcludes = Config::getOption("styleGuideExcludes"); 28 | 29 | } 30 | 31 | public function run($navItems) { 32 | 33 | // default vars 34 | $viewAllPaths = array(); 35 | 36 | foreach ($navItems["patternTypes"] as $patternTypeKey => $patternTypeValues) { 37 | 38 | $patternType = $patternTypeValues["patternType"]; 39 | $patternTypeDash = $patternTypeValues["patternTypeDash"]; 40 | 41 | if (!in_array($patternType,$this->styleGuideExcludes)) { 42 | 43 | foreach ($patternTypeValues["patternTypeItems"] as $patternSubtypeKey => $patternSubtypeValues) { 44 | 45 | $patternSubtype = $patternSubtypeValues["patternSubtype"]; 46 | $patternSubtypeDash = $patternSubtypeValues["patternSubtypeDash"]; 47 | 48 | if (isset($patternSubtypeValues["patternSubtypeItems"])) { 49 | 50 | foreach ($patternSubtypeValues["patternSubtypeItems"] as $patternSubtypeItemKey => $patternSubtypeItemValues) { 51 | 52 | if (strpos($patternSubtypeItemValues["patternPartial"],"viewall-") !== false) { 53 | 54 | $viewAllPaths[$patternTypeDash][$patternSubtypeDash] = $patternType."-".$patternSubtype; 55 | 56 | } 57 | 58 | } 59 | 60 | } 61 | 62 | if (strpos($patternSubtypeItemValues["patternPartial"],"viewall-") !== false) { 63 | 64 | $viewAllPaths[$patternTypeDash]["all"] = $patternType; 65 | 66 | } 67 | 68 | } 69 | 70 | } 71 | 72 | } 73 | 74 | return $viewAllPaths; 75 | 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Helper.php: -------------------------------------------------------------------------------- 1 | lineageMatch = html_entity_decode(Config::getOption("lineageMatch"),ENT_QUOTES); 30 | $this->lineageMatchKey = Config::getOption("lineageMatchKey"); 31 | 32 | } 33 | 34 | public function run() { 35 | 36 | // set-up default vars 37 | $foundLineages = array(); 38 | $patternSourceDir = Config::getOption("patternSourceDir"); 39 | $patternExtension = Config::getOption("patternExtension"); 40 | $suffixRendered = Config::getOption("outputFileSuffixes.rendered"); 41 | 42 | // check for the regular lineages in only normal patterns 43 | $store = PatternData::get(); 44 | foreach ($store as $patternStoreKey => $patternStoreData) { 45 | 46 | if (($patternStoreData["category"] == "pattern") && (!isset($patternStoreData["pseudo"]))) { 47 | 48 | $patternLineages = array(); 49 | $fileData = isset($patternStoreData["patternRaw"]) ? $patternStoreData["patternRaw"] : ""; 50 | $foundLineages = $this->findLineages($fileData); 51 | 52 | if (!empty($foundLineages)) { 53 | 54 | foreach ($foundLineages as $lineage) { 55 | 56 | /** 57 | * Fix for Pattern Lab Lineages when using Twig Namespaces. 58 | * Converts the full file path to PL-friendly shorthand so 59 | * they are internally registered. 60 | * 61 | * 1. Only handle instances where we aren't or can't use the 62 | * shorthand PL path reference in templates, specifically 63 | * in Twig / D8 when we need to use Twig namespaces in 64 | * our template paths. 65 | * 2. Strip off the @ sign at the beginning of our $lineage string. 66 | * 3. Break apart the full lineage path based on any slashes that 67 | * may exist. 68 | * 4. Store the length of our broken up path for reference below 69 | * 5. Store the first part of the string up to the first slash "/" 70 | * 6. Now grab the last part of the pattern key, based on the length 71 | * of the path we previously exploded. 72 | * 7. Remove any "_" from pattern Name. 73 | * 8. Remove any potential prefixed numbers or number + dash 74 | * combos on our Pattern Name. 75 | * 9. Strip off the pattern path extension (.twig, 76 | * .mustache, etc) if it exists. 77 | * 10. If the pattern name parsed had an extension, 78 | * re-assign our Pattern Name to that. 79 | * 11. Finally, re-assign $lineage to the default PL pattern key. 80 | */ 81 | 82 | if ($lineage[0] == '@') { /* [1] */ 83 | $lineage = ltrim($lineage, '@'); /* [2] */ 84 | $lineageParts = explode('/', $lineage); /* [3] */ 85 | $length = count($lineageParts); /* [4] */ 86 | $patternType = $lineageParts[0]; /* [5] */ 87 | 88 | $patternName = $lineageParts[$length - 1]; /* [6] */ 89 | $patternName = ltrim($patternName, '_'); /* [7] */ 90 | $patternName = preg_replace('/^[0-9\-]+/', '', 91 | $patternName); /* [8] */ 92 | 93 | $patternNameStripped = explode('.' . $patternExtension, $patternName); /* [9] */ 94 | 95 | if (count($patternNameStripped) > 1) { /* [10] */ 96 | $patternName = $patternNameStripped[0]; 97 | } 98 | $lineage = $patternType . "-" . $patternName; /* [11] */ 99 | } 100 | 101 | if (PatternData::getOption($lineage)) { 102 | 103 | $patternLineages[] = array("lineagePattern" => $lineage, 104 | "lineagePath" => "../../patterns/".$patternStoreData["pathDash"]."/".$patternStoreData["pathDash"].$suffixRendered.".html"); 105 | 106 | } else { 107 | 108 | if (strpos($lineage, '/') === false) { 109 | $fileName = $patternStoreData["pathName"].".".$patternExtension; 110 | // This doesn't work very consistently. @todo Improve error reporting when trying to include a Twig file that doesn't exist (b/c doing so simply outputs the path to the file you were trying to include, giving no error to user that include path had a typo) 111 | // Console::writeWarning("you may have a typo in ".$fileName.". `".$lineage."` is not a valid pattern..."); 112 | } 113 | 114 | } 115 | 116 | } 117 | 118 | // add the lineages to the PatternData::$store 119 | PatternData::setPatternOption($patternStoreKey,"lineages",$patternLineages); 120 | 121 | } 122 | 123 | } 124 | 125 | } 126 | 127 | // handle all of those pseudo patterns 128 | $store = PatternData::get(); 129 | foreach ($store as $patternStoreKey => $patternStoreData) { 130 | 131 | if (($patternStoreData["category"] == "pattern") && (isset($patternStoreData["pseudo"]))) { 132 | 133 | // add the lineages to the PatternData::$store 134 | $patternStoreKeyOriginal = $patternStoreData["original"]; 135 | PatternData::setPatternOption($patternStoreKey,"lineages",PatternData::getPatternOption($patternStoreKeyOriginal,"lineages")); 136 | 137 | } 138 | 139 | } 140 | 141 | // check for the reverse lineages and skip pseudo patterns 142 | $store = PatternData::get(); 143 | foreach ($store as $patternStoreKey => $patternStoreData) { 144 | 145 | if (($patternStoreData["category"] == "pattern") && (!isset($patternStoreData["pseudo"])) && isset($patternStoreData["partial"])) { 146 | 147 | $patternLineagesR = array(); 148 | 149 | $storeTake2 = PatternData::get(); 150 | foreach ($storeTake2 as $haystackKey => $haystackData) { 151 | 152 | if (($haystackData["category"] == "pattern") && (isset($haystackData["lineages"])) && (!empty($haystackData["lineages"]))) { 153 | 154 | foreach ($haystackData["lineages"] as $haystackLineage) { 155 | 156 | if ($haystackLineage["lineagePattern"] == $patternStoreData["partial"]) { 157 | 158 | $foundAlready = false; 159 | foreach ($patternLineagesR as $patternCheck) { 160 | 161 | if ($patternCheck["lineagePattern"] == $patternStoreData["partial"]) { 162 | $foundAlready = true; 163 | break; 164 | } 165 | 166 | } 167 | 168 | if (!$foundAlready) { 169 | 170 | if (PatternData::getOption($haystackKey)) { 171 | 172 | $path = PatternData::getPatternOption($haystackKey,"pathDash"); 173 | $patternLineagesR[] = array("lineagePattern" => $haystackKey, 174 | "lineagePath" => "../../patterns/".$path."/".$path.$suffixRendered.".html"); 175 | 176 | } 177 | 178 | } 179 | 180 | } 181 | 182 | } 183 | 184 | } 185 | 186 | } 187 | 188 | PatternData::setPatternOption($patternStoreKey,"lineagesR",$patternLineagesR); 189 | 190 | } 191 | 192 | } 193 | 194 | // handle all of those pseudo patterns 195 | $store = PatternData::get(); 196 | foreach ($store as $patternStoreKey => $patternStoreData) { 197 | 198 | if (($patternStoreData["category"] == "pattern") && (isset($patternStoreData["pseudo"]))) { 199 | 200 | // add the lineages to the PatternData::$store 201 | $patternStoreKeyOriginal = $patternStoreData["original"]; 202 | PatternData::setPatternOption($patternStoreKey,"lineagesR",PatternData::getPatternOption($patternStoreKeyOriginal,"lineagesR")); 203 | 204 | } 205 | 206 | } 207 | 208 | } 209 | 210 | 211 | /** 212 | * Get the lineage for a given pattern by parsing it and matching mustache partials 213 | * @param {String} the data from the raw pattern 214 | * 215 | * @return {Array} a list of patterns 216 | */ 217 | protected function findLineages($data) { 218 | if (preg_match_all("/".$this->lineageMatch."/",$data,$matches)) { 219 | return array_unique($matches[$this->lineageMatchKey]); 220 | } 221 | return array(); 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Helpers/PatternCodeHelper.php: -------------------------------------------------------------------------------- 1 | exportFiles = $options["exportFiles"]; 31 | $this->exportClean = $options["exportClean"]; 32 | $this->patternPaths = $options["patternPaths"]; 33 | 34 | } 35 | 36 | public function run() { 37 | 38 | // default vars 39 | $options = array(); 40 | $options["patternPaths"] = $this->patternPaths; 41 | $patternExtension = Config::getOption("patternExtension"); 42 | $patternSourceDir = Config::getOption("patternSourceDir"); 43 | $htmlHead = Template::getHTMLHead(); 44 | $htmlFoot = Template::getHTMLFoot(); 45 | $patternHead = Template::getPatternHead(); 46 | $patternFoot = Template::getPatternFoot(); 47 | $stringLoader = Template::getStringLoader(); 48 | 49 | // re-load the pattern data since we modified it 50 | $store = PatternData::get(); 51 | 52 | // load the pattern loader 53 | $patternEngineBasePath = PatternEngine::getInstance()->getBasePath(); 54 | $patternLoaderClass = $patternEngineBasePath."\Loaders\PatternLoader"; 55 | $patternLoader = new $patternLoaderClass($options); 56 | 57 | // iterate to process each pattern 58 | foreach ($store as $patternStoreKey => $patternStoreData) { 59 | 60 | if (($patternStoreData["category"] == "pattern") && isset($patternStoreData["name"])) { 61 | 62 | $data = Data::getPatternSpecificData($patternStoreKey); 63 | 64 | // add the pattern data so it can be exported 65 | $patternData = array(); 66 | //$patternFooterData["patternFooterData"]["cssEnabled"] = (Config::$options["enableCSS"] && isset($this->patternCSS[$p])) ? "true" : "false"; 67 | $patternData["cssEnabled"] = false; 68 | $patternData["lineage"] = isset($patternStoreData["lineages"]) ? $patternStoreData["lineages"] : array(); 69 | $patternData["lineageR"] = isset($patternStoreData["lineagesR"]) ? $patternStoreData["lineagesR"] : array(); 70 | $patternData["patternBreadcrumb"] = $patternStoreData["breadcrumb"]; 71 | $patternData["patternDesc"] = (isset($patternStoreData["desc"])) ? $patternStoreData["desc"] : ""; 72 | $patternData["patternExtension"] = $patternExtension; 73 | $patternData["patternName"] = $patternStoreData["nameClean"]; 74 | $patternData["patternPartial"] = $patternStoreData["partial"]; 75 | $patternData["patternState"] = $patternStoreData["state"]; 76 | 77 | // extra copy for the code view 78 | $patternData["extraOutput"] = isset($patternStoreData["extraOutput"]) ? $patternStoreData["extraOutput"] : array(); 79 | 80 | // add the pattern lab specific mark-up 81 | // set a default var 82 | $exportClean = (isset($options["exportClean"])) ? $options["exportClean"] : false; 83 | $data["patternLabHead"] = (!$this->exportFiles) ? $stringLoader->render(array("string" => $htmlHead, "data" => array("cacheBuster" => $data["cacheBuster"]))) : ""; 84 | $data["patternLabFoot"] = (!$this->exportFiles) ? $stringLoader->render(array("string" => $htmlFoot, "data" => array("cacheBuster" => $data["cacheBuster"], "isPattern" => true, "patternData" => json_encode($patternData)))) : ""; 85 | 86 | if (isset($patternStoreData["patternRaw"])) { 87 | 88 | $header = (!$this->exportClean) ? $patternLoader->render(array("pattern" => $patternHead, "data" => $data)) : ""; 89 | $code = $patternLoader->render(array("pattern" => $patternStoreData["patternRaw"], "data" => $data)); 90 | $footer = (!$this->exportClean) ? $patternLoader->render(array("pattern" => $patternFoot, "data" => $data)) : ""; 91 | 92 | PatternData::setPatternOption($patternStoreKey,"header",$header); 93 | PatternData::setPatternOption($patternStoreKey,"code",$code); 94 | PatternData::setPatternOption($patternStoreKey,"footer",$footer); 95 | 96 | } 97 | 98 | } 99 | 100 | } 101 | 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Helpers/PatternStateHelper.php: -------------------------------------------------------------------------------- 1 | $patternStoreData) { 41 | 42 | if (($patternStoreData["category"] == "pattern") && isset($patternStoreData["state"])) { 43 | 44 | $patternState = $patternStoreData["state"]; 45 | 46 | // make sure the pattern has a given state 47 | if ($patternState != "") { 48 | 49 | $patternStateDigit = array_search($patternState,$patternStates); 50 | 51 | // if this is a true pattern state update various patterns 52 | if ($patternStateDigit !== false) { 53 | 54 | $storeTake2 = PatternData::get(); 55 | foreach ($storeTake2 as $patternStoreKey2 => $patternStoreData2) { 56 | 57 | if (($patternStoreData2["category"] == "pattern") && isset($patternStoreData2["lineagesR"])) { 58 | 59 | foreach ($patternStoreData2["lineagesR"] as $patternCheckInfo) { 60 | 61 | $lineagePatternPartial = $patternCheckInfo["lineagePattern"]; 62 | 63 | // if the found pattern's lineage is empty and the pattern state isn't the last (e.g. complete) add the pattern state 64 | // otherwise, if the pattern state is less than the one being checked update the pattern 65 | if ((PatternData::getPatternOption($lineagePatternPartial,"state") == "") && ($patternStateDigit != $patternStateLast)) { 66 | 67 | PatternData::setPatternOption($lineagePatternPartial,"state",$patternState); 68 | 69 | } else { 70 | 71 | $patternStateCheck = array_search(PatternData::getPatternOption($lineagePatternPartial,"state"), $patternStates); 72 | if ($patternStateDigit < $patternStateCheck) { 73 | PatternData::setPatternOption($lineagePatternPartial,"state",$patternState); 74 | } 75 | 76 | } 77 | 78 | } 79 | 80 | } 81 | 82 | } 83 | 84 | } 85 | 86 | } 87 | 88 | } 89 | 90 | } 91 | 92 | // make sure we update the lineages with the pattern state if appropriate 93 | $store = PatternData::get(); 94 | foreach ($store as $patternStoreKey => $patternStoreData) { 95 | 96 | if ($patternStoreData["category"] == "pattern") { 97 | 98 | if (isset($patternStoreData["lineages"]) && !empty($patternStoreData["lineages"])) { 99 | 100 | foreach ($patternStoreData["lineages"] as $patternLineageKey => $patternLineageInfo) { 101 | 102 | $lineagePattern = $patternLineageInfo["lineagePattern"]; 103 | $patternState = PatternData::getPatternOption($lineagePattern,"state"); 104 | if (($patternState != "") && ($patternState != null)) { 105 | PatternData::setPatternSubOption($patternStoreKey,"lineages",$patternLineageKey,"lineageState",$patternState); 106 | } 107 | 108 | } 109 | 110 | } 111 | 112 | if (isset($patternStoreData["lineagesR"]) && !empty($patternStoreData["lineagesR"])) { 113 | 114 | foreach ($patternStoreData["lineagesR"] as $patternLineageKey => $patternLineageInfo) { 115 | 116 | $lineagePattern = $patternLineageInfo["lineagePattern"]; 117 | $patternState = PatternData::getPatternOption($lineagePattern,"state"); 118 | if (($patternState != "") && ($patternState != null)) { 119 | PatternData::setPatternSubOption($patternStoreKey,"lineagesR",$patternLineageKey,"lineageState",$patternState); 120 | } 121 | 122 | } 123 | 124 | } 125 | 126 | } 127 | 128 | } 129 | 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Helpers/Plugins/CSSRuleSaverHelperPlugin.php: -------------------------------------------------------------------------------- 1 | // set-up the mark-up for CSS Rule Saver so it can figure out which rules to save 2 | $patternCSSExists = $this->enableCSS; 3 | $patternCSS = ""; 4 | if ($this->enableCSS) { 5 | $this->cssRuleSaver->loadHTML($patternCodeRaw,false); 6 | $patternCSS = $this->cssRuleSaver->saveRules(); 7 | $this->patternCSS[$patternSubtypeItem["patternPartial"]] = $patternCSS; 8 | } -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Helpers/RawPatternHelper.php: -------------------------------------------------------------------------------- 1 | $patternStoreData) { 43 | 44 | if (($patternStoreData["category"] == "pattern") && isset($patternStoreData["name"])) { 45 | 46 | // figure out the source path for the pattern to render 47 | $srcPath = (isset($patternStoreData["pseudo"])) ? PatternData::getPatternOption($patternStoreData["original"],"pathName") : $patternStoreData["pathName"]; 48 | 49 | // load the raw data so it can be modified/rendered 50 | $path = $patternSourceDir.DIRECTORY_SEPARATOR.$srcPath.".".$patternExtension; 51 | if (file_exists($path)) { 52 | PatternData::setPatternOption($patternStoreKey,"patternRaw",file_get_contents($path)); 53 | } else { 54 | Console::writeWarning($patternStoreData["partial"]." wasn't found for loading. the given path: ".$path); 55 | } 56 | 57 | } 58 | 59 | } 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Rule.php: -------------------------------------------------------------------------------- 1 | depthProp != 3) && ($depth != $this->depthProp)) { 43 | return false; 44 | } 45 | 46 | if (($this->compareProp($ext,$this->extProp, true)) && ($isDir == $this->isDirProp) && ($isFile == $this->isFileProp)) { 47 | $result = true; 48 | if ($this->searchProp != "") { 49 | $result = $this->compareProp($name,$this->searchProp); 50 | } 51 | if ($this->ignoreProp != "") { 52 | $result = ($this->compareProp($name,$this->ignoreProp)) ? false : true; 53 | } 54 | return $result; 55 | } 56 | 57 | return false; 58 | 59 | } 60 | 61 | /** 62 | * Compare the search and ignore props against the name. 63 | * Can use && or || in the comparison 64 | * @param {String} the name of the item 65 | * @param {String} the value of the property to compare 66 | * 67 | * @return {Boolean} whether the compare was successful or not 68 | */ 69 | protected function compareProp($name, $propCompare, $exact = false) { 70 | 71 | if (($name == "") && ($propCompare == "")) { 72 | $result = true; 73 | } else if ((($name == "") && ($propCompare != "")) || (($name != "") && ($propCompare == ""))) { 74 | $result = false; 75 | } else if (strpos($propCompare,"&&") !== false) { 76 | $result = true; 77 | $props = explode("&&",$propCompare); 78 | foreach ($props as $prop) { 79 | $pos = $this->testProp($name, $prop, $exact); 80 | $result = ($result && $pos); 81 | } 82 | } else if (strpos($propCompare,"||") !== false) { 83 | $result = false; 84 | $props = explode("||",$propCompare); 85 | foreach ($props as $prop) { 86 | $pos = $this->testProp($name, $prop, $exact); 87 | $result = ($result || $pos); 88 | } 89 | } else { 90 | $result = $this->testProp($name, $propCompare, $exact); 91 | } 92 | 93 | return $result; 94 | 95 | } 96 | 97 | /** 98 | * Get the name for a given pattern sans any possible digits used for reordering 99 | * @param {String} the pattern based on the filesystem name 100 | * @param {Boolean} whether or not to strip slashes from the pattern name 101 | * 102 | * @return {String} a lower-cased version of the pattern name 103 | */ 104 | protected function getPatternName($pattern, $clean = true) { 105 | $patternBits = explode("-",$pattern,2); 106 | $patternName = (((int)$patternBits[0] != 0) || ($patternBits[0] == '00')) ? $patternBits[1] : $pattern; 107 | // replace possible dots with dashes. pattern names cannot contain dots 108 | // since they are used as id/class names in the styleguidekit. 109 | $patternName = str_replace('.', '-', $patternName); 110 | return ($clean) ? (str_replace("-"," ",$patternName)) : $patternName; 111 | } 112 | 113 | /** 114 | * Get the value for a property on the current PatternData rule 115 | * @param {String} the name of the property 116 | * 117 | * @return {Boolean} whether the set was successful 118 | */ 119 | public function getProp($propName) { 120 | 121 | if (isset($this->$propName)) { 122 | return $this->$propName; 123 | } 124 | 125 | return false; 126 | 127 | } 128 | 129 | /** 130 | * Set a value for a property on the current PatternData rule 131 | * @param {String} the name of the property 132 | * @param {String} the value of the property 133 | * 134 | * @return {Boolean} whether the set was successful 135 | */ 136 | public function setProp($propName, $propValue) { 137 | 138 | $this->$propName = $this->$propValue; 139 | return true; 140 | 141 | } 142 | 143 | /** 144 | * Test the given property 145 | * @param {String} the value of the property to be tested 146 | * @param {String} the value of the property to be tested against 147 | * @param {Boolean} if this should be an exact match or just a string appearing in another 148 | * 149 | * @return {Boolean} the test result 150 | */ 151 | protected function testProp($propToTest, $propToTestAgainst, $beExact) { 152 | if ($beExact) { 153 | $result = ($propToTest === $propToTestAgainst); 154 | } else { 155 | $result = (strpos($propToTest,$propToTestAgainst) !== false) ? true : false; 156 | } 157 | return $result; 158 | } 159 | 160 | /** 161 | * Update a property on a given rule 162 | * @param {String} the name of the property 163 | * @param {String} the value of the property 164 | * @param {String} the action that should be taken with the new value 165 | * 166 | * @return {Boolean} whether the update was successful 167 | */ 168 | public function updateProp($propName, $propValue, $action = "or") { 169 | 170 | if (!isset($this->$propName) || !is_scalar($propValue)) { 171 | return false; 172 | } 173 | 174 | if ($action == "or") { 175 | 176 | $propValue = $this->$propName."||".$propValue; 177 | 178 | } else if ($action == "and") { 179 | 180 | $propValue = $this->$propName."&&".$propValue; 181 | 182 | } 183 | 184 | return $this->setProp($this->$propName,$propValue); 185 | 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Rules/DocumentationRule.php: -------------------------------------------------------------------------------- 1 | depthProp = 3; // 3 means that depth won't be checked 27 | $this->extProp = "md"; 28 | $this->isDirProp = false; 29 | $this->isFileProp = true; 30 | $this->searchProp = ""; 31 | $this->ignoreProp = ""; 32 | 33 | } 34 | 35 | public function run($depth, $ext, $path, $pathName, $name) { 36 | 37 | // load default vars 38 | $patternType = PatternData::getPatternType(); 39 | $patternTypeDash = PatternData::getPatternTypeDash(); 40 | $dirSep = PatternData::getDirSep(); 41 | 42 | // set-up the names, $name == 00-colors.md 43 | $doc = str_replace(".".$this->extProp,"",$name); // 00-colors 44 | $docDash = $this->getPatternName(str_replace("_","",$doc),false); // colors 45 | $docPartial = $patternTypeDash."-".$docDash; 46 | 47 | // default vars 48 | $patternSourceDir = Config::getOption("patternSourceDir"); 49 | 50 | // parse data 51 | $text = file_get_contents($patternSourceDir.DIRECTORY_SEPARATOR.$pathName); 52 | list($yaml,$markdown) = Documentation::parse($text); 53 | 54 | // grab the title and unset it from the yaml so it doesn't get duped in the meta 55 | if (isset($yaml["title"])) { 56 | $title = $yaml["title"]; 57 | unset($yaml["title"]); 58 | } 59 | 60 | // figure out if this is a pattern subtype 61 | $patternSubtypeDoc = false; 62 | if ($depth == 1) { 63 | // go through all of the directories to see if this one matches our doc 64 | foreach (glob($patternSourceDir.DIRECTORY_SEPARATOR.$patternType.DIRECTORY_SEPARATOR."*",GLOB_ONLYDIR) as $dir) { 65 | $dir = str_replace($patternSourceDir.DIRECTORY_SEPARATOR.$patternType.DIRECTORY_SEPARATOR,"",$dir); 66 | if ($dir == $doc) { 67 | $patternSubtypeDoc = true; 68 | break; 69 | } 70 | } 71 | 72 | } 73 | 74 | $category = ($patternSubtypeDoc) ? "patternSubtype" : "pattern"; 75 | $patternStoreKey = ($patternSubtypeDoc) ? $docPartial."-plsubtype" : $docPartial; 76 | 77 | $patternStoreData = array("category" => $category, 78 | "desc" => trim($markdown), 79 | "descExists" => true, 80 | "meta" => $yaml, 81 | "full" => $doc); 82 | 83 | // can set `title: My Cool Pattern` instead of lifting from file name 84 | if (isset($title)) { 85 | $patternStoreData["nameClean"] = $title; 86 | } 87 | 88 | $availableKeys = [ 89 | 'state', // can use `state: inprogress` instead of `button@inprogress.mustache` 90 | 'hidden', // setting to `true`, removes from menu and viewall, which is same as adding `_` prefix 91 | 'noviewall', // setting to `true`, removes from view alls but keeps in menu, which is same as adding `-` prefix 92 | 'order', // @todo implement order 93 | 'tags', // not implemented, awaiting spec approval and integration with styleguide kit. adding to be in sync with Node version. 94 | 'links', // not implemented, awaiting spec approval and integration with styleguide kit. adding to be in sync with Node version. 95 | ]; 96 | 97 | foreach ($availableKeys as $key) { 98 | if (isset($yaml[$key])) { 99 | $patternStoreData[$key] = $yaml[$key]; 100 | } 101 | } 102 | 103 | // if the pattern data store already exists make sure this data overwrites it 104 | $patternStoreData = (PatternData::checkOption($patternStoreKey)) ? array_replace_recursive(PatternData::getOption($patternStoreKey),$patternStoreData) : $patternStoreData; 105 | PatternData::setOption($patternStoreKey, $patternStoreData); 106 | 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Rules/PatternInfoListItemsRule.php: -------------------------------------------------------------------------------- 1 | depthProp = 3; // 3 means that depth won't be checked 29 | $this->extProp = "json||yaml||yml"; 30 | $this->isDirProp = false; 31 | $this->isFileProp = true; 32 | $this->searchProp = ".listitems."; 33 | $this->ignoreProp = ""; 34 | 35 | } 36 | 37 | public function run($depth, $ext, $path, $pathName, $name) { 38 | 39 | // load default vars 40 | $patternTypeDash = PatternData::getPatternTypeDash(); 41 | 42 | // set-up the names, $name == foo.listitems.json 43 | $pattern = str_replace(".listitems.".$ext,"",$name); // foo 44 | $patternDash = $this->getPatternName($pattern,false); // foo 45 | $patternPartial = $patternTypeDash."-".$patternDash; // atoms-foo 46 | 47 | $patternStoreData = array("category" => "pattern"); 48 | 49 | $data = Data::getListItems("_patterns/".$pathName,$ext); 50 | $patternStoreData["listItems"] = $data; 51 | 52 | // create a key for the data store 53 | $patternStoreKey = $patternPartial; 54 | 55 | // if the pattern data store already exists make sure it is merged and overwrites this data 56 | $patternStoreData = (PatternData::checkOption($patternStoreKey)) ? array_replace_recursive(PatternData::getOption($patternStoreKey),$patternStoreData) : $patternStoreData; 57 | PatternData::setOption($patternStoreKey, $patternStoreData); 58 | 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Rules/PatternInfoRule.php: -------------------------------------------------------------------------------- 1 | depthProp = 3; // 3 means that depth won't be checked 29 | $this->extProp = "json||yaml||yml"; 30 | $this->isDirProp = false; 31 | $this->isFileProp = true; 32 | $this->searchProp = ""; 33 | $this->ignoreProp = "~||listitems"; 34 | 35 | } 36 | 37 | public function run($depth, $ext, $path, $pathName, $name) { 38 | 39 | // load default vars 40 | $patternTypeDash = PatternData::getPatternTypeDash(); 41 | 42 | // set-up the names, $name == foo.json 43 | $pattern = str_replace(".".$ext,"",$name); // foo 44 | $patternDash = $this->getPatternName($pattern,false); // foo 45 | $patternPartial = $patternTypeDash."-".$patternDash; // atoms-foo 46 | 47 | $patternStoreData = array("category" => "pattern"); 48 | 49 | $file = file_get_contents(Config::getOption("patternSourceDir")."/".$pathName); 50 | 51 | if ($ext == "json") { 52 | $data = json_decode($file,true); 53 | if ($jsonErrorMessage = JSON::hasError()) { 54 | JSON::lastErrorMsg($name,$jsonErrorMessage,$data); 55 | } 56 | } else { 57 | 58 | try { 59 | $data = YAML::parse($file); 60 | } catch (ParseException $e) { 61 | printf("unable to parse ".$pathNameClean.": %s..\n", $e->getMessage()); 62 | } 63 | 64 | // single line of text won't throw a YAML error. returns as string 65 | if (gettype($data) == "string") { 66 | $data = array(); 67 | } 68 | 69 | } 70 | 71 | $patternStoreData["data"] = $data; 72 | 73 | // create a key for the data store 74 | $patternStoreKey = $patternPartial; 75 | 76 | // if the pattern data store already exists make sure it is merged and overwrites this data 77 | $patternStoreData = (PatternData::checkOption($patternStoreKey)) ? array_replace_recursive(PatternData::getOption($patternStoreKey),$patternStoreData) : $patternStoreData; 78 | PatternData::setOption($patternStoreKey, $patternStoreData); 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Rules/PatternRule.php: -------------------------------------------------------------------------------- 1 | depthProp = 3; // 3 means that depth won't be checked 26 | $this->extProp = Config::getOption("patternExtension"); 27 | $this->isDirProp = false; 28 | $this->isFileProp = true; 29 | $this->searchProp = ""; 30 | $this->ignoreProp = ""; 31 | 32 | } 33 | 34 | public function run($depth, $ext, $path, $pathName, $name) { 35 | 36 | // load default vars 37 | $patternSubtype = PatternData::getPatternSubtype(); 38 | $patternSubtypeClean = PatternData::getPatternSubtypeClean(); 39 | $patternSubtypeDash = PatternData::getPatternSubtypeDash(); 40 | $patternType = PatternData::getPatternType(); 41 | $patternTypeClean = PatternData::getPatternTypeClean(); 42 | $patternTypeDash = PatternData::getPatternTypeDash(); 43 | $dirSep = PatternData::getDirSep(); 44 | $frontMeta = PatternData::getFrontMeta(); 45 | 46 | // should this pattern get rendered? 47 | $hidden = ($name[0] == "_"); 48 | $noviewall = ($name[0] == "-"); 49 | 50 | // set-up the names 51 | $patternFull = in_array($name[0],$frontMeta) ? substr($name,1) : $name; // 00-colors.mustache 52 | $pattern = str_replace(".".$this->extProp,"",$patternFull); // 00-colors 53 | $patternState = ""; 54 | 55 | // check for pattern state 56 | if (strpos($pattern,"@") !== false) { 57 | $patternBits = explode("@",$pattern,2); 58 | $pattern = $patternBits[0]; 59 | $patternState = $patternBits[1]; 60 | } 61 | 62 | // finish setting up vars 63 | $patternDash = $this->getPatternName($pattern,false); // colors 64 | $patternClean = str_replace("-"," ",$patternDash); // colors (dashes replaced with spaces) 65 | $patternPartial = $patternTypeDash."-".$patternDash; // atoms-colors 66 | $patternPath = str_replace(".".$this->extProp,"",$pathName); // 00-atoms/01-global/00-colors 67 | $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-01-global-00-colors (file path) 68 | 69 | // create a key for the data store 70 | $patternStoreKey = $patternPartial; 71 | 72 | // collect the data 73 | $patternStoreData = array("category" => "pattern", 74 | "name" => $pattern, 75 | "partial" => $patternPartial, 76 | "nameDash" => $patternDash, 77 | "nameClean" => $patternClean, 78 | "type" => $patternType, 79 | "typeDash" => $patternTypeDash, 80 | "breadcrumb" => array("patternType" => $patternTypeClean), 81 | "state" => $patternState, 82 | "hidden" => $hidden, 83 | "noviewall" => $noviewall, 84 | "depth" => $depth, 85 | "ext" => $ext, 86 | "path" => $path, 87 | "pathName" => $patternPath, 88 | "pathDash" => $patternPathDash, 89 | "isDir" => $this->isDirProp, 90 | "isFile" => $this->isFileProp, 91 | "partialViewDescAdditions" => array(), 92 | "partialViewExampleAdditions" => array(), 93 | "extraHolder" => array(), 94 | "extraOutput" => array()); 95 | 96 | // add any subtype info if necessary 97 | if ($depth > 1) { 98 | $patternStoreData["subtype"] = $patternSubtype; 99 | $patternStoreData["subtypeDash"] = $patternSubtypeDash; 100 | $patternStoreData["breadcrumb"] = array("patternType" => $patternTypeClean, "patternSubtype" => $patternSubtypeClean); 101 | } 102 | 103 | // if the pattern data store already exists make sure it is merged and overwrites this data 104 | $patternStoreData = (PatternData::checkOption($patternStoreKey)) ? array_replace_recursive($patternStoreData, PatternData::getOption($patternStoreKey)) : $patternStoreData; 105 | PatternData::setOption($patternStoreKey, $patternStoreData); 106 | 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Rules/PatternSubtypeRule.php: -------------------------------------------------------------------------------- 1 | depthProp = 1; 25 | $this->extProp = ""; 26 | $this->isDirProp = true; 27 | $this->isFileProp = false; 28 | $this->searchProp = ""; 29 | $this->ignoreProp = ""; 30 | 31 | } 32 | 33 | public function run($depth, $ext, $path, $pathName, $name) { 34 | 35 | // load default vars 36 | $patternType = PatternData::getPatternType(); 37 | $patternTypeDash = PatternData::getPatternTypeDash(); 38 | $patternTypeClean = PatternData::getPatternTypeClean(); 39 | $dirSep = PatternData::getDirSep(); 40 | 41 | // set-up the names 42 | $patternSubtype = $name; // 02-blocks 43 | $patternSubtypeDash = $this->getPatternName($name,false); // blocks 44 | $patternSubtypeClean = str_replace("-"," ",$patternSubtypeDash); // blocks (dashes replaced with spaces) 45 | $patternSubtypePath = $pathName; // 00-atoms/02-blocks 46 | $patternSubtypePathDash = str_replace($dirSep,"-",$patternSubtypePath); // 00-atoms-02-blocks (file path) 47 | 48 | $patternPartial = "viewall-".$patternTypeDash."-".$patternSubtypeDash; 49 | 50 | // create a key for the data store 51 | $patternStoreKey = $patternTypeDash."-".$patternSubtypeDash."-plsubtype"; 52 | 53 | // collect the data 54 | $patternStoreData = array("category" => "patternSubtype", 55 | "name" => $patternSubtype, 56 | "nameDash" => $patternSubtypeDash, 57 | "nameClean" => $patternSubtypeClean, 58 | "partial" => $patternPartial, 59 | "type" => $patternType, 60 | "typeDash" => $patternTypeDash, 61 | "breadcrumb" => array("patternType" => $patternTypeClean), 62 | "depth" => $depth, 63 | "ext" => $ext, 64 | "path" => $path, 65 | "pathName" => $patternSubtypePath, 66 | "pathDash" => $patternSubtypePathDash, 67 | "isDir" => $this->isDirProp, 68 | "isFile" => $this->isFileProp); 69 | 70 | // if the pattern data store already exists make sure it is merged and overwrites this data 71 | $patternStoreData = (PatternData::checkOption($patternStoreKey)) ? array_replace_recursive(PatternData::getOption($patternStoreKey),$patternStoreData) : $patternStoreData; 72 | PatternData::setOption($patternStoreKey, $patternStoreData); 73 | 74 | // starting a new set of pattern types. it might not have any pattern subtypes 75 | PatternData::setPatternSubtype($patternSubtype); 76 | PatternData::setPatternSubtypeClean($patternSubtypeClean); 77 | PatternData::setPatternSubtypeDash($patternSubtypeDash); 78 | PatternData::setPatternSubtypeSet(true); 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Rules/PatternTypeRule.php: -------------------------------------------------------------------------------- 1 | depthProp = 0; 25 | $this->extProp = ""; 26 | $this->isDirProp = true; 27 | $this->isFileProp = false; 28 | $this->searchProp = ""; 29 | $this->ignoreProp = ""; 30 | 31 | } 32 | 33 | public function run($depth, $ext, $path, $pathName, $name) { 34 | 35 | // load default vars 36 | $dirSep = PatternData::getDirSep(); 37 | 38 | // set-up the names 39 | $patternType = $name; // 00-atoms 40 | $patternTypeDash = $this->getPatternName($name,false); // atoms 41 | $patternTypeClean = str_replace("-"," ",$patternTypeDash); // atoms (dashes replaced with spaces) 42 | 43 | $patternTypePath = $pathName; // 00-atoms/02-blocks 44 | $patternTypePathDash = str_replace($dirSep,"-",$patternTypePath); // 00-atoms-02-blocks (file path) 45 | 46 | // create a key for the data store 47 | $patternStoreKey = $patternTypeDash."-pltype"; 48 | 49 | // add a new patternType to the nav 50 | $patternData = array("category" => "patternType", 51 | "name" => $patternType, 52 | "nameDash" => $patternTypeDash, 53 | "nameClean" => $patternTypeClean, 54 | "depth" => $depth, 55 | "ext" => $ext, 56 | "path" => $path, 57 | "pathName" => $patternTypePath, 58 | "pathDash" => $patternTypePathDash, 59 | "isDir" => $this->isDirProp, 60 | "isFile" => $this->isFileProp); 61 | 62 | PatternData::setOption($patternStoreKey,$patternData); 63 | 64 | // starting a new set of pattern types. it might not have any pattern subtypes 65 | PatternData::setPatternType($patternType); 66 | PatternData::setPatternTypeClean($patternTypeClean); 67 | PatternData::setPatternTypeDash($patternTypeDash); 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/PatternLab/PatternData/Rules/PseudoPatternRule.php: -------------------------------------------------------------------------------- 1 | depthProp = 3; // 3 means that depth won't be checked 29 | $this->extProp = "json||yaml||yml"; 30 | $this->isDirProp = false; 31 | $this->isFileProp = true; 32 | $this->searchProp = "~"; 33 | $this->ignoreProp = ""; 34 | 35 | } 36 | 37 | public function run($depth, $ext, $path, $pathName, $name) { 38 | 39 | // load default vars 40 | $patternSubtype = PatternData::getPatternSubtype(); 41 | $patternSubtypeClean = PatternData::getPatternSubtypeClean(); 42 | $patternSubtypeDash = PatternData::getPatternSubtypeDash(); 43 | $patternType = PatternData::getPatternType(); 44 | $patternTypeClean = PatternData::getPatternTypeClean(); 45 | $patternTypeDash = PatternData::getPatternTypeDash(); 46 | $dirSep = PatternData::getDirSep(); 47 | $frontMeta = PatternData::getFrontMeta(); 48 | 49 | // should this pattern get rendered? 50 | $hidden = ($name[0] == "_"); 51 | $noviewall = ($name[0] == "-"); 52 | 53 | // set-up the names 54 | $patternFull = in_array($name[0],$frontMeta) ? substr($name,1) : $name; // 00-colors~foo.mustache 55 | $patternState = ""; 56 | 57 | // check for pattern state 58 | if (strpos($patternFull,"@") !== false) { 59 | $patternBits = explode("@",$patternFull,2); 60 | $patternState = str_replace(".".$ext,"",$patternBits[1]); 61 | $patternFull = preg_replace("/@(.*?)\./",".",$patternFull); 62 | } 63 | 64 | // finish setting up vars 65 | $patternBits = explode("~",$patternFull); 66 | $patternBase = $patternBits[0].".".Config::getOption("patternExtension"); // 00-homepage.mustache 67 | $patternBaseDash = $this->getPatternName($patternBits[0],false); // homepage 68 | $patternBaseOrig = $patternTypeDash."-".$patternBaseDash; // pages-homepage 69 | $patternBaseData = $patternBits[0].".".$ext; // 00-homepage.json 70 | $stripJSON = str_replace(".".$ext,"",$patternBits[1]); 71 | $patternBitClean = preg_replace("/@(.*?)/","",$patternBits[0]); 72 | $pattern = $patternBitClean."-".$stripJSON; // 00-homepage-00-emergency 73 | $patternInt = $patternBitClean."-".$this->getPatternName($stripJSON, false); // 00-homepage-emergency 74 | $patternDash = $this->getPatternName($patternInt,false); // homepage-emergency 75 | $patternClean = str_replace("-"," ",$patternDash); // homepage emergency 76 | $patternPartial = $patternTypeDash."-".$patternDash; // pages-homepage-emergency 77 | $patternPath = str_replace(".".$ext,"",str_replace("~","-",$pathName)); // 00-atoms/01-global/00-colors 78 | $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-01-global-00-colors (file path) 79 | 80 | // check the original pattern path. if it doesn't exist make a guess 81 | $patternPathOrig = PatternData::getPatternOption($patternBaseOrig,"pathName"); // 04-pages/00-homepage 82 | $patternPathOrigDash = PatternData::getPatternOption($patternBaseOrig,"pathDash"); // 04-pages-00-homepage 83 | if (!$patternPathOrig) { 84 | $patternPathOrigBits = explode("~",$pathName); 85 | $patternPathOrig = $patternPathOrigBits[0]; // 04-pages/00-homepage 86 | $patternPathOrigDash = str_replace($dirSep,"-",$patternPathOrig); // 04-pages-00-homepage 87 | } 88 | 89 | // create a key for the data store 90 | $patternStoreKey = $patternPartial; 91 | 92 | // collect the data 93 | $patternStoreData = array("category" => "pattern", 94 | "name" => $pattern, 95 | "partial" => $patternPartial, 96 | "nameDash" => $patternDash, 97 | "nameClean" => $patternClean, 98 | "type" => $patternType, 99 | "typeDash" => $patternTypeDash, 100 | "breadcrumb" => array("patternType" => $patternTypeClean), 101 | "state" => $patternState, 102 | "hidden" => $hidden, 103 | "noviewall" => $noviewall, 104 | "depth" => $depth, 105 | "ext" => $ext, 106 | "path" => $path, 107 | "pathName" => $patternPath, 108 | "pathDash" => $patternPathDash, 109 | "isDir" => $this->isDirProp, 110 | "isFile" => $this->isFileProp, 111 | "pseudo" => true, 112 | "original" => $patternBaseOrig, 113 | "pathOrig" => $patternPathOrig, 114 | "pathOrigDash" => $patternPathOrigDash); 115 | 116 | // add any subtype info if necessary 117 | if ($depth > 1) { 118 | $patternStoreData["subtype"] = $patternSubtype; 119 | $patternStoreData["subtypeDash"] = $patternSubtypeDash; 120 | $patternStoreData["breadcrumb"] = array("patternType" => $patternTypeClean, "patternSubtype" => $patternSubtypeClean); 121 | } 122 | 123 | $patternDataBase = array(); 124 | if (file_exists(Config::getOption("patternSourceDir").DIRECTORY_SEPARATOR.$path.DIRECTORY_SEPARATOR.$patternBaseData)) { 125 | $data = file_get_contents(Config::getOption("patternSourceDir").DIRECTORY_SEPARATOR.$path.DIRECTORY_SEPARATOR.$patternBaseData); 126 | if ($ext == "json") { 127 | $patternDataBase = json_decode($data,true); 128 | if ($jsonErrorMessage = JSON::hasError()) { 129 | JSON::lastErrorMsg($patternBaseJSON,$jsonErrorMessage,$data); 130 | } 131 | } else { 132 | 133 | try { 134 | $patternDataBase = YAML::parse($data); 135 | } catch (ParseException $e) { 136 | printf("unable to parse ".$pathNameClean.": %s..\n", $e->getMessage()); 137 | } 138 | 139 | // single line of text won't throw a YAML error. returns as string 140 | if (gettype($patternDataBase) == "string") { 141 | $patternDataBase = array(); 142 | } 143 | 144 | } 145 | 146 | } 147 | 148 | // get the data for the pseudo-pattern 149 | $data = file_get_contents(Config::getOption("patternSourceDir").DIRECTORY_SEPARATOR.$pathName); 150 | if ($ext == "json") { 151 | $patternData = json_decode($data,true); 152 | if ($jsonErrorMessage = JSON::hasError()) { 153 | JSON::lastErrorMsg($name,$jsonErrorMessage,$data); 154 | } 155 | } else { 156 | 157 | try { 158 | $patternData = YAML::parse($data); 159 | } catch (ParseException $e) { 160 | printf("unable to parse ".$pathNameClean.": %s..\n", $e->getMessage()); 161 | } 162 | 163 | // single line of text won't throw a YAML error. returns as string 164 | if (gettype($patternData) == "string") { 165 | $patternData = array(); 166 | } 167 | 168 | } 169 | 170 | // make sure the pattern data is an array before merging the data 171 | $patternStoreData["data"] = is_array($patternData) ? array_replace_recursive($patternDataBase, $patternData) : $patternDataBase; 172 | 173 | // if the pattern data store already exists make sure it is merged and overwrites this data 174 | if (PatternData::checkOption($patternStoreKey)) { 175 | $existingData = PatternData::getOption($patternStoreKey); 176 | if (array_key_exists('nameClean', $existingData)) { 177 | // don't overwrite nameClean 178 | unset($patternStoreData['nameClean']); 179 | } 180 | $patternStoreData = array_replace_recursive($existingData, $patternStoreData); 181 | } 182 | PatternData::setOption($patternStoreKey, $patternStoreData); 183 | 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /src/PatternLab/PatternEngine.php: -------------------------------------------------------------------------------- 1 | test($patternExtension)) { 42 | self::$instance = $rule; 43 | $found = true; 44 | break; 45 | } 46 | } 47 | 48 | if (!$found) { 49 | Console::writeError("the supplied pattern extension didn't match a pattern loader rule. check your config..."); 50 | } 51 | 52 | } 53 | 54 | /** 55 | * Load all of the rules related to Pattern Engines. They're located in the plugin dir 56 | */ 57 | public static function loadRules() { 58 | 59 | // default var 60 | $configDir = Config::getOption("configDir"); 61 | 62 | // make sure the pattern engine data exists 63 | if (file_exists($configDir."/patternengines.json")) { 64 | 65 | // get pattern engine list data 66 | $patternEngineList = json_decode(file_get_contents($configDir."/patternengines.json"), true); 67 | 68 | // get the pattern engine info 69 | foreach ($patternEngineList["patternengines"] as $patternEngineName) { 70 | 71 | self::$rules[] = new $patternEngineName(); 72 | 73 | } 74 | 75 | } else { 76 | Console::writeError("The pattern engines list isn't available in ".$configDir."..."); 77 | } 78 | 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/PatternLab/PatternEngine/Loader.php: -------------------------------------------------------------------------------- 1 | basePath; 32 | 33 | } 34 | 35 | public function test($patternExtension) { 36 | 37 | return ($this->engineProp === $patternExtension); 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/PatternLab/PatternEngine/Util.php: -------------------------------------------------------------------------------- 1 | patternPaths = $options["patternPaths"]; 27 | 28 | } 29 | 30 | /** 31 | * Helper function to find and replace the given parameters in a particular partial before handing it back to Mustache 32 | * @param {String} the file contents 33 | * @param {Array} an array of paramters to match 34 | * 35 | * @return {String} the modified file contents 36 | */ 37 | public function findReplaceParameters($fileData, $parameters) { 38 | $numbers = array("zero","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); 39 | foreach ($parameters as $k => $v) { 40 | if (is_array($v)) { 41 | if (preg_match('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k.'[\s]*)}}/s',$fileData,$matches)) { 42 | if (isset($matches[2])) { 43 | $partialData = ""; 44 | foreach ($v as $v2) { 45 | $partialData .= $this->findReplaceParameters($matches[2], $v2); 46 | } 47 | $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s',$partialData,$fileData); 48 | } 49 | } 50 | } else if ($v == "true") { 51 | $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} 52 | $fileData = preg_replace('/{{\^([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} 53 | } else if ($v == "false") { 54 | $fileData = preg_replace('/{{\^([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} 55 | $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} 56 | } else if ($k == "listItems") { 57 | $v = ((int)$v != 0) && ((int)$v < 13) ? $numbers[$v] : $v; 58 | if (($v != "zero") && in_array($v,$numbers)) { 59 | $fileData = preg_replace('/{{\#([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{# listItems.'.$v.' }}',$fileData); 60 | $fileData = preg_replace('/{{\/([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{/ listItems.'.$v.' }}',$fileData); 61 | } 62 | } else { 63 | $fileData = preg_replace('/{{{([\s]*'.$k.'[\s]*)}}}/', $v, $fileData); // {{{ asdf }}} 64 | $fileData = preg_replace('/{{([\s]*'.$k.'[\s]*)}}/', htmlspecialchars($v), $fileData); // escaped {{ asdf }} 65 | } 66 | } 67 | return $fileData; 68 | } 69 | 70 | /** 71 | * Helper function for getting a Mustache template file name. 72 | * @param {String} the pattern type for the pattern 73 | * @param {String} the pattern sub-type 74 | * 75 | * @return {Array} an array of rendered partials that match the given path 76 | */ 77 | public function getFileName($name,$ext) { 78 | 79 | $fileName = ""; 80 | $dirSep = DIRECTORY_SEPARATOR; 81 | 82 | // test to see what kind of path was supplied 83 | $posDash = strpos($name,"-"); 84 | $posSlash = strpos($name,$dirSep); 85 | 86 | if (($posSlash === false) && ($posDash !== false)) { 87 | $fileName = $this->getPatternFileName($name); 88 | } else { 89 | $fileName = $name; 90 | } 91 | 92 | if (substr($fileName, 0 - strlen($ext)) !== $ext) { 93 | $fileName .= $ext; 94 | } 95 | 96 | return $fileName; 97 | 98 | } 99 | 100 | /** 101 | * Helper function to return the pattern file name 102 | * @param {String} the name of the pattern 103 | * 104 | * @return {String} the file path to the pattern 105 | */ 106 | public function getPatternFileName($name) { 107 | 108 | $patternFileName = ""; 109 | 110 | list($patternType,$pattern) = $this->getPatternInfo($name); 111 | 112 | // see if the pattern is an exact match for patternPaths. if not iterate over patternPaths to find a likely match 113 | if (isset($this->patternPaths[$patternType][$pattern])) { 114 | $patternFileName = $this->patternPaths[$patternType][$pattern]; 115 | } else if (isset($this->patternPaths[$patternType])) { 116 | foreach($this->patternPaths[$patternType] as $patternMatchKey=>$patternMatchValue) { 117 | $pos = strpos($patternMatchKey,$pattern); 118 | if ($pos !== false) { 119 | $patternFileName = $patternMatchValue; 120 | break; 121 | } 122 | } 123 | } 124 | 125 | return $patternFileName; 126 | 127 | } 128 | 129 | /** 130 | * Helper function to return the parts of a partial name 131 | * @param {String} the name of the partial 132 | * 133 | * @return {Array} the pattern type and the name of the pattern 134 | */ 135 | public function getPatternInfo($name) { 136 | 137 | $patternBits = explode("-",$name); 138 | 139 | $i = 1; 140 | $k = 2; 141 | $c = count($patternBits); 142 | $patternType = $patternBits[0]; 143 | while (!isset($this->patternPaths[$patternType]) && ($i < $c)) { 144 | $patternType .= "-".$patternBits[$i]; 145 | $i++; 146 | $k++; 147 | } 148 | 149 | $patternBits = explode("-",$name,$k); 150 | $pattern = $patternBits[count($patternBits)-1]; 151 | 152 | return array($patternType, $pattern); 153 | 154 | } 155 | 156 | /** 157 | * Helper function for finding if a partial name has style modifier or parameters 158 | * @param {String} the pattern name 159 | * 160 | * @return {Array} an array containing just the partial name, a style modifier, and any parameters 161 | */ 162 | public function getPartialInfo($partial) { 163 | 164 | $styleModifier = array(); 165 | $parameters = array(); 166 | 167 | if (strpos($partial, "(") !== false) { 168 | $partialBits = explode("(",$partial,2); 169 | $partial = trim($partialBits[0]); 170 | $parametersString = substr($partialBits[1],0,(strlen($partialBits[1]) - strlen(strrchr($partialBits[1],")")))); 171 | $parameters = $this->parseParameters($parametersString); 172 | } 173 | 174 | if (strpos($partial, ":") !== false) { 175 | $partialBits = explode(":",$partial,2); 176 | $partial = $partialBits[0]; 177 | $styleModifier = $partialBits[1]; 178 | if (strpos($styleModifier, "|") !== false) { 179 | $styleModifierBits = explode("|",$styleModifier); 180 | $styleModifier = join(" ",$styleModifierBits); 181 | } 182 | $styleModifier = array("styleModifier" => $styleModifier); 183 | } 184 | 185 | return array($partial,$styleModifier,$parameters); 186 | 187 | } 188 | 189 | /** 190 | * Helper function to parse the parameters and return them as an array 191 | * @param {String} the parameter string 192 | * 193 | * @return {Array} the keys and values for the parameters 194 | */ 195 | private function parseParameters($string) { 196 | 197 | $parameters = array(); 198 | $arrayParameters = array(); 199 | $arrayOptions = array(); 200 | $betweenSQuotes = false; 201 | $betweenDQuotes = false; 202 | $inKey = true; 203 | $inValue = false; 204 | $inArray = false; 205 | $inOption = false; 206 | $char = ""; 207 | $buffer = ""; 208 | $keyBuffer = ""; 209 | $arrayKeyBuffer = ""; 210 | $strLength = strlen($string); 211 | 212 | for ($i = 0; $i < $strLength; $i++) { 213 | 214 | $previousChar = $char; 215 | $char = $string[$i]; 216 | 217 | if ($inKey && !$betweenDQuotes && !$betweenSQuotes && (($char == "\"") || ($char == "'"))) { 218 | // if inKey, a quote, and betweenQuotes is false ignore quote, set betweenQuotes to true and empty buffer to kill spaces 219 | ($char == "\"") ? ($betweenDQuotes = true) : ($betweenSQuotes = true); 220 | } else if ($inKey && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'"))) && ($previousChar == "\\")) { 221 | // if inKey, a quote, betweenQuotes is true, and previous character is \ add to buffer 222 | $buffer .= $char; 223 | } else if ($inKey && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'")))) { 224 | // if inKey, a quote, betweenQuotes is true set betweenQuotes to false, save as key buffer, empty buffer set inKey false 225 | $keyBuffer = $buffer; 226 | $buffer = ""; 227 | $inKey = false; 228 | $betweenSQuotes = false; 229 | $betweenDQuotes = false; 230 | } else if ($inKey && !$betweenDQuotes && !$betweenSQuotes && ($char == ":")) { 231 | // if inKey, a colon, betweenQuotes is false, save as key buffer, empty buffer, set inKey false set inValue true 232 | $keyBuffer = $buffer; 233 | $buffer = ""; 234 | $inKey = false; 235 | $inValue = true; 236 | } else if ($inKey) { 237 | // if inKey add to buffer 238 | $buffer .= $char; 239 | } else if (!$inKey && !$inValue && ($char == ":")) { 240 | // if inKey is false, inValue false, and a colon set inValue true 241 | $inValue = true; 242 | } else if ($inValue && !$inArray && !$betweenDQuotes && !$betweenSQuotes && ($char == "[")) { 243 | // if inValue, outside quotes, and find a bracket set inArray to true and add to array buffer 244 | $inArray = true; 245 | $inValue = false; 246 | $arrayKeyBuffer = trim($keyBuffer); 247 | } else if ($inArray && !$betweenDQuotes && !$betweenSQuotes && ($char == "]")) { 248 | // if inValue, outside quotes, and find a bracket set inArray to true and add to array buffer 249 | $inArray = false; 250 | $parameters[$arrayKeyBuffer] = $arrayParameters; 251 | $arrayParameters = array(); 252 | } else if ($inArray && !$inOption && !$betweenDQuotes && !$betweenSQuotes && ($char == "{")) { 253 | $inOption = true; 254 | $inKey = true; 255 | } else if ($inArray && $inOption && !$betweenDQuotes && !$betweenSQuotes && ($char == "}")) { 256 | $inOption = false; 257 | $inValue = false; 258 | $inKey = false; 259 | $arrayParameters[] = $arrayOptions; 260 | $arrayOptions = array(); 261 | } else if ($inValue && !$betweenDQuotes && !$betweenSQuotes && (($char == "\"") || ($char == "'"))) { 262 | // if inValue, a quote, and betweenQuote is false set betweenQuotes to true and empty buffer to kill spaces 263 | ($char == "\"") ? ($betweenDQuotes = true) : ($betweenSQuotes = true); 264 | } else if ($inValue && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'"))) && ($previousChar == "\\")) { 265 | // if inValue, a quote, betweenQuotes is true, and previous character is \ add to buffer 266 | $buffer .= $char; 267 | } else if ($inValue && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'")))) { 268 | // if inValue, a quote, betweenQuotes is true set betweenQuotes to false, save to parameters array, empty buffer, set inValue false 269 | $buffer = str_replace("\\\"","\"",$buffer); 270 | $buffer = str_replace('\\\'','\'',$buffer); 271 | if ($inArray) { 272 | $arrayOptions[trim($keyBuffer)] = trim($buffer); 273 | } else { 274 | $parameters[trim($keyBuffer)] = trim($buffer); 275 | } 276 | $buffer = ""; 277 | $inValue = false; 278 | $betweenSQuotes = false; 279 | $betweenDQuotes = false; 280 | } else if ($inValue && !$betweenDQuotes && !$betweenSQuotes && ($char == ",")) { 281 | // if inValue, a comman, betweenQuotes is false, save to parameters array, empty buffer, set inValue false, set inKey true 282 | if ($inArray) { 283 | $arrayOptions[trim($keyBuffer)] = trim($buffer); 284 | } else { 285 | $parameters[trim($keyBuffer)] = trim($buffer); 286 | } 287 | $buffer = ""; 288 | $inValue = false; 289 | $inKey = true; 290 | } else if ($inValue && (($i + 1) == $strLength)) { 291 | // if inValue and end of the string add to buffer, save to parameters array 292 | $buffer .= $char; 293 | if ($inArray) { 294 | $arrayOptions[trim($keyBuffer)] = trim($buffer); 295 | } else { 296 | $parameters[trim($keyBuffer)] = trim($buffer); 297 | } 298 | } else if ($inValue) { 299 | // if inValue add to buffer 300 | $buffer .= $char; 301 | } else if (!$inValue && !$inKey && ($char == ",")) { 302 | // if inValue is false, inKey false, and a comma set inKey true 303 | if ($inArray && !$inOption) { 304 | // don't do anything 305 | } else { 306 | $inKey = true; 307 | } 308 | 309 | } 310 | } 311 | 312 | return $parameters; 313 | 314 | } 315 | 316 | 317 | } 318 | -------------------------------------------------------------------------------- /src/PatternLab/Saying.php: -------------------------------------------------------------------------------- 1 | <(((º>", 70 | "@}~}~~~", 71 | "(>'.')> (>'.')> (>'.')> ", 72 | "\(^-^)/", 73 | "you've been at this awhile; perhaps it's time for a walk outside?" 74 | ); 75 | 76 | // grab user sayings 77 | $userSayings = Config::getOption("sayings"); 78 | if (is_array($userSayings)) { 79 | $sayings = array_merge($sayings, $userSayings); 80 | } 81 | 82 | // i just didn't want to indent the crap above 83 | $this->sayings = $sayings; 84 | 85 | } 86 | 87 | /** 88 | * Randomly prints a saying after the generate is complete 89 | */ 90 | public function say() { 91 | 92 | // set a color 93 | $colors = array("ok","options","info","warning"); 94 | $randomNumber = rand(0,count($colors)-1); 95 | $color = (isset($colors[$randomNumber])) ? $colors[$randomNumber] : "desc"; 96 | 97 | // set a 1 in 3 chance that a saying is printed 98 | $randomNumber = rand(0,(count($this->sayings)-1)*3); 99 | if (isset($this->sayings[$randomNumber])) { 100 | Console::writeLine("<".$color.">".$this->sayings[$randomNumber]."..."); 101 | } 102 | 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/PatternLab/Template.php: -------------------------------------------------------------------------------- 1 | ".Console::getHumanReadablePath($styleguideKitPath)."? you can fix this in ./config/config.yml by editing styleguideKitPath..."); 51 | } 52 | 53 | // load pattern-lab's resources 54 | $partialPath = $styleguideKitPath.DIRECTORY_SEPARATOR."views".DIRECTORY_SEPARATOR."partials"; 55 | $generalHeaderPath = $partialPath.DIRECTORY_SEPARATOR."general-header.".$patternExtension; 56 | $generalFooterPath = $partialPath.DIRECTORY_SEPARATOR."general-footer.".$patternExtension; 57 | self::$htmlHead = (file_exists($generalHeaderPath)) ? file_get_contents($generalHeaderPath) : ""; 58 | self::$htmlFoot = (file_exists($generalFooterPath)) ? file_get_contents($generalFooterPath) : ""; 59 | 60 | // gather the user-defined header and footer information 61 | $patternHeadPath = $metaDir.DIRECTORY_SEPARATOR."_00-head.".$patternExtension; 62 | $patternFootPath = $metaDir.DIRECTORY_SEPARATOR."_01-foot.".$patternExtension; 63 | self::$patternHead = (file_exists($patternHeadPath)) ? file_get_contents($patternHeadPath) : ""; 64 | self::$patternFoot = (file_exists($patternFootPath)) ? file_get_contents($patternFootPath) : ""; 65 | 66 | // add the filesystemLoader 67 | $patternEngineBasePath = PatternEngine::getInstance()->getBasePath(); 68 | $filesystemLoaderClass = $patternEngineBasePath."\Loaders\FilesystemLoader"; 69 | 70 | $options = array(); 71 | $options["templatePath"] = $styleguideKitPath.DIRECTORY_SEPARATOR."views"; 72 | $options["partialsPath"] = $options["templatePath"].DIRECTORY_SEPARATOR."partials"; 73 | 74 | self::$filesystemLoader = new $filesystemLoaderClass($options); 75 | 76 | $stringLoaderClass = $patternEngineBasePath."\Loaders\StringLoader"; 77 | self::$stringLoader = new $stringLoaderClass(); 78 | 79 | // i can't remember why i chose to implement the pattern loader directly in classes 80 | // i figure i had a good reason which is why it's not showing up here 81 | 82 | } 83 | 84 | /* 85 | * Get the html header 86 | */ 87 | public static function getHTMLHead() { 88 | return self::$htmlHead; 89 | } 90 | 91 | /* 92 | * Get the html foot 93 | */ 94 | public static function getHTMLFoot() { 95 | return self::$htmlFoot; 96 | } 97 | 98 | /* 99 | * Get the pattern header 100 | */ 101 | public static function getPatternHead() { 102 | return self::$patternHead; 103 | } 104 | 105 | /* 106 | * Get the pattern footer 107 | */ 108 | public static function getPatternFoot() { 109 | return self::$patternFoot; 110 | } 111 | 112 | /* 113 | * Get the file system loader 114 | */ 115 | public static function getFilesystemLoader() { 116 | return self::$filesystemLoader; 117 | } 118 | 119 | /* 120 | * Get the html loader 121 | */ 122 | public static function getStringLoader() { 123 | return self::$stringLoader; 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/PatternLab/Timer.php: -------------------------------------------------------------------------------- 1 | ".$text." >> "; 41 | } 42 | 43 | // get the current time 44 | $checkTime = self::getTime(); 45 | 46 | // get the data for the output 47 | $totalTime = ($checkTime - self::$startTime); 48 | $mem = round((memory_get_peak_usage(true)/1024)/1024,2); 49 | 50 | // figure out what tag to show 51 | $timeTag = "info"; 52 | if (($checkTime - self::$checkTime) > 0.2) { 53 | $timeTag = "error"; 54 | } else if (($checkTime - self::$checkTime) > 0.1) { 55 | $timeTag = "warning"; 56 | } 57 | 58 | // set the checkTime for the next check comparison 59 | self::$checkTime = $checkTime; 60 | 61 | // write out time/mem stats 62 | Console::writeLine($insert."currently taken <".$timeTag.">".$totalTime." seconds and used ".$mem."MB of memory..."); 63 | 64 | } 65 | 66 | /* 67 | * Get the time stamp 68 | */ 69 | protected static function getTime() { 70 | $mtime = microtime(); 71 | $mtime = explode(" ",$mtime); 72 | $mtime = $mtime[1] + $mtime[0]; 73 | return $mtime; 74 | } 75 | 76 | /** 77 | * Start the timer 78 | */ 79 | public static function start() { 80 | 81 | // get the current time 82 | self::$startTime = self::getTime(); 83 | 84 | } 85 | 86 | /** 87 | * Stop the timer 88 | */ 89 | public static function stop() { 90 | 91 | // make sure start time is set 92 | if (empty(self::$startTime)) { 93 | Console::writeError("the timer wasn't started..."); 94 | } 95 | 96 | // get the current time 97 | $endTime = self::getTime(); 98 | 99 | // get the data for the output 100 | $totalTime = ($endTime - self::$startTime); 101 | $mem = round((memory_get_peak_usage(true)/1024)/1024,2); 102 | 103 | // figure out what tag to show 104 | $timeTag = "info"; 105 | if ($totalTime > 0.5) { 106 | $timeTag = "error"; 107 | } else if ($totalTime > 0.3) { 108 | $timeTag = "warning"; 109 | } 110 | 111 | // write out time/mem stats 112 | Console::writeLine("site generation took <".$timeTag.">".$totalTime." seconds and used ".$mem."MB of memory..."); 113 | 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/PatternLab/Util.php: -------------------------------------------------------------------------------- 1 | options = array(); 44 | 45 | } 46 | 47 | /** 48 | * Watch the source/ directory for any changes to existing files. Will run forever if given the chance. 49 | * @param {Boolean} decide if the reload server should be turned on 50 | * @param {Boolean} decide if static files like CSS and JS should be moved 51 | */ 52 | public function watch($options = array()) { 53 | 54 | // double-checks options was properly set 55 | if (empty($options)) { 56 | Console::writeError("need to pass options to generate..."); 57 | } 58 | 59 | // set default attributes 60 | $moveStatic = (isset($options["moveStatic"])) ? $options["moveStatic"] : true; 61 | $noCacheBuster = (isset($options["noCacheBuster"])) ? $options["noCacheBuster"] : false; 62 | 63 | // make sure a copy of the given options are saved for using when running generate 64 | $this->options = $options; 65 | 66 | // set-up the Dispatcher 67 | $dispatcherInstance = Dispatcher::getInstance(); 68 | $dispatcherInstance->dispatch("watcher.start"); 69 | 70 | if ($noCacheBuster) { 71 | Config::setOption("cacheBuster",0); 72 | } 73 | 74 | $c = false; // track that one loop through the pattern file listing has completed 75 | $o = new \stdClass(); // create an object to hold the properties 76 | $cp = new \stdClass(); // create an object to hold a clone of $o 77 | 78 | $o->patterns = new \stdClass(); 79 | 80 | Console::writeLine("watching your site for changes..."); 81 | 82 | // default vars 83 | $publicDir = Config::getOption("publicDir"); 84 | $sourceDir = Config::getOption("sourceDir"); 85 | $patternSourceDir = Config::getOption("patternSourceDir"); 86 | $ignoreExts = Config::getOption("ie"); 87 | $ignoreDirs = Config::getOption("id"); 88 | $patternExt = Config::getOption("patternExtension"); 89 | 90 | // build the file extensions based on the rules 91 | $fileExtensions = array(); 92 | $patternRules = PatternData::getRules(); 93 | foreach ($patternRules as $patternRule) { 94 | $extensions = $patternRule->getProp("extProp"); 95 | if (strpos($extensions,"&&") !== false) { 96 | $extensions = explode("&&",$extensions); 97 | } else if (strpos($extensions,"||") !== false) { 98 | $extensions = explode("||",$extensions); 99 | } else { 100 | $extensions = array($extensions); 101 | } 102 | foreach ($extensions as $extension) { 103 | if (!in_array($extension, $fileExtensions)) { 104 | $fileExtensions[] = $extension; 105 | } 106 | } 107 | } 108 | 109 | // run forever 110 | while (true) { 111 | 112 | // clone the patterns so they can be checked in case something gets deleted 113 | $cp = clone $o->patterns; 114 | 115 | // iterate over the patterns & related data and regenerate the entire site if they've changed 116 | $patternObjects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($patternSourceDir), \RecursiveIteratorIterator::SELF_FIRST); 117 | 118 | // make sure dots are skipped 119 | $patternObjects->setFlags(\FilesystemIterator::SKIP_DOTS); 120 | 121 | foreach($patternObjects as $name => $object) { 122 | 123 | // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored 124 | $fileName = str_replace($patternSourceDir.DIRECTORY_SEPARATOR,"",$name); 125 | $fileNameClean = str_replace(DIRECTORY_SEPARATOR."_",DIRECTORY_SEPARATOR,$fileName); 126 | 127 | if ($object->isFile() && in_array($object->getExtension(), $fileExtensions)) { 128 | 129 | // make sure this isn't a hidden pattern 130 | $patternParts = explode(DIRECTORY_SEPARATOR,$fileName); 131 | $pattern = isset($patternParts[2]) ? $patternParts[2] : $patternParts[1]; 132 | 133 | // make sure the pattern still exists in source just in case it's been deleted during the iteration 134 | if (file_exists($name)) { 135 | 136 | $mt = $object->getMTime(); 137 | if (isset($o->patterns->$fileName) && ($o->patterns->$fileName != $mt)) { 138 | $o->patterns->$fileName = $mt; 139 | $this->updateSite($fileName,"changed"); 140 | } else if (!isset($o->patterns->$fileName) && $c) { 141 | $o->patterns->$fileName = $mt; 142 | $this->updateSite($fileName,"added"); 143 | if ($object->getExtension() == $patternExt) { 144 | $patternSrcPath = str_replace(".".$patternExt,"",$fileName); 145 | $patternDestPath = str_replace("/","-",$patternSrcPath); 146 | $render = ($pattern[0] != "_") ? true : false; 147 | $this->patternPaths[$patternParts[0]][$pattern] = array("patternSrcPath" => $patternSrcPath, "patternDestPath" => $patternDestPath, "render" => $render); 148 | } 149 | } else if (!isset($o->patterns->$fileName)) { 150 | $o->patterns->$fileName = $mt; 151 | } 152 | 153 | if ($c && isset($o->patterns->$fileName)) { 154 | unset($cp->$fileName); 155 | } 156 | 157 | } else { 158 | 159 | // the file was removed during the iteration so remove references to the item 160 | unset($o->patterns->$fileName); 161 | unset($cp->$fileName); 162 | unset($this->patternPaths[$patternParts[0]][$pattern]); 163 | $this->updateSite($fileName,"removed"); 164 | 165 | } 166 | 167 | } 168 | 169 | } 170 | 171 | // make sure old entries are deleted 172 | // will throw "pattern not found" errors if an entire directory is removed at once but that shouldn't be a big deal 173 | if ($c) { 174 | 175 | foreach($cp as $fileName => $mt) { 176 | 177 | unset($o->patterns->$fileName); 178 | $patternParts = explode(DIRECTORY_SEPARATOR,$fileName); 179 | $pattern = isset($patternParts[2]) ? $patternParts[2] : $patternParts[1]; 180 | 181 | unset($this->patternPaths[$patternParts[0]][$pattern]); 182 | $this->updateSite($fileName,"removed"); 183 | 184 | } 185 | 186 | } 187 | 188 | // iterate over annotations, data, meta and any other _ dirs 189 | $watchDirs = glob($sourceDir.DIRECTORY_SEPARATOR."_*",GLOB_ONLYDIR); 190 | foreach ($watchDirs as $watchDir) { 191 | 192 | if ($watchDir != $patternSourceDir) { 193 | 194 | // iterate over the data files and regenerate the entire site if they've changed 195 | $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($watchDir), \RecursiveIteratorIterator::SELF_FIRST); 196 | 197 | // make sure dots are skipped 198 | $objects->setFlags(\FilesystemIterator::SKIP_DOTS); 199 | 200 | foreach($objects as $name => $object) { 201 | 202 | $fileName = str_replace($sourceDir.DIRECTORY_SEPARATOR,"",$name); 203 | $mt = $object->getMTime(); 204 | 205 | if (!isset($o->$fileName)) { 206 | $o->$fileName = $mt; 207 | if ($c) { 208 | $this->updateSite($fileName,"added"); 209 | } 210 | } else if ($o->$fileName != $mt) { 211 | $o->$fileName = $mt; 212 | if ($c) { 213 | $this->updateSite($fileName,"changed"); 214 | } 215 | } 216 | 217 | } 218 | 219 | } 220 | 221 | } 222 | 223 | // iterate over all of the other files in the source directory and move them if their modified time has changed 224 | if ($moveStatic) { 225 | 226 | $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($sourceDir.DIRECTORY_SEPARATOR), \RecursiveIteratorIterator::SELF_FIRST); 227 | 228 | // make sure dots are skipped 229 | $objects->setFlags(\FilesystemIterator::SKIP_DOTS); 230 | 231 | foreach($objects as $name => $object) { 232 | 233 | // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored 234 | $fileName = str_replace($sourceDir.DIRECTORY_SEPARATOR,"",$name); 235 | if (($fileName[0] != "_") && (!in_array($object->getExtension(),$ignoreExts)) && (!in_array($object->getFilename(),$ignoreDirs))) { 236 | 237 | // catch directories that have the ignored dir in their path 238 | $ignoreDir = FileUtil::ignoreDir($fileName); 239 | 240 | // check to see if it's a new directory 241 | if (!$ignoreDir && $object->isDir() && !isset($o->$fileName) && !is_dir($publicDir."/".$fileName)) { 242 | mkdir($publicDir."/".$fileName); 243 | $o->$fileName = "dir created"; // placeholder 244 | Console::writeLine($fileName."/ directory was created..."); 245 | } 246 | 247 | // check to see if it's a new file or a file that has changed 248 | if (file_exists($name)) { 249 | 250 | $mt = $object->getMTime(); 251 | if (!$ignoreDir && $object->isFile() && !isset($o->$fileName) && !file_exists($publicDir."/".$fileName)) { 252 | $o->$fileName = $mt; 253 | FileUtil::moveStaticFile($fileName,"added"); 254 | if ($object->getExtension() == "css") { 255 | $this->updateSite($fileName,"changed",0); // make sure the site is updated for MQ reasons 256 | } 257 | } else if (!$ignoreDir && $object->isFile() && isset($o->$fileName) && ($o->$fileName != $mt)) { 258 | $o->$fileName = $mt; 259 | FileUtil::moveStaticFile($fileName,"changed"); 260 | if ($object->getExtension() == "css") { 261 | $this->updateSite($fileName,"changed",0); // make sure the site is updated for MQ reasons 262 | } 263 | } else if (!isset($o->fileName)) { 264 | $o->$fileName = $mt; 265 | } 266 | 267 | } else { 268 | unset($o->$fileName); 269 | } 270 | 271 | } 272 | 273 | } 274 | 275 | } 276 | 277 | 278 | $c = true; 279 | 280 | // taking out the garbage. basically killing mustache after each run. 281 | if (gc_enabled()) gc_collect_cycles(); 282 | 283 | // pause for .05 seconds to give the CPU a rest 284 | usleep(50000); 285 | 286 | } 287 | 288 | } 289 | 290 | /** 291 | * Updates the Pattern Lab Website and prints the appropriate message 292 | * @param {String} file name to included in the message 293 | * @param {String} a switch for decided which message isn't printed 294 | * 295 | * @return {String} the final message 296 | */ 297 | private function updateSite($fileName,$message,$verbose = true) { 298 | 299 | $watchMessage = ""; 300 | if ($verbose) { 301 | if ($message == "added") { 302 | $watchMessage = "".$fileName." was added to Pattern Lab. reload the website to see this change in the navigation..."; 303 | } elseif ($message == "removed") { 304 | $watchMessage = "".$fileName." was removed from Pattern Lab. reload the website to see this change reflected in the navigation..."; 305 | } elseif ($message == "hidden") { 306 | $watchMessage = "".$fileName." was hidden from Pattern Lab. reload the website to see this change reflected in the navigation..."; 307 | } else { 308 | $watchMessage = "".$fileName." changed..."; 309 | } 310 | } 311 | 312 | $options = $this->options; 313 | 314 | $options["watchVerbose"] = $verbose; 315 | $options["watchMessage"] = $watchMessage; 316 | $options["moveStatic"] = false; 317 | 318 | // clear the various data stores for re-population 319 | Data::clear(); 320 | PatternData::clear(); 321 | Annotations::clear(); 322 | 323 | $g = new Generator(); 324 | $g->generate($options); 325 | 326 | } 327 | 328 | protected function starterKitPathPrompt() { 329 | 330 | // need to figure this out long-term 331 | InstallerUtil::$isInteractive = true; 332 | $input = Console::promptInput("Tell me the path to the starterkit you want to watch.","e.g. vendor/pattern-lab/starterkit-mustache-demo/dist","baz",false); 333 | 334 | // set-up the full starterkit path 335 | $starterKitPath = Config::getOption("baseDir").$input; 336 | if (!is_dir($starterKitPath)) { 337 | Console::writeWarning("that doesn't seem to be a real directory. let's try again..."); 338 | $starterKitPath = $this->starterKitPathPrompt(); 339 | } 340 | 341 | return $starterKitPath; 342 | 343 | } 344 | 345 | public function watchStarterKit() { 346 | 347 | // default vars 348 | $starterKitPath = $this->starterKitPathPrompt(); 349 | $sourceDir = Config::getOption("sourceDir"); 350 | 351 | $fs = new Filesystem(); 352 | 353 | $c = false; // track that one loop through the pattern file listing has completed 354 | $o = new \stdClass(); // create an object to hold the properties 355 | $cp = new \stdClass(); // create an object to hold a clone of $o 356 | 357 | $o->patterns = new \stdClass(); 358 | 359 | Console::writeLine("watching your starterkit for changes..."); 360 | 361 | // run forever 362 | while (true) { 363 | 364 | // clone the patterns so they can be checked in case something gets deleted 365 | $cp = clone $o->patterns; 366 | 367 | $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($starterKitPath), \RecursiveIteratorIterator::SELF_FIRST); 368 | 369 | // make sure dots are skipped 370 | $objects->setFlags(\FilesystemIterator::SKIP_DOTS); 371 | 372 | foreach ($objects as $name => $object) { 373 | 374 | // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored 375 | $fileName = str_replace($starterKitPath.DIRECTORY_SEPARATOR,"",$name); 376 | 377 | // check to see if it's a new directory 378 | if ($object->isDir() && !isset($o->$fileName) && !is_dir($starterKitPath."/".$fileName)) { 379 | mkdir($sourceDir."/".$fileName); 380 | $o->$fileName = "dir created"; // placeholder 381 | Console::writeLine($fileName."/ directory was created..."); 382 | } 383 | 384 | // check to see if it's a new file or a file that has changed 385 | if (file_exists($name)) { 386 | 387 | $mt = $object->getMTime(); 388 | if ($object->isFile() && !isset($o->$fileName) && !file_exists($sourceDir.DIRECTORY_SEPARATOR.$fileName)) { 389 | $o->$fileName = $mt; 390 | $fs->copy($starterKitPath.DIRECTORY_SEPARATOR.$fileName,$sourceDir.DIRECTORY_SEPARATOR.$fileName); 391 | Console::writeInfo($fileName." added..."); 392 | } else if ($object->isFile() && isset($o->$fileName) && ($o->$fileName != $mt)) { 393 | $o->$fileName = $mt; 394 | $fs->copy($starterKitPath.DIRECTORY_SEPARATOR.$fileName,$sourceDir.DIRECTORY_SEPARATOR.$fileName); 395 | Console::writeInfo($fileName." changed..."); 396 | } else if (!isset($o->fileName)) { 397 | $o->$fileName = $mt; 398 | } 399 | 400 | } else { 401 | unset($o->$fileName); 402 | } 403 | 404 | } 405 | 406 | $c = true; 407 | 408 | // taking out the garbage. basically killing mustache after each run. 409 | if (gc_enabled()) gc_collect_cycles(); 410 | 411 | // pause for .05 seconds to give the CPU a rest 412 | usleep(50000); 413 | 414 | } 415 | 416 | } 417 | 418 | } 419 | -------------------------------------------------------------------------------- /src/PatternLab/Zippy/UnpackAdapter.php: -------------------------------------------------------------------------------- 1 | container = AdapterContainer::load(); 24 | } 25 | 26 | public function getAdapters() { 27 | return array(UnpackAdapter::newInstance($this->container['executable-finder'],$this->container['resource-manager'],$this->container['gnu-tar.inflator'],$this->container['gnu-tar.deflator'])); 28 | } 29 | 30 | public function getFileExtension() { 31 | return 'tar.gz'; 32 | } 33 | 34 | } 35 | --------------------------------------------------------------------------------