X-Git-Url: https://scm.cri.ensmp.fr/git/ckeditor.git/blobdiff_plain/256592bf803e851aa7fc953e08a6e9e58d970f8c..871bad8291b6dbc29d489d95d185458caab25158:/skins/ckeditor/_source/plugins/link/plugin.js diff --git a/skins/ckeditor/_source/plugins/link/plugin.js b/skins/ckeditor/_source/plugins/link/plugin.js new file mode 100644 index 0000000..4442432 --- /dev/null +++ b/skins/ckeditor/_source/plugins/link/plugin.js @@ -0,0 +1,374 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.plugins.add( 'link', +{ + init : function( editor ) + { + // Add the link and unlink buttons. + editor.addCommand( 'link', new CKEDITOR.dialogCommand( 'link' ) ); + editor.addCommand( 'anchor', new CKEDITOR.dialogCommand( 'anchor' ) ); + editor.addCommand( 'unlink', new CKEDITOR.unlinkCommand() ); + editor.addCommand( 'removeAnchor', new CKEDITOR.removeAnchorCommand() ); + editor.ui.addButton( 'Link', + { + label : editor.lang.link.toolbar, + command : 'link' + } ); + editor.ui.addButton( 'Unlink', + { + label : editor.lang.unlink, + command : 'unlink' + } ); + editor.ui.addButton( 'Anchor', + { + label : editor.lang.anchor.toolbar, + command : 'anchor' + } ); + CKEDITOR.dialog.add( 'link', this.path + 'dialogs/link.js' ); + CKEDITOR.dialog.add( 'anchor', this.path + 'dialogs/anchor.js' ); + + // Add the CSS styles for anchor placeholders. + + var side = ( editor.lang.dir == 'rtl' ? 'right' : 'left' ); + var basicCss = + 'background:url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ') no-repeat ' + side + ' center;' + + 'border:1px dotted #00f;'; + + editor.addCss( + 'a.cke_anchor,a.cke_anchor_empty' + + // IE6 breaks with the following selectors. + ( ( CKEDITOR.env.ie && CKEDITOR.env.version < 7 ) ? '' : + ',a[name],a[data-cke-saved-name]' ) + + '{' + + basicCss + + 'padding-' + side + ':18px;' + + // Show the arrow cursor for the anchor image (FF at least). + 'cursor:auto;' + + '}' + + ( CKEDITOR.env.ie ? ( + 'a.cke_anchor_empty' + + '{' + + // Make empty anchor selectable on IE. + 'display:inline-block;' + + '}' + ) : '' ) + + 'img.cke_anchor' + + '{' + + basicCss + + 'width:16px;' + + 'min-height:15px;' + + // The default line-height on IE. + 'height:1.15em;' + + // Opera works better with "middle" (even if not perfect) + 'vertical-align:' + ( CKEDITOR.env.opera ? 'middle' : 'text-bottom' ) + ';' + + '}'); + + // Register selection change handler for the unlink button. + editor.on( 'selectionChange', function( evt ) + { + if ( editor.readOnly ) + return; + + /* + * Despite our initial hope, document.queryCommandEnabled() does not work + * for this in Firefox. So we must detect the state by element paths. + */ + var command = editor.getCommand( 'unlink' ), + element = evt.data.path.lastElement && evt.data.path.lastElement.getAscendant( 'a', true ); + if ( element && element.getName() == 'a' && element.getAttribute( 'href' ) && element.getChildCount() ) + command.setState( CKEDITOR.TRISTATE_OFF ); + else + command.setState( CKEDITOR.TRISTATE_DISABLED ); + } ); + + editor.on( 'doubleclick', function( evt ) + { + var element = CKEDITOR.plugins.link.getSelectedLink( editor ) || evt.data.element; + + if ( !element.isReadOnly() ) + { + if ( element.is( 'a' ) ) + { + evt.data.dialog = ( element.getAttribute( 'name' ) && ( !element.getAttribute( 'href' ) || !element.getChildCount() ) ) ? 'anchor' : 'link'; + editor.getSelection().selectElement( element ); + } + else if ( CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element ) ) + evt.data.dialog = 'anchor'; + } + }); + + // If the "menu" plugin is loaded, register the menu items. + if ( editor.addMenuItems ) + { + editor.addMenuItems( + { + anchor : + { + label : editor.lang.anchor.menu, + command : 'anchor', + group : 'anchor', + order : 1 + }, + + removeAnchor : + { + label : editor.lang.anchor.remove, + command : 'removeAnchor', + group : 'anchor', + order : 5 + }, + + link : + { + label : editor.lang.link.menu, + command : 'link', + group : 'link', + order : 1 + }, + + unlink : + { + label : editor.lang.unlink, + command : 'unlink', + group : 'link', + order : 5 + } + }); + } + + // If the "contextmenu" plugin is loaded, register the listeners. + if ( editor.contextMenu ) + { + editor.contextMenu.addListener( function( element, selection ) + { + if ( !element || element.isReadOnly() ) + return null; + + var anchor = CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element ); + + if ( !anchor && !( anchor = CKEDITOR.plugins.link.getSelectedLink( editor ) ) ) + return null; + + var menu = {}; + + if ( anchor.getAttribute( 'href' ) && anchor.getChildCount() ) + menu = { link : CKEDITOR.TRISTATE_OFF, unlink : CKEDITOR.TRISTATE_OFF }; + + if ( anchor && anchor.hasAttribute( 'name' ) ) + menu.anchor = menu.removeAnchor = CKEDITOR.TRISTATE_OFF; + + return menu; + }); + } + }, + + afterInit : function( editor ) + { + // Register a filter to displaying placeholders after mode change. + + var dataProcessor = editor.dataProcessor, + dataFilter = dataProcessor && dataProcessor.dataFilter, + htmlFilter = dataProcessor && dataProcessor.htmlFilter, + pathFilters = editor._.elementsPath && editor._.elementsPath.filters; + + if ( dataFilter ) + { + dataFilter.addRules( + { + elements : + { + a : function( element ) + { + var attributes = element.attributes; + if ( !attributes.name ) + return null; + + var isEmpty = !element.children.length; + + if ( CKEDITOR.plugins.link.synAnchorSelector ) + { + // IE needs a specific class name to be applied + // to the anchors, for appropriate styling. + var ieClass = isEmpty ? 'cke_anchor_empty' : 'cke_anchor'; + var cls = attributes[ 'class' ]; + if ( attributes.name && ( !cls || cls.indexOf( ieClass ) < 0 ) ) + attributes[ 'class' ] = ( cls || '' ) + ' ' + ieClass; + + if ( isEmpty && CKEDITOR.plugins.link.emptyAnchorFix ) + { + attributes.contenteditable = 'false'; + attributes[ 'data-cke-editable' ] = 1; + } + } + else if ( CKEDITOR.plugins.link.fakeAnchor && isEmpty ) + return editor.createFakeParserElement( element, 'cke_anchor', 'anchor' ); + + return null; + } + } + }); + } + + if ( CKEDITOR.plugins.link.emptyAnchorFix && htmlFilter ) + { + htmlFilter.addRules( + { + elements : + { + a : function( element ) + { + delete element.attributes.contenteditable; + } + } + }); + } + + if ( pathFilters ) + { + pathFilters.push( function( element, name ) + { + if ( name == 'a' ) + { + if ( CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element ) || + ( element.getAttribute( 'name' ) && ( !element.getAttribute( 'href' ) || !element.getChildCount() ) ) ) + { + return 'anchor'; + } + } + }); + } + }, + + requires : [ 'fakeobjects' ] +} ); + +CKEDITOR.plugins.link = +{ + /** + * Get the surrounding link element of current selection. + * @param editor + * @example CKEDITOR.plugins.link.getSelectedLink( editor ); + * @since 3.2.1 + * The following selection will all return the link element. + *
+	 *  li^nk
+	 *  [link]
+	 *  text[link]
+	 *  li[nk]
+	 *  [li]nk]
+	 *  [li]nk
+	 * 
+ */ + getSelectedLink : function( editor ) + { + try + { + var selection = editor.getSelection(); + if ( selection.getType() == CKEDITOR.SELECTION_ELEMENT ) + { + var selectedElement = selection.getSelectedElement(); + if ( selectedElement.is( 'a' ) ) + return selectedElement; + } + + var range = selection.getRanges( true )[ 0 ]; + range.shrink( CKEDITOR.SHRINK_TEXT ); + var root = range.getCommonAncestor(); + return root.getAscendant( 'a', true ); + } + catch( e ) { return null; } + }, + + // Opera and WebKit don't make it possible to select empty anchors. Fake + // elements must be used for them. + fakeAnchor : CKEDITOR.env.opera || CKEDITOR.env.webkit, + + // For browsers that don't support CSS3 a[name]:empty(), note IE9 is included because of #7783. + synAnchorSelector : CKEDITOR.env.ie, + + // For browsers that have editing issue with empty anchor. + emptyAnchorFix : CKEDITOR.env.ie && CKEDITOR.env.version < 8, + + tryRestoreFakeAnchor : function( editor, element ) + { + if ( element && element.data( 'cke-real-element-type' ) && element.data( 'cke-real-element-type' ) == 'anchor' ) + { + var link = editor.restoreRealElement( element ); + if ( link.data( 'cke-saved-name' ) ) + return link; + } + } +}; + +CKEDITOR.unlinkCommand = function(){}; +CKEDITOR.unlinkCommand.prototype = +{ + /** @ignore */ + exec : function( editor ) + { + /* + * execCommand( 'unlink', ... ) in Firefox leaves behind tags at where + * the was, so again we have to remove the link ourselves. (See #430) + * + * TODO: Use the style system when it's complete. Let's use execCommand() + * as a stopgap solution for now. + */ + var selection = editor.getSelection(), + bookmarks = selection.createBookmarks(), + ranges = selection.getRanges(), + rangeRoot, + element; + + for ( var i = 0 ; i < ranges.length ; i++ ) + { + rangeRoot = ranges[i].getCommonAncestor( true ); + element = rangeRoot.getAscendant( 'a', true ); + if ( !element ) + continue; + ranges[i].selectNodeContents( element ); + } + + selection.selectRanges( ranges ); + editor.document.$.execCommand( 'unlink', false, null ); + selection.selectBookmarks( bookmarks ); + }, + + startDisabled : true +}; + +CKEDITOR.removeAnchorCommand = function(){}; +CKEDITOR.removeAnchorCommand.prototype = +{ + /** @ignore */ + exec : function( editor ) + { + var sel = editor.getSelection(), + bms = sel.createBookmarks(), + anchor; + if ( sel && ( anchor = sel.getSelectedElement() ) && ( CKEDITOR.plugins.link.fakeAnchor && !anchor.getChildCount() ? CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, anchor ) : anchor.is( 'a' ) ) ) + anchor.remove( 1 ); + else + { + if ( ( anchor = CKEDITOR.plugins.link.getSelectedLink( editor ) ) ) + { + if ( anchor.hasAttribute( 'href' ) ) + { + anchor.removeAttributes( { name : 1, 'data-cke-saved-name' : 1 } ); + anchor.removeClass( 'cke_anchor' ); + } + else + anchor.remove( 1 ); + } + } + sel.selectBookmarks( bms ); + } +}; + +CKEDITOR.tools.extend( CKEDITOR.config, +{ + linkShowAdvancedTab : true, + linkShowTargetTab : true +} );