diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 41da76f4ce..0117dc7801 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,11 +7,11 @@ */ return array( 'names' => array( - 'core.pkg.css' => '97a49e3e', - 'core.pkg.js' => 'a5ed8c89', + 'core.pkg.css' => '68d4f4fb', + 'core.pkg.js' => '3bbe23c6', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '30602b8c', - 'differential.pkg.js' => '8c98ce21', + 'differential.pkg.js' => 'ebef29b1', 'diffusion.pkg.css' => '591664fa', 'diffusion.pkg.js' => '0115b37c', 'maniphest.pkg.css' => '68d4dd3d', @@ -111,7 +111,7 @@ return array( 'rsrc/css/application/uiexample/example.css' => '528b19de', 'rsrc/css/core/core.css' => 'aaea7a7a', 'rsrc/css/core/remarkup.css' => '07b7dc54', - 'rsrc/css/core/syntax.css' => '6b7b24d9', + 'rsrc/css/core/syntax.css' => '9fd11da8', 'rsrc/css/core/z-index.css' => 'c4732d32', 'rsrc/css/diviner/diviner-shared.css' => '38813222', 'rsrc/css/font/font-awesome.css' => 'e2e712fe', @@ -202,7 +202,7 @@ return array( 'rsrc/externals/javelin/ext/view/__tests__/ViewInterpreter.js' => '7a94d6a5', 'rsrc/externals/javelin/ext/view/__tests__/ViewRenderer.js' => '6ea96ac9', 'rsrc/externals/javelin/lib/Cookie.js' => '62dfea03', - 'rsrc/externals/javelin/lib/DOM.js' => '6f7962d5', + 'rsrc/externals/javelin/lib/DOM.js' => '147805fa', 'rsrc/externals/javelin/lib/History.js' => 'd4505101', 'rsrc/externals/javelin/lib/JSON.js' => '69adf288', 'rsrc/externals/javelin/lib/Leader.js' => '331b1611', @@ -399,7 +399,7 @@ return array( 'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf', 'rsrc/js/application/releeph/releeph-request-state-change.js' => 'a0b57eb8', 'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f', - 'rsrc/js/application/repository/repository-crossreference.js' => '3975b470', + 'rsrc/js/application/repository/repository-crossreference.js' => 'bea81850', 'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08', 'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f', 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'dbbf48b6', @@ -636,7 +636,7 @@ return array( 'javelin-behavior-remarkup-preview' => 'f7379f45', 'javelin-behavior-reorder-applications' => '76b9fc3e', 'javelin-behavior-reorder-columns' => 'e1d25dfb', - 'javelin-behavior-repository-crossreference' => '3975b470', + 'javelin-behavior-repository-crossreference' => 'bea81850', 'javelin-behavior-scrollbar' => '834a1173', 'javelin-behavior-search-reorder-queries' => 'e9581f08', 'javelin-behavior-select-on-click' => '4e3e79a6', @@ -652,7 +652,7 @@ return array( 'javelin-color' => '7e41274a', 'javelin-cookie' => '62dfea03', 'javelin-diffusion-locate-file-source' => 'b42eddc7', - 'javelin-dom' => '6f7962d5', + 'javelin-dom' => '147805fa', 'javelin-dynval' => 'f6555212', 'javelin-event' => '85ea0626', 'javelin-fx' => '54b612ba', @@ -821,7 +821,7 @@ return array( 'sprite-menu-css' => '9ef76324', 'sprite-projects-css' => 'b0d9e24f', 'sprite-tokens-css' => '1706b943', - 'syntax-highlighting-css' => '6b7b24d9', + 'syntax-highlighting-css' => '9fd11da8', 'tokens-css' => '3d0f239e', 'typeahead-browse-css' => 'd8581d2c', 'unhandled-exception-css' => '37d4f9a2', @@ -918,6 +918,13 @@ return array( 'javelin-uri', 'phabricator-textareautils', ), + '147805fa' => array( + 'javelin-magical-init', + 'javelin-install', + 'javelin-util', + 'javelin-vector', + 'javelin-stratcom', + ), '1499a8cb' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1062,12 +1069,6 @@ return array( '331b1611' => array( 'javelin-install', ), - '3975b470' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'javelin-uri', - ), '3ab51e2c' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -1380,13 +1381,6 @@ return array( 'javelin-util', 'javelin-stratcom', ), - '6f7962d5' => array( - 'javelin-magical-init', - 'javelin-install', - 'javelin-util', - 'javelin-vector', - 'javelin-stratcom', - ), '70baed2f' => array( 'javelin-install', 'javelin-dom', @@ -1792,6 +1786,12 @@ return array( 'javelin-util', 'phabricator-shaped-request', ), + 'bea81850' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'javelin-uri', + ), 'c1700f6f' => array( 'javelin-install', 'javelin-util', diff --git a/webroot/rsrc/css/core/syntax.css b/webroot/rsrc/css/core/syntax.css index 28564ff154..9cf3fc9a24 100644 --- a/webroot/rsrc/css/core/syntax.css +++ b/webroot/rsrc/css/core/syntax.css @@ -131,12 +131,13 @@ .remarkup-code .rbw_i { color: indigo; } .remarkup-code .rbw_v { color: violet; } -.repository-crossreference .remarkup-code .nc, -.repository-crossreference .remarkup-code .na, -.repository-crossreference .remarkup-code .nf { - cursor: help; +.repository-crossreference .remarkup-code .crossreference-item { + background: lightyellow; border-bottom: 1px dotted #bbddbb; } +.crossreference-cursor { + cursor: help; +} .remarkup-code .invisible { color: #222222; diff --git a/webroot/rsrc/externals/javelin/lib/DOM.js b/webroot/rsrc/externals/javelin/lib/DOM.js index 420f9afe79..c131d107cb 100644 --- a/webroot/rsrc/externals/javelin/lib/DOM.js +++ b/webroot/rsrc/externals/javelin/lib/DOM.js @@ -716,7 +716,7 @@ JX.install('DOM', { node.className += ' '+className; } else if (has && !add) { node.className = node.className.replace( - new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), ' '); + new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), ' ').trim(); } }, diff --git a/webroot/rsrc/js/application/repository/repository-crossreference.js b/webroot/rsrc/js/application/repository/repository-crossreference.js index d2339dc131..710d2e4e00 100644 --- a/webroot/rsrc/js/application/repository/repository-crossreference.js +++ b/webroot/rsrc/js/application/repository/repository-crossreference.js @@ -6,54 +6,87 @@ * javelin-uri */ -JX.behavior('repository-crossreference', function(config) { +JX.behavior('repository-crossreference', function(config, statics) { - // NOTE: Pretty much everything in this file is a worst practice. We're - // constrained by the markup generated by the syntax highlighters. + var highlighted; + var linked = []; + + var isMac = navigator.platform.indexOf('Mac') > -1; + var signalKey = isMac ? 91 /*COMMAND*/ : 17 /*CTRL*/; + function isSignalkey(event) { + return isMac ? + event.getRawEvent().metaKey : + event.getRawEvent().ctrlKey; + } + + var classHighlight = 'crossreference-item'; + var classMouseCursor = 'crossreference-cursor'; + + // TODO maybe move the dictionary part of this list to the server? + var class_map = { + nc : 'class', + nf : 'function', + na : null, + nb : 'builtin', + n : null, + }; function link(element, lang) { JX.DOM.alterClass(element, 'repository-crossreference', true); + linked.push(element); JX.DOM.listen( element, - 'click', + ['mouseover', 'mouseout', 'click'], 'tag:span', function(e) { - var target = e.getTarget(); - var map = {nc : 'class', nf : 'function', na : null}; - while (target !== document.body) { - if (JX.DOM.isNode(target, 'span') && (target.className in map)) { - var timeout = function() { - if (window.getSelection && !window.getSelection().isCollapsed) { - return; - } - var symbol = target.textContent || target.innerText; - var query = { - lang : lang, - repositories : config.repositories.join(','), - jump : true - }; - if (map[target.className]) { - query.type = map[target.className]; - } - if (target.hasAttribute('data-symbol-context')) { - query.context = target.getAttribute('data-symbol-context'); - } - if (target.hasAttribute('data-symbol-name')) { - symbol = target.getAttribute('data-symbol-name'); - } - var uri = JX.$U('/diffusion/symbol/' + symbol + '/'); - uri.addQueryParams(query); - window.open(uri); - }; - setTimeout(timeout, 250); - e.kill(); - break; + if (e.getType() === 'mouseout') { + highlighted && JX.DOM.alterClass(highlighted, classHighlight, false); + highlighted = null; + return; + } + if (!isSignalkey(e)) { + return; + } + if (e.getType() === 'mouseover') { + var target = e.getTarget(); + while (target !== document.body) { + if (JX.DOM.isNode(target, 'span') && + (target.className in class_map)) { + highlighted = target; + JX.DOM.alterClass(highlighted, classHighlight, true); + break; + } + target = target.parentNode; } - target = target.parentNode; + } else if (e.getType() === 'click') { + openSearch(highlighted, lang); } }); } + function openSearch(target, lang) { + var symbol = target.textContent || target.innerText; + var query = { + lang : lang, + repositories : config.repositories.join(','), + jump : true + }; + var c = target.className; + c = c.replace(classHighlight, '').trim(); + if (class_map[c]) { + query.type = class_map[c]; + } + if (target.hasAttribute('data-symbol-context')) { + query.context = target.getAttribute('data-symbol-context'); + } + if (target.hasAttribute('data-symbol-name')) { + symbol = target.getAttribute('data-symbol-name'); + } + var uri = JX.$U('/diffusion/symbol/' + symbol + '/'); + uri.addQueryParams(query); + window.open(uri); + } + function linkAll() { var blocks = JX.DOM.scry(document.body, 'div', 'remarkup-code-block'); for (var i = 0; i < blocks.length; ++i) { @@ -77,4 +110,21 @@ JX.behavior('repository-crossreference', function(config) { linkAll(e.getData().container); }); + JX.Stratcom.listen( + ['keydown', 'keyup'], + null, + function(e) { + if (e.getRawEvent().keyCode !== signalKey) { + return; + } + statics.active = (e.getType() === 'keydown'); + linked.forEach(function(element) { + JX.DOM.alterClass(element, classMouseCursor, statics.active); + }); + + if (!statics.active) { + highlighted && JX.DOM.alterClass(highlighted, classHighlight, false); + highlighted = null; + } + }); });