mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-30 13:45:27 +00:00
1559 lines
41 KiB
JavaScript
1559 lines
41 KiB
JavaScript
/*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is Mozilla Communicator client code, released
|
|
* March 31, 1998.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Pete Collins
|
|
* Brian King
|
|
*/
|
|
/*
|
|
if we ever need to use a different string bundle, use srGetStrBundle
|
|
by including
|
|
<script type="application/x-javascript" src="chrome://global/content/strres.js"/>
|
|
e.g.:
|
|
var bundle = srGetStrBundle("chrome://global/locale/filepicker.properties");
|
|
*/
|
|
|
|
// Each editor window must include this file
|
|
// Variables shared by all dialogs:
|
|
var editorShell;
|
|
|
|
// Object to attach commonly-used widgets (all dialogs should use this)
|
|
var gDialog = {};
|
|
|
|
// Bummer! Can't get at enums from nsIDocumentEncoder.h
|
|
var gOutputSelectionOnly = 1;
|
|
var gOutputFormatted = 2;
|
|
var gOutputNoDoctype = 4;
|
|
var gOutputBodyOnly = 8;
|
|
var gOutputPreformatted = 16;
|
|
var gOutputWrap = 32;
|
|
var gOutputFormatFlowed = 64;
|
|
var gOutputAbsoluteLinks = 128;
|
|
var gOutputEncodeEntities = 256;
|
|
var gStringBundle;
|
|
var gValidationError = false;
|
|
var gIOService;
|
|
var gOS = "";
|
|
const gWin = "Win";
|
|
const gUNIX = "UNIX";
|
|
const gMac = "Mac";
|
|
|
|
// Use for 'defaultIndex' param in InitPixelOrPercentMenulist
|
|
var gPixel = 0;
|
|
var gPercent = 1;
|
|
|
|
var maxPixels = 10000;
|
|
// For dialogs that expand in size. Default is smaller size see "onMoreFewer()" below
|
|
var SeeMore = false;
|
|
|
|
// A XUL element with id="location" for managing
|
|
// dialog location relative to parent window
|
|
var gLocation;
|
|
|
|
// The element being edited - so AdvancedEdit can have access to it
|
|
var globalElement;
|
|
|
|
function InitEditorShell()
|
|
{
|
|
// get the editor shell from the parent window
|
|
|
|
editorShell = window.opener.editorShell;
|
|
if (editorShell) {
|
|
editorShell = editorShell.QueryInterface(Components.interfaces.nsIEditorShell);
|
|
}
|
|
if (!editorShell) {
|
|
dump("EditorShell not found!!!\n");
|
|
window.close();
|
|
return false;
|
|
}
|
|
|
|
// Save as a property of the window so it can be used by child dialogs
|
|
|
|
window.editorShell = editorShell;
|
|
|
|
return true;
|
|
}
|
|
|
|
function StringExists(string)
|
|
{
|
|
if (string != null && string != "undefined" && string.length > 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Validate contents of an input field
|
|
*
|
|
* inputWidget The 'textbox' XUL element for text input of the attribute's value
|
|
* listWidget The 'menulist' XUL element for choosing "pixel" or "percent"
|
|
* May be null when no pixel/percent is used.
|
|
* minVal minimum allowed for input widget's value
|
|
* maxVal maximum allowed for input widget's value
|
|
* (when "listWidget" is used, maxVal is used for "pixel" maximum,
|
|
* 100% is assumed if "percent" is the user's choice)
|
|
* element The DOM element that we set the attribute on. May be null.
|
|
* attName Name of the attribute to set. May be null or ignored if "element" is null
|
|
* mustHaveValue If true, error dialog is displayed if "value" is empty string
|
|
*
|
|
* This calls "ValidateNumberRange()", which puts up an error dialog to inform the user.
|
|
* If error, we also:
|
|
* Shift focus and select contents of the inputWidget,
|
|
* Switch to appropriate panel of tabbed dialog if user implements "SwitchToValidate()",
|
|
* and/or will expand the dialog to full size if "More / Fewer" feature is implemented
|
|
*
|
|
* Returns the "value" as a string, or "" if error or input contents are empty
|
|
* The global "gValidationError" variable is set true if error was found
|
|
*/
|
|
function ValidateNumber(inputWidget, listWidget, minVal, maxVal, element, attName, mustHaveValue, mustShowMoreSection)
|
|
{
|
|
if (!inputWidget)
|
|
{
|
|
gValidationError = true;
|
|
return "";
|
|
}
|
|
|
|
// Global error return value
|
|
gValidationError = false;
|
|
var maxLimit = maxVal;
|
|
var isPercent = false;
|
|
|
|
var numString = TrimString(inputWidget.value);
|
|
if (numString || mustHaveValue)
|
|
{
|
|
if (listWidget)
|
|
isPercent = (listWidget.selectedIndex == 1);
|
|
if (isPercent)
|
|
maxLimit = 100;
|
|
|
|
// This method puts up the error message
|
|
numString = ValidateNumberRange(numString, minVal, maxLimit, mustHaveValue);
|
|
if(!numString)
|
|
{
|
|
// Switch to appropriate panel for error reporting
|
|
SwitchToValidatePanel();
|
|
|
|
// or expand dialog for users of "More / Fewer" button
|
|
if ("dialog" in window && dialog &&
|
|
"MoreSection" in gDialog && gDialog.MoreSection)
|
|
{
|
|
if ( !SeeMore )
|
|
onMoreFewer();
|
|
}
|
|
|
|
// Error - shift to offending input widget
|
|
SetTextboxFocus(inputWidget);
|
|
gValidationError = true;
|
|
}
|
|
else
|
|
{
|
|
if (isPercent)
|
|
numString += "%";
|
|
if (element)
|
|
element.setAttribute(attName, numString);
|
|
}
|
|
} else if (element) {
|
|
element.removeAttribute(attName);
|
|
}
|
|
return numString;
|
|
}
|
|
|
|
/* Validate contents of an input field
|
|
*
|
|
* value number to validate
|
|
* minVal minimum allowed for input widget's value
|
|
* maxVal maximum allowed for input widget's value
|
|
* (when "listWidget" is used, maxVal is used for "pixel" maximum,
|
|
* 100% is assumed if "percent" is the user's choice)
|
|
* mustHaveValue If true, error dialog is displayed if "value" is empty string
|
|
*
|
|
* If inputWidget's value is outside of range, or is empty when "mustHaveValue" = true,
|
|
* an error dialog is popuped up to inform the user. The focus is shifted
|
|
* to the inputWidget.
|
|
*
|
|
* Returns the "value" as a string, or "" if error or input contents are empty
|
|
* The global "gValidationError" variable is set true if error was found
|
|
*/
|
|
function ValidateNumberRange(value, minValue, maxValue, mustHaveValue)
|
|
{
|
|
// Initialize global error flag
|
|
gValidationError = false;
|
|
value = TrimString(String(value));
|
|
|
|
// We don't show error for empty string unless caller wants to
|
|
if (!value && !mustHaveValue)
|
|
return "";
|
|
|
|
var numberStr = "";
|
|
|
|
if (value.length > 0)
|
|
{
|
|
// Extract just numeric characters
|
|
var number = Number(value.replace(/\D+/g, ""));
|
|
if (number >= minValue && number <= maxValue )
|
|
{
|
|
// Return string version of the number
|
|
return String(number);
|
|
}
|
|
numberStr = String(number);
|
|
}
|
|
|
|
var message = "";
|
|
|
|
if (numberStr.length > 0)
|
|
{
|
|
// We have a number from user outside of allowed range
|
|
message = editorShell.GetString( "ValidateRangeMsg");
|
|
message = message.replace(/%n%/, numberStr);
|
|
message += "\n ";
|
|
}
|
|
message += editorShell.GetString( "ValidateNumberMsg");
|
|
|
|
// Replace variable placeholders in message with number values
|
|
message = message.replace(/%min%/, minValue).replace(/%max%/, maxValue);
|
|
ShowInputErrorMessage(message);
|
|
|
|
// Return an empty string to indicate error
|
|
gValidationError = true;
|
|
return "";
|
|
}
|
|
|
|
function SetTextboxFocusById(id)
|
|
{
|
|
SetTextboxFocus(document.getElementById(id));
|
|
}
|
|
|
|
function SetTextboxFocus(textbox)
|
|
{
|
|
if (textbox)
|
|
{
|
|
// Select entire contents
|
|
if (textbox.value.length > 0)
|
|
textbox.select();
|
|
else
|
|
textbox.focus();
|
|
}
|
|
}
|
|
|
|
function ShowInputErrorMessage(message)
|
|
{
|
|
AlertWithTitle(GetString("InputError"), message);
|
|
window.focus();
|
|
}
|
|
|
|
function AlertWithTitle(title, message)
|
|
{
|
|
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
|
|
promptService = promptService.QueryInterface(Components.interfaces.nsIPromptService);
|
|
|
|
if (promptService)
|
|
{
|
|
if (!title)
|
|
title = GetString("Alert");
|
|
|
|
// "window" is the calling dialog window
|
|
promptService.alert(window, title, message);
|
|
}
|
|
}
|
|
|
|
// Optional: Caller may supply text to substitue for "Ok" and/or "Cancel"
|
|
function ConfirmWithTitle(title, message, okButtonText, cancelButtonText)
|
|
{
|
|
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
|
|
promptService = promptService.QueryInterface(Components.interfaces.nsIPromptService);
|
|
|
|
if (promptService)
|
|
{
|
|
var result = {value:0};
|
|
var okFlag = okButtonText ? promptService.BUTTON_TITLE_IS_STRING : promptService.BUTTON_TITLE_OK;
|
|
var cancelFlag = cancelButtonText ? promptService.BUTTON_TITLE_IS_STRING : promptService.BUTTON_TITLE_CANCEL;
|
|
|
|
promptService.confirmEx(window, title, message,
|
|
(okFlag * promptService.BUTTON_POS_0) +
|
|
(cancelFlag * promptService.BUTTON_POS_1),
|
|
okButtonText, cancelButtonText, null, null, {value:0}, result);
|
|
return (result.value == 0);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function GetString(name)
|
|
{
|
|
if (editorShell)
|
|
{
|
|
return editorShell.GetString(name);
|
|
}
|
|
else
|
|
{
|
|
// Non-editors (like prefs) may use these methods
|
|
if (!gStringBundle)
|
|
{
|
|
gStringBundle = srGetStrBundle("chrome://editor/locale/editor.properties");
|
|
if (!gStringBundle)
|
|
return null;
|
|
}
|
|
return gStringBundle.GetStringFromName(name);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function TrimStringLeft(string)
|
|
{
|
|
if(!string) return "";
|
|
return string.replace(/^\s+/, "");
|
|
}
|
|
|
|
function TrimStringRight(string)
|
|
{
|
|
if (!string) return "";
|
|
return string.replace(/\s+$/, '');
|
|
}
|
|
|
|
// Remove whitespace from both ends of a string
|
|
function TrimString(string)
|
|
{
|
|
if (!string) return "";
|
|
return string.replace(/(^\s+)|(\s+$)/g, '')
|
|
}
|
|
|
|
function IsWhitespace(string)
|
|
{
|
|
return /^\s/.test(string);
|
|
}
|
|
|
|
function TruncateStringAtWordEnd(string, maxLength, addEllipses)
|
|
{
|
|
// Return empty if string is null, undefined, or the empty string
|
|
if (!string)
|
|
return "";
|
|
|
|
// We assume they probably don't want whitespace at the beginning
|
|
string = string.replace(/^\s+/, '');
|
|
if (string.length <= maxLength)
|
|
return string;
|
|
|
|
// We need to truncate the string to maxLength or fewer chars
|
|
if (addEllipses)
|
|
maxLength -= 3;
|
|
string = string.replace(RegExp("(.{0," + maxLength + "})\\s.*"), "$1")
|
|
|
|
if (string.length > maxLength)
|
|
string = string.slice(0, maxLength);
|
|
|
|
if (addEllipses)
|
|
string += "...";
|
|
return string;
|
|
}
|
|
|
|
// Replace all whitespace characters with supplied character
|
|
// E.g.: Use charReplace = " ", to "unwrap" the string by removing line-end chars
|
|
// Use charReplace = "_" when you don't want spaces (like in a URL)
|
|
function ReplaceWhitespace(string, charReplace)
|
|
{
|
|
return string.replace(/(^\s+)|(\s+$)/g,'').replace(/\s+/g,charReplace)
|
|
}
|
|
|
|
// Replace whitespace with "_" and allow only HTML CDATA
|
|
// characters: "a"-"z","A"-"Z","0"-"9", "_", ":", "-", ".",
|
|
// and characters above ASCII 127
|
|
function ConvertToCDATAString(string)
|
|
{
|
|
return string.replace(/\s+/g,"_").replace(/[^a-zA-Z0-9_\.\-\:\u0080-\uFFFF]+/g,'');
|
|
}
|
|
|
|
// this function takes an elementID and a flag
|
|
// if the element can be found by ID, then it is either enabled (by removing "disabled" attr)
|
|
// or disabled (setAttribute) as specified in the "doEnable" parameter
|
|
function SetElementEnabledById( elementID, doEnable )
|
|
{
|
|
var element = document.getElementById(elementID);
|
|
if ( element )
|
|
{
|
|
if ( doEnable )
|
|
{
|
|
element.removeAttribute( "disabled" );
|
|
}
|
|
else
|
|
{
|
|
element.setAttribute( "disabled", "true" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dump("Element "+elementID+" not found in SetElementEnabledById\n");
|
|
}
|
|
}
|
|
|
|
// This function relies on css classes for enabling and disabling
|
|
// This function takes an ID for a label and a flag
|
|
// if an element can be found by its ID, then it is either enabled or disabled
|
|
// The class is set to either "enabled" or "disabled" depending on the flag passed in.
|
|
// This function relies on css having a special appearance for these two classes.
|
|
|
|
function SetClassEnabledById( elementID, doEnable )
|
|
{
|
|
element = document.getElementById(elementID);
|
|
if ( element )
|
|
{
|
|
if ( doEnable )
|
|
{
|
|
element.setAttribute( "class", "enabled" );
|
|
}
|
|
else
|
|
{
|
|
element.setAttribute( "class", "disabled" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dump( "not changing element "+elementID+"\n" );
|
|
}
|
|
}
|
|
|
|
// Get the text appropriate to parent container
|
|
// to determine what a "%" value is refering to.
|
|
// elementForAtt is element we are actually setting attributes on
|
|
// (a temporary copy of element in the doc to allow canceling),
|
|
// but elementInDoc is needed to find parent context in document
|
|
function GetAppropriatePercentString(elementForAtt, elementInDoc)
|
|
{
|
|
var name = elementForAtt.nodeName.toLowerCase();
|
|
if ( name == "td" || name == "th")
|
|
return GetString("PercentOfTable");
|
|
|
|
// Check if element is within a table cell
|
|
// Check if current selection anchor node is within a table cell
|
|
if (editorShell.GetElementOrParentByTagName("td", elementInDoc))
|
|
return GetString("PercentOfCell");
|
|
else
|
|
return GetString("PercentOfWindow");
|
|
}
|
|
|
|
function InitPixelOrPercentMenulist(elementForAtt, elementInDoc, attribute, menulistID, defaultIndex)
|
|
{
|
|
if (!defaultIndex) defaultIndex = gPixel;
|
|
|
|
var size = elementForAtt.getAttribute(attribute);
|
|
var menulist = document.getElementById(menulistID);
|
|
var pixelItem;
|
|
var percentItem;
|
|
|
|
if (!menulist)
|
|
{
|
|
dump("NO MENULIST found for ID="+menulistID+"\n");
|
|
return size;
|
|
}
|
|
|
|
ClearMenulist(menulist);
|
|
pixelItem = AppendStringToMenulist(menulist, GetString("Pixels"));
|
|
|
|
if (!pixelItem) return 0;
|
|
|
|
percentItem = AppendStringToMenulist(menulist, GetAppropriatePercentString(elementForAtt, elementInDoc));
|
|
if (size && size.length > 0)
|
|
{
|
|
// Search for a "%" character
|
|
var percentIndex = size.search(/%/);
|
|
if (percentIndex > 0)
|
|
{
|
|
// Strip out the %
|
|
size = size.substr(0, percentIndex);
|
|
if (percentItem)
|
|
menulist.selectedItem = percentItem;
|
|
}
|
|
else
|
|
menulist.selectedItem = pixelItem;
|
|
}
|
|
else
|
|
menulist.selectedIndex = defaultIndex;
|
|
|
|
return size;
|
|
}
|
|
|
|
function AppendStringToMenulistById(menulist, stringID)
|
|
{
|
|
return AppendStringToMenulist(menulist, editorShell.GetString(stringID));
|
|
}
|
|
|
|
function AppendStringToMenulist(menulist, string)
|
|
{
|
|
if (menulist)
|
|
{
|
|
var menupopup = menulist.firstChild;
|
|
// May not have any popup yet -- so create one
|
|
if (!menupopup)
|
|
{
|
|
menupopup = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menupopup");
|
|
if (menupopup)
|
|
menulist.appendChild(menupopup);
|
|
else
|
|
{
|
|
dump("Failed to create menupoup\n");
|
|
return null;
|
|
}
|
|
}
|
|
var menuItem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem");
|
|
if (menuItem)
|
|
{
|
|
menuItem.setAttribute("label", string);
|
|
menupopup.appendChild(menuItem);
|
|
return menuItem;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function AppendLabelAndValueToMenulist(menulist, labelStr, valueStr)
|
|
{
|
|
if (menulist)
|
|
{
|
|
var menupopup = menulist.firstChild;
|
|
// May not have any popup yet -- so create one
|
|
if (!menupopup)
|
|
{
|
|
menupopup = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menupopup");
|
|
if (menupopup)
|
|
menulist.appendChild(menupopup);
|
|
else
|
|
{
|
|
dump("Failed to create menupoup\n");
|
|
return null;
|
|
}
|
|
}
|
|
var menuItem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem");
|
|
if (menuItem)
|
|
{
|
|
menuItem.setAttribute("label", labelStr);
|
|
menuItem.setAttribute("value", valueStr);
|
|
menupopup.appendChild(menuItem);
|
|
return menuItem;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function ClearMenulist(menulist)
|
|
{
|
|
// Always use "AppendStringToMenulist" so we know there's
|
|
// just one <menupopup> as 1st child of <menulist>
|
|
if (menulist) {
|
|
menulist.selectedItem = null;
|
|
var popup = menulist.firstChild;
|
|
if (popup)
|
|
while (popup.firstChild)
|
|
popup.removeChild(popup.firstChild);
|
|
}
|
|
}
|
|
|
|
/* These help using a <tree> for simple lists
|
|
Assumes this simple structure:
|
|
<tree>
|
|
<treecolgroup><treecol flex="1"/></treecolgroup>
|
|
<treechildren>
|
|
<treeitem>
|
|
<treerow>
|
|
<treecell label="the text the user sees"/>
|
|
*/
|
|
|
|
function AppendStringToTreelistById(tree, stringID)
|
|
{
|
|
return AppendStringToTreelist(tree, editorShell.GetString(stringID));
|
|
}
|
|
|
|
function AppendStringToTreelist(tree, string)
|
|
{
|
|
if (tree)
|
|
{
|
|
var treecols = tree.firstChild;
|
|
if (!treecols)
|
|
{
|
|
dump("Bad XUL: Must have <treecolgroup> as first child\n");
|
|
}
|
|
var treechildren = treecols.nextSibling;
|
|
if (!treechildren)
|
|
{
|
|
treechildren = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "treechildren");
|
|
if (treechildren)
|
|
{
|
|
treechildren.setAttribute("flex","1");
|
|
tree.appendChild(treechildren);
|
|
}
|
|
else
|
|
{
|
|
dump("Failed to create <treechildren>\n");
|
|
return null;
|
|
}
|
|
}
|
|
var treeitem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "treeitem");
|
|
var treerow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "treerow");
|
|
var treecell = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "treecell");
|
|
if (treeitem && treerow && treecell)
|
|
{
|
|
treerow.appendChild(treecell);
|
|
treeitem.appendChild(treerow);
|
|
treechildren.appendChild(treeitem)
|
|
treecell.setAttribute("label", string);
|
|
//var len = Number(tree.getAttribute("length"));
|
|
//if (!len) len = -1;
|
|
tree.setAttribute("length", treechildren.childNodes.length);
|
|
return treeitem;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function ReplaceStringInTreeList(tree, index, string)
|
|
{
|
|
if (tree)
|
|
{
|
|
var treecols = tree.firstChild;
|
|
if (!treecols)
|
|
{
|
|
dump("Bad XUL: Must have <treecolgroup> as first child\n");
|
|
return;
|
|
}
|
|
var treechildren = treecols.nextSibling;
|
|
if (!treechildren)
|
|
return;
|
|
|
|
// Each list item is a <treeitem><treerow><treecell> under <treechildren> node
|
|
var childNodes = treechildren.childNodes;
|
|
if (!childNodes || index >= childNodes.length)
|
|
return;
|
|
|
|
var row = childNodes.item(index).firstChild;
|
|
if (row && row.firstChild)
|
|
{
|
|
row.firstChild.setAttribute("label", string);
|
|
}
|
|
}
|
|
}
|
|
|
|
function ClearTreelist(tree)
|
|
{
|
|
if (tree)
|
|
{
|
|
tree.clearItemSelection();
|
|
// Skip over the first <treecolgroup> child
|
|
if (tree.firstChild)
|
|
{
|
|
var nextChild = tree.firstChild.nextSibling;
|
|
while (nextChild)
|
|
{
|
|
var nextTmp = nextChild.nextSibling;
|
|
tree.removeChild(nextChild);
|
|
nextChild = nextTmp;
|
|
}
|
|
}
|
|
// Count list items
|
|
tree.setAttribute("length", 0);
|
|
}
|
|
}
|
|
|
|
function GetSelectedTreelistAttribute(tree, attr)
|
|
{
|
|
if (tree)
|
|
{
|
|
if (tree.selectedIndex >= 0 &&
|
|
tree.selectedItems.length > 0 &&
|
|
tree.selectedItems[0] &&
|
|
tree.selectedItems[0].firstChild &&
|
|
tree.selectedItems[0].firstChild.firstChild)
|
|
{
|
|
return tree.selectedItems[0].firstChild.firstChild.getAttribute(attr);
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function GetSelectedTreelistValue(tree)
|
|
{
|
|
return GetSelectedTreelistAttribute(tree,"label")
|
|
}
|
|
|
|
function RemoveSelectedTreelistItem(tree)
|
|
{
|
|
if (tree)
|
|
{
|
|
if (tree.selectedIndex >= 0 &&
|
|
tree.selectedItems.length > 0)
|
|
{
|
|
// Get the node to delete
|
|
var treeItem = tree.selectedItems[0];
|
|
if (treeItem)
|
|
{
|
|
tree.clearItemSelection();
|
|
var parent = treeItem.parentNode;
|
|
if (parent)
|
|
{
|
|
parent.removeChild(treeItem);
|
|
tree.setAttribute("length", parent.childNodes.length);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function GetTreelistValueAt(tree, index)
|
|
{
|
|
if (tree)
|
|
{
|
|
var item = tree.getItemAtIndex(index);
|
|
if (item && item.firstChild && item.firstChild.firstChild)
|
|
return item.firstChild.firstChild.getAttribute("label");
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function forceInteger(elementID)
|
|
{
|
|
var editField = document.getElementById( elementID );
|
|
if ( !editField )
|
|
return;
|
|
|
|
var stringIn = editField.value;
|
|
if (stringIn && stringIn.length > 0)
|
|
{
|
|
// Strip out all nonnumeric characters
|
|
stringIn = stringIn.replace(/\D+/g,"");
|
|
if (!stringIn) stringIn = "";
|
|
|
|
// Write back only if changed
|
|
if (stringIn != editField.value)
|
|
editField.value = stringIn;
|
|
}
|
|
}
|
|
|
|
function LimitStringLength(elementID, length)
|
|
{
|
|
var editField = document.getElementById( elementID );
|
|
if ( !editField )
|
|
return;
|
|
|
|
var stringIn = editField.value;
|
|
if (stringIn && stringIn.length > length)
|
|
editField.value = stringIn.slice(0,length);
|
|
}
|
|
|
|
|
|
function onAdvancedEdit()
|
|
{
|
|
// First validate data from widgets in the "simpler" property dialog
|
|
if (ValidateData())
|
|
{
|
|
// Set true if OK is clicked in the Advanced Edit dialog
|
|
window.AdvancedEditOK = false;
|
|
// Open the AdvancedEdit dialog, passing in the element to be edited
|
|
// (the copy named "globalElement")
|
|
window.openDialog("chrome://editor/content/EdAdvancedEdit.xul", "_blank", "chrome,close,titlebar,modal,resizable=yes", "", globalElement);
|
|
window.focus();
|
|
if (window.AdvancedEditOK)
|
|
{
|
|
// Copy edited attributes to the dialog widgets:
|
|
InitDialog();
|
|
}
|
|
}
|
|
}
|
|
|
|
function GetSelectionAsText()
|
|
{
|
|
return editorShell.GetContentsAs("text/plain", gOutputSelectionOnly);
|
|
}
|
|
|
|
|
|
// ** getSelection ()
|
|
// ** This function checks for existence of table around the focus node
|
|
// ** Brian King - XML Workshop
|
|
|
|
function getContainer ()
|
|
{
|
|
tagName = "img";
|
|
selImage = editorShell.GetSelectedElement(tagName);
|
|
if (selImage) // existing image
|
|
{
|
|
oneup = selImage.parentNode;
|
|
return oneup;
|
|
}
|
|
else if (!selImage) // new image insertion
|
|
{
|
|
dump("Not an image element -- Trying for caret selection\n");
|
|
var selection = window.editorShell.editorSelection;
|
|
if (selection)
|
|
{
|
|
var focusN = selection.focusNode;
|
|
if (focusN.nodeName.toLowerCase == "td")
|
|
return focusN
|
|
else
|
|
{
|
|
oneup = focusN.parentNode;
|
|
return oneup;
|
|
}
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
|
|
function getColor(ColorPickerID)
|
|
{
|
|
var colorPicker = document.getElementById(ColorPickerID);
|
|
var color;
|
|
if (colorPicker)
|
|
{
|
|
// Extract color from colorPicker and assign to colorWell.
|
|
color = colorPicker.getAttribute("color");
|
|
if (color && color == "")
|
|
return null;
|
|
// Clear color so next if it's called again before
|
|
// color picker is actually used, we dedect the "don't set color" state
|
|
colorPicker.setAttribute("color","");
|
|
}
|
|
|
|
return color;
|
|
}
|
|
|
|
function setColorWell(ColorWellID, color)
|
|
{
|
|
var colorWell = document.getElementById(ColorWellID);
|
|
if (colorWell)
|
|
{
|
|
if (!color || color == "")
|
|
{
|
|
// Don't set color (use default)
|
|
// Trigger change to not show color swatch
|
|
colorWell.setAttribute("default","true");
|
|
// Style in CSS sets "background-color",
|
|
// but color won't clear unless we do this:
|
|
colorWell.removeAttribute("style");
|
|
}
|
|
else
|
|
{
|
|
colorWell.removeAttribute("default");
|
|
// Use setAttribute so colorwell can be a XUL element, such as button
|
|
colorWell.setAttribute("style", "background-color:"+color);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getColorAndSetColorWell(ColorPickerID, ColorWellID)
|
|
{
|
|
var color = getColor(ColorPickerID);
|
|
setColorWell(ColorWellID, color);
|
|
return color;
|
|
}
|
|
|
|
// Test for valid image by sniffing out the extension
|
|
function IsValidImage(imageName)
|
|
{
|
|
var image = TrimString(imageName);
|
|
if ( !image )
|
|
return false;
|
|
|
|
/* look for an extension */
|
|
var tailindex = image.lastIndexOf(".");
|
|
if ( tailindex == 0 || tailindex == -1 ) /* -1 is not found */
|
|
return false;
|
|
|
|
/* move past period, get the substring from the first character after the '.' to the last character (length) */
|
|
tailindex = tailindex + 1;
|
|
var type = image.substring(tailindex,image.length);
|
|
|
|
/* convert extension to lower case */
|
|
if (type)
|
|
type = type.toLowerCase();
|
|
|
|
// TODO: Will we convert .BMPs to a web format?
|
|
switch( type )
|
|
{
|
|
case "gif":
|
|
case "jpg":
|
|
case "jpeg":
|
|
case "png":
|
|
return true;
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function InitMoreFewer()
|
|
{
|
|
// Set SeeMore bool to the OPPOSITE of the current state,
|
|
// which is automatically saved by using the 'persist="more"'
|
|
// attribute on the gDialog.MoreFewerButton button
|
|
// onMoreFewer will toggle it and redraw the dialog
|
|
SeeMore = (gDialog.MoreFewerButton.getAttribute("more") != "1");
|
|
onMoreFewer();
|
|
}
|
|
|
|
function onMoreFewer()
|
|
{
|
|
if (SeeMore)
|
|
{
|
|
gDialog.MoreSection.setAttribute("collapsed","true");
|
|
window.sizeToContent();
|
|
gDialog.MoreFewerButton.setAttribute("more","0");
|
|
gDialog.MoreFewerButton.setAttribute("label",GetString("MoreProperties"));
|
|
SeeMore = false;
|
|
}
|
|
else
|
|
{
|
|
gDialog.MoreSection.removeAttribute("collapsed");
|
|
window.sizeToContent();
|
|
gDialog.MoreFewerButton.setAttribute("more","1");
|
|
gDialog.MoreFewerButton.setAttribute("label",GetString("FewerProperties"));
|
|
SeeMore = true;
|
|
}
|
|
}
|
|
|
|
function SwitchToValidatePanel()
|
|
{
|
|
// no default implementation
|
|
// Only EdTableProps.js currently implements this
|
|
}
|
|
|
|
function GetPrefs()
|
|
{
|
|
try {
|
|
var prefService = Components.classes["@mozilla.org/preferences-service;1"]
|
|
.getService(Components.interfaces.nsIPrefService);
|
|
var prefs = prefService.getBranch(null);
|
|
if (prefs)
|
|
return prefs;
|
|
else
|
|
dump("failed to get prefs service!\n");
|
|
|
|
}
|
|
catch(ex)
|
|
{
|
|
dump("failed to get prefs service!\n");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
|
|
|
function GetLocalFileURL(filterType)
|
|
{
|
|
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
|
|
|
|
if (filterType == "img")
|
|
{
|
|
fp.init(window, GetString("SelectImageFile"), nsIFilePicker.modeOpen);
|
|
fp.appendFilters(nsIFilePicker.filterImages);
|
|
}
|
|
// Current usage of this is in Link dialog,
|
|
// where we always want HTML first
|
|
else if (filterType.indexOf("html") == 0)
|
|
{
|
|
fp.init(window, GetString("OpenHTMLFile"), nsIFilePicker.modeOpen);
|
|
|
|
// When loading into Composer, direct user to prefer HTML files and text files,
|
|
// so we call separately to control the order of the filter list
|
|
fp.appendFilters(nsIFilePicker.filterHTML);
|
|
fp.appendFilters(nsIFilePicker.filterText);
|
|
|
|
// Link dialog also allows linking to images
|
|
if (filterType.indexOf("img") > 0)
|
|
fp.appendFilters(nsIFilePicker.filterImages);
|
|
|
|
}
|
|
// Default or last filter is "All Files"
|
|
fp.appendFilters(nsIFilePicker.filterAll);
|
|
|
|
/* doesn't handle *.shtml files */
|
|
try {
|
|
var ret = fp.show();
|
|
if (ret == nsIFilePicker.returnCancel)
|
|
return null;
|
|
}
|
|
catch (ex) {
|
|
dump("filePicker.chooseInputFile threw an exception\n");
|
|
return null;
|
|
}
|
|
|
|
return fp.fileURL.spec;
|
|
}
|
|
|
|
function GetMetaElement(name)
|
|
{
|
|
if (name)
|
|
{
|
|
name = name.toLowerCase();
|
|
if (name != "")
|
|
{
|
|
var metaNodes = editorShell.editorDocument.getElementsByTagName("meta");
|
|
if (metaNodes && metaNodes.length > 0)
|
|
{
|
|
for (var i = 0; i < metaNodes.length; i++)
|
|
{
|
|
var metaNode = metaNodes.item(i);
|
|
if (metaNode && metaNode.getAttribute("name") == name)
|
|
return metaNode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function CreateMetaElement(name)
|
|
{
|
|
var metaElement = editorShell.CreateElementWithDefaults("meta");
|
|
if (metaElement)
|
|
metaElement.setAttribute("name", name);
|
|
else
|
|
dump("Failed to create metaElement!\n");
|
|
|
|
return metaElement;
|
|
}
|
|
|
|
function GetHTTPEquivMetaElement(name)
|
|
{
|
|
if (name)
|
|
{
|
|
name = name.toLowerCase();
|
|
if (name != "")
|
|
{
|
|
var metaNodes = editorShell.editorDocument.getElementsByTagName("meta");
|
|
if (metaNodes && metaNodes.length > 0)
|
|
{
|
|
for (var i = 0; i < metaNodes.length; i++)
|
|
{
|
|
var metaNode = metaNodes.item(i);
|
|
if (metaNode)
|
|
{
|
|
var httpEquiv = metaNode.getAttribute("http-equiv");
|
|
if (httpEquiv && httpEquiv.toLowerCase() == name)
|
|
return metaNode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function CreateHTTPEquivMetaElement(name)
|
|
{
|
|
metaElement = editorShell.CreateElementWithDefaults("meta");
|
|
if (metaElement)
|
|
metaElement.setAttribute("http-equiv", name);
|
|
else
|
|
dump("Failed to create httpequivMetaElement!\n");
|
|
|
|
return metaElement;
|
|
}
|
|
|
|
function CreateHTTPEquivElement(name)
|
|
{
|
|
metaElement = editorShell.CreateElementWithDefaults("meta");
|
|
if (metaElement)
|
|
metaElement.setAttribute("http-equiv", name);
|
|
else
|
|
dump("Failed to create metaElement for http-equiv!\n");
|
|
|
|
return metaElement;
|
|
}
|
|
|
|
// Change "content" attribute on a META element,
|
|
// or delete entire element it if content is empty
|
|
// This uses undoable editor transactions
|
|
function SetMetaElementContent(metaElement, content, insertNew)
|
|
{
|
|
if (metaElement)
|
|
{
|
|
if(!content || content == "")
|
|
{
|
|
if (!insertNew)
|
|
editorShell.DeleteElement(metaElement);
|
|
}
|
|
else
|
|
{
|
|
if (insertNew)
|
|
{
|
|
metaElement.setAttribute("content", content);
|
|
AppendHeadElement(metaElement);
|
|
}
|
|
else
|
|
editorShell.SetAttribute(metaElement, "content", content);
|
|
}
|
|
}
|
|
}
|
|
|
|
function GetHeadElement()
|
|
{
|
|
var headList = editorShell.editorDocument.getElementsByTagName("head");
|
|
if (headList)
|
|
return headList.item(0);
|
|
|
|
return null;
|
|
}
|
|
|
|
function AppendHeadElement(element)
|
|
{
|
|
var head = GetHeadElement();
|
|
if (head)
|
|
{
|
|
var position = 0;
|
|
if (head.hasChildNodes())
|
|
position = head.childNodes.length;
|
|
|
|
// Use editor's undoable transaction
|
|
// Last param "true" says "don't change the selection"
|
|
editorShell.InsertElement(element, head, position, true);
|
|
}
|
|
}
|
|
|
|
function SetWindowLocation()
|
|
{
|
|
gLocation = document.getElementById("location");
|
|
if (gLocation)
|
|
{
|
|
window.screenX = Math.max(0, Math.min(window.opener.screenX + Number(gLocation.getAttribute("offsetX")),
|
|
screen.availWidth - window.outerWidth));
|
|
window.screenY = Math.max(0, Math.min(window.opener.screenY + Number(gLocation.getAttribute("offsetY")),
|
|
screen.availHeight - window.outerHeight));
|
|
}
|
|
}
|
|
|
|
function SaveWindowLocation()
|
|
{
|
|
if (gLocation)
|
|
{
|
|
var newOffsetX = window.screenX - window.opener.screenX;
|
|
var newOffsetY = window.screenY - window.opener.screenY;
|
|
gLocation.setAttribute("offsetX", window.screenX - window.opener.screenX);
|
|
gLocation.setAttribute("offsetY", window.screenY - window.opener.screenY);
|
|
}
|
|
}
|
|
|
|
function onCancel()
|
|
{
|
|
SaveWindowLocation();
|
|
// Close dialog by returning true
|
|
return true;
|
|
}
|
|
|
|
function GetDefaultBrowserColors()
|
|
{
|
|
var prefs = GetPrefs();
|
|
var colors = new Object();
|
|
var useSysColors = false;
|
|
colors.TextColor = 0;
|
|
colors.BackgroundColor = 0;
|
|
try { useSysColors = prefs.getBoolPref("browser.display.use_system_colors"); } catch (e) {}
|
|
|
|
if (!useSysColors)
|
|
{
|
|
try { colors.TextColor = prefs.getCharPref("browser.display.foreground_color"); } catch (e) {}
|
|
|
|
try { colors.BackgroundColor = prefs.getCharPref("browser.display.background_color"); } catch (e) {}
|
|
}
|
|
// Use OS colors for text and background if explicitly asked or pref is not set
|
|
if (!colors.TextColor)
|
|
colors.TextColor = "windowtext";
|
|
|
|
if (!colors.BackgroundColor)
|
|
colors.BackgroundColor = "window";
|
|
|
|
colors.LinkColor = prefs.getCharPref("browser.anchor_color");
|
|
colors.VisitedLinkColor = prefs.getCharPref("browser.visited_color");
|
|
|
|
return colors;
|
|
}
|
|
|
|
function TextIsURI(selectedText)
|
|
{
|
|
if (selectedText)
|
|
{
|
|
var text = selectedText.toLowerCase();
|
|
return text.match(/^http:\/\/|^https:\/\/|^file:\/\/|^ftp:\/\/|\
|
|
^about:|^mailto:|^news:|^snews:|^telnet:|\
|
|
^ldap:|^ldaps:|^gopher:|^finger:|^javascript:/);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function SetRelativeCheckbox()
|
|
{
|
|
var checkbox = document.getElementById("MakeRelativeCheckbox");
|
|
if (!checkbox)
|
|
return;
|
|
|
|
// Mail never allows relative URLs, so hide the checkbox
|
|
if (editorShell.editorType == "htmlmail")
|
|
{
|
|
checkbox.setAttribute("collapsed", "true");
|
|
return;
|
|
}
|
|
|
|
var input = document.getElementById(checkbox.getAttribute("for"));
|
|
if (!input)
|
|
return;
|
|
|
|
var url = TrimString(input.value);
|
|
var urlScheme = GetScheme(url);
|
|
|
|
// Check it if url is relative (no scheme).
|
|
checkbox.checked = url.length > 0 && !urlScheme;
|
|
|
|
// Now do checkbox enabling:
|
|
var enable = false;
|
|
|
|
var docUrl = GetDocumentBaseUrl();
|
|
var docScheme = GetScheme(docUrl);
|
|
|
|
if (url && docUrl && docScheme)
|
|
{
|
|
if (urlScheme)
|
|
{
|
|
// Url is absolute
|
|
// If we can make a relative URL, then enable must be true!
|
|
// (this lets the smarts of MakeRelativeUrl do all the hard work)
|
|
enable = (GetScheme(MakeRelativeUrl(url)).length == 0);
|
|
}
|
|
else
|
|
{
|
|
// Url is relative
|
|
// Check if url is a named anchor
|
|
// but document doesn't have a filename
|
|
// (it's probably "index.html" or "index.htm",
|
|
// but we don't want to allow a malformed URL)
|
|
if (url[0] == "#")
|
|
{
|
|
var docFilename = GetFilename(docUrl);
|
|
enable = docFilename.length > 0;
|
|
}
|
|
else
|
|
{
|
|
// Any other url is assumed
|
|
// to be ok to try to make absolute
|
|
enable = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
SetElementEnabledById("MakeRelativeCheckbox", enable);
|
|
}
|
|
|
|
// oncommand handler for the Relativize checkbox in EditorOverlay.xul
|
|
function MakeInputValueRelativeOrAbsolute()
|
|
{
|
|
var checkbox = document.getElementById("MakeRelativeCheckbox");
|
|
if (!checkbox)
|
|
return;
|
|
|
|
var input = document.getElementById(checkbox.getAttribute("for"));
|
|
if (!input)
|
|
return;
|
|
|
|
var docUrl = GetDocumentBaseUrl();
|
|
if (!docUrl)
|
|
{
|
|
// Checkbox should be disabled if not saved,
|
|
// but keep this error message in case we change that
|
|
AlertWithTitle("", GetString("SaveToUseRelativeUrl"));
|
|
window.focus();
|
|
}
|
|
else
|
|
{
|
|
// Note that "checked" is opposite of its last state,
|
|
// which determines what we want to do here
|
|
if (checkbox.checked)
|
|
input.value = MakeRelativeUrl(input.value);
|
|
else
|
|
input.value = MakeAbsoluteUrl(input.value);
|
|
|
|
// Reset checkbox to reflect url state
|
|
SetRelativeCheckbox(checkbox, input.value);
|
|
}
|
|
}
|
|
|
|
function MakeRelativeUrl(url)
|
|
{
|
|
var inputUrl = TrimString(url);
|
|
if (!inputUrl)
|
|
return inputUrl;
|
|
|
|
// Get the filespec relative to current document's location
|
|
// NOTE: Can't do this if file isn't saved yet!
|
|
var docUrl = GetDocumentBaseUrl();
|
|
var docScheme = GetScheme(docUrl);
|
|
|
|
// Can't relativize if no doc scheme (page hasn't been saved)
|
|
if (!docScheme)
|
|
return inputUrl;
|
|
|
|
var urlScheme = GetScheme(inputUrl);
|
|
|
|
// Do nothing if not the same scheme or url is already relativized
|
|
if (docScheme != urlScheme)
|
|
return inputUrl;
|
|
|
|
var IOService = GetIOService();
|
|
if (!IOService)
|
|
return inputUrl;
|
|
|
|
// Host must be the same
|
|
var docHost = GetHost(docUrl);
|
|
var urlHost = GetHost(inputUrl);
|
|
if (docHost != urlHost)
|
|
return inputUrl;
|
|
|
|
// Get just the file path part of the urls
|
|
var docPath = IOService.extractUrlPart(docUrl, IOService.url_Path, {start:0}, {end:0});
|
|
var urlPath = IOService.extractUrlPart(inputUrl, IOService.url_Path, {start:0}, {end:0});
|
|
|
|
// We only return "urlPath", so we can convert
|
|
// the entire docPath for case-insensitive comparisons
|
|
var os = GetOS();
|
|
var doCaseInsensitive = (docScheme.toLowerCase() == "file" && os == gWin);
|
|
if (doCaseInsensitive)
|
|
docPath = docPath.toLowerCase();
|
|
|
|
// Get document filename before we start chopping up the docPath
|
|
var docFilename = GetFilename(docPath);
|
|
|
|
// Both url and doc paths now begin with "/"
|
|
// Look for shared dirs starting after that
|
|
urlPath = urlPath.slice(1);
|
|
docPath = docPath.slice(1);
|
|
|
|
var firstDirTest = true;
|
|
var nextDocSlash = 0;
|
|
var done = false;
|
|
|
|
// Remove all matching subdirs common to both doc and input urls
|
|
do {
|
|
nextDocSlash = docPath.indexOf("\/");
|
|
var nextUrlSlash = urlPath.indexOf("\/");
|
|
|
|
if (nextUrlSlash == -1)
|
|
{
|
|
// We're done matching and all dirs in url
|
|
// what's left is the filename
|
|
done = true;
|
|
|
|
// Remove filename for named anchors in the same file
|
|
if (nextDocSlash == -1 && docFilename)
|
|
{
|
|
var anchorIndex = urlPath.indexOf("#");
|
|
if (anchorIndex > 0)
|
|
{
|
|
var urlFilename = doCaseInsensitive ? urlPath.toLowerCase() : urlPath;
|
|
|
|
if (urlFilename.indexOf(docFilename) == 0)
|
|
urlPath = urlPath.slice(anchorIndex);
|
|
}
|
|
}
|
|
}
|
|
else if (nextDocSlash >= 0)
|
|
{
|
|
// Test for matching subdir
|
|
var docDir = docPath.slice(0, nextDocSlash);
|
|
var urlDir = urlPath.slice(0, nextUrlSlash);
|
|
if (doCaseInsensitive)
|
|
urlDir = urlDir.toLowerCase();
|
|
|
|
if (urlDir == docDir)
|
|
{
|
|
|
|
// Remove matching dir+"/" from each path
|
|
// and continue to next dir
|
|
docPath = docPath.slice(nextDocSlash+1);
|
|
urlPath = urlPath.slice(nextUrlSlash+1);
|
|
}
|
|
else
|
|
{
|
|
// No match, we're done
|
|
done = true;
|
|
|
|
// Be sure we are on the same local drive or volume
|
|
// (the first "dir" in the path) because we can't
|
|
// relativize to different drives/volumes.
|
|
// UNIX doesn't have volumes, so we must not do this else
|
|
// the first directory will be misinterpreted as a volume name
|
|
if (firstDirTest && docScheme == "file" && os != gUNIX)
|
|
return inputUrl;
|
|
}
|
|
}
|
|
else // No more doc dirs left, we're done
|
|
done = true;
|
|
|
|
firstDirTest = false;
|
|
}
|
|
while (!done);
|
|
|
|
// Add "../" for each dir left in docPath
|
|
while (nextDocSlash > 0)
|
|
{
|
|
urlPath = "../" + urlPath;
|
|
nextDocSlash = docPath.indexOf("\/", nextDocSlash+1);
|
|
}
|
|
return urlPath;
|
|
}
|
|
|
|
function MakeAbsoluteUrl(url)
|
|
{
|
|
var resultUrl = TrimString(url);
|
|
if (!resultUrl)
|
|
return resultUrl;
|
|
|
|
// Check if URL is already absolute, i.e., it has a scheme
|
|
var urlScheme = GetScheme(resultUrl);
|
|
|
|
if (urlScheme)
|
|
return resultUrl;
|
|
|
|
var docUrl = GetDocumentBaseUrl();
|
|
var docScheme = GetScheme(docUrl);
|
|
|
|
// Can't relativize if no doc scheme (page hasn't been saved)
|
|
if (!docScheme)
|
|
return resultUrl;
|
|
|
|
var IOService = GetIOService();
|
|
if (!IOService)
|
|
return resultUrl;
|
|
|
|
// Make a URI object to use its "resolve" method
|
|
var absoluteUrl = resultUrl;
|
|
var docUri = IOService.newURI(docUrl, null);
|
|
|
|
try {
|
|
absoluteUrl = docUri.resolve(resultUrl);
|
|
// This is deprecated and buggy!
|
|
// If used, we must make it a path for the parent directory (remove filename)
|
|
//absoluteUrl = IOService.resolveRelativePath(resultUrl, docUrl);
|
|
} catch (e) {}
|
|
|
|
return absoluteUrl;
|
|
}
|
|
|
|
// Get the HREF of the page's <base> tag or the document location
|
|
// returns empty string if no base href and document hasn't been saved yet
|
|
function GetDocumentBaseUrl()
|
|
{
|
|
if (window.editorShell)
|
|
{
|
|
var docUrl;
|
|
|
|
// if document supplies a <base> tag, use that URL instead
|
|
var baseList = editorShell.editorDocument.getElementsByTagName("base");
|
|
if (baseList)
|
|
{
|
|
var base = baseList.item(0);
|
|
if (base)
|
|
docUrl = base.getAttribute("href");
|
|
}
|
|
if (!docUrl)
|
|
docUrl = editorShell.editorDocument.location.href;
|
|
|
|
if (docUrl != "about:blank")
|
|
return docUrl;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function GetIOService()
|
|
{
|
|
if (gIOService)
|
|
return gIOService;
|
|
|
|
var CID = Components.classes["@mozilla.org/network/io-service;1"];
|
|
gIOService = CID.getService(Components.interfaces.nsIIOService);
|
|
return gIOService;
|
|
}
|
|
|
|
// Extract the scheme (e.g., 'file', 'http') from a URL string
|
|
function GetScheme(url)
|
|
{
|
|
var resultUrl = TrimString(url);
|
|
// Unsaved document URL has no acceptable scheme yet
|
|
if (!resultUrl || resultUrl == "about:blank")
|
|
return "";
|
|
|
|
var IOService = GetIOService();
|
|
if (!IOService)
|
|
return "";
|
|
|
|
var scheme = "";
|
|
try {
|
|
// This fails if there's no scheme
|
|
scheme = IOService.extractScheme(resultUrl, {schemeStartPos:0}, {schemeEndPos:0});
|
|
} catch (e) {}
|
|
|
|
return scheme ? scheme : "";
|
|
}
|
|
|
|
function GetHost(url)
|
|
{
|
|
var IOService = GetIOService();
|
|
if (!IOService)
|
|
return "";
|
|
|
|
var host = "";
|
|
if (url)
|
|
{
|
|
try {
|
|
host = IOService.extractUrlPart(url, IOService.url_Host, {start:0}, {end:0});
|
|
} catch (e) {}
|
|
}
|
|
return host;
|
|
}
|
|
|
|
function GetFilename(url)
|
|
{
|
|
var IOService = GetIOService();
|
|
if (!IOService)
|
|
return "";
|
|
|
|
var filename;
|
|
|
|
if (url)
|
|
{
|
|
try {
|
|
filename = IOService.extractUrlPart(url, IOService.url_FileBaseName, {start:0}, {end:0});
|
|
if (filename)
|
|
{
|
|
var ext = IOService.extractUrlPart(url, IOService.url_FileExtension, {start:0}, {end:0});
|
|
if (ext)
|
|
filename += "."+ext;
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
return filename ? filename : "";
|
|
}
|
|
|
|
function GetOS()
|
|
{
|
|
if (gOS)
|
|
return gOS;
|
|
|
|
if (navigator.platform.toLowerCase().indexOf("win") >= 0)
|
|
gOS = gWin;
|
|
else if (navigator.platform.toLowerCase().indexOf("mac") >=0)
|
|
gOS = gMac;
|
|
else
|
|
gOS = gUNIX;
|
|
|
|
return gOS;
|
|
}
|