 97c690fc0f
			
		
	
	97c690fc0f
	
	
	
		
			
			Summary: This builds out and implements PHUIPropertyListView (container) and PHUIPropertyListItemView (section) as well as adding tabs. Test Plan: Tested each page I edited with the exception of Releeph and Phortune, though those changes look ok to me diff wise. Updated examples page with tabs. Reviewers: epriestley, btrahan Reviewed By: epriestley CC: Korvin, epriestley, aran Differential Revision: https://secure.phabricator.com/D7283
		
			
				
	
	
		
			414 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			414 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env php
 | |
| <?php
 | |
| 
 | |
| $package_spec = array(
 | |
|   'javelin.pkg.js' => array(
 | |
|     'javelin-util',
 | |
|     'javelin-install',
 | |
|     'javelin-event',
 | |
|     'javelin-stratcom',
 | |
|     'javelin-behavior',
 | |
|     'javelin-resource',
 | |
|     'javelin-request',
 | |
|     'javelin-vector',
 | |
|     'javelin-dom',
 | |
|     'javelin-json',
 | |
|     'javelin-uri',
 | |
|     'javelin-workflow',
 | |
|     'javelin-mask',
 | |
|     'javelin-typeahead',
 | |
|     'javelin-typeahead-normalizer',
 | |
|     'javelin-typeahead-source',
 | |
|     'javelin-typeahead-preloaded-source',
 | |
|     'javelin-typeahead-ondemand-source',
 | |
|     'javelin-tokenizer',
 | |
|     'javelin-history',
 | |
|   ),
 | |
|   'core.pkg.js' => array(
 | |
|     'javelin-behavior-aphront-basic-tokenizer',
 | |
|     'javelin-behavior-workflow',
 | |
|     'javelin-behavior-aphront-form-disable-on-submit',
 | |
|     'phabricator-keyboard-shortcut-manager',
 | |
|     'phabricator-keyboard-shortcut',
 | |
|     'javelin-behavior-phabricator-keyboard-shortcuts',
 | |
|     'javelin-behavior-refresh-csrf',
 | |
|     'javelin-behavior-phabricator-watch-anchor',
 | |
|     'javelin-behavior-phabricator-autofocus',
 | |
|     'phabricator-menu-item',
 | |
|     'phabricator-dropdown-menu',
 | |
|     'phabricator-phtize',
 | |
|     'javelin-behavior-phabricator-oncopy',
 | |
|     'phabricator-tooltip',
 | |
|     'javelin-behavior-phabricator-tooltips',
 | |
|     'phabricator-prefab',
 | |
|     'javelin-behavior-device',
 | |
|     'javelin-behavior-toggle-class',
 | |
|     'javelin-behavior-lightbox-attachments',
 | |
|     'phabricator-busy',
 | |
|     'javelin-aphlict',
 | |
|     'phabricator-notification',
 | |
|     'javelin-behavior-aphlict-listen',
 | |
|     'javelin-behavior-phabricator-search-typeahead',
 | |
|     'javelin-behavior-konami',
 | |
|     'javelin-behavior-aphlict-dropdown',
 | |
|     'javelin-behavior-history-install',
 | |
|     'javelin-behavior-phabricator-gesture',
 | |
| 
 | |
|     'javelin-behavior-phabricator-active-nav',
 | |
|     'javelin-behavior-phabricator-nav',
 | |
|     'javelin-behavior-phabricator-remarkup-assist',
 | |
|     'phabricator-textareautils',
 | |
|     'phabricator-file-upload',
 | |
|     'javelin-behavior-global-drag-and-drop',
 | |
|     'javelin-behavior-phabricator-reveal-content',
 | |
|     'phabricator-hovercard',
 | |
|     'javelin-behavior-phabricator-hovercards',
 | |
|     'javelin-color',
 | |
|     'javelin-fx',
 | |
|   ),
 | |
|   'core.pkg.css' => array(
 | |
|     'phabricator-core-css',
 | |
|     'phabricator-zindex-css',
 | |
|     'phui-button-css',
 | |
|     'phabricator-standard-page-view',
 | |
|     'aphront-dialog-view-css',
 | |
|     'phui-form-view-css',
 | |
|     'aphront-panel-view-css',
 | |
|     'aphront-table-view-css',
 | |
|     'aphront-tokenizer-control-css',
 | |
|     'aphront-typeahead-control-css',
 | |
|     'aphront-list-filter-view-css',
 | |
| 
 | |
|     'phabricator-jump-nav',
 | |
| 
 | |
|     'phabricator-remarkup-css',
 | |
|     'syntax-highlighting-css',
 | |
|     'aphront-pager-view-css',
 | |
|     'phabricator-transaction-view-css',
 | |
|     'aphront-tooltip-css',
 | |
|     'phabricator-flag-css',
 | |
|     'aphront-error-view-css',
 | |
| 
 | |
|     'sprite-icons-css',
 | |
|     'sprite-gradient-css',
 | |
|     'sprite-menu-css',
 | |
|     'sprite-apps-large-css',
 | |
|     'sprite-status-css',
 | |
| 
 | |
|     'phabricator-main-menu-view',
 | |
|     'phabricator-notification-css',
 | |
|     'phabricator-notification-menu-css',
 | |
|     'lightbox-attachment-css',
 | |
|     'phui-header-view-css',
 | |
|     'phabricator-filetree-view-css',
 | |
|     'phabricator-nav-view-css',
 | |
|     'phabricator-side-menu-view-css',
 | |
|     'phabricator-crumbs-view-css',
 | |
|     'phui-object-item-list-view-css',
 | |
|     'global-drag-and-drop-css',
 | |
|     'phui-spacing-css',
 | |
|     'phui-form-css',
 | |
|     'phui-icon-view-css',
 | |
| 
 | |
|     'phabricator-application-launch-view-css',
 | |
|     'phabricator-action-list-view-css',
 | |
|     'phui-property-list-view-css',
 | |
|     'phabricator-tag-view-css',
 | |
|     'phui-list-view-css',
 | |
|   ),
 | |
|   'differential.pkg.css' => array(
 | |
|     'differential-core-view-css',
 | |
|     'differential-changeset-view-css',
 | |
|     'differential-results-table-css',
 | |
|     'differential-revision-history-css',
 | |
|     'differential-revision-list-css',
 | |
|     'differential-table-of-contents-css',
 | |
|     'differential-revision-comment-css',
 | |
|     'differential-revision-add-comment-css',
 | |
|     'differential-revision-comment-list-css',
 | |
|     'phabricator-object-selector-css',
 | |
|     'phabricator-content-source-view-css',
 | |
|     'differential-local-commits-view-css',
 | |
|     'inline-comment-summary-css',
 | |
|   ),
 | |
|   'differential.pkg.js' => array(
 | |
|     'phabricator-drag-and-drop-file-upload',
 | |
|     'phabricator-shaped-request',
 | |
| 
 | |
|     'javelin-behavior-differential-feedback-preview',
 | |
|     'javelin-behavior-differential-edit-inline-comments',
 | |
|     'javelin-behavior-differential-populate',
 | |
|     'javelin-behavior-differential-show-more',
 | |
|     'javelin-behavior-differential-diff-radios',
 | |
|     'javelin-behavior-differential-accept-with-errors',
 | |
|     'javelin-behavior-differential-comment-jump',
 | |
|     'javelin-behavior-differential-add-reviewers-and-ccs',
 | |
|     'javelin-behavior-differential-keyboard-navigation',
 | |
|     'javelin-behavior-aphront-drag-and-drop-textarea',
 | |
|     'javelin-behavior-phabricator-object-selector',
 | |
|     'javelin-behavior-repository-crossreference',
 | |
|     'javelin-behavior-load-blame',
 | |
| 
 | |
|     'differential-inline-comment-editor',
 | |
|     'javelin-behavior-differential-dropdown-menus',
 | |
|     'javelin-behavior-differential-toggle-files',
 | |
|     'javelin-behavior-differential-user-select',
 | |
|   ),
 | |
|   'diffusion.pkg.css' => array(
 | |
|     'diffusion-commit-view-css',
 | |
|     'diffusion-icons-css',
 | |
|   ),
 | |
|   'diffusion.pkg.js' => array(
 | |
|     'javelin-behavior-diffusion-pull-lastmodified',
 | |
|     'javelin-behavior-diffusion-commit-graph',
 | |
|     'javelin-behavior-audit-preview',
 | |
|   ),
 | |
|   'maniphest.pkg.css' => array(
 | |
|     'maniphest-task-summary-css',
 | |
|     'phabricator-project-tag-css',
 | |
|   ),
 | |
|   'maniphest.pkg.js' => array(
 | |
|     'javelin-behavior-maniphest-batch-selector',
 | |
|     'javelin-behavior-maniphest-transaction-controls',
 | |
|     'javelin-behavior-maniphest-transaction-preview',
 | |
|     'javelin-behavior-maniphest-transaction-expand',
 | |
|     'javelin-behavior-maniphest-subpriority-editor',
 | |
|   ),
 | |
|   'darkconsole.pkg.js' => array(
 | |
|     'javelin-behavior-dark-console',
 | |
|     'javelin-behavior-error-log',
 | |
|   ),
 | |
| );
 | |
| 
 | |
| 
 | |
| require_once dirname(__FILE__).'/__init_script__.php';
 | |
| 
 | |
| $args = new PhutilArgumentParser($argv);
 | |
| $args->setTagline('map static resources');
 | |
| $args->setSynopsis(
 | |
|   "**celerity_mapper.php** [--output __path__] [--with-custom] <webroot>");
 | |
| $args->parse(
 | |
|   array(
 | |
|     array(
 | |
|       'name'     => 'output',
 | |
|       'param'    => 'path',
 | |
|       'default'  => '../src/__celerity_resource_map__.php',
 | |
|       'help'     => "Set the path for resource map. It is usually useful for ".
 | |
|                     "'celerity.resource-path' configuration.",
 | |
|     ),
 | |
|     array(
 | |
|       'name'     => 'with-custom',
 | |
|       'help'     => 'Include resources in <webroot>/rsrc/custom/.',
 | |
|     ),
 | |
|     array(
 | |
|       'name'     => 'webroot',
 | |
|       'wildcard' => true,
 | |
|     ),
 | |
|   ));
 | |
| 
 | |
| $root = $args->getArg('webroot');
 | |
| if (count($root) != 1 || !is_dir(reset($root))) {
 | |
|   $args->printHelpAndExit();
 | |
| }
 | |
| $root = Filesystem::resolvePath(reset($root));
 | |
| 
 | |
| $celerity_path = Filesystem::resolvePath($args->getArg('output'), $root);
 | |
| $with_custom = $args->getArg('with-custom');
 | |
| 
 | |
| $resource_hash = PhabricatorEnv::getEnvConfig('celerity.resource-hash');
 | |
| $runtime_map = array();
 | |
| 
 | |
| echo "Finding raw static resources...\n";
 | |
| $finder = id(new FileFinder($root))
 | |
|   ->withType('f')
 | |
|   ->withSuffix('png')
 | |
|   ->withSuffix('jpg')
 | |
|   ->withSuffix('gif')
 | |
|   ->withSuffix('swf')
 | |
|   ->withFollowSymlinks(true)
 | |
|   ->setGenerateChecksums(true);
 | |
| if (!$with_custom) {
 | |
|   $finder->excludePath('./rsrc/custom');
 | |
| }
 | |
| $raw_files = $finder->find();
 | |
| 
 | |
| echo "Processing ".count($raw_files)." files";
 | |
| foreach ($raw_files as $path => $hash) {
 | |
|   echo ".";
 | |
|   $path = '/'.Filesystem::readablePath($path, $root);
 | |
|   $type = CelerityResourceTransformer::getResourceType($path);
 | |
| 
 | |
|   $hash = md5($hash.$path.$resource_hash);
 | |
|   $uri  = '/res/'.substr($hash, 0, 8).$path;
 | |
| 
 | |
|   $runtime_map[$path] = array(
 | |
|     'hash' => $hash,
 | |
|     'uri'  => $uri,
 | |
|     'disk' => $path,
 | |
|     'type' => $type,
 | |
|   );
 | |
| }
 | |
| echo "\n";
 | |
| 
 | |
| $xformer = id(new CelerityResourceTransformer())
 | |
|   ->setMinify(false)
 | |
|   ->setRawResourceMap($runtime_map);
 | |
| 
 | |
| echo "Finding transformable static resources...\n";
 | |
| $finder = id(new FileFinder($root))
 | |
|   ->withType('f')
 | |
|   ->withSuffix('js')
 | |
|   ->withSuffix('css')
 | |
|   ->withFollowSymlinks(true)
 | |
|   ->setGenerateChecksums(true);
 | |
| if (!$with_custom) {
 | |
|   $finder->excludePath('./rsrc/custom');
 | |
| }
 | |
| $files = $finder->find();
 | |
| 
 | |
| echo "Processing ".count($files)." files";
 | |
| 
 | |
| $file_map = array();
 | |
| foreach ($files as $path => $raw_hash) {
 | |
|   echo ".";
 | |
|   $path = '/'.Filesystem::readablePath($path, $root);
 | |
|   $data = Filesystem::readFile($root.$path);
 | |
| 
 | |
|   $data = $xformer->transformResource($path, $data);
 | |
|   $hash = md5($data);
 | |
|   $hash = md5($hash.$path.$resource_hash);
 | |
| 
 | |
|   $file_map[$path] = array(
 | |
|     'hash' => $hash,
 | |
|     'disk' => $path,
 | |
|   );
 | |
| }
 | |
| echo "\n";
 | |
| 
 | |
| $resource_graph = array();
 | |
| $hash_map = array();
 | |
| 
 | |
| $parser = new PhutilDocblockParser();
 | |
| foreach ($file_map as $path => $info) {
 | |
|   $type = CelerityResourceTransformer::getResourceType($path);
 | |
| 
 | |
|   $data = Filesystem::readFile($root.$info['disk']);
 | |
|   $matches = array();
 | |
|   $ok = preg_match('@/[*][*].*?[*]/@s', $data, $matches);
 | |
|   if (!$ok) {
 | |
|     throw new Exception(
 | |
|       "File {$path} does not have a header doc comment. Encode dependency ".
 | |
|       "data in a header docblock.");
 | |
|   }
 | |
| 
 | |
|   list($description, $metadata) = $parser->parse($matches[0]);
 | |
| 
 | |
|   $provides = preg_split('/\s+/', trim(idx($metadata, 'provides')));
 | |
|   $requires = preg_split('/\s+/', trim(idx($metadata, 'requires')));
 | |
|   $provides = array_filter($provides);
 | |
|   $requires = array_filter($requires);
 | |
| 
 | |
|   if (!$provides) {
 | |
|     // Tests and documentation-only JS is permitted to @provide no targets.
 | |
|     continue;
 | |
|   }
 | |
| 
 | |
|   if (count($provides) > 1) {
 | |
|     throw new Exception(
 | |
|       "File {$path} must @provide at most one Celerity target.");
 | |
|   }
 | |
| 
 | |
|   $provides = reset($provides);
 | |
| 
 | |
|   $uri = '/res/'.substr($info['hash'], 0, 8).$path;
 | |
| 
 | |
|   $hash_map[$provides] = $info['hash'];
 | |
| 
 | |
|   $resource_graph[$provides] = $requires;
 | |
| 
 | |
|   $runtime_map[$provides] = array(
 | |
|     'uri'       => $uri,
 | |
|     'type'      => $type,
 | |
|     'requires'  => $requires,
 | |
|     'disk'      => $path,
 | |
|   );
 | |
| }
 | |
| 
 | |
| $celerity_resource_graph = new CelerityResourceGraph();
 | |
| $celerity_resource_graph->addNodes($resource_graph);
 | |
| $celerity_resource_graph->setResourceGraph($resource_graph);
 | |
| $celerity_resource_graph->loadGraph();
 | |
| 
 | |
| foreach ($resource_graph as $provides => $requires) {
 | |
|   $cycle = $celerity_resource_graph->detectCycles($provides);
 | |
|   if ($cycle) {
 | |
|     throw new Exception(
 | |
|       "Cycle detected in resource graph: ". implode($cycle, " => ")
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| $package_map = array();
 | |
| foreach ($package_spec as $name => $package) {
 | |
|   $hashes = array();
 | |
|   $type = null;
 | |
|   foreach ($package as $symbol) {
 | |
|     if (empty($hash_map[$symbol])) {
 | |
|       throw new Exception(
 | |
|         "Package specification for '{$name}' includes '{$symbol}', but that ".
 | |
|         "symbol is not defined anywhere.");
 | |
|     }
 | |
|     if ($type === null) {
 | |
|       $type = $runtime_map[$symbol]['type'];
 | |
|     } else {
 | |
|       $ntype = $runtime_map[$symbol]['type'];
 | |
|       if ($type !== $ntype) {
 | |
|         throw new Exception(
 | |
|           "Package specification for '{$name}' mixes resources of type ".
 | |
|           "'{$type}' with resources of type '{$ntype}'. Each package may only ".
 | |
|           "contain one type of resource.");
 | |
|       }
 | |
|     }
 | |
|     $hashes[] = $symbol.':'.$hash_map[$symbol];
 | |
|   }
 | |
|   $key = substr(md5(implode("\n", $hashes)), 0, 8);
 | |
|   $package_map['packages'][$key] = array(
 | |
|     'name'    => $name,
 | |
|     'symbols' => $package,
 | |
|     'uri'     => '/res/pkg/'.$key.'/'.$name,
 | |
|     'type'    => $type,
 | |
|   );
 | |
|   foreach ($package as $symbol) {
 | |
|     $package_map['reverse'][$symbol] = $key;
 | |
|   }
 | |
| }
 | |
| 
 | |
| ksort($runtime_map);
 | |
| $runtime_map = var_export($runtime_map, true);
 | |
| $runtime_map = preg_replace('/\s+$/m', '', $runtime_map);
 | |
| $runtime_map = preg_replace('/array \(/', 'array(', $runtime_map);
 | |
| 
 | |
| $package_map['packages'] = isort($package_map['packages'], 'name');
 | |
| ksort($package_map['reverse']);
 | |
| $package_map = var_export($package_map, true);
 | |
| $package_map = preg_replace('/\s+$/m', '', $package_map);
 | |
| $package_map = preg_replace('/array \(/', 'array(', $package_map);
 | |
| 
 | |
| $generated = '@'.'generated';
 | |
| $resource_map = <<<EOFILE
 | |
| <?php
 | |
| 
 | |
| /**
 | |
|  * This file is automatically generated. Use 'celerity_mapper.php' to rebuild
 | |
|  * it.
 | |
|  * {$generated}
 | |
|  */
 | |
| 
 | |
| celerity_register_resource_map({$runtime_map}, {$package_map});
 | |
| 
 | |
| EOFILE;
 | |
| 
 | |
| echo "Writing map...\n";
 | |
| Filesystem::writeFile($celerity_path, $resource_map);
 | |
| echo "Done.\n";
 |