/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var gUntitledString;

function TextEditorOnLoad()
{
  var url = "about:blank";
  // See if argument was passed.
  if (window.arguments && window.arguments[0])
  {
    // Opened via window.openDialog with URL as argument.
    url = window.arguments[0];
  }
  // Continue with normal startup.
  EditorStartup(url);
}

function EditorOnLoad()
{
  var url = "about:blank";
  var charset;
  // See if argument was passed.
  if (window.arguments)
  {
    if (window.arguments[0])
    {
      // Opened via window.openDialog with URL as argument.
      url = window.arguments[0];
    }

    // get default character set if provided
    if (window.arguments.length > 1 && window.arguments[1])
    {
      if (window.arguments[1].includes("charset="))
      {
        var arrayArgComponents = window.arguments[1].split("=");
        if (arrayArgComponents)
          charset = arrayArgComponents[1];
      }
    }
  }

  // XUL elements we use when switching from normal editor to edit source.
  gContentWindowDeck = document.getElementById("ContentWindowDeck");
  gFormatToolbar = document.getElementById("FormatToolbar");

  // Continue with normal startup.
  EditorStartup(url, charset);

  // Hide Highlight button if we are in an HTML editor with CSS mode off
  // and tell the editor if a CR in a paragraph creates a new paragraph.
  var cmd = document.getElementById("cmd_highlight");
  if (cmd) {
    if (!Services.prefs.getBoolPref(kUseCssPref))
      cmd.collapsed = true;
  }

  // Initialize our source text <editor>
  try {
    gSourceContentWindow = document.getElementById("content-source");
    gSourceContentWindow.makeEditable("text", false);
    gSourceTextEditor = gSourceContentWindow.getEditor(gSourceContentWindow.contentWindow);
    gSourceTextEditor.enableUndo(false);
    gSourceTextEditor.rootElement.style.fontFamily = "-moz-fixed";
    gSourceTextEditor.rootElement.style.whiteSpace = "pre";
    gSourceTextEditor.rootElement.style.margin = 0;
    var controller = Cc["@mozilla.org/embedcomp/base-command-controller;1"]
                       .createInstance(Ci.nsIControllerContext);
    controller.setCommandContext(gSourceContentWindow);
    gSourceContentWindow.contentWindow.controllers.insertControllerAt(0, controller);
    var commandTable = controller.QueryInterface(Ci.nsIInterfaceRequestor)
                                 .getInterface(Ci.nsIControllerCommandTable);
    commandTable.registerCommand("cmd_findReplace", nsFindReplaceCommand);
    commandTable.registerCommand("cmd_find",        nsFindCommand);
    commandTable.registerCommand("cmd_findNext",    nsFindAgainCommand);
    commandTable.registerCommand("cmd_findPrev",    nsFindAgainCommand);
  } catch (e) {
    dump("makeEditable failed: "+e+"\n");
  }
}

function toggleAffectedChrome(aHide)
{
  // chrome to toggle includes:
  //   (*) menubar
  //   (*) toolbox
  //   (*) sidebar
  //   (*) statusbar

  if (!gChromeState)
    gChromeState = new Object;

  var statusbar = document.getElementById("status-bar");

  // sidebar states map as follows:
  //   hidden    => hide/show nothing
  //   collapsed => hide/show only the splitter
  //   shown     => hide/show the splitter and the box
  if (aHide)
  {
    // going into print preview mode
    gChromeState.sidebar = SidebarGetState();
    SidebarSetState("hidden");

    // deal with the Status Bar
    gChromeState.statusbarWasHidden = statusbar.hidden;
    statusbar.hidden = true;
  }
  else
  {
    // restoring normal mode (i.e., leaving print preview mode)
    SidebarSetState(gChromeState.sidebar);

    // restore the Status Bar
    statusbar.hidden = gChromeState.statusbarWasHidden;
  }

  // if we are unhiding and sidebar used to be there rebuild it
  if (!aHide && gChromeState.sidebar == "visible")
    SidebarRebuild();

  document.getElementById("EditorToolbox").hidden = aHide;
  document.getElementById("appcontent").collapsed = aHide;
}

var PrintPreviewListener = {
  getPrintPreviewBrowser: function () {
    var browser = document.getElementById("ppBrowser");
    if (!browser) {
      browser = document.createXULElement("browser");
      browser.setAttribute("id", "ppBrowser");
      browser.setAttribute("flex", "1");
      browser.setAttribute("disablehistory", "true");
      browser.setAttribute("disablesecurity", "true");
      browser.setAttribute("type", "content");
      document.getElementById("sidebar-parent").
        insertBefore(browser, document.getElementById("appcontent"));
    }
    return browser;
  },
  getSourceBrowser: function () {
    return GetCurrentEditorElement();
  },
  getNavToolbox: function () {
    return document.getElementById("EditorToolbox");
  },
  onEnter: function () {
    toggleAffectedChrome(true);
  },
  onExit: function () {
    document.getElementById("ppBrowser").collapsed = true;
    toggleAffectedChrome(false);
  }
}

function EditorStartup(aUrl, aCharset)
{
  gUntitledString = GetFormattedString("untitledTitle", GetNextUntitledValue());

  var ds = GetCurrentEditorElement().docShell;
  ds.useErrorPages = false;
  var root = ds.QueryInterface(Ci.nsIDocShellTreeItem)
               .rootTreeItem.QueryInterface(Ci.nsIDocShell);

  root.QueryInterface(Ci.nsIDocShell).appType =
    Ci.nsIDocShell.APP_TYPE_EDITOR;

  // EditorSharedStartup also used by Message Composer.
  EditorSharedStartup();

  // Commands specific to the Composer Application window,
  //  (i.e., not embedded editors)
  //  such as file-related commands, HTML Source editing, Edit Modes...
  SetupComposerWindowCommands();

  gCSSPrefListener = new nsPrefListener(kUseCssPref);
  gReturnInParagraphPrefListener = new nsPrefListener(kCRInParagraphsPref);
  Services.obs.addObserver(EditorCanClose, "quit-application-requested");

  root.charset = aCharset;

  // Get url for editor content and load it. The editor gets instantiated by
  // the editingSession when the URL has finished loading.
  EditorLoadUrl(aUrl);

  // Before and after callbacks for the customizeToolbar code.
  var editorToolbox = getEditorToolbox();
  editorToolbox.customizeInit = EditorToolboxCustomizeInit;
  editorToolbox.customizeDone = EditorToolboxCustomizeDone;
  editorToolbox.customizeChange = EditorToolboxCustomizeChange;
}

function EditorShutdown()
{
  Services.obs.removeObserver(EditorCanClose, "quit-application-requested");

  gCSSPrefListener.shutdown();
  gReturnInParagraphPrefListener.shutdown();

  try
  {
    var commandManager = GetCurrentCommandManager();
    commandManager.removeCommandObserver(gEditorDocumentObserver,
                                         "obs_documentCreated");
    commandManager.removeCommandObserver(gEditorDocumentObserver,
                                         "obs_documentWillBeDestroyed");
    commandManager.removeCommandObserver(gEditorDocumentObserver,
                                         "obs_documentLocationChanged");
  } catch (e) { dump (e); }
}

// --------------------------- File menu ---------------------------

// Check for changes to document and allow saving before closing
// This is hooked up to the OS's window close widget (e.g., "X" for Windows)
async function EditorCanClose(aCancelQuit, aTopic, aData)
{
  if (aTopic == "quit-application-requested" &&
      aCancelQuit instanceof Ci.nsISupportsPRBool &&
      aCancelQuit.data)
    return false;

  // Returns FALSE only if user cancels save action

  // "true" means allow "Don't Save" button
  var canClose = await CheckAndSaveDocument("cmd_close", true);

  // This is our only hook into closing via the "X" in the caption
  //   or "Quit" (or other paths?)
  //   so we must shift association to another
  //   editor or close any non-modal windows now
  if (canClose && "InsertCharWindow" in window && window.InsertCharWindow)
    SwitchInsertCharToAnotherEditorOrClose();

  if (!canClose && aTopic == "quit-application-requested")
    aCancelQuit.data = true;

  return canClose;
}

function BuildRecentPagesMenu()
{
  var editor = GetCurrentEditor();
  if (!editor)
    return;

  var popup = document.getElementById("menupopup_RecentFiles");
  if (!popup || !editor.document)
    return;

  // Delete existing menu
  while (popup.hasChildNodes())
    popup.lastChild.remove();

  // Current page is the "0" item in the list we save in prefs,
  //  but we don't include it in the menu.
  var curUrl = StripPassword(GetDocumentUrl());
  var historyCount = Services.prefs.getIntPref("editor.history.url_maximum", 10);

  var menuIndex = 1;
  for (var i = 0; i < historyCount; i++)
  {
    var url = Services.prefs.getStringPref("editor.history_url_" + i, "");

    // Skip over current url
    if (url && url != curUrl)
    {
      // Build the menu
      var title = Services.prefs.getStringPref("editor.history_title_" + i, "");
      var fileType = Services.prefs.getStringPref("editor.history_type_" + i, "");
      AppendRecentMenuitem(popup, title, url, fileType, menuIndex);
      menuIndex++;
    }
  }
}

function AppendRecentMenuitem(aPopup, aTitle, aUrl, aFileType, aIndex)
{
  if (!aPopup)
    return;

  var menuItem = document.createXULElement("menuitem");
  if (!menuItem)
    return;

  var accessKey = aIndex <= 10 ? String(aIndex % 10) : " ";

  // Show "title [url]" or just the URL.
  var itemString = aTitle ? aTitle + " [" + aUrl + "]" : aUrl;

  menuItem.setAttribute("label", accessKey + " " + itemString);
  menuItem.setAttribute("crop", "center");
  menuItem.setAttribute("tooltiptext", aUrl);
  menuItem.setAttribute("value", aUrl);
  menuItem.setAttribute("fileType", aFileType);
  if (accessKey != " ")
    menuItem.setAttribute("accesskey", accessKey);
  aPopup.appendChild(menuItem);
}

function EditorInitFileMenu()
{
  // Disable "Save" menuitem when editing remote url. User should use "Save As"

  var docUrl = GetDocumentUrl();
  var scheme = GetScheme(docUrl);
  if (scheme && scheme != "file")
    SetElementEnabledById("menu_saveCmd", false);

  // Enable recent pages submenu if there are any history entries in prefs.
  var historyUrl = "";

  if (Services.prefs.getIntPref("editor.history.url_maximum", 10))
  {
    historyUrl = Services.prefs.getStringPref("editor.history_url_0", "");

    // See if there's more if current file is only entry in history list.
    if (historyUrl && historyUrl == docUrl)
      historyUrl = Services.prefs.getStringPref("editor.history_url_1", "");
  }
  SetElementEnabledById("menu_RecentFiles", historyUrl != "");
}

function EditorUpdateCharsetMenu(aMenuPopup)
{
  if (IsDocumentModified() && !IsDocumentEmpty())
  {
    for (var i = 0; i < aMenuPopup.childNodes.length; i++)
      aMenuPopup.childNodes[i].setAttribute("disabled", "true");
  }

  UpdateCharsetMenu(content.document.characterSet, aMenuPopup);
}

// Zoom support.
function getBrowser()
{
  return IsInHTMLSourceMode() ? gSourceContentWindow : GetCurrentEditorElement();
}

// override the site-specific zoom object in viewZoomOverlay.js
var FullZoom = {
  init: function() {},
  reduce: function() { ZoomManager.reduce(); },
  enlarge: function() { ZoomManager.enlarge(); },
  zoom: function(aZoomValue) { ZoomManager.zoom = aZoomValue; },
  reset: function() { ZoomManager.zoom = 1; },
  setOther: function() { openZoomDialog(); }
};

function hideEditorUI(aHide) {
  for (let id of ["EditModeToolbar", "content-source", "content-frame"]) {
    let element = document.getElementById(id);
    if (!element)
      continue;

    if (aHide) {
      element.setAttribute("moz-collapsed", true);
    } else {
      element.removeAttribute("moz-collapsed");
    }
  }
}

function getEditorToolbox() {
  return document.getElementById("EditorToolbox");
}

function EditorToolboxCustomizeInit() {
  if (document.commandDispatcher.focusedWindow == content)
    window.focus();
  hideEditorUI(true);
  toolboxCustomizeInit("main-menubar");
}

function EditorToolboxCustomizeDone(aToolboxChanged) {
  toolboxCustomizeDone("main-menubar", getEditorToolbox(), aToolboxChanged);
  hideEditorUI(false);
  gContentWindow.focus();
}

function EditorToolboxCustomizeChange(aEvent) {
  toolboxCustomizeChange(getEditorToolbox(), aEvent);
}
