/* 
 * 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
 *   Ben Goodger
 */

var insertNew     = true;
var insertNewIMap = true;
var wasEnableAll  = false;
var constrainOn = false;
// Note used in current version, but these are set correctly
//  and could be used to reset width and height used for constrain ratio
var constrainWidth  = 0;
var constrainHeight = 0;
var imageElement;
var imageMap = 0;
var canRemoveImageMap = false;
var imageMapDisabled = false;
var dialog;
var globalMap;
var doAltTextError = true;
var actualWidth = "";
var actualHeight = "";
var previewImage;
var timeoutId = -1;
// msec between attempts to load image
var interval = 200;
var intervalSum = 0;
// After this many msec, give up trying to load image
var intervalLimit = 60000;

// These must correspond to values in EditorDialog.css for each theme
// (unfortunately, setting "style" attribute here doesn't work!)
var previewImageWidth = 80;
var previewImageHeight = 50;
var StartupCalled = false;

// dialog initialization code

function Startup()
{
  //XXX Very weird! When calling this with an existing image,
  //    we get called twice. That causes dialog layout 
  //    to explode to fullscreen!
  if (StartupCalled)
  {
    dump("*** CALLING IMAGE DIALOG Startup() AGAIN! ***\n");
    return;
  }
  StartupCalled = true;

  if (!InitEditorShell())
    return;

  doSetOKCancel(onOK, onCancel);

  // Create dialog object to store controls for easy access
  dialog = new Object;

  dialog.srcInput          = document.getElementById( "srcInput" );
  dialog.altTextInput      = document.getElementById( "altTextInput" );
  dialog.MoreFewerButton   = document.getElementById( "MoreFewerButton" );
  dialog.AdvancedEditButton = document.getElementById( "AdvancedEditButton" );
  dialog.AdvancedEditButton2 = document.getElementById( "AdvancedEditButton2" );
  dialog.MoreSection       = document.getElementById( "MoreSection" );
  dialog.customSizeRadio   = document.getElementById( "customSizeRadio" );
  dialog.actualSizeRadio = document.getElementById( "actualSizeRadio" );
  dialog.constrainCheckbox = document.getElementById( "constrainCheckbox" );
  dialog.widthInput        = document.getElementById( "widthInput" );
  dialog.heightInput       = document.getElementById( "heightInput" );
  dialog.widthUnitsMenulist   = document.getElementById( "widthUnitsMenulist" );
  dialog.heightUnitsMenulist  = document.getElementById( "heightUnitsMenulist" );
  dialog.imagelrInput      = document.getElementById( "imageleftrightInput" );
  dialog.imagetbInput      = document.getElementById( "imagetopbottomInput" );
  dialog.border            = document.getElementById( "border" );
  dialog.alignTypeSelect   = document.getElementById( "alignTypeSelect" );
  dialog.alignImage        = document.getElementById( "alignImage" );
  dialog.alignText         = document.getElementById( "alignText" );
  dialog.editImageMap      = document.getElementById( "editImageMap" );
  dialog.removeImageMap    = document.getElementById( "removeImageMap" );
  dialog.ImageHolder       = document.getElementById( "preview-image-holder" );
  dialog.PreviewBox        = document.getElementById( "preview-image-box" );
  dialog.PreviewWidth      = document.getElementById( "PreviewWidth" );
  dialog.PreviewHeight     = document.getElementById( "PreviewHeight" );
  dialog.PreviewSize       = document.getElementById( "PreviewSize" );
  
  // Get a single selected image element
  var tagName = "img"
  imageElement = editorShell.GetSelectedElement(tagName);

  if (imageElement) 
  {
    // We found an element and don't need to insert one
    insertNew = false;
    actualWidth  = imageElement.naturalWidth;
    actualHeight = imageElement.naturalHeight;
  } 
  else
  {
    insertNew = true;

    // We don't have an element selected, 
    //  so create one with default attributes

    imageElement = editorShell.CreateElementWithDefaults(tagName);
    if( !imageElement )
    {
      dump("Failed to get selected element or create a new one!\n");
      window.close();
    }  
  }

  // Make a copy to use for AdvancedEdit
  globalElement = imageElement.cloneNode(false);
  

  // Get image map for image
  var usemap = globalElement.getAttribute("usemap");
  if (usemap)
  {
    var mapname = usemap.substring(1, usemap.length);
    var mapCollection = editorShell.editorDocument.getElementsByName(mapname);
    if (mapCollection[0] != null)
    {
      imageMap = mapCollection[0];
      globalMap = imageMap;
      canRemoveImageMap = true;
      insertNewIMap = false;
      dump(imageMap.childNodes.length+"\n");
    }
    else
    {
      insertNewIMap = true;
      globalMap = null;
    }
  }
  else
  {
    insertNewIMap = true;
    globalMap = null;
  }
  InitDialog();

  // By default turn constrain on, but both width and height must be in pixels
  dialog.constrainCheckbox.checked =
    dialog.widthUnitsMenulist.selectedIndex == 0 &&
    dialog.heightUnitsMenulist.selectedIndex == 0;    

  // Set SeeMore bool to the OPPOSITE of the current state,
  //   which is automatically saved by using the 'persist="more"' 
  //   attribute on the MoreFewerButton button
  //   onMoreFewerImage will toggle the state and redraw the dialog
  SeeMore = (dialog.MoreFewerButton.getAttribute("more") != "1");

  // Initialize widgets with image attributes in the case where the entire dialog isn't visible
  onMoreFewerImage();  // this call will initialize all widgets if entire dialog is visible

  SetTextfieldFocus(dialog.srcInput);

  SetWindowLocation();
}

// Set dialog widgets with attribute data
// We get them from globalElement copy so this can be used
//   by AdvancedEdit(), which is shared by all property dialogs
function InitDialog()
{
  // Set the controls to the image's attributes

  var str = globalElement.getAttribute("src");
  if (str)
  {
    dialog.srcInput.value = str;
    GetImageFromURL();
  }
  
  str = globalElement.getAttribute("alt");
  if (str)
    dialog.altTextInput.value = str;
  
  // setup the height and width widgets
  var width = InitPixelOrPercentMenulist(globalElement, 
                    insertNew ? null : imageElement, 
                    "width", "widthUnitsMenulist", gPixel);
  var height = InitPixelOrPercentMenulist(globalElement, 
                    insertNew ? null : imageElement, 
                    "height", "heightUnitsMenulist", gPixel);
  
  // Set actual radio button if both set values are the same as actual
  if (actualWidth && actualHeight)
    dialog.actualSizeRadio.checked = (width == actualWidth) && (height == actualHeight);
  else if ( !(width || height) )
    dialog.actualSizeRadio.checked = true;

  if (!dialog.actualSizeRadio.checked)
    dialog.customSizeRadio.checked = true;
  
  dialog.widthInput.value  = constrainWidth = width ? width : (actualWidth ? actualWidth : "");
  dialog.heightInput.value = constrainHeight = height ? height : (actualHeight ? actualHeight : "");

  // set spacing editfields
  dialog.imagelrInput.value = globalElement.getAttribute("hspace");
  dialog.imagetbInput.value = globalElement.getAttribute("vspace");
  dialog.border.value       = globalElement.getAttribute("border");    

  // Get alignment setting  
  var align = globalElement.getAttribute("align");
  if (align) {
	  align = align.toLowerCase();
  }
  var imgClass;
  var textID;
  switch ( align )
  {
	  case "top":
	  case "center":
	  case "right":
	  case "left":
      dialog.alignTypeSelect.data = align;
      break;
	  default:  // Default or "bottom"
	    dialog.alignTypeSelect.data = "bottom";
  }

  // we want to force an update so initialize "wasEnableAll" to be the opposite of what the actual state is
  wasEnableAll = !IsValidImage(dialog.srcInput.value);
  doOverallEnabling();
  doDimensionEnabling();
}

function chooseFile()
{
  // Get a local file, converted into URL format
  var fileName = GetLocalFileURL("img");
  if (fileName) {
    dialog.srcInput.value = fileName;
    doOverallEnabling();
  }
  GetImageFromURL();
  // Put focus into the input field
  SetTextfieldFocus(dialog.srcInput);
}

function GetImageFromURL()
{
  dialog.PreviewSize.setAttribute("collapsed", "true");

  // Remove existing preview image
  // (other attempts to just change "src" fail;
  //  once we fail to load, further setting of src fail)
  if (dialog.ImageHolder.firstChild)
  {
    //previewImage.setAttribute("width", 0);
    //previewImage.setAttribute("height", 0);
    dialog.ImageHolder.removeChild(dialog.ImageHolder.firstChild);
  }
  
  var imageSrc = dialog.srcInput.value;
  if (imageSrc) imageSrc = imageSrc.trimString();
  if (!imageSrc) return;
  if (IsValidImage(imageSrc))
  {
    if (!dialog.ImageHolder.firstChild)
    {
      // Append an image to the dialog to trigger image loading
      //   and also serves as a preview
      previewImage = editorShell.CreateElementWithDefaults("img");
      if (!previewImage) return;
      dialog.ImageHolder.appendChild(previewImage);
    }
    previewImage.src = imageSrc;

    // Get the origin width from the image or setup timer to get later
    if (previewImage.complete)
      GetActualSize();
    else
    {
      // Start timer to poll until image is loaded
      //dump("*** Starting timer to get natural image size...\n");
      timeoutId = window.setInterval("GetActualSize()", interval);
      intervalSum = 0;
    }
  }
}

function GetActualSize()
{
  if (intervalSum > intervalLimit)
  {
    dump(" Timeout trying to load preview image\n");
    CancelTimer();
    return;
  }

  if (!previewImage)
  {
    CancelTimer();
  }
  else
  {
    if (previewImage.complete)
    {
      // Image loading has completed -- we can get actual width
      CancelTimer();
      actualWidth  = previewImage.naturalWidth;
      actualHeight = previewImage.naturalHeight;
      if (actualWidth && actualHeight)
      {
        // Use actual size or scale to fit preview if either dimension is too large
        var width = actualWidth;
        var height = actualHeight;
        if (actualWidth > previewImageWidth)
        {
            width = previewImageWidth;
            height = actualHeight * (previewImageWidth / actualWidth);
        }
        if (height > previewImageHeight)
        {
          height = previewImageHeight;
          width = actualWidth * (previewImageHeight / actualHeight);
        }
        if (actualWidth > previewImageWidth || actualHeight > previewImageHeight)
        {
          // Resize image to fit preview frame
          previewImage.setAttribute("width", width);
          previewImage.setAttribute("height", height);
        }

        dialog.PreviewWidth.setAttribute("value", actualWidth);
        dialog.PreviewHeight.setAttribute("value", actualHeight);

        dialog.PreviewSize.setAttribute("collapsed", "false");
        dialog.ImageHolder.setAttribute("collapsed", "false");

        // Use values as start for constrain proportions
      }

      if (dialog.actualSizeRadio.checked)
        SetActualSize();
    }
    else 
    {
      //dump("*** Waiting for image loading...\n");
      // Accumulate time so we don't do try this forever
      intervalSum += interval;
    }
  }
}

function SetActualSize()
{
  dialog.widthInput.value = actualWidth ? actualWidth : "";
  dialog.widthUnitsMenulist.selectedIndex = 0;
  dialog.heightInput.value = actualHeight ? actualHeight : "";
  dialog.heightUnitsMenulist.selectedIndex = 0;
  doDimensionEnabling();
}

function CancelTimer()
{
  if (timeoutId != -1)
  {
    //dump("*** CancelTimer\n");
    window.clearInterval(timeoutId);
    timeoutId = -1;
  }
  intervalSum = 0;
}

function ChangeImageSrc()
{
  GetImageFromURL();
  doOverallEnabling();
}

function onMoreFewerImage()
{
  if (SeeMore)
  {
    dialog.MoreSection.setAttribute("collapsed","true");
    dialog.MoreFewerButton.setAttribute("value", GetString("MoreProperties"));
    dialog.MoreFewerButton.setAttribute("more","0");
    SeeMore = false;
    // Show the "Advanced Edit" button on same line as "More Properties"
    dialog.AdvancedEditButton.setAttribute("collapsed","false");
    dialog.AdvancedEditButton2.setAttribute("collapsed","true");
    // Weird caret appearing when we collapse, so force focus to URL textfield
    dialog.srcInput.focus();
  }
  else
  {
    dialog.MoreSection.setAttribute("collapsed","false");
    dialog.MoreFewerButton.setAttribute("value", "");
    dialog.MoreFewerButton.setAttribute("value", GetString("FewerProperties"));
    dialog.MoreFewerButton.setAttribute("more","1");
    SeeMore = true;

    // Show the "Advanced Edit" button at bottom
    dialog.AdvancedEditButton.setAttribute("collapsed","true");
    dialog.AdvancedEditButton2.setAttribute("collapsed","false");
  }
  window.sizeToContent();
}

function doDimensionEnabling()
{
  // Enabled only if "Custom" is checked
  var enable = (dialog.customSizeRadio.checked);
  
  if ( !dialog.customSizeRadio.checked && !dialog.actualSizeRadio.checked)
    dump("BUG!  neither radio button is checked!!!! \n");

  SetElementEnabledById( "widthInput", enable );
  SetElementEnabledById( "widthLabel", enable);
  SetElementEnabledById( "widthUnitsMenulist", enable );

  SetElementEnabledById( "heightInput", enable );
  SetElementEnabledById( "heightLabel", enable );
  SetElementEnabledById( "heightUnitsMenulist", enable );

  var constrainEnable = enable 
         && ( dialog.widthUnitsMenulist.selectedIndex == 0 )
         && ( dialog.heightUnitsMenulist.selectedIndex == 0 );

  SetElementEnabledById( "constrainCheckbox", constrainEnable );

  // Counteracting another weird caret 
  //  (appears in Height input, but not really focused!)

// not sure why we want to give the width field focus;
// it certainly doesn't make sense to do this in a generic 
// enable/disable function which could be called at any time
  //if (enable)
  //  dialog.widthInput.focus();
}

function doOverallEnabling()
{
  var canEnableOk = IsValidImage(dialog.srcInput.value);
  if ( wasEnableAll == canEnableOk )
    return;
  
  wasEnableAll = canEnableOk;

  SetElementEnabledById("ok", canEnableOk );

  SetElementEnabledById( "imagemapLabel",  canEnableOk );
  SetElementEnabledById( "editImageMap",   canEnableOk );
  SetElementEnabledById( "removeImageMap", canRemoveImageMap);
}

function ToggleConstrain()
{
  // If just turned on, save the current width and height as basis for constrain ratio
  // Thus clicking on/off lets user say "Use these values as aspect ration"
  if (dialog.constrainCheckbox.checked && !dialog.constrainCheckbox.disabled
     && (dialog.widthUnitsMenulist.selectedIndex == 0)
     && (dialog.heightUnitsMenulist.selectedIndex == 0))
  {
    constrainWidth = Number(dialog.widthInput.value.trimString());
    constrainHeight = Number(dialog.heightInput.value.trimString());
  }
// the following line is *really* annoying!
//  document.getElementById('widthInput').focus()

}

function constrainProportions( srcID, destID )
{
  var srcElement = document.getElementById ( srcID );
  if ( !srcElement )
    return;
  
  var destElement = document.getElementById( destID );
  if ( !destElement )
    return;

  // always force an integer (whether we are constraining or not)
  forceInteger( srcID );

//  if (!constrainWidth || !constrainHeight ||
  if (!actualWidth || !actualHeight ||
      !(dialog.constrainCheckbox.checked && !dialog.constrainCheckbox.disabled))
    return;
 
  // double-check that neither width nor height is in percent mode; bail if so!
  if ( (dialog.widthUnitsMenulist.selectedIndex != 0)
     || (dialog.heightUnitsMenulist.selectedIndex != 0) ) 
    return;

  // This always uses the actual width and height ratios
  // which is kind of funky if you change one number without the constrain
  // and then turn constrain on and change a number
  // I prefer the old strategy (below) but I can see some merit to this solution 
  if (srcID == "widthInput")
    destElement.value = Math.round( srcElement.value * actualHeight / actualWidth );
  else
    destElement.value = Math.round( srcElement.value * actualWidth / actualHeight );

/*  
  // With this strategy, the width and height ratio 
  //   can be reset to whatever the user entered.
  if (srcID == "widthInput")
    destElement.value = Math.round( srcElement.value * constrainHeight / constrainWidth );
  else
    destElement.value = Math.round( srcElement.value * constrainWidth / constrainHeight );
*/
}

function editImageMap()
{
  // Make a copy to use for image map editor
  if (insertNewIMap)
  {
    imageMap = editorShell.CreateElementWithDefaults("map");
    globalMap = imageMap.cloneNode(true);
  }
  else
    globalMap = imageMap;  
  
  window.openDialog("chrome://editor/content/EdImageMap.xul", "_blank", "chrome,close,titlebar,modal", globalElement, globalMap);
}

function removeImageMap()
{
  globalElement.removeAttribute("usemap");
  if (imageMap){
    canRemoveImageMap = false;
    SetElementEnabledById( "removeImageMap", false);
    editorShell.DeleteElement(imageMap);
    insertNewIMap = true;
    globalMap = null;
  }
}

// Get data from widgets, validate, and set for the global element
//   accessible to AdvancedEdit() [in EdDialogCommon.js]
function ValidateData()
{
  if ( !IsValidImage(dialog.srcInput.value )) {
    ShowInputErrorMessage(GetString("MissingImageError"));
    return false;
  }

  //TODO: WE NEED TO DO SOME URL VALIDATION HERE, E.G.:
  // We must convert to "file:///" or "http://" format else image doesn't load!
  var src = dialog.srcInput.value.trimString();
  globalElement.setAttribute("src", src);
  
  // Note: allow typing spaces, 
  // Warn user if empty string just once per dialog session
  //   but don't consider this a failure
  var alt = dialog.altTextInput.value;
  if (doAltTextError && !alt)
  {
    ShowInputErrorMessage(GetString("NoAltText"));
    SetTextfieldFocus(dialog.altTextInput);
    doAltTextError = false;
    return false;
  }
  globalElement.setAttribute("alt", alt);
  
  var width = "";
  var height = "";
  var isPercentWidth, isPercentHeight;
  var maxLimitWidth, maxLimitHeight;

  if (dialog.actualSizeRadio.checked)
  {
    width = actualWidth ? actualWidth : "";
    height = actualHeight ? actualHeight : "";
  }
  else
  {
	  isPercentWidth = (dialog.widthUnitsMenulist.selectedIndex == 1);
	  isPercentHeight = (dialog.heightUnitsMenulist.selectedIndex == 1);

	  maxLimitWidth = isPercentWidth ? 100 : maxPixels;  // Defined in EdDialogCommon.js
    width = ValidateNumberString(dialog.widthInput.value, 1, maxLimitWidth);
    if (width == "")
    {
      if ( !SeeMore )
        onMoreFewerImage();
      SetTextfieldFocus(dialog.widthInput);
      return false;
    }
    if (isPercentWidth)
      width = width + "%";

	  maxLimitHeight = isPercentHeight ? 100 : maxPixels;  // Defined in EdDialogCommon.js
    height = ValidateNumberString(dialog.heightInput.value, 1, maxLimitHeight);
    if (height == "")
    {
      if ( !SeeMore )
        onMoreFewerImage();
      SetTextfieldFocus(dialog.heightInput);
      return false;
    }
    if (isPercentHeight)
      height = height + "%";
  }
  // We always set the width and height attributes, even if same as actual.
  //  This speeds up layout of pages since sizes are known before image is downloaded
  // (But don't set if we couldn't obtain actual dimensions)
  if (width)
    globalElement.setAttribute("width", width);
  else
    globalElement.removeAttribute("width");

  if (height)
    globalElement.setAttribute("height", height);
  else
    globalElement.removeAttribute("height");

  // spacing attributes
  var amount;
	if ( dialog.imagelrInput.value )
	{
	  amount = ValidateNumberString(dialog.imagelrInput.value, 0, maxPixels);
	  if (amount == "")
	    return false;
    globalElement.setAttribute( "hspace", amount );
  }
  else
    globalElement.removeAttribute( "hspace" );

	if ( dialog.imagetbInput.value )
	{
	  var vspace = ValidateNumberString(dialog.imagetbInput.value, 0, maxPixels);
	  if (vspace == "")
	    return false;
	  globalElement.setAttribute( "vspace", vspace );
	}
	else
	  globalElement.removeAttribute( "vspace" );

  // note this is deprecated and should be converted to stylesheets
	if ( dialog.border.value )
	{
	  var border = ValidateNumberString(dialog.border.value, 0, maxPixels);
	  if (border == "")
	    return false;
	  globalElement.setAttribute( "border", border );
	}
	else
	  globalElement.removeAttribute( "border" );

	// Default or setting "bottom" means don't set the attribute
  // Note that the attributes "left" and "right" are opposite
  //  of what we use in the UI, which describes where the TEXT wraps,
  //  not the image location (which is what the HTML describes)
	switch ( dialog.alignTypeSelect.data )
	{
	  case "top":
	  case "center":
	  case "right":
	  case "left":
      globalElement.setAttribute( "align", dialog.alignTypeSelect.data );
      break;
    default:
      globalElement.removeAttribute( "align" );
	}

  return true;
}

function onOK()
{
  // handle insertion of new image
  if (ValidateData())
  {
	  // Assign to map if there is one
	  if ( globalMap )
	  {
      var mapName = globalMap.getAttribute("name");
      dump("mapName = "+mapName+"\n");
      if (mapName != "")
      {
        globalElement.setAttribute("usemap", ("#"+mapName));
        if (globalElement.getAttribute("border") == "")
          globalElement.setAttribute("border", 0);
      }

      // Copy or insert image map
      imageMap = globalMap.cloneNode(true);
      if (insertNewIMap)
      {
        try
        {
          editorShell.editorDocument.body.appendChild(imageMap);
        //editorShell.InsertElementAtSelection(imageMap, false);
        }
        catch (e)
        {
          dump("Exception occured in InsertElementAtSelection\n");
        }
      }
    }

    // All values are valid - copy to actual element in doc or 
    //   element created to insert
    editorShell.CloneAttributes(imageElement, globalElement);
    if (insertNew)
    {
      try {
        // 'true' means delete the selection before inserting
        editorShell.InsertElementAtSelection(imageElement, true);
      } catch (e) {
        dump("Exception occured in InsertElementAtSelection\n");
      }
    }

    // un-comment to see that inserting image maps does not work!
    /*test = editorShell.CreateElementWithDefaults("map");
    test.setAttribute("name", "testing");
    testArea = editorShell.CreateElementWithDefaults("area");
    testArea.setAttribute("shape", "circle");
    testArea.setAttribute("coords", "86,102,52");
    testArea.setAttribute("href", "test");
    test.appendChild(testArea);
    editorShell.InsertElementAtSelection(test, false);*/

    SaveWindowLocation();
    CancelTimer();
    return true;
  }
  return false;
}

function onImageCancel()
{
  CancelTimer();
  onCancel();
}

