Linker.prototype._createLink = function(a)
{
  if (!this._dialog)
  {
    this._dialog = new Linker.Dialog(this);
  }
  
  if(!a && this.editor.selectionEmpty(this.editor.getSelection()))
  {       
    alert(this._lc("You must select some text before making a new link."));
    return false;
  }

  var inputs =
  {
    type:     'url',
    href:     'http://www.example.com/',
    target:   '',
    p_width:  '',
    p_height: '',
    p_options: ['menubar=no','toolbar=yes','location=no','status=no','scrollbars=yes','resizeable=yes'],
    to:       'alice@example.com',
    subject:  '',
    body:     '',
    anchor:   ''
  };

  if(a && a.tagName.toLowerCase() == 'a')
  {
    var href =this.editor.fixRelativeLinks(a.getAttribute('href'));
    var m = href.match(/^mailto:(.*@[^?&]*)(\?(.*))?$/);
    var anchor = href.match(/^#(.*)$/);

    if(m)
    {
      // Mailto
      inputs.type = 'mailto';
      inputs.to = m[1];
      if(m[3])
      {
        var args  = m[3].split('&');
        for(var x = 0; x<args.length; x++)
        {
          var j = args[x].match(/(subject|body)=(.*)/);
          if(j)
          {
            inputs[j[1]] = decodeURIComponent(j[2]);
          }
        }
      }
    }
    else if (anchor)
    {
      //Anchor-Link
      inputs.type = 'anchor';
      inputs.anchor = anchor[1];
      
    }
    else
    {
      if(a.getAttribute('onclick'))
      {
        var m = a.getAttribute('onclick').match(/window\.open\(\s*this\.href\s*,\s*'([a-z0-9_]*)'\s*,\s*'([a-z0-9_=,]*)'\s*\)/i);

        // Popup Window
        inputs.href   = href ? href : '';
        inputs.target = 'popup';
        inputs.p_name = m[1];
        inputs.p_options = [ ];


        var args = m[2].split(',');
        for(var x = 0; x < args.length; x++)
        {
          var i = args[x].match(/(width|height)=([0-9]+)/);
          if(i)
          {
            inputs['p_' + i[1]] = parseInt(i[2]);
          }
          else
          {
            inputs.p_options.push(args[x]);
          }
        }
      }
      else
      {
        // Normal
        inputs.href   = href;
        inputs.target = a.target;
      }
    }
  }

  var linker = this;

  // If we are not editing a link, then we need to insert links now using execCommand
  // because for some reason IE is losing the selection between now and when doOK is
  // complete.  I guess because we are defocusing the iframe when we click stuff in the
  // linker dialog.

  this.a = a; // Why doesn't a get into the closure below, but if I set it as a property then it's fine?

  var doOK = function()
  {
    //if(linker.a) alert(linker.a.tagName);
    var a = linker.a;

    var values = linker._dialog.hide();
    var atr =
    {
      href: '',
      target:'',
      title:'',
      onclick:''
    };

    if(values.type == 'url')
    {
     if(values.href)
     {
       atr.href = values.href.trim();
       atr.target = values.target;
       if(values.target == 'popup')
       {

         if(values.p_width)
         {
           values.p_options.push('width=' + values.p_width);
         }
         if(values.p_height)
         {
           values.p_options.push('height=' + values.p_height);
         }
         atr.onclick = 'if(window.parent && window.parent.Xinha){return false}window.open(this.href, \'' + (values.p_name.replace(/[^a-z0-9_]/i, '_')) + '\', \'' + values.p_options.join(',') + '\');return false;';
       }
     }
    }
    else if(values.type == 'anchor')
    {
      if(values.anchor)
      {
        atr.href = values.anchor.value;
      }
    }
    else
    {
      if(values.to)
      {
        atr.href = 'mailto:' + values.to;
        if(values.subject) atr.href += '?subject=' + encodeURIComponent(values.subject);
        if(values.body)    atr.href += (values.subject ? '&' : '?') + 'body=' + encodeURIComponent(values.body);
      }
    }

    if (atr.href) atr.href = atr.href.trim();

    if(a && a.tagName.toLowerCase() == 'a')
    {
      if(!atr.href)
      {
        if(confirm(linker._dialog._lc('Are you sure you wish to remove this link?')))
        {
          var p = a.parentNode;
          while(a.hasChildNodes())
          {
            p.insertBefore(a.removeChild(a.childNodes[0]), a);
          }
          p.removeChild(a);
          linker.editor.updateToolbar();
          return;
        }
      }
      else
      {
        // Update the link
        for(var i in atr)
        {
          a.setAttribute(i, atr[i]);
        }
        
        // If we change a mailto link in IE for some hitherto unknown
        // reason it sets the innerHTML of the link to be the 
        // href of the link.  Stupid IE.
        if(Xinha.is_ie)
        {
          if(/mailto:([^?<>]*)(\?[^<]*)?$/i.test(a.innerHTML))
          {
            a.innerHTML = RegExp.$1;
          }
        }
      }
    }
    else
    {
      if(!atr.href) return true;

      // Insert a link, we let the browser do this, we figure it knows best
      var tmp = Xinha.uniq('http://www.example.com/Link');
      linker.editor._doc.execCommand('createlink', false, tmp);

      // Fix them up
      var anchors = linker.editor._doc.getElementsByTagName('a');
      for(var i = 0; i < anchors.length; i++)
      {
        var anchor = anchors[i];
        if(anchor.href == tmp)
        {
          // Found one.
          if (!a) a = anchor;
          for(var j in atr)
          {
            anchor.setAttribute(j, atr[j]);
          }
        }
      }
    }
    linker.editor.selectNodeContents(a);
    linker.editor.updateToolbar();
  };

  this._dialog.show(inputs, doOK);

};

Linker.prototype._getSelectedAnchor = function()
{
  var sel  = this.editor.getSelection();
  var rng  = this.editor.createRange(sel);
  var a    = this.editor.activeElement(sel);
  if(a != null && a.tagName.toLowerCase() == 'a')
  {
    return a;
  }
  else
  {
    a = this.editor._getFirstAncestor(sel, 'a');
    if(a != null)
    {
      return a;
    }
  }
  return null;
};

Linker.Dialog_dTrees = [ ];

Linker.Dialog = function (linker)
{
  var  lDialog = this;
  this.Dialog_nxtid = 0;
  this.linker = linker;
  this.id = { }; // This will be filled below with a replace, nifty

  this.ready = false;

  this.dialog = false;

  // load the dTree script
  this._prepareDialog();

};

Linker.Dialog.prototype._prepareDialog = function()
{
  var lDialog = this;
  var linker = this.linker;

  var files = this.linker.files;

  // Now we have everything we need, so we can build the dialog.
  if(!linker.lConfig.dialog && Xinha.Dialog) linker.lConfig.dialog = Xinha.Dialog;
  
  var dialog = this.dialog = new linker.lConfig.dialog(linker.editor, Linker.html, 'Linker',{width:600,height:400});
  var dTreeName = Xinha.uniq('dTree_');

  this.dTree = new dTree(dTreeName, Xinha.getPluginDir("Linker") + '/dTree/');
  eval(dTreeName + ' = this.dTree');

  this.dTree.add(this.Dialog_nxtid++, -1, linker.lConfig.treeCaption , null, linker.lConfig.treeCaption);
  this.makeNodes(files, 0);

  // Put it in
  var ddTree = this.dialog.getElementById('dTree');
  //ddTree.innerHTML = this.dTree.toString();
  ddTree.innerHTML = '';
//  ddTree.style.position = 'absolute';
//  ddTree.style.left = 1 + 'px';
 // ddTree.style.top =  0 + 'px';
  ddTree.style.overflow = 'auto';
  ddTree.style.height = '300px';
  if ( Xinha.is_ie )
  {
    ddTree.style.styleFloat = "left";
  }
  else
  {
    ddTree.style.cssFloat = "left";
  }
  ddTree.style.backgroundColor = 'white';
  this.ddTree = ddTree;
  
  this.dTree._linker_premade = this.dTree.toString();

  var options = this.dialog.getElementById('options');
  //options.style.position = 'absolute';
  //options.style.top      = 0   + 'px';
  //options.style.right    = 0   + 'px';
  options.style.width    = 320 + 'px';
  options.style.overflow = 'auto';

  // Hookup the resizer
  this.dialog.rootElem.style.paddingBottom ="0";
  this.dialog.onresize = function()
    {
      var h = parseInt(dialog.height) - dialog.getElementById('h1').offsetHeight;
      var w = parseInt(dialog.width)  - 330 ;

      // An error is thrown with IE when trying to set a negative width or a negative height
      // But perhaps a width / height of 0 is not the minimum required we need to set
      if (w<0) w = 0;
      if (h<0) h = 0;
      //options.style.height =
      lDialog.ddTree.style.height = h + 'px';
      lDialog.ddTree.style.width  = w + 'px';
    }

  // Set the onclick handlers for the link type radio buttons
  var self = this;
  this.dialog.getElementById('type_url').onclick = function() {
    self.showOptionsForType('url');
  };
  this.dialog.getElementById('type_mailto').onclick = function() {
    self.showOptionsForType('mailto');
  };
  this.dialog.getElementById('type_anchor').onclick = function() {
    self.showOptionsForType('anchor');
  };

  var hidePopupOptions = function() {
    self.showOptionsForTarget('none')
  };
  this.dialog.getElementById('noTargetRadio').onclick = hidePopupOptions;
  this.dialog.getElementById('sameWindowRadio').onclick = hidePopupOptions;
  this.dialog.getElementById('newWindowRadio').onclick = hidePopupOptions;
  this.dialog.getElementById('popupWindowRadio').onclick = function() {
    self.showOptionsForTarget('popup');
  };

  this.ready = true;
  ddTree = null;
  Xinha.freeLater(lDialog, 'ddTree');
  options = null;
};

Linker.Dialog.prototype.makeNodes = function(files, parent)
{ 
  for(var i = 0; i < files.length; i++)
  {
    if(typeof files[i] == 'string')
    {
      this.dTree.add(this.Dialog_nxtid++, parent,
                     files[i].replace(/^.*\//, ''),
                     'javascript:document.getElementsByName(\'' + this.dialog.id.href + '\')[0].value=decodeURIComponent(\'' + encodeURIComponent(files[i]) + '\');document.getElementsByName(\'' + this.dialog.id.type + '\')[0].click();document.getElementsByName(\'' + this.dialog.id.href + '\')[0].focus();void(0);',
                     files[i]);
    }
    else if(typeof files[i]=="object" && files[i] && typeof files[i].length==="number") // there seems to be a strange bug in IE that requires this complicated check, see #1197
    {
      var id = this.Dialog_nxtid++;
      this.dTree.add(id, parent, files[i][0].replace(/^.*\//, ''), null, files[i][0]);
      this.makeNodes(files[i][1], id);
    }
    else if(typeof files[i] == 'object')
    {
      var id = this.Dialog_nxtid++;     
      if(files[i].title) var title = files[i].title;
      else if(files[i].url) var title = files[i].url.replace(/^.*\//, '');
      else var title = "no title defined";
      if(files[i].url) var link = 'javascript:document.getElementsByName(\'' + this.dialog.id.href + '\')[0].value=decodeURIComponent(\'' + encodeURIComponent(files[i].url) + '\');document.getElementsByName(\'' + this.dialog.id.type + '\')[0].click();document.getElementsByName(\'' + this.dialog.id.href + '\')[0].focus();void(0);';
      else var link = '';
      
      this.dTree.add(id, parent, title, link, title);
      if(files[i].children) {
        this.makeNodes(files[i].children, id);
      }
    }
  }
};

Linker.Dialog.prototype._lc = Linker.prototype._lc;

Linker.Dialog.prototype.show = function(inputs, ok, cancel)
{
  if(!this.ready)
  {
    var lDialog = this;
    window.setTimeout(function() {lDialog.show(inputs,ok,cancel);},100);
    return;
  }

  if(this.ddTree.innerHTML == '')
  {
    this.ddTree.innerHTML = this.dTree._linker_premade;
  }
  
  if(!this.linker.lConfig.canSetTarget)
  {
    this.dialog.getElementById('target_options').style.display = 'none';    
  }
  
  this.showOptionsForType(inputs.type);
  this.showOptionsForTarget(inputs.target);
  
  var anchor = this.dialog.getElementById('anchor');
  for(var i=anchor.length;i>=0;i--) {
    anchor[i] = null;
  }

  var html = this.linker.editor.getHTML();  
  var anchors = new Array();

  var m = html.match(/<a[^>]+name="([^"]+)"/gi);
  if(m)
  {
    for(i=0;i<m.length;i++)
    {
        var n = m[i].match(/name="([^"]+)"/i);
        if(!anchors.contains(n[1])) anchors.push(n[1]);
    }
  }
  m = html.match(/id="([^"]+)"/gi);
  if(m)
  {
    for(i=0;i<m.length;i++)
    {
        n = m[i].match(/id="([^"]+)"/i);
        if(!anchors.contains(n[1])) anchors.push(n[1]);
    }
  }
  
  for(i=0;i<anchors.length;i++)
  {
    var opt = new Option(anchors[i],'#'+anchors[i],false,(inputs.anchor == anchors[i]));
    anchor[anchor.length] = opt;
  }

  // Configuration for disabling the mail link functionality.
  if(this.linker.lConfig.disableMailto)
  {
    this.dialog.getElementById('mailtofieldset').style.display = "none";
  }

  // Configuration for hiding the anchor functionality.  Also, no need to show
  // the UI if there are no anchors present in the document.
  if(anchor.length==0 || this.linker.lConfig.disableAnchors)
  {
    this.dialog.getElementById('anchorfieldset').style.display = "none";

    // If we disable the other two fieldsets, we'll hide the (now) unnecessary
    // radio button.
    if (this.linker.lConfig.disableMailto)
    {
      this.dialog.getElementById('type').style.display = "none";
    }
  }

  // Disable link targets (all targets available by default)
  var disabledTargets = this.linker.lConfig.disableTargetTypes; 
  if (typeof disabledTargets == 'undefined')
  {
    disabledTargets = [];
  } 
  else if (typeof disabledTargets == 'string')
  {
    disabledTargets = [disabledTargets];
  }
  for (var i=0; i<disabledTargets.length; i++)
  {
    this.dialog.getElementById(disabledTargets[i]).style.display = "none";
  }
  if (disabledTargets.length == 3) // only one target option is visible
  {
    if (disabledTargets.contains('popupWindow'))
    {
      // There's no need to show anything, so hide the entire div
      this.dialog.getElementById('target_options').style.display = "none";
    }
    else
    {
      // Only popups are allowed, hide the radio button
      this.dialog.getElementById('popupWindowRadio').style.display = "none";
      this.showOptionsForTarget('popup');
    }
  }

  var enabledTargets = new Array();
  if (!disabledTargets.contains('noTarget'))
  {
    enabledTargets.push('noTargetRadio');
  }
  if (!disabledTargets.contains('sameWindow'))
  {
    enabledTargets.push('sameWindowRadio');
  }
  if (!disabledTargets.contains('newWindow'))
  {
    enabledTargets.push('newWindowRadio');
  }
  if (!disabledTargets.contains('popupWindow'))
  {
    enabledTargets.push('popupWindowRadio');
  }

  // if we're not editing an existing link, hide the remove link button
  if (inputs.href == 'http://www.example.com/' && inputs.to == 'alice@example.com') { 
    this.dialog.getElementById('clear').style.display = "none";
  }
  else { // 
    var clearBtn = this.dialog.getElementById('clear');
    clearBtn.style.display = "";
    if (ok)
    {
      clearBtn.onclick = function() { lDialog.removeLink(ok); };
    }
  }
  
  // It could be forced not be able to be removed, as is the case with link-picker.js
  if(!this.linker.lConfig.canRemoveLink)
  {
    this.dialog.getElementById('clear').style.display = 'none';
  }
  
  // Connect the OK and Cancel buttons
  var dialog = this.dialog;
  var lDialog = this;
  if(ok)
  {
    this.dialog.getElementById('ok').onclick = ok;
  }
  else
  {
    this.dialog.getElementById('ok').onclick = function() {lDialog.hide();};
  }

  if(cancel)
  {
    this.dialog.getElementById('cancel').onclick = cancel;
  }
  else
  {
    this.dialog.getElementById('cancel').onclick = function() { lDialog.hide()};
  }

  // Show the dialog
  this.linker.editor.disableToolbar(['fullscreen','linker']);

  this.dialog.show(inputs);

  // If we set the default radio button *before* we call dialog.show()
  // it doesn' work...
  var targetSelected = false;
  for (var i=0; i<enabledTargets.length; i++)
  {
    if (this.dialog.getElementById(enabledTargets[i]).checked == true)
    {
      targetSelected = true;
      break;
    }
  }
  // If no target is selected, select the first one by default
  if (!targetSelected && enabledTargets.length > 0)
  {
    this.dialog.getElementById(enabledTargets[0]).checked = true;
  }

  // Init the sizes
  this.dialog.onresize();
};

Linker.Dialog.prototype.hide = function()
{
  this.linker.editor.enableToolbar();
  return this.dialog.hide();
};

Linker.Dialog.prototype.removeLink = function(applyFunc)
{
  this.dialog.getElementById('href').value = "";
  this.dialog.getElementById('to').value = "";

  return applyFunc();
};

Linker.Dialog.prototype.showOptionsForType = function(type)
{
  var urlOptions = this.dialog.getElementById('urltable');
  var mailtoOptions = this.dialog.getElementById('mailtable');
  var anchorOptions = this.dialog.getElementById('anchortable');

  if (type == 'anchor')
  {
    anchorOptions.style.display = '';
    urlOptions.style.display = 'none';
    mailtoOptions.style.display = 'none';
  }
  else if (type == 'mailto')
  {
    mailtoOptions.style.display = '';
    urlOptions.style.display = 'none';
    anchorOptions.style.display = 'none';
  }
  else 
  {
    urlOptions.style.display = '';
    mailtoOptions.style.display = 'none';
    anchorOptions.style.display = 'none';
  }
};

Linker.Dialog.prototype.showOptionsForTarget = function(target)
{
  var popupOptions = this.dialog.getElementById('popuptable');
  popupOptions.style.display = target == 'popup' ? '' : 'none';
};
