@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

DarkConsole: fix rendering, move request log, load over ajax

Summary:
This accomplishes three major goals:

# Fixes phutil_render_tag -> phutil_tag callsites in DarkConsole.
# Moves the Ajax request log to a new panel on the left. This panel (and the tabs panel) get scrollbars when they get large, instead of making the page constantly scroll down.
# Loads the panel content over ajax, instead of dumping it into the page body / ajax response body. I've been planning to do this for about 3 years, which is why the plugins are architected the way they are. This should make debugging easier by making response bodies not be 50%+ darkconsole stuff.

Additionally, load the plugins dynamically (the old method predates library maps and PhutilSymbolLoader).

Test Plan:
{F30675}

- Switched between requests and tabs, reloaded page, saw same tab.
- Used "analyze queries", "profile page", triggered errors.
- Verified page does not load anything by default if dark console is closed with Charles.
- Generally banged on it a bit.

Reviewers: vrana, btrahan, chad

Reviewed By: vrana

CC: aran

Maniphest Tasks: T2432

Differential Revision: https://secure.phabricator.com/D4692

+556 -477
-1
scripts/celerity_mapper.php
··· 166 166 'javelin-behavior-maniphest-subpriority-editor', 167 167 ), 168 168 'darkconsole.pkg.js' => array( 169 - 'javelin-behavior-dark-console-ajax', 170 169 'javelin-behavior-dark-console', 171 170 'javelin-behavior-error-log', 172 171 ),
+74 -81
src/__celerity_resource_map__.php
··· 42 42 'disk' => '/rsrc/image/credit_cards.png', 43 43 'type' => 'png', 44 44 ), 45 + '/rsrc/image/darkload.gif' => 46 + array( 47 + 'hash' => '3a52cb7145d6e70f461fed21273117f2', 48 + 'uri' => '/res/3a52cb71/rsrc/image/darkload.gif', 49 + 'disk' => '/rsrc/image/darkload.gif', 50 + 'type' => 'gif', 51 + ), 45 52 '/rsrc/image/divot.png' => 46 53 array( 47 54 'hash' => '3be267bd11ea375bf68e808893718e0e', ··· 598 605 ), 599 606 'aphront-dark-console-css' => 600 607 array( 601 - 'uri' => '/res/1e1f78d4/rsrc/css/aphront/dark-console.css', 608 + 'uri' => '/res/63841304/rsrc/css/aphront/dark-console.css', 602 609 'type' => 'css', 603 610 'requires' => 604 611 array( ··· 1057 1064 ), 1058 1065 'javelin-behavior-aphront-form-disable-on-submit' => 1059 1066 array( 1060 - 'uri' => '/res/ca54e8b9/rsrc/js/application/core/behavior-form.js', 1067 + 'uri' => '/res/70fd43fd/rsrc/js/application/core/behavior-form.js', 1061 1068 'type' => 'js', 1062 1069 'requires' => 1063 1070 array( ··· 1145 1152 ), 1146 1153 'javelin-behavior-dark-console' => 1147 1154 array( 1148 - 'uri' => '/res/aa6f8a71/rsrc/js/application/core/behavior-dark-console.js', 1155 + 'uri' => '/res/c3e8a3d8/rsrc/js/application/core/behavior-dark-console.js', 1149 1156 'type' => 'js', 1150 1157 'requires' => 1151 1158 array( ··· 1155 1162 3 => 'javelin-dom', 1156 1163 4 => 'javelin-request', 1157 1164 5 => 'phabricator-keyboard-shortcut', 1158 - 6 => 'javelin-behavior-dark-console-ajax', 1159 1165 ), 1160 1166 'disk' => '/rsrc/js/application/core/behavior-dark-console.js', 1161 1167 ), 1162 - 'javelin-behavior-dark-console-ajax' => 1163 - array( 1164 - 'uri' => '/res/ac3ab63a/rsrc/js/application/core/behavior-dark-console-ajax.js', 1165 - 'type' => 'js', 1166 - 'requires' => 1167 - array( 1168 - 0 => 'javelin-behavior', 1169 - 1 => 'javelin-dom', 1170 - ), 1171 - 'disk' => '/rsrc/js/application/core/behavior-dark-console-ajax.js', 1172 - ), 1173 1168 'javelin-behavior-device' => 1174 1169 array( 1175 1170 'uri' => '/res/a10b851b/rsrc/js/application/core/behavior-device.js', ··· 1493 1488 ), 1494 1489 'javelin-behavior-lightbox-attachments' => 1495 1490 array( 1496 - 'uri' => '/res/5efba371/rsrc/js/application/core/behavior-lightbox-attachments.js', 1491 + 'uri' => '/res/08f5e202/rsrc/js/application/core/behavior-lightbox-attachments.js', 1497 1492 'type' => 'js', 1498 1493 'requires' => 1499 1494 array( ··· 1986 1981 ), 1987 1982 'javelin-dom' => 1988 1983 array( 1989 - 'uri' => '/res/2826c532/rsrc/js/javelin/lib/DOM.js', 1984 + 'uri' => '/res/459f3c08/rsrc/js/javelin/lib/DOM.js', 1990 1985 'type' => 'js', 1991 1986 'requires' => 1992 1987 array( ··· 3393 3388 'uri' => '/res/pkg/023adc14/core.pkg.css', 3394 3389 'type' => 'css', 3395 3390 ), 3396 - '66dca903' => 3391 + 'b3c1b6e7' => 3397 3392 array( 3398 3393 'name' => 'core.pkg.js', 3399 3394 'symbols' => ··· 3432 3427 31 => 'javelin-behavior-global-drag-and-drop', 3433 3428 32 => 'javelin-behavior-phabricator-home-reveal-tiles', 3434 3429 ), 3435 - 'uri' => '/res/pkg/66dca903/core.pkg.js', 3430 + 'uri' => '/res/pkg/b3c1b6e7/core.pkg.js', 3436 3431 'type' => 'js', 3437 3432 ), 3438 - '8edbada5' => 3433 + '032118cf' => 3439 3434 array( 3440 3435 'name' => 'darkconsole.pkg.js', 3441 3436 'symbols' => 3442 3437 array( 3443 - 0 => 'javelin-behavior-dark-console-ajax', 3444 - 1 => 'javelin-behavior-dark-console', 3445 - 2 => 'javelin-behavior-error-log', 3438 + 0 => 'javelin-behavior-dark-console', 3439 + 1 => 'javelin-behavior-error-log', 3446 3440 ), 3447 - 'uri' => '/res/pkg/8edbada5/darkconsole.pkg.js', 3441 + 'uri' => '/res/pkg/032118cf/darkconsole.pkg.js', 3448 3442 'type' => 'js', 3449 3443 ), 3450 3444 'ec01d039' => ··· 3521 3515 'uri' => '/res/pkg/f96657b8/diffusion.pkg.js', 3522 3516 'type' => 'js', 3523 3517 ), 3524 - 'fbeded59' => 3518 + '1c6f020b' => 3525 3519 array( 3526 3520 'name' => 'javelin.pkg.js', 3527 3521 'symbols' => ··· 3546 3540 17 => 'javelin-typeahead-ondemand-source', 3547 3541 18 => 'javelin-tokenizer', 3548 3542 ), 3549 - 'uri' => '/res/pkg/fbeded59/javelin.pkg.js', 3543 + 'uri' => '/res/pkg/1c6f020b/javelin.pkg.js', 3550 3544 'type' => 'js', 3551 3545 ), 3552 3546 'e30a3fa8' => ··· 3608 3602 'diffusion-icons-css' => 'c8ce2d88', 3609 3603 'global-drag-and-drop-css' => '023adc14', 3610 3604 'inline-comment-summary-css' => 'ec01d039', 3611 - 'javelin-aphlict' => '66dca903', 3612 - 'javelin-behavior' => 'fbeded59', 3613 - 'javelin-behavior-aphlict-dropdown' => '66dca903', 3614 - 'javelin-behavior-aphlict-listen' => '66dca903', 3615 - 'javelin-behavior-aphront-basic-tokenizer' => '66dca903', 3605 + 'javelin-aphlict' => 'b3c1b6e7', 3606 + 'javelin-behavior' => '1c6f020b', 3607 + 'javelin-behavior-aphlict-dropdown' => 'b3c1b6e7', 3608 + 'javelin-behavior-aphlict-listen' => 'b3c1b6e7', 3609 + 'javelin-behavior-aphront-basic-tokenizer' => 'b3c1b6e7', 3616 3610 'javelin-behavior-aphront-drag-and-drop' => '310cd201', 3617 3611 'javelin-behavior-aphront-drag-and-drop-textarea' => '310cd201', 3618 - 'javelin-behavior-aphront-form-disable-on-submit' => '66dca903', 3612 + 'javelin-behavior-aphront-form-disable-on-submit' => 'b3c1b6e7', 3619 3613 'javelin-behavior-audit-preview' => 'f96657b8', 3620 - 'javelin-behavior-dark-console' => '8edbada5', 3621 - 'javelin-behavior-dark-console-ajax' => '8edbada5', 3622 - 'javelin-behavior-device' => '66dca903', 3614 + 'javelin-behavior-dark-console' => '032118cf', 3615 + 'javelin-behavior-device' => 'b3c1b6e7', 3623 3616 'javelin-behavior-differential-accept-with-errors' => '310cd201', 3624 3617 'javelin-behavior-differential-add-reviewers-and-ccs' => '310cd201', 3625 3618 'javelin-behavior-differential-comment-jump' => '310cd201', ··· 3634 3627 'javelin-behavior-differential-user-select' => '310cd201', 3635 3628 'javelin-behavior-diffusion-commit-graph' => 'f96657b8', 3636 3629 'javelin-behavior-diffusion-pull-lastmodified' => 'f96657b8', 3637 - 'javelin-behavior-error-log' => '8edbada5', 3638 - 'javelin-behavior-global-drag-and-drop' => '66dca903', 3639 - 'javelin-behavior-konami' => '66dca903', 3640 - 'javelin-behavior-lightbox-attachments' => '66dca903', 3630 + 'javelin-behavior-error-log' => '032118cf', 3631 + 'javelin-behavior-global-drag-and-drop' => 'b3c1b6e7', 3632 + 'javelin-behavior-konami' => 'b3c1b6e7', 3633 + 'javelin-behavior-lightbox-attachments' => 'b3c1b6e7', 3641 3634 'javelin-behavior-maniphest-batch-selector' => '7707de41', 3642 3635 'javelin-behavior-maniphest-subpriority-editor' => '7707de41', 3643 3636 'javelin-behavior-maniphest-transaction-controls' => '7707de41', 3644 3637 'javelin-behavior-maniphest-transaction-expand' => '7707de41', 3645 3638 'javelin-behavior-maniphest-transaction-preview' => '7707de41', 3646 - 'javelin-behavior-phabricator-active-nav' => '66dca903', 3647 - 'javelin-behavior-phabricator-autofocus' => '66dca903', 3648 - 'javelin-behavior-phabricator-home-reveal-tiles' => '66dca903', 3649 - 'javelin-behavior-phabricator-keyboard-shortcuts' => '66dca903', 3650 - 'javelin-behavior-phabricator-nav' => '66dca903', 3639 + 'javelin-behavior-phabricator-active-nav' => 'b3c1b6e7', 3640 + 'javelin-behavior-phabricator-autofocus' => 'b3c1b6e7', 3641 + 'javelin-behavior-phabricator-home-reveal-tiles' => 'b3c1b6e7', 3642 + 'javelin-behavior-phabricator-keyboard-shortcuts' => 'b3c1b6e7', 3643 + 'javelin-behavior-phabricator-nav' => 'b3c1b6e7', 3651 3644 'javelin-behavior-phabricator-object-selector' => '310cd201', 3652 - 'javelin-behavior-phabricator-oncopy' => '66dca903', 3653 - 'javelin-behavior-phabricator-remarkup-assist' => '66dca903', 3654 - 'javelin-behavior-phabricator-search-typeahead' => '66dca903', 3655 - 'javelin-behavior-phabricator-tooltips' => '66dca903', 3656 - 'javelin-behavior-phabricator-watch-anchor' => '66dca903', 3657 - 'javelin-behavior-refresh-csrf' => '66dca903', 3645 + 'javelin-behavior-phabricator-oncopy' => 'b3c1b6e7', 3646 + 'javelin-behavior-phabricator-remarkup-assist' => 'b3c1b6e7', 3647 + 'javelin-behavior-phabricator-search-typeahead' => 'b3c1b6e7', 3648 + 'javelin-behavior-phabricator-tooltips' => 'b3c1b6e7', 3649 + 'javelin-behavior-phabricator-watch-anchor' => 'b3c1b6e7', 3650 + 'javelin-behavior-refresh-csrf' => 'b3c1b6e7', 3658 3651 'javelin-behavior-repository-crossreference' => '310cd201', 3659 - 'javelin-behavior-toggle-class' => '66dca903', 3660 - 'javelin-behavior-workflow' => '66dca903', 3661 - 'javelin-dom' => 'fbeded59', 3662 - 'javelin-event' => 'fbeded59', 3663 - 'javelin-install' => 'fbeded59', 3664 - 'javelin-json' => 'fbeded59', 3665 - 'javelin-mask' => 'fbeded59', 3666 - 'javelin-request' => 'fbeded59', 3667 - 'javelin-resource' => 'fbeded59', 3668 - 'javelin-stratcom' => 'fbeded59', 3669 - 'javelin-tokenizer' => 'fbeded59', 3670 - 'javelin-typeahead' => 'fbeded59', 3671 - 'javelin-typeahead-normalizer' => 'fbeded59', 3672 - 'javelin-typeahead-ondemand-source' => 'fbeded59', 3673 - 'javelin-typeahead-preloaded-source' => 'fbeded59', 3674 - 'javelin-typeahead-source' => 'fbeded59', 3675 - 'javelin-uri' => 'fbeded59', 3676 - 'javelin-util' => 'fbeded59', 3677 - 'javelin-vector' => 'fbeded59', 3678 - 'javelin-workflow' => 'fbeded59', 3652 + 'javelin-behavior-toggle-class' => 'b3c1b6e7', 3653 + 'javelin-behavior-workflow' => 'b3c1b6e7', 3654 + 'javelin-dom' => '1c6f020b', 3655 + 'javelin-event' => '1c6f020b', 3656 + 'javelin-install' => '1c6f020b', 3657 + 'javelin-json' => '1c6f020b', 3658 + 'javelin-mask' => '1c6f020b', 3659 + 'javelin-request' => '1c6f020b', 3660 + 'javelin-resource' => '1c6f020b', 3661 + 'javelin-stratcom' => '1c6f020b', 3662 + 'javelin-tokenizer' => '1c6f020b', 3663 + 'javelin-typeahead' => '1c6f020b', 3664 + 'javelin-typeahead-normalizer' => '1c6f020b', 3665 + 'javelin-typeahead-ondemand-source' => '1c6f020b', 3666 + 'javelin-typeahead-preloaded-source' => '1c6f020b', 3667 + 'javelin-typeahead-source' => '1c6f020b', 3668 + 'javelin-uri' => '1c6f020b', 3669 + 'javelin-util' => '1c6f020b', 3670 + 'javelin-vector' => '1c6f020b', 3671 + 'javelin-workflow' => '1c6f020b', 3679 3672 'lightbox-attachment-css' => '023adc14', 3680 3673 'maniphest-task-summary-css' => 'e30a3fa8', 3681 3674 'maniphest-transaction-detail-css' => 'e30a3fa8', 3682 - 'phabricator-busy' => '66dca903', 3675 + 'phabricator-busy' => 'b3c1b6e7', 3683 3676 'phabricator-content-source-view-css' => 'ec01d039', 3684 3677 'phabricator-core-buttons-css' => '023adc14', 3685 3678 'phabricator-core-css' => '023adc14', 3686 3679 'phabricator-crumbs-view-css' => '023adc14', 3687 3680 'phabricator-directory-css' => '023adc14', 3688 3681 'phabricator-drag-and-drop-file-upload' => '310cd201', 3689 - 'phabricator-dropdown-menu' => '66dca903', 3690 - 'phabricator-file-upload' => '66dca903', 3682 + 'phabricator-dropdown-menu' => 'b3c1b6e7', 3683 + 'phabricator-file-upload' => 'b3c1b6e7', 3691 3684 'phabricator-filetree-view-css' => '023adc14', 3692 3685 'phabricator-flag-css' => '023adc14', 3693 3686 'phabricator-form-view-css' => '023adc14', 3694 3687 'phabricator-header-view-css' => '023adc14', 3695 3688 'phabricator-jump-nav' => '023adc14', 3696 - 'phabricator-keyboard-shortcut' => '66dca903', 3697 - 'phabricator-keyboard-shortcut-manager' => '66dca903', 3689 + 'phabricator-keyboard-shortcut' => 'b3c1b6e7', 3690 + 'phabricator-keyboard-shortcut-manager' => 'b3c1b6e7', 3698 3691 'phabricator-main-menu-view' => '023adc14', 3699 - 'phabricator-menu-item' => '66dca903', 3692 + 'phabricator-menu-item' => 'b3c1b6e7', 3700 3693 'phabricator-nav-view-css' => '023adc14', 3701 - 'phabricator-notification' => '66dca903', 3694 + 'phabricator-notification' => 'b3c1b6e7', 3702 3695 'phabricator-notification-css' => '023adc14', 3703 3696 'phabricator-notification-menu-css' => '023adc14', 3704 3697 'phabricator-object-item-list-view-css' => '023adc14', 3705 3698 'phabricator-object-selector-css' => 'ec01d039', 3706 - 'phabricator-paste-file-upload' => '66dca903', 3707 - 'phabricator-prefab' => '66dca903', 3699 + 'phabricator-paste-file-upload' => 'b3c1b6e7', 3700 + 'phabricator-prefab' => 'b3c1b6e7', 3708 3701 'phabricator-project-tag-css' => 'e30a3fa8', 3709 3702 'phabricator-remarkup-css' => '023adc14', 3710 3703 'phabricator-shaped-request' => '310cd201', 3711 3704 'phabricator-side-menu-view-css' => '023adc14', 3712 3705 'phabricator-standard-page-view' => '023adc14', 3713 - 'phabricator-textareautils' => '66dca903', 3714 - 'phabricator-tooltip' => '66dca903', 3706 + 'phabricator-textareautils' => 'b3c1b6e7', 3707 + 'phabricator-tooltip' => 'b3c1b6e7', 3715 3708 'phabricator-transaction-view-css' => '023adc14', 3716 3709 'phabricator-zindex-css' => '023adc14', 3717 3710 'sprite-apps-large-css' => '023adc14',
+2
src/__phutil_library_map__.php
··· 218 218 'ConpherenceViewController' => 'applications/conpherence/controller/ConpherenceViewController.php', 219 219 'DarkConsoleController' => 'aphront/console/DarkConsoleController.php', 220 220 'DarkConsoleCore' => 'aphront/console/DarkConsoleCore.php', 221 + 'DarkConsoleDataController' => 'aphront/console/DarkConsoleDataController.php', 221 222 'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/DarkConsoleErrorLogPlugin.php', 222 223 'DarkConsoleErrorLogPluginAPI' => 'aphront/console/plugin/errorlog/DarkConsoleErrorLogPluginAPI.php', 223 224 'DarkConsoleEventPlugin' => 'aphront/console/plugin/DarkConsoleEventPlugin.php', ··· 1693 1694 'ConpherenceUpdateController' => 'ConpherenceController', 1694 1695 'ConpherenceViewController' => 'ConpherenceController', 1695 1696 'DarkConsoleController' => 'PhabricatorController', 1697 + 'DarkConsoleDataController' => 'PhabricatorController', 1696 1698 'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin', 1697 1699 'DarkConsoleEventPlugin' => 'DarkConsolePlugin', 1698 1700 'DarkConsoleEventPluginAPI' => 'PhutilEventListener',
+4 -1
src/aphront/configuration/AphrontDefaultApplicationConfiguration.php
··· 73 73 'profile/(?P<phid>[^/]+)/' => 'PhabricatorXHProfProfileController', 74 74 ), 75 75 76 - '/~/' => 'DarkConsoleController', 76 + '/~/' => array( 77 + '' => 'DarkConsoleController', 78 + 'data/(?P<key>[^/]+)/' => 'DarkConsoleDataController', 79 + ), 77 80 78 81 '/search/' => array( 79 82 '' => 'PhabricatorSearchController',
+54 -149
src/aphront/console/DarkConsoleCore.php
··· 5 5 */ 6 6 final class DarkConsoleCore { 7 7 8 - const PLUGIN_ERRORLOG = 'ErrorLog'; 9 - const PLUGIN_SERVICES = 'Services'; 10 - const PLUGIN_EVENT = 'Event'; 11 - const PLUGIN_XHPROF = 'XHProf'; 12 - const PLUGIN_REQUEST = 'Request'; 13 - 14 - public static function getPlugins() { 15 - return array( 16 - self::PLUGIN_ERRORLOG, 17 - self::PLUGIN_REQUEST, 18 - self::PLUGIN_SERVICES, 19 - self::PLUGIN_EVENT, 20 - self::PLUGIN_XHPROF, 21 - ); 22 - } 23 - 24 8 private $plugins = array(); 25 - private $settings; 26 - private $coredata; 9 + const STORAGE_VERSION = 1; 27 10 28 - public function getPlugin($plugin_name) { 29 - return idx($this->plugins, $plugin_name); 30 - } 11 + public function __construct() { 12 + $symbols = id(new PhutilSymbolLoader()) 13 + ->setType('class') 14 + ->setAncestorClass('DarkConsolePlugin') 15 + ->selectAndLoadSymbols(); 31 16 32 - public function __construct() { 33 - foreach (self::getPlugins() as $plugin_name) { 34 - $plugin = self::newPlugin($plugin_name); 35 - if ($plugin->isPermanent() || !isset($disabled[$plugin_name])) { 36 - if ($plugin->shouldStartup()) { 37 - $plugin->didStartup(); 38 - $plugin->setConsoleCore($this); 39 - $this->plugins[$plugin_name] = $plugin; 40 - } 17 + foreach ($symbols as $symbol) { 18 + $plugin = newv($symbol['name'], array()); 19 + if (!$plugin->shouldStartup()) { 20 + continue; 41 21 } 22 + $plugin->setConsoleCore($this); 23 + $plugin->didStartup(); 24 + $this->plugins[$symbol['name']] = $plugin; 42 25 } 43 26 } 44 27 45 - public static function newPlugin($plugin) { 46 - $class = 'DarkConsole'.$plugin.'Plugin'; 47 - return newv($class, array()); 48 - } 49 - 50 - public function getEnabledPlugins() { 28 + public function getPlugins() { 51 29 return $this->plugins; 52 30 } 53 31 54 - public function render(AphrontRequest $request) { 55 - 56 - $user = $request->getUser(); 57 - 58 - $plugins = $this->getEnabledPlugins(); 32 + public function getKey(AphrontRequest $request) { 33 + $plugins = $this->getPlugins(); 59 34 60 35 foreach ($plugins as $plugin) { 61 36 $plugin->setRequest($request); ··· 70 45 $plugin->setData($plugin->generateData()); 71 46 } 72 47 73 - $selected = $user->getConsoleTab(); 74 - $visible = $user->getConsoleVisible(); 48 + $plugins = msort($plugins, 'getOrderKey'); 75 49 76 - if (!isset($plugins[$selected])) { 77 - $selected = head_key($plugins); 78 - } 50 + $key = Filesystem::readRandomCharacters(24); 79 51 80 52 $tabs = array(); 81 - foreach ($plugins as $key => $plugin) { 82 - $tabs[$key] = array( 53 + $data = array(); 54 + foreach ($plugins as $plugin) { 55 + $class = get_class($plugin); 56 + $tabs[] = array( 57 + 'class' => $class, 83 58 'name' => $plugin->getName(), 84 - 'panel' => $plugin->render(), 59 + 'color' => $plugin->getColor(), 85 60 ); 61 + $data[$class] = $plugin->getData(); 86 62 } 87 63 88 - $tabs_markup = array(); 89 - $panel_markup = array(); 90 - foreach ($tabs as $key => $data) { 91 - $is_selected = ($key == $selected); 92 - if ($is_selected) { 93 - $style = null; 94 - $tabclass = 'dark-console-tab-selected'; 95 - } else { 96 - $style = 'display: none;'; 97 - $tabclass = null; 98 - } 64 + $storage = array( 65 + 'vers' => self::STORAGE_VERSION, 66 + 'tabs' => $tabs, 67 + 'data' => $data, 68 + 'user' => $request->getUser() 69 + ? $request->getUser()->getPHID() 70 + : null, 71 + ); 72 + 73 + $cache = new PhabricatorKeyValueDatabaseCache(); 74 + $cache = new PhutilKeyValueCacheProfiler($cache); 75 + $cache->setProfiler(PhutilServiceProfiler::getInstance()); 76 + 77 + $cache->setKeys( 78 + array( 79 + 'darkconsole:'.$key => json_encode($storage), 80 + ), 81 + $ttl = (60 * 60 * 6)); 99 82 100 - $tabs_markup[] = javelin_tag( 101 - 'a', 102 - array( 103 - 'class' => "dark-console-tab {$tabclass}", 104 - 'sigil' => 'dark-console-tab', 105 - 'id' => 'dark-console-tab-'.$key, 106 - ), 107 - (string)$data['name']); 83 + return $key; 84 + } 108 85 109 - $panel_markup[] = javelin_render_tag( 110 - 'div', 111 - array( 112 - 'class' => 'dark-console-panel dark-console-panel-'.$key, 113 - 'style' => $style, 114 - 'sigil' => 'dark-console-panel', 115 - ), 116 - (string)$data['panel']); 117 - } 86 + public function render(AphrontRequest $request) { 87 + $user = $request->getUser(); 88 + $visible = $user ? $user->getConsoleVisible() : true; 118 89 119 - $console = javelin_render_tag( 120 - 'table', 90 + return javelin_tag( 91 + 'div', 121 92 array( 93 + 'id' => 'darkconsole', 122 94 'class' => 'dark-console', 123 - 'sigil' => 'dark-console', 124 95 'style' => $visible ? '' : 'display: none;', 96 + 'data-console-key' => $this->getKey($request), 125 97 ), 126 - '<tr>'. 127 - '<th class="dark-console-tabs">'. 128 - implode("\n", $tabs_markup). 129 - '</th>'. 130 - '<td>'.implode("\n", $panel_markup).'</td>'. 131 - '</tr>'); 98 + ''); 99 + } 132 100 133 - if (!empty($_COOKIE['phsid'])) { 134 - $console = str_replace( 135 - $_COOKIE['phsid'], 136 - phutil_escape_html('<session-key>'), 137 - $console); 138 - } 139 - 140 - if ($request->isAjax()) { 141 - 142 - // for ajax this HTML gets updated on the client 143 - $request_history = null; 144 - 145 - } else { 146 - 147 - $request_table_header = 148 - '<div class="dark-console-panel-request-log-separator"></div>'; 149 - 150 - $rows = array(); 151 - 152 - $table = new AphrontTableView($rows); 153 - $table->setHeaders( 154 - array( 155 - 'Sequence', 156 - 'Type', 157 - 'URI', 158 - )); 159 - $table->setColumnClasses( 160 - array( 161 - '', 162 - '', 163 - 'wide', 164 - )); 165 - 166 - $request_table = $request_table_header . $table->render(); 167 - $request_history = javelin_render_tag( 168 - 'table', 169 - array( 170 - 'class' => 'dark-console dark-console-request-log', 171 - 'sigil' => 'dark-console-request-log', 172 - 'style' => $visible ? '' : 'display: none;', 173 - ), 174 - '<tr>'. 175 - '<th class="dark-console-tabs">'. 176 - phutil_tag( 177 - 'a', 178 - array( 179 - 'class' => 'dark-console-tab dark-console-tab-selected', 180 - ), 181 - 'Request Log'). 182 - '</th>'. 183 - '<td>'. 184 - javelin_render_tag( 185 - 'div', 186 - array( 187 - 'class' => 'dark-console-panel dark-console-panel-RequestLog', 188 - ), 189 - $request_table). 190 - '</td>'. 191 - '</tr>'); 192 - } 193 - 194 - return "\n\n\n\n".$console.$request_history."\n\n\n\n"; 195 - } 196 101 } 197 102
+69
src/aphront/console/DarkConsoleDataController.php
··· 1 + <?php 2 + 3 + /** 4 + * @group console 5 + */ 6 + final class DarkConsoleDataController extends PhabricatorController { 7 + 8 + private $key; 9 + 10 + public function willProcessRequest(array $data) { 11 + $this->key = $data['key']; 12 + } 13 + 14 + public function processRequest() { 15 + $request = $this->getRequest(); 16 + $user = $request->getUser(); 17 + 18 + $cache = new PhabricatorKeyValueDatabaseCache(); 19 + $cache = new PhutilKeyValueCacheProfiler($cache); 20 + $cache->setProfiler(PhutilServiceProfiler::getInstance()); 21 + 22 + $result = $cache->getKey('darkconsole:'.$this->key); 23 + if (!$result) { 24 + return new Aphront400Response(); 25 + } 26 + 27 + $result = json_decode($result, true); 28 + 29 + if (!is_array($result)) { 30 + return new Aphront400Response(); 31 + } 32 + 33 + if ($result['vers'] != DarkConsoleCore::STORAGE_VERSION) { 34 + return new Aphront400Response(); 35 + } 36 + 37 + if ($result['user'] != $user->getPHID()) { 38 + return new Aphront400Response(); 39 + } 40 + 41 + $output = array(); 42 + $output['tabs'] = $result['tabs']; 43 + $output['panel'] = array(); 44 + 45 + foreach ($result['data'] as $class => $data) { 46 + try { 47 + $obj = newv($class, array()); 48 + $obj->setData($data); 49 + $obj->setRequest($request); 50 + 51 + $panel = $obj->renderPanel(); 52 + 53 + if (!empty($_COOKIE['phsid'])) { 54 + $panel = str_replace( 55 + $_COOKIE['phsid'], 56 + '(session-key)', 57 + $panel); 58 + } 59 + 60 + $output['panel'][$class] = $panel; 61 + } catch (Exception $ex) { 62 + $output['panel'][$class] = 'error'; 63 + } 64 + } 65 + 66 + return id(new AphrontAjaxResponse())->setContent($output); 67 + } 68 + 69 + }
+13 -10
src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php
··· 7 7 8 8 public function getName() { 9 9 $count = count($this->getData()); 10 - 11 10 if ($count) { 12 - return 13 - '<span style="color: #ff0000;">&bull;</span> '. 14 - "Error Log ({$count})"; 11 + return pht('Error Log (%d)', $count); 15 12 } 13 + return pht('Error Log'); 14 + } 16 15 17 - return 'Error Log'; 16 + public function getOrder() { 17 + return 0; 18 18 } 19 19 20 + public function getColor() { 21 + if (count($this->getData())) { 22 + return '#ff0000'; 23 + } 24 + return null; 25 + } 20 26 21 27 public function getDescription() { 22 - return 'Shows errors and warnings.'; 28 + return pht('Shows errors and warnings.'); 23 29 } 24 30 25 - 26 31 public function generateData() { 27 32 return DarkConsoleErrorLogPluginAPI::getErrors(); 28 33 } 29 34 30 - 31 - public function render() { 32 - 35 + public function renderPanel() { 33 36 $data = $this->getData(); 34 37 35 38 $rows = array();
+1 -1
src/aphront/console/plugin/DarkConsoleEventPlugin.php
··· 37 37 ); 38 38 } 39 39 40 - public function render() { 40 + public function renderPanel() { 41 41 $data = $this->getData(); 42 42 43 43 $out = array();
+16 -5
src/aphront/console/plugin/DarkConsolePlugin.php
··· 11 11 12 12 abstract public function getName(); 13 13 abstract public function getDescription(); 14 - abstract public function render(); 14 + abstract public function renderPanel(); 15 15 16 16 public function __construct() { 17 17 18 + } 19 + 20 + public function getColor() { 21 + return null; 22 + } 23 + 24 + final public function getOrderKey() { 25 + return sprintf( 26 + '%09d%s', 27 + (int)(999999999 * $this->getOrder()), 28 + $this->getName()); 29 + } 30 + 31 + public function getOrder() { 32 + return 1.0; 18 33 } 19 34 20 35 public function setConsoleCore(DarkConsoleCore $core) { ··· 50 65 51 66 public function getRequestURI() { 52 67 return $this->getRequest()->getRequestURI(); 53 - } 54 - 55 - public function isPermanent() { 56 - return false; 57 68 } 58 69 59 70 public function shouldStartup() {
+1 -2
src/aphront/console/plugin/DarkConsoleRequestPlugin.php
··· 20 20 ); 21 21 } 22 22 23 - public function render() { 24 - 23 + public function renderPanel() { 25 24 $data = $this->getData(); 26 25 27 26 $sections = array(
+8 -3
src/aphront/console/plugin/DarkConsoleServicesPlugin.php
··· 136 136 'start' => PhabricatorStartup::getStartTime(), 137 137 'end' => microtime(true), 138 138 'log' => $log, 139 + 'analyzeURI' => (string)$this 140 + ->getRequestURI() 141 + ->alter('__analyze__', true), 142 + 'didAnalyze' => isset($_REQUEST['__analyze__']), 139 143 ); 140 144 } 141 145 142 - public function render() { 146 + public function renderPanel() { 143 147 $data = $this->getData(); 148 + 144 149 $log = $data['log']; 145 150 $results = array(); 146 151 ··· 149 154 phutil_tag( 150 155 'a', 151 156 array( 152 - 'href' => $this->getRequestURI()->alter('__analyze__', true), 153 - 'class' => isset($_REQUEST['__analyze__']) 157 + 'href' => $data['analyzeURI'], 158 + 'class' => $data['didAnalyze'] 154 159 ? 'disabled button' 155 160 : 'green button', 156 161 ),
+20 -10
src/aphront/console/plugin/DarkConsoleXHProfPlugin.php
··· 8 8 protected $xhprofID; 9 9 10 10 public function getName() { 11 - $run = $this->getData(); 11 + return 'XHProf'; 12 + } 12 13 13 - if ($run) { 14 - return '<span style="color: #ff00ff;">&bull;</span> XHProf'; 14 + public function getColor() { 15 + $data = $this->getData(); 16 + if ($data['xhprofID']) { 17 + return '#ff00ff'; 15 18 } 16 - 17 - return 'XHProf'; 19 + return null; 18 20 } 19 21 20 22 public function getDescription() { ··· 22 24 } 23 25 24 26 public function generateData() { 25 - return $this->xhprofID; 27 + return array( 28 + 'xhprofID' => $this->xhprofID, 29 + 'profileURI' => (string)$this 30 + ->getRequestURI() 31 + ->alter('__profile__', 'page'), 32 + ); 26 33 } 27 34 28 35 public function getXHProfRunID() { 29 36 return $this->xhprofID; 30 37 } 31 38 32 - public function render() { 39 + public function renderPanel() { 40 + $data = $this->getData(); 41 + 42 + $run = $data['xhprofID']; 43 + $profile_uri = $data['profileURI']; 44 + 33 45 if (!DarkConsoleXHProfPluginAPI::isProfilerAvailable()) { 34 46 $href = PhabricatorEnv::getDoclink('article/Installation_Guide.html'); 35 47 $install_guide = phutil_tag( ··· 49 61 50 62 $result = array(); 51 63 52 - $run = $this->getXHProfRunID(); 53 - 54 64 $header = 55 65 '<div class="dark-console-panel-header">'. 56 66 phutil_tag( 57 67 'a', 58 68 array( 59 - 'href' => $this->getRequestURI()->alter('__profile__', 'page'), 69 + 'href' => $profile_uri, 60 70 'class' => $run 61 71 ? 'disabled button' 62 72 : 'green button',
+3 -3
src/aphront/response/AphrontAjaxResponse.php
··· 38 38 $console = $this->getConsole(); 39 39 if ($console) { 40 40 Javelin::initBehavior( 41 - 'dark-console-ajax', 41 + 'dark-console', 42 42 array( 43 - 'console' => $console->render($this->getRequest()), 44 - 'uri' => (string) $this->getRequest()->getRequestURI(), 43 + 'uri' => (string)$this->getRequest()->getRequestURI(), 44 + 'key' => $console->getKey($this->getRequest()), 45 45 )); 46 46 } 47 47
+7 -4
src/view/page/PhabricatorStandardPageView.php
··· 164 164 Javelin::initBehavior( 165 165 'dark-console', 166 166 array( 167 - 'uri' => '/~/', 168 - 'request_uri' => $request ? (string) $request->getRequestURI() : '/', 167 + 'uri' => $request ? (string)$request->getRequestURI() : '?', 168 + 'selected' => $user ? $user->getConsoleTab() : null, 169 + 'visible' => $user ? (int)$user->getConsoleVisible() : true, 169 170 )); 170 171 171 172 // Change this to initBehavior when there is some behavior to initialize ··· 225 226 } 226 227 227 228 protected function willSendResponse($response) { 229 + $request = $this->getRequest(); 228 230 $response = parent::willSendResponse($response); 229 231 230 - $console = $this->getRequest()->getApplicationConfiguration()->getConsole(); 232 + $console = $request->getApplicationConfiguration()->getConsole(); 233 + 231 234 if ($console) { 232 235 $response = str_replace( 233 236 '<darkconsole />', 234 - $console->render($this->getRequest()), 237 + $console->render($request), 235 238 $response); 236 239 } 237 240
+84 -39
webroot/rsrc/css/aphront/dark-console.css
··· 3 3 */ 4 4 5 5 .dark-console { 6 - background: #555555; 6 + background: #444444; 7 7 color: #eeeeee; 8 8 width: 100%; 9 9 font-family: "Verdana"; ··· 11 11 position: relative; 12 12 } 13 13 14 - .dark-console a:link { 15 - color: inherit; 14 + .dark-console-requests, 15 + .dark-console-tabs { 16 + position: absolute; 17 + overflow-y: auto; 18 + top: 0; 19 + left: 0; 20 + bottom: 0; 21 + width: 15%; 22 + padding: 8px 0; 23 + } 24 + 25 + .dark-console-requests, 26 + .dark-console-tabs, 27 + .dark-console-panel, 28 + .dark-console-load { 29 + border-left: 1px solid #111111; 30 + box-shadow: -2px 0px 2px rgba(0, 0, 0, 0.25); 31 + } 32 + 33 + .dark-console-requests { 34 + background: #222222; 16 35 } 17 36 18 37 .dark-console-tabs { 19 - width: 180px; 20 - background: #222222; 21 - border-right: 1px solid #888888; 22 - padding: 2.5em 0em; 38 + background: #333333; 39 + left: 15%; 23 40 } 24 41 42 + .dark-console-panel, 43 + .dark-console-load { 44 + position: relative; 45 + min-height: 320px; 46 + } 25 47 26 - a.dark-console-tab { 27 - padding: .75em 12px; 28 - text-align: right; 29 - background: #444444; 30 - position: relative; 31 - border: 1px solid #666666; 48 + .dark-console-panel { 49 + margin-left: 30%; 50 + background: #444444; 51 + } 52 + 53 + .dark-console-requests a.dark-console-request, 54 + .dark-console-tabs a.dark-console-tab { 55 + display: block; 56 + padding: 6px; 57 + overflow: hidden; 58 + background: #444444; 59 + margin: 3px 0; 60 + color: #cccccc; 61 + border-color: #666666; 32 62 border-width: 1px 0; 33 - border-right-color: #888888; 34 - margin-bottom: 2px; 35 - display: block; 36 - color: #cccccc; 63 + border-style: solid; 64 + } 65 + 66 + .dark-console-requests a.dark-selected, 67 + .dark-console-tabs a.dark-selected { 68 + background: #0066aa; 37 69 } 38 70 39 - a.dark-console-tab-selected { 40 - margin-right: -1px; 41 - padding-right: 13px; 42 - background: #555555; 43 - border-color: #888888; 44 - border-right-color: #555555; 45 - color: #eeeeee; 71 + .dark-console-requests a.dark-console-request:hover, 72 + .dark-console-tabs a.dark-console-tab:hover { 73 + background: #1188cc; 74 + } 75 + 76 + .dark-console-tabs a.dark-console-tab { 77 + text-align: right; 46 78 } 47 79 80 + .dark-console-load { 81 + background-image: url(/rsrc/image/darkload.gif); 82 + background-position: center center; 83 + background-repeat: no-repeat; 84 + background-color: #000; 85 + margin-left: 15%; 86 + } 48 87 49 88 .dark-console .aphront-table-view { 89 + font-size: 11px; 50 90 background: #888888; 51 91 color: #eeeeee; 52 - margin: 1em 1%; 53 - width: 98%; 92 + width: 100%; 54 93 border-color: #333333; 94 + margin: 8px 0; 55 95 } 56 96 57 97 .dark-console .aphront-table-view th { 98 + text-shadow: none; 99 + font-family: "Verdana"; 100 + font-size: 11px; 58 101 background: #333333; 59 102 color: #ffffff; 103 + } 104 + 105 + .dark-console .aphront-table-view td { 106 + font-size: 11px; 60 107 } 61 108 62 109 .dark-console .aphront-table-view td.header { ··· 77 124 color: #dddddd; 78 125 } 79 126 80 - .dark-console-panel-ErrorLog { 81 - max-height: 500px; 82 - overflow: auto; 83 - } 84 - 85 - .dark-console-panel-error-details { 86 - display: none; 127 + .dark-console-panel-core { 128 + padding: 12px; 87 129 } 88 130 89 131 .explain-sev-1 { ··· 119 161 } 120 162 121 163 .dark-console-panel-header { 122 - background: #606060; 123 - border-bottom: 1px solid #505050; 124 - padding: .25em 1em .25em 0; 164 + padding: 8px 4px 0; 125 165 } 126 166 127 167 .dark-console-panel-header h1 { 128 - padding: 1em; 129 - font-size: 12px; 130 - font-weight: normal; 168 + font-size: 15px; 131 169 } 132 170 133 171 .dark-console-panel-header .button { ··· 168 206 height: 2px; 169 207 } 170 208 209 + .dark-console-panel-ErrorLog { 210 + max-height: 500px; 211 + overflow: auto; 212 + } 171 213 214 + .dark-console-panel-error-details { 215 + display: none; 216 + }
webroot/rsrc/image/darkload.gif

This is a binary file and will not be displayed.

-49
webroot/rsrc/js/application/core/behavior-dark-console-ajax.js
··· 1 - /** 2 - * @provides javelin-behavior-dark-console-ajax 3 - * @requires javelin-behavior 4 - * javelin-dom 5 - */ 6 - 7 - JX.behavior('dark-console-ajax', function(config) { 8 - var requestLog = JX.DOM.find(document.body, 9 - 'table', 10 - 'dark-console-request-log'); 11 - var requestTable = JX.DOM.find(requestLog, 'table'); 12 - var requestRows = JX.DOM.scry(requestTable, 'tr'); 13 - var requestNumber = requestRows.length - 1; // header don't count 14 - var requestURI = config.uri; 15 - var console = JX.$H(config.console); 16 - var newRowType = 'ajax'; 17 - 18 - var newRowNumber = JX.$N( 19 - 'a', 20 - { 21 - 'sigil' : 'request-log-number', 22 - 'meta' : { 'console' : console } 23 - }, 24 - requestNumber 25 - ); 26 - var newRowURI = JX.$N( 27 - 'a', 28 - { 29 - 'sigil' : 'request-log-uri', 30 - 'meta' : { 'console' : console } 31 - }, 32 - requestURI 33 - ); 34 - 35 - var newRow = JX.$N( 36 - 'tr', 37 - { 38 - 'className' : requestNumber % 2 ? 'alt' : '' 39 - }, 40 - [ 41 - JX.$N('td', {}, newRowNumber), 42 - JX.$N('td', {}, newRowType), 43 - JX.$N('td', {}, newRowURI) 44 - ] 45 - ); 46 - 47 - JX.DOM.appendContent(requestTable, newRow); 48 - 49 - });
+200 -119
webroot/rsrc/js/application/core/behavior-dark-console.js
··· 6 6 * javelin-dom 7 7 * javelin-request 8 8 * phabricator-keyboard-shortcut 9 - * javelin-behavior-dark-console-ajax 10 9 */ 11 10 12 - JX.behavior('dark-console', function(config) { 13 - var selected_tab = null; 11 + JX.behavior('dark-console', function(config, statics) { 12 + var root = statics.root || setup_console(); 14 13 15 - JX.Stratcom.listen( 16 - 'click', 17 - ['dark-console', 'dark-console-tab'], 18 - function(e) { 19 - var console = e.getNode('dark-console'); 20 - var tabs = JX.DOM.scry(console, 'a', 'dark-console-tab'); 21 - var panels = JX.DOM.scry(console, 'div', 'dark-console-panel'); 22 - var target = e.getTarget(); 23 - for (var ii = 0; ii < tabs.length; ii++) { 24 - JX.DOM.alterClass( 25 - tabs[ii], 26 - 'dark-console-tab-selected', 27 - tabs[ii] == target); 28 - (tabs[ii] != target ? JX.DOM.hide : JX.DOM.show)(panels[ii]); 14 + config.key = config.key || root.getAttribute('data-console-key'); 15 + add_request(config); 16 + 17 + 18 + // Do first-time setup. 19 + function setup_console() { 20 + statics.root = JX.$('darkconsole'); 21 + statics.req = {all: {}, current: null}; 22 + statics.tab = {all: {}, current: null}; 23 + 24 + statics.el = {}; 25 + 26 + statics.el.reqs = JX.$N('div', {className: 'dark-console-requests'}); 27 + statics.root.appendChild(statics.el.reqs); 28 + 29 + statics.el.tabs = JX.$N('div', {className: 'dark-console-tabs'}); 30 + statics.root.appendChild(statics.el.tabs); 31 + 32 + statics.el.panel = JX.$N('div', {className: 'dark-console-panel'}); 33 + statics.root.appendChild(statics.el.panel); 34 + 35 + statics.el.load = JX.$N('div', {className: 'dark-console-load'}); 36 + statics.root.appendChild(statics.el.load); 37 + 38 + statics.cache = {}; 39 + 40 + statics.visible = config.visible; 41 + statics.selected = config.selected; 42 + 43 + return statics.root; 44 + } 45 + 46 + 47 + // Add a new request to the console (initial page load, or new Ajax response). 48 + function add_request(config) { 49 + 50 + // Ignore DarkConsole data requests. 51 + if (config.uri.match(new RegExp('^/~/data/'))) { 52 + return; 53 + } 54 + 55 + var attr = { 56 + className: 'dark-console-request', 57 + sigil: 'dark-console-request', 58 + title: config.uri, 59 + meta: config, 60 + href: '#' 61 + }; 62 + 63 + var link = JX.$N('a', attr, config.uri); 64 + statics.el.reqs.appendChild(link); 65 + statics.req.all[config.key] = link; 66 + 67 + if (!statics.req.current) { 68 + select_request(config.key); 69 + } 70 + } 71 + 72 + 73 + // Select a request (on load, or when the user clicks one). 74 + function select_request(key) { 75 + var req = statics.req; 76 + 77 + if (req.current) { 78 + JX.DOM.alterClass(req.all[req.current], 'dark-selected', false); 79 + } 80 + statics.req.current = key; 81 + JX.DOM.alterClass(req.all[req.current], 'dark-selected', true); 82 + 83 + if (statics.visible) { 84 + JX.log('visible!'); 85 + draw_request(key); 86 + } 87 + } 88 + 89 + // When the user clicks a request, select it. 90 + JX.Stratcom.listen('click', 'dark-console-request', function(e) { 91 + e.kill(); 92 + select_request(e.getNodeData('dark-console-request').key); 93 + }); 94 + 95 + 96 + // After the user selects a request, draw its tabs. 97 + function draw_request(key) { 98 + var cache = statics.cache; 99 + 100 + if (cache[key]) { 101 + render_request(key); 102 + return; 103 + } 104 + 105 + new JX.Request( 106 + '/~/data/' + key + '/', 107 + function(r) { 108 + cache[key] = r; 109 + if (statics.req.current == key) { 110 + render_request(key); 111 + } 112 + }) 113 + .send(); 114 + 115 + show_loading(); 116 + } 117 + 118 + // Show the loading indicator. 119 + function show_loading() { 120 + JX.DOM.hide(statics.el.tabs); 121 + JX.DOM.hide(statics.el.panel); 122 + JX.DOM.show(statics.el.load); 123 + } 124 + 125 + // Hide the loading indicator. 126 + function hide_loading() { 127 + JX.DOM.show(statics.el.tabs); 128 + JX.DOM.show(statics.el.panel); 129 + JX.DOM.hide(statics.el.load); 130 + } 131 + 132 + function render_request(key) { 133 + var data = statics.cache[key]; 134 + 135 + statics.tab.all = {}; 136 + 137 + var links = []; 138 + var first = null; 139 + for (var ii = 0; ii < data.tabs.length; ii++) { 140 + var tab = data.tabs[ii]; 141 + var attr = { 142 + className: 'dark-console-tab', 143 + sigil: 'dark-console-tab', 144 + meta: tab, 145 + href: '#' 146 + }; 147 + 148 + var bullet = null; 149 + if (tab.color) { 150 + bullet = JX.$N('span', {style: {color: tab.color}}, "\u2022"); 29 151 } 30 152 31 - selected_tab = target.id.replace('dark-console-tab-', ''); 153 + var link = JX.$N('a', attr, [bullet, ' ', tab.name]); 154 + links.push(link); 155 + statics.tab.all[tab['class']] = link; 156 + first = first || tab['class']; 157 + } 32 158 33 - new JX.Request(config.uri, JX.bag) 34 - .setData({ tab : selected_tab }) 159 + JX.DOM.setContent(statics.el.tabs, links); 160 + 161 + if (statics.tab.current in statics.tab.all) { 162 + select_tab(statics.tab.current); 163 + } else if (statics.selected in statics.tab.all) { 164 + select_tab(statics.selected); 165 + } else { 166 + select_tab(first); 167 + } 168 + 169 + hide_loading(); 170 + } 171 + 172 + function select_tab(tclass) { 173 + var tabs = statics.tab; 174 + 175 + if (tabs.current) { 176 + JX.DOM.alterClass(tabs.current, 'dark-selected', false); 177 + } 178 + tabs.current = tabs.all[tclass]; 179 + JX.DOM.alterClass(tabs.current, 'dark-selected', true); 180 + 181 + if (tclass != statics.selected) { 182 + // Save user preference. 183 + new JX.Request('/~/', JX.bag) 184 + .setData({ tab : tclass }) 35 185 .send(); 36 - }); 186 + } 187 + 188 + draw_panel(); 189 + } 37 190 191 + // When the user clicks a tab, select it. 192 + JX.Stratcom.listen('click', 'dark-console-tab', function(e) { 193 + e.kill(); 194 + select_tab(e.getNodeData('dark-console-tab')['class']); 195 + }); 196 + 197 + function draw_panel() { 198 + var data = statics.cache[statics.req.current]; 199 + var tclass = JX.Stratcom.getData(statics.tab.current)['class']; 200 + var html = data.panel[tclass]; 201 + 202 + var div = JX.$N('div', {className: 'dark-console-panel-core'}, JX.$H(html)); 203 + JX.DOM.setContent(statics.el.panel, div); 204 + } 205 + 206 + // Install keyboard shortcut. 38 207 var desc = 'Toggle visibility of DarkConsole.'; 39 208 new JX.KeyboardShortcut('`', desc) 40 209 .setHandler(function(manager) { 41 - var console = JX.DOM.find(document.body, 'table', 'dark-console'); 42 - var requestLog = JX.DOM.find( 43 - document.body, 44 - 'table', 45 - 'dark-console-request-log'); 210 + statics.visible = !statics.visible; 46 211 47 - config.visible = !config.visible; 48 - if (config.visible) { 49 - JX.DOM.show(console); 50 - JX.DOM.show(requestLog); 212 + if (statics.visible) { 213 + JX.DOM.show(root); 214 + if (statics.req.current) { 215 + draw_request(statics.req.current); 216 + } 51 217 } else { 52 - JX.DOM.hide(console); 53 - JX.DOM.hide(requestLog); 218 + JX.DOM.hide(root); 54 219 } 55 220 56 - new JX.Request(config.uri, JX.bag) 57 - .setData({visible: config.visible ? 1 : 0}) 221 + // Save user preference. 222 + new JX.Request('/~/', JX.bag) 223 + .setData({visible: statics.visible ? 1 : 0}) 58 224 .send(); 59 225 60 226 // Force resize listeners to take effect. 61 227 JX.Stratcom.invoke('resize'); 62 228 }) 63 229 .register(); 64 - 65 - var initRequestLog = function() { 66 - var console = JX.DOM.find(document.body, 67 - 'table', 68 - 'dark-console'); 69 - var requestLog = JX.DOM.find(document.body, 70 - 'table', 71 - 'dark-console-request-log'); 72 - var requestTable = JX.DOM.find(requestLog, 'table'); 73 - var rows = JX.DOM.scry(requestTable, 'tr'); 74 - var tableHeader = rows[0]; 75 - var newRowNumber = JX.$N( 76 - 'a', 77 - { 78 - 'sigil' : 'request-log-number', 79 - 'meta' : { 'console' : console } 80 - }, 81 - "0" 82 - ); 83 - var newRowURI = JX.$N( 84 - 'a', 85 - { 86 - 'sigil' : 'request-log-uri', 87 - 'meta' : { 'console' : console } 88 - }, 89 - config.request_uri 90 - ); 91 - 92 - var newRow = JX.$N( 93 - 'tr', 94 - { 95 - 'className' : 'highlight' 96 - }, 97 - [ 98 - JX.$N('td', {}, newRowNumber), 99 - JX.$N('td', {}, 'main'), 100 - JX.$N('td', {}, newRowURI) 101 - ] 102 - ); 103 - 104 - JX.DOM.setContent(requestTable, [tableHeader, newRow]); 105 - } 106 - 107 - initRequestLog(); 108 - 109 - var updateActiveRequest = function(e) { 110 - var log = e.getNode('dark-console-request-log'); 111 - var table = JX.DOM.find(log, 'table'); 112 - var rows = JX.DOM.scry(table, 'tr'); 113 - var targetRow = e.getTarget().parentNode.parentNode; 114 - var data = JX.Stratcom.getData(e.getTarget()); 115 - var newConsole = data.console; 116 - for (var ii = 0; ii < rows.length; ii++) { 117 - JX.DOM.alterClass( 118 - rows[ii], 119 - 'highlight', 120 - rows[ii] == targetRow); 121 - } 122 - var console = JX.DOM.find(document.body, 'table', 'dark-console'); 123 - JX.DOM.replace(console, newConsole); 124 - if (selected_tab) { 125 - console = JX.DOM.find(document.body, 'table', 'dark-console'); 126 - var s_id = 'dark-console-tab-' + selected_tab; 127 - var tabs = JX.DOM.scry(console, 'a', 'dark-console-tab'); 128 - var panels = JX.DOM.scry(console, 'div', 'dark-console-panel'); 129 - for (var ii = 0; ii < tabs.length; ii++) { 130 - JX.DOM.alterClass( 131 - tabs[ii], 132 - 'dark-console-tab-selected', 133 - tabs[ii].id == s_id); 134 - (tabs[ii].id != s_id ? JX.DOM.hide : JX.DOM.show)(panels[ii]); 135 - } 136 - } 137 - } 138 - 139 - JX.Stratcom.listen( 140 - 'click', 141 - ['dark-console-request-log', 'request-log-number'], 142 - updateActiveRequest 143 - ); 144 - JX.Stratcom.listen( 145 - 'click', 146 - ['dark-console-request-log', 'request-log-uri'], 147 - updateActiveRequest 148 - ); 149 230 150 231 });