/**
 * urleditor.js
 *
 * Ver  Date        Who  Description
 * 001  13.09.2009  SAJ  Initial version.
 * 002  21.09.2009  SAJ  Added BSD License.
 *
 * ---- BSD License ------------------------------------------------------------
 *
 * Copyright (c) 2009 Stuart Jones. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * ----------------------------------------------------------------------------
 */

/**
 * Register event handlers when the DOM has loaded.
 */
document.observe('dom:loaded', function () {
  $('original').observe('keyup', splitIt);
  $('original').observe('change', splitIt);

  $('btnSplitIt').observe('click', splitIt);
  $('btnOpen').observe('click', openUrl);
  $('btnReset').observe('click', reset);

  $('btnAdd').observe('click', addNameValue);
  $('btnEncode').observe('click', encode);
  $('btnDecode').observe('click', decode);

  $('original').activate();
});

/**
 * Global pointer to the window in which URLs are opened.
 */
var gOtherWin = null;

/**
 * Global to contain everything up to the "?".
 */
var gDomain = '';

/**
 * Global holding the fading state of the URL box.
 */
var gFading = false;

/**
 * Add a new name/value pair to the list by adding an ampersand
 * to the URL and re-splitting. Finally, place the cursor in the
 * name text box. Helpful, innit?
 */
function addNameValue() {
  $('original').value += '&';
  splitIt();
  var ns = $$('.n');
  if (ns.length > 0) {
    ns[ns.length - 1].activate();
  }
}

/**
 * Encode all values. Highlight any that change their appearance.
 */
function encode() {
  $$('.n', '.v').each(function(el) {
    var dec = encodeURIComponent(el.value);
    if (el.value != dec) {
      el.value = dec;

      //
      // Highlight the encoded text box (if it's not currently being highlighted).
      //
      var f = el.readAttribute('fading');
      if (!f || f == 'false') {
        new Effect.Highlight(el,
          {
            beforeStart: function (o) { o.element.writeAttribute('fading', 'true'); }, 
            afterFinish: function (o) { o.element.writeAttribute('fading', 'false'); }
          }
        );
      }
    }
  });
  updateUrl();
}

/**
 * Decode all values. Highlight any that change their appearance.
 */
function decode() {
  $$('.n', '.v').each(function(el) {
    var dec = decodeURIComponent(el.value);
    if (el.value != dec) {
      el.value = dec;

      //
      // Highlight the decoded text box (if it's not currently being highlighted).
      //
      var f = el.readAttribute('fading');
      if (!f || f == 'false') {
        new Effect.Highlight(el,
          {
            beforeStart: function (o) { o.element.writeAttribute('fading', 'true'); }, 
            afterFinish: function (o) { o.element.writeAttribute('fading', 'false'); }
          }
        );
      }

    }
  });
  updateUrl();
}

/**
 * Attempt to open the URL in the gOtherWin tab. If it has not
 * been created, or it's been closed, recreate it.
 */
function openUrl() {
  try {
    gOtherWin.location = $('original').value;
  }
  catch (exc) {
    gOtherWin = window.open($('original').value);
  }
}

/**
 * Create the name/value DOM object comprising a DIV containing
 * a couple of inputs and a delete button.
 */
function createObj(n, v, count) {

  if (!$('nvcontainer').visible()) {
    $('nvcontainer').show();
  }

  //
  // The class bit of this didn't work in IE8 so we do it long hand.
  // var elName = new Element('input', { 'class': 'n' });
  //
  var elName = new Element('input');
  elName.value = n;
  elName.addClassName('n');
  elName.observe('change', updateUrl);
  elName.observe('keyup', updateUrl);

  var elValue = new Element('input');
  elValue.value = v;
  elValue.addClassName('v');
  elValue.observe('change', updateUrl);
  elValue.observe('keyup', updateUrl);

  var elDeleteButton = new Element('button', {});
  elDeleteButton.innerHTML = 'Delete';
  elDeleteButton.observe('click', removeNameValue);

  var elNameValue = new Element('div');
  elNameValue.addClassName('nv');
  elNameValue.appendChild(elName);
  elNameValue.appendChild(elValue);
  elNameValue.appendChild(elDeleteButton);

  //
  // IDs must be in the format "{string}_{integer}"
  // for the sortable list functionality.
  //
  elNameValue.id = 'id_' + count;
  $('nvs').insert({ bottom: elNameValue });
}

/**
 * Called by the delete button in a name/value pair object,
 * this removes said object from the DOM.
 */
function removeNameValue() {
  this.parentNode.remove();
  updateUrl();
}

/**
 * Resets everything.
 */
function reset() {
  removeNameValues();
  $('original').value = '';
  $('original').activate();

// hide nvs container
}

/**
 * Removes all name/value objects from the DOM.
 */
function removeNameValues() {  
  $('nvcontainer').hide();
  $$('div.nv').each(function (o) { o.remove(); });
}

/**
 * Return the position of the "?" in the passed-in URL.
 */
function getQMPos(url) {
  var url = url.strip();
  return url.indexOf('?');
}

/**
 * Return the domain from the passed in URL. If a "?" doesn't exist
 * return the empty string.
 */
function getDomain(url) {
  var pos = getQMPos(url);
  if (pos == -1) return '';
  return url.substring(0, pos);
}

/**
 * Return the querystring from the passed in URL. If a "?" doesn't exist
 * return the empty string.
 */
function getQueryString(url) {
  var pos = getQMPos(url);
  if (pos == -1) return '';
  return url.substring(pos + 1);
}

/**
 * Chop up the querystring of the URL into name/value pairs and show them
 * in a sortable list dynamically.
 */
function splitIt() {
  removeNameValues();

  if ($('original').value.strip().blank()) return;

  gDomain = getDomain($('original').value);

  var querystring = getQueryString($('original').value);
  if (querystring.strip().blank()) return;

  var params = querystring.split('&');

  //
  // Draw 'em.
  //
  for (var i = 0; i < params.length; i++) {
    var nv = params[i].split('=');
    createObj(nv[0], nv[1], i);
  }

  //
  // Create the sortable list.
  //
  Sortable.create(
    'nvs',
    {
      tag: 'div',
      onUpdate: updateUrl
    }
  );
}

/**
 * Reconstruct the URL based upon the name/value pairs.
 */
function updateUrl() {
  var startingValue = $('original').value;
  var qs = '';
  var nvs = $('nvs').getElementsByTagName('div');
  for (var i = 0; i < nvs.length; i++) {
    qs += '&' + $(nvs[i]).childElements()[0].value + '=' + $(nvs[i]).childElements()[1].value;
  }
  qs = qs.substring(1);

  //
  // Update the URL box and highlight it
  // (if it's not currently being highlighted).
  //
  $('original').value = gDomain + '?' + qs;
  if ($('original').value != startingValue && !gFading) {
    new Effect.Highlight($('original'), // was 'url'
      { 
        beforeStart: function () { gFading = true; }, 
        afterFinish: function () { gFading = false; } 
      }
    );
  }
}

