// 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 = {};

// 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':
            break;
          default:
            return;
        }
      }

      // Force automatic height
      h = 0;

      // If not initialised, initialise
      if (!f) {
        // Create containing DIV and assign id
        p = document.createElement('div');
        p.id = 'tinybox';
        // 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';
        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>';
        p.style.width = p.style.height = '100px'
        p.style.lineHeight = '16px;';
      }

      // Mask the browser window
      this.mask();

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

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

      // Set timeout from time to live
      if (t) {
        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) {

      // Force automatic height
      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
      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
      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
      ) {

      // Don't allow animation
      // a = 0;

      // Force automatic height
      h = 0;

      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) {
            if (x.responseText == 'NOT ALLOWED') {
              TINY.box.hide();
            } else {
              TINY.box.psh(x.responseText, w, h, a)
            }
          }
        };

        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');
        x.send(args)          // send request
      } else {
        // Local content
        this.psh(c, w, h, a)
      }
    },

    psh: function(c, w, h, a) {
      // ('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() {
      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.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) {
      var o = Math.round(e.style.opacity * 100);
      if (o == a) {
        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 {
            TINY.box.fill(ic, iu, iw, ih, ia)
          }
        }
      } else {
        var n = Math.ceil((o + ((a - o) * .5)));
        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(w,h) {
      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 = 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');
      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, 50, 1)
        }
      };
      x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      x.send(args);
    },

    add_page: function() {
      TINY.box.show('new_page_dlg.php',1,350,430,1);
    },
    remove_page: function() {
      TINY.box.show('rem_page_dlg.php',1,300,190,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')
    },
    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)
    },
    justify_full: function() {
      document.execCommand('justifyfull', false, null)
    },
    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"')
    },
    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,430,1,0,'prodcat_id=' + prodcat);
    },
    show_change_prod_image_dlg: function(prodid) {
      TINY.box.show('change_prod_image_dlg.php',1,350,430,1,0,'prod_id=' + prodid);
    },
    show_add_product_dlg: function(prodcat) {
      TINY.box.show('add_product_dlg.php',1,350,430,1,0,'prodcat_id=' + prodcat);
    },
    show_add_department_dlg: function(prodcat) {
      TINY.box.show('add_department_dlg.php',1,350,430,1,0,'prodcat_id=' + prodcat);
    },
    show_delete_prodcat_dlg: function(prodcat) {
      TINY.box.show('delete_prodcat_dlg.php',1,350,430,1,0,'prodcat_id=' + prodcat);
    },
    show_edit_prodcat_dlg: function(prodcat) {
      TINY.box.show('edit_prodcat_dlg.php',1,350,430,1,0,'prodcat_id=' + prodcat);
    },
    show_edit_product_dlg: function(prodId) {
      TINY.box.show('edit_product_dlg.php',1,350,430,1,0,'product_id=' + prodId);
    },
    show_delete_product_dlg: function(prodId) {
      TINY.box.show('delete_product_dlg.php',1,300,430,1,0,'product_id=' + prodId);
    },
    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);
          TINY.box.show('insert_link_dlg.php',1,350,430,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,350,420,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,0,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,0,1,0);
    },
    rename_page: function() {
      TINY.box.show('rename_page_dlg.php',1,300,200,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, 200, 450, 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 = '';
          }
        }
      }
    },
    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,200,1,0);
        return false;
      }
    },
    get_mode: function() {
      return edit_mode;
    }
  }

} ();

// 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
    },

    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) {
      args = 'title=' + hdg + '&msg=' + msg;
      TINY.box.show('my_alert.php', 1, 300, 200, 1, 0, args);
      // or possibly, if auto works:
      // TINY.box.show('my_alert.php', 1, 0, 0, 1, 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;
      link_text = form.link_text.value;
      ul = form.show_underline.checked ? true : false;
      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');
          } else {
            this.insert_link(link_text, '?' + page_id, '_top', 'text-decoration:none');
          }
          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');
          } else {
            this.insert_link(link_text, url, '_blank', 'text-decoration:none');
          }
          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');
          } else {
            this.insert_link(link_text, 'get_lib_doc.php?doc_id=' + doc_id, '_top', 'text-decoration:none');
          }
          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,300,400,1,0,\'' + args + '\');';
          if (ul) {
            this.insert_link(link_text, href, '', 'text-decoration:underline');
          } else {
            this.insert_link(link_text, href, '', 'text-decoration:none');
          }
          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;
          }
          //alert('link_text: ' + link_text);
          //if (ul) {
          //  this.insert_link(link_text, 'mailto:' + email_addr, '', 'text-decoration:underline');
          //} else {
          //  this.insert_link(link_text, 'mailto:' + email_addr, '', 'text-decoration:none');
          //}

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



          // Get the encoded form of the address from the server - there doesn't
          // seem to be a simple way to do this in JS!
          //
          // 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.
          //
          // There doesn't seem to be any way around this. Anything inserted
          // into the DOM is going to be encoded on retrieval. In principle we
          // could get round it by modifying the HTML directly (by using the
          // insertHTML command) but this doesn't work because the browser
          // loses the insertion point when the dialog is used. It might have
          // worked with the old IFRAME architecture, but this is no longer
          // used.
          //
          //var x = window.XMLHttpRequest
          //  ? new XMLHttpRequest()
          //  : new ActiveXObject('Microsoft.XMLHTTP');
          //x.onreadystatechange = function() {
          //  if (x.readyState == 4 && x.status == 200) {
          //    alert('responseText = ' + x.responseText);
          //    if (x.responseText.substring(0, 3) == 'OK ') {
          //      var enc_addr = x.responseText.substring(3)
          //      if (ul) {
          //        TINY.page.insert_link(link_text, enc_addr, null, 'text-decoration:underline');
          //      } else {
          //        TINY.page.insert_link(link_text, enc_addr, null, 'text-decoration:none');
          //      }
          //    }
          //    return;
          //  }
          //};
          //x.open('POST', 'encode_it.php', 1);
          //x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
          //var args = 'addr=mailto:' + email_addr;
          //alert('Posting ' + args);
          //x.send(args);



          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 == '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 == '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 == '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 == '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('src', insert_comp_form.comp_sel.value);
      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 == 'TD') {
        insert_before_node = anchor_node;
      } else if (node_name != '#text') {
        this.showAlert('Error', 'You cannot insert an image 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 == '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 here');
            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 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;
            var img = document.createElement('img');
            img.setAttribute('src', imagePath);
            img.setAttribute('id', 'img_' + image_id);
            img.setAttribute('title', title);
            img.setAttribute('alt', title);
            img.setAttribute('class', 'replaceable_image');
            var alert_msg = 'image_id = ' + image_id;
            //img.setAttribute('oncontextmenu', 'javascript:alert(\'' + alert_msg + '\');return false');
            //img.setAttribute('onclick', 'javascript:TINY.box.show(\'replace_image.php\',1,350,350,1,0,\'column_type=' + column_type + '&image_id=' + image_id + '&up_type=replace\')');
            img.setAttribute('oncontextmenu', 'javascript:TINY.box.show(\'replace_image.php\',1,350,350,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', 'float:left; margin: 10px 15px ' + (caption ? '5px' : '10px') + ' 0');
            } else if (image_disp == 'wrap_left') {
              container_node.setAttribute('style', 'float:right; margin: 10px 0 ' + (caption ? '5px' : '10px') + ' 15px');
            } else if (image_disp == 'no_wrap') {
              container_node.setAttribute('style', '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);
            }
            TINY.toolbox.save_doc();
          } else {
            alert('Could not create placeholder image');
            return;
          }
        }
      };
      // Open connection to server
      x.open('POST', 'get_new_image_placeholder.php', 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') {
          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 here.');
      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)
    }
  }
} ();