/**
 * Image Gallery javascript routines
 *
 *    Copyright (C) 2005, Mike Tyson <mike@tzidesign.com>
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * $Id: script.js 337 2008-02-29 04:57:02Z tzi $
 */


/**
 * Check for anchors to activate items
 *
 *  Call in onLoad() for BODY tag.
 */
function CheckGalleryAnchor()
{
    if ( !location.hash ) return;
    
    hash = unescape(location.hash.substring(1));
    
    anchor = $("img_"+hash);
    
    if ( anchor )
    {
        anchor.parentNode.className = anchor.parentNode.className + " gallery_item_highlighted";
        anchor.onclick();
    }
}


var editor_eltid = null;


function showContent(response)
{
   var xmlDoc = response.responseXML;
   try{
       var result = xmlDoc.getElementsByTagName('result')[0].childNodes[0].nodeValue;
       var message = '';
       try { message = xmlDoc.getElementsByTagName('message')[0].childNodes[0].nodeValue; } catch ( e ) {}
       var script = '';
       try { script = xmlDoc.getElementsByTagName('script')[0].childNodes[0].nodeValue; } catch ( e ) {}
       
       if ( result && result == 'success' )
       { 
          dialog(message); 
          if ( script != '' ) 
          {
             eval(script);
          }
       }
       else
       {
           reportFailure(xmlDoc); 
       }
 

   } catch ( e ) {
       alert('Received an unexpected reply from the server. '+e.name+': '+e.message);
   }   
}

var checkingImages = false;

/**
 * Perform images check
 */

function doCheckImages()
{
    checkImagesErrorCount = 0;
    dialog('<p>Checking images</p><p>'+
                '<div class=\'progressbar_outer\' style=\'text-align: center;\'><img src=\'/images/stock/spinner.gif\' style=\'height: 100%;\'></div>'+
                '</p><p><small>This may take a long time</small></p>'+
                '<p style=\'text-align: center;\'><input type=\'button\' onClick=\'checkingImages = false; closeDialog();\' value=\'Stop\'></p>');
    
    checkingImages = true;
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=check_images",
          onSuccess: checkImagesPollCallback,
          onFailure: function() { checkImagesPollCallback(null); }});
}

var checkImagesErrorCount = 0;
function checkImagesPollCallback(response)
{
    if ( !checkingImages ) return;
    try{
        var xmlDoc = response.responseXML;
        var result = xmlDoc.getElementsByTagName('result')[0].childNodes[0].nodeValue;
        var stage = '';
        try { stage = xmlDoc.getElementsByTagName('stage')[0].childNodes[0].nodeValue; } catch ( e ) {}
        var percentage = '';
        try { percentage = xmlDoc.getElementsByTagName('percentage')[0].childNodes[0].nodeValue; } catch ( e ) {}
        var message = '';
        try { message = xmlDoc.getElementsByTagName('message')[0].childNodes[0].nodeValue; } catch ( e ) {}
        var script;
        try { script = xmlDoc.getElementsByTagName('script')[0].childNodes[0].nodeValue; } catch ( e ) { }

        if ( result && result == "success" )
        {
            errorCount = 0;
            if ( percentage == "100" )
            {
                closeDialog();
                checkingImage = false;
                return;
            }
            else if ( percentage )
            {
                dialog(
                    '<p>'+stage+'</p>'+
                    '<p>'+
                    '<div class="progressbar_outer">'+
                    '<div class="progressbar_inner" style="width: '+percentage+'%;"></div>'+
                    '</div>'+
                    '</p>'+
                    '<p><small>This may take a long time</small></p>'+
                    '<p style=\'text-align: center;\'><input type=\'button\' onClick=\'checkingImages = false; closeDialog();\' value=\'Stop\'></p>');
            }
        }
        else
        {
            alert(response.responseText);
        }
    } catch ( e ) {
        if ( checkImagesErrorCount++ > 10 )
        {
            alert("Received error while checking images: " + e);
            closeDialog();
            return;
        }
    }
    
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=check_images",
          onSuccess: checkImagesPollCallback,
          onFailure: function() { checkImagesPollCallback(null); }});
}

/**
 * Show metadata editor
 *
 * @param path Path to edit
 * @param eltid Associated document element ID
 */
function ShowMetadataEditor(path, eltid)
{
    if ( eltid == "NEXT" )
    {
        // Set 'editor_eltid' to the next element (increment numeric part of id)
        
        // If the last update caused a deletion of an element, we don't need to increment the id
        if ( !g_album_update_caused_delete )
        {
            editor_eltid = editor_eltid.substring(0, editor_eltid.lastIndexOf("_")) + "_" + ((editor_eltid.substring(editor_eltid.lastIndexOf("_") + 1) - 0) + 1);
        }
    }
    else if ( eltid == "PREV" )
    {
        // Set 'editor_eltid' to the previous element (decrement numeric part of id)
        editor_eltid = editor_eltid.substring(0, editor_eltid.lastIndexOf("_")) + "_" + (editor_eltid.substring(editor_eltid.lastIndexOf("_") + 1) - 1);    
    }
    else
    {
        editor_eltid = eltid;
    }

    g_album_update_caused_delete = false;

    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=metadata_editor&path="+path,
          onSuccess: showContent, onFailure: function(transport) { reportFailure('The request failed.'); }});
    
    return true;
}


/**
 * Show metadata editor for multiple items
 */
function ShowMetadataEditorMultiple()
{
    var paths="";
    editor_eltid = new Array();
    
    var elts = document.getElementsByTagName("input");
    var count=0;
    for ( i=0; i<elts.length; i++ )
    {
        if ( elts[i].type == "checkbox" && elts[i].name == "selections"  && elts[i].checked )
        {
            paths += "&paths["+i+"]="+elts[i].value;
            editor_eltid.push(elts[i].parentNode.parentNode.id);   
        }        
    }
    
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=metadata_editor_multiple"+paths,
          onSuccess: showContent, onFailure: function(transport) { reportFailure('The request failed.'); }});
    
    return true;
}

/**
 * Download multiple files
 */
function DownloadMultiple()
{
    var paths="";
    editor_eltid = new Array();
    
    var elts = document.getElementsByTagName("input");
    var count=0;
    for ( i=0; i<elts.length; i++ )
    {
        if ( elts[i].type == "checkbox" && elts[i].name == "selections"  && elts[i].checked )
        {
            paths += "&paths["+i+"]="+elts[i].value;
            editor_eltid.push(elts[i].parentNode.parentNode.id);   
        }        
    }
    
    document.location = "?download"+paths;
    
    return true;
}

/**
 * Show metadata viewer
 */
function ShowMetadataViewer(path)
{
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=metadata_viewer&path="+path,
          onSuccess: showContent, onFailure: function(transport) { reportFailure('The request failed.'); }});
    
    return true;
}

/**
 * Show new item dialog
 *
 */
function ShowAddItemDialog()
{
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=add_new_item_dialog",
          onSuccess: showContent, onFailure: function(transport) { reportFailure('The request failed.'); }});
    
    return true;
}

/**
 * Show add from FTP dialog
 *
 */
function ShowAddFromFTP()
{
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=add_ftp_dialog",
          onSuccess: function(response) {
            dialog(response.responseText);
          }, onFailure: function(transport) { reportFailure('The request failed.'); }});
    
    return true;
}

/**
 * Show new album dialog
 *
 */
function ShowAddAlbumDialog()
{
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=add_new_album_dialog",
          onSuccess: showContent, onFailure: function(transport) { reportFailure('The request failed.'); }});
    
    return true;
}

/**
 * Submit FTP file selection
 */
function UploadFTPFiles()
{
    var args = '';
    var elts = document.getElementsByTagName('input');
    for ( i=0; i<elts.length; i++ )
    {
        if ( elts[i].type == 'checkbox' && elts[i].name == 'ftpfiles[]' && elts[i].checked )
        {
            args += (args?"&":"")+"f["+i+"]="+elts[i].value;
        }
    }

    dialog('<p>Processing</p><p>'+
                '<div class=\'progressbar_outer\' style=\'text-align: center;\'><img src=\'/images/stock/spinner.gif\' style=\'height: 100%;\'></div>'+
                '</p><p><small>This may take a long time if you have selected large files</small></p>');
    

    errorCount = 0;
    new Ajax.Request(
        "?uploading",
        { method: "post", 
          postBody: args,
          onSuccess: UploadPollCallback,
          onFailure: function(transport) { alert('Cannot upload. The request failed.'); }});
}

/**
 * Poll upload script
 */
function DoUploadPolling()
{
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "uploading",
          onSuccess: UploadPollCallback,
          onFailure: function(transport) { DoUploadPolling(); }});
}

/**
 * Callback for upload poll
 */
var errorCount = 0;
function UploadPollCallback(response)
{
    try{
          var xmlDoc = response.responseXML;
            
          var result = xmlDoc.getElementsByTagName('result')[0].childNodes[0].nodeValue;
          var stage = '';
          try { stage = xmlDoc.getElementsByTagName('stage')[0].childNodes[0].nodeValue; } catch ( e ) {}
          var percentage = '';
          try { percentage = xmlDoc.getElementsByTagName('percentage')[0].childNodes[0].nodeValue; } catch ( e ) {}
          var message = '';
          try { message = xmlDoc.getElementsByTagName('message')[0].childNodes[0].nodeValue; } catch ( e ) {}
          var script;
          try { script = xmlDoc.getElementsByTagName('script')[0].childNodes[0].nodeValue; } catch ( e ) { }
          
          if ( result && result == "success" )
          {
              errorCount = 0;
              if ( percentage == "" && message != "" )
              {
                  alert(message);
              }
              else if ( percentage == "100" )
              {
                  window.location.reload();
                  return;
              }
              else if ( percentage )
              {
                  doProgressDialog(stage, percentage);
              }
          }
          else if ( result && result == "failure" )
          {
             dialog('<p>Encountered a problem while uploading: '+(message?message:'Unknown error')+'. Keep trying?</p>'+
                     '<p><input type="button" value="Yes" onClick="DoUploadPolling(); dialog(\"Please wait...\");"> &nbsp; '+
                     '<input type="button" value="No" onClick="closeDialog();">'+
                     '</p>');
             return;
          }
      } catch ( e ) {
          errorCount++;
          if ( errorCount > 20 )
          {
              dialog('<p>Encountered a problem while uploading. Keep trying?</p>'+
                     '<p><input type="button" value="Yes" onClick="DoUploadPolling(); dialog(\"Please wait...\");"> &nbsp; '+
                     '<input type="button" value="No" onClick="closeDialog();">'+
                     '</p>');
              errorCount=0;
              return;
          }
      }
      
      DoUploadPolling();
}

/**
 * Report progress
 *
 * @param stage Current stage
 * @param percentage Percentage through operation
 * @param extra Extra content, to append
 */
function doProgressDialog(stage, percentage)
{
    doProgressDialog(stage, percentage, '');
}

/**
 * Report progress
 *
 * @param stage Current stage
 * @param percentage Percentage through operation
 * @param extra Extra content, to append
 */
function doProgressDialog(stage, percentage, extra)
{
    dialog(
        '<p>'+stage+'</p>'+
        '<p>'+
        '<div class="progressbar_outer">'+
        '<div class="progressbar_inner" style="width: '+percentage+'%;"></div>'+
        '</div>'+
        '</p>'+
        '<p><small>This may take a long time if you have selected large files</small></p>'+
        extra);
 }


var delete_eltid = null;

/**
 * Delete item, after asking for confirmation
 *
 * @param name Item name
 * @param path Path to item
 * @param eltid ID of corresponding visual element
 */
function DeleteItem(name, path, eltid)
{
    if ( !confirm("Are you sure you want to delete "+name+"?") )
        return;
        
    delete_eltid = eltid;
    
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=delete_item&path="+path,
          onComplete: DeleteCallback
        });
}

/**
 * Delete album, after asking for confirmation
 *
 * @param name Album name
 * @param path Path to album
 * @param eltid ID of corresponding visual element
 */
function DeleteAlbum(name, path, eltid)
{
    if ( !confirm("Are you sure you want to delete "+name+"?") )
        return;
        
    delete_eltid = eltid;
    
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=delete_album&path="+path,
          onComplete: DeleteCallback
        });
}

/**
 * Callback for AJAX delete command
 *
 * @param response Response from server
 */
function DeleteCallback(response)
{
    if ( response.responseText.length == 0 )
    {
        alert("Couldn't delete item: blank response from server.  Please reload and try again.");
        return;
    }
    
    try {
        var xmldoc = Try.these(
            function() { return new DOMParser().parseFromString(response.responseText, "text/xml"); },
            function() { var xmldom = new ActiveXObject("Microsoft.XMLDOM"); xmldom.loadXML(response.responseText); return xmldom; }
            );
    
        if ( !xmldoc || !xmldoc.getElementsByTagName("resultcode") || !xmldoc.getElementsByTagName("resultstring") )
        {
            alert("Couldn't delete item: invalid response from server.  Please reload and try again.");
            return;
        }
        
        var resultCode = xmldoc.getElementsByTagName("resultcode").item(0).firstChild.data;
        var resultString = xmldoc.getElementsByTagName("resultstring").item(0).firstChild.data;
    
    } catch ( error ) {
        alert("Couldn't delete item: invalid response from server.  Please reload and try again.\nResponse was:\n"+response.responseText);
        return;
    }

    if ( resultCode != 0 )
    {
        alert("Couldn't delete item: " + resultString);
        return;
    }
    
    setTimeout(DeleteItemDisappearCallback, 500);
    
    Effect.DropOut(delete_eltid, {duration:0.4});
}


/**
 * Callback after disappear effect on deleted item
 */
function DeleteItemDisappearCallback()
{
    // Delete element
    var elt = $(delete_eltid);
    var container = elt.parentNode;
    container.removeChild(elt);

    RenumberElements(container);
}

var dragged_element = null;
var old_onclick = null;
var old_href = null;
var last_container = null;

/**
 * Callback for dragging an item
 *
 *  Remembers the element being dragged, for
 *  processing in OnGallerySort
 *
 * @param element Element being dragged
 */
function OnGalleryDragging(element)
{
    dragged_element = element;
}

/**
 * Callback for updating sort order with drag/drop
 *
 * @param container Container of gallery items
 */
function OnGallerySort(container)
{
    last_container = container;
    
    // Abort Litebox showing up by temporarily disabling link
    if ( dragged_element && dragged_element.getElementsByTagName )
    {
        var anchors = dragged_element.getElementsByTagName("a");
        var anchor = null;
        if ( anchors )
        {
           for ( i=0; i<anchors.length; i++ )
           {
              if ( anchors[i].href )
              {
                 anchor = anchors[i];
                 break;
              }
           }
        }
        if ( anchor )
        {
            old_onclick = anchor.onclick;
            old_href = anchor.href;
            anchor.onclick = null;
            anchor.href = "javascript:void(0);";
            setTimeout(OnDroppedTimeout, 100);
        }
    }
    
    var element_id = container.id;
    
    // Prepare AJAX request
    order = Sortable.serialize(element_id, {
        tag: 'div',
        name: 'order'
        });
    
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=" + (element_id=="gallery_items_container"?"order_items":"order_albums") + "&path="+g_path+"&"+order,
          onComplete: SortCallback
        });
}

/**
 * Callback for AJAX sort command
 *
 * @param response Response from server
 */
function SortCallback(response)
{
    if ( response.responseText.length == 0 )
    {
        alert("Couldn't set sort order: blank response from server.  Please reload and try again.");
        return;
    }
    
    try {
        var xmldoc = Try.these(
            function() { return new DOMParser().parseFromString(response.responseText, "text/xml"); },
            function() { var xmldom = new ActiveXObject("Microsoft.XMLDOM"); xmldom.loadXML(response.responseText); return xmldom; }
            );
    
        if ( !xmldoc || !xmldoc.getElementsByTagName("resultcode") || !xmldoc.getElementsByTagName("resultstring") )
        {
            alert("Couldn't set sort order: invalid response from server.  Please reload and try again.");
            return;
        }
        
        var resultCode = xmldoc.getElementsByTagName("resultcode").item(0).firstChild.data;
        var resultString = xmldoc.getElementsByTagName("resultstring").item(0).firstChild.data;
    
    } catch ( error ) {
        alert("Couldn't set sort order: invalid response from server.  Please reload and try again.\nResponse was:\n"+response.responseText);
        return;
    }

    if ( resultCode != 0 )
    {
        alert("Couldn't set sort order: " + resultString);
        return;
    }
    
    // Re-number elements
    if ( last_container )
    {
        var elts = last_container.getElementsByTagName("div");
        for ( i=0, count=0; i<elts.length; i++ )
        {
            var elt = elts[i];
            if ( !elt.id ) continue;
            elt.setAttribute("id", elt.id.substring(0, elt.id.lastIndexOf("_")) + "_" + count++);
        }
    }
}

/**
 * Reinstate Litebox linkage for element after drag
 */
function OnDroppedTimeout()
{
    if ( dragged_element && dragged_element.getElementsByTagName )
    {
        var anchors = dragged_element.getElementsByTagName("a");
        var anchor = null;
        if ( anchors )
        {
           for ( i=0; i<anchors.length; i++ )
           {
              if ( anchors[i].href )
              {
                 anchor = anchors[i];
                 break;
              }
           }
        }
        if ( anchor )
        {
            anchor.onclick = old_onclick;
            anchor.href = old_href;
        }
    }
    
    dragged_element = null;
    old_onclick = null;
    old_href = null;
}


var g_save_metadata_post; // Javascript function to be run by SaveMetadataCallback after save

/**
 * Save metadata
 *
 * @param path Path of item being edited
 * @param post Action to run afterwards (javascript function)
 */
function SaveMetadata(path, post)
{
    g_save_metadata_post = post;
    
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=metadata_save&path="+path+
                            "&title="+encodeURIComponent($("title").value)+
                            "&caption="+encodeURIComponent($("imagecaption").value)+
                            "&date="+encodeURIComponent($("date").value)+
                            "&author="+encodeURIComponent($("author").value)+
                            "&keywords="+encodeURIComponent($("keywords").value)+
                            "&authorsposition="+encodeURIComponent($("authorsposition").value)+
                            "&captionwriter="+encodeURIComponent($("captionwriter").value)+
                            "&jobname="+encodeURIComponent($("jobname").value)+
                            "&copyrightstatus="+encodeURIComponent($("copyrightstatus").value)+
                            "&copyrightnotice="+encodeURIComponent($("copyrightnotice").value)+
                            "&ownerurl="+encodeURIComponent($("ownerurl").value)+
                            "&supplementalcategories="+encodeURIComponent($("supplementalcategories").value)+
                            "&city="+encodeURIComponent($("city").value)+
                            "&state="+encodeURIComponent($("state").value)+
                            "&country="+encodeURIComponent($("country").value)+
                            "&credit="+encodeURIComponent($("credit").value)+
                            "&source="+encodeURIComponent($("source").value)+
                            "&headline="+encodeURIComponent($("headline").value)+
                            "&instructions="+encodeURIComponent($("instructions").value)+
                            "&transmissionreference="+encodeURIComponent($("transmissionreference").value)+
                            "&urgency="+encodeURIComponent($("urgency").value),
          onComplete: SaveMetadataCallback
        });
}

/**
 * Save metadata for multiple items
 *
 * @param paths Array of paths
 * @param post Action to run afterwards (javascript function)
 */
function SaveMetadataMultiple(paths, post)
{
    g_save_metadata_post = post;
    
    var paths_str = "";
    for ( i=0; i<paths.length; i++ )
    {
        paths_str += "&paths["+i+"]="+paths[i];
    }
    
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=metadata_save_multiple"+paths_str+
                            "&title="+encodeURIComponent($("title").value)+
                            "&caption="+encodeURIComponent($("imagecaption").value)+
                            "&date="+encodeURIComponent($("date").value)+
                            "&author="+encodeURIComponent($("author").value)+
                            "&keywords="+encodeURIComponent($("keywords").value)+
                            "&authorsposition="+encodeURIComponent($("authorsposition").value)+
                            "&captionwriter="+encodeURIComponent($("captionwriter").value)+
                            "&jobname="+encodeURIComponent($("jobname").value)+
                            "&copyrightstatus="+encodeURIComponent($("copyrightstatus").value)+
                            "&copyrightnotice="+encodeURIComponent($("copyrightnotice").value)+
                            "&ownerurl="+encodeURIComponent($("ownerurl").value)+
                            "&supplementalcategories="+encodeURIComponent($("supplementalcategories").value)+
                            "&city="+encodeURIComponent($("city").value)+
                            "&state="+encodeURIComponent($("state").value)+
                            "&country="+encodeURIComponent($("country").value)+
                            "&credit="+encodeURIComponent($("credit").value)+
                            "&source="+encodeURIComponent($("source").value)+
                            "&headline="+encodeURIComponent($("headline").value)+
                            "&instructions="+encodeURIComponent($("instructions").value)+
                            "&transmissionreference="+encodeURIComponent($("transmissionreference").value)+
                            "&urgency="+($("urgency").checked?"true":"")+
                            "&update_title="+($("update_title").checked?"true":"")+
                            "&update_caption="+($("update_imagecaption").checked?"true":"")+
                            "&update_date="+($("update_date").checked?"true":"")+
                            "&update_author="+($("update_author").checked?"true":"")+
                            "&update_keywords="+($("update_keywords").checked?"true":"")+
                            "&update_authorsposition="+($("update_authorsposition").checked?"true":"")+
                            "&update_captionwriter="+($("update_captionwriter").checked?"true":"")+
                            "&update_jobname="+($("update_jobname").checked?"true":"")+
                            "&update_copyrightstatus="+($("update_copyrightstatus").checked?"true":"")+
                            "&update_copyrightnotice="+($("update_copyrightnotice").checked?"true":"")+
                            "&update_ownerurl="+($("update_ownerurl").checked?"true":"")+
                            "&update_supplementalcategories="+($("update_supplementalcategories").checked?"true":"")+
                            "&update_city="+($("update_city").checked?"true":"")+
                            "&update_state="+($("update_state").checked?"true":"")+
                            "&update_country="+($("update_country").checked?"true":"")+
                            "&update_credit="+($("update_credit").checked?"true":"")+
                            "&update_source="+($("update_source").checked?"true":"")+
                            "&update_headline="+($("update_headline").checked?"true":"")+
                            "&update_instructions="+($("update_instructions").checked?"true":"")+
                            "&update_transmissionreference="+($("update_transmissionreference").checked?"true":"")+
                            "&update_urgency="+($("update_urgency").checked?"true":""),
          onComplete: SaveMetadataCallback
        });
}

/**
 * Callback for SaveMetadata/SaveMetadataMultiple
 *
 * @param response Response from server
 */
function SaveMetadataCallback(response)
{
    if ( response.responseText.length == 0 )
    {
        alert("Couldn't save metadata: blank response from server.  Please try again.");
        return;
    }
    
    try {
        var xmldoc = Try.these(
            function() { return new DOMParser().parseFromString(response.responseText, "text/xml"); },
            function() { var xmldom = new ActiveXObject("Microsoft.XMLDOM"); xmldom.loadXML(response.responseText); return xmldom; }
            );
    
        if ( !xmldoc || !xmldoc.getElementsByTagName("resultcode") || !xmldoc.getElementsByTagName("resultstring") )
        {
            alert("Couldn't save metadata: invalid response from server.  Please try again.");
            return;
        }
        
        var resultCode = xmldoc.getElementsByTagName("resultcode").item(0).firstChild.data;
        var resultString = xmldoc.getElementsByTagName("resultstring").item(0).firstChild.data;
    
    } catch ( error ) {
        alert("Couldn't save metadata: invalid response from server.  Please try again.\nResponse was:\n"+response.responseText);
        return;
    }

    if ( resultCode != 0 )
    {
        alert("Couldn't save metadata: " + resultString);
    }
    else
    {
        if ( g_save_metadata_post )
        {
            g_save_metadata_post();
            g_save_metadata_post = null;
        }
    }
}


var g_update_album_post; // Javascript function to be run by UpdateAlbumCallback


/**
 * Update album
 *
 * @param path Path of item being edited
 * @param oldalbum Old album
 * @param newalbum New album
 * @param post Action to run afterwards (javascript function)
 */
function UpdateAlbum(path, oldalbum, newalbum, post)
{
    if ( oldalbum == newalbum )
    {
        // No change needed
        if ( post )
        {
            post();
        }
        return;
    }

    g_update_album_post = post;
    
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=update_album&path="+path+
                            "&album="+newalbum,
          onComplete: UpdateAlbumCallback
        });
}

/**
 * Update album for multiple items
 *
 * @param paths Array of paths
 * @param oldalbum Old album
 * @param newalbum New album
 * @param post Action to run afterwards (javascript function)
 */
function UpdateAlbumMultiple(paths, oldalbum, newalbum, post)
{
    if ( oldalbum == newalbum )
    {
        // No change needed
        if ( post )
        {
            post();
        }
        return;
    }
    
    g_update_album_post = post;
    
    var paths_str = "";
    for ( i=0; i<paths.length; i++ )
    {
        paths_str += "&paths["+i+"]="+paths[i];
    }
    
    new Ajax.Request(
        "",
        { method: "get", 
          parameters: "command=update_album_multiple"+paths_str+
                            "&album="+newalbum,
          onComplete: UpdateAlbumCallback
        });
}

var g_album_update_caused_delete;


/**
 * Callback for UpdateAlbum/UpdateAlbumMultiple
 *
 * @param response Response from server
 */
function UpdateAlbumCallback(response)
{
    if ( response.responseText.length == 0 )
    {
        alert("Couldn't update album: blank response from server.  Please try again.");
        return;
    }
    
    try {
        var xmldoc = Try.these(
            function() { return new DOMParser().parseFromString(response.responseText, "text/xml"); },
            function() { var xmldom = new ActiveXObject("Microsoft.XMLDOM"); xmldom.loadXML(response.responseText); return xmldom; }
            );
    
        if ( !xmldoc || !xmldoc.getElementsByTagName("resultcode") || !xmldoc.getElementsByTagName("resultstring") )
        {
            alert("Couldn't update album: invalid response from server.  Please try again.");
            return;
        }
        
        var resultCode = xmldoc.getElementsByTagName("resultcode").item(0).firstChild.data;
        var resultString = xmldoc.getElementsByTagName("resultstring").item(0).firstChild.data;
    
    } catch ( error ) {
        alert("Couldn't update album: invalid response from server.  Please try again.\nResponse was:\n"+response.responseText);
        return;
    }

    if ( resultCode != 0 )
    {
        alert("Couldn't update album: " + resultString);
    }
    else
    {
        // Remove moved items, which are indicated in editor_eltid, either a single item or an array
        if ( typeof editor_eltid == "string" )
        {
            // Delete element
            var elt = $(editor_eltid);
            var container = elt.parentNode;
            container.removeChild(elt);
            
            // Set flag to indicate that a deletion just happened - this is used in ShowMetadataEditor()
            g_album_update_caused_delete = true;
        }
        else
        {
            for ( i=0; i<editor_eltid.length; i++ )
            {
                // Delete element
                var elt = $(editor_eltid[i]);
                var container = elt.parentNode;
                container.removeChild(elt);
            }
        }
    
        RenumberElements($('gallery_items_container'));
    
        if ( g_update_album_post )
        {
            g_update_album_post();
            g_update_album_post = null;
        }
    }
}

/**
 * Renumber elements, after removal
 */
function RenumberElements(container)
{
    // Re-number elements
    var elts = container.getElementsByTagName("div");
    for ( i=0, count=0; i<elts.length; i++ )
    {
        var elt = elts[i];
        if ( !elt.id ) continue;
        elt.setAttribute("id", elt.id.substring(0, elt.id.lastIndexOf("_")) + "_" + count++);
    }
}

var last_selected = false;

/**
 * Determine whether to show/hide the multi edit button
 *  
 *  Check to see whether at least one checkbox is checked
 */
function UpdateMultiButtons()
{
    var elts = document.getElementsByTagName("input");
    var count=0;
    for ( i=0; i<elts.length; i++ )
    {
        if ( elts[i].type == "checkbox" && elts[i].name == "selections" && elts[i].checked )
        {
            count++;
            if ( count > 1 )
            {
                break;
            }
        }
    }
    
    if ( last_selected == false && (count>1) )
    {
        Effect.Appear("multiple_controls", {duration: 0.2});
    }
    else if ( last_selected == true && !(count>1) )
    {
        Effect.Fade("multiple_controls", {duration: 0.2});
    }
    
    last_selected = (count>1);
}

/**
 * Select or deselect all images
 *
 * @param checkbox Checkbox used to set selection
 */
function SetSelectAll(checkbox)
{
    var elts = document.getElementsByTagName("input");
    var count=0;
    for ( i=0; i<elts.length; i++ )
    {
        if ( elts[i].type == "checkbox" && elts[i].name == "selections" )
        {
            elts[i].checked = checkbox.checked;
            if ( elts[i].onchange ) elts[i].onchange();
        }
    }
    UpdateMultiButtons();
}


/**
 * Report progress
 *
 * @param stage Current stage
 * @param percentage Percentage through operation
 */
function doProgressDialog(stage, percentage)
{
    dialog(
        '<p>'+stage+'</p>'+
        '<p>'+
        '<div class="progressbar_outer">'+
        '<div class="progressbar_inner" style="width: '+percentage+'%;"></div>'+
        '</div>'+
        '</p>'+
        '<p><small>This may take a long time if you have selected large files</small></p>');
 }
