!import
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is Mozilla Communicator client code, released
15 * March 31, 1998.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-1999
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 * Charles Manske (cmanske@netscape.com)
24 * Neil Rashbrook (neil@parkwaycc.co.uk)
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40 var gActiveEditor;
41 var anchorElement = null;
42 var imageElement = null;
43 var insertNew = false;
44 var replaceExistingLink = false;
45 var insertLinkAtCaret;
46 var needLinkText = false;
47 var href;
48 var newLinkText;
49 var gHNodeArray = {};
50 var gHaveNamedAnchors = false;
51 var gHaveHeadings = false;
52 var gCanChangeHeadingSelected = true;
53 var gCanChangeAnchorSelected = true;
54 var gHaveDocumentUrl = false;
55
56 // NOTE: Use "href" instead of "a" to distinguish from Named Anchor
57 // The returned node is has an "a" tagName
58 var tagName = "href";
59
60 // dialog initialization code
Startup
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
61 function Startup()
62 {
63 gActiveEditor = GetCurrentEditor();
64 if (!gActiveEditor)
65 {
66 dump("Failed to get active editor!\n");
67 window.close();
68 return;
69 }
70 // Message was wrapped in a <label> or <div>, so actual text is a child text node
71 gDialog.linkTextCaption = document.getElementById("linkTextCaption");
72 gDialog.linkTextMessage = document.getElementById("linkTextMessage");
73 gDialog.linkTextInput = document.getElementById("linkTextInput");
74 gDialog.hrefInput = document.getElementById("hrefInput");
75 gDialog.makeRelativeLink = document.getElementById("MakeRelativeLink");
76 gDialog.AdvancedEditSection = document.getElementById("AdvancedEdit");
77
78 // See if we have a single selected image
79 imageElement = gActiveEditor.getSelectedElement("img");
80
81 if (imageElement)
82 {
83 // Get the parent link if it exists -- more efficient than GetSelectedElement()
84 anchorElement = gActiveEditor.getElementOrParentByTagName("href", imageElement);
85 if (anchorElement)
86 {
87 if (anchorElement.childNodes.length > 1)
88 {
89 // If there are other children, then we want to break
90 // this image away by inserting a new link around it,
91 // so make a new node and copy existing attributes
92 anchorElement = anchorElement.cloneNode(false);
93 //insertNew = true;
94 replaceExistingLink = true;
95 }
96 }
97 }
98 else
99 {
100 // Get an anchor element if caret or
101 // entire selection is within the link.
102 anchorElement = gActiveEditor.getSelectedElement(tagName);
103
104 if (anchorElement)
105 {
106 // Select the entire link
107 gActiveEditor.selectElement(anchorElement);
108 }
109 else
110 {
111 // If selection starts in a link, but extends beyond it,
112 // the user probably wants to extend existing link to new selection,
113 // so check if either end of selection is within a link
114 // POTENTIAL PROBLEM: This prevents user from selecting text in an existing
115 // link and making 2 links.
116 // Note that this isn't a problem with images, handled above
117
118 anchorElement = gActiveEditor.getElementOrParentByTagName("href", gActiveEditor.selection.anchorNode);
119 if (!anchorElement)
120 anchorElement = gActiveEditor.getElementOrParentByTagName("href", gActiveEditor.selection.focusNode);
121
122 if (anchorElement)
123 {
124 // But clone it for reinserting/merging around existing
125 // link that only partially overlaps the selection
126 anchorElement = anchorElement.cloneNode(false);
127 //insertNew = true;
128 replaceExistingLink = true;
129 }
130 }
131 }
132
133 if(!anchorElement)
134 {
135 // No existing link -- create a new one
136 anchorElement = gActiveEditor.createElementWithDefaults(tagName);
137 insertNew = true;
138 // Hide message about removing existing link
139 //document.getElementById("RemoveLinkMsg").hidden = true;
140 }
141 if(!anchorElement)
142 {
143 dump("Failed to get selected element or create a new one!\n");
144 window.close();
145 return;
146 }
147
148 // We insert at caret only when nothing is selected
149 insertLinkAtCaret = gActiveEditor.selection.isCollapsed;
150
151 var selectedText;
152 if (insertLinkAtCaret)
153 {
154 // Groupbox caption:
155 gDialog.linkTextCaption.setAttribute("label", GetString("LinkText"));
156
157 // Message above input field:
158 gDialog.linkTextMessage.setAttribute("value", GetString("EnterLinkText"));
159 gDialog.linkTextMessage.setAttribute("accesskey", GetString("EnterLinkTextAccessKey"));
160 }
161 else
162 {
163 if (!imageElement)
164 {
165 // We get here if selection is exactly around a link node
166 // Check if selection has some text - use that first
167 selectedText = GetSelectionAsText();
168 if (!selectedText)
169 {
170 // No text, look for first image in the selection
171 var children = anchorElement.childNodes;
172 if (children)
173 {
174 for(var i=0; i < children.length; i++)
175 {
176 var nodeName = children.item(i).nodeName.toLowerCase();
177 if (nodeName == "img")
178 {
179 imageElement = children.item(i);
180 break;
181 }
182 }
183 }
184 }
185 }
186 // Set "caption" for link source and the source text or image URL
187 if (imageElement)
188 {
189 gDialog.linkTextCaption.setAttribute("label", GetString("LinkImage"));
190 // Link source string is the source URL of image
191 // TODO: THIS DOESN'T HANDLE MULTIPLE SELECTED IMAGES!
192 gDialog.linkTextMessage.setAttribute("value", imageElement.src);
193 } else {
194 gDialog.linkTextCaption.setAttribute("label", GetString("LinkText"));
195 if (selectedText)
196 {
197 // Use just the first 60 characters and add "..."
198 gDialog.linkTextMessage.setAttribute("value", TruncateStringAtWordEnd(ReplaceWhitespace(selectedText, " "), 60, true));
199 } else {
200 gDialog.linkTextMessage.setAttribute("value", GetString("MixedSelection"));
201 }
202 }
203 }
204
205 // Make a copy to use for AdvancedEdit and onSaveDefault
206 globalElement = anchorElement.cloneNode(false);
207
208 // Get the list of existing named anchors and headings
209 FillLinkMenulist(gDialog.hrefInput, gHNodeArray);
210
211 // We only need to test for this once per dialog load
212 gHaveDocumentUrl = GetDocumentBaseUrl();
213
214 // Set data for the dialog controls
215 InitDialog();
216
217 // Search for a URI pattern in the selected text
218 // as candidate href
219 selectedText = TrimString(selectedText);
220 if (!gDialog.hrefInput.value && TextIsURI(selectedText))
221 gDialog.hrefInput.value = selectedText;
222
223 // Set initial focus
224 if (insertLinkAtCaret) {
225 // We will be using the HREF inputbox, so text message
226 SetTextboxFocus(gDialog.linkTextInput);
227 } else {
228 SetTextboxFocus(gDialog.hrefInput);
229
230 // We will not insert a new link at caret, so remove link text input field
231 gDialog.linkTextInput.hidden = true;
232 gDialog.linkTextInput = null;
233 }
234
235 // This sets enable state on OK button
236 doEnabling();
237
238 SetWindowLocation();
239 }
240
241 // Set dialog widgets with attribute data
242 // We get them from globalElement copy so this can be used
243 // by AdvancedEdit(), which is shared by all property dialogs
InitDialog
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
244 function InitDialog()
245 {
246 // Must use getAttribute, not "globalElement.href",
247 // or foreign chars aren't coverted correctly!
248 gDialog.hrefInput.value = globalElement.getAttribute("href");
249
250 // Set "Relativize" checkbox according to current URL state
251 SetRelativeCheckbox(gDialog.makeRelativeLink);
252 }
253
doEnabling
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
254 function doEnabling()
255 {
256 // We disable Ok button when there's no href text only if inserting a new link
257 var enable = insertNew ? (TrimString(gDialog.hrefInput.value).length > 0) : true;
258
259 // anon. content, so can't use SetElementEnabledById here
260 var dialogNode = document.getElementById("linkDlg");
261 dialogNode.getButton("accept").disabled = !enable;
262
263 SetElementEnabledById( "AdvancedEditButton1", enable);
264 }
265
ChangeLinkLocation
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
266 function ChangeLinkLocation()
267 {
268 SetRelativeCheckbox(gDialog.makeRelativeLink);
269 // Set OK button enable state
270 doEnabling();
271 }
272
273 // Get and validate data from widgets.
274 // Set attributes on globalElement so they can be accessed by AdvancedEdit()
ValidateData
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
275 function ValidateData()
276 {
277 href = TrimString(gDialog.hrefInput.value);
278 if (href)
279 {
280 // Set the HREF directly on the editor document's anchor node
281 // or on the newly-created node if insertNew is true
282 globalElement.setAttribute("href",href);
283 }
284 else if (insertNew)
285 {
286 // We must have a URL to insert a new link
287 //NOTE: We accept an empty HREF on existing link to indicate removing the link
288 ShowInputErrorMessage(GetString("EmptyHREFError"));
289 return false;
290 }
291 if (gDialog.linkTextInput)
292 {
293 // The text we will insert isn't really an attribute,
294 // but it makes sense to validate it
295 newLinkText = TrimString(gDialog.linkTextInput.value);
296 if (!newLinkText)
297 {
298 if (href)
299 newLinkText = href
300 else
301 {
302 ShowInputErrorMessage(GetString("EmptyLinkTextError"));
303 SetTextboxFocus(gDialog.linkTextInput);
304 return false;
305 }
306 }
307 }
308 return true;
309 }
310
onAccept
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
311 function onAccept()
312 {
313 if (ValidateData())
314 {
315 if (href.length > 0)
316 {
317 // Copy attributes to element we are changing or inserting
318 gActiveEditor.cloneAttributes(anchorElement, globalElement);
319
320 // Coalesce into one undo transaction
321 gActiveEditor.beginTransaction();
322
323 // Get text to use for a new link
324 if (insertLinkAtCaret)
325 {
326 // Append the link text as the last child node
327 // of the anchor node
328 var textNode = gActiveEditor.document.createTextNode(newLinkText);
329 if (textNode)
330 anchorElement.appendChild(textNode);
331 try {
332 gActiveEditor.insertElementAtSelection(anchorElement, false);
333 } catch (e) {
334 dump("Exception occured in InsertElementAtSelection\n");
335 return true;
336 }
337 } else if (insertNew || replaceExistingLink)
338 {
339 // Link source was supplied by the selection,
340 // so insert a link node as parent of this
341 // (may be text, image, or other inline content)
342 try {
343 gActiveEditor.insertLinkAroundSelection(anchorElement);
344 } catch (e) {
345 dump("Exception occured in InsertElementAtSelection\n");
346 return true;
347 }
348 }
349 // Check if the link was to a heading
350 if (href in gHNodeArray)
351 {
352 var anchorNode = gActiveEditor.createElementWithDefaults("a");
353 if (anchorNode)
354 {
355 anchorNode.name = href.substr(1);
356
357 // Insert the anchor into the document,
358 // but don't let the transaction change the selection
359 gActiveEditor.setShouldTxnSetSelection(false);
360 gActiveEditor.insertNode(anchorNode, gHNodeArray[href], 0);
361 gActiveEditor.setShouldTxnSetSelection(true);
362 }
363 }
364 gActiveEditor.endTransaction();
365 }
366 else if (!insertNew)
367 {
368 // We already had a link, but empty HREF means remove it
369 EditorRemoveTextProperty("href", "");
370 }
371 SaveWindowLocation();
372 return true;
373 }
374 return false;
375 }