晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。 林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。 见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。”(间隔 一作:隔绝) 既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。 南阳刘子骥,高尚士也,闻之,欣然规往。未果,寻病终。后遂无问津者。
|
Server : Apache System : Linux srv.rainic.com 4.18.0-553.47.1.el8_10.x86_64 #1 SMP Wed Apr 2 05:45:37 EDT 2025 x86_64 User : rainic ( 1014) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/stando/public_html/wp-content/plugins/pretty-link/js/editor/components/link-editor/ |
Upload File : |
/**
* External dependencies
*/
import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Component, Fragment, createRef, useMemo } from '@wordpress/element';
import {
ExternalLink,
IconButton,
ToggleControl,
Button,
TextControl,
Notice,
Spinner,
withSpokenMessages,
} from '@wordpress/components';
import { LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER } from '@wordpress/keycodes';
import { getRectangleFromRange } from '@wordpress/dom';
import { prependHTTP, safeDecodeURI, filterURLForDisplay } from '@wordpress/url';
import {
create,
insert,
isCollapsed,
applyFormat,
getTextContent,
slice,
} from '@wordpress/rich-text';
import { URLPopover } from '@wordpress/block-editor';
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
import URLInput from '../url-input';
import './style.scss';
/**
* Internal dependencies
*/
import { createLinkFormat, isValidHref } from './utils';
const stopKeyPropagation = ( event ) => event.stopPropagation();
function isShowingInput( props, state ) {
return props.addingLink || state.editLink;
}
const LinkEditor = ( { value, onChangeInputValue, onKeyDown, submitLink, autocompleteRef } ) => (
// Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
<form
className="editor-format-toolbar__link-container-content block-editor-format-toolbar__link-container-content"
onKeyPress={ stopKeyPropagation }
onKeyDown={ onKeyDown }
onSubmit={ submitLink }
>
<URLInput
value={ value }
onChange={ onChangeInputValue }
autocompleteRef={ autocompleteRef }
/>
<IconButton icon="editor-break" label={ __( 'Insert Pretty Link' ) } type="submit" />
</form>
/* eslint-enable jsx-a11y/no-noninteractive-element-interactions */
);
const LinkViewerUrl = ( { url } ) => {
const prependedURL = prependHTTP( url );
const linkClassName = classnames( 'editor-format-toolbar__link-container-value block-editor-format-toolbar__link-container-value', {
'has-invalid-link': ! isValidHref( prependedURL ),
} );
if ( ! url ) {
return <span className={ linkClassName }></span>;
}
return (
<ExternalLink
className={ linkClassName }
href={ url }
>
{ filterURLForDisplay( safeDecodeURI( url ) ) }
</ExternalLink>
);
};
const URLPopoverAtLink = ( { isActive, addingLink, value, ...props } ) => {
const anchorRect = useMemo( () => {
const selection = window.getSelection();
const range = selection.rangeCount > 0 ? selection.getRangeAt( 0 ) : null;
if ( ! range ) {
return;
}
if ( addingLink ) {
return getRectangleFromRange( range );
}
let element = range.startContainer;
// If the caret is right before the element, select the next element.
element = element.nextElementSibling || element;
while ( element.nodeType !== window.Node.ELEMENT_NODE ) {
element = element.parentNode;
}
const closest = element.closest( 'a' );
if ( closest ) {
return closest.getBoundingClientRect();
}
}, [ isActive, addingLink, value.start, value.end ] );
if ( ! anchorRect ) {
return null;
}
return <URLPopover anchorRect={ anchorRect } { ...props } />;
};
const LinkViewer = ( { url, editLink } ) => {
return (
// Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar
/* eslint-disable jsx-a11y/no-static-element-interactions */
<div
className="editor-format-toolbar__link-container-content block-editor-format-toolbar__link-container-content"
onKeyPress={ stopKeyPropagation }
>
<LinkViewerUrl url={ url } />
<IconButton icon="edit" label={ __( 'Edit' ) } onClick={ editLink } />
</div>
/* eslint-enable jsx-a11y/no-static-element-interactions */
);
};
const createNewPrettyLink = (target, slug) => {
return new Promise((resolve, reject) => {
jQuery.post(
ajaxurl,
{
action: 'prli_create_pretty_link',
target: target,
slug: slug,
redirect: '',
nofollow: 1,
tracking: 1,
sponsored: 0
},
(data, textStatus, xhr) => {
'true' === data ? resolve(data) : reject(data);
}
).fail(error => {
reject(error);
});
});
}
class InlineLinkUI extends Component {
constructor() {
super( ...arguments );
this.editLink = this.editLink.bind( this );
this.submitLink = this.submitLink.bind( this );
this.onKeyDown = this.onKeyDown.bind( this );
this.onChangeInputValue = this.onChangeInputValue.bind( this );
this.setNoFollow = this.setNoFollow.bind( this );
this.setIsSponsored = this.setIsSponsored.bind( this );
this.setLinkTarget = this.setLinkTarget.bind( this );
this.onClickOutside = this.onClickOutside.bind( this );
this.resetState = this.resetState.bind( this );
this.autocompleteRef = createRef();
this.state = {
noFollow: false,
opensInNewWindow: false,
isSponsored: false,
inputValue: '',
newLinkUrl: '',
newLinkSlug: '',
creatingLink: false,
createdLink: false,
createdLinkError: false
};
}
static getDerivedStateFromProps( props, state ) {
const { activeAttributes: { url, target, isSponsored } } = props;
const opensInNewWindow = target === '_blank';
if ( ! isShowingInput( props, state ) ) {
if ( url !== state.inputValue ) {
return { inputValue: url };
}
if ( opensInNewWindow !== state.opensInNewWindow ) {
return { opensInNewWindow };
}
}
return null;
}
onKeyDown( event ) {
if ( [ LEFT, DOWN, RIGHT, UP, BACKSPACE, ENTER ].indexOf( event.keyCode ) > -1 ) {
// Stop the key event from propagating up to ObserveTyping.startTypingInTextField.
event.stopPropagation();
}
}
onChangeInputValue( inputValue ) {
this.setState( { inputValue } );
}
setLinkTarget( opensInNewWindow ) {
const { activeAttributes: { url = '' }, value, onChange } = this.props;
this.setState( { opensInNewWindow } );
// Apply now if URL is not being edited.
if ( ! isShowingInput( this.props, this.state ) ) {
const selectedText = getTextContent( slice( value ) );
onChange( applyFormat( value, createLinkFormat( {
url,
opensInNewWindow,
text: selectedText,
} ) ) );
}
}
setNoFollow( noFollow ) {
const { activeAttributes: { url = '' }, value, onChange } = this.props;
this.setState( { noFollow } );
// Apply now if URL is not being edited.
if ( ! isShowingInput( this.props, this.state ) ) {
const selectedText = getTextContent( slice( value ) );
onChange( applyFormat( value, createLinkFormat( {
url,
opensInNewWindow,
text: selectedText,
noFollow,
isSponsored
} ) ) );
}
}
setIsSponsored( isSponsored ) {
const { activeAttributes: { url = '' }, value, onChange } = this.props;
this.setState( { isSponsored } );
// Apply now if URL is not being edited.
if ( ! isShowingInput( this.props, this.state ) ) {
const selectedText = getTextContent( slice( value ) );
onChange( applyFormat( value, createLinkFormat( {
url,
opensInNewWindow,
text: selectedText,
noFollow,
isSponsored
} ) ) );
}
}
editLink( event ) {
this.setState( { editLink: true } );
event.preventDefault();
}
submitLink( event ) {
const { isActive, value, onChange, speak } = this.props;
const { inputValue, opensInNewWindow, noFollow, isSponsored } = this.state;
const url = prependHTTP( inputValue );
const selectedText = getTextContent( slice( value ) );
const format = createLinkFormat( {
url,
opensInNewWindow,
text: selectedText,
noFollow,
isSponsored
} );
event.preventDefault();
if ( isCollapsed( value ) && ! isActive ) {
const toInsert = applyFormat( create( { text: url } ), format, 0, url.length );
onChange( insert( value, toInsert ) );
} else {
onChange( applyFormat( value, format ) );
}
this.resetState();
if ( ! isValidHref( url ) ) {
speak( __( 'Warning: the link has been inserted but may have errors. Please test it.' ), 'assertive' );
} else if ( isActive ) {
speak( __( 'Link edited.' ), 'assertive' );
} else {
speak( __( 'Link inserted.' ), 'assertive' );
}
}
onClickOutside( event ) {
// The autocomplete suggestions list renders in a separate popover (in a portal),
// so onClickOutside fails to detect that a click on a suggestion occurred in the
// LinkContainer. Detect clicks on autocomplete suggestions using a ref here, and
// return to avoid the popover being closed.
const autocompleteElement = this.autocompleteRef.current;
if ( autocompleteElement && autocompleteElement.contains( event.target ) ) {
return;
}
this.resetState();
}
resetState() {
this.props.stopAddingLink();
this.setState( { editLink: false } );
}
render() {
const { isActive, activeAttributes: { url }, addingLink, value } = this.props;
if ( ! isActive && ! addingLink ) {
return null;
}
const { inputValue, noFollow, opensInNewWindow, isSponsored, newLinkUrl, newLinkSlug, creatingLink, createdLink, createdLinkError } = this.state;
const showInput = isShowingInput( this.props, this.state );
return (
<URLPopoverAtLink
className="pretty-link-inserter"
value={ value }
isActive={ isActive }
addingLink={ addingLink }
onClickOutside={ this.onClickOutside }
onClose={ this.resetState }
focusOnMount={ showInput ? 'firstElement' : false }
renderSettings={ () => (
<Fragment>
<div>
<ToggleControl
label={ __( 'Open in New Tab' ) }
checked={ opensInNewWindow }
onChange={ this.setLinkTarget }
/>
<ToggleControl
label={ __( 'Nofollow' ) }
checked={ noFollow }
onChange={ this.setNoFollow }
/>
<ToggleControl
label={ __( 'Sponsored Link' ) }
checked={ isSponsored }
onChange={ this.setIsSponsored }
/>
</div>
<div className="pretty-link-inserter-form-container">
{
createdLink && (
<Notice status="success" onRemove={() => this.setState({createdLink: false})}>
<p>{__( 'Pretty Link created successfully.', 'memberpress' )}</p>
</Notice>
)
}
{
createdLinkError && (
<Notice status="error" onRemove={() => this.setState({createdLink: false, createdLinkError: false})}>
<p>{__( 'Pretty Link could not be created. Please try a slug that is not already used.', 'memberpress' )}</p>
</Notice>
)
}
<strong>{__('New Pretty Link', 'pretty-link')}</strong>
<form onSubmit={(event) => {
event.preventDefault();
// Send request to create new Pretty Link
this.setState({
creatingLink: true,
createdLinkError: false,
});
createNewPrettyLink( newLinkUrl, newLinkSlug )
.then(data => {
this.setState({
createdLink: true,
creatingLink: false,
inputValue: plEditor.homeUrl + newLinkSlug,
newLinkUrl: '',
newLinkSlug: ''
});
})
.catch(error => {
this.setState({
createdLink: false,
creatingLink: false,
createdLinkError: true,
});
});
}}>
<p>
<TextControl
placeholder="URL"
className="pretty-link-new-link-url"
value={newLinkUrl}
onChange={ ( newLinkUrl ) => {
this.setState( { newLinkUrl } );
} }
/>
</p>
<p>
<TextControl
placeholder="Slug"
className="pretty-link-new-link-slug"
value={newLinkSlug}
onChange={ ( newLinkSlug ) => {
this.setState( { newLinkSlug } );
} }
/>
</p>
<p>
<button
className="pretty-link-submit-new-link components-button is-button is-primary"
onClick={ () => {
console.log('Creating new Pretty Link...');
} }
>
{ __( 'Create New Pretty Link', 'pretty-link' ) }
</button>
{
creatingLink && (
<Spinner />
)
}
</p>
</form>
</div>
</Fragment>
) }
>
{ showInput ? (
<LinkEditor
value={ inputValue }
onChangeInputValue={ this.onChangeInputValue }
onKeyDown={ this.onKeyDown }
submitLink={ this.submitLink }
autocompleteRef={ this.autocompleteRef }
/>
) : (
<LinkViewer
url={ url }
editLink={ this.editLink }
/>
) }
</URLPopoverAtLink>
);
}
}
export default withSpokenMessages( InlineLinkUI );