//Change log
// 02-03-11 - LP - Updated attributes in embed statement for flash with type='application/x-shockwave-flash'
//
//--------------
// Declare TINY to be an object, initially empty. Note that there is only one
// TINY - instances don't come into it. It can be thought of as defining a
// namespace.
var TINY = {};

var slideshowTimer,
    boxTimer;

// Short-hand function
function T$(i) {
  return document.getElementById(i)
}

// Add a function, 'box', to the TINY object. This function returns an object
// containing the methods, 'show', 'fill', etc., which control the behaviour
// of a DIV element that serves as the visible object. We could presumably
// create multiple instances by:
// inst1 = new TINY.box;
// inst2 = new TINY.box;
// etc., but what relationship they would have to each other I don't know.
// Needs checking.
TINY.box = function() {
  // Instance variables
  var
    p,          // containing DIV (p for picture?)
    m,          // mask DIV
    b,          // content div (b for body?)
    fn,         // not used?
    ic,         // content
    iu,         // use AJAX
    iw,         // width
    ih,         // height
    ia,         // animate
    f = 0,      // initialisation flag (zero until object initialised)
    iext,       // additional args to be posted as args to responder
    access;     // access level - 0 initially, set as appropriate when logged
                // in. Don't rely on it though as JS can be spoofed.

  return {

    // Show initialises the object if it hasn't been initialised. Then it kicks
    // off the process of masking the window and displaying the box according
    // to the supplied parameters. We don't call show again until we've done
    // all we want to - as long as one box simply gives place to another we use
    // fill (or refill).
    //   Additional arguments can be provided and should take the form of
    // quoted name-value pairs (e.g. 'id1=12345&id2=5678') presented as a
    // single string. These will be posted with any other arguments (see
    // refill) to any responder. If there are several responders they will all
    // receive these arguments.

    show: function(
      c,    // content or path to AJAX source
      u,    // use AJAX (= 1)
      w,    // width (auto = 0)
      h,    // height (auto = 0)
      a,    // animate (= 1)
      t,    // time to live (secs) or 0
      ext
      )
    {
      if (!this.is_webmaster()) {
        switch(c) {
          case 'login.php':
          case 'mlist_request_dlg.php':
          case 'invalid_pwd_conf_dlg.php':
          case 'my_alert.php':
          case 'event_details_dlg.php':
          case 'add_anti_clash_dlg.php':
          case 'view_image_dlg.php':
          // These should be replaced by a check on whether logged in to webmail
          case 'no_email_selected.php':
          case 'get_email_dlg.php':
          case 'send_reply_dlg.php':
          case 'email_contact_form.php':
          case 'upload_attachment.php':
            //alert('show(c=' + c + ', u=' + u + ', w=' + w + ', h=' + h + ', a=' + a + ', t=' + t + ', ext=' + ext + ')');
            break;
          default:
            return;
        }
      }

      if (slideshowTimer == undefined) {
        slideshowTimer = 0;
      } else if (slideshowTimer) {
        clearTimeout(slideshowTimer);
        slideshowTimer = 0;
      }

      // If not initialised, initialise
      if (!f) {
        // Create containing DIV and assign id
        p = document.createElement('div');
        p.id = 'tinybox';
        p.style.overflow = 'hidden';
        // Create mask DIV and assign id
        m = document.createElement('div');
        m.id = 'tinymask';
        // Create content DIV and assign id
        b = document.createElement('div');
        b.id = 'tinycontent';
        // Attach the mask and container DIVs to the document body
        document.body.appendChild(m);
        document.body.appendChild(p);
        // Attach the content DIV to the container DIV
        p.appendChild(b);
        // When the mask is clicked, hide the box
        m.onclick = TINY.box.hide;
        // When the window is resized, resize the box
        window.onresize = TINY.box.resize;
        // Flag as initialised
        f = 1
      }

      if (!a && !u) {
        // Not animated and not using AJAX
        p.style.width = w ? w + 'px' : 'auto';
        p.style.height = h ? h + 'px' : 'auto';
        p.style.lineHeight = '16px';
        p.style.backgroundImage = 'none';
        p.style.position = 'fixed';
        p.style.zIndex = '10000';
        b.innerHTML = c
      } else {
        // Animated or using AJAX - set to starting size and hide
        b.style.display = 'none'; // don't display content until ready
        // b.innerHTML = '<p class="new_dlg">Please wait...</p>';
        //if (w || h) {
        p.style.width = w ? w + 'px' : 'auto';
        p.style.height = h ? h + 'px' : 'auto';
        //} else {
        //  p.style.width = p.style.height = '100px';
        //}
        p.style.lineHeight = '16px';
        p.style.position = 'fixed';
        p.style.zIndex = '10000';
      }

      // Mask the browser window
      //alert('calling mask');
      this.mask();

      // Store arguments as instance variables
      ic = c;
      iu = u;
      iw = w;
      ih = h;
      //alert('iw = ' + iw + ', ih = ' + ih);
      ia = a;
      if (ext == undefined) {
        iext = null;
      } else {
        iext = ext;
      }

      // Set mask opacity
      //alert('setting opacity');
      this.alpha(m, 1, 40, 3);

      // Set timeout from time to live
      if (t) {
        boxTimer = setTimeout(
        function(){
          TINY.box.hide()
        },
        1000 * t
        )
      }
    },

    // refill is like fill, but it allows an indefinite number of arguments,
    // each giving the name of a variable to be posted, which should be the
    // same as the id of the element in the dialog that contains the value.
    // c, w and h are as for the fill function. We also simplify things by
    // assuming u = 1, a = 1. When working, this function should replace fill,
    // which should be treated as a special case. Note that we switch from GET
    // in fill to POST, which is much more secure.

    refill: function(c, w, h) {

      //alert('refill(' + c + ', ' + w + ', ' + h + ')');

      // Force automatic height
      if (h == undefined) {
        h = 0;
      }

      // Create argument list
      var v = null, args = null;
      for (var i = 3; i < arguments.length; i++) {
        if (i == 3) {
          args = arguments[i];
        } else {
          args += '&' + arguments[i];
        }
        // Retrieve the value of the argument from the dialog
        v = T$(arguments[i]);
        if (v === null) {
          //alert('Cannot find element with id ' + arguments[i]);
          return;
        }
        args += '=' + v.value;
      }
      //alert('args = ' + args);
      // Append any static arguments
      if (iext != null) {
        if (args == null) {
          args = iext;
        } else {
          args += '&' + iext;
        }
      }

      //alert('args = ' + args);

      // Initially container is empty
      p.style.backgroundImage = '';

      // Create HTTP Request
      var x = window.XMLHttpRequest
        ? new XMLHttpRequest()
        : new ActiveXObject('Microsoft.XMLHTTP');

      // Create response handler
      x.onreadystatechange = function() {
        if (x.readyState == 4 && x.status == 200) {
          if (x.responseText == 'NOT ALLOWED') {
            TINY.box.hide();
          } else {
            TINY.box.psh(x.responseText, w, h, 1)
          }
        }
      };

      // Open connection to server
      //alert('opening ' + c + ' for POST');
      x.open('POST', c, 1);
      // Set header for pseudo-form - otherwise it won't work
      x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      // Post the arguments
      //alert('posting ' + args);
      x.send(args);
    },

    // This is an alternative version of refill, in which the additional
    // arguments are listed explicitly as a 'POST' string (e.g. this=123&that=456')
    replace: function(c, w, h, args) {

      // Force automatic height
      if (h == undefined) {
        h = 0;
      }

      // Initially container is empty
      p.style.backgroundImage = '';

      // Create HTTP Request
      var x = window.XMLHttpRequest
        ? new XMLHttpRequest()
        : new ActiveXObject('Microsoft.XMLHTTP');

      // Create response handler
      x.onreadystatechange = function() {
        if (x.readyState == 4 && x.status == 200) {
          if (x.responseText == 'NOT ALLOWED') {
            TINY.box.hide();
          } else {
            TINY.box.psh(x.responseText, w, h, 1)
          }
        }
      };

      // Open connection to server
      //alert('opening ' + c + ' for POST');
      x.open('POST', c, 1);
      // Set header for pseudo-form - otherwise it won't work
      x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      // Post the arguments
      //alert('posting ' + args);
      x.send(args)
    },

    fill: function(
      c,    // required content
      u,    // use AJAX to get it
      w,    // width of window
      h,    // height of window
      a     // whether animated
      )
    {
      if (u) {
        // This is OK if all we want to do is retrieve an HTML file and put it
        // into the box, but if we want to post some information back and get a
        // variable response (e.g. logging in) we will need to use something
        // more sophisticated.
        p.style.backgroundImage = '';

        // Set up request
        var x = window.XMLHttpRequest
          ? new XMLHttpRequest()
          : new ActiveXObject('Microsoft.XMLHTTP');

        // Process response
        x.onreadystatechange = function() {
          if (x.readyState == 4 && x.status == 200) {
            //alert('responsetext = ' + x.responseText)
            if (x.responseText == 'NOT ALLOWED') {
              TINY.box.hide();
            } else {
              //alert('calling psh');
              TINY.box.psh(x.responseText, w, h, a)
            }
          }
        };

        //alert('opening ' + c + ' for post');
        x.open('POST', c, 1);  // open channel
        args = iext == undefined ? null : iext;
        // only needed if args not null, but probably harmless
        x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        //alert('sending args ' + args);
        x.send(args)          // send request
      } else {
        // Local content
        this.psh(c, w, h, a)
      }
    },

    psh: function(c, w, h, a) {
      //alert('psh: c = ' + c + ', w = ' + w + ', h = ' + h + ', a = ' + a);
      if (a) {  // Animation required
        if (!w || !h) { // Auto-size required
          // ***** Some oddities in here - sort out ***
          var x = p.style.width,
            y = p.style.height;
            b.innerHTML = c;
          // If either width or height supplied, use it
          p.style.width = w ? w + 'px' : '';
          p.style.height = h ? h + 'px' : '';
          b.style.display = '';
          w = parseInt(b.offsetWidth);
          h = parseInt(b.offsetHeight);
          b.style.display = 'none';
          p.style.width = x;
          p.style.height = y;
          p.style.lineHeight = '16px';
        } else {
          b.innerHTML = c
        }
        this.size(p, w, h)
      } else {
        b.innerHTML = 'Please wait!'
        p.style.backgroundImage = 'none'
      }
    },

    hide: function() {
      if (slideshowTimer) {
        clearTimeout(slideshowTimer);
        slideshowTimer = 0;
      }
      TINY.box.alpha(p, -1, 0, 3)
    },

    resize: function() {
      TINY.box.pos();
      TINY.box.mask()
    },

    mask: function() {
      m.style.height = TINY.page.total(1) + 'px';
      m.style.width = '';
      m.style.width = TINY.page.total(0) + 'px';
    },

    pos: function() {
      // Simplified height calculation uses the height of the browser window
      // rather than the height of the page. Since the page can be a lot taller
      // than the browser window this has the merit of keeping the dialog
      // inside the viewable area! The only drawback is that if the page is
      // shorter than the window it could be outside the page. This could be
      // fixed but it's hardly worth the effort.
      // var t = (TINY.page.height() / 2) - (p.offsetHeight / 2);
      // t = t < 10 ? 10 : t;
      // p.style.top = (t + TINY.page.top()) + 'px';
      // p.style.top = (window.innerHeight / 2) - (p.offsetHeight / 2) + 'px';
      // p.style.top = (TINY.page.height() / 2) - (p.offsetHeight / 2) + 'px';
      // alert('TINY.page.height() = ' + TINY.page.height());
      p.style.top = (TINY.page.height() / 2) - (p.offsetHeight / 2) + 'px';
      p.style.left = (TINY.page.width() / 2) - (p.offsetWidth / 2) + 'px';
    },

    alpha: function(e, d, a) {
      clearInterval(e.ai);
      if (d == 1) {
        e.style.opacity = 0;
        e.style.filter = 'alpha(opacity=0)';
        e.style.display = 'block';
        this.pos()
      }
      e.ai = setInterval(
        function() {
          TINY.box.ta(e, a, d)
        },
        20
      )
    },

    ta: function(e, a, d) {
      //alert('ta(' + e + ',' + a + ',' + d + ')')
      var o = Math.round(e.style.opacity * 100);
      if (o == a) {
        //alert('ta: opacity limit reached');
        clearInterval(e.ai);
        if (d == -1) {
          e.style.display = 'none';
          // e == p ? TINY.box.alpha(m, -1, 0, 2) : b.innerHTML = p.style.backgroundImage = ''
          if (e == p) {
            TINY.box.alpha(m, -1, 0, 2)
          } else {
            b.innerHTML = p.style.backgroundImage = '';
          }
        } else {
          // e == m ? this.alpha(p, 1, 100) : TINY.box.fill(ic, iu, iw, ih, ia)
          if (e == m) {
            this.alpha(p, 1, 100)
          } else {
            //alert('ta: filling box with ' +  ic);
            TINY.box.fill(ic, iu, iw, ih, ia)
          }
        }
      } else {
        var n = a;
        //var n = Math.ceil((o + ((a - o) * 1)));
        n = n == 1 ? 0 : n;
        e.style.opacity = n / 100;
        e.style.filter = 'alpha(opacity=' + n + ')'
      }
    },

    size: function(e, w, h) {
      e = typeof e == 'object' ? e : T$(e);
      clearInterval(e.si);
      var ow = e.offsetWidth,
        oh = e.offsetHeight,
        wo = ow - parseInt(e.style.width),
        ho = oh - parseInt(e.style.height);
      var wd = ow - wo > w ? 0 : 1,
        hd = (oh - ho > h) ? 0 : 1;
        e.si = setInterval(
          function() {
            TINY.box.ts(e, w, wo, wd, h, ho, hd)
          },
          20
        )
    },

    ts: function(e, w, wo, wd, h, ho, hd) {
      var ow = e.offsetWidth - wo,
        oh = e.offsetHeight-ho;
      if (ow == w && oh == h) {
        clearInterval(e.si);
        p.style.backgroundImage = 'none';
        b.style.display = 'block'
      } else {
        if (ow != w) {
          var n = ow + ((w-ow)*.5);
          e.style.width = wd ? Math.ceil(n) + 'px' : Math.floor(n) + 'px'
        }
        if (oh != h) {
          var n = oh + ((h - oh) * .5);
          e.style.height = hd ? Math.ceil(n) + 'px' : Math.floor(n) + 'px'
        }
        this.pos()
      }
    },

    // Set access and is_webmaster are used to prevent unnecessary calls to the
    // server to get the user's status. However, is_webmaster == true is not
    // conclusive proof that the user has webmaster's privileges, which should
    // also be checked on the server.
    set_access: function(level) {
      access = level;
      //alert('access = ' + access)
    },

    is_webmaster: function() {
      return access >= 4 ? true : false
    }
  }
} ();

// TINY.toolbox is a cut-down version of TINY.box
TINY.toolbox = function() {
  var
    p,          // containing DIV (p for picture?)
    b,          // content div (b for body?)
    ic,         // content
    iu,         // use AJAX
    iw,         // width
    ih,         // height
    ia,         // animate
    edit_mode,
    user_selection,
    init;

  return {
    show: function(level) {
      if (init == undefined) {
        init = true;
        edit_mode = false;
        user_selection = null;
      }
      c = 'toolbox.php';
      // Create containing DIV and assign id
      p = document.createElement('div');
      p.id = 'toolbox';
      // Create content DIV and assign id
      b = document.createElement('div');
      b.id = 'toolcontent';
      // Attach the container DIV to the document body
      document.body.appendChild(p);
      // Attach the content DIV to the container DIV
      p.appendChild(b);
      // Store arguments as instance variables
      ic = c;
      iw = 160;
      ih = 200;
      // ih = level > 4 ? 200 : 180;
      // When the window is resized, resize the box
      // window.onresize = TINY.toolbox.resize;

      b.style.display = 'block';
      b.style.backgroundColor = '#eeeeff'
      b.contentEditable = false;

      p.style.width = iw + 'px';
      p.style.height = ih + 'px'
      p.style.top = '0px';
      p.style.left = '0px';
      p.style.backgroundImage = '';
      p.style.backgroundColor = '#eeeeff'
      // p.style.overflow = 'auto';
      p.contentEditable = false;
      // p.onmousedown = function(event){dragStart(event, 'toolbox')};

      // Set up request
      var x = window.XMLHttpRequest
        ? new XMLHttpRequest()
        : new ActiveXObject('Microsoft.XMLHTTP');

      // Process response
      x.onreadystatechange = function() {
        if (x.readyState == 4 && x.status == 200) {
          b.innerHTML = x.responseText;
        }
      };
      x.open('GET', c, 1);  // open channel
      x.send(null)          // send request

      //TINY.box.show('toobox.php',1,100,200,1)
    },
    edit_doc: function() {
      //alert('edit_doc called');
      T$('main').contentEditable = true;
      T$('toolbox').contentEditable = false;
      // Note that we no longer set designMode as in the previous (IFRAME)
      // version, since it seems to make everything editable, regardless of
      // contentEditable settings. By using just contentEditable on its own we
      // seem to be able to avoid making the toolbox editable. Not sure it
      // works everywhere though.
      document.addEventListener('keypress', TINY.page.keyPressHandler, false);
      this.show_borders(true);
      edit_mode = true;
    },
    save_doc: function() {
      edit_mode = false;
      //alert('save_doc called');
      document.removeEventListener('keypress', TINY.page.keyPressHandler, false);
      this.show_borders(false);
      //alert('468');
      var divs = document.getElementsByTagName('div');
      //var divs = document.getElementsByTagName('div');
      //alert('470');
      var numDivs = divs.length;
      //alert('numDivs = ' + numDivs);
      var i = 0;
      var repFound = false;
      var args = '';
      // Convert to argument list for POST operation
      for (; i < numDivs; i++) {
        //alert('Checking div[' + i + ']');
        var divElement = divs[i];
        if (divElement.className == 'replaceable_text') {
          //alert('replaceable');
          if (!repFound) {
            repFound = true;
            //var args = 'domain=' + curDomain;
          } else {
            args += '&';
          }
          // Add an insertion point at the end of the editable area if there isn't
          // one already
          if (divElement.innerHTML.substr(-4) != '</p>') {
            divElement.innerHTML += '<p></p>';
          }
          //alert('innerHTML = ' + divElement.innerHTML)
          args += divElement.id + '=' + encodeURIComponent(divElement.innerHTML);
        }
      }
      T$('main').contentEditable = false;

      // Abort if nothing replaceable found
      if (!repFound) {
        //alert('Nothing replaceable found');
        return;
      }

      // Open a request to the updater script and send the argument list
      var x = createXmlHttpRequest();
      x.open("POST", 'update_site_text.php', true);
      x.onreadystatechange = function() {
        if (x.readyState == 4 && x.status == 200) {
          TINY.toolbox.edit_doc();
          TINY.box.show('confirm_save_dlg.php', 1, 250, 115, 1)
        }
      };
      x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      x.send(args);
    },

    add_page: function() {
      TINY.box.show('new_page_dlg.php',1,385,410,1);
    },
    remove_page: function() {
      TINY.box.show('rem_page_dlg.php',1,300,180,1);
    },
    undo: function() {
      document.execCommand('undo', false, null);
    },
    redo: function() {
      document.execCommand('redo', false, null);
    },
    embolden: function() {
      document.execCommand('bold', false, null)
    },
    italicise: function() {
      document.execCommand('italic', false, null)
    },
    underline: function() {
      document.execCommand('underline', false, null)
    },
    strikethrough: function() {
      document.execCommand('strikethrough', false, null)
    },
    remove_format: function() {
      document.execCommand('removeformat', false, null)
    },
    subscript: function() {
      document.execCommand('subscript', false, null)
    },
    superscript: function() {
      document.execCommand('superscript', false, null)
    },
    heading_1: function() {
      document.execCommand('heading', false, 'H1')
    },
    heading_2: function() {
      document.execCommand('heading', false, 'H2')
    },
    heading_3: function() {
      document.execCommand('heading', false, 'H3')
    },
    heading_4: function() {
      document.execCommand('heading', false, 'H4')
    },
    format_as_para: function() {
      document.execCommand('formatblock', false, 'P')
    },
    justify_left: function() {
      document.execCommand('justifyleft', false, null)
    },
    justify_center: function() {
      document.execCommand('justifycenter', false, null)
    },
    justify_right: function() {
      document.execCommand('justifyright', false, null)
    },
    // This no longer does justification - instead it adds 'clear:both' to the
    // style of the selection.
    justify_full: function() {
      var sel = window.getSelection();
      parent_node = sel.anchorNode.parentNode;
      // Find closest ancestor P or Hn (may extend later)
      var pnn = parent_node.nodeName;
      var count = 0;
      while (pnn != 'P' && pnn != 'H4' && pnn != 'H3' && pnn != 'H2' && pnn != 'H1') {
        if (++count == 20 || pnn == 'BODY') {
          return false;
        }
        parent_node = parent_node.parentNode;
        pnn = parent_node.nodeName;
      }
      parent_node.setAttribute('style', parent_node.getAttribute('style') + 'clear:both');
      return true;
    },
    insert_lorem: function() {
      // Generate lorem ipsum text
      var str = 'lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur excepteur sint occaecat cupidatat non proident sunt in culpa qui officia deserunt mollit anim id est laborum';
      var words = str.split(' ');
      var res = '';
      var len = Math.floor(Math.random() * 100);
      var comma = Math.floor(Math.random() * 15)
      for (var i = 0; i < len; i++) {
        var r = Math.floor(Math.random() * words.length);
        if (i == comma) {
          res += ',';
          comma += Math.floor(Math.random() * 15);
        }
        res += ' ' + words[r];
      }
      document.execCommand('insertHTML', false, 'Lorem ipsum dolor sit amet' + res + '.');
    },
    insert_hr: function() {
      //document.execCommand('inserthorizontalrule', false, null)
      document.execCommand('insertHTML', false, '<hr width="100%" height="1"')
    },
    insert_mdash: function() {
      //document.execCommand('inserthorizontalrule', false, null)
      document.execCommand('insertHTML', false, '&mdash;')
    },
    insert_copyright: function() {
      //document.execCommand('inserthorizontalrule', false, null)
      document.execCommand('insertHTML', false, '&copy;')
    },
    indent: function() {
      document.execCommand('indent', false, null)
    },
    outdent: function() {
      document.execCommand('outdent', false, null)
    },
    show_add_subcat_dlg: function(prodcat) {
      TINY.box.show('add_subcat_dlg.php',1,350,215,1,0,'prodcat_id=' + prodcat);
    },
    show_change_prod_image_dlg: function(prodid) {
      TINY.box.show('change_prod_image_dlg.php',1,350,170,1,0,'prod_id=' + prodid);
    },
    show_add_product_dlg: function(prodcat) {
      TINY.box.show('add_product_dlg.php',1,485,575,1,0,'prodcat_id=' + prodcat);
    },
    show_edit_subproduct_dlg: function(prodId) {
      // First we must retrieve the id of the subproduct from the document
      // var selector = document.getElementById('subprod_id_' + prodId);
      var selector = window.frames[0].document.getElementById('subprod_id_' + prodId);
      if (!selector) {
        // Something wrong with page
        return;
      }
      var subproduct_id = selector.value;
      TINY.box.show('edit_subproduct_dlg.php',1,350,195,1,0,'product_id=' + prodId + '&subproduct_id=' + subproduct_id);
    },
    show_add_subproduct_dlg: function(prodId) {
      TINY.box.show('add_subproduct_dlg.php',1,350,190,1,0,'product_id=' + prodId);
    },
    show_delete_subprod_dlg: function(prodId) {
      var selector = window.frames[0].document.getElementById('subprod_id_' + prodId);
      if (!selector) {
        // Something wrong with page
        return;
      }
      var subproduct_id = selector.value;
      TINY.box.show('delete_subproduct_dlg.php',1,350,160,1,0,'product_id=' + prodId + '&subproduct_id=' + subproduct_id);
    },
    show_add_department_dlg: function(prodcat) {
      TINY.box.show('add_department_dlg.php',1,400,215,1,0,'prodcat_id=' + prodcat);
    },
    show_delete_prodcat_dlg: function(prodcat) {
      TINY.box.show('delete_prodcat_dlg.php',1,300,140,1,0,'prodcat_id=' + prodcat);
    },
    show_edit_prodcat_dlg: function(prodcat) {
      TINY.box.show('edit_prodcat_dlg.php',1,450,255,1,0,'prodcat_id=' + prodcat);
    },
    show_edit_product_dlg: function(prodId) {
      TINY.box.show('edit_product_dlg.php',1,485,500,1,0,'product_id=' + prodId);
    },
    show_edit_bundle_dlg: function(prodId) {
      TINY.box.show('edit_bundle_dlg.php',1,480,500,1,0,'product_id=' + prodId);
    },
    show_delete_product_dlg: function(prodId) {
      TINY.box.show('delete_product_dlg.php',1,300,115,1,0,'product_id=' + prodId);
    },
    show_event_details_dlg: function(eventId) {
      TINY.box.show('event_details_dlg.php',1,350,430,1,0,'event_id=' + eventId);
    },
    show_add_anti_clash_dlg: function() {
      //alert('Adding entry');
      TINY.box.show('add_anti_clash_dlg.php',1,350,430,1,0);
    },
    show_insert_link_dlg: function() {
      //alert('testing');
      if (!this.check_editable()) {
        alert('Please select the text you wish to link and try again!');
        return;
      } else {
        // Set the selection into TINY.page so it can be retrieved when
        // eventually inserting the link - at that point we will need the
        // selection object, not just the text.
        var sel = window.getSelection();
        if (sel.isCollapsed) {
          alert('Please select the text you wish to link and try again!');
          return;
        } else {
          TINY.page.set_selection(sel);
          // It makes sense to limit dialog heights to 500 px so that screens
          // with a height of 768 overall can accommodate them, even with
          // toolbars and other junk.
          TINY.box.show('insert_link_dlg.php',1,385,500,1,0,'link_text=' + sel.toString());
        }
      }
    },
    show_insert_image_dlg: function() {
      var node_name, offset, anchor_node, class_name, parent_node_name;
      var sel = window.getSelection();
      // alert('1. Anchor node: ' + sel.anchorNode.nodeName);
      // Must be a single insertion point - if not, use the start
      if (!sel.isCollapsed) {
        sel.collapseToStart();
      }
      // Get the column type
      var column_type = TINY.page.getColumnType(sel);
      if (!column_type) {
        return;
      }
      //alert('2. Anchor node: ' + sel.anchorNode.nodeName);
      anchor_node = sel.anchorNode;
      offset = sel.anchorOffset;
      node_name = anchor_node.nodeName;
      class_name = anchor_node.className;
      parent_node_name = anchor_node.parentNode.nodeName;
      //alert('3. Anchor node: ' + sel.anchorNode.nodeName);
      if (node_name == 'BODY') {
        TINY.page.showAlert('Error', 'Please indicate where you would like to insert the image and try again!');
        return;
      }
      if (
        (node_name == 'DIV' && class_name == 'image_container') ||
        (node_name == 'A' && anchor_node.parentNode.className == 'image_container')
      ) {
        TINY.page.showAlert('Error', 'You cannot add a second image here.');
        return;
      }
      //alert('4. Anchor node: ' + sel.anchorNode.nodeName);
      // Display the image insertion dialog
      TINY.page.set_selection(sel);
      //alert('4a. Anchor node: ' + sel.anchorNode.nodeName);
      TINY.box.show('insert_image.php',1,385,500,1,0,'column_type=' + column_type);
    },
    show_insert_table_dlg: function() {
      var node_name, offset, anchor_node, class_name, parent_node_name;
      var sel = window.getSelection();
      // alert('1. Anchor node: ' + sel.anchorNode.nodeName);
      // Must be a single insertion point - if not, use the start
      if (!sel.isCollapsed) {
        sel.collapseToStart();
      }
      anchor_node = sel.anchorNode;
      offset = sel.anchorOffset;
      node_name = anchor_node.nodeName;
      class_name = anchor_node.className;
      parent_node_name = anchor_node.parentNode.nodeName;
      if (node_name == 'BODY') {
        TINY.page.showAlert('Error', 'Please indicate where you would like to insert the table and try again!');
        return;
      }
      if (
        (node_name == 'DIV' && class_name == 'image_container') ||
        (node_name == 'A' && anchor_node.parentNode.className == 'image_container')
      ) {
        TINY.page.showAlert('Error', 'You cannot add a table here.');
        return;
      }
      // Display the table insertion dialog
      TINY.page.set_selection(sel);
      TINY.box.show('insert_table.php',1,275,225,1,0);
    },
    show_insert_component_dlg: function() {
      var node_name, offset, anchor_node, class_name, parent_node_name;
      var sel = window.getSelection();
      // alert('1. Anchor node: ' + sel.anchorNode.nodeName);
      // Must be a single insertion point - if not, use the start
      if (!sel.isCollapsed) {
        sel.collapseToStart();
      }
      anchor_node = sel.anchorNode;
      offset = sel.anchorOffset;
      node_name = anchor_node.nodeName;
      class_name = anchor_node.className;
      parent_node_name = anchor_node.parentNode.nodeName;
      if (node_name == 'BODY') {
        TINY.page.showAlert('Error', 'Please indicate where you would like to insert the component and try again!');
        return;
      }
      if (
        (node_name == 'DIV' && class_name == 'image_container') ||
        (node_name == 'A' && anchor_node.parentNode.className == 'image_container')
      ) {
        TINY.page.showAlert('Error', 'You cannot add a component here.');
        return;
      }
      // Display the table insertion dialog
      TINY.page.set_selection(sel);
      TINY.box.show('insert_component.php',1,275,150,1,0);
    },
    rename_page: function() {
      TINY.box.show('rename_page_dlg.php',1,400,260,1);
    },
    switch_page: function() {
      TINY.box.show('switch_page_dlg.php', 1, 200, 450, 1);
    },
    log_out: function() {
      TINY.box.show('logout_dlg.php', 1, 300, 135, 1);
    },
    show_borders: function(newState) {
      // Set layout borders for replaceable areas and image containers
      var divs = document.getElementsByTagName('div');
      var numDivs = divs.length;
      for (var i = 0; i < numDivs; i++) {
        var divElement = divs[i];
        if (divElement.className == 'replaceable_text') {
          divElement.style.outline = (newState ? '#bbbbff solid thin' : 'none');
        } else if (divElement.className == 'image_container') {
          divElement.style.outline = (newState ? '#bbffbb solid thin' : 'none');
        } else if (divElement.className == 'uneditable') {
          divElement.style.outline = (newState ? '#ffbbbb solid thin' : 'none');
        }
      }
      var iframes = document.getElementsByTagName('iframe');
      var numIframes = iframes.length;
      for (var i = 0; i < numIframes; i++) {
        var iframeElement = iframes[i];
        if (iframeElement.className == 'uneditable') {
          iframeElement.style.outline = (newState ? '#ffbbbb solid thin' : 'none');
        }
      }

      // Set layout borders for TDs
      var cells = document.getElementsByTagName('td');
      var numCells = cells.length;
      for (var i = 0; i < numCells; i++) {
        var cellNode = cells[i];
        if (this.is_replaceable(cellNode)) {
          cellNode.style.outline = (newState ? '#bbffbb solid thin' : 'none');
        }
      }

      // Add or remove paragraph end markers
      var paras = document.getElementsByTagName('p');
      var numParas = paras.length;
      for (var i = 0; i < numParas; i++) {
        var paraNode = paras[i];
        if (this.is_replaceable(paraNode)) {
          if (newState) {
            paraNode.style.backgroundImage = 'url(images/end_para.gif)';
            paraNode.style.backgroundRepeat = 'no-repeat';
            paraNode.style.backgroundPosition = 'bottom right';
          } else {
            paraNode.style.backgroundImage = '';
            paraNode.style.backgroundRepeat = '';
            paraNode.style.backgroundPosition = '';
          }
        }
      }

      var paras = document.getElementsByTagName('h1');
      var numParas = paras.length;
      for (var i = 0; i < numParas; i++) {
        var paraNode = paras[i];
        if (this.is_replaceable(paraNode)) {
          if (newState) {
            paraNode.style.backgroundImage = 'url(images/end_para.gif)';
            paraNode.style.backgroundRepeat = 'no-repeat';
            paraNode.style.backgroundPosition = 'bottom right';
          } else {
            paraNode.style.backgroundImage = '';
            paraNode.style.backgroundRepeat = '';
            paraNode.style.backgroundPosition = '';
          }
        }
      }

      var paras = document.getElementsByTagName('h2');
      var numParas = paras.length;
      for (var i = 0; i < numParas; i++) {
        var paraNode = paras[i];
        if (this.is_replaceable(paraNode)) {
          if (newState) {
            paraNode.style.backgroundImage = 'url(images/end_para.gif)';
            paraNode.style.backgroundRepeat = 'no-repeat';
            paraNode.style.backgroundPosition = 'bottom right';
          } else {
            paraNode.style.backgroundImage = '';
            paraNode.style.backgroundRepeat = '';
            paraNode.style.backgroundPosition = '';
          }
        }
      }

      var paras = document.getElementsByTagName('h3');
      var numParas = paras.length;
      for (var i = 0; i < numParas; i++) {
        var paraNode = paras[i];
        if (this.is_replaceable(paraNode)) {
          if (newState) {
            paraNode.style.backgroundImage = 'url(images/end_para.gif)';
            paraNode.style.backgroundRepeat = 'no-repeat';
            paraNode.style.backgroundPosition = 'bottom right';
          } else {
            paraNode.style.backgroundImage = '';
            paraNode.style.backgroundRepeat = '';
            paraNode.style.backgroundPosition = '';
          }
        }
      }

      var paras = document.getElementsByTagName('h4');
      var numParas = paras.length;
      for (var i = 0; i < numParas; i++) {
        //alert('h4 found');
        var paraNode = paras[i];
        if (this.is_replaceable(paraNode)) {
          if (newState) {
            paraNode.style.backgroundImage = 'url(images/end_para.gif)';
            paraNode.style.backgroundRepeat = 'no-repeat';
            paraNode.style.backgroundPosition = 'bottom right';
          } else {
            paraNode.style.backgroundImage = 'none';
            paraNode.style.backgroundRepeat = '';
            paraNode.style.backgroundPosition = '';
          }
        }
      }
    },
    check_editable: function() {
      var sel = window.getSelection();
      //alert('typeof sel = ' + typeof sel);
      return(this.is_replaceable(sel.isCollapsed ? sel.parentNode : sel.anchorNode));
    },
    is_replaceable: function(node) {
      // We maintain the instance variable is_replaceable.level to keep track of
      // recursion and make sure it doesn't get out of hand. As there's no way of
      // maintaining a class variable (that I know of) we have to reset the value
      // to zero on termination.
      //alert('is_replaceable called');
      if (typeof this.level == 'undefined') {
        // alert('initialising level to zero');
        this.level = 0;
      }
      if (node == null) {
        // alert('Node is null');
        this.level = 0;
        return false;
      }
      // alert(node.nodeName);
      while (node.nodeName != 'BODY' && this.level < 20) {
        // alert('Level is: ' + this.level);
        if (node.nodeName == 'DIV' && node.className == 'replaceable_text') {
          // alert('Ancestor replaceable_text found');
          this.level = 0;
          return true;
        } else {
          node = node.parentNode;
          this.level = parseInt(this.level) + 1;
        }
      }
      this.level = 0;
      return false;
    },
    select_text: function() {
      user_selection = window.getSelection();
      return user_selection;
    },
    check_new_pwd: function(p1, p2) {
      pwd1 = T$(p1).value;
      pwd2 = T$(p2).value;
      if (pwd1 == pwd2) {
        return true;
      } else {
        TINY.box.show('invalid_pwd_conf_dlg.php',1,300,215,1,0);
        return false;
      }
    },
    get_mode: function() {
      return edit_mode;
    }
  }

} ();

TINY.slideshow = function() {

  var
    playlist,
    delay,
    ix;

  return {
    /*
     * startAt is an image id, not a playlist index. This provides compatibility
     * with single image viewing and also allows the playlist to be re-ordered
     * without changing the images displayed by existing links.
     */
    start: function(startAt, auto) {

      TINY.slideshow.clearTimer();
      auto = (auto == 'auto' ? true : false);

      var x = createXmlHttpRequest();
      x.open("POST", 'get_slideshow_dets.php', true);
      x.onreadystatechange = function() {
        if (x.readyState == 4 && x.status == 200) {
          if (x.responseText.substring(0, 4) == 'FAIL') {
            playlist = false;
          } else {
            /*
             * Create the playlist.
             */
            playlist = x.responseText.split('\n');
            /*
             * If delay is, say, 10 secs, first line of playlist is:
             * OK\t10
             */
            var params = playlist[0].split('\t');
            delay = parseInt(params[1]) * 1000;
            /*
             * Get details of the first image
             */
            var vars = TINY.slideshow.getVars(-parseInt(startAt))
            if (vars) {
              var args = 'img_id=' + vars[0];
              var w = parseInt(vars[1]) + 30;
              var h = parseInt(vars[2]) + 45;
              ix = parseInt(vars[3]) + 1;
              var j = ix;
              if (j >= playlist.length) {
                j = 1;
              }
              TINY.box.show('view_image_dlg.php', 1, w, h, 1, 0, args);
              if (auto) {
                TINY.slideshow.clearTimer();
                /*
                 * If the user has a slow connection this can cause problems
                 * because we don't even start downloading until the delay is
                 * completed. It weould be better to download the image during
                 * the dleay. However, the current mechanism for reloading the
                 * viewer gets the image and the surrounding HTML at the same
                 * time. This should be reviewed some time.
                 */
                slideshowTimer = setTimeout('TINY.slideshow.showNext(' + j + ',' + auto + ')', delay);
              }
            } else {
              TINY.slideshow.stop();
              TINY.page.showAlert('Error', 'The requested image has been removed from the lightbox.');
            }
          }
        }
      };
      x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      x.send('start_at=' + startAt);
    },

    showNext: function(i, auto) {

      if (i == undefined || i == 0) {
        i = ix;
      }

      auto = (auto == undefined ? false : true);

      if (playlist.length == 0) {
        TINY.slideshow.stop();
        return;
      }

      if (!auto) {
        TINY.slideshow.clearTimer();
      }

      if (i < 1 || i >= playlist.length - 1) {
        i = 1;
      }

      var vars = TINY.slideshow.getVars(i);
      if (vars) {
        var w = parseInt(vars[1]) + 30;
        var h = parseInt(vars[2]) + 45;
        var id = vars[0];
        var j = parseInt(i) + 1;
        if (j > playlist.length - 1) {
          j = 1;
        }
        ix = j;
        TINY.box.replace('view_image_dlg.php', w, h, 'next_id=' + id);
        if (auto) {
          TINY.slideshow.clearTimer();
          slideshowTimer = setTimeout('TINY.slideshow.showNext(' + j + ',' + auto + ')', delay);
          //alert('Just set timer ' + slideshowTimer);
        }
      } else {
        TINY.slideshow.stop();
      }
    },

    showPrev: function() {
      if (ix < 3) {
        ix = playlist.length - 2;
      } else {
        ix = ix - 2;
      }
      TINY.slideshow.showNext(0);
    },

    getVars: function(id) {
      if (id >= 1 && id < playlist.length - 1 && playlist[id].length > 1) {
        return playlist[id].split('\t');
      } else if (id < 0) {
        // id is a negated image id, not an index on the playlist array.
        id = -parseInt(id);
        for (var i = 1; i < playlist.length - 1; i++) {
          var vars = playlist[i].split('\t');
          if (vars[0] == id) {
            vars[3] = i;  // This allows the slideshow to use playlist indices
            return vars;
          }
        }
      }
      return false;
    },

    clearTimer: function() {
      if (slideshowTimer) {
        clearTimeout(slideshowTimer);
        slideshowTimer = 0;
      }
    },

    stop: function() {
      TINY.box.hide();
    }

  }
} ();

// The methods in the object returned by this function control the relationship
// between the TINY.box object and the page (TINY.page) within which it exists.
TINY.page = function() {

  return {

    top: function() {
      return document.documentElement.scrollTop || document.body.scrollTop
    },

    width: function() {
      return self.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
    },

    height: function() {
      return self.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
    },

    /*
     * Attempt to change the password as requested, giving the current username
     * and password as entered for verification.
     */
    updatePassword: function() {

      //alert('updatePassword called');

      var userName = document.login_details_form.username.value.trim();

      if (userName.length == 0) {
        alert('You did not enter a user name. Please correct this error and try again.');
        return false;
      }
      var curPwd = document.login_details_form.cur_pwd.value.trim();
      if (curPwd.length == 0) {
        alert('You did not enter your current password. Please correct this error and try again.');
        return false;
      }
      var newPwd = document.login_details_form.new_pwd.value.trim();
      if (newPwd.length == 0) {
        alert('You did not enter a new password. Please correct this error and try again.');
        return false;
      }
      var confnewPwd = document.login_details_form.conf_new_pwd.value.trim();
      if (confnewPwd.length == 0) {
        alert('You did not confirm the new password. Please correct this error and try again.');
        return false;
      }
      if (confnewPwd != newPwd) {
        alert('You entered different passwords each time. Please correct this error and try again.');
        return false;
      }

      var args  = 'username=' + userName;
      args += '&curpwd=' + curPwd;
      args += '&newpwd=' + newPwd;

      var xmlHttp = createXmlHttpRequest();
      xmlHttp.open("POST", 'do_update_pwd.php', true);

      xmlHttp.onreadystatechange = function() {
        if (xmlHttp.readyState == 4) {
          if (xmlHttp.status == 200) {
            var resp = xmlHttp.responseText;
            if (resp == 'OK') {
              alert('Your password has been changed as requested.');
            }
            else {
              alert('Error: ' + resp);
            }
          }
          else {
            alert('<p class="red"><strong>The server could not be accessed for this operation. This may be due to a temporary internet problem, so please try again later, but do let us know if the problem persists!</strong></p>');
          }
        }
      }

      xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      xmlHttp.send(args);

      return true;
    },

    setStyles: function(numColumns, leftWidth, rightWidth) {

      return true;

      // alert('setStyles(' + numColumns + ', ' + leftWidth + ', ' + rightWidth + ')');
      var divs = document.getElementsByTagName('div');
      var numDivs = divs.length;
      // alert('numDivs = ' + numDivs);
      var lc_done = parseInt(leftWidth) == 0 ? true : false;
      var rc_done = parseInt(rightWidth) == 0 ? true : false;
      if (lc_done && rc_done) {
        return true;
      }
      for (var i = 0; i < numDivs; i++) {
        var className = divs[i].className;
        //alert('divs[' + i + '].className = ' + divs[i].className);
        //alert('divs[' + i + '].style.width = ' + divs[i].style.width);
        if (className == 'left_2column' && parseInt(leftWidth) > 0) {
          divs[i].style.width = leftWidth + 'px';
          //alert('left_column width adjusted');
          if (rc_done) {
            return true;
          }
          lc_done = true;
        } else if (className == 'right_column') {
          divs[i].style.display = parseInt(numColumns) == 1 ? 'none' : 'block';
          //alert('right_column display adjusted');
          if (parseInt(rightWidth) > 0) {
            divs[i].style.width = rightWidth + 'px';
            //alert('right_column width adjusted');
          }
          if (lc_done) {
            return true;
          }
          rc_done = true;
        }
      }
      //alert('No columns found');
      return false;
    },

    total: function(d) {
      var b = document.body,
        e = document.documentElement;
      return d
        ? Math.max(
            Math.max(b.scrollHeight,e.scrollHeight),
            Math.max(b.clientHeight,e.clientHeight)
          )
        : Math.max(
            Math.max(b.scrollWidth,e.scrollWidth),
            Math.max(b.clientWidth,e.clientWidth)
          )
    },

    showAlert: function(hdg, msg) {
      var args = 'title=' + hdg + '&msg=' + msg;
      TINY.box.show('my_alert.php', 1, 300, 155, 1, 0, args);
      // or possibly, if auto works:
      // TINY.box.show('my_alert.php', 1, 0, 0, 1, args);
    },

    showImage: function(image_id) {
      //var props = image_id.split(':');
      TINY.slideshow.start(image_id);
      //alert('image_id = ' + image_id);
      /*
      var props = image_id.split(':');
      var args = 'img_id=' + props[0];
      //alert('args = ' + args);
      var w = parseInt(props[1]) + 30;
      var h = parseInt(props[2]) + 50;
      //alert('w = ' + w + ', h = ' + h);
      TINY.box.show('view_image_dlg.php', 1, w, h, 1, 0, args);
      */
    },

    keyPressHandler: function(evt) {
      evt = evt || window.event;
      if (evt) {
        var keyCode = evt.charCode || evt.keyCode;
        // Check for delete or backspace key
        if (parseInt(keyCode) == 8 || parseInt(keyCode) == 46) {
          // If an image container has been selected, delete it
          var sel = window.getSelection();
          var anchor_node = sel.anchorNode;
          var node_name = anchor_node.nodeName;
          var class_name = anchor_node.className;
          var parent_node = anchor_node.parentNode;
          if (node_name == 'DIV' && class_name == 'image_container') {
            // The default action is to delect whatever is at the cursor.
            // Unless we prevent it we will delete both the image container
            // (and its contents) and whatever comes next.
            evt.preventDefault();
            // Delete image container
            parent_node.removeChild(anchor_node);
            return false;
          }
        }
        lastKeyCode = keyCode;
      }
      return true;
    },

    insert_link: function(caption, href, target, style, onclick) {

      var anchor_node = this.anchor_node;
      var offset = this.offset;
      var node_name = this.node_name;
      var class_name = this.class_name;
      var parent_node = this.parent_node;
      var parent_node_name = this.parent_node_name;

      // Collapsed selections should have been weeded out already, but check
      // just in case...
      if (this.is_collapsed) {
        return false;
      }
      var focus_node = this.focus_node;
      // this.selection should have been checked for validity when it was
      // stored, but we do it again here just in case.
      if (anchor_node.nodeType != 3 || !anchor_node.isSameNode(focus_node)) {
        this.showAlert('Invalid selection', '<p>Your link text selection is invalid - you cannot select text that is interrupted by links, style changes, images, paragraph ends, etc.</p>');
        return false;
      }
      // As anchorNode and focusNode are the same, we can use anchorOffset
      // and focusOffset (reversed if selected R-L) as the start and end of
      // the selection. As the text is in a text node contained in the parent
      // node, it is the innerHTML of the parent node that we need to change.
      var oa = this.offset;
      var of = this.focus_offset;
      if (of < oa) {
        var tmp = oa;
        oa = of;
        of = tmp;
      }
      var parent = parent_node;

      // Create nodes for text up to (t1) and following (t2) insertion point
      var t1node = document.createTextNode(anchor_node.textContent.substring(0, oa));
      var t2node = document.createTextNode(anchor_node.textContent.substring(of));

      // Create a link element containing required HREF and text
      var link = document.createElement('a');
      link.setAttribute('href', href);
      if (target) {
        link.setAttribute('target', target);
      }
      if (style) {
        link.setAttribute('style', style);
      }
      if (onclick) {
        link.setAttribute('onclick', onclick)
      }
      link.appendChild(document.createTextNode(caption));

      // Create a node containing the new stuff
      var new_node = document.createElement('span');
      new_node.appendChild(t1node);
      new_node.appendChild(link);
      new_node.appendChild(t2node);

      // Remove the current anchorNode and replace it with the t1 and t2 nodes,
      // with the link node in between
      // parent.removeChild(anchorNode);
      parent.replaceChild(new_node, anchor_node);
      //parent.normalize();
      return true;
    },

    set_selection: function(sel) {
      // It's no good storing the selection object, because when they select
      // something else (e.g. an input field in a dialog) the values in the
      // selection object change. What may work is to extract the nodes that
      // the selection object refers to and retrieve them when needed, rather
      // than retrieving the selection object and extracting the references at
      // that point.

      // this.selection = sel; // don't retrieve this - retrieve these instead
      this.anchor_node = sel.anchorNode;
      this.offset = sel.anchorOffset;
      this.node_name = this.anchor_node.nodeName;
      this.class_name = this.anchor_node.className;
      this.parent_node = this.anchor_node.parentNode;
      this.parent_node_name = this.parent_node.nodeName;
      this.is_collapsed = sel.isCollapsed;
      if (!sel.isCollapsed) {
        this.focus_node = sel.focusNode;
        this.focus_offset = sel.focusOffset;
      }
    },
    insertLink: function(form) {
      var link_text, ul, link_type, page, doc, email, html,font_family;
      link_text = form.link_text.value;
      ul = form.show_underline.checked ? true : false;
      font_family = form.local_text_font.value;
      var num_link_options = form.link_type.length;
      for (var i = 0; i < num_link_options; i++) {
        if (form.link_type[i].checked) {
          link_type = form.link_type[i].value;
          break;
        }
      }
      switch(link_type) {
        case 'local':
          var page_id = form.page_sel.value;
          if (parseInt(page_id) < 0) {
            page_id = '364&cat=' + (-parseInt(page_id));
          }
          if (link_text == '') {
            link_text = '[ Please enter link text here ]';
          }
          if (ul) {
            this.insert_link(link_text, '?' + page_id, '_top', 'text-decoration:underline; font-family:' + font_family);
          } else {
            this.insert_link(link_text, '?' + page_id, '_top', 'text-decoration:none; font-family:' + font_family);
          }
          break;
        case 'remote':
          var url = form.url.value;
          if (url == '') {
            this.showAlert('Error', 'You must enter a web address to link to');
            return;
          }
          if (link_text == '') {
            link_text = url;
          }
          if (ul) {
            this.insert_link(link_text, url, '_blank', 'text-decoration:underline; font-family:' + font_family);
          } else {
            this.insert_link(link_text, url, '_blank', 'text-decoration:none; font-family:' + font_family);
          }
          break;
        case 'doc_lib':
          var doc_id = form.doc_sel.value;
          if (link_text == '') {
            link_text = '[Please enter link text here!]';
          }
          if (ul) {
            this.insert_link(link_text, 'get_lib_doc.php?doc_id=' + doc_id, '_top', 'text-decoration:underline; font-family:' + font_family);
          } else {
            this.insert_link(link_text, 'get_lib_doc.php?doc_id=' + doc_id, '_top', 'text-decoration:none; font-family:' + font_family);
          }
          break;
        case 'img_lib':
          var img_id = form.img_sel.value;
          var slideshow = form.slide.value == 'on' ? true : false;
          //alert('img_id = ' + img_id);
          if (link_text == '') {
            link_text = '[Please enter link text here!]';
          }
          if (slideshow) {
            href =  "javascript:TINY.slideshow.start('" + img_id + "','auto')";
          } else {
            href =  "javascript:TINY.slideshow.start('" + img_id + "')";
          }
          if (ul) {
            this.insert_link(link_text, href, '_top', 'text-decoration:underline; font-family:' + font_family);
          } else {
            this.insert_link(link_text, href, '_top', 'text-decoration:none; font-family:' + font_family);
          }
          break;
        case 'mlist':
          if (link_text == '') {
            this.showAlert('Error', 'Please select the text you wish to link and try again.');
            return;
          }
          href = 'javascript:TINY.box.show(\'mlist_request_dlg.php\',1,350,485,1,0,\'' + args + '\');';
          if (ul) {
            this.insert_link(link_text, href, '', 'text-decoration:underline; font-family:' + font_family);
          } else {
            this.insert_link(link_text, href, '', 'text-decoration:none; font-family:' + font_family);
          }
          break;
        case 'email':
          email_addr = form.email_addr.value;
          if (email_addr == '') {
            this.showAlert('Error', 'Please enter an address to send emails to.');
            return;
          }
          if (link_text == '') {
            link_text = email_addr;
          }

          // This is a good idea, but it just doesn't work. The ampersands in
          // the entity encodings are converted by the browser (FF at least)
          // into '&amp;' - apparently when they are retrieved from the DOM.
          // mailto = obfusc(email_addr); // Requires user.js
          // alert('mailto = ' + mailto);

          if (ul) {
            this.insert_link(link_text, 'mailto:' + email_addr, '', 'text-decoration:underline; font-family:' + font_family);
          } else {
            this.insert_link(link_text, 'mailto:' + email_addr, '', 'text-decoration:none; font-family:' + font_family);
          }

          break;
        default:
          this.showAlert('Error', 'Invalid option');
          return;
      }
    },

    strrev: function(string) {
      string = string + '';
      return string.split('').reverse().join('');
    },

    build_email_link: function(str) {
      return('mailto:' + this.strrev(str));
    },

    insert_new_image: function() {

      alert('insert_new_image called');

    },

    insert_table: function(insert_table_form) {

      var anchor_node = this.anchor_node;
      var offset = this.offset;
      var node_name = this.node_name;
      var class_name = this.class_name;
      var parent_node = this.parent_node;
      var parent_node_name = this.parent_node_name;

      // We should be in a #text node or possibly in a P/Hn node if it's empty
      if (node_name == 'P' || node_name == 'H1' || node_name == 'H2' || node_name == 'H3' || node_name == 'H4'|| node_name == 'TD') {
        insert_before_node = anchor_node;
      } else if (node_name != '#text') {
        this.showAlert('Error', 'You cannot insert a table here');
        return;
      } else {
        // Find the closest P/Hn/TD ancestor
        var level = 0;
        for (var cnode = anchor_node; true; cnode = cnode.parentNode) {
          var c = cnode.nodeName;
          if (c == 'P' || c == 'H4' || c == 'H3' || c == 'H2' || c == 'H1' || node_name == 'TD') {
            insert_before_node = cnode;
            break;
          }
          if (c == 'BODY' || ++level > 20) {
            this.showAlert('Error', 'You cannot insert a table here');
            return;
          }
        }
      }

      // Get form data
      var table_disp = insert_table_form.table_disp;
      var count = table_disp.length;
      for (var i = 0; i < count; i++) {
        if (table_disp[i].checked) {
          table_disp = table_disp[i].value;
          break;
        }
      }

      var rows = insert_table_form.num_rows.value;
      var cols = insert_table_form.num_cols.value;

      if (rows < 1) {
        rows = 1;
      }
      if (cols < 1) {
        cols = 1;
      } else if (cols > 9) {
        cols = 2;
      }

      var table = document.createElement('table');
      table.setAttribute('cellspacing', '0');
      table.setAttribute('cellpadding', '2')
      for (var i = 0; i < rows; i++) {
        var row = document.createElement('tr');
        for (var j = 0; j < cols; j++) {
          var cell = document.createElement('td');
          //var para = document.createElement('p');
          var text = document.createTextNode('Row ' + (parseInt(i) + 1) + ', column ' + (parseInt(j) + 1));
          //para.appendChild(text);
          //cell.appendChild(para);
          cell.appendChild(text);
          row.appendChild(cell);
        }
        table.appendChild(row);
      }

      if (table_disp == 'wrap_right') {
        table.setAttribute('style', 'float:left; margin: 10px 15px 10px 0');
      } else if (table_disp == 'wrap_left') {
        table.setAttribute('style', 'float:right; margin: 10px 0 10px 15px');
      } else if (table_disp == 'no_wrap') {
        table.setAttribute('style', 'margin: 10px 0 10px 0');
      } else {
        alert('invalid table position ' + table_disp);
      }

      if (insert_before_node.nodeName == 'TD') {
        if (insert_before_node.hasChildNodes) {
          insert_before_node.insertBefore(table, insert_before_node.firstChild);
        } else {
          insert_before_node.appendChild(table);
        }
      } else {
        insert_before_node.parentNode.insertBefore(table, insert_before_node);
      }

      TINY.toolbox.save_doc();
    },

    insert_component: function(insert_comp_form) {
      // alert('inserting component');
      var anchor_node = this.anchor_node;
      var offset = this.offset;
      var node_name = this.node_name;
      var class_name = this.class_name;
      var parent_node = this.parent_node;
      var parent_node_name = this.parent_node_name;
      // We should be in a #text node or possibly in a P/Hn node if it's empty
      if (node_name == 'P' || node_name == 'H1' || node_name == 'H2' || node_name == 'H3' || node_name == 'H4' || node_name == 'TD') {
        insert_before_node = anchor_node;
      } else if (node_name != '#text') {
        this.showAlert('Error', 'You cannot insert a component here');
        return;
      } else {
        // Find the closest P/Hn/TD ancestor
        var level = 0;
        for (var cnode = anchor_node; true; cnode = cnode.parentNode) {
          var c = cnode.nodeName;
          //alert('level = ' + level + '; node name = ' + c)
          // Possibly we ought to add DIV and IMG to this list so they can add a
          // sequence of images. May come back to this later.
          if (c == 'P' || c == 'H4' || c == 'H3' || c == 'H2' || c == 'H1' || node_name == 'TD') {
            insert_before_node = cnode;
            break;
          }
          if (c == 'BODY' || ++level > 20) {
            this.showAlert('Error', 'You cannot insert a component here');
            return;
          }
        }
      }
      //alert('creating iframe');
      // Create the IFRAME for the component
      var iframe = document.createElement('iframe');
      // Set the IFRAME attributes
      iframe.setAttribute('FRAMEBORDER', '0')
      var src = insert_comp_form.comp_sel.value;
      iframe.setAttribute('src', src);
      var d = new Date();
      var id = 'component_' + d.getTime(); // OK unless they create two components in the same msec
      iframe.setAttribute('id', id);

      // This would put a red line around the component to show it's uneditable.
      // Usually, however, components are in a page or at least section of
      // their own, and the red line is ugly and unnecessary.
      // iframe.setAttribute('class', 'uneditable');

      iframe.setAttribute('style', 'width:100%');
      iframe.setAttribute('onload', 'javascript:adjustComponentFrameHeight("' + id + '", 50);');
      //alert('inserting iframe');
      if (insert_before_node.nodeName == 'TD') {
        if (insert_before_node.hasChildNodes) {
          insert_before_node.insertBefore(iframe, insert_before_node.firstChild);
        } else {
          insert_before_node.appendChild(iframe);
        }
      } else {
        insert_before_node.parentNode.insertBefore(iframe, insert_before_node);
      }
      TINY.toolbox.save_doc();
    },

    insert_image_placeholder: function(insert_image_form, column_type) {
      var anchor_node = this.anchor_node;
      var offset = this.offset;
      var node_name = this.node_name;
      var class_name = this.class_name;
      var parent_node = this.parent_node;
      var parent_node_name = this.parent_node_name;
      // We should be in a #text node or possibly in a P/Hn node if it's empty
      if (node_name == 'P' || node_name == 'H1' || node_name == 'H2' || node_name == 'H3' || node_name == 'H4' || node_name == 'TD') {
        insert_before_node = anchor_node;
      } else if (node_name != '#text') {
        this.showAlert('Error', 'You cannot insert an image or Flash movie here. Please ensure that you have selected a paragraph, header or table cell.');
        return;
      } else {
        // Find the closest P/Hn/TD ancestor
        var level = 0;
        for (var cnode = anchor_node; true; cnode = cnode.parentNode) {
          var c = cnode.nodeName;
          // alert('level = ' + level + '; node name = ' + c)
          // Possibly we ought to add DIV and IMG to this list so they can add a
          // sequence of images. May come back to this later.
          if (c == 'P' || c == 'H4' || c == 'H3' || c == 'H2' || c == 'H1' || c == 'TD') {
            insert_before_node = cnode;
            break;
          }
          if (c == 'BODY' || ++level > 20) {
            this.showAlert('Error', 'You cannot insert an image or Flash movie here. Please ensure that you have selected a paragraph, header or table cell.');
            return;
          }
        }
      }
      // Get form data
      var form = T$(insert_image_form);
      var caption = form.img_caption.value;
      if (caption == null || caption.trim().length == 0) {
        caption = false;
      }
      var title = form.img_title.value;
      if (title == null || title.trim().length == 0) {
        title = 'No title'
      }
      var f_object_type = form.img_or_flash;
      var object_type = null;
      count = f_object_type.length;
      for (var i = 0; i < count; i++) {
        if (f_object_type[i].checked) {
          object_type = f_object_type[i].value;
          break;
        }
      }
      //alert('object_type = ' + object_type);
      var img_disp = form.img_disp;
      var image_disp = null;
      var count = img_disp.length;
      for (var i = 0; i < count; i++) {
        if (img_disp[i].checked) {
          image_disp = img_disp[i].value;
          break;
        }
      }
      var f_link_type = form.link_type;
      var link_type = null;
      count = f_link_type.length;
      for (var i = 0; i < count; i++) {
        if (f_link_type[i].checked) {
          link_type = f_link_type[i].value;
          break;
        }
      }
      var href;
      switch(link_type) {
        case 'none':
          break;
        case 'local':
          // Get local page id
          var page_id = form.page_sel.value;
          if (parseInt(page_id) < 0) {
            page_id = '364&cat=' + (-parseInt(page_id));
          }
          href = '?' + page_id;
          break;
        case 'remote':
          // Get remote web address
          href = form.url.value;
          break;
        default:
          alert('invalid link type ' + link_type);
          return;
      }
      // Get a placeholder image id
      var x = window.XMLHttpRequest
        ? new XMLHttpRequest()
        : new ActiveXObject('Microsoft.XMLHTTP');
      x.onreadystatechange = function() {
        if (x.readyState == 4 && x.status == 200) {
          if (x.responseText.substring(0, 15) == 'OK - IMAGE ID: ') {
            // Get image id for SRC attribute
            var image_id = x.responseText.substr(15);
            var imageDisposition = 'inline'; // default
            var imagePath = 'get_image.php?id=' + image_id;
            if (object_type == 'swf') {
              // Without this get_image.php will try to return a jpeg:
              imagePath += '&type=movie';
              // For IE
              var img = document.createElement('object');
              img.setAttribute('id', 'obj_' + image_id);
              //img.setAttribute('width', '250');
              //img.setAttribute('height', '250');
              //img.setAttribute('style', 'border: 1px solid #cccccc');
              var param = document.createElement('param');
              param.setAttribute('name', 'movie');
              // <PARAM NAME="WMODE" VALUE="transparent">
              param.setAttribute('WMODE','transparent');
              param.setAttribute('menu', true);
              // For the rest
              var embed = document.createElement('embed');
              embed.setAttribute('src', imagePath);
              //embed.setAttribute('width', '250');
              //embed.setAttribute('height', '250');
              embed.setAttribute('id', 'emb_' + image_id);
              // <EMBED SRC="movie.swf" ... WMODE="transparent" ...>
              embed.setAttribute('WMODE', 'transparent');
              embed.setAttribute('menu', true);
              embed.setAttribute('type','application/x-shockwave-flash');
              img.appendChild(param);
              img.appendChild(embed);
              var alert_msg = 'Flash image_id = ' + image_id;
            } else {
              var img = document.createElement('img');
              img.setAttribute('src', imagePath);
              img.setAttribute('id', 'img_' + image_id);
              var alert_msg = 'image_id = ' + image_id;
            }
            img.setAttribute('title', title);
            img.setAttribute('alt', title);
            img.setAttribute('class', 'replaceable_image');
            
//            img.setAttribute('oncontextmenu', 'javascript:TINY.box.show(\'replace_image.php\',1,350,441,1,0,\'column_type=' + column_type + '&image_id=' + image_id + '&up_type=replace\');return false;');

            if (object_type == 'swf') {
	            img.setAttribute('ondblclick', 'javascript:TINY.box.show(\'replace_movie.php\',1,350,192,1,0,\'column_type=' + column_type + '&image_id=' + image_id + '&up_type=replace\');return false;');
            }
	        else {
                img.setAttribute('ondblclick', 'javascript:TINY.box.show(\'replace_image.php\',1,350,441,1,0,\'column_type=' + column_type + '&image_id=' + image_id + '&up_type=replace\');return false;');
            }
               
            var container_node = document.createElement('div');
            container_node.setAttribute('class', 'image_container');
            if (image_disp == 'wrap_right') {
              container_node.setAttribute('style', 'position:relative; z-index:0; float:left; margin: 0 15px ' + (caption ? '5px' : '10px') + ' 0');
            } else if (image_disp == 'wrap_left') {
              container_node.setAttribute('style', 'position:relative; z-index:0; float:right; margin: 0 0 ' + (caption ? '5px' : '10px') + ' 15px');
            } else if (image_disp == 'no_wrap') {
              container_node.setAttribute('style', 'position:relative; z-index:0; margin: 10px 0 ' + (caption ? '5px' : '10px') + ' 0');
            } else {
              alert('invalid image position ' + image_disp);
            }
            if (link_type != 'none') {
              var anode = document.createElement('a');
              anode.setAttribute('href', href);
              anode.setAttribute('target', (link_type == 'local' ? '_top' : '_blank'));
              anode.appendChild(img);
              container_node.appendChild(anode);
            } else {
              container_node.appendChild(img);
            }
            if (caption) {
              caption = caption.trim();
              //alert('creating caption text node');
              caption_node = document.createTextNode(caption);
              //alert('creating caption para node');
              caption_para_node = document.createElement('P');
              //alert('Setting para attributes');
              caption_para_node.setAttribute('class', 'small');
              caption_para_node.setAttribute('style', 'margin-bottom:0;text-align:center');
              //alert('Appending caption text node to caption para node');
              caption_para_node.appendChild(caption_node);
              //alert('Appending caption para node to container node');
              container_node.appendChild(caption_para_node);
              //alert('Caption processing complete');
            }
            if (insert_before_node.nodeName == 'TD') {
              if (insert_before_node.hasChildNodes) {
                insert_before_node.insertBefore(container_node, insert_before_node.firstChild);
              } else {
                insert_before_node.appendChild(container_node);
              }
            } else {
              insert_before_node.parentNode.insertBefore(container_node, insert_before_node);
            }
            if (object_type == 'swf') {
	          /*
              var overlay =  document.createElement('DIV');
              overlay.setAttribute('style', 'display:block; position:absolute; z-index:1; top:0; left:0; opacity: 0');
              overlay.setAttribute('ondblclick', 'javascript:TINY.box.show(\'replace_movie.php\',1,350,192,1,0,\'column_type=' + column_type + '&image_id=' + image_id + '&up_type=replace\');return false;');
              overlay.setAttribute('id', 'ovr_' + image_id);
              container_node.appendChild(overlay);
              */
            }
            TINY.toolbox.save_doc();
          } else {
            alert('Could not create placeholder image');
            return;
          }
        }
      };
      // Open connection to server and request id of new image or Flash object
      x.open('POST', 'get_new_image_placeholder.php?type=' + object_type, 1);
      x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      x.send(args)
    },
    changeImageProperties: function(form) {
      var img_id = 'img_' + form.imageId.value;
      var img = T$(img_id);
      if (img == null) {
        var msg = '<p class="new_dlg"><strong>Cannot change image attributes</strong></p><p class="new_dlg">This image was inserted using a previous version of MySiteNow that did not allow image attributes to be changed.</p><p class="new_dlg">In order to be able to change the attributes of the image you will need to delete it completely (including its container) and re-insert it.</p><p class="new_dlg">Please consult the Tutorial for more information.</p>';
        this.showAlert('MySiteNow version change', msg);
      } else {
        img.setAttribute('alt', form.alt_text.value);
        img.setAttribute('title', form.img_title.value);
        TINY.toolbox.save_doc();
      }
    },
    checkEditable: function(selection) {
      var userSelection = (selection == null ? this.getUserSelection() : selection);
      // Make sure the selection starts and ends in the same text node
      if (this.selection.anchorNode.nodeType != 3 || !this.selection.anchorNode.isSameNode(this.selection.focusNode)) {
        return false;
      }
      // Check that selection is inside a DIV defined to be replaceable
      var node = userSelection.anchorNode;
      var replaceable = false;
      var level = 0;
      if (node == null) {
        return false;
      }
      while (node.nodeName != 'BODY' && level < 20) {
        if (node.nodeName == 'DIV' && node.className == 'replaceable_text') {
          replaceable = true;
          break;
        } else {
          node = node.parentNode;
          level = parseInt(level) + 1;
        }
      }
      if (replaceable) {
        return true;
      } else {
        return false;
      }
    },

    // Gets user selection, collapsed to start if it spans more than one node.
    getUserSelection: function() {
      var userSelection;
      var container = document.getElementById('container'); // main iframe
      //if (container) {
        userSelection = container.contentWindow.getSelection();
      //} else {
      //  userSelection = window.getSelection();
      //}
      // Check start and end nodes are the same - if not, collapse to start node
      if (!(userSelection.isCollapsed)) {
        if (userSelection.anchorNode != userSelection.focusNode) {
          userSelection.collapseToStart();
        }
      }
      return userSelection;
    },

    // Checks whether current selection is inside an image container
    isContained: function(selection) {
      if (selection.anchorNode.nodeName == 'DIV' && selection.anchorNode.className == 'image_container') {
        return true;
      } else if (selection.anchorNode.nodeName == 'A' && selection.anchorNode.parentNode.className == 'image_container') {
        return true;
      } else {
        return false;
      }
    },

    hasContext: function(selection) {
      if (selection.anchorNode.nodeName == '#text') {
        var pName = selection.anchorNode.parentNode.nodeName;
        if (pName == 'P' || pName == 'H1' || pName == 'H2' || pName == 'H3' || pName == 'H4') {
          return pName;
        }
      }
      return false;
    },

    getColumnType: function(selection) {
      if (selection == null || selection == undefined) {
        alert('Selection is not set in getColumnType');
        return false;
      }
      // Climb up tree to containing column DIV
      var node = selection.anchorNode;
      if (node == null) {
        alert('Node is null in getColumnType');
        return false;
      }
      var level = 0;
      while (node.nodeName != 'BODY' && level < 20) {
        if (node.nodeName == 'DIV' && (node.className == 'left_2column' || node.className == 'right_column')) {
          return node.className;
        } else {
          node = node.parentNode;
          level = parseInt(level) + 1;
        }
      }
      this.showAlert('Error', 'You cannot insert an image or Flash movie here. Please ensure that you have selected a paragraph, header or table cell.');
      return false; // not in editable column - shouldn't happen if selected
                    // unless nodes are at least 20 deep.
    },

    check_email_code: function() {
      var email_code = T$('promcode').value;
      var args = 'email_code=' + email_code;
      var x = window.XMLHttpRequest
        ? new XMLHttpRequest()
        : new ActiveXObject('Microsoft.XMLHTTP');
      x.onreadystatechange = function() {
        if (x.readyState == 4 && x.status == 200) {
          if (x.responseText.substring(0, 2) == 'OK') {
            alert('THIS IS A VALID EMAIL CODE.\n\nNote that for security reasons, if you check it\nagain it will be reported as invalid.');
          } else {
            alert('THIS IS NOT A VALID EMAIL CODE!');
          }
        }
      }
      x.open('POST', 'check_email_code.php', 1);
      x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      x.send(args)
    }
  }
} ();

