
var browseType = "browse"; // 'browse' or 'edit'

var draggingEl = false;  //any element we are currently dragging
var draggingThumbEl = null;  //any thumbnail we are currently dragging
var selectedNode = null;
var selectedTextNode = null;
var selectedDroppable = null;
var lastSelectedNode = null;
var transparencyHandle = null;
var transparencyToolsTimer = null;
var textToolsTimer = null;
var saveBookTimer = null;
var globalPageScale = 1;  //how much we are scaling down the book on screen
var globalBlockData = null;  //metadata about block
var globalEditorType = null;  //the blockType setting is either Basic or Personal - this is used to set permissions
var globalUserData = null;
var arrTransport = new Array();  //hold ajax request transport objects
var isSeller=false; //shows whether user is selling this block (userId and blockId are registered in the sales table) or not

var isDragUnlocked = false;

//cover images are actually one big image for both covers
//so we need to fudge an extra shift of their position
var fudgeLeft_frontFactor = 0.64;  //amount to skew the cover in inches
var fudgeLeft_backFactor = 0;  //amount to skew the cover in inches
var fudgeTopFactor = 0;  //amount to skew the cover


var fudgeLeftFront = 0;
var fudgeLeftBack = 0;
var fudgeTop = 0;

var blockViewMode = "double";  //we can either view one or two pages at a time.

var ZOOMTOOLSTIMER_DELAY = 10;  //milliseconds to keep zoomtools open after mousing out
var TEXTTOOLSTIMER_DELAY = 10;  //milliseconds to keep zoomtools open after mousing out
var PAGE_LEFT_ID = 'template_left_page';
//var PAGE_RIGHT_ID = 'template_right_page';
var PAGE_LEFT_CONTAINER_ID = 'template_left';
//var PAGE_RIGHT_CONTAINER_ID = 'template_right';
var PAGES_CONTAINER_ID = 'template_box';
var NEXT_PAGE_BUTTON_ID = 'template_next_page';
var PREV_PAGE_BUTTON_ID = 'template_prev_page';
var PAGINATION_CONTAINER_ID = 'double-page';


var ZOOM_TOOL_ID = 'img_tool_box';
var ZOOM_TRACK_ID = 'track1';
var ZOOM_HANDLE_ID = 'handle1';
var TRANSPARENCY_TOOL_ID = 'img_transparency_box';
var TRANSPARENCY_TRACK_ID = 'track-transparency';
var TRANSPARENCY_HANDLE_ID = 'handle-transparency';

var ZOOM_RANGE_MAX = 2;  
var ZOOM_RANGE_MIN = ZOOM_RANGE_MAX/10;
var ZOOM_MIN_PIXELS=30;
var ZOOM_MAX_RATIO=2;
var ZOOM_TOP_OFFSET = 15; //  let zoom-tool have an upper offset to be able to drag small images
var PAGE_DISPLAY_WIDTH = 305;
var PAGE_DISPLAY_HEIGHT = 395;

//class to identify droppable elements
var DROPPABLE_CLASSNAME = 'isadrop';  //classname for all droppable images
var GHOST_CLASSNAME = DROPPABLE_CLASSNAME + 'ghost';  //classname for all droppable ghosts
var GHOST_CLASSNAME_HOVER = GHOST_CLASSNAME + '_over';  //classname for all droppable ghosts
var GHOST_PREFIX = 'dropghost_';  //prefix before id of ghosts

var TEXT_TOOL_CLASSNAME = "isatextnode";
var TEXT_TOOL_CLASSNAME_HOVER = TEXT_TOOL_CLASSNAME +"_over";

//servers
//var SERVER_URL = "http://staging.blockr.com/api/";  //staging server
//var SERVER_URL = "http://10.201.105.151/api/";    //dev server
var SERVER_URL = "/api/";  //live server
//var ROOT_URL = "http://82.78.33.125";
var ROOT_URL = "http://blockr.com";  
var BASE_URL = "http://blockr.com";  // used on paypal

var HOME_PAGE_URL = "/";

var SAVE_BLOCK_XML_URL = SERVER_URL + "SaveBlockPage";
var SAVE_QUOTE_URL = SERVER_URL + "SaveBlockQuote";
var GET_BLOCK_XML_URL = SERVER_URL + "GetBlockPage";
var GET_BLOCK_METADATA_URL = SERVER_URL + "GetBlockMetadata";
var SAVE_BLOCK_METADATA_URL = SERVER_URL + "SaveBlockMetadata";
var PUBLISH_BLOCK_URL = SERVER_URL + "PublishBlock";
var SEND_BLOCK_URL = SERVER_URL + "SendBlock";
var PURCHASE_BLOCK_URL = SERVER_URL + "qoop/PurchaseBlock";
var PUBLISHED_PUBLIC = "Published_Public";
var PUBLISHED_PRIVATE = "Published";
var GET_PHOTO_SOURCES_URL = SERVER_URL + "photos/GetSources?source=0";
var GET_PHOTO_SETS_URL = SERVER_URL + "photos/GetSets";
var SAVE_PHOTO_SET_METADATA_URL = SERVER_URL + "photos/SaveSetMetadata";
var REMOVE_PHOTO_URL = SERVER_URL + "photos/RemovePhoto";
var GET_PHOTOS_URL = SERVER_URL + "photos/GetPhotos";
var GET_CART_DATA_URL = SERVER_URL +"GetCartData";
var ADD_CART_PRODUCT_URL = SERVER_URL +"AddCartProduct";
var REMOVE_CART_PRODUCT_URL = SERVER_URL +"RemoveCartProduct";
var DELETE_BLOCK = SERVER_URL +"DeleteBlock";


var SAVE_BLOCK_TAG_URL = "SaveBlockTag";  //block=&tag=&action=(add|remove)
var SAVE_BLOCK_CATEGORY_URL = "SaveBlockCategory";  //block=&category=&action=(add|remove)
var GET_TAG_CLOUD_URL = "GetTagCloud";

var GET_BLOCK_THEMES_URL = SERVER_URL + "GetBlockThemes";
var SAVE_BLOCK_THEME_URL = SERVER_URL + "ApplyBlockTheme";

var REGISTER_USER_URL = SERVER_URL + "RegisterUser";
var LOGIN_USER_URL = SERVER_URL + "LoginUser";
var LOGOUT_USER_URL = SERVER_URL + "LogoutUser";
var GET_USER_METADATA_URL = SERVER_URL + "GetUserMetadata";
var SAVE_USER_METADATA_URL = SERVER_URL + "SaveUserMetadata";
var SAVE_USER_PHOTO_URL = SERVER_URL + "UploadFile"; //expectes file in input field name=profile_photo
var REGISTER_RESELLER_URL = SERVER_URL + "RegisterReseller";
var RANK_BLOCK_URL = SERVER_URL + "RankBlock";

var CHECKOUT_URL = SERVER_URL + "Checkout";
var GET_ORDER_DATA = SERVER_URL + "GetOrderData";

var PNG_REPLACEMENT_DIV = '_pngReplacementDiv';

var usedImagesArray = new Array();
/*
var tempUserId = null;

var resellBlockId=null; // block ID user only by saveResellerId()
//get info on this user
Event.observe(window, 'load', function(evt) {
  tempUserId=getCookie('BLOCKR_USER');
  if (tempUserId) getUserMetaData(tempUserId);
  else getUserMetaData(false);
});

*/

//variables about which book and what part to load
var blockId = 1;  //should be received from server
var startPage = 0;  //should be 0 always?
var currentPage = 0;
var isBackCoverPage = false;  //flag to indicate we are at the back cover, which is technically the same page number as front cover
var isFrontCoverPage = false;  
var blockType = 1;

var leftPageNum = null;
var rightPageNum = null;

//variables to store the xml data for the book
var pageOriginalXML = new Array();
var pageModifiedXML = new Array();
var undoData = new Array();

//global value of how big the bleed margins are on the printed page
var SCREEN_RESOLUTION = 72;  //pixels per inch on screen
var PRINT_RESOLUTION = 300;  //pixels per inch in print
var BLEED_MARGIN = "0.125in";

var thumb_width_offset;

var curTheme;


function getSelectedNode() {
  return window.selectedNode;
}
function getSelectedTransparencyNode() {
  return window.selectedTransparencyNode;
}

function showResponse(pageEl, req) {
  //put returned XML in the textarea
//  console.error(req.responseText);
//  console.error(req.responseXML);
}

function storePageXML(response, pageNum) {
  var cloneNode = response.cloneNode(true);

  //SAFARI BUG: cloneNode returns null always here for no good reason, so just reference the original
  //this is weird since safari is supposed to support this method
//  if (browser.isSafari) cloneNode = document.importNode(response, true);  //cannot import document nodes
  if (browser.isSafari) cloneNode = response;  //assigns variable by reference, which is useless

  pageOriginalXML[pageNum] = cloneNode;
  pageModifiedXML[pageNum] = cloneNode;

}

function attachTextTools(node) {
    Element.classNames(node).add(TEXT_TOOL_CLASSNAME_HOVER);
    node.style.left = parseInt(node.style.left) - 1 + "px";
    node.style.top = parseInt(node.style.top) - 1 + "px";
}

function detachTextTools(node) {
    Element.classNames(node).remove(TEXT_TOOL_CLASSNAME_HOVER);
    node.style.left = parseInt(node.style.left) + 1 + "px";
    node.style.top = parseInt(node.style.top) + 1 + "px";
}

function getWhichPageId(node) {
  if (node && node.parentNode) {
      // createPNGNode creates a div containing the image so use the image instead
    if(node.parentNode.getAttribute('id').lastIndexOf(PNG_REPLACEMENT_DIV)>0) {
      whichPageId = node.parentNode.parentNode.getAttribute('id');
    }
    else {
      whichPageId = node.parentNode.getAttribute('id');
    }
    return whichPageId;
  }
  return false;
}

function updateBlockId(newBookId) {
  blockId = newBookId;
}

function isLockedNode(node) {
  //get permissions for this element
  var isLocked = true;  //by default everything is locked
  if (globalEditorType) {  //if an editor type is set
    //admins can edit everything, other users can edit some things
    switch(globalEditorType) {
      case 'admin':
        isLocked = false;
        break;
      case 'preview':
        isLocked = true;
        break;
      default:
        //read the corresponding locked setting for this editorType
        var lockAttributeName = 'ms:locked_' + globalEditorType.toLowerCase();  //the locked attribute that corresponds to this editorType
        //SAFARI BUG: namespaces are stripped from attribute names
        if (browser.isSafari) lockAttributeName = 'locked_' + globalEditorType.toLowerCase();

        var lockAttrib = node.getAttribute(lockAttributeName);
        //SAFARI BUG: getAttribute() doesnt work in safari here
        if (browser.isSafari) lockAttrib = extractAttributeFromNode(lockAttributeName, node);

        if(lockAttrib) {
          isLocked = eval(node.getAttribute(lockAttributeName));  //whether users can edit this node

          //SAFARI BUG: getAttribute() doesnt work here
          if (browser.isSafari) isLocked = eval(extractAttributeFromNode(lockAttributeName, node));
        }
        break;
    }
  }
  return isLocked;
}

function showTextToolsOverlay() {
  var el = $('text-tools-out');
  if (el) {
    el.style.display = "block";
  }
}

function hideTextToolsOverlay() {
  var el = $('text-tools-out');
  if (el) {
    el.style.display = "none";
  }
}



function createTextImage(svgNode, pageElId, scale) {
  var isUnlocked = !isLockedNode(svgNode);  //is this element unlocked for this user?

  var pageEl = $(pageElId);
  var caption = (isUnlocked) ? "Click to edit text" : "";
  var node = createElementFromSVG('img', svgNode, scale, caption, pageElId);

  var blockContainer = $('template_left_page');
  //put the node on the page
  var container = pageEl;
  container.appendChild(node);

  // make sure text field is on top
  node.style.zIndex = 2;
  // createPNGNode creates a div containing the image so use the image instead
  if(node.getAttribute('id').lastIndexOf(PNG_REPLACEMENT_DIV)>0) {
    node = node.firstChild;
    // make sure text field is on top
    node.style.zIndex = 2;
  }
  //assign it a class
  Element.classNames(node).add(TEXT_TOOL_CLASSNAME);

  if (isUnlocked) {

    //set the cursor for editable text
    node.style.cursor = "text";

    //highlight editable text
    Event.observe(node, 'mouseover', function(evt) {
      //if we are not dragging a thumbnail image, show the text tools
      if (!draggingThumbEl) {
        attachTextTools(node);
      }
    }, true);

    Event.observe(node, 'mouseout', function(evt) {
      if (!draggingThumbEl) {
        detachTextTools(node);
      }
    }, true);

    var nodeId = node.id;
    if(nodeId) {
      nodeId = nodeId.replace(PNG_REPLACEMENT_DIV,'');
    }
    switch(nodeId) {
      case 'text-image_flowRoot0_1':
      case 'text-image_flowRoot2_1':
      case 'text-image_flowRoot2_2':
      case 'text-image_flowRoot3_1':
      case 'text-image_flowRoot3_2':
        Event.observe(node, 'click',
          function(evt) {
            var textToolObj = $('text_tool_box_title');
            textToolObj.style.display = "block";
            textToolObj.style.left = blockContainer.getWidth()/2 + Position.cumulativeOffset(blockContainer)[0] - textToolObj.getWidth()/2 + "px";
            textToolObj.style.top = blockContainer.getHeight()/2 + Position.cumulativeOffset(blockContainer)[1] - textToolObj.getHeight()/2 + "px";
            showTextToolsOverlay();
          }, true);
        break;

      case 'text-image_flowRoot1_1':
      case 'text-image_flowRoot15_1':
        Event.observe(node, 'click',
          function(evt) {
            var textToolObj = $('text_tool_box_wallpaper');
            textToolObj.style.display = "block";
            textToolObj.style.left = blockContainer.getWidth()/2 + Position.cumulativeOffset(blockContainer)[0] - textToolObj.getWidth()/2 + "px";
            textToolObj.style.top = blockContainer.getHeight()/2 + Position.cumulativeOffset(blockContainer)[1] - textToolObj.getHeight()/2 + "px";
            showTextToolsOverlay();
          }, true);
        break;      
        
      case 'text-image_flowRoot4_1':
      case 'text-image_flowRoot4_2':
        Event.observe(node, 'click',
          function(evt) {
            var textToolObj = $('text_tool_box_quote');
            textToolObj.style.display = "block";

            textToolObj.style.left = blockContainer.getWidth()/2 + Position.cumulativeOffset(blockContainer)[0] - textToolObj.getWidth()/2 + "px";
            textToolObj.style.top = blockContainer.getHeight()/2 + Position.cumulativeOffset(blockContainer)[1] - textToolObj.getHeight()/2 + "px";
            showTextToolsOverlay();
          }, true);
        break;
        
      default:
        Event.observe(node, 'click', function(evt) {
          //keep track of which node we are dragging
          var svgNodeId = node.id;
          var textRootId = svgNodeId.split('text-image_')[1];
          selectedTextNode = node;

          var thisText = "";
          //figure out which page the user clicked
          var POX = (pageElId == PAGE_LEFT_ID) ? pageModifiedXML[leftPageNum] : pageModifiedXML[rightPageNum];
          var textRoots = POX.getElementsByTagName('flowRoot');
          for (var i=0; i<textRoots.length; i++) {
            if (textRoots[i].getAttribute('id') == textRootId) {
              var flowPara = textRoots[i].getElementsByTagName('flowPara');
              for (var j=0; j<flowPara.length; j++) {
                thisText = extractTextFromXML(flowPara[j]);
              }
            }
          }
          //clear the form to make way for the new text
          //this is necessary for some reason even though we replace the innerHTML later
          var textToolObj = $('text_tool_box');
          textToolObj.style.display = "block";
          var textToolForm = $('text_tool_form');

          

          var textEditArea = $('text_tool_box_textarea');
          textToolForm.reset();
    //      textEditArea.innerHTML = thisText;
          textEditArea.value = thisText;  //IE6 BUG - if replacing textarea.innerHTML, IE6 ignores \n characters.  use textarea.value instead

          $('text_tool_box_textarea').resetCharCount(textRootId);

          //set position to middle of the screen
          textToolObj.style.left = blockContainer.getWidth()/2 + Position.cumulativeOffset(blockContainer)[0] - textToolObj.getWidth()/2 + "px";
          textToolObj.style.top = blockContainer.getHeight()/2 + Position.cumulativeOffset(blockContainer)[1] - textToolObj.getHeight()/2 + "px";
          showTextToolsOverlay();
        },
      true);
        break;
    }

  } //end is unlocked
}

function createPhoto(svgNode, pageElId, scale) {
  var isUnlocked = !isLockedNode(svgNode);  //is this element unlocked for this user?
  var pageEl = $(pageElId);

  var caption = (isUnlocked) ? "Click and drag to pan this image" : "";
  var node = createElementFromSVG('img', svgNode, scale, caption, pageElId);

  //put the node on the page
  var container = pageEl;
//  container = document.getElementsByTagName('body')[0];
  container.appendChild(node);

  // createPNGNode creates a div containing the image so use the image instead
  if(node.getAttribute('id').lastIndexOf(PNG_REPLACEMENT_DIV)>0) {
    node = node.firstChild;
  }
  
  //set some necessary properties for this node
  //node.setAttribute('scale', 1);    //parameter not used
  node.setAttribute('clip_top', "0px");
  node.setAttribute('clip_left', "0px");
  node.setAttribute('clip_bottom', node.style.height);
  node.setAttribute('clip_right', node.style.width);

  setImgProps(node, isUnlocked);

  //some images are editable, others are not. if so for this image, make it droppable
  if (isUnlocked) makeDroppable(node);

  if (node.id == "image0_3") {
    node.style.zIndex = 2;
  }
  
}

function getDescendentsByTagName(node, tagName, arrDescendents) {
  for (var i=0; i < node.childNodes.length; i++) {
    var thisChild = node.childNodes[i];
    var name = thisChild.tagName;
    if (name == tagName) {
      //if this child matches the desired tag name, save it to the list
      arrDescendents[arrDescendents.length] = thisChild;  
    }
    else {
      //otherwise go deeper into descendents tree looking for more
      getDescendentsByTagName(thisChild, tagName, arrDescendents);
    }
  }
}

function getChildrenByTagName(node, tagName) {
  var res = new Array();
  for (var i = 0, j = 0; i < node.childNodes.length; i++)
    if (typeof(node.childNodes[i].tagName) != 'undefined' && node.childNodes[i].tagName.toLowerCase() == tagName.toLowerCase())
      res[j++] = node.childNodes[i];
  
  return res;
}

function extractTagsFromNode(tagName, node) {
      var tags = new Array();
      getDescendentsByTagName(node, tagName, tags);
      return tags;
}

function extractAttributeFromNode(name, node) {
  //SAFARI BUG: getAttribute doesn't work for namespaces
  //it seems to not be able to getAttribue() on any attribute that has a namespace
  //so... we use regular expressions to get an attribute from a node instead
  var exprss = new RegExp(name + "=['\"]([^ ]*)['\"]", "i");
  var strNode = getXMLNodeSerialisation(node);
  var matches = strNode.match(exprss);

  var returnVal = "";
  if (matches && matches.length>1) returnVal = matches[1];
//if (name=='src') console.error(strNode + "\n" + returnVal);
//if (matches) console.error(name + "=" + matches[1]);
//else console.error(name + "\n" + strNode);
  return returnVal;
}

function createElementFromSVG(type, svgNode, scale, caption, pageElId) {
  var node = document.createElement(type);

  var nodeWidth = adjustResolution(svgNode.getAttribute('width'), scale);
  var nodeHeight = adjustResolution(svgNode.getAttribute('height'), scale);

  //if this is a PNG, make it cross-browser compatible
  var imgSrc = svgNode.getAttribute('ms:alt-href');
  
  //SAFARI BUG: safari strips off namespaces from attributes, so get it without the namespace
  if (browser.isSafari) {
    imgSrc = extractAttributeFromNode('ms:alt-href', svgNode);
  }

  var pngNode = null;
  if (imgSrc.substr(imgSrc.length-4, imgSrc.length) == ".png") {
    pngNode = new createPNGNode({src: imgSrc, id: svgNode.getAttribute('id')+PNG_REPLACEMENT_DIV, width: nodeWidth, height: nodeHeight});
    node = pngNode.firstChild;
  }

  node.setAttribute('id', svgNode.getAttribute('id'));
  Element.classNames(node).add('page_element');

  node.setAttribute('title', caption);
  node.setAttribute('src', svgNode.getAttribute('ms:alt-href'));
  node.setAttribute('xlink:href', svgNode.getAttribute('xlink:href'));
  node.setAttribute('ms:thumb-href', svgNode.getAttribute('ms:thumb-href'));
  node.setAttribute('ms:alt-href', svgNode.getAttribute('ms:alt-href'));
  node.setAttribute('ms:scale', svgNode.getAttribute('ms:scale'));
  node.setAttribute('ms:scale-min', svgNode.getAttribute('ms:scale-min'));
  node.setAttribute('ms:scale-max', svgNode.getAttribute('ms:scale-max'));
  node.setAttribute('ms:href-width', svgNode.getAttribute('ms:href-width'));
  node.setAttribute('ms:href-height', svgNode.getAttribute('ms:href-height'));

  //SAFARI BUG: getATtribute doesnt work all the time
  if (browser.isSafari) {
    node.setAttribute('id', extractAttributeFromNode('id', svgNode));
    node.setAttribute('class', extractAttributeFromNode('class', svgNode));
    node.setAttribute('title', extractAttributeFromNode('title', svgNode));
    node.setAttribute('src', extractAttributeFromNode('ms:alt-href', svgNode));
    node.setAttribute('xlink:href', extractAttributeFromNode('xlink:href', svgNode));
    node.setAttribute('ms:thumb-href', extractAttributeFromNode('ms:thumb-href', svgNode));
    node.setAttribute('ms:alt-href', extractAttributeFromNode('ms:alt-href', svgNode));
    node.setAttribute('ms:scale', extractAttributeFromNode('ms:scale', svgNode));
    node.setAttribute('ms:scale-min', extractAttributeFromNode('ms:scale-min', svgNode));
    node.setAttribute('ms:scale-max', extractAttributeFromNode('ms:scale-max', svgNode));
    node.setAttribute('ms:href-width', extractAttributeFromNode('ms:href-width', svgNode));
    node.setAttribute('ms:href-height', extractAttributeFromNode('ms:href-height', svgNode));
  }

  //offset to compensate for added bleed margin in svg doc
  var svgLeft = svgNode.getAttribute('x');
  var svgTop = svgNode.getAttribute('y');
  var bleed = parseFloat(BLEED_MARGIN);
  //ARE WE EDITING A COVER?
  if (pageElId == PAGE_LEFT_ID && (leftPageNum==0 || isBackCoverPage) ) {
    //editing front or back cover
    //there is a bigger bleed on covers and entire cover is one big image that needs adjusting

    //figure fudge factor for cover in inches
    //var fudgeLeft_front = $(pageElId).getWidth()/SCREEN_RESOLUTION/globalPageScale + fudgeLeft_frontFactor;
   
    fudgeLeft_front = - (thumb_width_offset + bleed);
    
    var fudgeLeft_back = fudgeLeft_backFactor;
    var fudgeTop = fudgeTopFactor;

    

    window.fudgeLeftFront = fudgeLeft_front;
    window.fudgeLeftBack = fudgeLeft_back;
    window.fudgeTop = fudgeTop;


    //calculate fudged position for cover
    var fudgeLeftVal = (isBackCoverPage) ? fudgeLeft_back : fudgeLeft_front;
    var elLeft = parseFloat(svgLeft) - fudgeLeftVal - bleed + "in";

    var elTop = parseFloat(svgTop) - fudgeTop - bleed + "in";
    //FIX IE6 BUG
    if (browser.isIE6x) {
      //if IE6, fudge a little because IE is horrible
      elLeft = parseFloat(svgLeft) - fudgeLeftVal - parseFloat(BLEED_MARGIN) + "in";
    }
  }
  else {
    //normal page with normal bleed and position
    var elLeft = parseFloat(svgLeft) - bleed + "in";
    var elTop = parseFloat(svgTop) - bleed + "in";

    //FIX IE6 BUG
    if (browser.isIE6x) {
      //if IE6, fudge a little because IE is horrible
      elLeft = parseFloat(svgLeft) - parseFloat(BLEED_MARGIN) + "in";
    }
  }

  
  var left = adjustResolution(elLeft, scale);
  var top = adjustResolution(elTop, scale);
  node.style.position = "absolute";
  node.style.left = left;
  node.style.top = top;
  node.style.width = nodeWidth;
  node.style.height = nodeHeight;

  //do special clip sauce for images
  if (svgNode.tagName == "image") {
    //get clip path from SVG and apply it in CSS
    var clipPathSVG = svgNode.getAttribute('clip-path');

    //SAFARI BUG: doesnt getAttribute properly
    if (browser.isSafari) clipPathSVG = extractAttributeFromNode('clip-path', svgNode); 
   
    var clipPathCSS = (clipPathSVG) ? getClipPathCSS(svgNode, pageElId, scale) : null;

    if (clipPathCSS) {
//      node.style.clip = clipPathCSS;
      clipNode(node, clipPathCSS.top, clipPathCSS.right, clipPathCSS.bottom, clipPathCSS.left);
    }
  }

  if(pngNode) {
    return pngNode;
  }
  else {
    return node;
  }
}

function getClipPathCSS(svgNode, pageElId, scale) {
  var clipPathSVG = svgNode.getAttribute('clip-path');
  //SAFARI BUG: doesnt getAttribute properly
  if (browser.isSafari) clipPathSVG = extractAttributeFromNode('clip-path', svgNode);    

  var reg = /url\(#(.*)\)$/;  //match svg clip path url
  var match = clipPathSVG.match(reg);
  var clipNodeId = match[1];  //get the id of the clip element in the svg

  // TODO: remove rightPage completely
  //var svgDoc = (pageElId == PAGE_LEFT_ID) ? pageModifiedXML[leftPageNum] : pageModifiedXML[rightPageNum];  //get svg doc of the page we are working on
  var svgDoc = pageModifiedXML[leftPageNum];  
//  svgDoc = svgDoc.getElementsByTagName('svg')[0];

  var clipNodes = svgDoc.getElementsByTagName("clipPath");

  for (var i=0; i<clipNodes.length; i++) {
    var thisId = clipNodes[i].getAttribute('id');
    if (thisId == clipNodeId) {
      cnode = clipNodes[i];//TODO put BREAK
    }
  }

//  var cnode = svgDoc.getElementById(clipNodeId);  //grab the svg clipPath node


  if (cnode) {
    var clipRect = cnode.getElementsByTagName('rect')[0];  //get the rect element of the clip area
    //make clipPath object
    var clipWidth = adjustResolution(clipRect.getAttribute('width'), scale);
    var clipHeight = adjustResolution(clipRect.getAttribute('height'), scale);

    //in SVG, clip left and top are relative to top left of page.
    //in CSS, clip left and top are relatively to the top left of the node we are clipping
    var clipLeft =   parseFloat(clipRect.getAttribute('x')) - parseFloat(svgNode.getAttribute('x')) + "in";  //in inches
    var clipTop =   parseFloat(clipRect.getAttribute('y')) - parseFloat(svgNode.getAttribute('y')) + "in";  //in inches
    clipLeft =   adjustResolution(clipLeft, scale);
    clipTop =   adjustResolution(clipTop, scale);

    var clipPath = {
      width: clipWidth,
      height: clipHeight,
      left: clipLeft,
      top: clipTop,
      right: parseInt(clipLeft) + parseInt(clipWidth) + "px",
      bottom: parseInt(clipTop) + parseInt(clipHeight) + "px"
    }

    return clipPath;
//    var strClipPathCSS = "rect(" + clipPath.top + "," + clipPath.right + "," + clipPath.bottom + "," + clipPath.left + ")";
//    return strClipPathCSS;
  }
  else {
    return false;
  }
}


function extractTextFromXML(rootNode) {
  //recursively parse the XML to extract all text 
  var textStr = "";
  for (var i=0; i<rootNode.childNodes.length; i++) {
    //convert flowLine elements into text line breaks
    if (rootNode.childNodes[i].nodeType == 1) {
      if (rootNode.childNodes[i].tagName.toLowerCase() == 'flowline') {
        textStr += "\n";
        continue;  //skip to next node
      }
    }
    //grab any text from this node
    if(rootNode.childNodes[i].nodeType == 3) {
      if (rootNode.childNodes[i].nodeValue) {
        textStr += rootNode.childNodes[i].nodeValue;
      }
    }
    else {
      //recursively probe this child node
      textStr += extractTextFromXML(rootNode.childNodes[i]);
    }
  }

  return textStr;
}


function makeDroppable(node) {
  makeDroppableGhost(node);
}

function displayGhosts(value) {
  //get list of ghost droppable images
  var ghostList = document.getElementsByClassName(GHOST_CLASSNAME);
  for (var i=0; i<ghostList.length; i++) {
    var ghostNode = ghostList[i];
    //cancel any existing effect on this node
    if (ghostNode.effect) ghostNode.effect.cancel();



    if (value == "none") {
//      ghostNode.style.display = "none";
        new Effect.Opacity(ghostNode.getElementsBySelector("img")[0], {to: 0.1});
    }
    else {
//      Effect.Appear(ghostNode, {duration: 0.5});
      new Effect.Opacity(ghostNode.getElementsBySelector("img")[0], {to: 1, duration: 0.5});
    }
  }
}

function hiliteGhosts(pageEl) {
  //get list of ghost droppable images
  var ghostList = pageEl.getElementsByClassName(GHOST_CLASSNAME);
  for (var i=0; i<ghostList.length; i++) {
    var ghostNode = ghostList[i];
//    ghostNode.effect = Effect.Fade(ghostNode, {duration: 2});
    ghostNode.effect = new Effect.Opacity(ghostNode.getElementsBySelector("img")[0], {duration: 2, to: 0.1});

  }
}

function makeDroppableGhost(dropNode) {
  //create the ghost of a droppable element that lies between the cursor and any draggable thumbnails
  //use it to trigger mouseover events that then propagate to the droppable

  var ghostNode = document.createElement('div');

  //set special class so we can easily remove this droppable from the droppables list when page is cleared
  Element.classNames(ghostNode).add(DROPPABLE_CLASSNAME);

  //figure out width and height of clippable area
  var clipDims = getClipDims(dropNode);
  var clipWidth = parseInt(clipDims.right) - parseInt(clipDims.left) + "px";
  var clipHeight = parseInt(clipDims.bottom) - parseInt(clipDims.top) + "px";

  //assign id related to the droppable nodes id
  var dropNodeId = dropNode.getAttribute('id');
  var ghostNodeId = GHOST_PREFIX + dropNode.getAttribute('id');
  ghostNode.setAttribute('id', ghostNodeId);
  Element.classNames(ghostNode).add(GHOST_CLASSNAME);


  // compensate border's 2 pixels
  ghostNode.style.width = parseInt(clipWidth) - 2 + "px";
  ghostNode.style.height = parseInt(clipHeight) - 2 + "px";

  //insert clear gif to hold the div to the proper size
  var containerNode = $(getWhichPageId(dropNode));
  containerNode.appendChild(ghostNode);  //append it first
  var scaffNode = insertScaffoldImage(ghostNodeId, clipWidth, clipHeight);

  //position the ghost element to sit about the clippable area of the droppable node
  ghostNode.style.left = parseInt(dropNode.style.left) + parseInt(clipDims.left) + "px";
  ghostNode.style.top = parseInt(dropNode.style.top) + parseInt(clipDims.top) + "px";

  // make sure dropnode is on top of ghost
  dropNode.style.zIndex = 1;


  //make the node accept thumbnail images
  Droppables.add(ghostNode, {
    hoverclass: GHOST_CLASSNAME_HOVER,
    accept: 'photo_thumb',
    onHover: function(element, dropon) {
      if (zoomToolsTimer) clearTimeout(zoomToolsTimer);
      isDropping = true;
    },
    onUnhover: function(dropon) {
      isDropping = false;  
    },
    onDrop: function(element, dropon, event){
      var targetImg = $(dropon.id.substr(GHOST_PREFIX.length));
      swapImage(element, targetImg);
      startZoomToolsTimer();
      element.style.display = 'none';
    }
  });
}

function swapImage(element, dropon) {
  //figure out the appropriate scale to get this new image to fit the alloted space
  //create an image object in order to grab width and height automagically
  var newImage = new Image();
  //wait for the new image to load before doing anything
  newImage.onload = function() {
    completeImageSwap(element, dropon, newImage);
  }
  
  //SAFARI BUG: getAttribute is a piece of snot in Safari
  if (browser.isSafari)
    newImage.src = extractAttributeFromNode('ms:alt-href', element);
  else
    newImage.src = element.getAttribute('ms:alt-href'); 

// this bugfix is useless since the problem was in setting img.src attribute
// before any onload event handler were added.
//  //SAFARI BUG: images that are already cached dont fire an onload event
//  if (browser.isSafari) {
//    //keep track of which images we have already loaded
//    //this allows us to not rely on the buggy onload event of the image object
//    if (!window.imageCache) window.imageCache = new Array();
//    if (!imageCache[newImage.src]) {
//      //image has not yet loaded
//      imageCache[newImage.src] = true;
//      newImage.onload = function() {
//        completeImageSwap(element, dropon, newImage);
//      }
//    }
//    else {
//      //image is already loaded
//      completeImageSwap(element, dropon, newImage);
//    }
//  }
}

function completeImageSwap(element, dropon, newImage) {

  var oldImageBigHref = dropon.getAttribute('xlink:href');
  var newImageBigHref = element.getAttribute('xlink:href');
  if(browser.isSafari) {
    oldImageBigHref = extractAttributeFromNode('xlink:href', dropon);
    newImageBigHref = extractAttributeFromNode('xlink:href', element);
  }
  decreaseDogEaring(oldImageBigHref);
  increaseDogEaring(newImageBigHref);
  updateDogEarings();



  //swap in the new image
  dropon.setAttribute('src', newImage.src);
  //set new attributes that we use when generating the xml
  dropon.setAttribute('ms:thumb-href', element.getAttribute('ms:thumb-href'));
  dropon.setAttribute('ms:alt-href', element.getAttribute('ms:alt-href'));
  dropon.setAttribute('xlink:href', element.getAttribute('xlink:href'));
  dropon.setAttribute('ms:href-width', element.getAttribute('ms:href-width'));
  dropon.setAttribute('ms:href-height', element.getAttribute('ms:href-height'));

  var thisHrefWidth = dropon.getAttribute('ms:href-width');
  var thisHrefHeight = dropon.getAttribute('ms:href-height');
  //SAFARI BUG: safari's implementation of getAttribute is totally broken
  
  if (browser.isSafari) {
    dropon.setAttribute('ms:thumb-href', extractAttributeFromNode('ms:thumb-href', element));
    dropon.setAttribute('ms:alt-href', extractAttributeFromNode('ms:alt-href', element));
    dropon.setAttribute('xlink:href', extractAttributeFromNode('xlink:href', element));
    dropon.setAttribute('ms:href-width', extractAttributeFromNode('ms:href-width', element));
    dropon.setAttribute('ms:href-height', extractAttributeFromNode('ms:href-height', element));

    thisHrefWidth = extractAttributeFromNode('ms:href-width', element);
    thisHrefHeight = extractAttributeFromNode('ms:href-height', element);
  }

  //fit new image snugly in the existing clip area
  //get size of ghost image for this photo
  //get handle on ghost of this node
  var ghostNode = getGhostNode(dropon);
  var clipWidth = ghostNode.getWidth();
  var clipHeight = ghostNode.getHeight();
  var oldLayout = (clipWidth > clipHeight) ? "landscape" : "portrait";
  var newLayout = (newImage.width > newImage.height) ? "landscape" : "portrait";
  //calculate how much to zoom into image to fill at least one dimension of the existing clip area
  var scale = (newLayout=="portrait") ? clipWidth / newImage.width : clipHeight / newImage.height;
  //allow smaller images, but keep them at their original sizes, dont zoom them in automatically
  if (scale>1) {
    scale = 1;
  }


  //calculate the minimum and maximum scale factors for this node
/*
  //ghostWidth(inches) * 300(pixels/inch) = minWidth(pixels);
  var imgAreaWidth = parseFloat(ghostNode.getWidth()/(72*globalPageScale)); //width of image area in inches as printed
  var imgMinWidth = imgAreaWidth * 300;  //minimum width of image in pixels
  var scaleMax = parseFloat(imgMinWidth / thisHrefWidth);
  var scaleMin = scaleMax / 10;
*/

  //set variables used in resizing and scaling to new dimensions
  dropon.originalWidth = newImage.width;
  dropon.originalHeight = newImage.height;
  var scaleMin=(dropon.originalWidth < dropon.originalHeight)?ZOOM_MIN_PIXELS/dropon.originalWidth:ZOOM_MIN_PIXELS/dropon.originalHeight;

  if((dropon.originalWidth/clipWidth)<(dropon.originalHeight/clipHeight)){
    var scaleMax=ZOOM_MAX_RATIO*clipWidth/dropon.originalWidth;
  }
  else {
    var scaleMax=ZOOM_MAX_RATIO*clipHeight/dropon.originalHeight;
  }

  scaleMax=(scaleMax<ZOOM_RANGE_MAX)?scaleMax:ZOOM_RANGE_MAX;  //prevent image quality loss
  dropon.setAttribute('ms:scale-min', parseFloat(scaleMin));
  dropon.setAttribute('ms:scale-max', parseFloat(scaleMax));
  scaleNode(dropon, scale);  //scale to existing scale setting
  translateNode(dropon);

  alignNodes(dropon, ghostNode, 'center');
  alignNodes(dropon, ghostNode, 'middle');

  //set some useful variables for image manipulation
//    dropon.originalWidth = dropon.style.width;
//    dropon.originalHeight = dropon.style.height
  dropon.originalTop = dropon.style.top;
  dropon.originalLeft = dropon.style.left;
  //make sure zoom tools appear on the new image
  attachZoomTools(dropon);
  updateXMLNodeServer(dropon, 'image', true);  //last parameter shows server that this image was personalized
}

function setImgProps(node, isUnlocked) {
  isDragUnlocked = isUnlocked;
  if (isUnlocked) node.style.cursor = "move";

  //only make item draggable if required for dragging or image manipulation
  if (isUnlocked) {
    //make image draggable
    var dragging = new Draggable(node, {
      starteffect: "",
      endeffect: "",
      change: function() {
        //enable panning
        translateNode(node);
      }
    });
  

  Event.observe(node, 'mousedown', function(evt) {
      draggingEl = node;
      //keep track of last position
      var pos = getPageXY(node);
      node.lastX = pos.x;
      node.lastY = pos.y;

    },
  true);
  
  }

  //when a user has stopped panning an image, save new position to server
//  Event.observe(node, 'mouseup', function(evt) {
//    updateXMLNodeServer(node, 'image', false);
//  }, true);

  //detect mouseup on body because a user may have dragged a photo out of the clip area
  Event.observe(document.body, 'mouseup', function(evt) {
      //save any changes to a dragged element
      if (draggingEl) {
        updateXMLNodeServer(draggingEl, 'image', false);
//        startZoomToolsTimer();
        draggingEl = false;
      }
    }, 
  true);

  // TODO: find a generic way to attach transparency tools
  Event.observe(node, 'mouseover', function(evt) {
      
      if (node.id == 'image0_3') {
        $('text-image_flowRoot0_1').onmouseover = function() {
          window.selectedTransparencyNode = $('image0_3');
          attachTransparencyTools($('image0_3'));
        }
        attachTransparencyTools(node);
      }
      else {
        window.selectedNode = node;
        if (!draggingEl && !draggingThumbEl) {
          //decide whether to show zoomtools
          if (isUnlocked) {
            attachZoomTools(node);
          }
        }
      }
    },
  true);

  //get scale attributes
  node.currentScale = (node.getAttribute('ms:scale')) ? parseFloat(node.getAttribute('ms:scale')) : 1;
  
/*
  //not used
  node.minScale = (node.getAttribute('ms:scale-min')) ? parseFloat(node.getAttribute('ms:scale-min')) : ZOOM_RANGE_MIN;
  node.maxScale = (node.getAttribute('ms:scale-max')) ? parseFloat(node.getAttribute('ms:scale-max')) : ZOOM_RANGE_MAX;
*/
  //SAFARI BUG: getAttribute doesnt get the attribute
  if (browser.isSafari) {
    node.currentScale = extractAttributeFromNode('ms:scale', node);
    // not used
    //node.minScale = extractAttributeFromNode('ms:cale-min', node);
    //node.maxScale = extractAttributeFromNode('ms:scale-max', node);
  }

  //set some useful variables for image manipulation
  node.originalWidth = parseInt(node.style.width) / node.currentScale;  //offset dimensions by any scaling already applied
  node.originalHeight = parseInt(node.style.height) / node.currentScale;
  node.originalTop = node.style.top;
  node.originalLeft = node.style.left;

  scaleNode(node, node.currentScale);
//  scaleNode(node, 1);

}

function startZoomToolsTimer() {
  zoomToolsTimer = setTimeout("detachZoomTools(lastSelectedNode)", ZOOMTOOLSTIMER_DELAY);
}

function detachZoomTools(node) {

  if (draggingEl) startZoomToolsTimer();  //extend timer

  selectedNode = null;
  if (node) lastSelectedNode = node;
  draggingEl = false;
  if ($(ZOOM_TOOL_ID)) $(ZOOM_TOOL_ID).style.display = "none";
  zoomToolsTimer = null;  //reset timer
}

function attachZoomTools(node) {

  //02.02.09, TODO: remove needless calls to this functions.
  if (!$(ZOOM_TOOL_ID)) {return;}

  //get handle on ghost node
  var ghostNode = getGhostNode(node);

  //clear any existing timouts
  if (zoomToolsTimer) clearTimeout(zoomToolsTimer);

  var toolsObj = $(ZOOM_TOOL_ID);

  //make it so mousing over zoom tools doesnt trigger onmouseout of tool area and cause tool box to timeout
  Event.observe(toolsObj, 'mouseover', function(evt) {
      //clear any existing timouts
      if (zoomToolsTimer) clearTimeout(zoomToolsTimer);
    },
  true);

  toolsObj.style.display = "block";  //show tools

  //figure out where to position the zoom tools
  var nodeClipDims = getClipDims(node);  //clip dimensions for this node
  var nodePos = Position.cumulativeOffset(node);  //absolute position of this node
  var newLeft = ((nodeClipDims.left <= 0) ? nodePos[0] : nodePos[0] + parseInt(nodeClipDims.left)) + "px";
  var newTop = ((nodeClipDims.top <= 0) ? nodePos[1] - ZOOM_TOP_OFFSET : nodePos[1] - ZOOM_TOP_OFFSET + parseInt(nodeClipDims.top)) + "px";

  toolsObj.style.top = newTop;
  toolsObj.style.left = newLeft;

  //FIX IE6 BUG
//  if (browser.isIE6x) {
//    toolsObj.style.left = Position.cumulativeOffset(node)[0] + parseInt(node.getAttribute('clip_left')) + "px";
//  }

  //make sure tool area isn't placed outside of page area
  var pagePosX = Position.cumulativeOffset($(PAGE_LEFT_ID))[0];
  var pagePosY = Position.cumulativeOffset($(PAGE_LEFT_ID))[1];
  if (parseInt(pagePosY) > parseInt(toolsObj.style.top)) {
    //toolsObj.style.top = pagePosY + "px";
  }
  if (pagePosX > parseInt(toolsObj.style.left)) {
    toolsObj.style.left = pagePosX + "px";
  }

  //make sure tool area closes after mouseout
  node.onmouseout = function() {
    lastSelectedNode = node;
    //set timer to close the zoom tools
    if (!draggingEl)
      startZoomToolsTimer();
  }

  toolsObj.onmouseout = function() {
    if (!draggingEl) {
      startZoomToolsTimer();
      //Prevent fireing onChange event if there was no drag and
      //as result no modification
      if(zoomHandle.dragging)
        zoomHandle.finishDrag(null,true);
    }
  }

  //make sure the current node and tool box are on top
  toolsObj.style.zIndex = 100;

  //set the zoom slider to correspond to the current node
  //SAFARI BUG: getAttribute doesnt get the attribute
  var zoomSetting = parseFloat((browser.isSafari) ? extractAttributeFromNode('ms:scale', node) : node.getAttribute('ms:scale'));

  var zoomMin = parseFloat((browser.isSafari) ? extractAttributeFromNode('ms:scale-min', node) : node.getAttribute('ms:scale-min'));
  var zoomMax = parseFloat((browser.isSafari) ? extractAttributeFromNode('ms:scale-max', node) : node.getAttribute('ms:scale-max'));

  //get min and max range of slider for this image
  if(zoomMax && zoomMin){
    zoomHandle.range = $R(parseFloat(zoomMin), parseFloat(zoomMax));
    zoomSetting=(zoomSetting<zoomMax)?zoomSetting:zoomMax;
    zoomSetting=(zoomSetting>zoomMin)?zoomSetting:zoomMin;
  }
  else{
    zoomHandle.range = $R(ZOOM_RANGE_MIN, ZOOM_RANGE_MAX)
    zoomSetting=(zoomSetting<ZOOM_RANGE_MAX)?zoomSetting:ZOOM_RANGE_MAX;
    zoomSetting=(zoomSetting>ZOOM_RANGE_MIN)?zoomSetting:ZOOM_RANGE_MIN;
  }
  //Avoid fireing onChange event during setting setValue
  var oc=zoomHandle.options.onChange;
  zoomHandle.options.onChange=null;
  zoomHandle.setValue(zoomSetting);
  zoomHandle.options.onChange=oc;

}

function setInitialZoomValue(node) {
  //set the zoom slider to correspond to the current node
  //SAFARI BUG: getAttribute doesnt get the attribute
  var zoomSetting = parseFloat((browser.isSafari) ? extractAttributeFromNode('ms:scale', node) : node.getAttribute('ms:scale'));

  var zoomMin = parseFloat((browser.isSafari) ? extractAttributeFromNode('ms:scale-min', node) : node.getAttribute('ms:scale-min'));
  var zoomMax = parseFloat((browser.isSafari) ? extractAttributeFromNode('ms:scale-max', node) : node.getAttribute('ms:scale-max'));

  //get min and max range of slider for this image
  if(zoomMax && zoomMin){
    zoomHandle.range = $R(parseFloat(zoomMin), parseFloat(zoomMax));
    zoomSetting=(zoomSetting<zoomMax)?zoomSetting:zoomMax;
    zoomSetting=(zoomSetting>zoomMin)?zoomSetting:zoomMin;
  }
  else{
    zoomHandle.range = $R(ZOOM_RANGE_MIN, ZOOM_RANGE_MAX)
    zoomSetting=(zoomSetting<ZOOM_RANGE_MAX)?zoomSetting:ZOOM_RANGE_MAX;
    zoomSetting=(zoomSetting>ZOOM_RANGE_MIN)?zoomSetting:ZOOM_RANGE_MIN;
  }
  //Avoid fireing onChange event during setting setValue
  var oc=zoomHandle.options.onChange;
  zoomHandle.options.onChange=null;
  zoomHandle.setValue(zoomSetting);
  zoomHandle.options.onChange=oc;
}

function startTransparencyToolsTimer() {
  transparencyToolsTimer = setTimeout("detachTransparencyTools(lastSelectedNode)", ZOOMTOOLSTIMER_DELAY);
}

function detachTransparencyTools(node) {
  
  if (draggingEl) startTransparencyToolsTimer();  //extend timer

  window.selectedTransparencyNode = null;
  if (node) lastSelectedNode = node;
  draggingEl = false;
  if ($(TRANSPARENCY_TOOL_ID)) $(TRANSPARENCY_TOOL_ID).style.display = "none";
  transparencyToolsTimer = null;  //reset timer
}

function validateTransparencyTool(src) {
  var valid = false;
  var urls = [
    '/mc_stripe-small',
    '/fh_stripe-small',
    '/friendly_stripe-small',
    '/tiffany_stripe-small'
  ];
  for (var i=0; i<urls.length; i++) {
    if (src.lastIndexOf(urls[i]) != -1)
      valid = true;
  }
  return valid;
}

function getTransparencySource(src, v) {
  v = parseInt(v);
  var pattern = /((-[0-9]+\.png)|(\.jpg))$/;
  return src.replace(pattern, '-' + v + '.png');
}

function getTransparencyValue(src) {
  var value = 10;
  var match = src.match(/-[0-9]+\.png$/)
  if (match) {
    value = match[0].replace('-', '').replace('.png', '');
    value = parseInt(value);
  }
  return value;
}

function attachTransparencyTools(node) {

  //02.02.09, TODO: remove needless calls to this functions.
  if (!$(TRANSPARENCY_TOOL_ID)) {return;}

  if(!validateTransparencyTool(node.src)) {return;}

  //clear any existing timouts
  if (transparencyToolsTimer) clearTimeout(transparencyToolsTimer);
  
  var toolsObj = $(TRANSPARENCY_TOOL_ID);

  //make it so mousing over zoom tools doesnt trigger onmouseout of tool area and cause tool box to timeout
  Event.observe(toolsObj, 'mouseover', function(evt) {
      //clear any existing timouts
      if (transparencyToolsTimer) clearTimeout(transparencyToolsTimer);
    },
  true);

  toolsObj.style.display = "block";  //show tools

  //figure out where to position the zoom tools
  var nodeClipDims = getClipDims(node);  //clip dimensions for this node
  var nodePos = Position.cumulativeOffset(node);  //absolute position of this node
  var newLeft = ((nodeClipDims.left <= 0) ? nodePos[0] : nodePos[0] + parseInt(nodeClipDims.left)) + "px";
  var newTop = ((nodeClipDims.top <= 0) ? nodePos[1] - ZOOM_TOP_OFFSET : nodePos[1] - ZOOM_TOP_OFFSET + parseInt(nodeClipDims.top)) + "px";
  
  toolsObj.style.top = newTop;
  toolsObj.style.left = newLeft;

  //FIX IE6 BUG
//  if (browser.isIE6x) {
//    toolsObj.style.left = Position.cumulativeOffset(node)[0] + parseInt(node.getAttribute('clip_left')) + "px";
//  }

  //make sure tool area isn't placed outside of page area
  var pagePosX = Position.cumulativeOffset($(PAGE_LEFT_ID))[0];
  var pagePosY = Position.cumulativeOffset($(PAGE_LEFT_ID))[1];
  if (parseInt(pagePosY) > parseInt(toolsObj.style.top)) {
    //toolsObj.style.top = pagePosY + "px";
  }
  if (pagePosX > parseInt(toolsObj.style.left)) {
    toolsObj.style.left = pagePosX + "px";
  }

  //make sure tool area closes after mouseout
  node.onmouseout = function() {
    lastSelectedNode = node;
    //set timer to close the zoom tools
    if (!draggingEl) 
      startTransparencyToolsTimer();
  }
  
  toolsObj.onmouseout = function() {
    if (!draggingEl) {
      startTransparencyToolsTimer();
      //Prevent fireing onChange event if there was no drag and
      //as result no modification
      if(transparencyHandle.dragging)
        transparencyHandle.finishDrag(null,true);
    }
  }

  //make sure the current node and tool box are on top
  toolsObj.style.zIndex = 100;

  var zoomSetting = getTransparencyValue(node.src);

  //Avoid fireing onChange event during setting setValue 
  var onChange = transparencyHandle.options.onChange;
  transparencyHandle.options.onChange = null;
  transparencyHandle.setValue(zoomSetting);
  transparencyHandle.options.onChange = onChange;
}


function getPageXY(elm)
{
  var point = {x: 0, y: 0};
  while (elm)
  {
    point.x += elm.offsetLeft;
    point.y += elm.offsetTop;
    elm = elm.offsetParent;
  }
  return point;
}

function setPageXY(elm, x, y)
{
  var parentXY = {x: 0, y: 0};

  if (elm.offsetParent)
  {
    parentXY = getPageXY(elm.offsetParent);
  }

  elm.style.left = (x - parentXY.x) + 'px';
  elm.style.top  = (y - parentXY.y) + 'px';
}

function clearPage(pageElId) {
  var pageEl = $(pageElId);
  var containerNode = pageEl;

  detachZoomTools();  //hide the zoom tools

  //remove any droppables on the page because this causes errors!
  var dropList;
  if (pageEl) dropList = pageEl.getElementsByClassName(DROPPABLE_CLASSNAME);
  if(dropList) for (var i=0; i<dropList.length; i++) {
    Droppables.remove(dropList[i]);
  }

  //wipe the page clean
  if (containerNode)
    containerNode.innerHTML = "";

//  var pickin = containerNode.childNodes;  //pidgin english for children
//  $A(pickin);
//  for (var i=0; i<pickin.length; i++) {
//    //remove all the book elements from the screen
//    pickin[i].removeNode(true);
//    containerNode.removeChild(pickin[i]);
//  }
}

function loadBlock(thisBlockId) {
  blockId = thisBlockId;  //set global
  resellBlockId = thisBlockId; // save blockId in case user wants to resell it
  
  currentPage = startPage;
  getBlockMetaData(blockId);
  gotoPage(currentPage);
  
}

function parseTags(strTags) {
  //tags are space delimited
  //convert any commas entered by user into spaces
  strTags = strTags.replace(/,/g, " ");
  //tags in quotes are multi-word tags
  //server expects all tags to be semi-colen delimited
  strTags += " ";  //pattern matching needs an extra space because it's buggy
  var wordPattern = /(['"^\s])?([^'"])+?(['"\$\s])/gi;
  var matches = strTags.match(wordPattern);
  var strMatches = "";
  if (matches) {
    for (var i=0; i<matches.length; i++) {
      matches[i] = matches[i].replace(/['"\s]/gi, "");
    }
    strMatches = matches.join(';');  //make into a semi-colen delimted list
  }
  return strMatches;
}

function setBlockStatus(strStatus) {
  //saving block means flipping the metadata status bit to Saved
  //publishing means setting status to Published
  switch(strStatus) {
    case "Published_Public":
      globalBlockData.status = "Published";
      globalBlockData.publicStatus = true;
      break;
    case "Published_Private":
      globalBlockData.status = "Published";
      globalBlockData.publicStatus = false;
      break;
    default:
      globalBlockData.status = strStatus;
    }
  
  saveBlockMetaData();
  
  clearLightBox();  //close any open lightboxes
  if(strStatus == "Published_Public" || strStatus == "Published_Private")
    setEditorType("wizard");
}

function purchaseBlock() {
  var pars = "id=" + blockId;
  clearLightBox();

  var myAjax = new Ajax.Request(
    PURCHASE_BLOCK_URL,
    {
      method: 'post',
      postBody: pars,
      onComplete: function(req) {
      },
      onFailure: function(req) {
        console.error(req.responseText);
      },
      onSuccess: function(req) {
        var response = req.responseXML;
        if (response) {
          var responseCode = parseInt(getXMLTagValue(response, 'responseCode'));
          var responseMessage = getXMLTagValue(response, 'responseMessage');

          switch(responseCode) {
            case 1000:
//              console.error('This block has been purchased');
              var redirectUrl = getXMLTagValue(response, 'redirect');
              window.location.href = redirectUrl;
              break;
            default:
              console.error('Failed purchasing block!');
              break;
          }
        }
      }
  });
  return false;
}

function publishBlock(forceSave) {
  var pars = "id=" + blockId;
  pars += "&rand=" + getRandom();  //tack on a random number to prevent IE6 caching
  if($("royalty")!=null&&$("royalty").checked){
      pars +="&royalty=true"
  }
  if ($('field_description')) {
    pars += '&description=' +  Url.encodeURIComponent($('field_description').value);
  }
  if (forceSave){
    pars += "&forceSave=true";
  }
  var myAjax = new Ajax.Request(
    PUBLISH_BLOCK_URL,
    {
      method: 'post',
      postBody: pars,
      onComplete: function(req) {
      },
      onSuccess: function(req) {
        var response = req.responseXML;
        if (response) {
          var responseCode = parseInt(getXMLTagValue(response, 'responseCode'));
          var responseMessage = getXMLTagValue(response, 'responseMessage');

          switch(responseCode) {
            case 1000:
              clearLightBox();
              fireAjaxEvent("onPublishBlockSuccess");
              window.location.href = '/blocks/edit/promote?block-id=' + blockId;
              break;
            case 9110:
              var strMessage = 'You do not have permission to publish this block';
              openLightBox_NEW('error', 'Error', strMessage);
              break;
            case 9120:
              var pages = getXMLTagValue(response, 'uneditedPages');
              var strMessage = "This block is not ready to be published.";
              strMessage += "\nYou must first personalize the photos and text on the following pages:";
              strMessage += "\n" + pages.replace(/;/g, ", ");
              
              openLightBox_NEW('error', 'Error', strMessage);
              break;
            case 9170:
              var pages = getXMLTagValue(response, 'uneditedPages');
              var strMessage = "It looks like you haven’t written captions for all your photos. ";
              strMessage += "\nClick “OK” to proceed anyway (in those spots, the caption space will be empty). ";
              strMessage += "Click “Cancel” to go back and add captions. ";
              strMessage += "\n" + pages.replace(/;/g, ", ");
              openLightBox_NEW('images-not-ready', 'Oops!', strMessage);
              break;
            case 9180:
              var pages = getXMLTagValue(response, 'uneditedPages');
              var strMessage = "It looks like you haven’t written captions for all your photos. ";
              strMessage += "\nClick “OK” to proceed anyway (in those spots, the caption space will be empty). ";
              strMessage += "Click “Cancel” to go back and add captions. ";
              strMessage += "\n" + pages.replace(/;/g, ", ");
              openLightBox_NEW('text-not-ready', 'Oops!', strMessage);
              break;

            case 9190:
              var pages = getXMLTagValue(response, 'uneditedFields');
              var strMessage = "It looks like you’re still missing some fields. ";
              strMessage += " Before you can publish your block, you’ll need to go back and edit following fields:";
              strMessage += "\n" + pages.replace(/;/g, ", ");
              openLightBox_NEW('fields-not-ready', 'Oops!', strMessage);
              break;
            default:
              break;
          }
          
        }
      }
  });
}

function forwardBlock(email, message, block) {

  if (!validateMultipleEmail(email)) {
    openLightBox_NEW('error', 'Error','Please specify a valid email address');
    return;
  }

  var sendCopyToSelf = false;
  if(($("input-send-to-self")).checked) {
    sendCopyToSelf = true;
  }

  if (!block) {
    block = blockId;
  }

  if (typeof(block) == 'undefined' || !block) {
    console.error('BlockId is null');
    return;
  }

  var pars = "id=" + block;
  pars += "&email=" + Url.encodeURIComponent(email);
  pars += "&message=" + Url.encodeURIComponent(message);
  pars += sendCopyToSelf ? "&sendCopyToSelf=true" : "&sendCopyToSelf=false";
  pars += "&rand=" + getRandom();  //tack on a random number to prevent IE6 caching

  clearLightBox();


  var myAjax = new Ajax.Request(
    SEND_BLOCK_URL,
    {
      method: 'post',
      postBody: pars,
      onComplete: function(req) {
      },
      onSuccess: function(req) {
        var response = req.responseXML;
        if (response) {
          var responseCode = parseInt(getXMLTagValue(response, 'responseCode'));
          var responseMessage = getXMLTagValue(response, 'responseMessage');

          switch(responseCode) {
            case 1000:
              openLightBox_NEW('error', 'Success', 'This block has been sent to ' + email);
              break;
            default:
              openLightBox_NEW('error', 'Success', 'Failed sending email!');
              break;
          }
        }
      }
  });

  return;
}

function setNoteWizard(noteObj) {
  var strNote = noteObj.value;
}

function setEditorType(strType) {
  //set the editor level and permissions settings
  globalEditorType = strType;

  userId = null;
  switch(globalEditorType) {
    case 'edit_cover':
      break;
    case 'view_cover':
      break;
    case 'admin':
      break;
    case 'preview':
      //hide all tools
      break;
    case 'basic':
      break;
//    case 'personal':
//      break;
    case 'wizard':
      break;
      
    default:
      break;
  }

}
function forceSave() {
  clearLightBox();
  saveBlockMetaData(null, "true");
}

function saveBlockMetaData(type, forceSave) {
  //GET TITLE
  var blockTitle = $('input_title')?$('input_title').value:globalBlockData?globalBlockData.title:"";
  //GET DESCRIPTION
  var blockDesc = $('input_description') ? $('input_description').value :
    (globalBlockData ? String.toHtmlNewLines(globalBlockData.eventDescription) : "");
  var eventName = $('input_event_name')?$('input_event_name').value:globalBlockData?globalBlockData.eventName:"";
  var eventLocation = $('input_location')?$('input_location').value:globalBlockData?globalBlockData.eventLoaction:"";
  var eventDate = $('input_date')?$('input_date').value:globalBlockData?globalBlockData.eventDate:"";
  //var eventDate = ''; // globalBlockData.eventDate;
  //GET CURRENT STATUS
  var blockStatus = globalBlockData?globalBlockData.status:"";
  var publicStatus = globalBlockData?globalBlockData.publicStatus:"";
  var wallpaperWords = $('input_words')?$('input_words').value:globalBlockData?globalBlockData.wallpaperWords:"";
  var quote = $('input_quote')?$('input_quote').value:globalBlockData?globalBlockData.quote:"";
  var quoteSource = $('input_quote_source')?$('input_quote_source').value:globalBlockData?globalBlockData.quoteSource:"";

  //GET TAGS
  //prepend new tags the user has entered into hidden tag field
  var blockTags=globalBlockData?globalBlockData.tags:"";

  //update the title and description for this book
  var pars = "id=" + blockId;
  pars += "&no_thumb="+(type=="no_thumb"?"true":"false");
  if (blockTitle) pars += "&title=" +  Url.encodeURIComponent(blockTitle);
  if (blockDesc) pars += "&eventDescription=" + Url.encodeURIComponent(blockDesc);
  if (eventName) pars += "&event_name=" + Url.encodeURIComponent(eventName);
  if (eventLocation) pars += "&event_location=" + Url.encodeURIComponent(eventLocation);
  if (eventDate) pars += "&event_date=" + Url.encodeURIComponent(eventDate);
  if (blockTags) pars += "&tags=" + Url.encodeURIComponent(blockTags);
  if (blockStatus) pars += "&status=" + Url.encodeURIComponent(blockStatus);
  if (wallpaperWords) pars += "&wallpaper_words=" + Url.encodeURIComponent(wallpaperWords);
  if (quote) pars += "&quote=" + Url.encodeURIComponent(quote);
  if (quoteSource) pars += "&quote_source=" + Url.encodeURIComponent(quoteSource);
  if(null!=forceSave){
      pars+="&forceSave="+forceSave
  }
  pars += "&public_status=";
  pars += (publicStatus == true)?"true":"false"
  pars += "&rand=" + getRandom();  //tack on a random number to prevent IE6 caching

  var myAjax = new Ajax.Request(
    SAVE_BLOCK_METADATA_URL,
    {
      method: 'get',
      parameters: pars,
      onComplete: function(req) {
      },
      onSuccess: function(req) {
        var response = req.responseXML;

        if (response) {
          var responseCode = parseInt(getXMLTagValue(response, 'responseCode'));
          var responseMessage = getXMLTagValue(response, 'responseMessage');

          switch(responseCode) {

            case 1000:

              var newBookId = getXMLTagValue(response, 'blockId');
              if (newBookId) updateBlockId(newBookId);
              
              fireAjaxEvent("onSaveBlockMetaDataSuccess", response);

              getBlockMetaData(blockId);
              break;
            case 9120:
              var pages = getXMLTagValue(response, 'uneditedPages');
              var strMessage = "This block is not ready to be published.";
              strMessage += "\nYou must first personalize the photos and text on the following pages:";
              strMessage += "\n" + pages.replace(/;/g, ", ");
              openLightBox_NEW('error', 'Error', strMessage);
              return;
              break;
            case 9170:
              var pages = getXMLTagValue(response, 'uneditedPages');
              var strMessage = "It looks like you’re still missing photos. ";
              strMessage += " Before you can publish your block, you’ll need to go back and add a photo to the following pages:";
              strMessage += "\n" + pages.replace(/;/g, ", ");
              openLightBox_NEW('images-not-ready', 'Oops!', strMessage);
              break;
            case 9180:
              var pages = getXMLTagValue(response, 'uneditedPages');
              var strMessage = "It looks like you haven’t written captions for all your photos. ";
              strMessage += "\nClick “OK” to proceed anyway (in those spots, the caption space will be empty). ";
              strMessage += "Click “Cancel” to go back and add captions. ";
              strMessage += "\nThe pages where captions are missing are:" + pages.replace(/;/g, ", ");
              openLightBox_NEW('text-not-ready', 'Oops!', strMessage );
              break;
           case 9190:
              var pages = getXMLTagValue(response, 'uneditedFields');
              var strMessage = "It looks like you’re still missing some fields. ";
              strMessage += " Before you can publish your block, you’ll need to go back and edit following fields:";
              strMessage += "\n" + pages.replace(/;/g, ", ");
              openLightBox_NEW('fields-not-ready', 'Oops!', strMessage);
              break;
            default:

              var responseMessage = getXMLTagValue(response, 'responseMessage');
              var strMessage = responseMessage;
              openLightBox_NEW('error', 'Error', strMessage);
              break;
          }
        }
      }
  });

}

function deleteBlock(blockId) {

  //update the title and description for this book
  var pars = "blockId=" + blockId;

  var myAjax = new Ajax.Request(
    DELETE_BLOCK,
    {
      method: 'get',
      parameters: pars,
      onComplete: function(req) {
      },
      onSuccess: function(req) {
        var response = req.responseXML;
        
        if (response) {
          var responseCode = parseInt(getXMLTagValue(response, 'responseCode'));
          var responseMessage = getXMLTagValue(response, 'responseMessage');

          switch(responseCode) {
            case 1000:
              window.location.href = "/user/dashboard";
              break;
          }
        }
      }
  });
}

function getBlockMetaData(blockId) {
  //load metadata about this book
  var pars = "id=" + blockId;
  pars += "&rand=" + getRandom();  //tack on a random number to prevent IE6 caching

  var myAjax = new Ajax.Request(
    GET_BLOCK_METADATA_URL,
    {
      method: 'get',
      parameters: pars,
      onComplete: function(req) {
      },
      onSuccess: function(req) {
        var response = req.responseXML;
        if (response) {
          var responseCode = response.getElementsByTagName('responseCode')[0].firstChild.nodeValue;
          switch(responseCode) {
            case RESPONSE_CODES.SUCCESS:
              var blockData = {
                title:     getXMLTagNonNullValue(response, 'title'),
                description:   getXMLTagNonNullValue(response, 'description'),
                eventDescription:   getXMLTagNonNullValue(response, 'eventDescription'),
                eventName:   getXMLTagNonNullValue(response, 'eventName'),
                eventLocation:   getXMLTagNonNullValue(response, 'eventLocation'),
                eventDate:   getXMLTagNonNullValue(response, 'eventDate'),
                status:   getXMLTagValue(response, 'status'),
    //              categories:   getXMLTagValue(response, 'categories'),
                tags:     getXMLTagValue(response, 'tags'),
                themeId:  getXMLTagValue(response, 'themeId'),
                userId:   getXMLTagValue(response, 'userId'),
                userName:   getXMLTagValue(response, 'userName'),
                createDate:   getXMLTagValue(response, 'createDate'),
                modifiedDate:   getXMLTagValue(response, 'modifiedDate'),
                blockType:   getXMLTagValue(response, 'blockType'),
                numPages:   getXMLTagValue(response, 'numberOfPages'),
                rate:   getXMLTagValue(response, 'rank'),
                price:   getXMLTagValue(response, 'price'),
                smallThumbnail:   getXMLTagValue(response, 'smallThumbnailUri'),
                rated:   (getXMLTagValue(response, 'rated')=="true")?true:false,
                wallpaperWords:     getXMLTagValue(response, 'wallpaperWords'),
                quote:     getXMLTagValue(response, 'quote'),
                quoteSource:     getXMLTagValue(response, 'quoteSource')
              };
              isSeller= (getXMLTagValue(response, 'isSeller')=="true")?true:false;
              blockData.publicStatus=(getXMLTagValue(response, 'publicStatus')=="true")?true:false;
              //set pagination mechanism to link to all pages
              blockData.numPages = parseInt(blockData.numPages);
              globalBlockData = blockData;


              setPagination(globalBlockData.numPages);  //set pages
              updateBlockData(globalBlockData);  //display info
              loadBlockBehaviors();  //update lightbox behavior based on user type
              fireAjaxEvent("onGetBlockMetaDataSuccess", response);
              break;
            case RESPONSE_CODES.UNAUTHORIZED_ACCESS:
              openLightBox_NEW('error', 'Oops!', USER_MESSAGES.CANT_VIEW_BLOCK, {onclose:function() {window.location.href = "/blocks/gallery";}});
              break;
          }
        }
    }
  });
}

function getXMLTagValue(response, tagName) {
  var returnVal = null;
  if (response) {
    if (response.getElementsByTagName(tagName) && response.getElementsByTagName(tagName).length && response.getElementsByTagName(tagName)[0].firstChild) {
      returnVal = response.getElementsByTagName(tagName)[0].firstChild.nodeValue;
    }
    else {
    }
  }
  return returnVal;
}

function getXMLTagNonNullValue(response, tagName) {
  var res = getXMLTagValue(response, tagName);
  if (res == null) return ""; else return res;
}

function getResponseCode(response) {
  return parseInt(getXMLTagValue(response, 'responseCode'));
}


function updateBlockData(blockData)
{
}

function setBlockViewMode(newMode, action) { 
  //if choosing single or double mode, show button selected states
  if (action == "add" && newMode != "cover") {
    //set global variable
    blockViewMode = newMode;

    //highlight selected mode button
    //if($('page-single')) Element.classNames($('page-single')).remove('selected');  
    //if($('page-double')) Element.classNames($('page-double')).remove('selected');
    //if($('page-' + newMode)) Element.classNames($('page-' + newMode)).add('selected');
  }
  

  //action is either add or remove className
  //change classes
  if($(PAGES_CONTAINER_ID)) eval('Element.classNames($(PAGES_CONTAINER_ID)).' + action + '(newMode)');
  if($(PAGE_LEFT_ID)) eval('Element.classNames($(PAGE_LEFT_ID)).' + action + '(newMode)');
  //if($(PAGE_RIGHT_ID)) eval('Element.classNames($(PAGE_RIGHT_ID)).' + action + '(newMode)');
  if($(PAGE_LEFT_CONTAINER_ID)) eval('Element.classNames($(PAGE_LEFT_CONTAINER_ID)).' + action + '(newMode)');
  //if($(PAGE_RIGHT_CONTAINER_ID)) eval('Element.classNames($(PAGE_RIGHT_CONTAINER_ID)).' + action + '(newMode)');
  //eval('Element.classNames($$("#" + NEXT_PAGE_BUTTON_ID + " a")[0]).' + action + '(newMode)');
  //eval('Element.classNames($$("#" + PREV_PAGE_BUTTON_ID + " a")[0]).' + action + '(newMode)');
  
  if($$('.pageViewPrev').length>0) eval('Element.classNames($$(".pageViewPrev")[0]).' + action + '(newMode)');
  if($$('.pageViewNext').length>0) eval('Element.classNames($$(".pageViewNext")[0]).' + action + '(newMode)');
  
  
  if($('theme')) eval('Element.classNames($("theme")).' + action + '(newMode)');
  if($('theme_label')) eval('Element.classNames($("theme_label")).' + action + '(newMode)');
  if($('book-background')) eval('Element.classNames($("book-background")).' + action + '(newMode)');
  if($(NEXT_PAGE_BUTTON_ID)) eval('Element.classNames($("template_next_page")).' + action + '(newMode)');

  if($('blockView')) {$('blockView').show();}
}

function setPagination(numPages) {
  var maxLinks = 30;
  var startPage = 1;
  var containerNode = $(PAGINATION_CONTAINER_ID);
  if (!containerNode) {return;}
  var strPageLinks = "";

  //are either of the front or back covers selected?
  var backCoverPage = numPages + 1;
  //show links to cover and first page
  var frontCoverSelected = (currentPage==0) ? " class='active' " : "";
  var backCoverSelected = (currentPage==backCoverPage) ? "  class='active'  " : "";
  

  switch (globalEditorType) {
    case 'single':
      break;
      
    case 'view_cover':
      // redirect to block viewing page
      var href = '/blocks/view?block-id=' + blockId + '&page=';
      strPageLinks += '<li class="pageFront"><a ' + frontCoverSelected + ' href="' + href + '0">FRONT COVER</a></li>';
      for (var i=startPage; i<=numPages; i=i+2) {
        var selected = "";
        if (i == currentPage || i+1 == currentPage) {
          strPageLinks += '<li class="pageDoubleLeft"><a class="active" href="'+href+(i+1)+'">'+i+'</a></li>';
          strPageLinks += '<li class="pageDoubleRight"><a class="active" href="'+href+(i+1)+'">'+(i+1)+'</a></li>';
          }
        else {
          strPageLinks += '<li class="pageDoubleLeft"><a class="" href="'+href+(i+1)+'">'+i+'</a></li>';
          strPageLinks += '<li class="pageDoubleRight"><a class="" href="'+href+(i+1)+'">'+(i+1)+'</a></li>';
        } 
      }
      strPageLinks += '<li class="pageBack"><a '+backCoverSelected+' href="'+href + backCoverPage +'">BACK COVER</a></li>';
      containerNode.innerHTML = strPageLinks;
      break;
      
    default:
      //show normal links
      if (browseType == 'browse') {
        strPageLinks += '<li class="pageFront"><a '+frontCoverSelected+' href="javascript:gotoPage(0);">FRONT COVER</a></li>';
      }
      else {
        strPageLinks += '<li class="pageFront"><a '+frontCoverSelected+' href="javascript:gotoPage(0);">FRONT COVER</a></li>';
      }
      for (var i=startPage; i<=numPages; i=i+2) {
        var selected = "";
        if (i == currentPage || i+1 == currentPage) {
          strPageLinks += '<li class="pageDoubleLeft"><a class="active" href="#" onclick="return false;">'+i+'</a></li>';
          strPageLinks += '<li class="pageDoubleRight"><a class="active" href="#" onclick="return false;">'+(i+1)+'</a></li>';
        }
        else {
          strPageLinks += '<li class="pageDoubleLeft"><a class="" href="javascript:gotoPage('+(i+1)+');">'+i+'</a></li>';
          strPageLinks += '<li class="pageDoubleRight"><a class="" href="javascript:gotoPage('+(i+1)+');">'+(i+1)+'</a></li>';
        }
      }
      strPageLinks += '<li class="pageBack"><a '+backCoverSelected+' href="javascript:gotoPage('+ backCoverPage +')">BACK COVER</a></li>';
      containerNode.innerHTML = strPageLinks;
      break;
  }
}


function loadPage(pageElId, url, blockId, pageNum) {

  leftPageNum = pageNum;

  //cancel any other ajax requests for pages that may update into this page on screen
  if (arrTransport[pageElId]) {
    arrTransport[pageElId].transport.abort();
    arrTransport[pageElId] = null;
  }

  //if this is the back cover, load page 0, which has both covers
  if (pageNum == 31) {
    isBackCoverPage = true;  //flag this as the back cover
    pageNum = 0;
  }

  var pageEl = $(pageElId);
  pageEl.style.overflow = "hidden";
  var clipPath = "0px,0px," + pageEl.getWidth() + "px," + pageEl.getHeight() + "px";
  pageEl.style.clip = "rect(" + clipPath + ")";

  var pars = "id=" + blockId + "&pages=" + Math.ceil(pageNum/2);
  //pars += (typeof(blockViewPage)!='undefined' && blockViewPage=="browse") ? "&textSize=large" : "&textSize=large";  //load bigger text-images if in single mode view
  pars += "&textSize=large";
  
  pars += (window.loadUsedImages)? "&getImages=true":"&getImages=false";
  window.loadUsedImages = false;
  pars += "&rand=" + getRandom();  //tack on a random number to prevent IE6 caching

  fireAjaxEvent("onLoadBlock");

  //show loading message for this page;
  var overlayEl = showLoadingOverlay(pageEl);

  //if we already cached the page, use the cached copy.
//  var responseExists = pageModifiedXML[pageNum];
//  if (responseExists) {
//    parsePage(pageElId, responseExists);
//    hideOverlay(pageEl, overlayEl);
//  }
//  else {
    //load a new copy of this page from the server
    arrTransport[pageElId] = new Ajax.Request(
      url,
      {
        method: 'get',
        parameters: pars,
        onComplete: function(req) {
            //clear any loading overlays
            hideOverlay(pageEl, overlayEl);
//            displayGhosts('none');
            hiliteGhosts(pageEl);
            showResponse(pageEl, req);
        },
        onSuccess: function(req) {
          var response = req.responseXML;
          var responseCode = response.getElementsByTagName('responseCode')[0].firstChild.nodeValue;
          switch(responseCode) {
            case RESPONSE_CODES.SUCCESS:
              storePageXML(response, pageNum);
              parsePage(pageElId, response);
              break;
            case RESPONSE_CODES.UNAUTHORIZED_ACCESS:
              openLightBox_NEW('error', 'Oops!', USER_MESSAGES.CANT_VIEW_BLOCK, {onclose:function() {window.location.href = "/blocks/gallery";}});
              break;
          }
          fireAjaxEvent("onLoadBlockSuccess");
        }
      });
//  }
}

function gotoPage(pageNum) {
  currentPage = pageNum;

  showPageNavigationArrows(currentPage);

  var numPages = globalBlockData ? globalBlockData.numPages + 1 : 31;
  window.isBackCoverPage = false;  //assume this is not the back cover
  window.isFrontCoverPage = false;  //assume this is not the front cover

  //figure out which page goes where
  var leftPage = currentPage;
  leftPageNum = null;
  
  //clear the contents of the pages
  clearPage(PAGE_LEFT_ID); // clear display

  if (currentPage == 0) {
    //set class for cover
    setBlockViewMode('cover front', 'add');
    window.isFrontCoverPage = true;  //flag this as the back cover
  }
  else if (currentPage == numPages) {
    //set class for cover
    setBlockViewMode('cover back', 'add');

    //load page 0, which has both covers
    window.isBackCoverPage = true;  //flag this as the back cover
    leftPage = 0;
  }
  else {
    //remove classes for cover if any set
    setBlockViewMode('cover', 'remove');
    setBlockViewMode('front', 'remove');
    setBlockViewMode('back', 'remove');
  }


  // if we are in browse mode front cover must be a link to open the book
  if (window.enableFrontPageLink) {
    var coverEl = $(PAGE_LEFT_ID);
    if (coverEl) {
      if (window.isFrontCoverPage) {
        coverEl.onclick = nextPage;
        coverEl.style.cursor = "pointer";
      }
      else {
        coverEl.onclick = null;
        coverEl.style.cursor = "inherit";
      }
    }
  }


  if (leftPage != null) {
    //load left page
    leftPageNum = leftPage;
    loadPage(PAGE_LEFT_ID, GET_BLOCK_XML_URL, blockId, leftPage);
  }
  
  if (globalBlockData) {
    setPagination(globalBlockData.numPages);
  }
}

function previousPage() {
/*  //if in single page view mode or about to look at the cover, we go backwards by only one page, otherwise skip 2
  if ((blockViewMode == "single") || (currentPage==1) || (currentPage==2) || (currentPage==globalBlockData.numPages+1)) currentPage--;
  else */
  currentPage -= 2;  //get previous two pages
  currentPage = (currentPage<=0) ? 0 : currentPage;  //prevent from going above max pages
  showPageNavigationArrows(currentPage);
  gotoPage(currentPage);
}

function nextPage() {
/*
  //if in single page view mode or looking at the cover or first page, we go forward by only one page, otherwise skip 2
  if ((blockViewMode == "single") || (currentPage==0) || (currentPage==1) || (currentPage==globalBlockData.numPages))
    currentPage++;
  else 
  */

  currentPage += 2; // get next two pages otherwise    
  currentPage = (currentPage > globalBlockData.numPages+1) ? globalBlockData.numPages+1 : currentPage;  // prevent from going above max pages
  showPageNavigationArrows(currentPage);
  gotoPage(currentPage);
}

function showPageNavigationArrows(currentPage) {
  if($(PREV_PAGE_BUTTON_ID)) {
    if (currentPage <= 0)
      $(PREV_PAGE_BUTTON_ID).style.visibility = 'hidden';
    else
      $(PREV_PAGE_BUTTON_ID).style.visibility = 'visible';  
  }

  if($(NEXT_PAGE_BUTTON_ID)){
    if (currentPage >= 31)
      $(NEXT_PAGE_BUTTON_ID).style.visibility = 'hidden';
    else
      $(NEXT_PAGE_BUTTON_ID).style.visibility = 'visible';  
  } 
}

function hideOverlay(pageEl, overlayEl) {
  if (pageEl) {
    //remove overlay from the specified page
    pageEl.removeChild(overlayEl);
  }
}

function showLoadingOverlay(pageEl) {
  //create the loading image for this page
  if(pageEl.id=="template_left_page" || pageEl.id=="blockSpotlightInside_1" || pageEl.id=="blockSpotlightInside_2" ){
    var node = document.createElement('div');
    var img_right = document.createElement('img');
    img_right.setAttribute('src', '/images/loading_trans.gif');
    var img_left= document.createElement('img');
    img_left.setAttribute('src', '/images/loading_trans.gif');
    
    
    Element.classNames(img_right).add('loading_icon_right');
    Element.classNames(img_left).add('loading_icon_left');
    
    node.appendChild(img_left);
    node.appendChild(img_right);

  }
  else if(pageEl.id=="content") {
    var node = document.createElement('div');
    var nodeUl = document.createElement('ul');

    Element.classNames(nodeUl).add('photoPane');
    Element.classNames(node).add('pane overlay');
    for (var i=0; i<10; i++) {
      var nodeImg = document.createElement('img');
      var nodeLi = document.createElement('li');
      nodeImg.style.width='80px';
      nodeImg.style.height='80px';
      nodeImg.setAttribute('src', '/images/loading_trans_old.gif');
      nodeLi.appendChild(nodeImg);
      nodeUl.appendChild(nodeLi);
    }
    node.appendChild(nodeUl);
  }
  else {
    var node = document.createElement('img');
    node.setAttribute('src', '/images/loading_trans.gif');

    Element.classNames(node).add('loading_icon');
  }

//IE6 BUG - needs to have absolute positioning
  if (browser.isIE6x) node.style.position = "absolute";

//  node.style.margin = "0 auto";
//  node.style.width = "32px";
//  node.style.height = "32px";
  
  //create the overlay
  var overlayEl = createPageOverlay(pageEl, node);
  return overlayEl;
}

function createPageOverlay(pageEl, node) {
  //position the contents of the page in reference to the appropriate book editor page div
  //and remove the bleed margin from the print dimensions before displaying on screen

//  node.style.margin = "50px";

  //put it on screen
  var overlayEl = node;
  if(pageEl) pageEl.appendChild(overlayEl);

  return overlayEl;
}

function getScale(screenDims, printDims) {

  var bleed = BLEED_MARGIN.replace("in", "");  //get bleed dimension as float
    
  //convert print dimensions from print inches to screen pixels
  printDims.width = parseFloat(printDims.width.replace("in", ""));  //strip inches units
  printDims.height = parseFloat(printDims.height.replace("in", ""));  //strip inches units
  printDims.width -= (2*bleed);  //strip off bleed margins
  printDims.height -= (2*bleed);  //strip off bleed margins
  printDims.width *= SCREEN_RESOLUTION;  //convert to screen pixels
  printDims.height *= SCREEN_RESOLUTION;  //convert to screen pixels


  //get screen dimensions for this page
  screenDims.width = parseInt(screenDims.width);
  screenDims.height = parseInt(screenDims.height);

/*
  var scale = { 
    x: screenDims.width / printDims.width,
    y: screenDims.height / printDims.height
  };
*/
  var scale = screenDims.height / printDims.height;
  
  return scale;
}

function adjustResolution(origSize, scale) {

  if (!scale) scale = 1;  //by default dont scale the image

  //assume measurements are all coming in inches at 300dpi
  //convert everything to pixels at 72dpi for the web
  if(typeof(origSize) == 'string') {
    origSize = origSize.replace("in", "");  //strip inches units
    origSize = parseFloat(origSize);
  }

  newSize = origSize * SCREEN_RESOLUTION;  //get pixel size
  newSize = Math.round(newSize*scale);
  newSize += "px";  //add pixel unit
  return newSize;
}

function parsePage(pageElId, response) {

  pageEl = $(pageElId);

  //  var response = req.responseXML.documentElement;
  if (response) {
    //get xml document from response object
    response = response.documentElement

    //get dimensions of a page in the book both in print and on the screen
    var svgEl = response.getElementsByTagName('svg')[0];  //get svg element

    //if set, get the editorType for this block
    //setEditorType(getXMLTagValue(response, 'editorType').toLowerCase());
    if (browseType == "edit") setEditorType("basic");
    if (browseType == "edit_cover") setEditorType("edit_cover");
    if (browseType == "view_cover") setEditorType("view_cover");

    //get print dimensions 
    var printDims = {
      width: svgEl.getAttribute('width'), 
      height: svgEl.getAttribute('height') 
    };

    var screenDims = {
      width: pageEl.getWidth(), 
      height: pageEl.getHeight() 
    };
    
    //the scale we need to use to shrink the book for screen display
    var scale = getScale(screenDims, printDims);

    globalPageScale = scale;
    
    var page = svgEl.getElementsByTagName('page')[0];
    
    thumb_width_offset = page.getAttribute('ms:thumb_width_offset');
    if(typeof(thumb_width_offset) == 'string') {
      thumb_width_offset = parseFloat(thumb_width_offset.replace("in", ""));
    }
    else {
      thumb_width_offset = 0.0;
    }
    


//    clearPage(pageElId);

    //SAFARI BUG: safari cant return getELementsByTagName('image') for some reason
    //so get the images manually by walking through the dom
    if (browser.isSafari) {
      var pageEl = response.getElementsByTagName('page')[0];
      var photoList = extractTagsFromNode('image', pageEl);
    }
    else {
      //get list of photos
      var photoList = response.getElementsByTagName('image');  //get node list of photo nodes

    }

    var textImageList = response.getElementsByTagName('text-image');

    for (i=0; i<photoList.length; i++) {
      createPhoto(photoList[i], pageElId, scale);
    }

    for (i=0; i<textImageList.length; i++) {
      createTextImage(textImageList[i], pageElId, scale);
    }

    
    var usedImagesList = response.getElementsByTagName('usedImage');
    for (i=0; i<usedImagesList.length; i++) {
      var key = usedImagesList[i].getAttribute('href');
      var value = usedImagesList[i].getAttribute('usage');
      usedImagesArray[key] = parseInt(value);
    }
    
    updateDogEarings();
  }
}

function updateTextNode() {
  //get the user-entered new text, and put it in the svg doc
  //then save the svg doc and refresh the page to display the updated text-image
  var newText = $('text_tool_box_textarea').value;
  
  //
  
  var htmlNode = selectedTextNode;
  var nodeId = htmlNode.getAttribute('id');

  //remove the text-image part of the id to get the id of the corresponding flowRoot
  //ids should all start with text-image_ which is 11 characters long
  var svgNodeId = nodeId.substr(11);
  if(svgNodeId) {
    svgNodeId = svgNodeId.replace(PNG_REPLACEMENT_DIV,'');
  }
  switch(svgNodeId) {
    case 'flowRoot0_1':
    case 'flowRoot2_1':
      globalBlockData.title = newText;
      saveBlockMetaData();
      break;
    case 'flowRoot2_2':
      globalBlockData.eventDate = newText;
      saveBlockMetaData();
      break;

    case 'flowRoot3_2':
      globalBlockData.eventDescription = newText;
      saveBlockMetaData();
      break;

    default:
  }
  
  if(svgNodeId == "flowRoot1_1"||svgNodeId == "flowRoot15_1"){
    while (newText.length < 1500) {
      newText +=  " " +newText;
    }
    //newText = newText.substr(100);
  }

//THIS DOESNT WORK AND I HAVE NO IDEA WHY NOT
  //find the corresponding svg node and svg doc
//  var svgDocAndNode = getSVGNodeById(nodeId);  //return both the node and the xml doc object


  //find the appropriate doc and svgNode for the node with this id
  var svgDocAndNode = getSVGDocAndNode(nodeId, svgNodeId, 'flowRoot');
  var xmlDoc = svgDocAndNode.xmlDoc;  //the svg xml doc
  var svgNode = svgDocAndNode.node;  //the svg container node
  var whichPageId = svgDocAndNode.whichPageId;  //which page holds this node
  var whichPageNum = svgDocAndNode.whichPageNum;

  //save data before doing operation
  saveUndoData(xmlDoc);

  //if we found the flowRoot node, extract its flowPara child
  if (svgNode) {
    //show loading message for this page;
    var pageEl = $(whichPageId);
    clearPage(whichPageId);
    var overlayEl = showLoadingOverlay(pageEl);

  
    //indicate that the user has modified this node
    markXMLNodeAsModified(svgNode);

    //put the new text in the svg flowPara node
    //first replace all line breaks with flowLine tags
    var flowPara = svgNode.getElementsByTagName('flowPara')[0];

    while (flowPara.childNodes.length) {
      flowPara.removeChild(flowPara.firstChild);
    }
    
    //insert the new text
    var newNode = null;
    //split the string by new line
    var arrNewText = newText.split("\n");
    if(arrNewText.length == 1 && arrNewText[0].length == 0) arrNewText[0] = " ";  //server barfs on blank text fields, so insert SOMETHING
    for (var i=0; i<arrNewText.length; i++) {
      //insert one line of text at a time
      newNode = xmlDoc.createTextNode(arrNewText[i]);
      flowPara.appendChild(newNode);

      if (i<arrNewText.length-1) {
        //add a flowLine node to separate lines of text
        newNode = xmlDoc.createElement('flowLine');
        newNode.appendChild(xmlDoc.createTextNode(' '));  //populate this flowLine with a space
        flowPara.appendChild(newNode);
      }
    }
    //save changes to server
    saveXMLPageServer(xmlDoc, {
      onComplete: function(evt) {
          hideOverlay(pageEl, overlayEl);
          //clear contents of this page first before updating
          clearPage(whichPageId);
          loadPage(whichPageId, GET_BLOCK_XML_URL, blockId, whichPageNum);
        }
      });
  }
  else {
//    console.error('failed to find node');
  }
}

function markXMLNodeAsModified(xmlNode) {
    //set attribute indicating that the user has modified this node
    //users must modify certain attributes before publishing or printing
    xmlNode.setAttribute('ms:edited', 'true');
}

function getSVGNodeById(nodeId) {
  //find the corresponding element in the SVG doc
  var xmlDoc = pageModifiedXML[currentPage];

  //grab the svg node we need to edit
  svgNode = xmlDoc.getElementById(nodeId);

  //if the node is not in the current page, check the next page
  if (!svgNode) {
    var xmlDoc = pageModifiedXML[currentPage+1];
    svgNode = xmlDoc.getElementById(nodeId);
//$('debugdiv2').innerHTML += "\n\n" + getXMLNodeSerialisation(xmlDoc);
  }

  var svgDoc = {
    node: svgNode,
    xmlDoc: xmlDoc
  };

  return svgDoc;
}

function getSVGDocAndNode(nodeId, svgNodeId, nodeType) {
//SO I HAVE TO DO THIS RIDICULOUS CODE INSTEAD BECAUSE IE stinks
  var svgDoc = null;
  var svgNode = null;
  var whichPageNum = null;
  
  var htmlNode = $(nodeId);
  var whichPageId = getWhichPageId(htmlNode);

  var whichPageNum = (whichPageId == PAGE_LEFT_ID) ? leftPageNum : rightPageNum;
  var xmlDoc = pageModifiedXML[whichPageNum];
  var nodeList = xmlDoc.getElementsByTagName(nodeType);
  
  //SAFARI BUG: getElementsByTagName refuses to work for 'image' tagName
  if (browser.isSafari) {
    nodeList = extractTagsFromNode(nodeType, xmlDoc);
  }

  for (var i=0; i<nodeList.length; i++) {
    if (nodeList[i].getAttribute('id') == svgNodeId) {
      svgNode = nodeList[i];
    }
  }

/*
  if (!svgNode) {
    xmlDoc = pageModifiedXML[rightPageNum];
    nodeList = xmlDoc.getElementsByTagName('flowRoot');
    for (var i=0; i<nodeList.length; i++) {
      if (nodeList[i].getAttribute('id') == svgNodeId) {
        svgNode = nodeList[i];
//        whichPageId = PAGE_RIGHT_ID;
        whichPageNum = currentPage+1;
      }
    }  
  }
*/

  svgDoc = {
    node: svgNode,
    xmlDoc: xmlDoc,
    whichPageId: whichPageId,
    whichPageNum: whichPageNum
  };

  return svgDoc;

//END RIDICULOUS CODE

}

function updateXMLNodeServer(htmlNode, nodeType, isPersonalized) {
  var whichPageId = getWhichPageId(htmlNode);
  //this function makes sure the update to the HTML is reflected in the SVG XML doc
  var thisId = htmlNode.getAttribute('id');
  var thisThumbImg = htmlNode.getAttribute('ms:thumb-href');
  var thisAltImg = htmlNode.getAttribute('ms:alt-href');
  var thisBigImg = htmlNode.getAttribute('xlink:href');  //cant use xlink:href because href is a reserved word
  var thisScale = htmlNode.getAttribute('ms:scale');  //how much we zoomed in this image
  var thisScaleMin = htmlNode.getAttribute('ms:scale-min');  //how much we zoomed in this image
  var thisScaleMax = htmlNode.getAttribute('ms:scale-max');  //how much we zoomed in this image
  var thisHrefWidth = htmlNode.getAttribute('ms:href-width');  //how much we zoomed in this image
  var thisHrefHeight = htmlNode.getAttribute('ms:href-height');  //how much we zoomed in this image

  //IE6 BUG -- it thinks the value returned from getAttribute is an OBJECT, so convert it to a STRING
  if (browser.isIE6up) {
    if (thisId==null) thisId = "";
    if (thisThumbImg==null) thisThumbImg = "";
    if (thisAltImg==null) thisAltImg = "";
    if (thisBigImg==null) thisBigImg = "";
    if (thisScale==null) thisScale = "";
    if (thisScaleMin==null) thisScaleMin = "";
    if (thisScaleMax==null) thisScaleMax = "";
    if (thisHrefWidth==null) thisHrefWidth = "";
    if (thisHrefHeight==null) thisHrefHeight = "";
  }
  //SAFARI BUG: safari does not properly support either namespaces or getAttribute()
  else if (browser.isSafari) {
    thisId = extractAttributeFromNode('id', htmlNode);
    thisThumbImg = extractAttributeFromNode('ms:thumb-href', htmlNode);
    thisAltImg = extractAttributeFromNode('ms:alt-href', htmlNode);
    thisBigImg = extractAttributeFromNode('xlink:href', htmlNode);  //cant use xlink:href because href is a reserved word
    thisScale = extractAttributeFromNode('ms:scale', htmlNode);
    thisScaleMin = extractAttributeFromNode('ms:scale-min', htmlNode);
    thisScaleMax = extractAttributeFromNode('ms:scale-max', htmlNode);
    thisHrefWidth = extractAttributeFromNode('ms:href-width', htmlNode);
    thisHrefHeight = extractAttributeFromNode('ms:href-height', htmlNode);
  }

  //find the corresponding svg node and svg doc
//  var svgDocAndNode = getSVGNodeById(thisId);  //return both the node and the xml doc object
  var svgDocAndNode = getSVGDocAndNode(thisId, thisId, nodeType);

  var xmlDoc = svgDocAndNode.xmlDoc;
  var svgNode = svgDocAndNode.node;

  //save data before doing operation
  saveUndoData(xmlDoc);

  if (svgNode) {
    //indicate that the user has modified this node
    if (isPersonalized) markXMLNodeAsModified(svgNode);

    //update all the image source attributes of the modified SVG document
    if (thisThumbImg) svgNode.setAttribute('ms:thumb-href', thisThumbImg);
    svgNode.setAttribute('ms:alt-href', thisAltImg);
    svgNode.setAttribute('xlink:href', thisBigImg);
    //update the position of the element in inches in the svg doc
    //each item on screen has been scaled down from its normal size, so make sure to undo that scaling
    //and add the bleed margin back in since thats necessary for print dimensions
    var thisX = parseFloat(parseInt(htmlNode.style.left) / SCREEN_RESOLUTION / globalPageScale) + parseFloat(BLEED_MARGIN) + "in";
    var thisY = parseFloat(parseInt(htmlNode.style.top) / SCREEN_RESOLUTION / globalPageScale) + parseFloat(BLEED_MARGIN) + "in";
    var thisWidth = parseFloat(parseInt(htmlNode.style.width) / SCREEN_RESOLUTION / globalPageScale) + "in";
    var thisHeight = parseFloat(parseInt(htmlNode.style.height) / SCREEN_RESOLUTION / globalPageScale) + "in";

    //IE6 BUG - we fudged the position to display it correctly on screen, so we have to un-fudge it now before saving
    if (browser.isIE6x) {
      //add the ie fudge factor to the position
      thisX = parseFloat(thisX) + "in";
    }
    
    //figure out if we are editing a cover
    //if so, re-offset the front or back cover by shifting it back to the right
    var pageNode = xmlDoc.getElementsByTagName('page')[0];
    if (pageNode.getAttribute('id') == "page0") {
      //saving front or back cover
      //fudge factor is different for front and back covers
      var fudgeLeftVal = (isBackCoverPage) ? window.fudgeLeftBack : window.fudgeLeftFront;

      //redo the fudge we did when inputing the cover svg
      var newX = parseFloat(thisX) + fudgeLeftVal + "in";
      var newY = parseFloat(thisY) + window.fudgeTop + "in";
      thisX = newX;
      thisY = newY;
    }

    svgNode.setAttribute('x', thisX);
    svgNode.setAttribute('y', thisY);
    svgNode.setAttribute('width', thisWidth);
    svgNode.setAttribute('height', thisHeight);

    //remember how much we zoomed in!
    svgNode.setAttribute('ms:scale', thisScale);
    svgNode.setAttribute('ms:scale-min', thisScaleMin);
    svgNode.setAttribute('ms:scale-max', thisScaleMax);
    
    //remember dimensions of hi-res version of this image
    svgNode.setAttribute('ms:href-width', thisHrefWidth);
    svgNode.setAttribute('ms:href-height', thisHrefHeight);
    saveXMLPageServer(xmlDoc);
  }
}

function undoChanges() {
  var lastXMLData = undoData.pop();  //pop off the last
  if (lastXMLData) {
    var t;
    try {
      t=lastXMLData.hasAttribute('themeValue');
    }
    catch(err){
      t=false;
    }
    if(t==true)
    {
      var prevTheme=lastXMLData.getAttribute('themeValue');
      var containerNode = $('theme');
      containerNode.value= prevTheme;
      saveBlockTheme(prevTheme,false)
    }
    else
    {
      //figure out which page number this data is from
      var pageNode = lastXMLData.getElementsByTagName('page')[0];
      var pageNum = pageNode.getAttribute('id').substr('page'.length);
      pageNum = 2*parseInt(pageNum);
      pageModifiedXML[pageNum] = lastXMLData;
      if (leftPageNum == pageNum || rightPageNum == pageNum) {
        //var pageElId = (leftPageNum == pageNum) ? PAGE_LEFT_ID : PAGE_RIGHT_ID;
        var pageElId = PAGE_LEFT_ID;
        clearPage(pageElId);
        saveXMLPageServer(lastXMLData);  //save update to server
        parsePage(pageElId, lastXMLData);  //show update
        hiliteGhosts($(pageElId));  //hilite ghost images
      }
      else {
        //we are not currently viewing the page with the undo action
        //re-insert the last xml data
        undoData.push(lastXMLData);
        var strPage = 'Page ' + pageNum;  
        if (pageNum==0) strPage = 'the Front Cover page';
        else if (pageNum==globalBlockData.numPages+1) strPage = 'the Back Cover page';
        //console.error('You must first go to page ' + strPage + ' before you can undo the last operation');
        openLightBox_NEW('notification', 'Notification', 'You must first go to page ' + strPage + ' before you can undo the last operation');
        
      }
    } 
  }
  
  //reset array if empty
  if (!undoData.length) {
    undoData = [];
    $('aUndoChanges').hide();
  }
}

function saveUndoData(xmlDoc) {
  undoData[undoData.length] = xmlDoc.cloneNode(true);
  if($('aUndoChanges')) {$('aUndoChanges').show();}
}

function saveXMLPageServer(xmlDoc, options) {
  //grab the svg element from the xml doc
  var pageNode = xmlDoc.documentElement.getElementsByTagName('svg')[0];

  //serialize this page into regular text
  var pageText = getXMLNodeSerialisation(pageNode);
  pageText = pageText.replace(/FLOWLINE/gi, 'flowLine');  //compensate for lack of proper capitalization
  pageText = pageText.replace(/(\r)?(\n)?<flowLine xmlns="">(\r)?(\n)?/gi, '<flowLine>');  //IE insists on adding newline characters and an xmlns attribute even though I can't specify it.  remove it!
  
  //put together the url
  var url = SAVE_BLOCK_XML_URL;
  var pars = "id=" + blockId;
  if(window.parentId !== undefined){
      pars += "&parentId=" + window.parentId;
  }
  // force generation of a new personal block
  if(window.createPersonalBlock) {
      pars += "&createPersonalBlock=true";
  }
  // force generation of a new personal block
  if(window.createPersonalizedBlock) {
      pars += "&createPersonalizedBlock=true";
  }
  
  pars += "&blockPage=" + Url.encodeURIComponent(pageText);
  pars += "&rand=" + getRandom();  //tack on a random number to prevent IE6 caching
  //send the updated page text to the server

  var myAjax = new Ajax.Request(
    url,
    {
      method: 'post',
      //      parameters: pars,
      postBody: pars,
      requestHeaders: ['Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'],
      onFailure: function(req) {
        console.error('failed');
      },
      onComplete: function(req) {
        //if an oncomplete command is wanted, perform it
        if (options && options.onComplete) options.onComplete();
        fireAjaxEvent("onSaveBlockXmlComplete");
      },
      onSuccess: function(req) {
        var response = req.responseXML;
        if (response) {
          //check for a new bookID, and update it if present
          var newBookId = response.getElementsByTagName('blockId')[0].firstChild.nodeValue;
          if (newBookId){
            updateBlockId(newBookId);
            
          }
          fireAjaxEvent("onSaveBlockXmlSuccess");
        
        }

      }
    });
}


function GetXMLDocumentText(myXML) {
   var myNodes;
   var display="";
   //Put the <name> element into an object.
   myNodes=myXML.getElementsByTagName("name");
   //Extract the different values using a loop.
   for(var counter=0;counter<myNodes.length;counter++) {
      display += myNodes.item(counter).firstChild.nodeValue +
"\n";
   }
   return display;
} 

function loadBlockThemes(blockId) {
  var pars = "";
  pars += "id=" + blockId;
  pars += "&rand=" + getRandom();  //tack on a random number to prevent IE6 caching

  var url = GET_BLOCK_THEMES_URL;
  //send the updated page text to the server
  
  var myAjax = new Ajax.Request(
    url,
    {
      method: 'get',
      parameters: pars,
      //      postBody: pars,
      requestHeaders: ['Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'],
      onFailure: function(req) {
      },
      onComplete: function(req) {
      },
      onSuccess: function(req) {
        var response = req.responseXML;
        if (response)
        {
          //get list of themes
          var themes = response.getElementsByTagName('item');
          //loop through each theme and add to options dropdown  
            

//IE6 BUG - event.observe doesn't provide a "this" object for the function, so we cant get this.value
//          //Add event listener on drop down to call save theme if changed
//          Event.observe(containerNode, 'change', function(evt) {
//            saveBlockTheme(this.value);
//          });
          saveBlockId(curTheme);
          containerNode.onchange = function() {
            saveBlockTheme(this.value,true);
          };
        }
      }
    });
}

//function which initialize saving of the Block so
//when the saving will take place the ID of the block will be known.
function saveBlockId(curVal){
  curTheme=curVal;
  //remove content to prevent changes while we update the theme
  clearPage(PAGE_LEFT_ID);    //clear display
  //var overlayEl_left = showLoadingOverlay($(PAGE_LEFT_ID));
  if (blockViewMode == 'double') {
    //clearPage(PAGE_RIGHT_ID);    //clear display
    //var overlayEl_right = showLoadingOverlay($(PAGE_RIGHT_ID));
  }
    
  var pars = "id=" + blockId;
  pars += "&themeId=" + curVal;
  var url = SAVE_BLOCK_THEME_URL;
  pars += "&rand=" + getRandom();  //tack on a random number to prevent IE6 caching

  //send the updated page text to the server
  var myAjax = new Ajax.Request(
    url,
    {
      method: 'get',
      parameters: pars,
//      postBody: pars,
      requestHeaders: ['Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'],
      onFailure: function(req) {
      },
      onComplete: function(req) {

        //hideOverlay($(PAGE_LEFT_ID), overlayEl_left);
        //if (blockViewMode == 'double') hideOverlay($(PAGE_RIGHT_ID), overlayEl_right);
      },
      onSuccess: function(req) {
        var response = req.responseXML;
        if (response) {
          var responseCode = response.getElementsByTagName('responseCode')[0].firstChild.nodeValue;
          if (responseCode == "1000") {  //SUCCESS!
            //check for a new bookID, and update it if present
            var newBookId = getXMLTagValue(response, 'blockId');
            
            updateBlockId(newBookId);
            //getBlockMetaData(newBookId);
            gotoPage(currentPage);
          }
        }
      }
    });
}

function saveBlockTheme(newVal,saveForUndo) {
  var prevTheme=curTheme;
  curTheme=newVal;
  if(saveForUndo)
  {
    var xmlTheme=document.createElement('option');
    xmlTheme.setAttribute('themeValue', prevTheme);
    saveUndoData(xmlTheme);
  }
  //remove content to prevent changes while we update the theme
  clearPage(PAGE_LEFT_ID);    //clear display
  var overlayEl_left = showLoadingOverlay($(PAGE_LEFT_ID));
  if (blockViewMode == 'double') {
    //clearPage(PAGE_RIGHT_ID);    //clear display
    //var overlayEl_right = showLoadingOverlay($(PAGE_RIGHT_ID));
  }
    
  var pars = "id=" + blockId;
  pars += "&themeId=" + newVal;
  var url = SAVE_BLOCK_THEME_URL;
  pars += "&rand=" + getRandom();  //tack on a random number to prevent IE6 caching

  //send the updated page text to the server
  var myAjax = new Ajax.Request(
    url,
    {
      method: 'get',
      parameters: pars,
//      postBody: pars,
      requestHeaders: ['Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'],
      onFailure: function(req) {
      },
      onComplete: function(req) {
        hideOverlay($(PAGE_LEFT_ID), overlayEl_left);
        //if (blockViewMode == 'double') hideOverlay($(PAGE_RIGHT_ID), overlayEl_right);
      },
      onSuccess: function(req) {
        var response = req.responseXML;
        if (response) {
          var responseCode = response.getElementsByTagName('responseCode')[0].firstChild.nodeValue;
          switch(responseCode) {
          case "1000":  //SUCCESS!
            //check for a new bookID, and update it if present
            var newBookId = getXMLTagValue(response, 'blockId');
            // load publicStatus
            globalBlockData.publicStatus=(getXMLTagValue(response, 'publicStatus')=="true")?true:false;
            updateBlockId(newBookId);
            getBlockMetaData(newBookId);
            gotoPage(currentPage);
            break;
          case "9000":
          console.error('error');
          default:
            openLightBox_NEW('error', 'Your theme hasn\'t been saved', 'Your theme hasn\'t been saved because an error has occurred');
          }
        }
      }
    });
}

//function to do cross-browser xml serialization
function getXMLNodeSerialisation(xmlNode) {
  var text = false;
  try {
    // Gecko-based browsers, Safari, Opera.
    var serializer = new XMLSerializer();
    text = serializer.serializeToString(xmlNode);
  }
  catch (e) {
    try {
      // Internet Explorer.
      text = xmlNode.xml;
    }
    catch (e) {}
  }
  return text;
}

// this function gets the cookie, if it exists
function getCookie( name ) {
  var start = document.cookie.indexOf( name + "=" );
  var len = start + name.length + 1;
  if ( ( !start ) && ( name != document.cookie.substring( 0, name.length ) ) ) {
    return null;
  }
  if ( start == -1 ) return null;
  var end = document.cookie.indexOf( ";", len );
  if ( end == -1 ) end = document.cookie.length;
  return unescape( document.cookie.substring( len, end ) );
}

function insertScaffoldImage(cId, width, height) {
  var containerNode = $(cId);
  node = document.createElement('img');
  node.setAttribute('src', '/images/pixel_clear.gif');
  node.setAttribute('width', width); //'308'x '395' for pages
  node.setAttribute('height', height);
  containerNode.appendChild(node);
//  node.style.visibility = "hidden";
  return node;
}

function saveResellerId(){
  //userId=getCookie('BLOCKR_USER');
  userId = globalBlockData.userId;
  //update the title and description for this book
  var pars = "blockId=" + resellBlockId;
  pars += "&user_id=" + Url.encodeURIComponent(userId);
  pars += "&rand=" + getRandom();    //tack on a random number to prevent IE6 caching
  var myAjax = new Ajax.Request(
      REGISTER_RESELLER_URL,{
          method: 'get',
          parameters: pars,
          onComplete: function(req) {
          },
          onSuccess: function(req) {
          $('resel-text1').style.display="none";
          $('resel-text2').style.display="block";
          $('resell_link').style.display="block";
          $('resell-no').value="close";
          $('resell-yes').style.display="none";
          $('resel-text3').style.display="none";
          $('resell_share').style.display="none";
          
          }
      }
  )
}


function buildBlockSmallThumb(xmlBlockData, posx, posy, type)
{
  var title = xmlBlockData.getAttribute("title");
  var authorName = xmlBlockData.getAttribute("authorName");
  var authorId = xmlBlockData.getAttribute("authorId");
  var thumbUri = xmlBlockData.getAttribute("thumbUri");
  var blockId = xmlBlockData.getAttribute("id");
  var browsePage;
  switch(type) {
    case 'edit':
      browsePage='/blocks/edit/builder';
      break;
    case 'view':
      browsePage='/blocks/view';
      break;
  }
  var strBlock = '';
  strBlock += '<div style="position: absolute; left: ' + posx + 'px; top: ' + posy + 'px;">';
  strBlock += '<a href="'+browsePage+'?block-id=' + blockId + '"><img src="' + thumbUri + '" style="position: absolute; left: 0px; top: 0px; border: 0px; width: 67px; height: 67px;" /></a>';
  strBlock += '<div style="position: absolute; left: 70px; top: 0px; min-width: 130px; text-align: left">'
              + '<a class="nav-link-block-name" href="'+browsePage+'?block-id=' + blockId + '">'+title+'</a>'
              + '<br/>'
              + '<a class="nav-link-username" href="/user/profile/' + Url.encodeURIComponent(authorName) + '">' + authorName + '</a>'
              + '</div>';
  strBlock += '</div>';
  
  return strBlock;  
}

function buildBlockThumb(xmlBlockData, posx, posy, type)
{
  var title=xmlBlockData.getAttribute("title");
  var authorName=xmlBlockData.getAttribute("authorName");
  var authorId=xmlBlockData.getAttribute("authorId");
  var thumbUri=xmlBlockData.getAttribute("thumbUri");
  var blockId=xmlBlockData.getAttribute("id");
  var browsePage=(type=="FeaturedMySaved" || type=="FeaturedMyPublished")?'/blocks/edit/builder':'/blocks/view';

  var strBlock= '<a href="'+browsePage+'?block-id=' + blockId + "\"><img src=\""+thumbUri+"\" style=\"border:0px; position: absolute; left: "+posx+"px; top: "+posy+"px; height: 150px;\"></img></a>"
                + "<img src=\"/images/line6.jpg\" style=\"position: absolute; left:"+(posx-11)+"px; top:" +(posy-8)+"px; width: 263px; height: 1px;\"></img>"
                + "<div style=\"position: absolute; left: "+(posx-8)+"px; top: "+(posy+153)+"px;\" >"
                + '<a class="nav-link-block-name" href="'+browsePage+'?block-id=' + blockId + '">'+title+'</a></div>'
                + "<div style=\"position: absolute; left: "+(posx-8)+"px; top: "+(posy+169)+"px;\" class=\"nav-link-username\">"
                + "<a class=\"nav-link-username\" href=\"/user/profile/"+Url.encodeURIComponent(authorName)+"\">"+authorName+"</a></div>";
  if(type=="MyPublished")
  {
    soldCount=xmlBlockData.getAttribute("sold");
    strBlock+="<div style=\"position: absolute; left: "+(posx-8)+"px; top: "+(posy+184)+"px;\" class=\"nav-link-date\">"+soldCount+"sold</div>";
  }
  return strBlock;  
}

function buildNavLinks(numPages, currPage, functions){ 
// functions - array with function names called on click, without parameters or parentethis

  if(numPages<2) return '';
  
  var prevOnclick = (typeof(functions['prev'])=='string')?(' onclick="'+functions['prev']+'(); return false;" '):"";
  var strBlock='<div style="float:right; display:block;">';
  strBlock += "<a href=\"#\"  return false> <img  src=\"/images/arrow3.gif\" border=\"0\" width=\"7\" height=\"7\""+prevOnclick+"> </img> <a/>";
  var i;
  for(i=0;i<numPages;i++) 
  {
    if(numPages>8){
      if(currPage>=numPages-4){
        if(i<numPages-8) continue;
      }
      else {
        if(i==numPages-2){
          strBlock+="<a href=\"#\"  return false id=PageNavLink"+(i+1)+" class=\"nav-link-page-number \">...</a>";
          strBlock+=(i!=numPages-1)?"<span id=SeparatorNavLink"+(i+1)+" class=\"nav-link-page-number\"> | </span>":"";
          continue;
        }        
        else if(i==numPages-1){
          strBlock+="<a href=\"#\"  return false id=PageNavLink"+(numPages)+" class=\"nav-link-page-number \">"+numPages+"</a>";
          strBlock+=(i!=numPages-1)?"<span id=SeparatorNavLink"+(numPages)+" class=\"nav-link-page-number\"> | </span>":"";
          continue;
        }

        if(currPage<=4){
          if(i>6) continue;
        }
        else if(currPage<=numPages-4){
          if(i<currPage-4 || i>=currPage+3) continue;
        }
      }
      
    }
    var pageOnclick = (typeof(functions['page'])=='string')?(' onclick="'+functions['page']+'('+(i+1)+');return false;"'):"";
    classSelected = (typeof(currPage)=='number' && currPage==i+1)?'nav-link-page-number-selected':"";
    strBlock+="<a href=\"#\"  return false id=PageNavLink"+(i+1)+" class=\"nav-link-page-number "+classSelected+"\""+pageOnclick+">"+(i+1)+"</a>";
    strBlock+=(i!=numPages-1)?"<span id=SeparatorNavLink"+(i+1)+" class=\"nav-link-page-number\"> | </span>":"";
  }  
  var nextOnclick = (typeof(functions['prev'])=='string')?(' onclick="'+functions['next']+'(); return false;" '):"";
  strBlock += "<a href=\"#\"  return false> <img href=\"#\"  return false src=\"/images/arrow.gif\" border=\"0\" width=\"7\" height=\"7\" "+nextOnclick+"></img> </a>";

  strBlock+='<div>';
  return strBlock;
}


// Check whether the user has the rights to view the current block.
function checkViewBlockRights() {
  // make sure both user and block data are loaded
  if (globalBlockData == null || globalUserData == null) return true;

  if (globalBlockData.status != "Published" && globalBlockData.userId != globalUserData.userId) {
    // temporarly let users view their own published blocks
//    return false;
    return true;
  }
  else {
    return true;
  }
}

// Check whether the user has the rights to edit the current block.
function checkEditBlockRights()
{
  // make sure both user and block data are loaded
  if (globalBlockData == null || globalUserData == null) {
    return true;
  }
  if (globalBlockData.userId != globalUserData.userId) {
    return false;
  }
  else {
    if (globalBlockData.status === "Published") {


//     return false;
//     temporarly let users edit their own published blocks
      return true;
    }
    return true;
  }
}


/**
 * Performs a check on whether user can edit block and redirects if not.
 * Example:
 *      addEditBlockRightsCheck(function() {return (currentStep > 0);}) - will
 *      also check if currentPage is greater than zero
 * @param check[optional] - additional function to be executed to check whether user
 * can edit block.
 */
function addEditBlockRightsCheck(check) {
  if (typeof check != 'function') {
    check = function() {
      return true;
    }
  }
  observeEvent('onGetBlockMetaDataSuccess', checkEditBlockRightsAndRedirect);

  function checkEditBlockRightsAndRedirect() {
    if (checkEditBlockRights() == false || !check()) {
      openLightBox_NEW(
        'error',
        'Oops!',
        'You dont have the rights to edit this block.',
        {
          onclose:function() {
            window.location.href = "/blocks/gallery";
          }
        }
      );
      return;
    }
  }
}

/**
 * Performs a check on whether user can view block and redirects if not.
 * Example:
 *      addEditBlockRightsCheck(function() {return (currentStep > 0);}) - will
 *      also check if currentPage is greater than zero
 * @param check[optional] - additional function to be executed to check whether user
 * can view block.
 */
function addViewBlockRightsCheck(check) {
  if (typeof check != 'function') {
    check = function() {
      return true;
    }
  }
  observeEvent('onGetBlockMetaDataSuccess', checkViewBlockRightsAndRedirect);

  function checkViewBlockRightsAndRedirect() {
    if (checkViewBlockRights() == false || !check()) {
      openLightBox_NEW('error', 'Oops!', USER_MESSAGES.CANT_VIEW_BLOCK, {onclose:function() {window.location.href = "/blocks/gallery";}});
      return;
    }
  }
}

/*
* Image Zooming
*/
function fitWidth(){
  var node = getSelectedNode();

  var ghostNode = getGhostNode(node);
  //prevent calling UpdateXmlServer if no change is seen
  if (ghostNode.getWidth() != node.getWidth()) {
    //console.error(ghostNode.getWidth()/node.getWidth());
    var scale = zoomHandle.value * ghostNode.getWidth() / node.getWidth();
    scaleNode(node, scale);  //scale to existing scale setting
    zoomHandle.setValue(scale);
  }
}
  
function fitHeight(){
  var node = getSelectedNode();
  var ghostNode = getGhostNode(node);
  //prevent calling UpdateXmlServer if no change is seen
  if(ghostNode.getHeight()!=node.getHeight()){
    var scale = zoomHandle.value * ghostNode.getHeight() / node.getHeight();
    scaleNode(node, scale);  //scale to existing scale setting
    zoomHandle.setValue(scale);
  }
}

function rotate() {

  var node = getSelectedNode();
  var src = node.getAttribute('xlink:href');
    //put together the url
  var url = '/api/RotateAsset';
  var pars = "angle=" + 90;
  pars += "&href=" + src;
  pars += "&rand=" + getRandom();  //tack on a random number to prevent IE6 caching

  new Ajax.Request(
    url,
    {
      method: 'get',
      parameters: pars,
      onFailure: function(req) {

      },
      onComplete: function(req) {

      },
      onSuccess: function(req) {
        var response = req.responseXML;
        if (response) {
          node.src = "";
          var assetUri = getXMLTagValue(response, 'assetUri');
          var assetId = getXMLTagValue(response, 'assetId');
          var width = getXMLTagValue(response, 'width');
          var height = getXMLTagValue(response, 'height');
          // prevent image from being cached
          node.src = assetUri + '?rand=' + getRandom();
          node.setAttribute('ms:assetId', assetId);
          node.setAttribute('xlink:href', assetUri);
          node.setAttribute('ms:alt-href', assetUri);
          node.setAttribute('ms:thumb-href', assetUri);
          node.setAttribute('ms:href-width', width);
          node.setAttribute('ms:href-height', height);
          node.originalWidth  = width;
          node.originalHeight = height;
          var scale = parseFloat(node.getAttribute('ms:scale'));
          scaleNode(node, scale);
          updateXMLNodeServer(node, 'image', false);
        }
      }
    });

}

function updateDroppablesThumbnails() {

  var photoList = $$('img.photo_thumb');
  photoList.each(function (node) {
    setThumbProps(node);
  });
  updateDogEarings();
}

function removeDogEarings() {

  $$('ul.photoPane img').each(function(el) {
    if (el && el.parentNode && el.parentNode.parentNode) {
      var parentEl = el.parentNode.parentNode;
      Element.classNames(parentEl).remove('usedPhoto');
    }
  });
  

}

function updateDogEarings() {
  removeDogEarings();
  $$('ul.photoPane img').each(function(el) {
    if (el && el.parentNode && el.parentNode.parentNode) {
      var parentEl = el.parentNode.parentNode;
      for (key in usedImagesArray) {
        if(typeof(usedImagesArray[key]) == 'number') {
          if (browser.isSafari) {
            var elKey = extractAttributeFromNode('xlink:href', el);
          }
          else {
            var elKey = el.getAttribute('xlink:href');
          }

          if(elKey == key && usedImagesArray[key] > 0) {
              Element.classNames(parentEl).add('usedPhoto');
          }
        }
      }
    }
  });
}

function increaseDogEaring(key) {
  if(typeof(usedImagesArray[key]) == 'number') {
    usedImagesArray[key]++;
  }
  else {
    usedImagesArray[key] = 1;
  }
}

function decreaseDogEaring(key) {
  if(typeof(usedImagesArray[key]) == 'number') {
    usedImagesArray[key]--;
  }
  else {
    usedImagesArray[key] = 0;
  }
}

function createBrandedBlock() {
  window.blockId = 2; // frame block for branded blocks
}
