]>
Commit | Line | Data |
---|---|---|
1 | /**\r | |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.\r | |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license\r | |
4 | */\r | |
5 | \r | |
6 | /**\r | |
7 | * @fileOverview The "elementspath" plugin. It shows all elements in the DOM\r | |
8 | * parent tree relative to the current selection in the editing area.\r | |
9 | */\r | |
10 | \r | |
11 | ( function() {\r | |
12 | var commands = {\r | |
13 | toolbarFocus: {\r | |
14 | editorFocus: false,\r | |
15 | readOnly: 1,\r | |
16 | exec: function( editor ) {\r | |
17 | var idBase = editor._.elementsPath.idBase;\r | |
18 | var element = CKEDITOR.document.getById( idBase + '0' );\r | |
19 | \r | |
20 | // Make the first button focus accessible for IE. (#3417)\r | |
21 | // Adobe AIR instead need while of delay.\r | |
22 | element && element.focus( CKEDITOR.env.ie || CKEDITOR.env.air );\r | |
23 | }\r | |
24 | }\r | |
25 | };\r | |
26 | \r | |
27 | var emptyHtml = '<span class="cke_path_empty"> </span>';\r | |
28 | \r | |
29 | var extra = '';\r | |
30 | \r | |
31 | // Some browsers don't cancel key events in the keydown but in the\r | |
32 | // keypress.\r | |
33 | // TODO: Check if really needed.\r | |
34 | if ( CKEDITOR.env.gecko && CKEDITOR.env.mac )\r | |
35 | extra += ' onkeypress="return false;"';\r | |
36 | \r | |
37 | // With Firefox, we need to force the button to redraw, otherwise it\r | |
38 | // will remain in the focus state.\r | |
39 | if ( CKEDITOR.env.gecko )\r | |
40 | extra += ' onblur="this.style.cssText = this.style.cssText;"';\r | |
41 | \r | |
42 | var pathItemTpl = CKEDITOR.addTemplate( 'pathItem', '<a' +\r | |
43 | ' id="{id}"' +\r | |
44 | ' href="{jsTitle}"' +\r | |
45 | ' tabindex="-1"' +\r | |
46 | ' class="cke_path_item"' +\r | |
47 | ' title="{label}"' +\r | |
48 | extra +\r | |
49 | ' hidefocus="true" ' +\r | |
50 | ' onkeydown="return CKEDITOR.tools.callFunction({keyDownFn},{index}, event );"' +\r | |
51 | ' onclick="CKEDITOR.tools.callFunction({clickFn},{index}); return false;"' +\r | |
52 | ' role="button" aria-label="{label}">' +\r | |
53 | '{text}' +\r | |
54 | '</a>' );\r | |
55 | \r | |
56 | CKEDITOR.plugins.add( 'elementspath', {\r | |
57 | // jscs:disable maximumLineLength\r | |
58 | lang: 'af,ar,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%\r | |
59 | // jscs:enable maximumLineLength\r | |
60 | init: function( editor ) {\r | |
61 | editor._.elementsPath = {\r | |
62 | idBase: 'cke_elementspath_' + CKEDITOR.tools.getNextNumber() + '_',\r | |
63 | filters: []\r | |
64 | };\r | |
65 | \r | |
66 | editor.on( 'uiSpace', function( event ) {\r | |
67 | if ( event.data.space == 'bottom' )\r | |
68 | initElementsPath( editor, event.data );\r | |
69 | } );\r | |
70 | }\r | |
71 | } );\r | |
72 | \r | |
73 | function initElementsPath( editor, bottomSpaceData ) {\r | |
74 | var spaceId = editor.ui.spaceId( 'path' ),\r | |
75 | spaceElement,\r | |
76 | getSpaceElement = function() {\r | |
77 | if ( !spaceElement )\r | |
78 | spaceElement = CKEDITOR.document.getById( spaceId );\r | |
79 | return spaceElement;\r | |
80 | },\r | |
81 | elementsPath = editor._.elementsPath,\r | |
82 | idBase = elementsPath.idBase;\r | |
83 | \r | |
84 | bottomSpaceData.html += '<span id="' + spaceId + '_label" class="cke_voice_label">' + editor.lang.elementspath.eleLabel + '</span>' +\r | |
85 | '<span id="' + spaceId + '" class="cke_path" role="group" aria-labelledby="' + spaceId + '_label">' + emptyHtml + '</span>';\r | |
86 | \r | |
87 | // Register the ui element to the focus manager.\r | |
88 | editor.on( 'uiReady', function() {\r | |
89 | var element = editor.ui.space( 'path' );\r | |
90 | element && editor.focusManager.add( element, 1 );\r | |
91 | } );\r | |
92 | \r | |
93 | function onClick( elementIndex ) {\r | |
94 | var element = elementsPath.list[ elementIndex ];\r | |
95 | if ( element.equals( editor.editable() ) || element.getAttribute( 'contenteditable' ) == 'true' ) {\r | |
96 | var range = editor.createRange();\r | |
97 | range.selectNodeContents( element );\r | |
98 | range.select();\r | |
99 | } else {\r | |
100 | editor.getSelection().selectElement( element );\r | |
101 | }\r | |
102 | \r | |
103 | // It is important to focus() *after* the above selection\r | |
104 | // manipulation, otherwise Firefox will have troubles. #10119\r | |
105 | editor.focus();\r | |
106 | }\r | |
107 | \r | |
108 | elementsPath.onClick = onClick;\r | |
109 | \r | |
110 | var onClickHanlder = CKEDITOR.tools.addFunction( onClick ),\r | |
111 | onKeyDownHandler = CKEDITOR.tools.addFunction( function( elementIndex, ev ) {\r | |
112 | var idBase = elementsPath.idBase,\r | |
113 | element;\r | |
114 | \r | |
115 | ev = new CKEDITOR.dom.event( ev );\r | |
116 | \r | |
117 | var rtl = editor.lang.dir == 'rtl';\r | |
118 | switch ( ev.getKeystroke() ) {\r | |
119 | case rtl ? 39 : 37: // LEFT-ARROW\r | |
120 | case 9: // TAB\r | |
121 | element = CKEDITOR.document.getById( idBase + ( elementIndex + 1 ) );\r | |
122 | if ( !element )\r | |
123 | element = CKEDITOR.document.getById( idBase + '0' );\r | |
124 | element.focus();\r | |
125 | return false;\r | |
126 | \r | |
127 | case rtl ? 37 : 39: // RIGHT-ARROW\r | |
128 | case CKEDITOR.SHIFT + 9: // SHIFT + TAB\r | |
129 | element = CKEDITOR.document.getById( idBase + ( elementIndex - 1 ) );\r | |
130 | if ( !element )\r | |
131 | element = CKEDITOR.document.getById( idBase + ( elementsPath.list.length - 1 ) );\r | |
132 | element.focus();\r | |
133 | return false;\r | |
134 | \r | |
135 | case 27: // ESC\r | |
136 | editor.focus();\r | |
137 | return false;\r | |
138 | \r | |
139 | case 13: // ENTER // Opera\r | |
140 | case 32: // SPACE\r | |
141 | onClick( elementIndex );\r | |
142 | return false;\r | |
143 | }\r | |
144 | return true;\r | |
145 | } );\r | |
146 | \r | |
147 | editor.on( 'selectionChange', function() {\r | |
148 | var html = [],\r | |
149 | elementsList = elementsPath.list = [],\r | |
150 | namesList = [],\r | |
151 | filters = elementsPath.filters,\r | |
152 | isContentEditable = true,\r | |
153 | \r | |
154 | // Use elementPath to consider children of editable only (#11124).\r | |
155 | elementsChain = editor.elementPath().elements,\r | |
156 | name;\r | |
157 | \r | |
158 | // Starts iteration from body element, skipping html.\r | |
159 | for ( var j = elementsChain.length; j--; ) {\r | |
160 | var element = elementsChain[ j ],\r | |
161 | ignore = 0;\r | |
162 | \r | |
163 | if ( element.data( 'cke-display-name' ) )\r | |
164 | name = element.data( 'cke-display-name' );\r | |
165 | else if ( element.data( 'cke-real-element-type' ) )\r | |
166 | name = element.data( 'cke-real-element-type' );\r | |
167 | else\r | |
168 | name = element.getName();\r | |
169 | \r | |
170 | isContentEditable = element.hasAttribute( 'contenteditable' ) ?\r | |
171 | element.getAttribute( 'contenteditable' ) == 'true' : isContentEditable;\r | |
172 | \r | |
173 | // If elem is non-contenteditable, and it's not specifying contenteditable\r | |
174 | // attribute - then elem should be ignored.\r | |
175 | if ( !isContentEditable && !element.hasAttribute( 'contenteditable' ) )\r | |
176 | ignore = 1;\r | |
177 | \r | |
178 | for ( var i = 0; i < filters.length; i++ ) {\r | |
179 | var ret = filters[ i ]( element, name );\r | |
180 | if ( ret === false ) {\r | |
181 | ignore = 1;\r | |
182 | break;\r | |
183 | }\r | |
184 | name = ret || name;\r | |
185 | }\r | |
186 | \r | |
187 | if ( !ignore ) {\r | |
188 | elementsList.unshift( element );\r | |
189 | namesList.unshift( name );\r | |
190 | }\r | |
191 | }\r | |
192 | \r | |
193 | for ( var iterationLimit = elementsList.length, index = 0; index < iterationLimit; index++ ) {\r | |
194 | name = namesList[ index ];\r | |
195 | var label = editor.lang.elementspath.eleTitle.replace( /%1/, name ),\r | |
196 | item = pathItemTpl.output( {\r | |
197 | id: idBase + index,\r | |
198 | label: label,\r | |
199 | text: name,\r | |
200 | jsTitle: 'javascript:void(\'' + name + '\')', // jshint ignore:line\r | |
201 | index: index,\r | |
202 | keyDownFn: onKeyDownHandler,\r | |
203 | clickFn: onClickHanlder\r | |
204 | } );\r | |
205 | \r | |
206 | html.unshift( item );\r | |
207 | }\r | |
208 | \r | |
209 | var space = getSpaceElement();\r | |
210 | space.setHtml( html.join( '' ) + emptyHtml );\r | |
211 | editor.fire( 'elementsPathUpdate', { space: space } );\r | |
212 | } );\r | |
213 | \r | |
214 | function empty() {\r | |
215 | spaceElement && spaceElement.setHtml( emptyHtml );\r | |
216 | delete elementsPath.list;\r | |
217 | }\r | |
218 | \r | |
219 | editor.on( 'readOnly', empty );\r | |
220 | editor.on( 'contentDomUnload', empty );\r | |
221 | \r | |
222 | editor.addCommand( 'elementsPathFocus', commands.toolbarFocus );\r | |
223 | editor.setKeystroke( CKEDITOR.ALT + 122 /*F11*/, 'elementsPathFocus' );\r | |
224 | }\r | |
225 | } )();\r | |
226 | \r | |
227 | /**\r | |
228 | * Fired when the contents of the elementsPath are changed.\r | |
229 | *\r | |
230 | * @event elementsPathUpdate\r | |
231 | * @member CKEDITOR.editor\r | |
232 | * @param {CKEDITOR.editor} editor This editor instance.\r | |
233 | * @param data\r | |
234 | * @param {CKEDITOR.dom.element} data.space The elementsPath container.\r | |
235 | */\r |