home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 January / 01_02.iso / software / netscape62win / browser.xpi / bin / chrome / comm.jar / content / editor / EdDialogCommon.js < prev    next >
Encoding:
JavaScript  |  2001-09-24  |  42.0 KB  |  1,539 lines

  1. /*
  2.  * The contents of this file are subject to the Netscape Public
  3.  * License Version 1.1 (the "License"); you may not use this file
  4.  * except in compliance with the License. You may obtain a copy of
  5.  * the License at http://www.mozilla.org/NPL/
  6.  *
  7.  * Software distributed under the License is distributed on an "AS
  8.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9.  * implied. See the License for the specific language governing
  10.  * rights and limitations under the License.
  11.  *
  12.  * The Original Code is Mozilla Communicator client code, released
  13.  * March 31, 1998.
  14.  *
  15.  * The Initial Developer of the Original Code is Netscape
  16.  * Communications Corporation. Portions created by Netscape are
  17.  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
  18.  * Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Pete Collins
  22.  *   Brian King
  23.  */
  24. /*
  25.   if we ever need to use a different string bundle, use srGetStrBundle
  26.   by including
  27.   <script type="application/x-javascript" src="chrome://global/content/strres.js"/>
  28.   e.g.:
  29.   var bundle = srGetStrBundle("chrome://global/locale/filepicker.properties");
  30. */
  31.  
  32. // Each editor window must include this file
  33. // Variables  shared by all dialogs:
  34. var editorShell;
  35. // Bummer! Can't get at enums from nsIDocumentEncoder.h
  36. var gOutputSelectionOnly = 1;
  37. var gOutputFormatted     = 2;
  38. var gOutputNoDoctype     = 4;
  39. var gOutputBodyOnly      = 8;
  40. var gOutputPreformatted  = 16;
  41. var gOutputWrap          = 32;
  42. var gOutputFormatFlowed  = 64;
  43. var gOutputAbsoluteLinks = 128;
  44. var gOutputEncodeEntities = 256;
  45. var gStringBundle;
  46. var gValidationError = false;
  47. var gIOService;
  48. var gOS = "";
  49. const gWin = "Win";
  50. const gUNIX = "UNIX";
  51. const gMac = "Mac";
  52.  
  53. // Use for 'defaultIndex' param in InitPixelOrPercentMenulist
  54. var gPixel = 0;
  55. var gPercent = 1;
  56.  
  57. var maxPixels  = 10000;
  58. // For dialogs that expand in size. Default is smaller size see "onMoreFewer()" below
  59. var SeeMore = false;
  60.  
  61. // A XUL element with id="location" for managing
  62. // dialog location relative to parent window
  63. var gLocation;
  64.  
  65. // The element being edited - so AdvancedEdit can have access to it
  66. var globalElement;
  67.  
  68. function InitEditorShell()
  69. {
  70.     // get the editor shell from the parent window
  71.  
  72.   editorShell = window.opener.editorShell;
  73.   if (editorShell) {
  74.     editorShell = editorShell.QueryInterface(Components.interfaces.nsIEditorShell);
  75.   }
  76.   if (!editorShell) {
  77.     dump("EditorShell not found!!!\n");
  78.     window.close();
  79.     return false;
  80.   }
  81.  
  82.   // Save as a property of the window so it can be used by child dialogs
  83.  
  84.   window.editorShell = editorShell;
  85.  
  86.   return true;
  87. }
  88.  
  89. function StringExists(string)
  90. {
  91.   if (string != null && string != "undefined" && string.length > 0)
  92.     return true;
  93.  
  94.   return false;
  95. }
  96.  
  97. /* Validate contents of an input field 
  98.  *
  99.  *  inputWidget    The 'textbox' XUL element for text input of the attribute's value
  100.  *  listWidget     The 'menulist' XUL element for choosing "pixel" or "percent"
  101.  *                  May be null when no pixel/percent is used.
  102.  *  minVal         minimum allowed for input widget's value
  103.  *  maxVal         maximum allowed for input widget's value
  104.  *                 (when "listWidget" is used, maxVal is used for "pixel" maximum,
  105.  *                  100% is assumed if "percent" is the user's choice)
  106.  *  element        The DOM element that we set the attribute on. May be null.
  107.  *  attName        Name of the attribute to set.  May be null or ignored if "element" is null
  108.  *  mustHaveValue  If true, error dialog is displayed if "value" is empty string
  109.  *
  110.  *  This calls "ValidateNumberRange()", which puts up an error dialog to inform the user. 
  111.  *    If error, we also: 
  112.  *      Shift focus and select contents of the inputWidget,
  113.  *      Switch to appropriate panel of tabbed dialog if user implements "SwitchToValidate()",
  114.  *      and/or will expand the dialog to full size if "More / Fewer" feature is implemented
  115.  *
  116.  *  Returns the "value" as a string, or "" if error or input contents are empty
  117.  *  The global "gValidationError" variable is set true if error was found
  118.  */
  119. function ValidateNumber(inputWidget, listWidget, minVal, maxVal, element, attName, mustHaveValue, mustShowMoreSection)
  120. {
  121.   if (!inputWidget)
  122.   {
  123.     gValidationError = true;
  124.     return "";
  125.   }
  126.  
  127.   // Global error return value
  128.   gValidationError = false;
  129.   var maxLimit = maxVal;
  130.   var isPercent = false;
  131.  
  132.   var numString = inputWidget.value.trimString();
  133.   if (numString)
  134.   {
  135.     if (listWidget)
  136.       isPercent = (listWidget.selectedIndex == 1);
  137.     if (isPercent)
  138.       maxLimit = 100;
  139.  
  140.     // This method puts up the error message
  141.     numString = ValidateNumberRange(numString, minVal, maxLimit, mustHaveValue);
  142.     if(!numString)
  143.     {
  144.       // Switch to appropriate panel for error reporting
  145.       SwitchToValidatePanel();
  146.  
  147.       // or expand dialog for users of "More / Fewer" button
  148.       if ("dialog" in window && dialog && 
  149.            "MoreSection" in dialog && dialog.MoreSection)
  150.       {
  151.         if ( !SeeMore )
  152.           onMoreFewer();
  153.       }
  154.  
  155.       // Error - shift to offending input widget
  156.       SetTextboxFocus(inputWidget);
  157.       gValidationError = true;
  158.     }
  159.     else
  160.     {
  161.       if (isPercent)
  162.         numString += "%";
  163.       if (element)
  164.         element.setAttribute(attName, numString);
  165.     }
  166.   } else if (element) {
  167.     element.removeAttribute(attName);
  168.   }
  169.   return numString;
  170. }
  171.  
  172. /* Validate contents of an input field 
  173.  *
  174.  *  value          number to validate
  175.  *  minVal         minimum allowed for input widget's value
  176.  *  maxVal         maximum allowed for input widget's value
  177.  *                 (when "listWidget" is used, maxVal is used for "pixel" maximum,
  178.  *                  100% is assumed if "percent" is the user's choice)
  179.  *  mustHaveValue  If true, error dialog is displayed if "value" is empty string
  180.  *
  181.  *  If inputWidget's value is outside of range, or is empty when "mustHaveValue" = true,
  182.  *      an error dialog is popuped up to inform the user. The focus is shifted
  183.  *      to the inputWidget.
  184.  *
  185.  *  Returns the "value" as a string, or "" if error or input contents are empty
  186.  *  The global "gValidationError" variable is set true if error was found
  187.  */
  188. function ValidateNumberRange(value, minValue, maxValue, mustHaveValue)
  189. {
  190.   // Initialize global error flag
  191.   gValidationError = false;
  192.   value = TrimString(String(value));
  193.  
  194.   // We don't show error for empty string unless caller wants to
  195.   if (!value && !mustHaveValue)
  196.     return "";
  197.  
  198.   var numberStr = "";
  199.  
  200.   if (value.length > 0)
  201.   {
  202.     // Extract just numeric characters
  203.     var number = Number(value.replace(/\D+/g, ""));
  204.     if (number >= minValue && number <= maxValue )
  205.     {
  206.       // Return string version of the number
  207.       return String(number);
  208.     }
  209.     numberStr = String(number);
  210.   }
  211.  
  212.   var message = "";
  213.  
  214.   if (numberStr.length > 0)
  215.   {
  216.     // We have a number from user outside of allowed range
  217.     message = editorShell.GetString( "ValidateRangeMsg");
  218.     message = message.replace(/%n%/, numberStr);
  219.     message += "\n ";
  220.   }
  221.   message += editorShell.GetString( "ValidateNumberMsg");
  222.  
  223.   // Replace variable placeholders in message with number values
  224.   message = message.replace(/%min%/, minValue).replace(/%max%/, maxValue);
  225.   ShowInputErrorMessage(message);
  226.  
  227.   // Return an empty string to indicate error
  228.   gValidationError = true;
  229.   return "";
  230. }
  231.  
  232. function SetTextboxFocusById(id)
  233. {
  234.   SetTextboxFocus(document.getElementById(id));
  235. }
  236.  
  237. function SetTextboxFocus(textbox)
  238. {
  239.   if (textbox)
  240.   {
  241.     // Select entire contents
  242.     if (textbox.value.length > 0)
  243.       textbox.select();
  244.     else
  245.       textbox.focus();
  246.   }
  247. }
  248.  
  249. function ShowInputErrorMessage(message)
  250. {
  251.   AlertWithTitle(GetString("InputError"), message);
  252.   window.focus();
  253. }
  254.  
  255. function AlertWithTitle(title, message)
  256. {
  257.   var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
  258.   promptService = promptService.QueryInterface(Components.interfaces.nsIPromptService);
  259.  
  260.   if (promptService)
  261.   {
  262.     if (!title)
  263.       title = GetString("Alert");
  264.  
  265.     // "window" is the calling dialog window
  266.     promptService.alert(window, title, message);
  267.   }
  268. }
  269.  
  270. // Optional: Caller may supply text to substitue for "Ok" and/or "Cancel"
  271. function ConfirmWithTitle(title, message, okButtonText, cancelButtonText)
  272. {
  273.   var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
  274.   promptService = promptService.QueryInterface(Components.interfaces.nsIPromptService);
  275.  
  276.   if (promptService)
  277.   {
  278.     var result = {value:0};
  279.     var okFlag = okButtonText ? promptService.BUTTON_TITLE_IS_STRING : promptService.BUTTON_TITLE_OK;
  280.     var cancelFlag = cancelButtonText ? promptService.BUTTON_TITLE_IS_STRING : promptService.BUTTON_TITLE_CANCEL;
  281.  
  282.     promptService.confirmEx(window, title, message,
  283.                             (okFlag * promptService.BUTTON_POS_0) +
  284.                             (cancelFlag * promptService.BUTTON_POS_1),
  285.                             okButtonText, cancelButtonText, null, null, {value:0}, result);
  286.     return (result.value == 0);      
  287.   }
  288. }
  289.  
  290. function GetString(name)
  291. {
  292.   if (editorShell)
  293.   {
  294.     return editorShell.GetString(name);
  295.   }
  296.   else
  297.   {
  298.     // Non-editors (like prefs) may use these methods
  299.     if (!gStringBundle)
  300.     {
  301.       gStringBundle = srGetStrBundle("chrome://editor/locale/editor.properties");
  302.       if (!gStringBundle)
  303.         return null;
  304.     }
  305.     return gStringBundle.GetStringFromName(name);
  306.   }
  307.   return null;
  308. }
  309.  
  310. function TrimStringLeft(string)
  311. {
  312.   if(!string) return "";
  313.   return string.replace(/^\s+/, "");
  314. }
  315.  
  316. function TrimStringRight(string)
  317. {
  318.   if (!string) return "";
  319.   return string.replace(/\s+$/, '');
  320. }
  321.  
  322. // Remove whitespace from both ends of a string
  323. function TrimString(string)
  324. {
  325.   if (!string) return "";
  326.   return string.replace(/(^\s+)|(\s+$)/g, '')
  327. }
  328.  
  329. String.prototype.trimString = function() {
  330.   return this.replace(/(^\s+)|(\s+$)/g, '')
  331. }
  332.  
  333. function IsWhitespace(string)
  334. {
  335.   return /^\s/.test(string);
  336. }
  337.  
  338. function TruncateStringAtWordEnd(string, maxLength, addEllipses)
  339. {
  340.     // Return empty if string is null, undefined, or the empty string
  341.     if (!string)
  342.       return "";
  343.  
  344.     // We assume they probably don't want whitespace at the beginning
  345.     string = string.replace(/^\s+/, '');
  346.     if (string.length <= maxLength)
  347.       return string;
  348.  
  349.     // We need to truncate the string to maxLength or fewer chars
  350.     if (addEllipses)
  351.       maxLength -= 3;
  352.     string = string.replace(RegExp("(.{0," + maxLength + "})\\s.*"), "$1")
  353.  
  354.     if (string.length > maxLength)
  355.       string = string.slice(0, maxLength);
  356.  
  357.     if (addEllipses)
  358.       string += "...";
  359.     return string;
  360. }
  361.  
  362. // Replace all whitespace characters with supplied character
  363. // E.g.: Use charReplace = " ", to "unwrap" the string by removing line-end chars
  364. //       Use charReplace = "_" when you don't want spaces (like in a URL)
  365. function ReplaceWhitespace(string, charReplace) {
  366.   return string.replace(/(^\s+)|(\s+$)/g,'').replace(/\s+/g,charReplace)
  367. }
  368.  
  369. // Replace whitespace with "_" and allow only HTML CDATA
  370. //   characters: "a"-"z","A"-"Z","0"-"9", "_", ":", "-", ".",
  371. //   and characters above ASCII 127
  372. function ConvertToCDATAString(string)
  373. {
  374.   return string.replace(/\s+/g,"_").replace(/[^a-zA-Z0-9_\.\-\:\u0080-\uFFFF]+/g,'');
  375. }
  376.  
  377. // this function takes an elementID and a flag
  378. // if the element can be found by ID, then it is either enabled (by removing "disabled" attr)
  379. // or disabled (setAttribute) as specified in the "doEnable" parameter
  380. function SetElementEnabledById( elementID, doEnable )
  381. {
  382.   var element = document.getElementById(elementID);
  383.   if ( element )
  384.   {
  385.     if ( doEnable )
  386.     {
  387.       element.removeAttribute( "disabled" );
  388.     }
  389.     else
  390.     {
  391.       element.setAttribute( "disabled", "true" );
  392.     }
  393.   }
  394.   else
  395.   {
  396.     dump("Element "+elementID+" not found in SetElementEnabledById\n");
  397.   }
  398. }
  399.  
  400. // This function relies on css classes for enabling and disabling
  401. // This function takes an ID for a label and a flag
  402. // if an element can be found by its ID, then it is either enabled or disabled
  403. // The class is set to either "enabled" or "disabled" depending on the flag passed in.
  404. // This function relies on css having a special appearance for these two classes.
  405.  
  406. function SetClassEnabledById( elementID, doEnable )
  407. {
  408.   element = document.getElementById(elementID);
  409.   if ( element )
  410.   {
  411.     if ( doEnable )
  412.     {
  413.      element.setAttribute( "class", "enabled" );
  414.     }
  415.     else
  416.     {
  417.      element.setAttribute( "class", "disabled" );
  418.     }
  419.   }
  420.   else
  421.   {
  422.     dump( "not changing element "+elementID+"\n" );
  423.   }
  424. }
  425.  
  426. // Get the text appropriate to parent container
  427. //  to determine what a "%" value is refering to.
  428. // elementForAtt is element we are actually setting attributes on
  429. //  (a temporary copy of element in the doc to allow canceling),
  430. //  but elementInDoc is needed to find parent context in document
  431. function GetAppropriatePercentString(elementForAtt, elementInDoc)
  432. {
  433.   var name = elementForAtt.nodeName.toLowerCase();
  434.   if ( name == "td" || name == "th")
  435.     return GetString("PercentOfTable");
  436.  
  437.   // Check if element is within a table cell
  438.   // Check if current selection anchor node is within a table cell
  439.   if (editorShell.GetElementOrParentByTagName("td", elementInDoc))
  440.     return GetString("PercentOfCell");
  441.   else
  442.     return GetString("PercentOfWindow");
  443. }
  444.  
  445. function InitPixelOrPercentMenulist(elementForAtt, elementInDoc, attribute, menulistID, defaultIndex)
  446. {
  447.   if (!defaultIndex) defaultIndex = gPixel;
  448.  
  449.   var size  = elementForAtt.getAttribute(attribute);
  450.   var menulist = document.getElementById(menulistID);
  451.   var pixelItem;
  452.   var percentItem;
  453.  
  454.   if (!menulist)
  455.   {
  456.     dump("NO MENULIST found for ID="+menulistID+"\n");
  457.     return size;
  458.   }
  459.  
  460.   ClearMenulist(menulist);
  461.   pixelItem = AppendStringToMenulist(menulist, GetString("Pixels"));
  462.  
  463.   if (!pixelItem) return 0;
  464.  
  465.   percentItem = AppendStringToMenulist(menulist, GetAppropriatePercentString(elementForAtt, elementInDoc));
  466.   if (size && size.length > 0)
  467.   {
  468.     // Search for a "%" character
  469.     var percentIndex = size.search(/%/);
  470.     if (percentIndex > 0)
  471.     {
  472.       // Strip out the %
  473.       size = size.substr(0, percentIndex);
  474.       if (percentItem)
  475.         menulist.selectedItem = percentItem;
  476.     }
  477.     else
  478.       menulist.selectedItem = pixelItem;
  479.   }
  480.   else
  481.     menulist.selectedIndex = defaultIndex;
  482.  
  483.   return size;
  484. }
  485.  
  486. function AppendStringToMenulistById(menulist, stringID)
  487. {
  488.   return AppendStringToMenulist(menulist, editorShell.GetString(stringID));
  489. }
  490.  
  491. function AppendStringToMenulist(menulist, string)
  492. {
  493.   if (menulist)
  494.   {
  495.     var menupopup = menulist.firstChild;
  496.     // May not have any popup yet -- so create one
  497.     if (!menupopup)
  498.     {
  499.       menupopup = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menupopup");
  500.       if (menupopup)
  501.         menulist.appendChild(menupopup);
  502.       else
  503.       {
  504.         dump("Failed to create menupoup\n");
  505.         return null;
  506.       }
  507.     }
  508.     var menuItem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem");
  509.     if (menuItem)
  510.     {
  511.       menuItem.setAttribute("label", string);
  512.       menupopup.appendChild(menuItem);
  513.       return menuItem;
  514.     }
  515.   }
  516.   return null;
  517. }
  518.  
  519. function AppendLabelAndValueToMenulist(menulist, labelStr, valueStr)
  520. {
  521.   if (menulist)
  522.   {
  523.     var menupopup = menulist.firstChild;
  524.     // May not have any popup yet -- so create one
  525.     if (!menupopup)
  526.     {
  527.       menupopup = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menupopup");
  528.       if (menupopup)
  529.         menulist.appendChild(menupopup);
  530.       else
  531.       {
  532.         dump("Failed to create menupoup\n");
  533.         return null;
  534.       }
  535.     }
  536.     var menuItem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem");
  537.     if (menuItem)
  538.     {
  539.       menuItem.setAttribute("label", labelStr);
  540.       menuItem.setAttribute("value", valueStr);
  541.       menupopup.appendChild(menuItem);
  542.       return menuItem;
  543.     }
  544.   }
  545.   return null;
  546. }
  547.  
  548. function ClearMenulist(menulist)
  549. {
  550.   // Always use "AppendStringToMenulist" so we know there's 
  551.   //  just one <menupopup> as 1st child of <menulist>
  552.   if (menulist) {
  553.     menulist.selectedItem = null;
  554.     var popup = menulist.firstChild;
  555.     if (popup)
  556.       while (popup.firstChild)
  557.         popup.removeChild(popup.firstChild);
  558.   }
  559. }
  560.  
  561. /* These help using a <tree> for simple lists
  562.   Assumes this simple structure:
  563.   <tree>
  564.     <treecolgroup><treecol flex="1"/></treecolgroup>
  565.     <treechildren>
  566.       <treeitem>
  567.         <treerow>
  568.           <treecell label="the text the user sees"/>
  569. */
  570.  
  571. function AppendStringToTreelistById(tree, stringID)
  572. {
  573.   return AppendStringToTreelist(tree, editorShell.GetString(stringID));
  574. }
  575.  
  576. function AppendStringToTreelist(tree, string)
  577. {
  578.   if (tree)
  579.   {
  580.     var treecols = tree.firstChild;
  581.     if (!treecols)
  582.     {
  583.       dump("Bad XUL: Must have <treecolgroup> as first child\n");
  584.     }
  585.     var treechildren = treecols.nextSibling;
  586.     if (!treechildren)
  587.     {
  588.       treechildren = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "treechildren");
  589.       if (treechildren)
  590.       {
  591.         treechildren.setAttribute("flex","1");
  592.         tree.appendChild(treechildren);
  593.       }
  594.       else
  595.       {
  596.         dump("Failed to create <treechildren>\n");
  597.         return null;
  598.       }
  599.     }
  600.     var treeitem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "treeitem");
  601.     var treerow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "treerow");
  602.     var treecell = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "treecell");
  603.     if (treeitem && treerow && treecell)
  604.     {
  605.       treerow.appendChild(treecell);
  606.       treeitem.appendChild(treerow);
  607.       treechildren.appendChild(treeitem)
  608.       treecell.setAttribute("label", string);
  609.       //var len = Number(tree.getAttribute("length"));
  610.       //if (!len) len = -1;
  611.       tree.setAttribute("length", treechildren.childNodes.length);
  612.       return treeitem;
  613.     }
  614.   }
  615.   return null;
  616. }
  617.  
  618. function ReplaceStringInTreeList(tree, index, string)
  619. {
  620.   if (tree)
  621.   {
  622.     var treecols = tree.firstChild;
  623.     if (!treecols)
  624.     {
  625.       dump("Bad XUL: Must have <treecolgroup> as first child\n");
  626.       return;
  627.     }
  628.     var treechildren = treecols.nextSibling;
  629.     if (!treechildren)
  630.       return;
  631.  
  632.     // Each list item is a <treeitem><treerow><treecell> under <treechildren> node
  633.     var childNodes = treechildren.childNodes;
  634.     if (!childNodes || index >= childNodes.length)
  635.       return;
  636.  
  637.     var row = childNodes.item(index).firstChild;
  638.     if (row && row.firstChild)
  639.     {
  640.       row.firstChild.setAttribute("label", string);
  641.     }
  642.   }
  643. }
  644.  
  645. function ClearTreelist(tree)
  646. {
  647.   if (tree)
  648.   {
  649.     tree.clearItemSelection();
  650.     // Skip over the first <treecolgroup> child
  651.     if (tree.firstChild)
  652.     {
  653.       var nextChild = tree.firstChild.nextSibling;
  654.       while (nextChild)
  655.       {
  656.         var nextTmp = nextChild.nextSibling;
  657.         tree.removeChild(nextChild);
  658.         nextChild = nextTmp;
  659.       }
  660.     }
  661.     // Count list items
  662.     tree.setAttribute("length", 0);
  663.   }
  664. }
  665.  
  666. function GetSelectedTreelistAttribute(tree, attr)
  667. {
  668.   if (tree)
  669.   {
  670.     if (tree.selectedIndex >= 0 &&
  671.         tree.selectedItems.length > 0 &&
  672.         tree.selectedItems[0] &&
  673.         tree.selectedItems[0].firstChild &&
  674.         tree.selectedItems[0].firstChild.firstChild)
  675.     {
  676.       return tree.selectedItems[0].firstChild.firstChild.getAttribute(attr);
  677.     }
  678.   }
  679.   return "";
  680. }
  681.  
  682. function GetSelectedTreelistValue(tree)
  683. {
  684.   return GetSelectedTreelistAttribute(tree,"label")
  685. }
  686.  
  687. function RemoveSelectedTreelistItem(tree)
  688. {
  689.   if (tree)
  690.   {
  691.     if (tree.selectedIndex >= 0 &&
  692.         tree.selectedItems.length > 0)
  693.     {
  694.       // Get the node to delete
  695.       var treeItem = tree.selectedItems[0];
  696.       if (treeItem)
  697.       {
  698.         tree.clearItemSelection();
  699.         var parent = treeItem.parentNode;
  700.         if (parent)
  701.         {
  702.           parent.removeChild(treeItem);
  703.           tree.setAttribute("length", parent.childNodes.length);
  704.         }
  705.       }
  706.     }
  707.   }
  708. }
  709.  
  710. function GetTreelistValueAt(tree, index)
  711. {
  712.   if (tree)
  713.   {
  714.     var item = tree.getItemAtIndex(index);
  715.     if (item && item.firstChild && item.firstChild.firstChild)
  716.       return item.firstChild.firstChild.getAttribute("label");
  717.   }
  718.   return "";
  719. }
  720.  
  721. function forceInteger(elementID)
  722. {
  723.   var editField = document.getElementById( elementID );
  724.   if ( !editField )
  725.     return;
  726.  
  727.   var stringIn = editField.value;
  728.   if (stringIn && stringIn.length > 0)
  729.   {
  730.     // Strip out all nonnumeric characters
  731.     stringIn = stringIn.replace(/\D+/g,"");
  732.     if (!stringIn) stringIn = "";
  733.  
  734.     // Write back only if changed
  735.     if (stringIn != editField.value)
  736.       editField.value = stringIn;
  737.   }
  738. }
  739.  
  740. function LimitStringLength(elementID, length)
  741. {
  742.   var editField = document.getElementById( elementID );
  743.   if ( !editField )
  744.     return;
  745.  
  746.   var stringIn = editField.value;
  747.   if (stringIn && stringIn.length > length)
  748.     editField.value = stringIn.slice(0,length);
  749. }
  750.  
  751.  
  752. function onAdvancedEdit()
  753. {
  754.   // First validate data from widgets in the "simpler" property dialog
  755.   if (ValidateData())
  756.   {
  757.     // Set true if OK is clicked in the Advanced Edit dialog
  758.     window.AdvancedEditOK = false;
  759.     // Open the AdvancedEdit dialog, passing in the element to be edited
  760.     //  (the copy named "globalElement")
  761.     window.openDialog("chrome://editor/content/EdAdvancedEdit.xul", "_blank", "chrome,close,titlebar,modal,resizable=yes", "", globalElement);
  762.     window.focus();
  763.     if (window.AdvancedEditOK)
  764.     {
  765.       // Copy edited attributes to the dialog widgets:
  766.       InitDialog();
  767.     }
  768.   }
  769. }
  770.  
  771. function GetSelectionAsText()
  772. {
  773.   return editorShell.GetContentsAs("text/plain", gOutputSelectionOnly);
  774. }
  775.  
  776.  
  777. // ** getSelection ()
  778. // ** This function checks for existence of table around the focus node
  779. // ** Brian King - XML Workshop
  780.  
  781. function getContainer ()
  782. {
  783.   tagName = "img";
  784.   selImage = editorShell.GetSelectedElement(tagName);
  785.   if (selImage)  // existing image
  786.   {
  787.     oneup = selImage.parentNode;
  788.     return oneup;
  789.   }
  790.   else if (!selImage)  // new image insertion
  791.   {
  792.     dump("Not an image element -- Trying for caret selection\n");
  793.     var selection = window.editorShell.editorSelection;
  794.     if (selection)
  795.     {
  796.         var focusN = selection.focusNode;
  797.         if (focusN.nodeName.toLowerCase == "td")
  798.           return focusN
  799.                 else
  800.         {
  801.           oneup = focusN.parentNode;
  802.           return oneup;
  803.          }
  804.     }
  805.     else
  806.       return null;
  807.    }
  808.    else
  809.      return null;
  810. }
  811.  
  812. function getColor(ColorPickerID)
  813. {
  814.   var colorPicker = document.getElementById(ColorPickerID);
  815.   var color;
  816.   if (colorPicker)
  817.   {
  818.     // Extract color from colorPicker and assign to colorWell.
  819.     color = colorPicker.getAttribute("color");
  820.     if (color && color == "")
  821.       return null;
  822.     // Clear color so next if it's called again before
  823.     //  color picker is actually used, we dedect the "don't set color" state
  824.     colorPicker.setAttribute("color","");
  825.   }
  826.  
  827.   return color;
  828. }
  829.  
  830. function setColorWell(ColorWellID, color)
  831. {
  832.   var colorWell = document.getElementById(ColorWellID);
  833.   if (colorWell)
  834.   {
  835.     if (!color || color == "")
  836.     {
  837.       // Don't set color (use default)
  838.       // Trigger change to not show color swatch
  839.       colorWell.setAttribute("default","true");
  840.       // Style in CSS sets "background-color",
  841.       //   but color won't clear unless we do this:
  842.       colorWell.removeAttribute("style");
  843.     }
  844.     else
  845.     {
  846.       colorWell.removeAttribute("default");
  847.       // Use setAttribute so colorwell can be a XUL element, such as button
  848.       colorWell.setAttribute("style", "background-color:"+color);
  849.     }
  850.   }
  851. }
  852.  
  853. function getColorAndSetColorWell(ColorPickerID, ColorWellID)
  854. {
  855.   var color = getColor(ColorPickerID);
  856.   setColorWell(ColorWellID, color);
  857.   return color;
  858. }
  859.  
  860. // Test for valid image by sniffing out the extension
  861. function IsValidImage(imageName)
  862. {
  863.   var image = imageName.trimString();
  864.   if ( !image )
  865.     return false;
  866.  
  867.   /* look for an extension */
  868.   var tailindex = image.lastIndexOf(".");
  869.   if ( tailindex == 0 || tailindex == -1 ) /* -1 is not found */
  870.     return false;
  871.  
  872.   /* move past period, get the substring from the first character after the '.' to the last character (length) */
  873.   tailindex = tailindex + 1;
  874.   var type = image.substring(tailindex,image.length);
  875.  
  876.   /* convert extension to lower case */
  877.   if (type)
  878.     type = type.toLowerCase();
  879.  
  880.   // TODO: Will we convert .BMPs to a web format?
  881.   switch( type )
  882.   {
  883.     case "gif":
  884.     case "jpg":
  885.     case "jpeg":
  886.     case "png":
  887.       return true;
  888.       break;
  889.   }
  890.   return false;
  891. }
  892.  
  893. function InitMoreFewer()
  894. {
  895.   // Set SeeMore bool to the OPPOSITE of the current state,
  896.   //   which is automatically saved by using the 'persist="more"'
  897.   //   attribute on the dialog.MoreFewerButton button
  898.   //   onMoreFewer will toggle it and redraw the dialog
  899.   SeeMore = (dialog.MoreFewerButton.getAttribute("more") != "1");
  900.   onMoreFewer();
  901. }
  902.  
  903. function onMoreFewer()
  904. {
  905.   if (SeeMore)
  906.   {
  907.     dialog.MoreSection.setAttribute("collapsed","true");
  908.     window.sizeToContent();
  909.     dialog.MoreFewerButton.setAttribute("more","0");
  910.     dialog.MoreFewerButton.setAttribute("label",GetString("MoreProperties"));
  911.     SeeMore = false;
  912.   }
  913.   else
  914.   {
  915.     dialog.MoreSection.removeAttribute("collapsed");
  916.     window.sizeToContent();
  917.     dialog.MoreFewerButton.setAttribute("more","1");
  918.     dialog.MoreFewerButton.setAttribute("label",GetString("FewerProperties"));
  919.     SeeMore = true;
  920.   }
  921. }
  922.  
  923. function SwitchToValidatePanel()
  924. {
  925.   // no default implementation
  926.   // Only EdTableProps.js currently implements this
  927. }
  928.  
  929. function GetPrefs()
  930. {
  931.   var prefs;
  932.   try {
  933.     prefs = Components.classes['@mozilla.org/preferences;1'];
  934.     if (prefs) prefs = prefs.getService();
  935.     if (prefs) prefs = prefs.QueryInterface(Components.interfaces.nsIPref);
  936.     if (prefs)
  937.       return prefs;
  938.     else
  939.       dump("failed to get prefs service!\n");
  940.  
  941.   }
  942.   catch(ex)
  943.   {
  944.     dump("failed to get prefs service!\n");
  945.   }
  946.   return null;
  947. }
  948.  
  949. const nsIFilePicker = Components.interfaces.nsIFilePicker;
  950.  
  951. function GetLocalFileURL(filterType)
  952. {
  953.   var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  954.  
  955.   if (filterType == "img")
  956.   {
  957.     fp.init(window, GetString("SelectImageFile"), nsIFilePicker.modeOpen);
  958.     fp.appendFilters(nsIFilePicker.filterImages);
  959.   }
  960.   else if (filterType == "html")
  961.   {
  962.     fp.init(window, GetString("OpenHTMLFile"), nsIFilePicker.modeOpen);
  963.  
  964.     // When loading into Composer, direct user to prefer HTML files and text files,
  965.     //   so we call separately to control the order of the filter list
  966.     fp.appendFilters(nsIFilePicker.filterHTML);
  967.     fp.appendFilters(nsIFilePicker.filterText);
  968.   }
  969.   // Default or last filter is "All Files"
  970.   fp.appendFilters(nsIFilePicker.filterAll);
  971.  
  972.   /* doesn't handle *.shtml files */
  973.   try {
  974.     var ret = fp.show();
  975.     if (ret == nsIFilePicker.returnCancel)
  976.       return null;
  977.   }
  978.   catch (ex) {
  979.     dump("filePicker.chooseInputFile threw an exception\n");
  980.     return null;
  981.   }
  982.  
  983.   return fp.fileURL.spec;
  984. }
  985.  
  986. function GetMetaElement(name)
  987. {
  988.   if (name)
  989.   {
  990.     name = name.toLowerCase();
  991.     if (name != "")
  992.     {
  993.       var metaNodes = editorShell.editorDocument.getElementsByTagName("meta");
  994.       if (metaNodes && metaNodes.length > 0)
  995.       {
  996.         for (var i = 0; i < metaNodes.length; i++)
  997.         {
  998.           var metaNode = metaNodes.item(i);
  999.           if (metaNode && metaNode.getAttribute("name") == name)
  1000.             return metaNode;
  1001.         }
  1002.       }
  1003.     }
  1004.   }
  1005.   return null;
  1006. }
  1007.  
  1008. function CreateMetaElement(name)
  1009. {
  1010.   var metaElement = editorShell.CreateElementWithDefaults("meta");
  1011.   if (metaElement)
  1012.     metaElement.setAttribute("name", name);
  1013.   else
  1014.     dump("Failed to create metaElement!\n");
  1015.  
  1016.   return metaElement;
  1017. }
  1018.  
  1019. function GetHTTPEquivMetaElement(name)
  1020. {
  1021.   if (name)
  1022.   {
  1023.     name = name.toLowerCase();
  1024.     if (name != "")
  1025.     {
  1026.       var metaNodes = editorShell.editorDocument.getElementsByTagName("meta");
  1027.       if (metaNodes && metaNodes.length > 0)
  1028.       {
  1029.         for (var i = 0; i < metaNodes.length; i++)
  1030.         {
  1031.           var metaNode = metaNodes.item(i);
  1032.           if (metaNode && metaNode.getAttribute("http-equiv").toLowerCase() == name)
  1033.             return metaNode;
  1034.         }
  1035.       }
  1036.     }
  1037.   }
  1038.   return null;
  1039. }
  1040.  
  1041. function CreateHTTPEquivMetaElement(name)
  1042. {
  1043.   metaElement = editorShell.CreateElementWithDefaults("meta");
  1044.   if (metaElement)
  1045.     metaElement.setAttribute("http-equiv", name);
  1046.   else
  1047.     dump("Failed to create httpequivMetaElement!\n");
  1048.  
  1049.   return metaElement;
  1050. }
  1051.  
  1052. function CreateHTTPEquivElement(name)
  1053. {
  1054.   metaElement = editorShell.CreateElementWithDefaults("meta");
  1055.   if (metaElement)
  1056.     metaElement.setAttribute("http-equiv", name);
  1057.   else
  1058.     dump("Failed to create metaElement for http-equiv!\n");
  1059.  
  1060.   return metaElement;
  1061. }
  1062.  
  1063. // Change "content" attribute on a META element,
  1064. //   or delete entire element it if content is empty
  1065. // This uses undoable editor transactions
  1066. function SetMetaElementContent(metaElement, content, insertNew)
  1067. {
  1068.   if (metaElement)
  1069.   {
  1070.     if(!content || content == "")
  1071.     {
  1072.       if (!insertNew)
  1073.         editorShell.DeleteElement(metaElement);
  1074.     }
  1075.     else
  1076.     {
  1077.       if (insertNew)
  1078.       {
  1079.         metaElement.setAttribute("content", content);
  1080.         AppendHeadElement(metaElement);
  1081.       }
  1082.       else
  1083.         editorShell.SetAttribute(metaElement, "content", content);
  1084.     }
  1085.   }
  1086. }
  1087.  
  1088. function GetHeadElement()
  1089. {
  1090.   var headList = editorShell.editorDocument.getElementsByTagName("head");
  1091.   if (headList)
  1092.     return headList.item(0);
  1093.  
  1094.   return null;
  1095. }
  1096.  
  1097. function AppendHeadElement(element)
  1098. {
  1099.   var head = GetHeadElement();
  1100.   if (head)
  1101.   {
  1102.     var position = 0;
  1103.     if (head.hasChildNodes())
  1104.       position = head.childNodes.length;
  1105.  
  1106.     // Use editor's undoable transaction
  1107.     // Last param "true" says "don't change the selection"
  1108.     editorShell.InsertElement(element, head, position, true);
  1109.   }
  1110. }
  1111.  
  1112. function SetWindowLocation()
  1113. {
  1114.   gLocation = document.getElementById("location");
  1115.   if (gLocation)
  1116.   {
  1117.     window.screenX = Math.max(0, Math.min(window.opener.screenX + Number(gLocation.getAttribute("offsetX")),
  1118.                                           screen.availWidth - window.outerWidth));
  1119.     window.screenY = Math.max(0, Math.min(window.opener.screenY + Number(gLocation.getAttribute("offsetY")),
  1120.                                           screen.availHeight - window.outerHeight));
  1121.   }
  1122. }
  1123.  
  1124. function SaveWindowLocation()
  1125. {
  1126.   if (gLocation)
  1127.   {
  1128.     var newOffsetX = window.screenX - window.opener.screenX;
  1129.     var newOffsetY = window.screenY - window.opener.screenY;
  1130.     gLocation.setAttribute("offsetX", window.screenX - window.opener.screenX);
  1131.     gLocation.setAttribute("offsetY", window.screenY - window.opener.screenY);
  1132.   }
  1133. }
  1134.  
  1135. function onCancel()
  1136. {
  1137.   SaveWindowLocation();
  1138.   window.close();
  1139. }
  1140.  
  1141. function GetDefaultBrowserColors()
  1142. {
  1143.   var prefs = GetPrefs();
  1144.   var colors = new Object();
  1145.   var useSysColors = false;
  1146.   colors.TextColor = 0;
  1147.   colors.BackgroundColor = 0;
  1148.   try { useSysColors = prefs.GetBoolPref("browser.display.use_system_colors"); } catch (e) {}
  1149.  
  1150.   if (!useSysColors)
  1151.   {
  1152.     try { colors.TextColor = prefs.CopyCharPref("browser.display.foreground_color"); } catch (e) {}
  1153.  
  1154.     try { colors.BackgroundColor = prefs.CopyCharPref("browser.display.background_color"); } catch (e) {}
  1155.   }
  1156.   // Use OS colors for text and background if explicitly asked or pref is not set
  1157.   if (!colors.TextColor)
  1158.     colors.TextColor = "windowtext";
  1159.  
  1160.   if (!colors.BackgroundColor)
  1161.     colors.BackgroundColor = "window";
  1162.  
  1163.   colors.LinkColor = prefs.CopyCharPref("browser.anchor_color");
  1164.   colors.VisitedLinkColor = prefs.CopyCharPref("browser.visited_color");
  1165.  
  1166.   return colors;
  1167. }
  1168.  
  1169. function TextIsURI(selectedText)
  1170. {
  1171.   if (selectedText)
  1172.   {
  1173.     var text = selectedText.toLowerCase();
  1174.     return text.match(/^http:\/\/|^https:\/\/|^file:\/\/|^ftp:\/\/|\
  1175.                       ^about:|^mailto:|^news:|^snews:|^telnet:|\
  1176.                       ^ldap:|^ldaps:|^gopher:|^finger:|^javascript:/);
  1177.   }
  1178.   return false;
  1179. }
  1180.  
  1181. function SetRelativeCheckbox()
  1182. {
  1183.   var checkbox = document.getElementById("MakeRelativeCheckbox");
  1184.   if (!checkbox)
  1185.     return;
  1186.  
  1187.   var input =  document.getElementById(checkbox.getAttribute("for"));
  1188.   if (!input)
  1189.     return;
  1190.  
  1191.   var url = TrimString(input.value);
  1192.   var urlScheme = GetScheme(url);
  1193.  
  1194.   // Check it if url is relative (no scheme).
  1195.   checkbox.checked = url.length > 0 && !urlScheme;
  1196.  
  1197.   // Now do checkbox enabling:
  1198.   var enable = false;
  1199.  
  1200.   var docUrl = GetDocumentBaseUrl();
  1201.   var docScheme = GetScheme(docUrl);
  1202.  
  1203.   if (url && docUrl && docScheme)
  1204.   {
  1205.     if (urlScheme)
  1206.     {
  1207.       // Url is absolute
  1208.       // If we can make a relative URL, then enable must be true!
  1209.       // (this lets the smarts of MakeRelativeUrl do all the work)
  1210.       enable = (GetScheme(MakeRelativeUrl(url)).length == 0);
  1211.     }
  1212.     else
  1213.     {
  1214.       // Url is relative
  1215.       // Check if url is a named anchor
  1216.       //  but document doesn't have a filename
  1217.       // (it's probably "index.html" or "index.htm",
  1218.       //  but we don't want to allow a malformed URL)
  1219.       if (url[0] == "#")
  1220.       {
  1221.         var docFilename = GetFilename(docUrl);
  1222.         enable = docFilename.length > 0;
  1223.       }
  1224.       else
  1225.       {
  1226.         // Any other url is assumed 
  1227.         //  to be ok to try to make absolute
  1228.         enable = true;
  1229.       }
  1230.     }
  1231.   }
  1232.  
  1233.   SetElementEnabledById("MakeRelativeCheckbox", enable);
  1234. }
  1235.  
  1236. // oncommand handler for the Relativize checkbox in EditorOverlay.xul
  1237. function MakeInputValueRelativeOrAbsolute()
  1238. {
  1239.   var checkbox = document.getElementById("MakeRelativeCheckbox");
  1240.   if (!checkbox)
  1241.     return;
  1242.  
  1243.   var input =  document.getElementById(checkbox.getAttribute("for"));
  1244.   if (!input)
  1245.     return;
  1246.  
  1247.   var docUrl = GetDocumentBaseUrl();
  1248.   if (!docUrl)
  1249.   {
  1250.     // Checkbox should be disabled if not saved,
  1251.     //  but keep this error message in case we change that
  1252.     AlertWithTitle("", GetString("SaveToUseRelativeUrl"));
  1253.     window.focus();
  1254.   }
  1255.   else 
  1256.   {
  1257.     // Note that "checked" is opposite of its last state,
  1258.     //  which determines what we want to do here
  1259.     if (checkbox.checked)
  1260.       input.value = MakeRelativeUrl(input.value);
  1261.     else
  1262.       input.value = MakeAbsoluteUrl(input.value);
  1263.  
  1264.     // Reset checkbox to reflect url state
  1265.     SetRelativeCheckbox(checkbox, input.value);
  1266.   }
  1267. }
  1268.  
  1269. function MakeRelativeUrl(url)
  1270. {
  1271.   var inputUrl = TrimString(url);
  1272.   if (!inputUrl)
  1273.     return inputUrl;
  1274.  
  1275.   // Get the filespec relative to current document's location
  1276.   // NOTE: Can't do this if file isn't saved yet!
  1277.   var docUrl = GetDocumentBaseUrl();
  1278.   var docScheme = GetScheme(docUrl);
  1279.  
  1280.   // Can't relativize if no doc scheme (page hasn't been saved)
  1281.   if (!docScheme)
  1282.     return inputUrl;
  1283.  
  1284.   var urlScheme = GetScheme(inputUrl);
  1285.  
  1286.   // Do nothing if not the same scheme or url is already relativized
  1287.   if (docScheme != urlScheme)
  1288.     return inputUrl;
  1289.  
  1290.   var IOService = GetIOService();
  1291.   if (!IOService)
  1292.     return inputUrl;
  1293.  
  1294.   // Host must be the same
  1295.   var docHost = GetHost(docUrl);
  1296.   var urlHost = GetHost(inputUrl);
  1297.   if (docHost != urlHost)
  1298.     return inputUrl;
  1299.  
  1300.   // Get just the file path part of the urls
  1301.   var docPath = IOService.extractUrlPart(docUrl, IOService.url_Directory, {start:0}, {end:0}); 
  1302.   var urlPath = IOService.extractUrlPart(inputUrl, IOService.url_Directory, {start:0}, {end:0});
  1303.  
  1304.   // We only return "urlPath", so we can convert
  1305.   //  the entire docPath for case-insensitive comparisons
  1306.   var os = GetOS();
  1307.   var doCaseInsensitive = (docScheme.toLowerCase() == "file" && os == gWin);
  1308.   if (doCaseInsensitive)
  1309.     docPath = docPath.toLowerCase();
  1310.  
  1311.   // Get document filename before we start chopping up the docPath
  1312.   var docFilename = GetFilename(docPath);
  1313.  
  1314.   // Both url and doc paths now begin with "/"
  1315.   // Look for shared dirs starting after that
  1316.   urlPath = urlPath.slice(1);
  1317.   docPath = docPath.slice(1);
  1318.  
  1319.   var firstDirTest = true;
  1320.   var nextDocSlash = 0;
  1321.   var done = false;
  1322.  
  1323.   // Remove all matching subdirs common to both doc and input urls
  1324.   do {
  1325.     nextDocSlash = docPath.indexOf("\/");
  1326.     var nextUrlSlash = urlPath.indexOf("\/");
  1327.  
  1328.     if (nextUrlSlash == -1)
  1329.     {
  1330.       // We're done matching and all dirs in url
  1331.       // what's left is the filename
  1332.       done = true;
  1333.  
  1334.       // Remove filename for named anchors in the same file
  1335.       if (nextDocSlash == -1 && docFilename)
  1336.       { 
  1337.         var anchorIndex = urlPath.indexOf("#");
  1338.         if (anchorIndex > 0)
  1339.         {
  1340.           var urlFilename = doCaseInsensitive ? urlPath.toLowerCase() : urlPath;
  1341.         
  1342.           if (urlFilename.indexOf(docFilename) == 0)
  1343.             urlPath = urlPath.slice(anchorIndex);
  1344.         }
  1345.       }
  1346.     }
  1347.     else if (nextDocSlash >= 0)
  1348.     {
  1349.       // Test for matching subdir
  1350.       var docDir = docPath.slice(0, nextDocSlash);
  1351.       var urlDir = urlPath.slice(0, nextUrlSlash);
  1352.       if (doCaseInsensitive)
  1353.         urlDir = urlDir.toLowerCase();
  1354.  
  1355.       if (urlDir == docDir)
  1356.       {
  1357.  
  1358.         // Remove matching dir+"/" from each path
  1359.         //  and continue to next dir
  1360.         docPath = docPath.slice(nextDocSlash+1);
  1361.         urlPath = urlPath.slice(nextUrlSlash+1);
  1362.       }
  1363.       else
  1364.       {
  1365.         // No match, we're done
  1366.         done = true;
  1367.  
  1368.         // Be sure we are on the same local drive or volume 
  1369.         //   (the first "dir" in the path) because we can't 
  1370.         //   relativize to different drives/volumes.
  1371.         // UNIX doesn't have volumes, so we must not do this else
  1372.         //  the first directory will be misinterpreted as a volume name
  1373.         if (firstDirTest && docScheme == "file" && os != gUNIX)
  1374.           return inputUrl;
  1375.       }
  1376.     }
  1377.     else  // No more doc dirs left, we're done
  1378.       done = true;
  1379.  
  1380.     firstDirTest = false;
  1381.   }
  1382.   while (!done);
  1383.  
  1384.   // Add "../" for each dir left in docPath
  1385.   while (nextDocSlash > 0)
  1386.   {
  1387.     urlPath = "../" + urlPath;
  1388.     nextDocSlash = docPath.indexOf("\/", nextDocSlash+1);
  1389.   }
  1390.   return urlPath;
  1391. }
  1392.  
  1393. function MakeAbsoluteUrl(url)
  1394. {
  1395.   var resultUrl = TrimString(url);
  1396.   if (!resultUrl)
  1397.     return resultUrl;
  1398.  
  1399.   // Check if URL is already absolute, i.e., it has a scheme
  1400.   var urlScheme = GetScheme(resultUrl);
  1401.  
  1402.   if (urlScheme)
  1403.     return resultUrl;
  1404.  
  1405.   var docUrl = GetDocumentBaseUrl();
  1406.   var docScheme = GetScheme(docUrl);
  1407.  
  1408.   // Can't relativize if no doc scheme (page hasn't been saved)
  1409.   if (!docScheme)
  1410.     return resultUrl;
  1411.  
  1412.   var  IOService = GetIOService();
  1413.   if (!IOService)
  1414.     return resultUrl;
  1415.   
  1416.   // Make a URI object to use its "resolve" method
  1417.   var absoluteUrl = resultUrl;
  1418.   var docUri = IOService.newURI(docUrl, null);
  1419.  
  1420.   try {
  1421.     absoluteUrl = docUri.resolve(resultUrl);
  1422.     // This is deprecated and buggy! 
  1423.     // If used, we must make it a path for the parent directory (remove filename)
  1424.     //absoluteUrl = IOService.resolveRelativePath(resultUrl, docUrl);
  1425.   } catch (e) {}
  1426.  
  1427.   return absoluteUrl;
  1428. }
  1429.  
  1430. // Get the HREF of the page's <base> tag or the document location
  1431. // returns empty string if no base href and document hasn't been saved yet
  1432. function GetDocumentBaseUrl()
  1433. {
  1434.   if (window.editorShell)
  1435.   {
  1436.     var docUrl;
  1437.  
  1438.     // if document supplies a <base> tag, use that URL instead 
  1439.     var baseList = editorShell.editorDocument.getElementsByTagName("base");
  1440.     if (baseList)
  1441.     {
  1442.       var base = baseList.item(0);
  1443.       if (base)
  1444.         docUrl = base.getAttribute("href");
  1445.     }
  1446.     if (!docUrl)
  1447.       docUrl = editorShell.editorDocument.location.href;
  1448.  
  1449.     if (docUrl != "about:blank")
  1450.       return docUrl;
  1451.   }
  1452.   return "";
  1453. }
  1454.  
  1455. function GetIOService()
  1456. {
  1457.   if (gIOService)
  1458.     return gIOService;
  1459.  
  1460.   var CID = Components.classes["@mozilla.org/network/io-service;1"];
  1461.   gIOService = CID.getService(Components.interfaces.nsIIOService);
  1462.   return gIOService;
  1463. }
  1464.  
  1465. // Extract the scheme (e.g., 'file', 'http') from a URL string
  1466. function GetScheme(url)
  1467. {
  1468.   var resultUrl = TrimString(url);
  1469.   // Unsaved document URL has no acceptable scheme yet
  1470.   if (!resultUrl || resultUrl == "about:blank")
  1471.     return "";
  1472.  
  1473.   var IOService = GetIOService();
  1474.   if (!IOService)
  1475.     return "";
  1476.  
  1477.   var scheme = "";
  1478.   try {
  1479.     // This fails if there's no scheme
  1480.     scheme = IOService.extractScheme(resultUrl, {schemeStartPos:0}, {schemeEndPos:0});
  1481.    } catch (e) {}
  1482.  
  1483.   return scheme ? scheme : "";
  1484. }
  1485.  
  1486. function GetHost(url)
  1487. {
  1488.   var IOService = GetIOService();
  1489.   if (!IOService)
  1490.     return "";
  1491.  
  1492.   var host = "";
  1493.   if (url)
  1494.   {
  1495.     try {
  1496.       host = IOService.extractUrlPart(url, IOService.url_Host, {start:0}, {end:0}); 
  1497.      } catch (e) {}
  1498.   }
  1499.   return host;
  1500. }
  1501.  
  1502. function GetFilename(url)
  1503. {
  1504.   var IOService = GetIOService();
  1505.   if (!IOService)
  1506.     return "";
  1507.  
  1508.   var filename;
  1509.  
  1510.   if (url)
  1511.   {
  1512.     try {
  1513.       filename = IOService.extractUrlPart(url, IOService.url_FileBaseName, {start:0}, {end:0});
  1514.       if (filename)
  1515.       {
  1516.         var ext = IOService.extractUrlPart(url, IOService.url_FileExtension, {start:0}, {end:0});
  1517.         if (ext)
  1518.           filename += "."+ext;
  1519.       }
  1520.      } catch (e) {}
  1521.   }
  1522.   return filename ? filename : "";
  1523. }
  1524.  
  1525. function GetOS()
  1526. {
  1527.   if (gOS)
  1528.     return gOS;
  1529.  
  1530.   if (navigator.platform.toLowerCase().indexOf("win") >= 0)
  1531.     gOS = gWin;
  1532.   else if (navigator.platform.toLowerCase().indexOf("mac") >=0)
  1533.     gOS = gMac;
  1534.   else
  1535.     gOS = gUNIX;
  1536.  
  1537.   return gOS;
  1538. }
  1539.