Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* import-globals-from ../../composer/content/editorUtilities.js */
/* import-globals-from EdDialogCommon.js */
var gActiveEditor;
var anchorElement = null;
var imageElement = null;
var insertNew = false;
var replaceExistingLink = false;
var insertLinkAtCaret;
var needLinkText = false;
var href;
var newLinkText;
var gHNodeArray = {};
var gHaveNamedAnchors = false;
var gHaveHeadings = false;
var gCanChangeHeadingSelected = true;
var gCanChangeAnchorSelected = true;
// NOTE: Use "href" instead of "a" to distinguish from Named Anchor
// The returned node is has an "a" tagName
var tagName = "href";
// dialog initialization code
document.addEventListener("dialogaccept", onAccept);
document.addEventListener("dialogcancel", onCancel);
function Startup() {
gActiveEditor = GetCurrentEditor();
if (!gActiveEditor) {
dump("Failed to get active editor!\n");
window.close();
return;
}
// Message was wrapped in a <label> or <div>, so actual text is a child text node
gDialog.linkTextCaption = document.getElementById("linkTextCaption");
gDialog.linkTextMessage = document.getElementById("linkTextMessage");
gDialog.linkTextInput = document.getElementById("linkTextInput");
gDialog.hrefInput = document.getElementById("hrefInput");
gDialog.makeRelativeLink = document.getElementById("MakeRelativeLink");
gDialog.AdvancedEditSection = document.getElementById("AdvancedEdit");
// See if we have a single selected image
imageElement = gActiveEditor.getSelectedElement("img");
if (imageElement) {
// Get the parent link if it exists -- more efficient than GetSelectedElement()
anchorElement = gActiveEditor.getElementOrParentByTagName(
"href",
imageElement
);
if (anchorElement) {
if (anchorElement.childNodes.length > 1) {
// If there are other children, then we want to break
// this image away by inserting a new link around it,
// so make a new node and copy existing attributes
anchorElement = anchorElement.cloneNode(false);
// insertNew = true;
replaceExistingLink = true;
}
}
} else {
// Get an anchor element if caret or
// entire selection is within the link.
anchorElement = gActiveEditor.getSelectedElement(tagName);
if (anchorElement) {
// Select the entire link
gActiveEditor.selectElement(anchorElement);
} else {
// If selection starts in a link, but extends beyond it,
// the user probably wants to extend existing link to new selection,
// so check if either end of selection is within a link
// POTENTIAL PROBLEM: This prevents user from selecting text in an existing
// link and making 2 links.
// Note that this isn't a problem with images, handled above
anchorElement = gActiveEditor.getElementOrParentByTagName(
"href",
gActiveEditor.selection.anchorNode
);
if (!anchorElement) {
anchorElement = gActiveEditor.getElementOrParentByTagName(
"href",
gActiveEditor.selection.focusNode
);
}
if (anchorElement) {
// But clone it for reinserting/merging around existing
// link that only partially overlaps the selection
anchorElement = anchorElement.cloneNode(false);
// insertNew = true;
replaceExistingLink = true;
}
}
}
if (!anchorElement) {
// No existing link -- create a new one
anchorElement = gActiveEditor.createElementWithDefaults(tagName);
insertNew = true;
// Hide message about removing existing link
// document.getElementById("RemoveLinkMsg").hidden = true;
}
if (!anchorElement) {
dump("Failed to get selected element or create a new one!\n");
window.close();
return;
}
// We insert at caret only when nothing is selected
insertLinkAtCaret = gActiveEditor.selection.isCollapsed;
var selectedText;
if (insertLinkAtCaret) {
// Groupbox caption:
gDialog.linkTextCaption.setAttribute("label", GetString("LinkText"));
// Message above input field:
gDialog.linkTextMessage.setAttribute("value", GetString("EnterLinkText"));
gDialog.linkTextMessage.setAttribute(
"accesskey",
GetString("EnterLinkTextAccessKey")
);
} else {
if (!imageElement) {
// We get here if selection is exactly around a link node
// Check if selection has some text - use that first
selectedText = GetSelectionAsText();
if (!selectedText) {
// No text, look for first image in the selection
var children = anchorElement.childNodes;
if (children) {
for (var i = 0; i < children.length; i++) {
var nodeName = children.item(i).nodeName.toLowerCase();
if (nodeName == "img") {
imageElement = children.item(i);
break;
}
}
}
}
}
// Set "caption" for link source and the source text or image URL
if (imageElement) {
gDialog.linkTextCaption.setAttribute("label", GetString("LinkImage"));
// Link source string is the source URL of image
// TODO: THIS DOESN'T HANDLE MULTIPLE SELECTED IMAGES!
gDialog.linkTextMessage.setAttribute("value", imageElement.src);
} else {
gDialog.linkTextCaption.setAttribute("label", GetString("LinkText"));
if (selectedText) {
// Use just the first 60 characters and add "..."
gDialog.linkTextMessage.setAttribute(
"value",
TruncateStringAtWordEnd(
ReplaceWhitespace(selectedText, " "),
60,
true
)
);
} else {
gDialog.linkTextMessage.setAttribute(
"value",
GetString("MixedSelection")
);
}
}
}
// Make a copy to use for AdvancedEdit and onSaveDefault
globalElement = anchorElement.cloneNode(false);
// Get the list of existing named anchors and headings
FillLinkMenulist(gDialog.hrefInput, gHNodeArray);
// We only need to test for this once per dialog load
gHaveDocumentUrl = GetDocumentBaseUrl();
// Set data for the dialog controls
InitDialog();
// Search for a URI pattern in the selected text
// as candidate href
selectedText = TrimString(selectedText);
if (!gDialog.hrefInput.value && TextIsURI(selectedText)) {
gDialog.hrefInput.value = selectedText;
}
// Set initial focus
if (insertLinkAtCaret) {
// We will be using the HREF inputbox, so text message
SetTextboxFocus(gDialog.linkTextInput);
} else {
SetTextboxFocus(gDialog.hrefInput);
// We will not insert a new link at caret, so remove link text input field
gDialog.linkTextInput.hidden = true;
gDialog.linkTextInput = null;
}
// This sets enable state on OK button
doEnabling();
SetWindowLocation();
}
// Set dialog widgets with attribute data
// We get them from globalElement copy so this can be used
// by AdvancedEdit(), which is shared by all property dialogs
function InitDialog() {
// Must use getAttribute, not "globalElement.href",
// or foreign chars aren't converted correctly!
gDialog.hrefInput.value = globalElement.getAttribute("href");
// Set "Relativize" checkbox according to current URL state
SetRelativeCheckbox(gDialog.makeRelativeLink);
}
function doEnabling() {
// We disable Ok button when there's no href text only if inserting a new link
var enable = insertNew
? TrimString(gDialog.hrefInput.value).length > 0
: true;
// anon. content, so can't use SetElementEnabledById here
var dialogNode = document.getElementById("linkDlg");
dialogNode.getButton("accept").disabled = !enable;
SetElementEnabledById("AdvancedEditButton1", enable);
}
function ChangeLinkLocation() {
SetRelativeCheckbox(gDialog.makeRelativeLink);
// Set OK button enable state
doEnabling();
}
// Get and validate data from widgets.
// Set attributes on globalElement so they can be accessed by AdvancedEdit()
function ValidateData() {
href = TrimString(gDialog.hrefInput.value);
if (href) {
// Set the HREF directly on the editor document's anchor node
// or on the newly-created node if insertNew is true
globalElement.setAttribute("href", href);
} else if (insertNew) {
// We must have a URL to insert a new link
// NOTE: We accept an empty HREF on existing link to indicate removing the link
ShowInputErrorMessage(GetString("EmptyHREFError"));
return false;
}
if (gDialog.linkTextInput) {
// The text we will insert isn't really an attribute,
// but it makes sense to validate it
newLinkText = TrimString(gDialog.linkTextInput.value);
if (!newLinkText) {
if (href) {
newLinkText = href;
} else {
ShowInputErrorMessage(GetString("EmptyLinkTextError"));
SetTextboxFocus(gDialog.linkTextInput);
return false;
}
}
}
return true;
}
function onAccept(event) {
if (ValidateData()) {
if (href.length > 0) {
// Copy attributes to element we are changing or inserting
gActiveEditor.cloneAttributes(anchorElement, globalElement);
// Coalesce into one undo transaction
gActiveEditor.beginTransaction();
// Get text to use for a new link
if (insertLinkAtCaret) {
// Append the link text as the last child node
// of the anchor node
var textNode = gActiveEditor.document.createTextNode(newLinkText);
if (textNode) {
anchorElement.appendChild(textNode);
}
try {
gActiveEditor.insertElementAtSelection(anchorElement, false);
} catch (e) {
dump("Exception occurred in InsertElementAtSelection\n");
return;
}
} else if (insertNew || replaceExistingLink) {
// Link source was supplied by the selection,
// so insert a link node as parent of this
// (may be text, image, or other inline content)
try {
gActiveEditor.insertLinkAroundSelection(anchorElement);
} catch (e) {
dump("Exception occurred in InsertElementAtSelection\n");
return;
}
}
// Check if the link was to a heading
if (href in gHNodeArray) {
var anchorNode = gActiveEditor.createElementWithDefaults("a");
if (anchorNode) {
anchorNode.name = href.substr(1);
// Insert the anchor into the document,
// but don't let the transaction change the selection
gActiveEditor.insertNode(
anchorNode,
gHNodeArray[href],
0,
true /* preserve selection */
);
}
}
gActiveEditor.endTransaction();
} else if (!insertNew) {
// We already had a link, but empty HREF means remove it
EditorRemoveTextProperty("href", "");
}
SaveWindowLocation();
return;
}
event.preventDefault();
}