@@ -68,7 +68,6 @@ function RemoteFunctions(config = {}) {
6868 let _moreOptionsDropdown ;
6969 let _aiPromptBox ;
7070 let _imageRibbonGallery ;
71- let _hyperlinkEditor ;
7271 let _currentRulerLines ;
7372 let _hotCorner ;
7473 let _setup = false ;
@@ -106,16 +105,24 @@ function RemoteFunctions(config = {}) {
106105 } , time * 1000 ) ;
107106 }
108107
109- const _moreOptionsHandlers = { } ;
108+ const _moreOptionsHandlers = new Map ( ) ;
110109 function registerNodeMoreOptionsHandler ( handlerName , handler ) {
111110 if ( _moreOptionsHandlers [ handlerName ] ) {
112111 console . error ( `lp: More options handler '${ handlerName } ' already registered. Ignoring new registration` ) ;
113112 return ;
114113 }
115- _moreOptionsHandlers [ handlerName ] = handler ;
114+ if ( ! handler || ! handler . dismiss || ! handler . renderMoreOptions ) {
115+ console . error ( `lp: More options handler Ignoring registration: '${
116+ handlerName } ' missing one of required function : 'dismiss', 'renderMoreOptions'`) ;
117+ return ;
118+ }
119+ _moreOptionsHandlers . set ( handlerName , handler ) ;
116120 }
117121 function getNodeMoreOptionsHandler ( handlerName ) {
118- return _moreOptionsHandlers [ handlerName ] ;
122+ return _moreOptionsHandlers . get ( handlerName ) ;
123+ }
124+ function getAllNodeMoreOptionsHandlers ( ) {
125+ return _moreOptionsHandlers . values ( ) ;
119126 }
120127
121128 /**
@@ -151,11 +158,36 @@ function RemoteFunctions(config = {}) {
151158 return isElementInspectable ( element , onlyHighlight ) && element . hasAttribute ( GLOBALS . DATA_BRACKETS_ID_ATTR ) ;
152159 }
153160
161+ /**
162+ * this function calc the screen offset of an element
163+ *
164+ * @param {DOMElement } element
165+ * @returns {{left: number, top: number} }
166+ */
167+ function screenOffset ( element ) {
168+ const elemBounds = element . getBoundingClientRect ( ) ;
169+ const body = window . document . body ;
170+ let offsetTop ;
171+ let offsetLeft ;
172+
173+ if ( window . getComputedStyle ( body ) . position === "static" ) {
174+ offsetLeft = elemBounds . left + window . pageXOffset ;
175+ offsetTop = elemBounds . top + window . pageYOffset ;
176+ } else {
177+ const bodyBounds = body . getBoundingClientRect ( ) ;
178+ offsetLeft = elemBounds . left - bodyBounds . left ;
179+ offsetTop = elemBounds . top - bodyBounds . top ;
180+ }
181+ return { left : offsetLeft , top : offsetTop } ;
182+ }
183+
154184 const LivePreviewView = {
155185 registerNodeMoreOptionsHandler : registerNodeMoreOptionsHandler ,
156186 getNodeMoreOptionsHandler : getNodeMoreOptionsHandler ,
187+ getAllNodeMoreOptionsHandlers : getAllNodeMoreOptionsHandlers ,
157188 isElementEditable : isElementEditable ,
158- isElementInspectable : isElementInspectable
189+ isElementInspectable : isElementInspectable ,
190+ screenOffset : screenOffset
159191 } ;
160192
161193 /**
@@ -369,16 +401,6 @@ function RemoteFunctions(config = {}) {
369401 } ) ;
370402 }
371403
372- /**
373- * This function gets called when the edit hyperlink button is clicked
374- * @param {Event } event
375- * @param {DOMElement } element - the HTML link element
376- */
377- function _handleEditHyperlinkOptionClick ( event , element ) {
378- dismissHyperlinkEditor ( ) ;
379- _hyperlinkEditor = new HyperlinkEditor ( element ) ;
380- }
381-
382404 /**
383405 * This function gets called when the delete button is clicked
384406 * it sends a message to the editor using postMessage to delete the element from the source code
@@ -524,8 +546,6 @@ function RemoteFunctions(config = {}) {
524546 _handleSelectParentOptionClick ( e , element ) ;
525547 } else if ( action === "edit-text" ) {
526548 startEditing ( element ) ;
527- } else if ( action === "edit-hyperlink" ) {
528- _handleEditHyperlinkOptionClick ( e , element ) ;
529549 } else if ( action === "duplicate" ) {
530550 _handleDuplicateOptionClick ( e , element ) ;
531551 } else if ( action === "delete" ) {
@@ -540,6 +560,9 @@ function RemoteFunctions(config = {}) {
540560 _handleAIOptionClick ( e , element ) ;
541561 } else if ( action === "image-gallery" ) {
542562 _handleImageGalleryOptionClick ( e , element ) ;
563+ } else if ( LivePreviewView . getNodeMoreOptionsHandler ( action ) ) {
564+ const handler = LivePreviewView . getNodeMoreOptionsHandler ( action ) ;
565+ handler . handleClick ( e , element ) ;
543566 }
544567 }
545568
@@ -1851,30 +1874,6 @@ function RemoteFunctions(config = {}) {
18511874 return true ;
18521875 }
18531876
1854-
1855- /**
1856- * this function calc the screen offset of an element
1857- *
1858- * @param {DOMElement } element
1859- * @returns {{left: number, top: number} }
1860- */
1861- function _screenOffset ( element ) {
1862- const elemBounds = element . getBoundingClientRect ( ) ;
1863- const body = window . document . body ;
1864- let offsetTop ;
1865- let offsetLeft ;
1866-
1867- if ( window . getComputedStyle ( body ) . position === "static" ) {
1868- offsetLeft = elemBounds . left + window . pageXOffset ;
1869- offsetTop = elemBounds . top + window . pageYOffset ;
1870- } else {
1871- const bodyBounds = body . getBoundingClientRect ( ) ;
1872- offsetLeft = elemBounds . left - bodyBounds . left ;
1873- offsetTop = elemBounds . top - bodyBounds . top ;
1874- }
1875- return { left : offsetLeft , top : offsetTop } ;
1876- }
1877-
18781877 /**
18791878 * Check if two rectangles overlap
18801879 * @param {Object } rect1 - First rectangle {left, top, right, bottom}
@@ -1931,7 +1930,7 @@ function RemoteFunctions(config = {}) {
19311930 * @returns {Object } - {leftPos, topPos}
19321931 */
19331932 function _getCoordinatesForPosition ( position , element , boxDimensions , verticalOffset , horizontalOffset ) {
1934- const offsetBounds = _screenOffset ( element ) ;
1933+ const offsetBounds = LivePreviewView . screenOffset ( element ) ;
19351934 const elemBounds = element . getBoundingClientRect ( ) ;
19361935
19371936 let leftPos , topPos ;
@@ -2228,24 +2227,24 @@ function RemoteFunctions(config = {}) {
22282227
22292228 // Only include select parent option if element supports it
22302229 if ( showSelectParentOption ) {
2231- content += `<span data-action="select-parent" title="${ strings . selectParent } ">
2230+ content += `<span data-action="select-parent" class="lp-opt-select-parent" title="${ strings . selectParent } ">
22322231 ${ icons . arrowUp }
22332232 </span>` ;
22342233 }
22352234
22362235 // Only include edit text option if element supports it
22372236 if ( showEditTextOption ) {
2238- content += `<span data-action="edit-text" title="${ strings . editText } ">
2237+ content += `<span data-action="edit-text" class="lp-opt-edit-text" title="${ strings . editText } ">
22392238 ${ icons . edit }
22402239 </span>` ;
22412240 }
22422241
2243- // if its a link element, we show the edit hyperlink icon
2244- if ( this . element && this . element . tagName . toLowerCase ( ) === 'a' ) {
2245- content += `<span data-action="edit-hyperlink" title=" ${ strings . editHyperlink } ">
2246- ${ icons . link }
2247- </span>` ;
2248- }
2242+ LivePreviewView . getAllNodeMoreOptionsHandlers ( ) . forEach ( handler => {
2243+ const optionalContent = handler . renderMoreOptions ( this . element ) ;
2244+ if ( optionalContent ) {
2245+ content += optionalContent ;
2246+ }
2247+ } ) ;
22492248
22502249 const selectedClass = imageGallerySelected ? 'class="selected"' : "" ;
22512250
@@ -2335,123 +2334,6 @@ function RemoteFunctions(config = {}) {
23352334 }
23362335 } ;
23372336
2338- /**
2339- * This shows a floating input box above the element which allows you to edit the link of the 'a' tag
2340- */
2341- function HyperlinkEditor ( element ) {
2342- this . element = element ;
2343- this . remove = this . remove . bind ( this ) ;
2344- this . create ( ) ;
2345- }
2346-
2347- HyperlinkEditor . prototype = {
2348- create : function ( ) {
2349- const currentHref = this . element . getAttribute ( 'href' ) || '' ;
2350-
2351- // Create shadow DOM container
2352- this . body = document . createElement ( 'div' ) ;
2353- this . body . setAttribute ( GLOBALS . PHCODE_INTERNAL_ATTR , "true" ) ;
2354- document . body . appendChild ( this . body ) ;
2355-
2356- const shadow = this . body . attachShadow ( { mode : 'open' } ) ;
2357-
2358- // Create input HTML + styles
2359- const html = `
2360- <style>
2361- ${ cssStyles . hyperlinkEditor }
2362- </style>
2363- <div class="hyperlink-input-box">
2364- <div class="link-icon" title="${ currentHref . trim ( ) || strings . hyperlinkNoHref } ">
2365- ${ icons . link }
2366- </div>
2367- <input type="text" value="${ currentHref . trim ( ) } " placeholder="https://example.com" spellcheck="false" />
2368- </div>
2369- ` ;
2370-
2371- shadow . innerHTML = html ;
2372- this . _shadow = shadow ;
2373-
2374- this . _positionInput ( ) ;
2375-
2376- // setup the event listeners
2377- const input = shadow . querySelector ( 'input' ) ;
2378- input . focus ( ) ;
2379- input . select ( ) ;
2380-
2381- input . addEventListener ( 'keydown' , ( e ) => this . _handleKeydown ( e ) ) ;
2382- input . addEventListener ( 'blur' , ( ) => this . _handleBlur ( ) ) ;
2383- } ,
2384-
2385- _positionInput : function ( ) {
2386- const inputBoxElement = this . _shadow . querySelector ( '.hyperlink-input-box' ) ;
2387- if ( ! inputBoxElement ) {
2388- return ;
2389- }
2390-
2391- const boxRect = inputBoxElement . getBoundingClientRect ( ) ;
2392- const elemBounds = this . element . getBoundingClientRect ( ) ;
2393- const offset = _screenOffset ( this . element ) ;
2394-
2395- let topPos = offset . top - boxRect . height - 6 ;
2396- let leftPos = offset . left + elemBounds . width - boxRect . width ;
2397-
2398- // If would go off top, position below
2399- if ( elemBounds . top - boxRect . height < 6 ) {
2400- topPos = offset . top + elemBounds . height + 6 ;
2401- }
2402-
2403- // If would go off left, align left
2404- if ( leftPos < 0 ) {
2405- leftPos = offset . left ;
2406- }
2407-
2408- inputBoxElement . style . left = leftPos + 'px' ;
2409- inputBoxElement . style . top = topPos + 'px' ;
2410- } ,
2411-
2412- _handleKeydown : function ( event ) {
2413- if ( event . key === 'Enter' ) {
2414- event . preventDefault ( ) ;
2415- this . _save ( ) ;
2416- } else if ( event . key === 'Escape' ) {
2417- event . preventDefault ( ) ;
2418- dismissHyperlinkEditor ( ) ;
2419- }
2420- } ,
2421-
2422- _handleBlur : function ( ) {
2423- setTimeout ( ( ) => this . _save ( ) , 100 ) ;
2424- } ,
2425-
2426- _save : function ( ) {
2427- const input = this . _shadow . querySelector ( 'input' ) ;
2428- const newHref = input . value . trim ( ) ;
2429- const oldHref = this . element . getAttribute ( 'href' ) || '' ;
2430-
2431- if ( newHref !== oldHref ) {
2432- this . element . setAttribute ( 'href' , newHref ) ;
2433-
2434- const tagId = this . element . getAttribute ( GLOBALS . DATA_BRACKETS_ID_ATTR ) ;
2435- window . _Brackets_MessageBroker . send ( {
2436- livePreviewEditEnabled : true ,
2437- livePreviewHyperlinkEdit : true ,
2438- element : this . element ,
2439- tagId : Number ( tagId ) ,
2440- newHref : newHref
2441- } ) ;
2442- }
2443-
2444- this . remove ( ) ;
2445- } ,
2446-
2447- remove : function ( ) {
2448- if ( this . body && this . body . parentNode ) {
2449- this . body . parentNode . removeChild ( this . body ) ;
2450- this . body = null ;
2451- }
2452- }
2453- } ;
2454-
24552337 /**
24562338 * this is called when user clicks on the Show Ruler lines option in the more options dropdown
24572339 * @param {Event } event - click event
@@ -2784,7 +2666,7 @@ function RemoteFunctions(config = {}) {
27842666 AIPromptBox . prototype = {
27852667 _getBoxPosition : function ( boxWidth , boxHeight ) {
27862668 const elemBounds = this . element . getBoundingClientRect ( ) ;
2787- const offset = _screenOffset ( this . element ) ;
2669+ const offset = LivePreviewView . screenOffset ( this . element ) ;
27882670
27892671 let topPos = offset . top - boxHeight - 6 ; // 6 for just some little space to breathe
27902672 let leftPos = offset . left + elemBounds . width - boxWidth ;
@@ -4240,7 +4122,7 @@ function RemoteFunctions(config = {}) {
42404122
42414123 highlight . className = GLOBALS . HIGHLIGHT_CLASSNAME ;
42424124
4243- var offset = _screenOffset ( element ) ;
4125+ var offset = LivePreviewView . screenOffset ( element ) ;
42444126
42454127 // some code to find element left/top was removed here. This seems to be relevant to box model
42464128 // live highlights. firether reading: https://github.com/adobe/brackets/pull/13357/files
@@ -5616,13 +5498,6 @@ function RemoteFunctions(config = {}) {
56165498 }
56175499 }
56185500
5619- function dismissHyperlinkEditor ( ) {
5620- if ( _hyperlinkEditor ) {
5621- _hyperlinkEditor . remove ( ) ;
5622- _hyperlinkEditor = null ;
5623- }
5624- }
5625-
56265501 /**
56275502 * Helper function to dismiss all UI boxes at once
56285503 */
@@ -5632,8 +5507,8 @@ function RemoteFunctions(config = {}) {
56325507 dismissAIPromptBox ( ) ;
56335508 dismissNodeInfoBox ( ) ;
56345509 dismissImageRibbonGallery ( ) ;
5635- dismissHyperlinkEditor ( ) ;
56365510 dismissToastMessage ( ) ;
5511+ getAllNodeMoreOptionsHandlers ( ) . forEach ( handler => handler . dismiss ( ) ) ;
56375512 }
56385513
56395514 let _toastTimeout = null ;
0 commit comments