2016-04-01 13:17:09 +00:00
/ *
*
2019-01-17 13:05:03 +00:00
* ( c ) Copyright Ascensio System SIA 2010 - 2019
2016-04-01 13:17:09 +00:00
*
* This program is a free software product . You can redistribute it and / or
* modify it under the terms of the GNU Affero General Public License ( AGPL )
* version 3 as published by the Free Software Foundation . In accordance with
* Section 7 ( a ) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non - infringement
* of any third - party rights .
*
* This program is distributed WITHOUT ANY WARRANTY ; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . For
* details , see the GNU AGPL at : http : //www.gnu.org/licenses/agpl-3.0.html
*
2019-01-17 13:00:34 +00:00
* You can contact Ascensio System SIA at 20 A - 12 Ernesta Birznieka - Upisha
* street , Riga , Latvia , EU , LV - 1050.
2016-04-01 13:17:09 +00:00
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices , as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7 ( b ) of the License you must retain the original Product
* logo when distributing the program . Pursuant to Section 7 ( e ) we decline to
* grant you any rights under trademark law for use of our trademarks .
*
* All the Product ' s GUI elements , including illustrations and icon sets , as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution - ShareAlike 4.0 International . See the License
* terms at http : //creativecommons.org/licenses/by-sa/4.0/legalcode
*
* /
2016-03-11 00:48:53 +00:00
/ * *
* HyperlinkSettingsDialog . js
*
* Created by Alexander Yuzhin on 2 / 20 / 14
2018-03-01 12:16:38 +00:00
* Copyright ( c ) 2018 Ascensio System SIA . All rights reserved .
2016-03-11 00:48:53 +00:00
*
* /
if ( Common === undefined )
var Common = { } ;
2018-02-27 11:55:58 +00:00
var c _oHyperlinkType = {
InternalLink : 0 ,
WebLink : 1
} ;
2016-03-11 00:48:53 +00:00
define ( [
'common/main/lib/util/utils' ,
'common/main/lib/component/InputField' ,
'common/main/lib/component/Window'
] , function ( ) { 'use strict' ;
DE . Views . HyperlinkSettingsDialog = Common . UI . Window . extend ( _ . extend ( {
options : {
width : 350 ,
style : 'min-width: 230px;' ,
2019-09-11 08:38:08 +00:00
cls : 'modal-dlg' ,
2019-11-05 10:34:44 +00:00
buttons : [ 'ok' , 'cancel' ]
2016-03-11 00:48:53 +00:00
} ,
initialize : function ( options ) {
_ . extend ( this . options , {
title : this . textTitle
} , options || { } ) ;
this . template = [
2020-03-16 10:31:12 +00:00
'<div class="box" style="height: 319px;">' ,
2018-02-27 11:55:58 +00:00
'<div class="input-row" style="margin-bottom: 10px;">' ,
'<button type="button" class="btn btn-text-default auto" id="id-dlg-hyperlink-external" style="border-top-right-radius: 0;border-bottom-right-radius: 0;">' , this . textExternal , '</button>' ,
'<button type="button" class="btn btn-text-default auto" id="id-dlg-hyperlink-internal" style="border-top-left-radius: 0;border-bottom-left-radius: 0;">' , this . textInternal , '</button>' ,
'</div>' ,
'<div id="id-external-link">' ,
'<div class="input-row">' ,
2020-03-04 11:45:42 +00:00
'<label>' + this . textUrl + '</label>' ,
2018-02-27 11:55:58 +00:00
'</div>' ,
'<div id="id-dlg-hyperlink-url" class="input-row" style="margin-bottom: 5px;"></div>' ,
'</div>' ,
'<div id="id-internal-link">' ,
2020-03-04 11:45:42 +00:00
'<div class="input-row">' ,
'<label>' + this . textUrl + '</label>' ,
'</div>' ,
2020-03-16 10:31:12 +00:00
'<div id="id-dlg-hyperlink-list" style="width:100%; height: 171px;border: 1px solid #cfcfcf;"></div>' ,
2016-03-11 00:48:53 +00:00
'</div>' ,
'<div class="input-row">' ,
'<label>' + this . textDisplay + '</label>' ,
'</div>' ,
'<div id="id-dlg-hyperlink-display" class="input-row" style="margin-bottom: 5px;"></div>' ,
'<div class="input-row">' ,
'<label>' + this . textTooltip + '</label>' ,
'</div>' ,
'<div id="id-dlg-hyperlink-tip" class="input-row" style="margin-bottom: 5px;"></div>' ,
'</div>'
] . join ( '' ) ;
2017-04-21 08:10:07 +00:00
this . options . tpl = _ . template ( this . template ) ( this . options ) ;
2016-03-11 00:48:53 +00:00
this . api = this . options . api ;
2017-12-15 10:06:52 +00:00
this . _originalProps = null ;
2016-03-11 00:48:53 +00:00
Common . UI . Window . prototype . initialize . call ( this , this . options ) ;
} ,
render : function ( ) {
Common . UI . Window . prototype . render . call ( this ) ;
var me = this ,
$window = this . getChild ( ) ;
2018-02-27 11:55:58 +00:00
me . btnExternal = new Common . UI . Button ( {
el : $ ( '#id-dlg-hyperlink-external' ) ,
enableToggle : true ,
toggleGroup : 'hyperlink-type' ,
allowDepress : false ,
pressed : true
} ) ;
me . btnExternal . on ( 'click' , _ . bind ( me . onLinkTypeClick , me , c _oHyperlinkType . WebLink ) ) ;
me . btnInternal = new Common . UI . Button ( {
el : $ ( '#id-dlg-hyperlink-internal' ) ,
enableToggle : true ,
toggleGroup : 'hyperlink-type' ,
allowDepress : false
} ) ;
me . btnInternal . on ( 'click' , _ . bind ( me . onLinkTypeClick , me , c _oHyperlinkType . InternalLink ) ) ;
2016-03-11 00:48:53 +00:00
me . inputUrl = new Common . UI . InputField ( {
el : $ ( '#id-dlg-hyperlink-url' ) ,
allowBlank : false ,
blankError : me . txtEmpty ,
style : 'width: 100%;' ,
validateOnBlur : false ,
validation : function ( value ) {
var urltype = me . api . asc _getUrlType ( $ . trim ( value ) ) ;
me . isEmail = ( urltype == 2 ) ;
return ( urltype > 0 ) ? true : me . txtNotUrl ;
}
} ) ;
2020-03-04 11:45:42 +00:00
me . inputUrl . _input . on ( 'input' , function ( e ) {
2020-03-11 10:34:37 +00:00
me . isInputFirstChange && me . inputUrl . showError ( ) ;
me . isInputFirstChange = false ;
2020-03-11 12:39:33 +00:00
var val = $ ( e . target ) . val ( ) ;
2020-03-11 13:56:06 +00:00
if ( me . isAutoUpdate ) {
me . inputDisplay . setValue ( val ) ;
me . isTextChanged = true ;
}
2020-03-11 12:39:33 +00:00
me . btnOk . setDisabled ( $ . trim ( val ) == '' ) ;
2020-03-04 11:45:42 +00:00
} ) ;
2016-03-11 00:48:53 +00:00
me . inputDisplay = new Common . UI . InputField ( {
el : $ ( '#id-dlg-hyperlink-display' ) ,
allowBlank : true ,
validateOnBlur : false ,
style : 'width: 100%;'
} ) . on ( 'changed:after' , function ( ) {
me . isTextChanged = true ;
} ) ;
2020-03-11 12:39:33 +00:00
me . inputDisplay . _input . on ( 'input' , function ( e ) {
me . isAutoUpdate = ( $ ( e . target ) . val ( ) == '' ) ;
} ) ;
2016-03-11 00:48:53 +00:00
me . inputTip = new Common . UI . InputField ( {
el : $ ( '#id-dlg-hyperlink-tip' ) ,
style : 'width: 100%;' ,
2016-04-05 11:52:34 +00:00
maxLength : Asc . c _oAscMaxTooltipLength
2016-03-11 00:48:53 +00:00
} ) ;
2018-03-29 12:22:58 +00:00
me . internalList = new Common . UI . TreeView ( {
el : $ ( '#id-dlg-hyperlink-list' ) ,
store : new Common . UI . TreeViewStore ( ) ,
2018-03-29 13:02:54 +00:00
enableKeyEvents : true
} ) ;
me . internalList . on ( 'item:select' , _ . bind ( this . onSelectItem , this ) ) ;
me . btnOk = new Common . UI . Button ( {
el : $window . find ( '.primary' )
2018-03-29 12:22:58 +00:00
} ) ;
2016-03-11 00:48:53 +00:00
$window . find ( '.dlg-btn' ) . on ( 'click' , _ . bind ( this . onBtnClick , this ) ) ;
2018-09-21 13:01:09 +00:00
me . internalList . on ( 'entervalue' , _ . bind ( me . onPrimary , me ) ) ;
2018-02-27 11:55:58 +00:00
me . externalPanel = $window . find ( '#id-external-link' ) ;
me . internalPanel = $window . find ( '#id-internal-link' ) ;
} ,
ShowHideElem : function ( value ) {
this . externalPanel . toggleClass ( 'hidden' , value !== c _oHyperlinkType . WebLink ) ;
this . internalPanel . toggleClass ( 'hidden' , value !== c _oHyperlinkType . InternalLink ) ;
2018-03-29 12:22:58 +00:00
var store = this . internalList . store ;
2018-03-29 13:02:54 +00:00
if ( value == c _oHyperlinkType . InternalLink ) {
if ( store . length < 1 ) {
var anchors = this . api . asc _GetHyperlinkAnchors ( ) ,
count = anchors . length ,
prev _level = 0 ,
header _level = 0 ,
arr = [ ] ;
arr . push ( new Common . UI . TreeViewModel ( {
name : this . txtBeginning ,
level : 0 ,
index : 0 ,
hasParent : false ,
isEmptyItem : false ,
isNotHeader : true ,
hasSubItems : false
} ) ) ;
arr . push ( new Common . UI . TreeViewModel ( {
name : this . txtHeadings ,
level : 0 ,
index : 1 ,
hasParent : false ,
isEmptyItem : false ,
isNotHeader : false ,
2020-03-10 14:49:42 +00:00
type : Asc . c _oAscHyperlinkAnchor . Heading ,
2018-03-29 13:02:54 +00:00
hasSubItems : false
} ) ) ;
2018-03-29 12:22:58 +00:00
2018-03-29 13:02:54 +00:00
for ( var i = 0 ; i < count ; i ++ ) {
var anchor = anchors [ i ] ,
2018-06-19 13:09:51 +00:00
level = anchors [ i ] . asc _GetHeadingLevel ( ) + 1 ,
2018-03-29 13:02:54 +00:00
hasParent = true ;
if ( anchor . asc _GetType ( ) == Asc . c _oAscHyperlinkAnchor . Heading ) {
if ( level > prev _level )
arr [ arr . length - 1 ] . set ( 'hasSubItems' , true ) ;
if ( level <= header _level ) {
header _level = level ;
hasParent = false ;
}
arr . push ( new Common . UI . TreeViewModel ( {
name : anchor . asc _GetHeadingText ( ) ,
level : level ,
index : i + 2 ,
2018-03-30 12:43:48 +00:00
hasParent : hasParent ,
type : Asc . c _oAscHyperlinkAnchor . Heading ,
headingParagraph : anchor . asc _GetHeadingParagraph ( )
2018-03-29 13:02:54 +00:00
} ) ) ;
prev _level = level ;
2018-03-29 12:22:58 +00:00
}
}
2018-03-29 13:02:54 +00:00
arr . push ( new Common . UI . TreeViewModel ( {
name : this . txtBookmarks ,
level : 0 ,
index : arr . length ,
hasParent : false ,
isEmptyItem : false ,
isNotHeader : false ,
2020-03-10 14:49:42 +00:00
type : Asc . c _oAscHyperlinkAnchor . Bookmark ,
2018-03-29 13:02:54 +00:00
hasSubItems : false
} ) ) ;
2018-03-29 12:22:58 +00:00
2018-03-29 13:02:54 +00:00
prev _level = 0 ;
for ( var i = 0 ; i < count ; i ++ ) {
var anchor = anchors [ i ] ,
hasParent = true ;
if ( anchor . asc _GetType ( ) == Asc . c _oAscHyperlinkAnchor . Bookmark ) {
if ( prev _level < 1 )
arr [ arr . length - 1 ] . set ( 'hasSubItems' , true ) ;
arr . push ( new Common . UI . TreeViewModel ( {
name : anchor . asc _GetBookmarkName ( ) ,
level : 1 ,
index : arr . length ,
2018-03-30 12:43:48 +00:00
hasParent : false ,
type : Asc . c _oAscHyperlinkAnchor . Bookmark
2018-03-29 13:02:54 +00:00
} ) ) ;
prev _level = 1 ;
}
2018-03-29 12:22:58 +00:00
}
2018-03-29 13:02:54 +00:00
store . reset ( arr ) ;
2020-03-04 11:45:42 +00:00
this . internalList . collapseAll ( ) ;
2018-03-29 12:22:58 +00:00
}
2018-03-29 13:02:54 +00:00
var rec = this . internalList . getSelectedRec ( ) ;
2019-06-27 11:18:01 +00:00
this . btnOk . setDisabled ( ! rec || rec . get ( 'level' ) == 0 && rec . get ( 'index' ) > 0 ) ;
2018-03-29 13:02:54 +00:00
} else
2020-03-04 11:45:42 +00:00
this . btnOk . setDisabled ( $ . trim ( this . inputUrl . getValue ( ) ) == '' ) ;
2018-02-27 11:55:58 +00:00
} ,
onLinkTypeClick : function ( type , btn , event ) {
this . ShowHideElem ( type ) ;
2020-03-11 12:39:33 +00:00
if ( this . isAutoUpdate ) {
if ( type == c _oHyperlinkType . InternalLink ) {
var rec = this . internalList . getSelectedRec ( ) ;
this . inputDisplay . setValue ( rec && ( rec . get ( 'level' ) || rec . get ( 'index' ) == 0 ) ? rec . get ( 'name' ) : '' ) ;
} else {
this . inputDisplay . setValue ( this . inputUrl . getValue ( ) ) ;
}
2020-03-11 13:56:06 +00:00
this . isTextChanged = true ;
2020-03-11 12:39:33 +00:00
}
2016-03-11 00:48:53 +00:00
} ,
2018-03-29 13:02:54 +00:00
onSelectItem : function ( picker , item , record , e ) {
this . btnOk . setDisabled ( record . get ( 'level' ) == 0 && record . get ( 'index' ) > 0 ) ;
2020-03-11 13:56:06 +00:00
if ( this . isAutoUpdate ) {
this . inputDisplay . setValue ( ( record . get ( 'level' ) || record . get ( 'index' ) == 0 ) ? record . get ( 'name' ) : '' ) ;
this . isTextChanged = true ;
}
2018-03-29 13:02:54 +00:00
} ,
2016-03-11 00:48:53 +00:00
show : function ( ) {
Common . UI . Window . prototype . show . apply ( this , arguments ) ;
var me = this ;
_ . delay ( function ( ) {
me . inputUrl . cmpEl . find ( 'input' ) . focus ( ) ;
2019-02-25 14:28:20 +00:00
} , 50 ) ;
2016-03-11 00:48:53 +00:00
} ,
setSettings : function ( props ) {
if ( props ) {
var me = this ;
2018-03-29 12:22:58 +00:00
var bookmark = props . get _Bookmark ( ) ,
2020-03-17 07:42:03 +00:00
type = ( bookmark === null || bookmark == '' ) ? ( ( props . get _Value ( ) || ! Common . Utils . InternalSettings . get ( "de-settings-link-type" ) ) ? c _oHyperlinkType . WebLink : c _oHyperlinkType . InternalLink ) : c _oHyperlinkType . InternalLink ;
2018-03-29 12:22:58 +00:00
2018-02-27 11:55:58 +00:00
( type == c _oHyperlinkType . WebLink ) ? me . btnExternal . toggle ( true ) : me . btnInternal . toggle ( true ) ;
me . ShowHideElem ( type ) ;
2018-03-29 12:22:58 +00:00
if ( type == c _oHyperlinkType . WebLink ) {
if ( props . get _Value ( ) ) {
me . inputUrl . setValue ( props . get _Value ( ) . replace ( new RegExp ( " " , 'g' ) , "%20" ) ) ;
} else {
me . inputUrl . setValue ( '' ) ;
}
2020-03-10 14:49:42 +00:00
this . btnOk . setDisabled ( $ . trim ( this . inputUrl . getValue ( ) ) == '' ) ;
2016-03-11 00:48:53 +00:00
} else {
2018-03-29 12:22:58 +00:00
if ( props . is _TopOfDocument ( ) )
this . internalList . selectByIndex ( 0 ) ;
2018-03-30 12:43:48 +00:00
else if ( props . is _Heading ( ) ) {
2020-03-10 14:49:42 +00:00
var rec = this . internalList . store . findWhere ( { type : Asc . c _oAscHyperlinkAnchor . Heading , headingParagraph : props . get _Heading ( ) } ) ;
if ( rec ) {
this . internalList . expandRecord ( this . internalList . store . at ( 1 ) ) ;
2018-03-30 12:43:48 +00:00
this . internalList . scrollToRecord ( this . internalList . selectRecord ( rec ) ) ;
2020-03-10 14:49:42 +00:00
}
2018-03-30 12:43:48 +00:00
} else {
var rec = this . internalList . store . findWhere ( { type : Asc . c _oAscHyperlinkAnchor . Bookmark , name : bookmark } ) ;
2020-03-10 14:49:42 +00:00
if ( rec ) {
this . internalList . expandRecord ( this . internalList . store . findWhere ( { type : Asc . c _oAscHyperlinkAnchor . Bookmark , level : 0 } ) ) ;
2018-03-29 12:22:58 +00:00
this . internalList . scrollToRecord ( this . internalList . selectRecord ( rec ) ) ;
2020-03-10 14:49:42 +00:00
}
2018-03-29 12:22:58 +00:00
}
2020-03-10 14:49:42 +00:00
var rec = this . internalList . getSelectedRec ( ) ;
this . btnOk . setDisabled ( ! rec || rec . get ( 'level' ) == 0 && rec . get ( 'index' ) > 0 ) ;
2016-03-11 00:48:53 +00:00
}
if ( props . get _Text ( ) !== null ) {
me . inputDisplay . setValue ( props . get _Text ( ) ) ;
me . inputDisplay . setDisabled ( false ) ;
2020-03-11 13:56:06 +00:00
me . isAutoUpdate = ( me . inputDisplay . getValue ( ) == '' || type == c _oHyperlinkType . WebLink && me . inputUrl . getValue ( ) == me . inputDisplay . getValue ( ) ) ;
2016-03-11 00:48:53 +00:00
} else {
me . inputDisplay . setValue ( this . textDefault ) ;
me . inputDisplay . setDisabled ( true ) ;
}
this . isTextChanged = false ;
me . inputTip . setValue ( props . get _ToolTip ( ) ) ;
2017-12-15 10:06:52 +00:00
me . _originalProps = props ;
2016-03-11 00:48:53 +00:00
}
} ,
getSettings : function ( ) {
var me = this ,
2018-03-29 12:31:03 +00:00
props = new Asc . CHyperlinkProperty ( ) ,
2020-03-11 13:56:06 +00:00
display = '' ,
type = this . btnExternal . isActive ( ) ? c _oHyperlinkType . WebLink : c _oHyperlinkType . InternalLink ;
2016-03-11 00:48:53 +00:00
2020-03-11 13:56:06 +00:00
if ( type == c _oHyperlinkType . WebLink ) { //WebLink
2018-03-29 12:22:58 +00:00
var url = $ . trim ( me . inputUrl . getValue ( ) ) ;
2016-03-11 00:48:53 +00:00
2018-03-29 12:22:58 +00:00
if ( ! /(((^https?)|(^ftp)):\/\/)|(^mailto:)/i . test ( url ) )
url = ( ( me . isEmail ) ? 'mailto:' : 'http://' ) + url ;
url = url . replace ( new RegExp ( "%20" , 'g' ) , " " ) ;
props . put _Value ( url ) ;
props . put _Bookmark ( null ) ;
2018-03-29 12:31:03 +00:00
display = url ;
2018-03-29 12:22:58 +00:00
} else {
var rec = this . internalList . getSelectedRec ( ) ;
2019-06-27 11:18:01 +00:00
if ( rec ) {
props . put _Bookmark ( rec . get ( 'name' ) ) ;
if ( rec . get ( 'index' ) == 0 )
2018-03-29 12:22:58 +00:00
props . put _TopOfDocument ( ) ;
2019-06-27 11:18:01 +00:00
var para = rec . get ( 'headingParagraph' ) ;
2018-03-30 12:43:48 +00:00
if ( para )
props . put _Heading ( para ) ;
2019-06-27 11:18:01 +00:00
display = rec . get ( 'name' ) ;
2018-03-29 12:22:58 +00:00
}
}
2016-03-11 00:48:53 +00:00
2020-03-11 13:56:06 +00:00
if ( ! me . inputDisplay . isDisabled ( ) && ( me . isTextChanged || _ . isEmpty ( me . inputDisplay . getValue ( ) ) ) ) {
if ( _ . isEmpty ( me . inputDisplay . getValue ( ) ) || type == c _oHyperlinkType . WebLink && me . isAutoUpdate )
2018-03-29 12:31:03 +00:00
me . inputDisplay . setValue ( display ) ;
2016-03-11 00:48:53 +00:00
props . put _Text ( me . inputDisplay . getValue ( ) ) ;
} else {
props . put _Text ( null ) ;
}
props . put _ToolTip ( me . inputTip . getValue ( ) ) ;
2017-12-15 10:06:52 +00:00
props . put _InternalHyperlink ( me . _originalProps . get _InternalHyperlink ( ) ) ;
2016-03-11 00:48:53 +00:00
return props ;
} ,
onBtnClick : function ( event ) {
this . _handleInput ( event . currentTarget . attributes [ 'result' ] . value ) ;
} ,
2018-09-21 13:01:09 +00:00
onPrimary : function ( event ) {
this . _handleInput ( 'ok' ) ;
return false ;
2016-03-11 00:48:53 +00:00
} ,
_handleInput : function ( state ) {
if ( this . options . handler ) {
if ( state == 'ok' ) {
2018-04-03 10:22:24 +00:00
if ( this . btnExternal . isActive ( ) ) { //WebLink
2018-03-29 12:22:58 +00:00
if ( this . inputUrl . checkValidate ( ) !== true ) {
2020-03-11 10:34:37 +00:00
this . isInputFirstChange = true ;
2018-03-29 12:22:58 +00:00
this . inputUrl . cmpEl . find ( 'input' ) . focus ( ) ;
return ;
}
} else {
var rec = this . internalList . getSelectedRec ( ) ;
2019-06-27 11:18:01 +00:00
if ( ! rec || rec . get ( 'level' ) == 0 && rec . get ( 'index' ) > 0 )
2018-03-29 12:22:58 +00:00
return ;
2016-03-11 00:48:53 +00:00
}
2018-03-29 12:22:58 +00:00
if ( this . inputDisplay . checkValidate ( ) !== true ) {
2016-03-11 00:48:53 +00:00
this . inputDisplay . cmpEl . find ( 'input' ) . focus ( ) ;
return ;
}
2020-03-17 07:42:03 +00:00
( ! this . _originalProps . get _Bookmark ( ) && ! this . _originalProps . get _Value ( ) ) && Common . Utils . InternalSettings . set ( "de-settings-link-type" , this . btnInternal . isActive ( ) ) ; // save last added hyperlink
2016-03-11 00:48:53 +00:00
}
this . options . handler . call ( this , this , state ) ;
}
this . close ( ) ;
} ,
textUrl : 'Link to' ,
textDisplay : 'Display' ,
txtEmpty : 'This field is required' ,
txtNotUrl : 'This field should be a URL in the format \"http://www.example.com\"' ,
textTooltip : 'ScreenTip text' ,
textDefault : 'Selected text' ,
2018-02-27 11:55:58 +00:00
textTitle : 'Hyperlink Settings' ,
textExternal : 'External Link' ,
2018-03-29 12:22:58 +00:00
textInternal : 'Place in Document' ,
txtBeginning : 'Beginning of document' ,
txtHeadings : 'Headings' ,
txtBookmarks : 'Bookmarks'
2016-03-11 00:48:53 +00:00
} , DE . Views . HyperlinkSettingsDialog || { } ) )
} ) ;