diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,8 +7,8 @@ */ return array( 'names' => array( - 'core.pkg.css' => '47793306', - 'core.pkg.js' => 'e72140ce', + 'core.pkg.css' => '743edc44', + 'core.pkg.js' => '75d29e8c', 'darkconsole.pkg.js' => '8ab24e01', 'differential.pkg.css' => '3500921f', 'differential.pkg.js' => 'c0506961', @@ -27,7 +27,7 @@ 'rsrc/css/aphront/panel-view.css' => '8427b78d', 'rsrc/css/aphront/phabricator-nav-view.css' => '7aeaf435', 'rsrc/css/aphront/table-view.css' => '59e2c0f8', - 'rsrc/css/aphront/tokenizer.css' => '8b520614', + 'rsrc/css/aphront/tokenizer.css' => '95e931ab', 'rsrc/css/aphront/tooltip.css' => '7672b60f', 'rsrc/css/aphront/two-column.css' => '16ab3ad2', 'rsrc/css/aphront/typeahead-browse.css' => '343ab59f', @@ -222,7 +222,7 @@ 'rsrc/externals/javelin/lib/__tests__/URI.js' => '1e45fda9', 'rsrc/externals/javelin/lib/__tests__/behavior.js' => '1ea62783', 'rsrc/externals/javelin/lib/behavior.js' => '61cbc29a', - 'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => 'f82bdab0', + 'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => '0fd17937', 'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => '70baed2f', 'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => '6f7a9da8', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '503e17fd', @@ -390,10 +390,10 @@ 'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => 'e5822781', 'rsrc/js/application/files/behavior-icon-composer.js' => '8ef9ab58', 'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888', - 'rsrc/js/application/herald/HeraldRuleEditor.js' => '6e2de6f2', + 'rsrc/js/application/herald/HeraldRuleEditor.js' => '9229e764', 'rsrc/js/application/herald/PathTypeahead.js' => 'f7fc67ec', 'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3', - 'rsrc/js/application/maniphest/behavior-batch-editor.js' => 'f24f3253', + 'rsrc/js/application/maniphest/behavior-batch-editor.js' => 'f5d1233b', 'rsrc/js/application/maniphest/behavior-batch-selector.js' => '7b98d7c5', 'rsrc/js/application/maniphest/behavior-line-chart.js' => '88f0c5b3', 'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2', @@ -411,7 +411,7 @@ 'rsrc/js/application/phortune/behavior-test-payment-form.js' => 'fc91ab6c', 'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef', 'rsrc/js/application/policy/behavior-policy-control.js' => 'f3fef818', - 'rsrc/js/application/policy/behavior-policy-rule-editor.js' => 'fe9a552f', + 'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c', 'rsrc/js/application/ponder/behavior-votebox.js' => '4e9b766b', 'rsrc/js/application/projects/behavior-boards-dropdown.js' => '0ec56e1d', 'rsrc/js/application/projects/behavior-project-boards.js' => '87cb6b51', @@ -450,7 +450,7 @@ 'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f', 'rsrc/js/core/MultirowRowManager.js' => 'b5d57730', 'rsrc/js/core/Notification.js' => '0c6946e7', - 'rsrc/js/core/Prefab.js' => 'e364733e', + 'rsrc/js/core/Prefab.js' => '7df3b8e9', 'rsrc/js/core/ShapedRequest.js' => '7cbe244b', 'rsrc/js/core/TextAreaUtils.js' => '5c93c52c', 'rsrc/js/core/Title.js' => 'df5e11d2', @@ -510,7 +510,7 @@ 'aphront-pager-view-css' => '2e3539af', 'aphront-panel-view-css' => '8427b78d', 'aphront-table-view-css' => '59e2c0f8', - 'aphront-tokenizer-control-css' => '8b520614', + 'aphront-tokenizer-control-css' => '95e931ab', 'aphront-tooltip-css' => '7672b60f', 'aphront-two-column-view-css' => '16ab3ad2', 'aphront-typeahead-control-css' => '0e403212', @@ -544,7 +544,7 @@ 'global-drag-and-drop-css' => '697324ad', 'harbormaster-css' => '49d64eb4', 'herald-css' => '826075fa', - 'herald-rule-editor' => '6e2de6f2', + 'herald-rule-editor' => '9229e764', 'herald-test-css' => '778b008e', 'homepage-panel-css' => 'e34bf140', 'inline-comment-summary-css' => 'eb5f8e8c', @@ -602,7 +602,7 @@ 'javelin-behavior-lightbox-attachments' => 'f8ba29d7', 'javelin-behavior-line-chart' => '88f0c5b3', 'javelin-behavior-load-blame' => '42126667', - 'javelin-behavior-maniphest-batch-editor' => 'f24f3253', + 'javelin-behavior-maniphest-batch-editor' => 'f5d1233b', 'javelin-behavior-maniphest-batch-selector' => '7b98d7c5', 'javelin-behavior-maniphest-list-editor' => 'a9f88de2', 'javelin-behavior-maniphest-subpriority-editor' => '84845b5b', @@ -640,7 +640,7 @@ 'javelin-behavior-phui-object-box-tabs' => '2bfa2836', 'javelin-behavior-phui-timeline-dropdown-menu' => '4d94d9c3', 'javelin-behavior-policy-control' => 'f3fef818', - 'javelin-behavior-policy-rule-editor' => 'fe9a552f', + 'javelin-behavior-policy-rule-editor' => '5e9f347c', 'javelin-behavior-ponder-votebox' => '4e9b766b', 'javelin-behavior-project-boards' => '87cb6b51', 'javelin-behavior-project-create' => '065227cc', @@ -689,7 +689,7 @@ 'javelin-scrollbar' => 'eaa5b321', 'javelin-sound' => '949c0fe5', 'javelin-stratcom' => '6c53634d', - 'javelin-tokenizer' => 'f82bdab0', + 'javelin-tokenizer' => '0fd17937', 'javelin-typeahead' => '70baed2f', 'javelin-typeahead-composite-source' => '503e17fd', 'javelin-typeahead-normalizer' => '6f7a9da8', @@ -744,7 +744,7 @@ 'phabricator-notification-menu-css' => '3c9d8aa1', 'phabricator-object-selector-css' => '029a133d', 'phabricator-phtize' => 'd254d646', - 'phabricator-prefab' => 'e364733e', + 'phabricator-prefab' => '7df3b8e9', 'phabricator-profile-css' => '1a20dcbf', 'phabricator-remarkup-css' => 'bc65f3cc', 'phabricator-search-results-css' => '15c71110', @@ -921,6 +921,12 @@ 'javelin-install', 'javelin-util', ), + '0fd17937' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + ), '13c739ea' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1244,6 +1250,14 @@ 'javelin-dom', 'javelin-vector', ), + '5e9f347c' => array( + 'javelin-behavior', + 'multirow-row-manager', + 'javelin-dom', + 'javelin-util', + 'phabricator-prefab', + 'javelin-json', + ), '5fefb143' => array( 'javelin-behavior', 'javelin-dom', @@ -1308,15 +1322,6 @@ 'phabricator-drag-and-drop-file-upload', 'phabricator-textareautils', ), - '6e2de6f2' => array( - 'multirow-row-manager', - 'javelin-install', - 'javelin-util', - 'javelin-dom', - 'javelin-stratcom', - 'javelin-json', - 'phabricator-prefab', - ), '6e8cefa4' => array( 'javelin-install', 'javelin-dom', @@ -1408,6 +1413,18 @@ 'javelin-request', 'javelin-router', ), + '7df3b8e9' => array( + 'javelin-install', + 'javelin-util', + 'javelin-dom', + 'javelin-typeahead', + 'javelin-tokenizer', + 'javelin-typeahead-preloaded-source', + 'javelin-typeahead-ondemand-source', + 'javelin-dom', + 'javelin-stratcom', + 'javelin-util', + ), '7e41274a' => array( 'javelin-install', ), @@ -1515,9 +1532,6 @@ 'javelin-request', 'javelin-typeahead-source', ), - '8b520614' => array( - 'aphront-typeahead-control-css', - ), '8ce821c5' => array( 'phabricator-notification', 'javelin-stratcom', @@ -1546,6 +1560,15 @@ 'javelin-dom', 'javelin-stratcom', ), + '9229e764' => array( + 'multirow-row-manager', + 'javelin-install', + 'javelin-util', + 'javelin-dom', + 'javelin-stratcom', + 'javelin-json', + 'phabricator-prefab', + ), 93568464 => array( 'javelin-behavior', 'javelin-dom', @@ -1579,6 +1602,9 @@ 'javelin-resource', 'javelin-routable', ), + '95e931ab' => array( + 'aphront-typeahead-control-css', + ), '988040b4' => array( 'javelin-install', 'javelin-dom', @@ -1859,18 +1885,6 @@ 'javelin-workflow', 'javelin-vector', ), - 'e364733e' => array( - 'javelin-install', - 'javelin-util', - 'javelin-dom', - 'javelin-typeahead', - 'javelin-tokenizer', - 'javelin-typeahead-preloaded-source', - 'javelin-typeahead-ondemand-source', - 'javelin-dom', - 'javelin-stratcom', - 'javelin-util', - ), 'e379b58e' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1939,14 +1953,6 @@ 'javelin-install', 'javelin-util', ), - 'f24f3253' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-util', - 'phabricator-prefab', - 'multirow-row-manager', - 'javelin-json', - ), 'f36e01af' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -1963,6 +1969,14 @@ 'phuix-action-view', 'javelin-workflow', ), + 'f5d1233b' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-util', + 'phabricator-prefab', + 'multirow-row-manager', + 'javelin-json', + ), 'f6555212' => array( 'javelin-install', 'javelin-reactornode', @@ -1996,12 +2010,6 @@ 'javelin-install', 'javelin-dom', ), - 'f82bdab0' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - ), 'f8ba29d7' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2033,14 +2041,6 @@ 'javelin-view-visitor', 'javelin-util', ), - 'fe9a552f' => array( - 'javelin-behavior', - 'multirow-row-manager', - 'javelin-dom', - 'javelin-util', - 'phabricator-prefab', - 'javelin-json', - ), ), 'packages' => array( 'core.pkg.css' => array( diff --git a/src/applications/herald/controller/HeraldRuleController.php b/src/applications/herald/controller/HeraldRuleController.php --- a/src/applications/herald/controller/HeraldRuleController.php +++ b/src/applications/herald/controller/HeraldRuleController.php @@ -605,9 +605,12 @@ ); foreach ($sources as $key => $source) { + $source->setViewer($this->getViewer()); + $sources[$key] = array( 'uri' => $source->getDatasourceURI(), 'placeholder' => $source->getPlaceholderText(), + 'browseURI' => $source->getBrowseURI(), ); } diff --git a/src/applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php b/src/applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php --- a/src/applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php +++ b/src/applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php @@ -3,11 +3,6 @@ final class PhabricatorMailingListDatasource extends PhabricatorTypeaheadDatasource { - public function isBrowsable() { - // TODO: Make this browsable if we don't delete it before then. - return false; - } - public function getPlaceholderText() { return pht('Type a mailing list name...'); } @@ -20,11 +15,10 @@ $viewer = $this->getViewer(); $raw_query = $this->getRawQuery(); - $results = array(); + $query = id(new PhabricatorMailingListQuery()); + $lists = $this->executeQuery($query); - $lists = id(new PhabricatorMailingListQuery()) - ->setViewer($viewer) - ->execute(); + $results = array(); foreach ($lists as $list) { $results[] = id(new PhabricatorTypeaheadResult()) ->setName($list->getName()) @@ -32,7 +26,10 @@ ->setPHID($list->getPHID()); } - return $results; + // TODO: It would be slightly preferable to do this as part of the query, + // this is just simpler for the moment. + + return $this->filterResultsAgainstTokens($results); } } diff --git a/src/applications/maniphest/controller/ManiphestBatchEditController.php b/src/applications/maniphest/controller/ManiphestBatchEditController.php --- a/src/applications/maniphest/controller/ManiphestBatchEditController.php +++ b/src/applications/maniphest/controller/ManiphestBatchEditController.php @@ -65,7 +65,9 @@ $projects_source = new PhabricatorProjectDatasource(); $mailable_source = new PhabricatorMetaMTAMailableDatasource(); + $mailable_source->setViewer($user); $owner_source = new PhabricatorTypeaheadOwnerDatasource(); + $owner_source->setViewer($user); require_celerity_resource('maniphest-batch-editor'); Javelin::initBehavior( @@ -75,17 +77,20 @@ 'tokenizerTemplate' => $template, 'sources' => array( 'project' => array( - 'src' => $projects_source->getDatasourceURI(), - 'placeholder' => $projects_source->getPlaceholderText(), + 'src' => $projects_source->getDatasourceURI(), + 'placeholder' => $projects_source->getPlaceholderText(), + 'browseURI' => $projects_source->getBrowseURI(), ), 'owner' => array( - 'src' => $owner_source->getDatasourceURI(), - 'placeholder' => $owner_source->getPlaceholderText(), - 'limit' => 1, + 'src' => $owner_source->getDatasourceURI(), + 'placeholder' => $owner_source->getPlaceholderText(), + 'browseURI' => $owner_source->getBrowseURI(), + 'limit' => 1, ), - 'cc' => array( - 'src' => $mailable_source->getDatasourceURI(), - 'placeholder' => $mailable_source->getPlaceholderText(), + 'cc' => array( + 'src' => $mailable_source->getDatasourceURI(), + 'placeholder' => $mailable_source->getPlaceholderText(), + 'browseURI' => $mailable_source->getBrowseURI(), ), ), 'input' => 'batch-form-actions', diff --git a/src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php b/src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php --- a/src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php +++ b/src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php @@ -40,13 +40,7 @@ } public function getValueControlTemplate() { - $datasource = new LegalpadDocumentDatasource(); - - return array( - 'markup' => new AphrontTokenizerTemplateView(), - 'uri' => $datasource->getDatasourceURI(), - 'placeholder' => $datasource->getPlaceholderText(), - ); + return $this->getDatasourceTemplate(new LegalpadDocumentDatasource()); } public function getRuleOrder() { diff --git a/src/applications/policy/rule/PhabricatorPolicyRule.php b/src/applications/policy/rule/PhabricatorPolicyRule.php --- a/src/applications/policy/rule/PhabricatorPolicyRule.php +++ b/src/applications/policy/rule/PhabricatorPolicyRule.php @@ -22,6 +22,16 @@ return null; } + protected function getDatasourceTemplate( + PhabricatorTypeaheadDatasource $datasource) { + return array( + 'markup' => new AphrontTokenizerTemplateView(), + 'uri' => $datasource->getDatasourceURI(), + 'placeholder' => $datasource->getPlaceholderText(), + 'browseURI' => $datasource->getBrowseURI(), + ); + } + public function getRuleOrder() { return 500; } diff --git a/src/applications/policy/rule/PhabricatorProjectsPolicyRule.php b/src/applications/policy/rule/PhabricatorProjectsPolicyRule.php --- a/src/applications/policy/rule/PhabricatorProjectsPolicyRule.php +++ b/src/applications/policy/rule/PhabricatorProjectsPolicyRule.php @@ -38,13 +38,7 @@ } public function getValueControlTemplate() { - $projects_source = new PhabricatorProjectDatasource(); - - return array( - 'markup' => new AphrontTokenizerTemplateView(), - 'uri' => $projects_source->getDatasourceURI(), - 'placeholder' => $projects_source->getPlaceholderText(), - ); + return $this->getDatasourceTemplate(new PhabricatorProjectDatasource()); } public function getRuleOrder() { diff --git a/src/applications/policy/rule/PhabricatorUsersPolicyRule.php b/src/applications/policy/rule/PhabricatorUsersPolicyRule.php --- a/src/applications/policy/rule/PhabricatorUsersPolicyRule.php +++ b/src/applications/policy/rule/PhabricatorUsersPolicyRule.php @@ -20,13 +20,7 @@ } public function getValueControlTemplate() { - $users_datasource = new PhabricatorPeopleDatasource(); - - return array( - 'markup' => new AphrontTokenizerTemplateView(), - 'uri' => $users_datasource->getDatasourceURI(), - 'placeholder' => $users_datasource->getPlaceholderText(), - ); + return $this->getDatasourceTemplate(new PhabricatorPeopleDatasource()); } public function getRuleOrder() { diff --git a/src/view/control/AphrontTokenizerTemplateView.php b/src/view/control/AphrontTokenizerTemplateView.php --- a/src/view/control/AphrontTokenizerTemplateView.php +++ b/src/view/control/AphrontTokenizerTemplateView.php @@ -67,35 +67,40 @@ $content[] = $input; $content[] = phutil_tag('div', array('style' => 'clear: both;'), ''); - $container = phutil_tag( + $container = javelin_tag( 'div', array( 'id' => $id, 'class' => 'jx-tokenizer-container', + 'sigil' => 'tokenizer-container', ), $content); - $browse = null; + $icon = id(new PHUIIconView()) + ->setIconFont('fa-list-ul'); + + // TODO: This thing is ugly and the ugliness is not intentional. + // We have to give it text or PHUIButtonView collapses. It should likely + // just be an icon and look more integrated into the input. + $browse = id(new PHUIButtonView()) + ->setTag('a') + ->setIcon($icon) + ->addSigil('tokenizer-browse') + ->setColor(PHUIButtonView::GREY) + ->setSize(PHUIButtonView::SMALL) + ->setText(pht('Browse...')); + + $classes = array(); + $classes[] = 'jx-tokenizer-frame'; + if ($this->browseURI) { - $icon = id(new PHUIIconView()) - ->setIconFont('fa-list-ul'); - - // TODO: This thing is ugly and the ugliness is not intentional. - // We have to give it text or PHUIButtonView collapses. It should likely - // just be an icon and look more integrated into the input. - $browse = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon($icon) - ->addSigil('tokenizer-browse') - ->setColor(PHUIButtonView::GREY) - ->setSize(PHUIButtonView::SMALL) - ->setText(pht('Browse...')); + $classes[] = 'has-browse'; } $frame = javelin_tag( 'table', array( - 'class' => 'jx-tokenizer-frame', + 'class' => implode(' ', $classes), 'sigil' => 'tokenizer-frame', ), phutil_tag( diff --git a/webroot/rsrc/css/aphront/tokenizer.css b/webroot/rsrc/css/aphront/tokenizer.css --- a/webroot/rsrc/css/aphront/tokenizer.css +++ b/webroot/rsrc/css/aphront/tokenizer.css @@ -109,7 +109,15 @@ width: 100%; } -.jx-tokenizer-frame-input { +.jx-tokenizer-frame .jx-tokenizer-frame-browse { + display: none; +} + +.has-browse .jx-tokenizer-frame-browse { + display: table-cell; +} + +.jx-tokenizer-frame td.jx-tokenizer-frame-input { width: 100%; } diff --git a/webroot/rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js b/webroot/rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js --- a/webroot/rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js +++ b/webroot/rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js @@ -85,6 +85,7 @@ } if (this._frame) { + JX.DOM.alterClass(this._frame, 'has-browse', !!this.getBrowseURI()); JX.DOM.listen( this._frame, 'click', diff --git a/webroot/rsrc/js/application/herald/HeraldRuleEditor.js b/webroot/rsrc/js/application/herald/HeraldRuleEditor.js --- a/webroot/rsrc/js/application/herald/HeraldRuleEditor.js +++ b/webroot/rsrc/js/application/herald/HeraldRuleEditor.js @@ -277,25 +277,21 @@ }, _newTokenizer : function(type) { - var template = JX.$N( - 'div', - JX.$H(this._config.template.markup)); - template = template.firstChild; - template.id = ''; - var tokenizerConfig = { - root : template, src : this._config.template.source[type].uri, placeholder: this._config.template.source[type].placeholder, + browseURI: this._config.template.source[type].browseURI, icons : this._config.template.icons, username : this._config.username }; - var build = JX.Prefab.buildTokenizer(tokenizerConfig); + var build = JX.Prefab.newTokenizerFromTemplate( + this._config.template.markup, + tokenizerConfig); build.tokenizer.start(); return [ - template, + build.node, function() { return build.tokenizer.getTokens(); }, diff --git a/webroot/rsrc/js/application/maniphest/behavior-batch-editor.js b/webroot/rsrc/js/application/maniphest/behavior-batch-editor.js --- a/webroot/rsrc/js/application/maniphest/behavior-batch-editor.js +++ b/webroot/rsrc/js/application/maniphest/behavior-batch-editor.js @@ -136,18 +136,14 @@ }); function build_tokenizer(tconfig) { - var template = JX.$N('div', JX.$H(config.tokenizerTemplate)).firstChild; - template.id = ''; - - var build_config = JX.copy({}, tconfig); - build_config.root = template; - - var built = JX.Prefab.buildTokenizer(build_config); + var built = JX.Prefab.newTokenizerFromTemplate( + config.tokenizerTemplate, + JX.copy({}, tconfig)); built.tokenizer.start(); return { object: built.tokenizer, - template: template + template: built.node }; } diff --git a/webroot/rsrc/js/application/policy/behavior-policy-rule-editor.js b/webroot/rsrc/js/application/policy/behavior-policy-rule-editor.js --- a/webroot/rsrc/js/application/policy/behavior-policy-rule-editor.js +++ b/webroot/rsrc/js/application/policy/behavior-policy-rule-editor.js @@ -118,17 +118,20 @@ switch (type) { case 'tokenizer': - node = JX.$H(template.markup).getNode(); - node.id = ''; - var options = { - root: node, src: template.uri, placeholder: template.placeholder, + browseURI: template.browseURI, limit: template.limit }; - var tokenizer = JX.Prefab.buildTokenizer(options).tokenizer; + var build = JX.Prefab.newTokenizerFromTemplate( + template.markup, + options); + + node = build.node; + + var tokenizer = build.tokenizer; tokenizer.start(); get_fn = function() { return JX.keys(tokenizer.getTokens()); }; diff --git a/webroot/rsrc/js/core/Prefab.js b/webroot/rsrc/js/core/Prefab.js --- a/webroot/rsrc/js/core/Prefab.js +++ b/webroot/rsrc/js/core/Prefab.js @@ -31,6 +31,17 @@ return select; }, + newTokenizerFromTemplate: function(markup, config) { + var template = JX.$H(markup).getFragment().firstChild; + var container = JX.DOM.find(template, 'div', 'tokenizer-container'); + + container.id = ''; + config.root = container; + + var build = JX.Prefab.buildTokenizer(config); + build.node = template; + return build; + }, /** * Build a Phabricator tokenizer out of a configuration with application