// Cloud engines global namespace
if(!CE) var CE = {};

// Cloud Engines UI namespace
CE.CEUI = new function() 
{
    //----------------------------------------------------------------------------------------
    // Private configuration
    var NPERPAGE = 32;
    var NPERPAGE_SLIDESHOW = 100;
    var NPERPAGE_SLIDEALB  = 500;
    var NPERPAGE_MAX       = 500;

    //----------------------------------------------------------------------------------------
    // Private variables
    var that = this;

    var g_xferprogress = null;

    // Warning Message Dialog
    var g_warnmessage = null;
    // New Album dialog
    var g_newalbum = null;
    // Preview Pane helper class
    var g_preview = null;
    // Slidshow pane
    var g_slideshow = null;
    var g_slideshowObj = null;
    // Delete Confirm Dialog
    var g_deleteconfirm = null;
    // Remove Share Confirm 
    var g_removeshareconfirm = null;
    // Share notification e-mail message
    var g_mailNotifMsg = '';
    // Video item icon preview timer
    var g_videoHoverTimer = null;
    var g_videoHoverTranscode = false;
    var g_videoSupport = null;

    var g_deleteshareconfirm = null;
    // Rename
    var g_lastRename = null;
    var g_renameEvts = {};
    // file upload dialog
    var g_uploadfiles = null;
    
    // Slideshow album state/order
    var g_slideAlbMode = null;
    var g_slideAlbImages = [];
    var g_slideAlbMusic  = [];

    var g_contactsDialog = null;
    var g_contacts = null;
    var g_ejectservicedialog = null;
    var g_ejectedsvc = null;
    // Indication that init has complted
    var g_inited = false;
    // Counter for all init async operations
    var g_initops = 0;
    // Counter for all sidebar async operations
    var g_sbops = 0;

    // Global mousse move handler calls this function if set
    var g_mousedisp = null;
    // The current mouse position
    var g_curmousepos = {x: 0, y:0};
    // Drag-and-drop support
    // Current drag object
    var g_curdrag = null;
    // Indication that suffucient distance has passed for origin of drag that it is a drag
    // insead of a click
    var g_diddrag = false;
    // The mouse position at the origin of the drag
    var g_dragorigin = null;

    // List view mode (0 == grid, 1 == table)
    var g_viewmode = 0;

    // view sizes large = 0, medium = 1, small = 2
    var g_viewsize = 0;

    var g_orphans = {};

    // Map from ID to library data
    var g_svcmap = {};
    var g_devmap = {};


    // First found printer service
    var g_printer      = null;
    var g_printSupport = false;
    var g_docPrevCache = {};

    // Map from ID to album data
    var g_albummap = {};
    // Map from ID to shared album data
    // The current search ID that is applied
    var g_cursearch = null;
    var g_cursrcsearch = null;
    // The current search criteria that is applied
    var g_curscrit  = null;
    // Was the last file listing the result of a search?
    var g_wasSearch = false;
    // Hash of search results from the services
    var g_searchResults  = {};
    var g_lastScrit      = {};
    var g_searchLastPage = {};
    
    var g_sharemap = {};
    var g_sharedfolders = {};
    var g_sharedfiles = {};
    var g_shareedit = null;

    // The stack of files (array of bco objects) we have descended into in our path
    var g_curpath = null;
    // Offset of current page of content
    var g_curpageoffset = 0;
    // The total amount of content on the current page
    var g_curpagetotal = 0;
    // Array holding current page of content (g_curpage['celist'] or g_curpage['cesrc_list'])
    var g_curpage = {};

    var g_topleveloffset = 0;

    var g_trayResizeId;
    var g_trayResizeHeight;
    var g_trayResizePos;

    // the service from which we are currently dragging content.
    var g_srcsvc = null;

    // The stack of files (array of bco objects) we have descended into in our path
    var g_srcpath = null;
    // Offset of current page of content
    var g_srcpageoffset = 0;
    // The total amount of content on the current page
    var g_srcpagetotal = 0;
    // Containers holding current selected content (g_selfiles[grd] = {} ...)
    var g_selfiles = {};
    var g_selrect  = null;

    var g_curshares   = null;
    var g_jumpToShare = null;
    var g_hints       = [];
    var g_hints_cur   = 0;
    var g_hints_close = [];
    var g_hints_showing = false;
    var g_hints_hiding = false;
    var g_hints_hider  = null;
    var g_checkMediaProcessingTimer = null;

    var g_searchkeytimer = null;

    var g_pollops = 0;
    var g_polltimer = null;
    var g_dopoll = false;

    var g_showedtrialwarning = false;

    var g_sortcrit = "none";
    
    var g_audioPlayer = null;

    var UI_POLL_INTERVAL = 300 * 1000;

    var FILE_TYPE_DIRECTORY     = "1";
    var FILE_TYPE_ALBUM         = "10";
    var FILE_TYPE_ARTIST        = "11";
    var FILE_TYPE_GENRE         = "12";
    var FILE_TYPE_IMGTIMELINE   = "20";
    var FILE_TYPE_MOVIETIMELINE = "30";
    var FILE_TYPE_SLIDEALBUM    = "10000";
    var FILE_TYPE_FILEALBUM     = "10001";

    var MESSAGE_MAX_CHARS = 1000;

    var FS_FLAGS_SEARCHABLE = 0x1000;

    var FS_FLAGS_SORTABLE = 0x2000;

    var FLASH_VIDEO_MIME_TYPES = [
        "video/mp4",
        "video/x-flv",
        "video/x-m4v",
        "video/3gpp",
        "video/quicktime"
    ];

    var FLASH_AUDIO_MIME_TYPES = [
        "audio/mp4a-latm",
        "audio/mp4",
        "audio/mp3",
        "audio/aac",
        "audio/aacp",
        "audio/mpg",
        "audio/mpeg"
    ];
    
    var DOC_MIME_TYPES = [
        "text/plain",
        "text/html",
        "text/xml",
        "text/css",
        "text/javascript",
        "application/pdf",
        "application/msword",
        "application/rtf",
        "application/vnd.ms-excel",
        "application/vnd.ms-powerpoint",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        "application/vnd.openxmlformats-officedocument.presentationml.presentation",
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    ];

    var DOC_EXTENSIONS = [
        "apf", "awd", "bfx", "bib", "cal", "csv", "dbf", "dcx", "dif", "doc", "docx", "fmf", "fxd",
        "fxm", "fxr", "fxs", "g3f", "gp4", "html", "ig4", "img", "ltx", "mil", "ods", "odt", "ott",
        "pdb", "pdf", "psw", "pts", "pxl", "qfx", "ras", "rtf", "sdc", "sdw", "slk", "stc", "stw",
        "sxc", "sxw", "txt", "vor", "xhtml", "xls", "xlsx", "xlt", "xml", "met", "odd",
        "odg", "odp", "pct", "pot", "ppt", "pptx", "pwp", "ras", "sda", "sdd",
        "sti", "stp", "svg", "svm", "sxi", "wmf", "xpm"
    ];

    var MIME_NAME_MAP = {
        "audio/mpg":        [ "view.type.mpegaudio",  "sound.png", false ],
        "audio/mpeg":       [ "view.type.mpegaudio",  "sound.png", false ],
        "audio/mp3":        [ "view.type.mpegaudio",  "sound.png", false ],
        "audio/x-ms-wma":   [ "view.type.wmaudio",    "sound.png", false ],
        "audio/x-wav":      [ "view.type.wavaudio",   "sound.png", false ],
        "audio/mp4":        [ "view.type.mp4audio",   "sound.png", false ],
        "audio/mp4a-latm":  [ "view.type.mp4audio",   "sound.png", false ],
        "audio/aac":        [ "view.type.mp4audio",   "sound.png", false ],
        "audio/aacp":       [ "view.type.mp4audio",   "sound.png", false ],
        "image/jpeg":       [ "view.type.jpeg",       "image.png", true ],
        "image/png":        [ "view.type.png",        "image.png", true ],
        "image/gif":        [ "view.type.gif",        "image.png", true ],
        "image/bmp":        [ "view.type.bmp",        "image.png", true ],
        "video/mpeg":       [ "view.type.mpegvideo",  "movie_track.png", false ],
        "video/x-ms-wmv":   [ "view.type.wmvideo",    "movie_track.png", false ],
        "video/quicktime":  [ "view.type.qtimevideo", "movie_track.png", false ],
        "video/mp4":        [ "view.type.mp4video",   "movie_track.png", false ],
        "video/x-m4v":      [ "view.type.mp4video",   "movie_track.png", false ],
        "video/avi":        [ "view.type.avi",        "movie_track.png", false ],
        "text/plain":       [ "view.type.text",       "notes_edit.png", true ],
        "text/html":        [ "view.type.html",       "page.png", false ],
        "text/xml":         [ "view.type.xml",        "page.png", false ],
        "text/css":         [ "view.type.css",        "page.png", false ],
        "text/javascript":  [ "view.type.js",         "page.png", false ],
        "application/pdf":  [ "view.type.pdf",        "pdf.png", true ],
        "application/zip":  [ "view.type.zip",        "archive.png", false ],
        "image/vnd.microsoft.icon":     [ "view.type.icon",         "image.png", false ],
        "application/vnd.ms-excel":     [ "view.type.excel",        "chart.png", true ],
        "application/vnd.ms-powerpoint":[ "view.type.powerpoint",   "presentation.png", true ],
        "application/msword":           [ "view.type.doc",          "word.png", true ],
        "application/rtf":              [ "view.type.doc",          "word.png", true ],
        "application/x-zip-compressed": [ "view.type.zip",          "archive.png", false ],
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":            [ "view.type.excel",      "chart.png", true ],
        "application/vnd.openxmlformats-officedocument.presentationml.presentation":    [ "view.type.powerpoint", "presentation.png", true ],
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document":      [ "view.type.doc",        "word.png", true ]
    };

    var TRANSCODE_ENABLED = true;

    var TRANSCODABLE_MIME_TYPES = {
        "video/x-ms-asf": true,
        "video/x-msvideo": true,
        "video/x-flv": true,
        "video/quicktime": true,
        "video/mp4": true,
        "video/avi": true,
        "video/mpeg": true,
        "video/x-ms-wmv": true,
        "video/avi": true
    };
    
    var g_pendfeatures = 0;

    //----------------------------------------------------------------------------------------
    // Public variables

    this.g_options = {};
    // The current service we are browsing (or NULL for album)
    this.g_cursvc = null;
    // The current album we are browsing (or NULL for service)
    this.g_curalbum            = null;
    this.g_sharetoken          = null;
    this.g_isnewalbum          = false;
    this.g_allfeatures         = {};
    this.g_videoPrevDisabled   = false;
    this.g_videoPrevTranscode  = false;
    this.g_videoSlideTranscode = false;
    this.g_videoTranscodeFile  = null;
    this.g_newfolder           = null;
    this.g_isSlideAlb          = false;
    this.g_shareTypeStr        = '';

    //----------------------------------------------------------------------------------------
    // Private Classes
    //
    
    function DocumentPreview(file, parentDiv) {
        var m_file    = file;
        var m_parent  = parentDiv || CE.CEU.$('cecontent');
        var m_conv    = null;
        var m_zoom    = 100;
        var m_multi   = 2;
        var m_pageImg = null;
        var m_butPageNum, m_butNumPages, m_butPrev, m_butNext, m_butDocPages1, m_butDocPages2, m_docPrevDiv, m_docDiv;
        
        function onNewWin() {
            var args = ['docprev_fileid', m_file.fileid];
            var svc  = CE.CEUI.getCurSvc(m_file);
            if(svc && svc.deviceid) {
                args.push('docprev_deviceid',  svc.deviceid);
                args.push('docprev_serviceid', svc.serviceid);
            }
            if(CE.CEU.getSearchParam('sharetoken')) {
                args.push('sharetoken', CE.CEU.getSearchParam('sharetoken'));
            }
            
            var url = window.location.protocol + '//' + window.location.host + window.location.pathname + '?';
            for(var i = 0; i < (args.length-1); i+=2) {
                if(i != 0) url += '&';
                url += args[i] + '=' + encodeURIComponent(args[i+1]);
            }
            window.open(url);
        }
        
        function onDownload() {
            downloadfile(m_file);
        }
        
        function onSinglePage() {
            CE.aCN(m_butDocPages1, 'selected');
            CE.rCN(m_butDocPages2, 'selected');
            showPage();
        }
        function onMultiPage() {
            CE.rCN(m_butDocPages1, 'selected');
            CE.aCN(m_butDocPages2, 'selected');
            showPage();
        }
        
        function onZoomOut() {
            if(CE.hCN(m_butDocPages1,'selected') && m_pageImg && m_zoom > 25) {
                m_zoom -= 25;
                m_pageImg.style.width = m_zoom + '%';
            } else if(!CE.hCN(m_butDocPages1,'selected')) {
                ++m_multi;
                if(m_multi > 4)
                    m_multi = 4;
                showPage();
            }
        }
        function onZoomIn() {
            if(CE.hCN(m_butDocPages1,'selected') && m_pageImg) {
                m_zoom += 25;
                m_pageImg.style.width = m_zoom + '%';
            } else if(!CE.hCN(m_butDocPages1,'selected')) {
                if(m_multi == 2) {
                    onSinglePage();
                } else {
                    --m_multi;
                    showPage();
                }
            }
        }
        
        function onGotoPageNum(e) {
            m_conv.pagenum = e.num;
            onSinglePage();
        }
        function onGotoPage() {
            CE.CEU.showMultiInputDialog('docprev.page.goto', [{message:'docprev.page.goto.num', dflt:m_conv.pagenum,
                inputcls:'std-field-wide', inputid:'pagenum', inputtype:'text', twoline:false}], 'button.ok',
            function() {
                var num = parseInt(CE.CEU.$('pagenum').value);
                if(isNaN(num) || num < 1 || num > m_conv.numpages) {
                    CE.CEU.showMessage(CE.STRTAB.lookup('docprev.page.goto'), CE.STRTAB.lookup('docprev.page.goto.invalid',m_conv.numpages));
                    return false;
                }
                m_conv.pagenum = num;
                showPage();
                return true;
            });
        }
        function onPrevPage() {
            if(m_conv.pagenum > 1) {
                if(CE.hCN(m_butDocPages1,'selected')) {
                    --m_conv.pagenum;
                } else {
                    m_conv.pagenum -= (m_multi*m_multi);
                    if(m_conv.pagenum < 1)
                        m_conv.pagenum = 1;
                }
                showPage();
            }
        }
        function onNextPage() {
            if(CE.hCN(m_butDocPages1,'selected')) {
                if(m_conv.pagenum < m_conv.numpages) {
                    ++m_conv.pagenum;
                    showPage();
                }
            } else {
                m_conv.pagenum += (m_multi*m_multi);
                if(m_conv.pagenum > m_conv.numpages)
                    m_conv.pagenum = m_conv.numpages;
                showPage();
            }
        }
        
        function updateToolbar() {
            m_butPageNum.appendChild(CE.dctn(m_conv.pagenum));
            m_butNumPages.appendChild(CE.dctn(m_conv.numpages));
            
            if(m_conv.pagenum > 1)
                CE.rCN(m_butPrev, 'cedisabled');
            else
                CE.aCN(m_butPrev, 'cedisabled');
    
            if((CE.hCN(m_butDocPages1,'selected') && m_conv.pagenum < m_conv.numpages) ||
               (CE.hCN(m_butDocPages2,'selected') && m_conv.pagenum < (m_conv.numpages-1)))
                CE.rCN(m_butNext, 'cedisabled');
            else
                CE.aCN(m_butNext, 'cedisabled');
        }

        function showToolbar(el) {
            if(g_printSupport && g_printer) {
                (el.appendChild(CE.dca({onEvent:onShowPrintDoc},null,'cebutton',CE.STRTAB.lookup('view.hint.print'))))
                    .appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-print-dark-small.png'));
            }
            
            m_butPrev = el.appendChild(CE.dca({onEvent:onPrevPage},'cedocpageprev','cebutton',CE.STRTAB.lookup('docprev.page.prev')));
            m_butPrev.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-prev.png'));
            m_butPageNum = el.appendChild(CE.dce('span','cedocpagenum'));
            el.appendChild(CE.dctn(' / '));
            m_butNumPages = el.appendChild(CE.dce('span','cedocnumpages'));
            m_butNext = el.appendChild(CE.dca({onEvent:onNextPage},'cedocpagenext','cebutton ceflushright',CE.STRTAB.lookup('docprev.page.next')));
            m_butNext.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-next.png'));
            (el.appendChild(CE.dca({onEvent:onGotoPage},'cedocgoto','cebutton ceflushleft',CE.STRTAB.lookup('docprev.page.goto'))))
                .appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-goto.png'));

            el = el.appendChild(CE.dce('span',null,'ceright'));
            
            (el.appendChild(CE.dca({onEvent:onZoomOut},'cedoczoomout','cebutton ceflushright',CE.STRTAB.lookup('docprev.zoom.out'))))
                .appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-zoom-out.png'));
            (el.appendChild(CE.dca({onEvent:onZoomIn},'cedoczoomin','cebutton ceflushleft',CE.STRTAB.lookup('docprev.zoom.in'))))
                .appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-zoom-dark-small.png'));

            m_butDocPages1 = el.appendChild(CE.dca({onEvent:onSinglePage},'cedocpages1','cebutton ceflushright selected',CE.STRTAB.lookup('docprev.pages.1')));
            m_butDocPages1.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-1page.png'));
            m_butDocPages2 = el.appendChild(CE.dca({onEvent:onMultiPage},'cedocpages2','cebutton ceflushleft',CE.STRTAB.lookup('docprev.pages.2')));
            m_butDocPages2.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-2pages.png'));

            (el.appendChild(CE.dca({onEvent:onDownload},'cedocdl','cebutton',CE.STRTAB.lookup('docprev.download'))))
                .appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-download-dark-small.png'));
            
            if(CE.CEU.isLoggedIn()) {
                (el.appendChild(CE.dca({onEvent:onNewWin},'cedocdl','cebutton',CE.STRTAB.lookup('docprev.fullscreen'))))
                    .appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-fullscreen.png'));
            }
        }
        
        function showPage() {
            if(!m_docPrevDiv) {
                m_docPrevDiv = m_parent.appendChild(CE.dce('div','cedocprev'));
                showToolbar(m_docPrevDiv.appendChild(CE.dce('div',null,'cedoctools')));
                m_docDiv = m_docPrevDiv.appendChild(CE.dce('div','cedocpane','cedoc'));
            } else {
                CE.rac(m_docDiv);
                CE.rac(m_butPageNum);
                CE.rac(m_butNumPages);
            }
            
            var pageUrl = '/svc/api/convertGetPDFPage' + '?source=' + encodeURIComponent(m_conv.fullurl) + '&pageno=';
            
            if(CE.hCN(m_butDocPages1,'selected')) {
                m_pageImg = m_docDiv.appendChild(CE.dci(pageUrl+m_conv.pagenum,null,'cesinglepage'));
                m_pageImg.style.width = m_zoom + '%';
            } else {
                m_pageImg = null;
                for(var row = 0; row < m_multi; ++row) {
                    var rowDiv = m_docDiv.appendChild(CE.dce('div',null,'cedocrow'));
                    for(var col = 0; col < m_multi; ++col) {
                        var pageNum = m_conv.pagenum +(row*m_multi) + col;
                        if(pageNum <= m_conv.numpages) {
                            var img = rowDiv.appendChild(CE.dci(pageUrl+pageNum,null,'cemulti-'+m_multi+'-'+(col+1)));
                            CE.CEU.attachEvent(img, 'click', {onEvent:onGotoPageNum, num:pageNum});
                        }
                    }
                }
            }
            
            updateToolbar();
            CE.CEU.showLoadingAni(false);
        }

        function getPdfInfo(waitDlg, progress) {
            CE.CEU.svc.asyncRPC('POST', 'convertGetPDFInfo', ['source',m_conv.fullurl]);
            
            var updateProgress = function() {
                if(!m_conv.aborted) {
                    CE.CEU.svc.asyncRPC('POST', 'convertGetPDFInfoProgress', ['source',m_conv.fullurl],
                    function(r) {
                        if(!m_conv.aborted && progress) {
                            if(r.npages) {
                                // Done
                                waitDlg.hide();
                                m_conv.pagenum  = 1;
                                m_conv.numpages = r.npages;
                                showPage();
                            } else {
                                progress.style.width = (50+parseInt(r.percent/2.0)) + '%';
                                setTimeout(updateProgress, 1000);
                            }
                        }
                    },
                    function() {
                        waitDlg.hide();
                        if(!m_conv.aborted) {
                            m_conv.aborted = true; // to stop polling of progress
                            CE.CEU.showMessage(CE.STRTAB.lookup('docprev.converror.title'), CE.STRTAB.lookup('docprev.converror.msg'),
                            function() {
                                CE.CEUI.reloadContentCWD();
                                return true;
                            });
                        }
                    });
                }
            };
            setTimeout(updateProgress, 1000);
        }
    
        this.show = function() {
            CE.CEUI.clearContent('celist');
            enableDisableToolbarButtons(true);
    
            var svc = CE.CEUI.getCurSvc(m_file);
            var dlUrl;
            if(CE.CEU.isLoggedIn()) {
                dlUrl = '/svc/files/' + svc.deviceid + '/' + svc.serviceid + '/' + m_file.fileid + '/dl/' + encodeURIComponent(m_file.name);
            } else if(CE.CEU.getSearchParam('sharetoken')) {
                dlUrl = '/svc/files/' + CE.CEU.getSearchParam('sharetoken') + '/'
                      + svc.deviceid + '/' + svc.serviceid + '/' + m_file.fileid + '/dl/' + encodeURIComponent(m_file.name);
            } else {
                dlUrl = '/share/' + CE.CEUI.g_sharetoken + '/' + m_file.fileid + '/dl/' + encodeURIComponent(m_file.name);
            }
            
            var cacheKey = m_file.fileid + m_file.mtime;
            if(!g_docPrevCache[cacheKey])
                g_docPrevCache[cacheKey] = {};
            m_conv = g_docPrevCache[cacheKey];

            if(m_conv.numpages) {
                // We've already converted this document, so just show it
                m_conv.pagenum = 1;
                showPage();
            } else {
                m_conv.aborted = false;
                var waitPane = CE.dce('div');
                var waitDlg  = new CE.CEU.Dialog(CE.STRTAB.lookup('docprev.wait.title'), waitPane,
                                                 [{name:'cancel', label:CE.STRTAB.lookup('button.cancel'), callback:function()
                                                   {m_conv.aborted=true; CE.CEUI.reloadContentCWD(); return true;}}], null, true);
                waitPane.appendChild(CE.STRTAB.lookupel('docprev.wait.msg'));
                var progress = (waitPane.appendChild(CE.dce('div',null,'ceprogressbar'))).appendChild(CE.dce('div',null,'ceprogress'));
                var aniDiv = waitPane.appendChild(CE.dce('div', null, 'ceaniwait'));
                aniDiv.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'loading.gif'));
                waitDlg.show();
    
                if(m_file.name.length > 4 && m_file.name.substr(m_file.name.length-4).toLowerCase() == '.pdf') {
                    // PDFs don't need to be converted before PNGing and viewing
                    m_conv.fullurl = dlUrl;
                    getPdfInfo(waitDlg, progress);
                } else {
                    // Convert the document to PDF and poll for progress
                    CE.CEU.svc.asyncRPC('POST', 'convertDoc', ['docurl',dlUrl]);
    
                    var updateProgress = function() {
                        if(!m_conv.aborted) {
                            CE.CEU.svc.asyncRPC('POST', 'convertDocGetProgress', ['docurl',dlUrl],
                            function(r) {
                                if(!m_conv.aborted && progress) {
                                    progress.style.width = parseInt(r.progress/2.0) + '%';
                                    if(r.filename) {
                                        // We're done
                                        m_conv.fullurl = r.fileurl + '/' + r.filename;
                                        getPdfInfo(waitDlg, progress);
                                    } else {
                                        setTimeout(updateProgress, 1000);
                                    }
                                } else {
                                    waitDlg.hide();
                                }
                            },
                            function() {
                                waitDlg.hide();
                                if(!m_conv.aborted) {
                                    m_conv.aborted = true; // to stop polling of progress
                                    CE.CEU.showMessage(CE.STRTAB.lookup('docprev.converror.title'), CE.STRTAB.lookup('docprev.converror.msg'),
                                    function() {
                                        CE.CEUI.reloadContentCWD();
                                        return true;
                                    });
                                }
                            });
                        }
                    };
                    setTimeout(updateProgress, 1000);
                }
            }
        };
    }

    function XferProgressDialog() {
        var that = this;
        
        var m_pane = CE.dce('div');
        var m_dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.copyprog'), m_pane, null, null, true);
        var m_progressid = null;
        var m_svc = null;
        var m_cb = null;

        function xferProgressSuccess(r) {

            if(m_dlg.isShowing()) {
                if(r) {
                    if(r.name) {
                        var l = CE.CEU.$("xfernm");
                        if(l) {
                            CE.rac(l);
                            l.appendChild(CE.dctn(CE.CEUI.getDispFn(r.name)));
                        }
                    }                     
                    var offset = -1, length = -1;
                    
                    if(r.copiedsize && r.totalsize) {
                        offset = parseInt(r.copiedsize);
                        length = parseInt(r.totalsize);
                    } else if(r.offset && r.length) {
                        offset = parseInt(r.offset);
                        length = parseInt(r.length);
                    }
                    if(offset > 0 && length > 0) {
                        var frac = offset / length;
                        var percent = 0;
                        
                        if(isNaN(frac) || (frac < 0) || (frac > 1)) {
                        } else {
                            percent = Math.round(frac * 100);
                        }
                        var m = CE.CEU.$("xferpbf");                        
                        if(m) {
                            m.style.width = "" + percent + "%";
                        }
                        
                    }
                }
                if(r.done && r.done != '0') {
                    m_dlg.hide();
                    if(m_cb) m_cb((r.copiedsize == r.totalsize));
                } else if(m_dlg.isShowing()) {
                    CE.rTimer(checkXferProgress, 1000);
                }
            }
        }

        function xferProgressFailure(r) {

        }

        function onCancelOpClick() {
            var args = [ 
                "deviceid", m_svc.deviceid, 
                "serviceid", m_svc.serviceid,
                "opid", m_progressid 
            ];
            CE.CEU.svc.asyncRPC("POST", "cancelOperation", args); 
        }
        
        function checkXferProgress() {
            var args = [ 
                "deviceid", m_svc.deviceid, 
                "serviceid", m_svc.serviceid,
                "progressid", m_progressid 
            ];
            
            CE.CEU.svc.asyncRPC("POST", "transferProgress", args, 
                                xferProgressSuccess, xferProgressFailure);
        }

        function onHideClick() {
            m_dlg.hide();
        }

        (function() {
            var l = CE.dce("div", "xferpbb", "cepbb");
            var m = CE.dce("div", "xferpbf", "cepbf");
            var n = CE.dce("div", "xfernm");

            m_pane.appendChild(n);           
            m_pane.appendChild(l);
            l.appendChild(m);

            m_dlg.addButton('hide', CE.STRTAB.lookup('view.hidecaps'), onHideClick);
            m_dlg.addButton('cancel', CE.STRTAB.lookup('view.cancelcaps'), onCancelOpClick);
        })();
        
        this.show = function(progressid, svc, cb) {
            m_dlg.show();
            m_cb = cb;

            var m = CE.CEU.$("xferpbf");                        
            if(m) m.style.width = "0%";

            m = CE.CEU.$("xfernm");
            if(m) CE.rac(m);

            m_progressid = progressid;
            m_svc = svc;
            checkXferProgress();
        }
    }

    function ContactsDialog() {
        var that    = this;
        var m_pane  = CE.dce('div');
        var m_dlg   = new CE.CEU.Dialog(CE.STRTAB.lookup('contacts.title'), m_pane, null, null, false);
        var m_table = null;
        
        function onClickOk() {
            m_dlg.hide();
            
            var inviteMails = '';
            var checks      = m_table.getElementsByTagName('input');
            for(var i = 0; i < checks.length; ++i) {
                if(checks[i].checked)
                    inviteMails += ',' + checks[i].value;
            }
            if(inviteMails.length > 0) {
                var addresses = shbyid('ceshare_email').value;
                if(addresses == CE.STRTAB.lookup('view.enteraddrs')) {
                    shbyid('ceshare_email').value = inviteMails.substr(1);
                } else {
                    shbyid('ceshare_email').value = addresses + inviteMails;
                }

                CE.aCN(shbyid('ceshare_email'), 'email_focus');
            }
        }
        
        function onClickCancel() {
            m_dlg.hide();
        }

        (function() {
            m_dlg.addButton('ok',     CE.STRTAB.lookup('view.add'),    onClickOk);
            m_dlg.addButton('cancel', CE.STRTAB.lookup('ceui.cancel'), onClickCancel);
        })();
        
        function onRemoveContact() {
            g_contacts = null;
            
            var rowNum = parseInt(this.href.substr(this.href.indexOf('#')+1));
            var email  = CE.CEU.$('contacts_check_'+rowNum).value;

            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('contacts.remove.title'), CE.STRTAB.lookup('contacts.remove.msg',email),
                [{name:'yes',label:CE.STRTAB.lookup("view.yes"),callback:function(){
                    // Delete the contact
                    CE.CEU.svc.asyncRPC("POST", "removeContact", ['contact', email]);
                    m_table.removeChild(CE.CEU.$('contacts_row_'+rowNum));
                    return true;
                }},
                {name:'no',label:CE.STRTAB.lookup("view.no")}]);
            dlg.show();
        }

        function onGotContacts(r) {
            if(r && r.contacts) {
                for(var i = 0; i < r.contacts.length; ++i) {
                    var tr = CE.dce('tr', 'contacts_row_' + i);
                    m_table.appendChild(tr);
                    var td = CE.dce('td');
                    tr.appendChild(td);
                    var check = CE.dce('input', 'contacts_check_'+i);
                    check.type  = 'checkbox';
                    check.value = r.contacts[i];
                    td.appendChild(check);
                    var label = CE.dce('label');
                    label.htmlFor = 'contacts_check_'+i;
                    label.appendChild(CE.dctn(' '+r.contacts[i]));
                    td.appendChild(label);
                    
                    td = CE.dce('td', null, 'right');
                    tr.appendChild(td);
                    var closeBut = CE.dce('a');
                    closeBut.href    = '#' + i;
                    closeBut.onclick = onRemoveContact;
                    closeBut.appendChild(CE.dci(CE.STRTAB.lookup("imgbase")+'list-close-x.png'));
                    td.appendChild(closeBut);
                }
            }
            m_dlg.show();
        }
        
        this.show = function() {
            CE.rac(m_pane);
            var div = CE.dce('div');
            div.innerHTML = CE.STRTAB.lookup('contacts.select.html');
            m_pane.appendChild(div);
            var scroll = CE.dce('div', 'cecontacts_scroll');
            m_pane.appendChild(scroll);
            var table = CE.dce('table', 'cecontacts_list');
            scroll.appendChild(table);
            m_table = CE.dce('tbody');
            table.appendChild(m_table);
            CE.CEU.svc.asyncRPC("POST", "getContacts", [], onGotContacts, onGotContacts);
        }
    }
    
    this.showContactsDialog = function() {
        if(!g_contactsDialog)
            g_contactsDialog = new ContactsDialog();
        g_contactsDialog.show();
    };
    
    function EjectConfirmDialog() {
        var that = this;
        var m_pane = CE.dce('div');
        var m_dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('ceui.eject.title'), m_pane, null, null, true);
        var m_waitDlg = null;
        var m_svc = null;

        function onEjectSuccess(r, data, method) {
            g_ejectedsvc = m_svc;
            
            m_waitDlg.hide();
            m_waitDlg = null;
            m_svc = null;
            m_success = null;
            CE.CEUI.g_cursvc = null;
            reloadSidebar();
        } 

        function onEjectFailure(r, data, method) {
            m_waitDlg.hide();
            m_waitDlg = null;
            m_svc = null;
            return onGenericFailure(r, data, method);
        }
        
        function onEjectOkClick() {
            // Show a wait message/animation while the drive is being ejected
            var waitPane = CE.dce('div');
            m_waitDlg = new CE.CEU.Dialog(CE.STRTAB.lookup('ceui.eject.title'), waitPane, null, null, true);
            waitPane.appendChild(CE.STRTAB.lookupel('ceui.eject.wait'));
            var aniPane = CE.dce('div', null, 'ceaniwait');
            waitPane.appendChild(aniPane);
            aniPane.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'loading.gif'));
            m_waitDlg.show();
            
            var args = [ "deviceid", m_svc.deviceid, "serviceid", m_svc.serviceid ];
            CE.CEU.svc.asyncRPC("POST", "ejectService", args, onEjectSuccess, onEjectFailure);
            return true;
        }
        
        function onEjectCancelClick() {
            m_svc = null;
            m_success = null;
            return true;
        }

        (function() {
            m_dlg.addButton('ok', CE.STRTAB.lookup('ceui.ok'), onEjectOkClick);
            m_dlg.addButton('cancel', CE.STRTAB.lookup('ceui.cancel'), onEjectCancelClick);
        })();

        this.show = function(svc) {
            m_svc = svc;
            CE.rac(m_pane);
            m_pane.appendChild(CE.STRTAB.lookupel('ceui.eject.service', CE.CEUI.getDispFn(m_svc.name)))
            m_dlg.show();
        }
    }

    function DeleteConfirmDialog() {
        var that = this;
        var pane = CE.dce('div');
        var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('ceui.delete.title'), pane, null, null, true);
        var m_file = null;
        var m_share = false;

        function onRemoveSuccess(r, data, method) {
            m_file = null;
            CE.CEUI.reloadContentCWD();
        } 

        function onRemoveFailure(r, data, method) {
            m_file = null;
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('ceu.error.title'), CE.STRTAB.lookupel("view.removefailure"),
                [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
            dlg.show();
            dlg = null;
        }

        function removeFile() {
            var args = [];
            var svc  = CE.CEUI.getCurSvc(m_file);

            args.push("deviceid");
            args.push(svc.deviceid);
            args.push("serviceid");
            args.push(svc.serviceid);
            
            args.push("fileid");
            args.push(m_file.fileid);
            args.push("recurse");
            args.push("1");
            
            if(CE.CEUI.g_curalbum) {
                args.push("albumid");
                args.push(CE.CEUI.g_curalbum.albumid);
            }
            
            trackEvent('Delete', null, m_file);
            CE.CEU.svc.asyncRPC("POST", "removeFile", args, onRemoveSuccess, onRemoveFailure);
            g_searchResults['celist'] = null;
        }

        function onRemoveOkClick() {
            CE.CEU.showLoadingAni(true);
            // If there is an associated share, delete it first
            var sh = findShare(m_file);
            if(sh) {
                var shroot = sh.root ? sh.root : sh;
                shareDisableNotify(shroot.serviceid, shroot.deviceid, sh.albumid, function(){
                    CE.CEU.svc.asyncRPC("POST", "deleteAlbum", [ "albumid", sh.albumid], function(){
                        removeFile();
                        reloadSidebar(true, 'cemyalb_albums');
                    });
                });
            } else {
                removeFile();
            }
            dlg.hide();
        }
        
        function onRemoveCancelClick() {
            m_file = null;
            dlg.hide();
        }

        (function() {
            dlg.addButton('ok', CE.STRTAB.lookup('ceui.ok'), onRemoveOkClick);
            dlg.addButton('cancel', CE.STRTAB.lookup('ceui.cancel'), onRemoveCancelClick);
        })();

        this.show = function(file, share) {
            m_file = file;
            m_share = share;
            CE.rac(pane);

            if(m_file.albumid) {
                pane.appendChild(CE.STRTAB.lookupel('ceui.delete.msg', CE.CEUI.getDispFn(file)));
            } else {
                pane.appendChild(CE.STRTAB.lookupel('ceui.delete.msg', CE.CEUI.getDispFn(file)));
            }
            dlg.show();
        }
    }

    function RemoveShareDialog() {
        var that = this;
        var pane = CE.dce('div');
        var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup("view.removesharecaps"), pane, null, null, true);
        var m_share = null;
        var m_noPrompt, m_noRefresh, m_cb;

        function onRemoveSuccess() {
            m_share = null;
            if(!m_noRefresh) {
                reloadSidebar(false, 'cemyshare_albums', function(){
                    if(CE.CEUI.isSharedFoldersListing()) {
                        CE.CEUI.reloadContentCWD();
                        if(m_cb) m_cb();
                    }
                });
            } else {
                if(m_cb) m_cb();
            }
        } 

        function onRemoveFailure(r, data, method) {
            m_share = null;
            onGenericFailure(r, data, method);
            if(m_cb) m_cb();
        }

        function onRemoveOkClick() {
            if(CE.CEU.user) {
                // Remove the share association with all of our e-mail addresses
                var emails = CE.CEU.user.emails;
                if(!emails || emails.length < 1) {
                    emails = [];
                    if(CE.CEU.user.email)
                        emails.push({'address':CE.CEU.user.email});
                }
                var removeNextAddress = function() {
                    if(emails.length > 0) {
                        var email = emails.pop();
                        CE.CEU.svc.asyncRPC("POST", "removeAlbumShare", ["albumid",m_share.albumid, "email",email.address],
                                            removeNextAddress, onRemoveFailure);
                    } else {
                        onRemoveSuccess();
                    }
                };
                removeNextAddress();
            }
            dlg.hide();
        }
        
        function onRemoveCancelClick() {
            m_share = null;
            dlg.hide();
        }

        (function() {
            dlg.addButton('ok', CE.STRTAB.lookup('ceui.ok'), onRemoveOkClick);
            dlg.addButton('cancel', CE.STRTAB.lookup('ceui.cancel'), onRemoveCancelClick);
        })();

        this.show = function(share, noPrompt, noRefresh, cb) {
            m_share     = share;
            m_noPrompt  = noPrompt;
            m_noRefresh = noRefresh;
            m_cb        = cb;
            CE.rac(pane);
            pane.appendChild(CE.STRTAB.lookupel('view.removeself', CE.CEUI.getDispFn(m_share.name)));
            if(!m_noPrompt) {
                dlg.show();
            } else {
                onRemoveOkClick();
            }
        }
    }

    function DeleteShareDialog() {
        var that            = this;
        var pane            = CE.dce('div');
        var dlg             = new CE.CEU.Dialog(CE.STRTAB.lookup('view.removesharecaps'),pane, null, null, true);
        var m_share         = null;
        var m_delSlideAlb   = false;
        var m_noPrompt      = false;
        var m_noRefresh     = false;
        var m_cb            = null;

        function onRemoveSuccess() {
            m_share = null;
            if(!m_noRefresh) {
                reloadSidebar(null, null, function(){
                    CE.CEU.showLoadingAni(false);
                    if(CE.CEUI.isSharedFoldersListing()) {
                        CE.CEUI.reloadContentCWD();
                    }
                    if(m_cb) {
                        m_cb();
                    }
                });
            } else {
                if(m_cb) {
                    m_cb();
                }
            }
        } 

        function onRemoveFailure(r, data, method) {
            m_share = null;
            onGenericFailure(r, data, method);
            if(m_cb) {
                m_cb();
            }
        }

        function onRemoveOkClick() {
            if(CE.CEU.user && CE.CEU.user.email) {
                CE.CEU.showLoadingAni(true);
                if(!m_delSlideAlb && m_share.albumtype == FILE_TYPE_SLIDEALBUM) {
                    // Disable the metashare
                    removeAllInvitations(m_share.albumid);
                    setOption('slideshare_'+m_share.albumid, false);
                    onRemoveSuccess();
                } else {
                    var sh = m_share.root ? m_share.root : m_share;
                    shareDisableNotify(sh.serviceid, sh.deviceid, m_share.albumid, function(r,d){
                        CE.CEU.svc.asyncRPC("POST", "deleteAlbum", ["albumid", m_share.albumid], onRemoveSuccess, onRemoveFailure);
                    });
                }
            }
            dlg.hide();
        }
        
        function onRemoveCancelClick() {
            m_share = null;
            dlg.hide();
        }

        (function() {
            dlg.addButton('ok', CE.STRTAB.lookup('ceui.ok'), onRemoveOkClick);
            dlg.addButton('cancel', CE.STRTAB.lookup('ceui.cancel'), onRemoveCancelClick);
        })();

        this.show = function(share, deleteSlideAlb, noPrompt, noRefresh, cb) {
            m_delSlideAlb = deleteSlideAlb;
            m_noPrompt    = noPrompt;
            m_noRefresh   = noRefresh;
            m_cb          = cb;
            if(!share)
                return;
            m_share = share;
            if(m_delSlideAlb && m_share.albumtype == FILE_TYPE_SLIDEALBUM)
                dlg.setHeader(CE.STRTAB.lookup('view.removesharecaps.slidealb'));
            else
                dlg.setHeader(CE.STRTAB.lookup('view.removesharecaps.this'));
            CE.rac(pane);
            var type = CE.STRTAB.lookup('view.folder');
            if(m_share.albumtype == FILE_TYPE_SLIDEALBUM) {
                type = CE.STRTAB.lookup("view.slideshow");
            } else if(m_share.root && m_share.root.fileid == '0') {
                type = CE.STRTAB.lookup("view.drive");
            } else if(m_share.root && m_share.root.type == '0') {
                type = CE.STRTAB.lookup("view.file");
            }
            if(m_delSlideAlb && m_share.albumtype == FILE_TYPE_SLIDEALBUM)
                pane.appendChild(CE.STRTAB.lookupel('view.stopshare.slidealb', CE.CEUI.getDispFn(m_share.name)));
            else
                pane.appendChild(CE.STRTAB.lookupel('view.stopshare', type, CE.CEUI.getDispFn(m_share.name)));
            
            if(!m_noPrompt) {
                dlg.show();
            } else {
                onRemoveOkClick();
            }
        }
    }

    function appendNTFSMountFailMessage(msgbody) {
        var div = msgbody;
        var p = CE.dce("p");
                    
        p.appendChild(CE.STRTAB.lookupel('view.problems.intro'));
        div.appendChild(p);
        
        p = CE.dce("p");
        p.appendChild(CE.STRTAB.lookupel("view.problems.one"));
        p.appendChild(CE.dce("br"));
        p.appendChild(CE.STRTAB.lookupel("view.problems.two"));
        div.appendChild(p);
                
        p = CE.dce("p");
        p.appendChild(CE.STRTAB.lookupel("view.OR"));
        div.appendChild(p);
                
        p = CE.dce("p");
        p.appendChild(CE.STRTAB.lookupel("view.OR.one"));
        p.appendChild(CE.dce("br"));
        p.appendChild(CE.STRTAB.lookupel("view.OR.two"));

        p.appendChild(CE.dce("br"));
        p.appendChild(CE.STRTAB.lookupel("view.OR.three"));
        p.appendChild(CE.dce("br"));
        p.appendChild(CE.STRTAB.lookupel("view.OR.four"));
        p.appendChild(CE.dce("br"));
        p.appendChild(CE.STRTAB.lookupel("view.OR.five"));
        p.appendChild(CE.dce("br"));
        p.appendChild(CE.STRTAB.lookupel("view.OR.six"));
        p.appendChild(CE.dce("br"));
        p.appendChild(CE.STRTAB.lookupel("view.OR.seven"));
        p.appendChild(CE.dce("br"));
        p.appendChild(CE.STRTAB.lookupel("view.OR.eight"));
        p.appendChild(CE.dce("br"));
        p.appendChild(CE.STRTAB.lookupel("view.OR.nine"));
        p.appendChild(CE.dce("br"));
        div.appendChild(p);
        
        p = CE.dce("p");
        p.appendChild(CE.STRTAB.lookupel("view.problems.persists"));
        var a = CE.dce("a");
        a.href = CE.STRTAB.lookup("supporturl");
        a.target = "_blank";
        a.appendChild(CE.STRTAB.lookupel("view.clickhere"));
        p.appendChild(a);
        p.appendChild(CE.STRTAB.lookupel("view.problems.contact"));
        div.appendChild(p);
    }

    function appendMessageToElem(msgbody, messages, curmsgnum) {

        switch(messages[curmsgnum-1].msgcode) {
        case '1000': 
            msgbody.appendChild(CE.STRTAB.lookupel("view.problems.notrecognized"));

            var a = CE.dce("a");
            a.href = CE.STRTAB.lookup("supporturl");
            a.target = "_blank";
            a.appendChild(CE.STRTAB.lookupel("view.clickspace"));
            msgbody.appendChild(a);
            msgbody.appendChild(CE.STRTAB.lookupel("view.problems.contact"));
            break;
        case '1001':
            msgbody.appendChild(CE.STRTAB.lookupel("view.problems.nospace"));
            break;
        case "1002":
            if(messages[curmsgnum-1].details.FSTYPE == "ntfs" && 
               that.g_cursvc && 
               that.g_cursvc.serviceid.indexOf("XXX")==0 && 
               that.g_cursvc.version.indexOf("0.9.9.90")!= -1) {
                appendNTFSMountFailMessage(msgbody);
            } else {
                msgbody.appendChild(CE.STRTAB.lookupel("view.problems.notsupported"));
                var a = CE.dce("a");
                a.href = CE.STRTAB.lookup("supporturl");
                a.target = "_blank";
                a.appendChild(CE.STRTAB.lookupel("view.clickspace"));
                msgbody.appendChild(a);
                msgbody.appendChild(CE.STRTAB.lookupel("view.problems.contact"));
            }
            break;
        case "1003":
            if(messages[curmsgnum-1].details) {
                if(messages[curmsgnum-1].details.FSTYPE == "hfsj" ||
//                   ) {
//                     msgbody.appendChild(CE.STRTAB.lookupel("view.problems.hfsj"));
//                     break;
//                 } else if(
                   messages[curmsgnum-1].details.FSTYPE == "hfsplus") {
                    msgbody.appendChild(CE.STRTAB.lookupel("view.problems.hfsnowrite"));
                    var a = CE.dce("a");
                    a.href = CE.STRTAB.lookup("supporturl");
                    a.target = "_blank";
                    a.appendChild(CE.dctn("click here "));
                    msgbody.appendChild(a);
                    msgbody.appendChild(CE.STRTAB.lookupel("view.problems.contact"));
                    break;
                }
            }
            msgbody.appendChild(CE.STRTAB.lookupel("view.problems.cantwrite"));
            var a = CE.dce("a");
            a.href = CE.STRTAB.lookup("supporturl");
            a.target = "_blank";
            a.appendChild(CE.STRTAB.lookupel("view.clickspace"));
            msgbody.appendChild(a);
            msgbody.appendChild(CE.STRTAB.lookupel("view.problems.contact"));
            break;
        case "1004":
            if(messages[curmsgnum-1].details) {
                if(messages[curmsgnum-1].details.FSTYPE == "hfsplus") {
                    var div = CE.dce("div");
                    
                    div.appendChild(CE.STRTAB.lookupel('view.problems.notsafelyremoved')); 
                    
                    var ul = CE.dce("ul");
                    div.appendChild(ul);
                    
                    var li = CE.dce("li");
                    
                    li.appendChild(CE.STRTAB.lookupel('view.problems.notsafelyremoved'));
                    ul.appendChild(li);
                    
                    li = CE.dce("li");
                    li.appendChild(CE.STRTAB.lookupel('view.problems.diskutility'));
                    ul.appendChild(li);
                    
                    li = CE.dce("li");
                    li.appendChild(CE.STRTAB.lookupel("view.problems.clickrepair"));
                    ul.appendChild(li);

                    li = CE.dce("li");
                    li.appendChild(CE.STRTAB.lookupel('view.problems.reinsert'));
                    ul.appendChild(li);
                    msgbody.appendChild(div);
                    break;
                } 
            }
            var div = CE.dce("div");
            div.appendChild(CE.STRTAB.lookupel('view.problems.notsafelyremoved'));
            var ul = CE.dce("ul");
            div.appendChild(ul);
            
            var li = CE.dce("li");
            li.appendChild(CE.STRTAB.lookupel('view.problems.reinsert'));
            ul.appendChild(li);
            
            li = CE.dce("li");
            li.appendChild(CE.STRTAB.lookupel("view.problems.chkdsk"));
            ul.appendChild(li);
            
            li = CE.dce("li");
            li.appendChild(CE.STRTAB.lookupel('view.problems.clickrepair'));
            ul.appendChild(li);
            
            li = CE.dce("li");
            li.appendChild(CE.STRTAB.lookupel('view.problems.reattach'));
            ul.appendChild(li);
            
            msgbody.appendChild(div);
            break;                                        
        default:
            msgbody.appendChild(CE.dctn(messages[curmsgnum-1].description));
        }
    }

    // Class WarnMessageDialog -- Implements UI for warning message display
    function WarnMessageDialog() {
        var that = this;
        var pane = CE.dce('div');
        var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup("view.warmsgstitle"), pane);
        var messages;
        var curmsgnum;
        var sname;
        var dname;
        var cmsg;
        var nmsgs;
        var msgbody;
        var svc;
        var hidefunc;

        // Private Functions
        function updateDisplay() {
            CE.rac(cmsg);
            cmsg.appendChild(CE.dctn(''+curmsgnum));
            CE.rac(nmsgs);
            nmsgs.appendChild(CE.dctn(''+messages.length));

            if(!messages) return;
            
            CE.rac(msgbody);
            if(curmsgnum > 0) {
                appendMessageToElem(msgbody, messages, curmsgnum);
            } else {
                msgbody.appendChild(CE.STRTAB.lookupel("ceui.nomoremsgs"));
            }
        }

        function onGetMessagesSuccess(r, data, method) {
            if(dlg.isShowing()) {
                CE.rac(msgbody);
                if(r.messages) {
                    messages = r.messages;
                    curmsgnum = 1;
                    updateDisplay();
                } else {
                    if(svc)
                        svc.msgpending = 0;
                    dlg.hide();
                }
            }
        }
        function onGetMessagesFailure(r, data, method) {
          if(dlg.isShowing()) {
              message = null;
              curmsgnum = -1;
          }
        }
        function onPrevClick() {
          if(curmsgnum>1) {
            curmsgnum--;
            updateDisplay();
          }
        }

        function onClearClick(eh) {
            if(messages) {
                if(svc.msgpending > 0) {
                    var args = ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                        'cmd', 'clearmessages', 'messageindex', "" + (curmsgnum-1) ];
                    CE.CEU.svc.asyncRPC('POST', 'sendServiceCommand', args);
                    messages.splice(curmsgnum-1, 1);
                    if(curmsgnum > messages.length) {
                        curmsgnum = messages.length;
                    }
                    svc.msgpending --;
                    if(messages.length > 0) {
                        updateDisplay();
                        return false;
                    }
                }
            }
            return true;
        }

        function onNextClick() {
            if(messages) {
                if(curmsgnum<messages.length) {
                    curmsgnum++;
                    updateDisplay();
                }
            }
        }

        function onHide() {
            if(hidefunc) {
                hidefunc({ service: svc });
            }
        }

        // Build the pane
        (function() {
            var d = CE.dce('div', null, 'section');
            var s = CE.dce('span');
            s.appendChild(CE.dctn('Drive '));
            d.appendChild(s); s = null;
            sname = CE.dce('span');
            d.appendChild(sname);
            var s = CE.dce('span');
            s.appendChild(CE.dctn(' on '));
            d.appendChild(s); s = null;
            dname = CE.dce('span');
            d.appendChild(dname);
            pane.appendChild(d); d = null;

            var d = CE.dce('div', null, 'section');
            var s = CE.dce('span');
            s.appendChild(CE.STRTAB.lookupel('view.msgspace'));
            d.appendChild(s); s = null;
            cmsg = CE.dce('span');
            d.appendChild(cmsg);
            var s = CE.dce('span');
            s.appendChild(CE.dctn(' of '));
            d.appendChild(s); s = null;
            nmsgs = CE.dce('span');
            d.appendChild(nmsgs);
            pane.appendChild(d); d = null;

            d = CE.dce('div', null, 'section');
            msgbody = CE.dce('div', null, 'msgbody');
            d.appendChild(msgbody);
            pane.appendChild(d); d = null;
            dlg.addButton('prev', CE.STRTAB.lookup('view.previous'), onPrevClick);
            dlg.addButton('clear', CE.STRTAB.lookup('view.clear'), onClearClick);
            dlg.addButton('next', CE.STRTAB.lookup('view.next'), onNextClick);
            dlg.onhide = onHide;
        })();


        // Public methods
        this.show = function(eh) {
            if(!dlg.isShowing()) {
                CE.rac(sname);
                sname.appendChild(CE.dctn(eh.name));
                CE.rac(dname);
                dname.appendChild(CE.dctn(eh.device.name));

                messages = null;
                curmsgnum = 1;
                CE.rac(cmsg);
                cmsg.appendChild(CE.dctn(''+curmsgnum));
                CE.rac(nmsgs);
                nmsgs.appendChild(CE.dctn(''+eh.service.msgpending));

                CE.rac(msgbody);
                msgbody.appendChild(CE.STRTAB.lookupel('view.loading'));
                
                svc = eh.service;
                hidefunc = eh.onHide;

                var args = ['deviceid', eh.service.deviceid, 'serviceid', eh.service.serviceid,
                            'cmd', 'getmessages'];
                CE.CEU.svc.asyncRPC('POST', 'sendServiceCommand', args,
                                    onGetMessagesSuccess, onGetMessagesFailure);

                dlg.show();
            }
        }
    }

    //----------------------------------------------------------------------------------------
    // Private Functions
    
    function trackEvent(action, actionType, file) {
        if(_gaq && (!CE.CEDBG.isEnabled || !CE.CEDBG.isEnabled())) {
            var category = 'Sharee';
            for(var s in g_svcmap) { category='Owner'; break; }
            if(actionType) {
                action = action + ' - ' + actionType;
            } else if(CE.CEU.user && CE.CEU.user.userid) {
                if(file && file.svc && file.svc.device && file.svc.device.ownerid) {
                    if(file.svc.device.ownerid == CE.CEU.user.userid)
                        action = action + ' - Owned';
                    else
                        action = action + ' - Shared';
                } else if(CE.CEUI.g_cursvc) {
                    action = action + ' - Owned';
                } else if(CE.CEUI.g_curalbum) {
                    if(CE.CEUI.g_curalbum.ownerid == CE.CEU.user.userid)
                        action = action + ' - Owned';
                    else
                        action = action + ' - Shared';
                }
            }
            
            var label = '';
            var value = 'none';
            if(file) {
                if(CE.CEUI.isDescendableFile(file)) {
                    label = CE.STRTAB.lookup('view.directory.' + file.type);
                } else if(file.mimetype) {
                    label = getNameForMime(file.mimetype);
                    if(!label) label = file.mimetype;
                }
                var dotPos = file.name.lastIndexOf('.');
                if(dotPos != -1)
                    value = file.name.substr(dotPos+1);
            }
            
            setTimeout(function() {
                _gaq.push(['_trackEvent', category, action, label]);
            }, 1);
        }
    }

    // --- Private UI event helpers

    // Drag-and-drop
    function startDrag(srcobj, srcevt, srctel)
    {
        g_curdrag = {src: srcobj};
        g_diddrag = false;
        g_dragorigin = {x:g_curmousepos.x, y:g_curmousepos.y};

        // Leverage our ICON code below to create a new visual dragging DOM element...
        g_curdrag.d = CE.dce('div', 'cedrag');
        var go = CE.CEU.shallowCopy(srcobj.go);
        if(go.viewmode == 1) {
            go.viewmode = 0;
            go.viewsize = 'small';
            CE.aCN(g_curdrag.d, 'small');
        } else if(go.grd == 'celist') {
            CE.aCN(g_curdrag.d, g_viewsize);
        }
        appendContentIcon(g_curdrag.d, srcobj, false, go);
        
        var gid = getGid(go.grd, srcobj.id, srcobj.file);
        var sel = CE.getByClass(CE.CEU.$(gid), 'div', 'selectcheck');
        if(sel.length > 0 && CE.hCN(sel[0],'selected')) {
            (g_curdrag.d.appendChild(CE.dce('div',null,'multiple'))).appendChild(CE.STRTAB.lookupel('view.selectcheck.multiple'));
        }
        
        g_curdrag.d.style.position = 'fixed';
        g_curdrag.d.style.left = ''+(g_curmousepos.x+1)+'px';
        g_curdrag.d.style.top  = ''+(g_curmousepos.y+1)+'px';

        // Debug code...
        var el = CE.CEU.$('debug-drag');
        if(el) el.value = ''+g_curdrag.src.name;
        el = null;
        CE.CEDBG.println('CEUI: Drag Possible: '+g_curdrag.src.name+' ('+g_curdrag.src.id+')');
    }

    function stopDrag()
    {
        if(g_curdrag) {
            CE.CEDBG.println('CEUI: Drag Stop: '+g_curdrag.src.name+' ('+g_curdrag.src.id+')');
            CE.CEUI.g_videoPrevDisabled = false;
            if(g_diddrag) {
                document.body.removeChild(g_curdrag.d);
            }
            g_curdrag.d  = null;
            g_curdrag    = null;
            g_diddrag    = false;
            g_dragorigin = null;

            // Debug code...
            var el = CE.CEU.$('debug-drag');
            if(el) el.value = '';
            el = null;
        }
    }
    
    function updateSelRect(ev) {
        if(!g_selrect.touched) {
            if(Math.abs(ev.clientX-g_selrect.startX) < 3 && Math.abs(ev.clientY-g_selrect.startY) < 3)
                return;
            
            g_selrect.div            = document.body.appendChild(CE.dce('div',null,'ceselrect'));
            g_selrect.div.style.left = g_selrect.startX + 'px';
            g_selrect.div.style.top  = g_selrect.startY + 'px';
            g_selrect.touched = true;
        }
        
        // Update the rectangle
        var left   = g_selrect.startX;
        var width  = ev.clientX - g_selrect.startX;
        var top    = g_selrect.startY;
        var height = ev.clientY - g_selrect.startY;
        if(width < 0) {
            left  = ev.clientX;
            width = -width;
        }
        if(height < 0) {
            top    = ev.clientY;
            height = -height;
        }
        
        var mainDiv, mainPos, mainWidth, mainHeight;
        if(g_selrect.grd == 'celist') {
            mainDiv    = CE.CEU.$(g_selrect.grd).parentNode;
            mainWidth  = mainDiv.offsetWidth;
            mainHeight = mainDiv.offsetHeight;
            mainPos    = CE.CEU.getAbsPos(mainDiv);
            mainPos.x  += 5;
            mainHeight -= 2;
            mainWidth  -= 9;
        } else {
            mainDiv    = CE.CEU.$(g_selrect.grd).parentNode.parentNode;
            mainWidth  = mainDiv.offsetWidth - 5;
            mainHeight = mainDiv.offsetHeight - 5;
            mainPos    = CE.CEU.getAbsPos(mainDiv);
            mainPos.x  += 3;
            mainPos.y  += 3;
            mainHeight -= 3;
            mainWidth  -= 3;
        }
        if(left < mainPos.x) {
            var diff = mainPos.x - left;
            left  += diff;
            width -= diff;
            if(width < 0)
                width = 0;
        }
        if(top < mainPos.y) {
            var diff = mainPos.y - top;
            top    += diff;
            height -= diff;
            if(height < 0)
                height = 0;
        }
        if((left+width) > (mainPos.x+mainWidth)) {
            width -= (left+width) - (mainPos.x+mainWidth);
        }
        if((top+height) > (mainPos.y+mainHeight)) {
            height -= (top+height) - (mainPos.y+mainHeight);
        }
        g_selrect.div.style.top    = top + 'px';
        g_selrect.div.style.height = height + 'px';
        g_selrect.div.style.left   = left + 'px';
        g_selrect.div.style.width  = width + 'px';
        
        // Select the items that intersect the rectangle
        var previds = g_selrect.fileids;
        g_selrect.fileids = {};
        var items = CE.CEU.$(g_selrect.grd).getElementsByTagName('tr');
        if(!items || !items.length || items.length < 1)
            items = CE.CEU.$(g_selrect.grd).getElementsByTagName('div');
        if(items && items.length) {
            for(var i = 0; i < items.length; ++i) {
                if(!items[i].id)
                    continue;
                var sel = CE.getByClass(items[i], 'div', 'selectcheck');
                if(sel.length == 0)
                    continue;
                
                var inRect = true;
                var pos    = CE.CEU.getAbsPos(items[i]);
                if(pos.x > (left+width))
                    inRect = false;
                if((pos.x+items[i].offsetWidth) < left)
                    inRect = false;
                if(pos.y > (top+height))
                    inRect = false;
                if((pos.y+items[i].offsetHeight) < top)
                    inRect = false;
                
                if(inRect) {
                    g_selrect.fileids[items[i].id] = true;
                    if(!previds[items[i].id]) {
                        var evt = CE.CEU.getEventObj(sel[0], 'click');
                        evt.onEvent(evt);
                    }
                } else {
                    if(previds[items[i].id]) {
                        // Fell outside of shrunken rectangle; deselect
                        var evt = CE.CEU.getEventObj(sel[0], 'click');
                        evt.onEvent(evt);
                    }
                }
            }
        }
    }

    function onMouseMove(ev)
    {
        if(!ev) {
            ev = window.event;
        }
        g_curmousepos.x = ev.clientX;
        g_curmousepos.y = ev.clientY;
        
        if(g_slideshowObj && CE.CEU.$('slide_flv')) {
            g_slideshowObj.showController();
        }

        if(g_selrect) {
            updateSelRect(ev);
            return false;
        }

        // Debug code...
        var el = CE.CEU.$('debug-mouse-x');
        if(el) el.value = ''+g_curmousepos.x;
        el = CE.CEU.$('debug-mouse-y');
        if(el) el.value = ''+g_curmousepos.y;
        el = null;

        // Drag-and-drop support
        // OK, if we are actively dragging, move the floating item
        if(g_curdrag && g_curdrag.src) {
            if(!g_diddrag) {
                // Check if we passed our delta for starting dragging
                var dx = g_curmousepos.x - g_dragorigin.x;
                var dy = g_curmousepos.y - g_dragorigin.y;
                if(dx > 5 || dx < -5 || dy > 5 || dy < -5) {
                    CE.CEDBG.println('CEUI: Drag Start: '+g_curdrag.src.name+' ('+g_curdrag.src.id+')');
                    closeHoverPreviewVideo(true);
                    g_diddrag = true;
                    document.body.appendChild(g_curdrag.d);
                }
            }
            g_curdrag.d.style.left = ''+(g_curmousepos.x+1)+'px';
            g_curdrag.d.style.top  = ''+(g_curmousepos.y+1)+'px';
            return false;
        }

        if(g_mousedisp) {
            //CE.CEDBG.println("Dispatching Mouse Event...");
            return g_mousedisp(ev, g_curmousepos);
        }

        // Continue event processing...
        return true;
    }
    
    function cancelSelRect() {
        if(g_selrect) {
            document.body.ondragstart   = null;
            document.body.onselectstart = null;
            CE.CEU.$(g_selrect.grd).style.MozUserSelect = null;
            CE.CEU.$(g_selrect.grd).parentNode.style.MozUserSelect = null;

            if(g_selrect.div)
                g_selrect.div.parentNode.removeChild(g_selrect.div);
                
            var touched = g_selrect.touched;
            g_selrect = null;
            if(touched)
                return false;
        }
    }

    function onMouseUp(ev)
    {
        //CE.CEDBG.println("CEUI: onMouseUp");
        
        cancelSelRect();

        if(g_audioPlayer) {
            if(g_audioPlayer.onDragStop())
                return false;
        }

        if(!ev)         ev        = window.event;
        if(!ev.target)  ev.target = ev.srcElement;
        closeContextMenu(ev);
        if(g_curdrag && g_diddrag) {
            var nnotify = 0;
            var tel;
            if(ev.target) { tel = ev.target; } else { tel = ev.srcElement; }
            CE.CEDBG.println('CEUI: Drop occurred on: '+tel.nodeName+
                             ' (ONCEDROP: '+(tel.oncedrop?'yes':'no')+' ID: '+tel.id+
                             ' CLASS: '+tel.className+')');
            if(!tel || !tel.oncedrop || tel.oncedrop(g_curdrag, ev, tel)) {
                while(tel && !tel.oncedrop) {
                    // look up until we find the ID of the drop location...
                    tel = tel.parentNode;
                    if(tel) {
                        CE.CEDBG.println('CEUI:   -- Try Parent: '+tel.nodeName+
                                         ' (ONCEDROP: '+(tel.oncedrop?'yes':'no')+' ID: '+tel.id+
                                         ' CLASS: '+tel.className+')');
                        if(tel.oncedrop) {
                            nnotify++;
                            if(!tel.oncedrop(g_curdrag, ev, tel)) {
                                break;
                            }
                        }
                    }
                }
            }
            CE.CEDBG.println('CEUI: Found and notified '+nnotify+' callbacks');
        }
        
        stopDrag();
        // Continue event processing...
        return true;
    }
        
    function printErrorInDiv(msg, e, dflt) {
        if(e) {
            if(e.ecode && e.ecode == '812') {
                msg.appendChild(CE.STRTAB.lookupel("view.error.toolong"));
                return;
            } else if(e.ecode && e.ecode == '808') {
                msg.appendChild(CE.STRTAB.lookupel("view.error.exists"));
                return;
            } else if(e.message) {
                msg.appendChild(CE.dctn(e.message));
                return;
            }
        }
        msg.appendChild(CE.dctn(dflt));
    }

    function findFolderShare(f) {
        var share = null;

        if(f) {
            if(f.album) {
                share = g_albummap[f.album.albumid];
            } else {
                var svc = CE.CEUI.getCurSvc(f);
                if(svc) {
                    var shareid = svc.deviceid + ':_:' + svc.serviceid + ':_:' + f.fileid;
                    share = g_sharedfolders[shareid];
                }
            }
        }
        
        return share;
    }

    function findFileShare(f) {
        if(f) {
            var svc = CE.CEUI.getCurSvc(f);
            if(svc) {
                var shareid = svc.deviceid + ':_:' + svc.serviceid + ':_:' + f.fileid;
                return g_sharedfiles[shareid];
            }
        }
        return null;
    }
    
    function findShare(f) {
        var share        = findFolderShare(f);
        if(!share) share = findFileShare(f);
        return share;
    }

    function activateDevice(id) {
        updateSidebarState(id);
        enterDeviceMode();

        that.g_curalbum = that.g_cursvc = null;
        CE.CEU.releaseAllEvents(CE.CEU.$("celist"), 'click', true);
        showEmptyContentDiv("drives");
        enableDisableToolbarButtons();
        return true;
    }

    function activateNothing() {
        enterDeviceMode();
        CE.aCN("cebc", "hidden");
        showEmptyContentDiv("nothing");
        enableDisableToolbarButtons();
        return true;
    }

    function activateSomething(oobeok) {
        var foundsomething = false;

        if(!CE.CEU.$("cesidebar")) {
            // there's no sidebar, we are not really logged in. don't mess anything up.
            return;
        }

        // If we have at least one service, let's activate it implicitly...
        for(var s in g_svcmap) {
            if(g_svcmap[s].online && g_svcmap[s].online == '1') {
                foundsomething = activateService(s);
                break;
            }
        }
        if(!foundsomething) CE.CEDBG.println("No Services to activate!");

        if(!foundsomething) {
            // Ok, try an inactive device
            for(var d in g_devmap) {
                foundsomething = activateDevice(d);
                break;
            }
        }
        
        if(!foundsomething) {
            for(var a in g_albummap) {
                foundsomething = activateAlbum(a, false);
                break;
            }
        }
        if(!foundsomething)CE.CEDBG.println("No Albums to activate!");        

        if(!foundsomething) {
            for(var a in g_sharemap) {
                CE.CEUI.showSharedFolders('share', 'celist');
                foundsomething = true;
                break;
            }
        }
        if(!foundsomething) CE.CEDBG.println("No Shares to activate!");        

        if(oobeok) {
            if(tryOOBE()) {
                foundsomething = true;
            } else {
                CE.CEDBG.println("No OOBE to show!");                        
            }
        }

        if(!foundsomething) {
            for(var d in g_orphans) {
                foundsomething = activateDevice(d);
                break;
            }
        }
        if(!foundsomething) CE.CEDBG.println("No Empty Devices to activate!");        

        if(!foundsomething) {
            foundsomething = activateNothing();
        }        
    }
    
    // Initialization
    function initOpDone() {
        if(!g_inited) {
            g_initops--;
            CE.CEDBG.println('CEUI: Init Op Done: '+g_initops);
            if(g_initops<=0) {
                // Create our helper UI classes
                g_preview   = new CE.CEUI.PreviewPane();
                g_slideshow = new CE.CEUI.SlideshowPane();

                if(!CE.CEU.getSearchParam('docprev_fileid')) {
                    CE.CEDBG.println('CEUI: Init Complete, turning on UI bits...');
                    if(!g_jumpToShare) {
                        CE.CEU.showLoadingAni(false);
                        var albumid = CE.CEU.getSearchParam("albumid");
                        if(!albumid) {
                            albumid = window.location.hash;
                        }
                        if(albumid) {
                            if(!activateAlbum(albumid, true)) {
                                if(!activateAlbum(albumid, false)) {                        
                                    activateSomething(true);
                                }
                            }
                        } else {
                            activateSomething(true);
                        }
                    }
                    
                    // OK, hook up the other UI bits...
                    el = CE.CEU.$('cehead_controls');
                    if(el) CE.rCN(el, 'hidden');
                    el = CE.CEU.$('cemain');
                    if(el) CE.rCN(el, 'hidden');
                    el = CE.CEU.$('cetoolbar');
                    if(el) CE.rCN(el, 'hidden');
                    CE.rCN('cemylib_ss_text', 'hidden');
                } else {
                    // Full-window document preview
                    var fileid    = CE.CEU.getSearchParam('docprev_fileid');
                    var deviceid  = CE.CEU.getSearchParam('docprev_deviceid');
                    var serviceid = CE.CEU.getSearchParam('docprev_serviceid');
                    if(deviceid && serviceid) {
                        CE.CEU.svc.asyncRPC('POST', 'getFile', ['deviceid',deviceid, 'serviceid',serviceid, 'fileid',fileid],
                        function(r) {
                            var docdiv = document.body.appendChild(CE.dce('div',null,'cedocprevfull'));
                            r.file.svc = g_svcmap[deviceid + ':_:' + serviceid] || {'deviceid':deviceid, 'serviceid':serviceid};
                            r.file.deviceid  = deviceid;
                            r.file.serviceid = serviceid;
                            var docprev = new DocumentPreview(r.file, docdiv);
                            docprev.show();
                        },
                        onGenericFailure);
                    }
                }
                
                // Shareing bits...
                el = shbyid('ceshare_email');
                if(el) {
                    CE.CEU.attachEvent(el, 'keydown', {onEvent:onShareInviteKeyDown});
                    CE.CEU.attachEvent(el, 'keyup', {onEvent:showContactSuggestions});
                }
                el = null;
                
                // Drag-and-drop support
                document.onmousemove = onMouseMove;
                document.onmouseup   = onMouseUp;
                
                // Supress keystroke processing if celoading showing
                document.onkeydown = function() { return (!CE.CEU.$('celoading') || CE.hCN(CE.CEU.$('celoading'), 'hidden')); };

                g_inited = true;               
            }
        }
    }

    function onGenericFailure(r, data, method) {
        var reason = null;
        CE.CEU.showLoadingAni(false);
        if(r && r['HB-EXCEPTION']) {
            reason = r['HB-EXCEPTION'];
        }
        if(reason) {
            CE.CEDBG.println('CEUI: RPC EXCEPTION ('+method+'): '+CE.CEDBG.serialize(reason));
        } else {
            CE.CEDBG.println('CEUI: RPC ERROR ('+method+'): '+CE.CEDBG.serialize(r));
        }
        if(!CE.CEU.isLoggedIn() &&
           window.location.pathname.indexOf('album.html') == -1 &&
           window.location.pathname.indexOf('/share/') == -1) {
            //alert('LOGGING OUT -- RPC FAILURE!');
            CE.CEDBG.println("CEUI: No Valtoken!");
            return window.location.replace('index.html#logout');
        }
        CE.CEU.notifyError(r, 'RPC', method);
    }

    function updateServiceMessageCount(eh) {
        var grd = CE.CEU.$("cemylib_devices")
        var svc = eh.service;
        if(!svc) return;
        if(!svc.id) {
            svc.id = svc.deviceid + ":_:" + svc.serviceid;
        }
        // this is just to update the exclam image. 
        appendSidebarItem(grd, {onEvent: onClickSidebar, 
                                id: svc.id,
                                name: svc.name,
                                type: 'service',
                                device: svc.device,
                                service: svc,
                                owner: svc.device.owner,
                                disabled: !svc.online || svc.online == "0"
                               });
    }

    function onClickWarningMessages(eh, evt, tel) {
        CE.CEDBG.println('CEUI: User clicked on warning messages: "'+eh.eh.name+'" ('+eh.eh.id+')');

        if(!g_warnmessage) {
            g_warnmessage = new WarnMessageDialog();  
        }
        eh.eh.onHide = updateServiceMessageCount;
        g_warnmessage.show(eh.eh);
        return false;
    }

    function onServiceEjectSuccess(s) {
        reloadSidebar();
    } 

    function onEjectServiceClick(eh, evt, tel) {
        if(!g_ejectservicedialog) {
            g_ejectservicedialog = new EjectConfirmDialog();
        }
        g_ejectservicedialog.show(eh.eh.service, onServiceEjectSuccess);
    }
    
    function onEjectCloudServiceClick(e) {
        if(!e.eh.disconnecting) {
            e.eh.disconnecting = true;
            CE.CEU.promptYesNo(CE.STRTAB.lookup('settings.unregcloud.dlgtitle'),
                               CE.STRTAB.lookup('ceui.unregcloud.device', e.eh.name),
            function() {
                e.eh.disconnecting = false;
                CE.CEU.showLoadingAni(true);
                CE.CEU.svc.asyncRPC('POST', 'unregisterDevice', ['deviceid',e.eh.device.deviceid],
                function() {
                    CE.CEU.showLoadingAni(false);
                    reloadSidebar();
                });
                return true;
            },
            function() {
                e.eh.disconnecting = false;
                return true;
            });
        }
    }

    function enterAppMode() {
      CE.aCN('cecontent', 'hidden');
      //CE.rac(CE.CEU.$('ceappcontent'));
      CE.rCN('ceappcontent', 'hidden');
    }

    function leaveAppMode() {
      CE.aCN('ceappcontent', 'hidden');
      //CE.rac(CE.CEU.$('ceappcontent'));
      var f = CE.CEU.$('ceappframe');
      if(f) {
        f.src = 'about:blank';
        f = null;
      }
      CE.rCN('cecontent', 'hidden');
    }

    function enterDeviceMode() {
        enterServiceMode();
        //CE.aCN("cetool_newfolder", "hidden");
        //CE.aCN("cetool_upload", "hidden");
        //CE.aCN("cecontent_controls", "hidden");
        //CE.aCN('cemylib_ss', "hidden");
    }

    function onClickSidebar(eh, evt, tel) {
        CE.CEDBG.println('CEUI: User clicked on "'+eh.name+'" ('+eh.id+')');

        // always want to clear the name search if we can
        var e = CE.CEU.$("cesearch_entry");
        if(e) e.value = CE.STRTAB.lookup("view.search");

        // First disable any applied search
        that.applySearch(null, null, true);
        if(eh.service) {
            // They clicked on a service, let's see how we should view it...g
            activateService(eh.id);
        } else if(eh.device) {
            activateDevice(eh.id);
        } else {
            CE.CEDBG.println('CEUI: Unknown Sidebar Click!');
        }
        return false;
    }

    function onRemoveFolderShareClick(e, noPrompt, noRefresh, cb) {
        if(!g_removeshareconfirm) {
            g_removeshareconfirm = new RemoveShareDialog();
        }
        if(e && e.album) {
            g_removeshareconfirm.show(e.album, noPrompt, noRefresh, cb);
        }
    }

    function onDeleteFolderShareClick(e, evt, el, deleteSlideAlb, noPrompt, noRefresh, cb) {
        if(!g_deleteshareconfirm) {
            g_deleteshareconfirm = new DeleteShareDialog();
        }
        if(e && e.albumid) {
            g_deleteshareconfirm.show(CE.CEUI.getAlbum(e.albumid), deleteSlideAlb, noPrompt, noRefresh, cb);
        }
    }
    
    function checkServiceMessagesStillValid(eh) {
        if(eh.service.readonly)
            return;

        // The drive isn't read-only, so get rid of any old messages that suggest that it is
        CE.CEU.svc.asyncRPC('POST', 'sendServiceCommand',
                            ['deviceid',eh.service.deviceid, 'serviceid',eh.service.serviceid, 'cmd','getmessages'],
                            function(r){
                                if(r && r.messages) {
                                    var numRemoved = 0;
                                    for(var i = r.messages.length-1; i >= 0; --i) {
                                        if(r.messages[i].msgcode == '1003' || r.messages[i].msgcode == '1004') {
                                            CE.CEU.svc.asyncRPC('POST', 'sendServiceCommand',
                                                                ['deviceid',eh.service.deviceid, 'serviceid',eh.service.serviceid,
                                                                 'cmd','clearmessages', 'messageindex',''+i]);
                                            ++numRemoved;
                                        }
                                    }
                                    if(numRemoved == r.messages.length) {
                                        eh.service.msgpending = 0;
                                        updateServiceMessageCount(eh);
                                    }
                                }
                            });
    }

    function appendSidebarItem(grd, eh) {
        if(!g_printSupport && eh.service && eh.service.type && eh.service.type.indexOf("xce:printer")==0)
            return;

        var li, a, updateonly = false, didmsgs = false;
        li = CE.CEU.$(eh.id);
        if(li) {
            updateonly = true;
            CE.rac(li);
        } else {
            li = CE.dce('li', eh.id);
            // TODO: we need to improve this...
            if(eh.onEvent) {
                CE.CEU.attachEvent(li, 'click', eh);
            }
            if(eh.onCEDrop) {
                li.oncedrop = eh.onCEDrop;
            }
        }
        a = CE.dce('a');
        a.href = '#';
        
        var dispLen = 17;
        if(eh.album && eh.album.feedid && eh.album.feedid != '')
            dispLen = 13;
        
        if(eh.service && eh.service.type && eh.service.type.indexOf("xce:plugfs:cloud")==0) {
            a.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'blank.gif',null,'icon drivecloud'));
        } else if(eh.service && eh.service.type && eh.service.type.indexOf("xce:printer")==0) {
            a.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'blank.gif',null,'icon icoprinter'));
        } else {
            a.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'blank.gif',null,'icon drive'));
        }
        
        if(eh.album) {
            var sp = CE.dce('span', 'dispname_'+eh.album.albumid);
            sp.appendChild(CE.dctn(CE.CEU.trimstring(CE.CEUI.getDispFn(eh.name), dispLen)));
            a.appendChild(sp);
        } else {
            a.appendChild(CE.dctn(CE.CEU.trimstring(CE.CEUI.getDispFn(eh.name), dispLen)));
        }

        a.title = CE.CEUI.getDispFn(eh.name);

        if(eh.device && eh.device.name) {
            var d = CE.dce('div', null, 'devname');
            if(eh.disabled) {
                var e = CE.dce('div', null, 'disabled');
                d.appendChild(e);
                e.appendChild(CE.dctn('on '+CE.CEU.trimstring(eh.device.name, 17)));
            } else {
                d.appendChild(CE.dctn('on '+CE.CEU.trimstring(eh.device.name, 17)));
            }
            a.appendChild(d);
        }
        if(eh.service && eh.service.msgpending) {
            var nmsg = 0;
            try {
                nmsg = parseInt(eh.service.msgpending);
            } catch(e) {
            }
            if(nmsg>0) {
                var d = CE.dce('div', null, 'msgpending');
                d.appendChild(CE.dctn(' '));
                CE.CEU.attachEvent(d, 'click', {onEvent: onClickWarningMessages, eh:eh});
                a.appendChild(d);
                didmsgs = true;
                checkServiceMessagesStillValid(eh);
            } 
        }
        if(!didmsgs && eh.type == 'service') {
            var isCloudSvc = false;
            if(eh && eh.service) {
                for(var c = 0; c < CE.CEU.CLOUD_TYPES.length; ++c) {
                    if(eh.service.type == CE.CEU.CLOUD_TYPES[c].type)
                        isCloudSvc = true;
                }
            }
            var d = CE.dce('div', null, 'ejectservice');
            d.appendChild(CE.dctn(' '));
            if(!isCloudSvc) {
                CE.CEU.attachEvent(d, 'click', {onEvent: onEjectServiceClick, eh:eh});
                d.title = CE.STRTAB.lookup("view.hint.safelyremove");
            } else {
                CE.CEU.attachEvent(d, 'click', {onEvent: onEjectCloudServiceClick, eh:eh});
                d.title = CE.STRTAB.lookup("view.hint.safelyremove.cloud");
            }
            a.appendChild(d);
        }

        if(eh.disabled) CE.aCN(a, "disabled");
        li.appendChild(a); a = null;
        if(!updateonly) {
            grd.appendChild(li); li = null;
        }
    }

    function populateSourceServiceList() {
        if(!CE.hCN('cesrctray', 'hidden'))
            return; // only do this if tray is not opened

        var idx = CE.CEU.$("cesrcsvc").selectedIndex;
        var sel = [CE.CEU.$("cesrcsvc_svcs"), CE.CEU.$("cemaindrop_svcs")];
        if(!sel[0]) return;  // album.html does not have the copy files or the src service selector...
        CE.rac(sel[0]);
        CE.rac(sel[1]);
        g_srcsvc = null;
        
        // Services
        for(var s in g_svcmap) {
            if(g_svcmap[s].online) {
                if(g_svcmap[s].type) {
                    if(g_svcmap[s].type.indexOf("xce:plugfs")==0) {
                        for(var i = 0; i < 2; ++i) {
                            var o = CE.dce('option');
                            o.value = s;
                            o.appendChild(CE.dctn(g_svcmap[s].name));
                            sel[i].appendChild(o);
                            if(!g_srcsvc)
                                g_srcsvc = g_svcmap[s];
                        }
                    } else if(g_svcmap[s].type.indexOf("xce:printer")==0) {
                        if(!g_printer && g_printSupport) {
                            g_printer = g_svcmap[s];
                        }
                    }
                }
            }
        }
        
        // Shares
        sel = CE.CEU.$("cesrcsvc_sharedwithme");
        CE.rac(sel);
        for(var s in g_sharemap) {
            var svc = g_sharemap[s];
            if(svc && svc.root) {
                var o = CE.dce('option');
                o.value = s;
                o.appendChild(CE.dctn(svc.name));
                sel.appendChild(o);
                if(!g_srcsvc)
                    g_srcsvc = svc;
            }
        }
        
        CE.CEU.$("cesrcsvc").selectedIndex = idx;
    }

    function enterAlbumMode(a, share) {
        leaveAppMode();
        CE.rCN("cecontent_controls", "hidden");
    }

    function initialAlbumFileListSuccess(r, go) {
        if(!g_inited) {
            g_initops--;
            if(g_initops <= 0) {
                initOpDone();
            }
        }
        if(r.album) {
            that.g_curalbum = r.album;
            if(go) go.album = r.album;
        }
        if(!CE.CEUI.g_isSlideAlb && r && r.files && r.files.length == 1 && CE.CEUI.isDescendableFile(r.files[0])) {
            reloadContent(r.files[0].id, go);            
        } else {
            populateContent(r, go);
        }
    }

    function initialAlbumFileListFailure(r, go) {
        showEmptyContentDiv("albumdrive");
    }
    
    function fixAlbumFiles(r, d, nextOp) {
        // The preview field in album files that are videos actually contains the transcoded video stream
        //   Update the files so that things look the way we'd expect
        if(r.files) {
            for(var i = 0; i < r.files.length; ++i) {
                var file = r.files[i];
                if((!file.stream || file.stream == '') && file.mimetype && file.mimetype.substr(0,6) == 'video/') {
                    file.stream = file.preview;
                }
            }
        }
        
        if(nextOp) {
            nextOp(r, d);
        }
    }

    function loadInitialAlbumFiles(a, openshare, ismetashare) {
        var go = { album: a, viewmode: g_viewmode, grd: 'celist', actions: true, shared: openshare, metashare: ismetashare };
        if((a && a.albumtype == FILE_TYPE_FILEALBUM) || (a && a.root && !CE.CEUI.isDescendableFile(a.root))) {
            initialAlbumFileListSuccess({'album':a, 'files':[a.root]}, go);
        } else if(a && a.albumtype != FILE_TYPE_SLIDEALBUM && a.root) {
            if(!g_inited) {
                g_initops --;
                if(g_initops < 1) {
                    //initOpDone();
                }
            }
            go.pid = a.root.fileid;
            reloadContent(a.root.id, go, a.root.cousins ? a.root : null);
        } else if(a && a.albumtype != FILE_TYPE_SLIDEALBUM && a.files) {
            var root     = CE.CEU.shallowCopy(a.files[0]);
            root.svc     = { deviceid:root.deviceid, serviceid:root.serviceid };
            root.cousins = [];
            for(var i = 1; i < a.files.length; ++i) {
                a.files[i].svc = { deviceid:a.files[i].deviceid, serviceid:a.files[i].serviceid };
                root.cousins.push(a.files[i]);
            }
            reloadContent(null, go, root);
        } else {
            var numPerPage = NPERPAGE;
            if(a && a.albumtype == FILE_TYPE_SLIDEALBUM) {
                CE.CEUI.g_isSlideAlb = true;
                numPerPage           = NPERPAGE_SLIDEALB;
            }
            var args = [];
            if(a)
                args.push('albumid', a.albumid);
            args.push("pageoffset", 0);
            args.push("maxcount",   numPerPage);
            if(that.g_sharetoken)
                args.push("sharetoken", that.g_sharetoken);
            var el = CE.CEU.$('celist');
            CE.CEU.releaseAllEvents(el, 'mousedown', false);
            CE.CEU.releaseAllEvents(el, 'mousemove', false);
            CE.CEU.releaseAllEvents(el, 'click', true);
            CE.CEU.showLoadingAni(true);
            CE.CEU.svc.asyncRPC("POST", "listAlbumFiles", args,
                                function(r,d){fixAlbumFiles(r,d,initialAlbumFileListSuccess);},
                                initialAlbumFileListFailure, go);
        }
    }

    function onCheckReadOnlyFSSuccess(r, a) {
        if(a && a.root) {
            a.root.readonly = false;
            if(r) {
                if(r.flags & 1) {
                    a.root.readonly = true;
                    if(a.perms && a.perms != '0') {
                        CE.rCN("cereadonly", "hidden");
                    }
                } 
                if(r.flags & FS_FLAGS_SORTABLE) {
                    a.root.sortable = true;
                    CE.rCN('cetool_sort', 'cedisabled');
                } else {
                    CE.aCN('cetool_sort', 'cedisabled');
                }
            }
        }
    }

    function onCheckReadOnlyFSFailure(r, a) {
        // TODO: Is this the right thing to do?
        if(a && a.root) {
            a.root.readonly = true;
            if(a.perms && a.perms != '0') {
                CE.rCN("cereadonly", "hidden");
            }
        }
    }

    function activateAlbum(id, isshare, ismetashare) {
        if(!isshare && disallowedShareWarning()) {
            return false;
        }
        
        var shareMap = isshare ? g_sharemap : g_albummap;
        var a        = shareMap[id] ? CE.CEU.shallowCopy(shareMap[id]) : null;
        
        if(a) {
            CE.CEDBG.println('CEUI: Activating album: '+a.name+' ['+id+']');

            // If this is a single-file album, just display it along with the listing of other albums
            if(a.albumtype == FILE_TYPE_FILEALBUM) {
                CE.CEUI.showSharedFolders(isshare?'share':'album', 'celist');
                return true;
            }
            
            // If this is a virtual album containing one or more folder shares, merge them in like cousins
            if(a.files && a.files.length > 0) {
                a              = CE.CEU.shallowCopy(a);
                a.root         = CE.CEU.shallowCopy(a.files[0]);
                a.root.cousins = null;
                a.root.svc     = { deviceid:a.root.deviceid, serviceid:a.root.serviceid };
                for(var i = 1; i < a.files.length; ++i) {
                    if(!a.root.cousins)
                        a.root.cousins = [];
                    var cuz = CE.CEU.shallowCopy(a.files[i]);
                    cuz.svc = { deviceid:cuz.deviceid, serviceid:cuz.serviceid };
                    a.root.cousins.push(cuz);
                }
            }
            
            // Update global state
            that.g_cursvc   = null;
            that.g_curalbum = a;

            CE.CEUI.clearContent('celist');
            g_cursearch               = null;
            g_curscrit                = null;
            g_wasSearch               = false;
            g_searchResults['celist'] = null;

            // update our sidebar
            if(a.albumtype == FILE_TYPE_SLIDEALBUM) {
                updateSidebarState('cesearch_slidedirs');
            } else {
                updateSidebarState(isshare ? 'celibshares_share' : 'celibshares_album');
            }
            // Change view mode
            enterAlbumMode(a, isshare);

            // Reload our content state
            g_curpageoffset = 0;
            bcFlush();

            loadInitialAlbumFiles(a, !isshare, ismetashare);

            if(isshare && a.perms == '0') {
                that.addContent(false);
            }

            if(a.root) {
                if(a.root.sortable) {
                    CE.rCN('cetool_sort', 'cedisabled');
                } else {
                    CE.aCN('cetool_sort', 'cedisabled');
                } 

                if(a.root.readonly === undefined) {
                    CE.CEU.svc.asyncRPC("POST", "statFS", ["deviceid", a.root.deviceid, "serviceid", a.root.serviceid, "fileid", a.root.fileid ],
                                        onCheckReadOnlyFSSuccess, null, a);
                }
                if(a.root.readonly) {
                    CE.rCN("cereadonly", "hidden");
                }
            }
            return true;
        } else {
            CE.CEDBG.println('CEUI: activateAlbum: Cannot find album with id: '+id);
        }
        return false;
    }

    function enterServiceMode() {
      leaveAppMode();
      CE.rCN("cecontent_controls", "hidden");
    }

    function activateService(id, noreload) {
        // TODO: use apiurl to cut through direclty to device...
        var s = g_svcmap[id];
        if(s) {
            CE.CEDBG.println('CEUI: Activating service: '+s.name+' ['+id+']');
            CE.CEDBG.println('CEUI:   -- Service Type:  '+s.type);
            CE.CEDBG.println('CEUI:   -- Service Class: '+s.sclass);
            switch(s.sclass) {
            case 1:
            case '1':
                // This is a "folder-share" style UI
                CE.CEDBG.println('CEUI:   -- Using Folder-View UI: '+s.id);
                // TODO: Add online time updates...
                // TODO: use apiurl or uiurl to cut through direclty to device...
                // Update global state
                that.g_cursvc   = s;
                that.g_curalbum = null;
                // update our sidebar
                updateSidebarState(that.g_cursvc.id);

                if(s.online && s.online != '0') {
                    if(s.flags) {
                        var flags = parseInt(s.flags);
                        if(flags & FS_FLAGS_SORTABLE) {
                            CE.rCN('cetool_sort', 'cedisabled');
                        } else {
                            CE.aCN('cetool_sort', 'cedisabled');
                        }
                    } else {
                        CE.aCN('cetool_sort', 'cedisabled');
                    }
                }
                // Change view mode
                enterServiceMode();

                if(s.readonly) {
                    CE.rCN("cereadonly", "hidden");
                }
                
                // Reload our content state
                g_curpageoffset = 0;
                bcFlush();

                if(!noreload) reloadContent();
                return true;
            default:
                CE.CEDBG.println("Service Class not supprted for: " + s.name + "class: " + s.sclass);
                break;
            }
        } else {
            CE.CEDBG.println('CEUI: activateService: Cannot find service with id: '+id);
        }
        return false;
    }

    function activateSidebar(id, grd) {
        var el = CE.CEU.$(grd);
        if(el) {
            if(el.hasChildNodes()) {
                var c = el.childNodes;
                for(var i=c.length-1; i>=0; i--) {
                    var e   = c[i];
                    var eid = e.id;
                    if((!eid || eid != id) && e.hasChildNodes()) {
                        var as = e.getElementsByTagName('a');
                        if(as && as.length > 0)
                            eid = as[0].id;
                    }
                    if(eid && eid == id) {
                        CE.aCN(e, 'active');
                    } else {
                        CE.rCN(e, 'active');
                    }
                }
            }
        }
    }
    
    function updateMainDropState(id) {
        var mainSel = CE.CEU.$('cemaindrop');
        if(mainSel) {
            for(var i = 0; i < mainSel.options.length; ++i) {
                if(mainSel.options[i].value.indexOf(id) != -1) {
                    mainSel.selectedIndex = i;
                    break;
                }
            }
        }
    }
    
    function updateSidebarState(id) {
        if(id) { 
            if(id == 'cesearch_albums' || id == 'cesearch_artists' || id == 'cesearch_genres' || id == 'cesearch_audio')
                id = 'cesearch_fleximusic';
            if(id == 'cesearch_image')
                id = 'cesearch_imagetime';
            if(id == 'cesearch_movietime')
                id = 'cesearch_video';

            activateSidebar(id, 'cemylib_devices');
            activateSidebar(id, 'cemyapps_apps');
            activateSidebar(id, 'cemylib_shares');
            activateSidebar(id, 'cemymedia_ss');
            activateSidebar(id, 'ceactivecopyul');
            activateSidebar(id, 'cemylib_ss');
            activateSidebar(id, 'cemylib_ss_text');
            
            updateMainDropState(id);
        } else {
            activateSidebar(that.g_cursvc?that.g_cursvc.id:null, 'cemylib_devices');
            activateSidebar(null, 'cemyapps_apps');
            activateSidebar(null, 'cemylib_shares');
            activateSidebar(null, 'cemymedia_ss');
            activateSidebar(null, 'ceactivecopyul');
            activateSidebar(null, 'cemylib_ss');
            activateSidebar(null, 'cemylib_ss_text');
        }
    }

    function updateSvcXcodeMode(sid, deviceid, serviceid) {
        // Figure out if this service is setup to transcode videos
        CE.CEU.svc.asyncRPC('POST', 'featureCommand',
                            ['feature','xcode', 'command','getMode', 'deviceid',deviceid, 'serviceid',serviceid],
                            function(r){
                                var svc = g_svcmap[sid];
                                if(svc && r) {
                                    svc.xcodeStream = r.stream;
                                }
                            });
    }

    function populateSidebar(r, psd) {
        var grd = psd.grd;
        var el = CE.CEU.$(grd);
        
        if(r && r.devices && el) {
            g_svcmap = {};
            g_devmap = {};
            for(var i = 0; i < r.devices.length; i++) {
                g_devmap[r.devices[i].deviceid] = r.devices[i];
                var showdev = true;
                if(!r.devices[i].services || r.devices[i].services.length < 1) {
                } else {
                    var services = r.devices[i].services;
                    var allsvcoffline = true;
                    // Check that we received a valid list of services.  Bug 1074, hbconn was returning an error string.
                    // Should happen anymore but a little error handling just in case 
                    if (typeof services == 'string') {
                        continue;
                    }

                    for(var m = 0; m < services.length; ++m) {
                        if(g_ejectedsvc && g_ejectedsvc.deviceid == r.devices[i].deviceid && g_ejectedsvc.serviceid == services[m].serviceid) {
                            services[m].online = '0';
                        }
                        if(services[m].online && services[m].online == '1') {
                            allsvcoffline = false;
                        }
                    }
                    g_ejectedsvc = null;
                    
                    for(var j = 0; j < services.length; j++) {
                        // TODO: Should I leave the offline service in the service map?
                        showdev = false;
                        if(!services[j].name) {
                            services[j].name = CE.STRTAB.lookup("view.svc.unnamed");
                        }
                        var id;
                        if(services[j].id) {
                            id = services[j].id;
                        } else {
                            id = r.devices[i].deviceid + ':_:' + services[j].serviceid;
                            services[j].id = id;
                        }
                        // need this back reference for later. 
                        services[j].device = r.devices[i];
                        g_svcmap[id] = services[j];

                        if(allsvcoffline || (services[j].online && services[j].online == '1')) {
                            appendSidebarItem(el, {onEvent: onClickSidebar, 
                                                   id: id,
                                                   name: services[j].name,
                                                   type: 'service',
                                                   device: r.devices[i],
                                                   service: services[j],
                                                   owner: r.devices[i].owner,
                                                   disabled: !services[j].online || services[j].online == "0"
                                                  });
                        }
                        updateSvcXcodeMode(id, r.devices[i].deviceid, services[j].serviceid);
                    }
                }
                if(showdev) {
                    var id = r.devices[i].deviceid;

                    g_orphans[id] = r.devices[i];
                    
                    appendSidebarItem(el, {onEvent: onClickSidebar, 
                                           id: id,
                                           name: CE.STRTAB.lookup("view.device.nodrive"),
                                           type: 'device',
                                           device: r.devices[i],
                                           service: null,
                                           owner: r.devices[i].owner,
                                           disabled: true
                                          });
                }
            }
        } else if(r && r.albums) {
            if(grd=='cemyalb_albums') {
                g_albummap      = {};
                g_sharedfolders = {};
                g_sharedfiles   = {};
            } else {
                g_sharemap      = {};
            }
            for(var i=0; i<r.albums.length;i++) {
                var id = r.albums[i].albumid;
                var shareid = null;
                if(grd=="cemyalb_albums") {
                    if(r.albums[i].albumtype != FILE_TYPE_SLIDEALBUM) {
                        if(r.albums[i].files) {
                            g_albummap[id] = r.albums[i];
                            for(var ir = 0; ir < r.albums[i].files.length; ++ir) {
                                var ar  = r.albums[i].files[ir];
                                shareid = ar.deviceid + ':_:' + ar.serviceid + ':_:' + ar.fileid;
                                if(r.albums[i].albumtype == FILE_TYPE_FILEALBUM)
                                    g_sharedfiles[shareid] = r.albums[i];
                                else
                                    g_sharedfolders[shareid] = r.albums[i];
                            }
                        } else if(r.albums[i].root) {
                            g_albummap[id] = r.albums[i];
                            var ar  = r.albums[i].root;
                            shareid = ar.deviceid + ':_:' + ar.serviceid + ':_:' + ar.fileid;
                            if(r.albums[i].albumtype == FILE_TYPE_FILEALBUM)
                                g_sharedfiles[shareid] = r.albums[i];
                            else
                                g_sharedfolders[shareid] = r.albums[i];
                        }
                    } else {
                        g_albummap[id] = r.albums[i];
                    }
                } else {
                    g_sharemap[id] = r.albums[i];
                }
            }
        }
        
        // sidebar op counting...
        g_sbops--;
        CE.CEDBG.println('CEUI: populateSidebar: dec(SBOPS) - '+g_sbops);

        // these are things i want to do on every refresh, but only when all my 
        // sidebar ops are done.
        if(g_sbops==0) {
            populateSourceServiceList();
        }
        if(!g_inited) {
            initOpDone();
            if(g_jumpToShare && g_sbops==0) {
                var shareId = g_jumpToShare;
                g_jumpToShare = null;
                activateAlbum(shareId, true);
            }
        } else if(g_sbops==0) {
            CE.CEU.showLoadingAni(false);
            // Only do this if we were inited priorly...
            if(!psd.rl) {
                if(g_jumpToShare) {
                    var shareId = g_jumpToShare;
                    g_jumpToShare = null;
                    activateAlbum(shareId, true);
                } else {
                    var foundsomething = false;
                    
                    if(g_cursearch && g_cursearch != '') {
                        updateSidebarState(g_cursearch);
                        foundsomething = true;
                    } else if(that.g_cursvc!=null) {
                        var id = that.g_cursvc.deviceid + ':_:' + that.g_cursvc.serviceid;
                        CE.CEDBG.println('CEUI: populateSidebar: calling activateService('+id+')');
                        foundsomething = activateService(id);
                    } else if(that.g_curalbum!=null) {
                        CE.CEDBG.println('CEUI: populateSidebar: calling activateAlbum('+that.g_curalbum.albumid+')');
                        // the current album might be one the user owns or it might be shard with him. 
                        foundsomething = activateAlbum(that.g_curalbum.albumid, false);
                        if(!foundsomething) {
                            foundsomething = activateAlbum(that.g_curalbum.albumid, true);
                        }
                    } 
                    if(!foundsomething) {
                        // activate someting but not the oobe. 
                        activateSomething(false);
                        CE.aCN("ceemptymsg", "hidden");
                    }
                }
            } 
        }
        
        if(g_sbops == 0 && psd.cb) {
            psd.cb();
        }
    }

    function onClickAppSidebar(eh, evt, tel) {
      // Update global state
      that.g_cursvc   = null;
      that.g_curalbum = null;
      updateSidebarState(eh.id);

      // Reload our content state
      g_curpageoffset = 0;
      bcFlush();
      reloadContent();

      // Enter application mode
      enterAppMode();

      // Set the src URL of the app iframe
      var f = CE.CEU.$('ceappframe');
      if(f) {
        var url = 'about:blank';
        if(eh.detail && eh.detail.url) {
          url = eh.detail.url;
          if(url && url.indexOf('http')!=0) {
            if(url.indexOf('/')==0) {
              url = window.location.protocol+'//'+window.location.host+url;
            } else {
              url = window.location.protocol+'//'+window.location.host+'/'+url;
            }
          }
        }
        CE.CEDBG.println('CEUI: FEATURE -- Displaying application '+eh.id+' @ '+url);
        f.src = url;
        f = null;
      }

      return false;
    }

    function findFeature(featureid) {
        for(var k in that.g_allfeatures) {
            if(k == featureid) {
                return that.g_allfeatures[k];
            }
        }
        return null;
    }

    function buildSidebarFromAppList(apps) {
        var el = CE.CEU.$('cemyapps_apps');
        if(!el) return;

        for(var i in apps) {
            var f = null;
            if(apps[i].feature) {
                f = findFeature(apps[i].feature);
                if(!f) continue;
            }
            if(apps[i].uiurl) {
                // NOTE: We need to proxy the UI url in order to allow XSS to work correctly
                var url = apps[i].uiurl;
                if(url.indexOf('http')==0) {
                    var myurl = window.location.protocol+'//'+window.location.host;
                    if(url.indexOf(myurl)==0) {
                        // It is a local site reference, so let it be
                    } else {
                        // It is not a local site reference, so forward it
                        url = window.location.protocol+'//'+window.location.host+'/app/'+apps[i].id+'/';
                    }
                }
                var details = { name: apps[i].name, url: url };
                appendSidebarItem(el, { onEvent: onClickAppSidebar,
                                        id: 'app_id_'+ apps[i].id,
                                        name: CE.STRTAB.lookupdb(apps[i].name),
                                        type: 'app',
                                        detail: details
                                      });            
                CE.rCN("cemyapps", "hidden");
            }
        }
    }

    // so instead of showing an app for every feature just list the apps and show any that either 
    // don't have a feature requirement or have their feature requirement fulfilled by at least 
    // one device.
    function rebuildApplicationSidebar() {
        //Was: CE.CEU.svc.asyncRPC("POST", "listApps", [ "type", "a" ], onListAppsSuccess, onGenericFailure);
        var avail = {
            "apps": [],
            "publicapps": [{"id":"WRzTYUIqSpCohhOl2O9cFA",
                            "ownerid":"00000000000000000000000000000000",
                            "name":"%%SQI18N:apps.samba.settings",
                            "ispublic":"1",
                            "flags":"0",
                            "feature":"cifs",
                            "settingsurl":"cewfs.html"},
                           {"id":"uizWLk8q1ZwEdWn1S8vj",
                            "ownerid":"00000000000000000000000000000000",
                            "name":"%%SQI18N:apps.social.settings",
                            "ispublic":"1",
                            "flags":"0",
                            "feature":"twitter",
                            "settingsurl":"cesns.html"},
                           {"id":"yIEIvEjt4axmuuk53nRA",
                            "ownerid":"00000000000000000000000000000000",
                            "name":"%%SQI18N:apps.sync.settings",
                            "ispublic":"1",
                            "flags":"0",
                            "feature":"xcode",
                            "settingsurl":"cesync.html"},
                           {"id":"jd0hZpEM3gX6kc5lEJnm",
                            "ownerid":"00000000000000000000000000000000",
                            "name":"%%SQI18N:apps.video.settings",
                            "ispublic":"1",
                            "flags":"0",
                            "feature":"xcode",
                            "settingsurl":"cexcode.html"}],
            "ownerapps": []
        };

        if(avail.apps) {
            buildSidebarFromAppList(avail.apps);
        }
        if(avail.publicapps) {
            buildSidebarFromAppList(avail.publicapps);
        }
        if(avail.ownerapps) {
            buildSidebarFromAppList(avail.ownerapps);
        }
        
        if(hasFeature('foldersync')) {
            CE.rCN('ceactivecopysbg', 'hidden');
            CE.rCN('cetool_sync',     'hidden');
        } else {
            CE.aCN('ceactivecopysbg', 'hidden');
            CE.aCN('cetool_sync',     'hidden');
        }
    }
    
    function hasFeature(featureName, deviceId) {
        var f = that.g_allfeatures[featureName];
        if(!f || !f.length)
            return false;
        if(deviceId) {
            for(var i=0; i<f.length; ++i) {
                if(f[i].deviceid == deviceId)
                    return true;
                
            }
        } else {
            return (f.length > 0); // if no device specified, any device will do
        }
        return false;
    }

    function onListFeaturesSuccess(r, d) {
      if(r && r.features && r.features.length) {
        for(var f = 0; f < r.features.length; ++f) {
          if(that.g_allfeatures[r.features[f]] === undefined) {
            that.g_allfeatures[r.features[f]] = [];
          }
          that.g_allfeatures[r.features[f]].push(d.device);
        }
      }
      if(--g_pendfeatures <= 0) {
        rebuildApplicationSidebar();
      }
    }

    function onListFeaturesFailure(r, d) {
      if(--g_pendfeatures <= 0) {
        rebuildApplicationSidebar();
      }
    }

    function populateSidebarApps(r, psd) {
        if(r && r.devices) {
            // For each device, we need to list supported features and build an exhaustive array
            // in g_allfeatures
            for(var i = 0; i < r.devices.length; i++) {
                g_pendfeatures++;
                CE.CEU.svc.asyncRPC('POST', 'listFeatures', ['deviceid', r.devices[i].deviceid],
                                    onListFeaturesSuccess, onListFeaturesFailure, {device: r.devices[i]});
            }
        }
        return populateSidebar(r, psd);
    }

    function reloadSidebar(rl, onegrd, cb) {
        var grd;

        if(!onegrd || onegrd == 'cemylib_devices') {
            grd = 'cemylib_devices';
            var el = CE.CEU.$(grd);
            g_sbops = 0;
            if(el) {
                if(!g_inited) g_initops++;
                g_sbops++;
                CE.CEDBG.println('CEUI: reloadSidebar: inc(SBOPS, services) - '+g_sbops);
                
                CE.CEU.releaseAllEvents(el, 'click', true);
                g_pendfeatures = 0;
                that.g_allfeatures = {};
                CE.CEU.svc.asyncRPC('POST', 'listDevices', null, 
                                    populateSidebarApps, onGenericFailure, {'grd':grd, 'rl':rl, 'cb':cb});
            }
        }
        if(!onegrd || onegrd == 'cemyalb_albums') {
            // Doesn't actually update sidebar, but does refresh our cached list of albums
            grd = 'cemyalb_albums';
            if(!g_inited) g_initops++;
            g_sbops++;
            CE.CEDBG.println('CEUI: reloadSidebar: inc(SBOPS, myalbums) - '+g_sbops);
            CE.CEU.svc.asyncRPC('POST', 'listOwnerAlbums', null, 
                                populateSidebar, onGenericFailure, {'grd':grd, 'rl':rl, 'cb':cb});
        }
        if(!onegrd || onegrd == 'cemyshare_albums') {
            // Doesn't actually update sidebar, but does refresh our cached list of shares
            grd = 'cemyshare_albums';
            if(!g_inited) g_initops++;
            g_sbops++;
            CE.CEDBG.println('CEUI: reloadSidebar: inc(SBOPS, sharealbums) - '+g_sbops);
            CE.CEU.svc.asyncRPC('POST', 'listSharedAlbums', null, 
                                populateSidebar, onGenericFailure, {'grd':grd, 'rl':rl, 'cb':cb});
        }
        el = null;
        grd = null;
        if(g_sbops > 0) {
            CE.CEU.showLoadingAni(true);
        }
    }

    function showPrintQueue(svc) {
        CE.CEU.showLoadingAni(true);
        CE.CEUI.clearContent('celist');

        CE.aCN('cetools_normal', 'hidden');
        CE.rCN('cetools_print',  'hidden');
        
        var table = (CE.CEU.$('celist').appendChild(CE.dce('table',null,'celist_lv'))).appendChild(CE.dce('tbody'));
        var head  = table.appendChild(CE.dce('tr',null,'head'));

        var check = (head.appendChild(CE.dce('td',null,'head icon'))).appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'check-header.png'));;
        check.title = CE.STRTAB.lookup('view.check.all');
        CE.CEU.attachEvent(check, 'click', {onEvent:selectAllFiles});

        (head.appendChild(CE.dce('td',null,'head name'))).appendChild(CE.STRTAB.lookupel('print.queue.source'));
        (head.appendChild(CE.dce('td',null,'head date'))).appendChild(CE.STRTAB.lookupel('print.queue.date.created'));
        (head.appendChild(CE.dce('td',null,'head status'))).appendChild(CE.STRTAB.lookupel('print.queue.status'));
        
        CE.CEU.svc.asyncRPC('PRINT', 'getJobs', ['deviceid',svc.deviceid, 'serviceid',svc.serviceid],
        function(r) {
            if(r && r.jobs) {
                r.jobs.sort(function(l, r) {
                    if(l.status == '5' && r.status != '5')
                        return -1; // put currently printing jobs first
                    if(l.status == '3' && r.status != '3')
                        return -1; // put pending jobs second
                    // Put most recent first
                    var ldate = new Date(l.qtime * 1);
                    var rdate = new Date(r.qtime * 1);
                    if(ldate > rdate)
                        return -1;
                    if(ldate < rdate)
                        return 1;
                    return 0;
                });
                
                var even = false;
                for(var i = 0; i < r.jobs.length; ++i) {
                    (function(job) {
                        even = !even;
                        job.svc = svc;
                        var id = 'pq_' + job.jobid;
                        var tr = table.appendChild(CE.dce('tr',id,'item '+(even?'even':'odd')));
                        
                        // Icon / selection checkbox
                        var td = (((tr.appendChild(CE.dce('td',null,'icon'))).appendChild(CE.dce('div',null,'itemicon'))).appendChild(CE.dce('div',null,'itemicondiv')));
                        var check = td.appendChild(CE.dce('div',null,'selectcheck'));
                        check.title = CE.STRTAB.lookup('view.selectcheck');
                        CE.CEU.attachEvent(check, 'click', {onEvent:onSelectFile, 'co':{file:job}, 'gid':id, 'grd':'celist'});
                        td.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-print-dark.png'));
                        
                        (tr.appendChild(CE.dce('td'))).appendChild(CE.dctn(job.title));
                        
                        // Queued time
                        var date = new Date(job.qtime * 1);
                        (tr.appendChild(CE.dce('td'))).appendChild(CE.dctn(date.format('mmmm d, yyyy "at" h:MMTT')));
                        
                        // Status
                        (tr.appendChild(CE.dce('td'))).appendChild(CE.STRTAB.lookupel('print.queue.status.'+job.status));
                    })(r.jobs[i]);
                }
            }
            
            CE.CEU.showLoadingAni(false);
        },
        onGenericFailure);
    }

    function showEmptyContentDiv(id) {
        CE.rac(CE.CEU.$("cesrcpagination"));
        CE.rac(CE.CEU.$("cepagination"));
        CE.aCN("cebuttons_preview", "hidden");

        if(id == 'folder' && CE.CEUI.g_cursvc && g_curpath.length == 0)
            id = 'service-root';
        
        if(id == 'folder' || id == 'service-root') {
            if(!CE.hCN('cesrctophintto_cont', 'hidden') || !CE.hCN('cesrctophinttofrom_cont', 'hidden')) {
                id = ''; // 'copy to here' message is all we need
            }
        }
        
        var e = CE.CEU.$("ceempty");
        if(e) {
            if(!id) {
                CE.aCN(e, "hidden");
            } else {
                CE.CEU.releaseAllEvents("celist", "click", true);
                CE.rCN(e,  "hidden"); 
                for(var c = e.firstChild; c != null; c = c.nextSibling) {
                    if(c.id == id) {
                        CE.rCN(c, "hidden");
                    } else {
                        CE.aCN(c, "hidden");
                    }                        
                }
            }
        }
    }
    

    //-----------------------------------------------------------------
    // Invitation stuff
    function onInviteSuccess(r, data, method) {
        CE.CEU.showLoadingAni(false);
        var inviteInfo = {};
        reloadInvitationList(data.a, inviteInfo);
        if(data.rl) {
            reloadSidebar(true, 'cemyshare_albums');
        }

        var interval = setInterval(function(){
            if(inviteInfo.done) {
                clearInterval(interval);
                // Auto disable e-mail notifications if no peeps
                var repost = shbyid('ceshare-check-repost');
                if(inviteInfo.count == 0 && repost.checked) {
                    repost.checked = false;
                    CE.CEUI.enableShareRepost();
                } else if(inviteInfo.count == 1 && !repost.checked) {
                    CE.CEUI.enableShareRepost(true);
                }
            }
        }, 200);
    }

    function isEmailOfCurrentUser(email) {
        if(CE.CEU.user && CE.CEU.user.emails) {
            for(var i = 0; i <CE.CEU.user.emails.length; ++i) {
                // oh crap this is me!
                if(CE.CEU.user.emails[i].address == email) {
                    return true;
                }
            }
        }
        return false;
    }
    
    function removeAllInvitations(albumid) {
        // Remove all e-mail addresses this album is currently shared with
        CE.CEU.svc.asyncRPC('POST', 'listAlbumShares', ['albumid',albumid], function(r){
            if(r && r.shares) {
                for(var i = 0; i < r.shares.length; ++i) {
                    CE.CEU.svc.asyncRPC("POST", "removeAlbumShare", ['albumid',albumid, 'email',r.shares[i].user.email]);
                }
            }
        });
        
        CE.aCN(shbyid('ceshare_listsection'), 'hidden');
    }

    function removeInvitation(u, e, tel) {
        var a = null;
        var refresh = false;
        if(u.album) {
            a = u.album;
        } else {
            a = that.g_curalbum;
        }

        var args = [
            'albumid', a.albumid,
            'email',   u.user.email
        ];
        refresh = isEmailOfCurrentUser(u.user.email);
        CE.CEU.svc.asyncRPC("POST", "removeAlbumShare", args, onInviteSuccess, 
                            onGenericFailure, {'a': a, rl: refresh});
    }

    function updateInvitationAccess(eh) {
        var album = CE.CEUI.getAlbum(eh.albumid);
        
        if(album) {
            if(eh.lbid) {
                var lb = CE.CEU.$(eh.lbid);
                if(lb) {
                    CE.CEU.svc.asyncRPC("POST", "updateAlbumShare", ["email", eh.user.email, "albumid", album.albumid,
                                                                     "perms", (lb.selectedIndex==1) ? "upload" : "readonly"]);
                } 
            } else if(eh.cbid) {
                var cb = CE.CEU.$(eh.cbid);
                CE.CEU.svc.asyncRPC("POST", "updateAlbumShare", ["email", eh.user.email, "albumid", album.albumid,
                                                                 "perms", cb.checked ? "upload" : "readonly"]);
            }
        }
    }

    function populateInvitationList(r, outInfo) {
        var el = shbyid('ceshare_list');
        var nshare = 0;
        if(g_curshares) {
            g_curshares = null;
        }
        if(el) { 
            CE.rac(el);
            var tb = CE.dce('tbody');
            el.appendChild(tb);
            el = tb; tb = null;
            if(r && r.shares) {
                g_curshares = r.shares;
                var added   = {};

                for(var i=0; i<r.shares.length;i++) {
                    if(added[r.shares[i].user.email])
                        continue;
                    else
                        added[r.shares[i].user.email] = true;
                    
                    var tr, td, a, lb = null, cb = null;
                    nshare++;
                    tr = CE.dce('tr', r.shares[i].user.userid);
                    r.shares[i].onEvent = removeInvitation;
                    
                    
                    td = CE.dce('td', null, 'close_col');
                    a = CE.dca(r.shares[i]);
                    a.appendChild(CE.dci(CE.STRTAB.lookup("imgbase")+'list-close-x.png'));
                    a.title = CE.STRTAB.lookup('view.share.access.remove');
                    td.appendChild(a); a = null;
                    tr.appendChild(td); 

                    td = CE.dce('td');
                    var spn = CE.dce('span');
                    var shortMail = CE.trims(r.shares[i].user.email, 27);
                    if(shortMail != r.shares[i].user.email)
                        spn.title = r.shares[i].user.email;
                    spn.appendChild(CE.dctn(shortMail));
                    td.appendChild(spn);
                    tr.appendChild(td);
                    
                    td = CE.dce('td', null, 'acc_col');
                    lb = CE.dce("select", 'lb_' + r.shares[i].user.userid, 'acc_sel');
                    lb.options[0] = new Option(CE.STRTAB.lookup("view.share.access.read"),  "0");

                    var album = r.shares[i].album ? r.shares[i].album : that.g_curalbum;
                    if(!album || !album.albumtype || album.albumtype == '0' || album.albumtype == FILE_TYPE_DIRECTORY) {
                        var files = that.g_curalbum ? CE.CEUI.getCurShareFiles() : [];
                        if(files.length < 1 || !CE.CEUI.isDescendableMetaFile(files[0])) {
                            lb.options[1] = new Option(CE.STRTAB.lookup("view.share.access.write"), "1");
                        }
                    }
                    
                    CE.CEU.attachEvent(lb, 'change',  { onEvent: updateInvitationAccess, 
                                                        user:    r.shares[i].user,
                                                        albumid: r.shares[i].album.albumid,
                                                        lbid:    'lb_' + r.shares[i].user.userid });

                    td.appendChild(lb);

                    tr.appendChild(td); td = null;
                    el.appendChild(tr); tr = null;

                    if(lb) {
                        if(r.shares[i].perms == '0') {
                        lb.selectedIndex = 0;
                        } else {
                            lb.selectedIndex = 1;
                        }
                        lb = null;
                    } else if(cb) {
                        if(r.shares[i].perms == '0') {
                            cb.checked = false;
                        } else {
                            cb.checked = true;
                        }
                    }
                }
            }
        }
        if(nshare>0) {
            CE.aCN(shbyid('ceshare_listsectionhint'), 'hidden');
            CE.rCN(shbyid('ceshare_listsection'), 'hidden');
        } else {
            CE.rCN(shbyid('ceshare_listsectionhint'), 'hidden');
            if(shbyid('ceshare_check') && shbyid('ceshare_check').checked) {
                CE.aCN('ceshare_hint_noshare', 'hidden');
                CE.rCN('ceshare_hint_share', 'hidden');
            } else {
                CE.rCN('ceshare_hint_noshare', 'hidden');
                CE.aCN('ceshare_hint_share', 'hidden');
            }
            CE.aCN(shbyid('ceshare_listsection'), 'hidden');
        }
        if(outInfo) {
            outInfo.count = nshare;
            outInfo.done  = true;
        }
    }

    function reloadInvitationList(a, outInfo) {
        if(!a) a = that.g_curalbum;
        var el = shbyid('ceshare_email');
        if(el) {
            el.value = CE.STRTAB.lookup('view.enteraddrs');
            el.blur();
            CE.rCN(el, 'email_focus');
        }
        el = shbyid('ceshare_list');
        if(el) {
            CE.CEU.releaseAllEvents(el, 'click', true);
        }
        CE.CEU.svc.asyncRPC('POST', 'listAlbumShares', ['albumid', a.albumid],
                            populateInvitationList, onGenericFailure, outInfo);
    }

    //-----------------------------------------------------------------
    // Breadcrumb stuff
    function bcClick(bco, evt, tel) {
        CE.CEDBG.println('CEUI: bcClick: bco: '+CE.CEDBG.serialize(bco));
        var go = null;

        // if the breadcrumb was on another page then lets 'remember' that page
        if(bco.pageoffset) {
            go = {};
            g_curpageoffset = go.pageoffset = bco.pageoffset;
        }
        if(bco.id) {
            // This was a real object that should be in our stack
            if(bcPopTo(bco.id)) {
                if(bco.file.svc) {
                    if(!go) go = {};
                    go.svc      = bco.file.svc;
                    go.viewmode = g_viewmode;
                    go.actions  = true;
                    if(g_curscrit)
                        go.scrit = g_curscrit;
                }
                reloadContent(bco.id, go, bco.file);
            }
        } else {
            if(CE.CEUI.g_curalbum || CE.CEUI.g_cursvc) {
                // This was the root node.
                // time to flush full breadcrumb
                bcFlush();
                g_curpageoffset = g_topleveloffset;
                reloadContent(null, go);
            } else if(g_cursearch && g_cursearch.indexOf('celibshares_') != -1 && (!g_curpath || g_curpath.length == 0)) {
                CE.CEUI.showSharedFolders(g_cursearch.substr(12), 'celist');
            } else {
                CE.CEUI.reloadAll();
            }
        }
        return false;
    }

    function bcFlush() {
        var li, a;
        var bc = CE.CEU.$('cebc');
        CE.CEU.releaseAllEvents(bc, 'click', true);

        // Clear our javascript path element array
        g_curpath = [];

        g_curpageoffset = 0;

        // Create left edge
        li = CE.dce('li','cebc_l', 'cebc_keep');
        li.appendChild(CE.dcib(null,'cebc_keep'));
        bc.appendChild(li);

        // Create root node
        li = CE.dce('li', 'cebc_root', 'cebc_keep');
        a = CE.dca({onEvent:bcClick});
        if(g_cursearch) {
            CE.CEUI.g_curalbum = null;
            CE.CEUI.g_cursvc   = null;
            a.appendChild(CE.dctn(CE.CEUI.getScritName(g_cursearch)));
        } else if(CE.CEUI.g_curalbum) {
            a.appendChild(CE.dctn(CE.CEUI.getDispFn(CE.CEUI.g_curalbum.name)));
        } else if(CE.CEUI.g_cursvc) {
            a.appendChild(CE.dctn(CE.CEUI.getDispFn(CE.CEUI.g_cursvc.name)));
        } else {
            a.appendChild(CE.STRTAB.lookupel("view.toplevel"));
        }
        li.appendChild(a);
        bc.appendChild(li);

        // Create right edge
        li = CE.dce('li','cebc_r', 'cebc_keep');
        li.appendChild(CE.dcib(null,'cebc_keep'));
        bc.appendChild(li);

        bcCheckShare();

        CE.aCN("cebc", "hidden");
    }

    function bcAppend(co) {
        var li, a;
        var bc = CE.CEU.$('cebc');
        var bcr = CE.CEU.$('cebc_r');

        // Create a bcobject
        var bco = {
            onEvent:    bcClick, 
            id:         co.id, 
            name:       co.name, 
            pageoffset: 0,
            file:       co.file
        };

        // Store the current page offset in our parent bco
        if(g_curpath.length > 0) {
            var pbco = g_curpath[g_curpath.length-1];
            if(pbco) {
                pbco.pageoffset = g_curpageoffset;
            }
        } else {
            g_topleveloffset = g_curpageoffset;
        }

        // Push the object on our stack of objects
        g_curpath.push(bco);

        // Create new node
        var name = CE.CEUI.getDispFn(bco.file);
        li = CE.dce('li', 'cebc_'+bco.id, 'child');
        a = CE.dca(bco);
        a.appendChild(CE.dctn(CE.trims(name, 15)));

        li.appendChild(a);        
        var s = findFolderShare(bco.file);
        if(s && (!co.file.album || co.file.album.albumtype != FILE_TYPE_SLIDEALBUM || CE.CEUI.g_options['slideshare_'+co.file.album.albumid])) {
            li.appendChild(CE.dci(CE.STRTAB.lookup("imgbase")+"share-icon.png", bco.id+'cebcshare', 'ceshare-icon-breadcrumb'), li.firstChild);
        }
        // Stick it in the right place on the breadcrumb
        bc.insertBefore(li,bcr);
        
        CE.rCN("cebc", "hidden");

        // If we have more breadcrumbs than we have space, show an ellipsis for the first few
        bcShowFittingCrumbsOnly();
    }
    
    function bcShowFittingCrumbsOnly()
    {
        var bcUl   = CE.CEU.$('cebc');
        var bcRoot = CE.CEU.$('cebc_root');
        var lis    = bcUl.childNodes;
        var elip   = CE.CEU.$('cebc_elip');
        var pagin  = CE.CEU.$('cepagination');
        
        if(!elip) {
            elip = CE.dce('li', 'cebc_elip', 'child');
            elip.appendChild(CE.dctn("..."));
            bcUl.insertBefore(elip, bcRoot.nextSibling);
        }
        
        elip.style.display = 'block';
        var maxWidth   = bcUl.offsetWidth;
        var totalWidth = CE.CEU.$('cebc_l').offsetWidth + bcRoot.offsetWidth + elip.offsetWidth + CE.CEU.$('cebc_r').offsetWidth;
        elip.style.display = 'none';
        
        if(pagin)
            totalWidth += pagin.offsetWidth;
        totalWidth += CE.CEU.$('cebuttons_right').offsetWidth;
        
        // Display the li's that fit
        var i;
        for(i=lis.length-2; i>1; i--) {
            if(lis[i].id=="cebc_elip")
                break;
            // to measure the width of this li, it must be displayed
            lis[i].style.display = 'block';
            if(maxWidth <= (totalWidth+lis[i].offsetWidth)) {
                // Show the ellipsis
                elip.style.display = 'block';
                break;
            }
            totalWidth += lis[i].offsetWidth;
        }
        // Hide the li's that don't fit
        for(; i>1; i--) {
            if(lis[i].id=="cebc_elip")
                break;
            lis[i].style.display = 'none';
        }
    }    

    function bcCheckShare() {
        if(g_curpath.length > 0) {
            var bco = g_curpath[g_curpath.length-1];
            var share = findFolderShare(bco.file);

            var li = CE.CEU.$("cebc_" + bco.id);
            if(share) {
                var img = CE.CEU.$(bco.id + 'cebcshare');
                if(!img) {
                   li.appendChild(CE.dci(CE.STRTAB.lookup("imgbase")+"share-icon.png", bco.id + 'cebcshare', 'ceshare-icon-breadcrumb'), li.firstChild);
                }
            } else {
                var img = CE.CEU.$(bco.id + 'cebcshare');
                if(img) {
                    if(img.parentNode) {
                        img.parentNode.removeChild(img);
                    }
                }
            }
        } else {
            var li = CE.CEU.$("cebc_root");
            if(li) {
                var f = null;
            
                if(that.g_curalbum) {
                    f = that.g_curalbum.root;
                } else if(that.g_cursvc) {
                    f = { fileid: '0' };
                }
                sh = findFolderShare(f);
                if(sh) {
                    var img = CE.CEU.$("cebc_root_share");
                    if(!img) {
                        li.appendChild(CE.dci(CE.STRTAB.lookup("imgbase")+"share-icon.png", "cebc_root_share", "ceshare-icon-breadcrumb"), li.firstChild);
                    }
                } else {
                    var img = CE.CEU.$("cebc_root_share");
                    if(img) {
                        if(img.parentNode) {
                            img.parentNode.removeChild(img);
                        }
                    }
                }
            }
        }
    }
    function bcPopTo(id) {
        var r = false;;

        g_curpageoffset = 0;

       //CE.CEDBG.println('CEUI: bcPopTo: BEFORE g_curpath: '+CE.CEDBG.serialize(g_curpath));
        for(var i=g_curpath.length-1;i>=0;i--) {
            if(g_curpath[i].id == id) {

                if(g_curpath[i].pageoffset) {
                    g_curpageoffset = g_curpath[i].pageoffset;
                }

                // Remove all items after this one in the stack...
                g_curpath.splice(i+1, g_curpath.length-i);
                // Remove all items after this one in the DOM...c
                var bc = CE.CEU.$('cebc');
                var found = false;
                CE.CEU.releaseAllEvents(bc, 'click', true, 
                                        function(x){
                                            if(!found) {
                                                if(x.id == 'cebc_'+id) {
                                                    found = true;
                                                } else if(CE.hCN(x, 'cebc_keep')) {
                                                } else {
                                                    return true;
                                                }
                                            }
                                            return false;
                                        });
                bc = null;
                //CE.CEDBG.println('CEUI: bcPopTo: AFTER g_curpath: '+CE.CEDBG.serialize(g_curpath));
                r = true;
                break;
            }
        }

        // If we have more breadcrumbs than we have space, show an ellipsis for the first few
        bcShowFittingCrumbsOnly();
        
        return r;
    }


    //-----------------------------------------------------------------
    // Breadcrumb stuff
    function bcSrcClick(bco, evt, tel) {
        CE.CEDBG.println('CEUI: bcClick: bco: '+CE.CEDBG.serialize(bco));
        if(bco.id) {
            // This was a real object that should be in our stack
            if(bcSrcPopTo(bco.id)) {
                var go = bco.go;
                if(bco.file.svc) {
                    if(!go) go = {};
                    go.svc = bco.file.svc;
                }
                reloadContent(bco.id, go, bco.file);
            }
        } else {
            that.srcSvcSelected();
        }
        return false;
    }

    function bcSrcFlush() {
        var li, a;
        var bc = CE.CEU.$('srcbc');
        CE.CEU.releaseAllEvents(bc, 'click', true);

        // Clear our javascript path element array
        g_srcpath = [];

        // Create left edge
        li = CE.dce('li','srcbc_l', 'cebc_keep');
        li.appendChild(CE.dcib(null,'cebc_keep'));
        bc.appendChild(li);

        // Create root node
        li = CE.dce('li', 'srcbc_root', 'cebc_keep');
        a = CE.dca({onEvent:bcSrcClick});

        if(g_cursrcsearch) {
            a.appendChild(CE.dctn(CE.CEUI.getScritName(g_cursrcsearch)));
        } else if(g_srcsvc) {
            a.appendChild(CE.dctn(CE.CEUI.getDispFn(g_srcsvc.name)));
        } else {
            a.appendChild(CE.STRTAB.lookupel("view.toplevel"));
        }
        li.appendChild(a);
        bc.appendChild(li);

        // Create right edge
        li = CE.dce('li','srcbc_r', 'cebc_keep');
        li.appendChild(CE.dcib(null,'cebc_keep'));
        bc.appendChild(li);
    }

    function bcSrcAppend(co) {
        var li, a;
        var bc = CE.CEU.$('srcbc');
        var bcr = CE.CEU.$('srcbc_r');

        // Create a bcobject
        var bco = {
            onEvent:    bcSrcClick, 
            id:         co.id, 
            name:       co.name, 
            // Store the page offset of our prarent
            pageoffset: 0,
            file:       co.file,
            go:         co.go
        };

        // Store the current page offset in our parent bco
        if(g_srcpath.length > 0) {
            var pbco = g_srcpath[g_srcpath.length-1];
            if(pbco) {
                pbco.pageoffset = g_curpageoffset;
            }
        }

        // Push the object on our stack of objects
        g_srcpath.push(bco);

        // Create new node
        li = CE.dce('li', 'srcbc_'+bco.id, 'child');
        a = CE.dca(bco);
        a.appendChild(CE.dctn(CE.trims(CE.CEUI.getDispFn(bco.file), 15)));
        li.appendChild(a);
        
        // Stick it in the right place on the breadcrumb
        bc.insertBefore(li,bcr);
    }

    function bcSrcPopTo(id) {
        var r = false;;

        //CE.CEDBG.println('CEUI: bcPopTo: BEFORE g_curpath: '+CE.CEDBG.serialize(g_curpath));
        for(var i=g_srcpath.length-1;i>=0;i--) {
            if(g_srcpath[i].id == id) {
                // Remove all items after this one in the stack...
                g_srcpath.splice(i+1, g_srcpath.length-i);
                // Remove all items after this one in the DOM...
                var bc = CE.CEU.$('srcbc');
                var found = false;
                CE.CEU.releaseAllEvents(bc, 'click', true, 
                                        function(x){
                                            if(!found) {
                                                if(x.id == 'srcbc_'+id) {
                                                    found = true;
                                                } else if(CE.hCN(x, 'cebc_keep')) {
                                                } else {
                                                    return true;
                                                }
                                            }
                                            return false;
                                        });
                bc = null;
                //CE.CEDBG.println('CEUI: bcPopTo: AFTER g_curpath: '+CE.CEDBG.serialize(g_curpath));
                r = true;
                break;
            }
        }
        return r;
    }

    //-----------------------------------------------------------------
    // Content Pane stuff
    function appendActionIcon(co, n, name, dt, title, iconName) {
        var e, i;
        if(!dt) dt = '';
        e = {
            onEvent: function(eh,evt,tel) {
                if(evt.type == 'mouseout' && eh.co && eh.co.onEvent)
                    eh.co.onEvent(eh.co, evt, tel);
                else if(eh.co && eh.co.onAction)
                    eh.co.onAction(eh.co, eh.action, evt, tel); 
                return false;
            }, 
            co: co, 
            action: name
        }
        if(!iconName) iconName = name;
        i = CE.dci(CE.STRTAB.lookup("imgbase")+'icon-'+iconName+dt+'.png');
        if(title) i.title = title
        CE.CEU.attachEvent(i, 'click', e);
        CE.CEU.attachEvent(i, 'mouseout', e);
        n.appendChild(i); i = null;

        //var a = CE.dca(e, null, name+dt);
        //a.appendChild(i); i = null;
        //n.appendChild(a); a = null;
    }

    function contentIconImageLoad(eh) {
        if(eh && eh.ic && eh.img) {
            //eh.ic.style.background = 'transparent url('+eh.img.src+') no-repeat center center';
            document.body.removeChild(eh.img);
            var span = CE.dce("span", null, "image-center-span-1");
            eh.ic.appendChild(span);

            span = CE.dce("span", null, 'image-center-span-2');
            eh.ic.appendChild(span);
            span.appendChild(eh.img);
            CE.CEU.releaseAllEvents(eh.img, 'error');
            eh.co = eh.ic = eh.img = null;
        }
    }

    function contentIconImageError(eh) {
        if(eh && eh.ic && eh.img) {
            eh.img.src = CE.STRTAB.lookup("imgbase")+"broken-image.png"
            eh.co.broken = true;
            CE.CEU.releaseAllEvents(eh.img, 'error');
            eh.co = eh.ic = eh.img = null;
        }
    }
    
    function getGid(grd, coId, file) {
        var gid = (grd == 'cesrc_list') ? 'cesrccnt_' : 'cecnt_';
        if(coId == '-1' && file.album && file.album.albumid)
            gid += file.album.albumid;
        else
            gid += coId;
        if(file.deviceid && file.serviceid)
            gid += '_' + file.deviceid + '_' + file.serviceid;
        return gid;
    }

    function appendContentIcon(gi, co, doevt, go) {
        var ic, url, im, sp;
        if(gi.tagName=='TD') {
            ic = CE.dce('div', null, "itemicon");
        } else {
            ic = CE.dce('div', null, getFullClassName('itemicon', go));
        }
        var thumb = CE.CEUI.getFileThumbnail(co.file);
        if(go.viewmode==1) {
            var icd = CE.dce('div', null, 'itemicondiv');
            ic.appendChild(icd);

            if(CE.CEU.isLoggedIn()) {
                var gid     = getGid('celist', co.id, co.file);
                var check   = icd.appendChild(CE.dce('div',null,'selectcheck'));
                check.title = CE.STRTAB.lookup('view.selectcheck');
                CE.CEU.attachEvent(check, 'click', {onEvent:onSelectFile, 'co':co, 'gid':gid, 'grd':'celist'});
            }
            
            if(thumb.share)
                icd.appendChild(CE.dci(CE.STRTAB.lookup("imgbase")+"share-icon.png", null, 'shareicon-list'));
            
            if(thumb.folder) {
                icd.appendChild(CE.dci(thumb.url, null, 'folderpage3-list'));
                icd.appendChild(CE.dci(thumb.url, null, 'folderpage2-list'));
                icd.appendChild(CE.dci(thumb.url, null, 'folderpage1-list'));
            } else {
                im = CE.dce('img');
                im.border = "0";
                im.alt = CE.CEUI.getDispFn(co.file);
                im.src = thumb.url;
                icd.appendChild(im);
            }
        } else {
            if(thumb.share)
                ic.appendChild(CE.dci(CE.STRTAB.lookup("imgbase")+"share-icon.png", null, getFullClassName('shareicon',go)));
            
            if(thumb.folder) {
                ic.appendChild(CE.dci(thumb.url, null, getFullClassName('folderpage3',go)));
                ic.appendChild(CE.dci(thumb.url, null, getFullClassName('folderpage2',go)));
                im = CE.dci(thumb.url, null, getFullClassName('folderpage1',go));
            } else {
                im = CE.dci(thumb.url, null, '');
            }
            sp = CE.dce("span", null, "image-center-span-1");
            ic.appendChild(sp);

            sp = CE.dce("span", null, 'image-center-span-2');
            ic.appendChild(sp);
            sp.appendChild(im);
            CE.CEU.attachEvent(im, 'error', { 'img': im, 'co': co, 'ic': ic, onEvent: contentIconImageError });
        }
        if(doevt) {
            if(co.onEvent) {
                if(im) {
                    var co2 = CE.CEU.shallowCopy(co);
                    delete co2.__EID;

                    CE.CEU.attachEvent(im, 'mousedown', co2);
                    CE.CEU.attachEvent(im, 'mouseup', co2);                    
                    CE.CEU.attachEvent(im, 'mouseover', co2);
                    CE.CEU.attachEvent(im, 'mouseout', co2);
                }
                if(sp) {
                    var co2 = CE.CEU.shallowCopy(co);
                    delete co2.__EID;

                    CE.CEU.attachEvent(sp, 'mousedown', co2);
                    CE.CEU.attachEvent(sp, 'mouseup', co2);
                    CE.CEU.attachEvent(sp, 'mouseover', co2);
                    CE.CEU.attachEvent(sp, 'mouseout', co2);
                }
                CE.CEU.attachEvent(ic, 'mousedown', co);
                CE.CEU.attachEvent(ic, 'mouseup', co);
                CE.CEU.attachEvent(ic, 'mouseover', co);
                CE.CEU.attachEvent(ic, 'mouseout', co);
            }
        }
        gi.appendChild(ic); ic = null;
    }

    function canDoPreview(f) {
        if(CE.CEUI.isVideoFile(f) || CE.CEUI.isAudioFile(f)) {
            return true;
        } else if(f.preview && !CE.CEUI.isDescendableFile(f)) {
            return true;
        } else if(CE.CEUI.isImageFile(f)) {
            return true;
        } else if(CE.CEUI.isDocFile(f)) {
            return true;
        }
        return false;
    }

    function getNameForMime(mime) {
        var n = MIME_NAME_MAP[mime];
        if(n) return CE.STRTAB.lookup(n[0]);
    }
    
    function getIconForMime(mime) {
        var n = MIME_NAME_MAP[mime];
        if(n) return n[1];
    }

    function isFilePrintable(f) {
        if(f.mimetype) {
            var n = MIME_NAME_MAP[f.mimetype];
            if(n) return n[2];
        }
        return false;
    }

    function startTranscode(eh) {
        if(eh.co && eh.co.file) {
            var args = [];
            var svc  = CE.CEUI.getCurSvc(eh.co.file);

            if(svc) {
                args.push("deviceid");
                args.push(svc.deviceid);
                args.push("serviceid");
                args.push(svc.serviceid);
            } else {
                return;
            }
            args.push("cmd");
            args.push("transcode");
            args.push("fileid");
            args.push(eh.co.file.fileid);
            args.push("force");
            args.push("1");
            CE.CEU.svc.asyncRPC("POST", "sendServiceCommand", args);

            // set some state so we don't forget what we just did.
            var date = new Date();
            eh.co.file.xcstamp = "" + date.getTime();
            
            var div = CE.CEU.$("transcode_" + eh.co.id);
            if(div) {
                CE.CEU.releaseAllEvents(div, "click", true);
                if(div.parentNode) {
                    div.parentNode.removeChild(div);
                }
            }
        }
    }

    that.showRSSDialog = function() {
        var folder = getCurrentFolder();
        var share = findFolderShare(folder);
        if(share && share.feedid) {
            var url = "http://"
            url += window.location.host;
            url += "/feeds/";
            url += share.feedid;

            var d = CE.dce('div');
            // A link to actually follow for full URL
            var d2 = CE.dce('div');
            var a = document.createElement('a');
            a.href = url;
            a.target = '_blank';
            var i = CE.dci(CE.STRTAB.lookup("imgbase")+'rss-icon.png');
            a.appendChild(i);
            i = null;

            var svc = CE.CEUI.getCurSvc();
            if(svc) {
                if(folder.fileid=='0') {
                    a.appendChild(CE.dctn(CE.STRTAB.lookup('view.drive.subscribe')+CE.CEUI.getDispFn(svc.name)));
                } else {
                    a.appendChild(CE.dctn(CE.STRTAB.lookup('view.drive.subscribe')+CE.CEUI.getDispFn(folder)));
                }
            }
            a.style.textDecoration = 'none';
            d2.appendChild(a);
            a=null;
            d2.style.padding = '2px';
            d.appendChild(d2);
            d2=null;
            
            // A link to display the full URL
            var d2 = CE.dce('div');
            var a = CE.dca({onEvent:function(eh){CE.tCN('cerss-display','hidden')}});
            a.appendChild(CE.STRTAB.lookup('view.showurl'));
            a.style.fontSize='9px';
            d2.appendChild(a);
            a=null;
            d2.style.padding = '2px';
            d.appendChild(d2);
            d2=null;
            
            // The full URL
            var drss = CE.dce('textarea', 'cerss-display', 'hidden');
            drss.readOnly = true;
            drss.style.fontSize = "11px";
            drss.style.fontFamily = 'monospace'
            drss.style.border = '1px solid';
            drss.style.padding = '2px';
            drss.style.width = '380px';
            drss.appendChild(CE.dctn(url));
            d.appendChild(drss);
            drss = null;
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup("view.mediarss"), d
                                        //                                     ,[{name:'ok',label:CE.STRTAB.lookup('button.ok')}]
                                       );
            d = null;
            url = null;
            dlg.show();
            dlg = null;
            svc = null;
        }
        return false;
    }
    
    function getAlbum(file) {
        var svc = CE.CEUI.getCurSvc(file);
        for(var id in g_sharemap) {
            if(g_sharemap[id].deviceid == svc.deviceid && g_sharemap[id].serviceid == svc.serviceid) {
                if(g_sharemap[id].root && g_sharemap[id].root.fileid == file.fileid) {
                    return g_sharemap[id];
                } else if(g_sharemap[id].files) {
                    for(var i = 0; i < g_sharemap[id].files.length; ++i) {
                        if(g_sharemap[id].files[i].fileid == file.fileid)
                            return g_sharemap[id];
                    }
                }
            }
        }
        return null;
    }

    function canDoEditTypeActions(file) {
        if(file) {
            var svc = CE.CEUI.getCurSvc(file);
            if(isOwnedSvc(svc))
                return true;
            var alb = getAlbum(file);
            if(alb)
                return (CE.CEU.isLoggedIn() && alb.perms != '0');
        }
        
        var share = getCurShare();
        if(share && CE.CEU.user && share.ownerid == CE.CEU.user.userid) {
            return CE.CEU.isLoggedIn();
        } else if(share) {
            return (CE.CEU.isLoggedIn() && share.perms != '0');
        } else {
            return (that.g_cursvc != null);
        }
    }
    
    function getTrashAction(actions) {
        for(var i = 0; i < actions.length; ++i) {
            if(actions[i].substr(0,5) == 'trash')
                return actions[i];
        }
        return null;
    }
    function hasTrashAction(actions) {
        return (getTrashAction(actions) != null);
    }
    function getTrashActionForFiles(fileStruct) {
        // Return the *common* trash action, if any; fileStruct is a structure (not array) of files
        var lastAct = null;
        for(var f in fileStruct) {
            var action = getTrashAction(getActionTypesForFile(fileStruct[f],CE.CEUI.g_isSlideAlb));
            if(action == null) {
                return null;
            } else if(lastAct == null) {
                lastAct = action;
            } else if(lastAct != action) {
                return null;
            }
        }
        return lastAct;
    }

    function getActionTypesForFile(file, isSlideFile) {
        var actions = [];
        var didzoom = false;
        
        var isShareListing = CE.CEUI.isSharedFoldersListing();
        if(!isShareListing) {
            if(g_printer && isFilePrintable(file)) {
                actions.push('print');
            } else if((!isSlideFile && canDoPreview(file)) || CE.CEUI.isDescendableFile(file)) {
                actions.push(CE.CEUI.isDescendableFile(file)?'zoomfolder':'zoom');
                didzoom = true;
            }
            if(!CE.CEUI.isDescendableFile(file) || file.type == FILE_TYPE_DIRECTORY) {
                actions.push('download');
            }
            if(canDoEditTypeActions(file) && (file.type == FILE_TYPE_DIRECTORY || !CE.CEUI.isDescendableFile(file))) {
                actions.push('rename');
                actions.push(isSlideFile?'trashslide':'trash');
            }
            if(that.g_curalbum && CE.CEU.user && !isSlideFile) {
                if(that.g_curalbum.ownerid == CE.CEU.user.userid) {
                    actions.push('share');
                }
            } else if(file.svc && file.svc.device && file.svc.device.ownerid && CE.CEU.user && !isSlideFile) {
                if(file.svc.device.ownerid == CE.CEU.user.userid) {
                    actions.push('share');
                }
            } else if(that.g_cursvc && !isSlideFile) {
                actions.push('share');
            }                
            if(g_cursearch && g_cursearch != 'celibshares_share' &&
               (!CE.CEUI.isDescendableFile(file) || file.type == FILE_TYPE_DIRECTORY) && isOwnedFile(file) && !isSlideFile) {
                actions.push('folder');
            }
        } else {
            if(g_printer && isFilePrintable(file)) {
                actions.push('print');
            } else if((!isSlideFile && canDoPreview(file)) || CE.CEUI.isDescendableFile(file)) {
                actions.push(CE.CEUI.isDescendableFile(file)?'zoomfolder':'zoom');
                didzoom = true;
            }
            if(file.svc && (!file.svc.albumtype || file.svc.albumtype == '0' ||
               file.svc.albumtype == FILE_TYPE_DIRECTORY || file.svc.albumtype == FILE_TYPE_FILEALBUM)) {
                actions.push('download');
            }
            if(CE.CEUI.isSharedWithMeListing())
                actions.push('trashshare');
            else if(CE.CEUI.isSlideAlbListing())
                actions.push('trashslidealb');
            else
                actions.push('trashmyshare');
            if(CE.CEUI.isSlideAlbListing()) {
                if(file.album && (file.album.ownerid == CE.CEU.user.userid)) {
                    actions.push('rename');
                    actions.push('share');
                }
            } else if(g_cursearch == 'celibshares_album' && !isSlideFile) {
                actions.push('share');
            }
        }

        // put zoom in last if we didn't do it yet so "zoomable" class gets set correctly below
        if(!didzoom && ((!isSlideFile && canDoPreview(file)) || CE.CEUI.isDescendableFile(file))) {
            actions.push(CE.CEUI.isDescendableFile(file)?'zoomfolder':'zoom');
        }
        return actions;
    }

    function appendActionIcons(n, co, clr, actions) {
        CE.CEU.attachEvent(n, 'mouseout', co);
        
        for(var i = 0; i < actions.length; ++i) {
            if(actions[i] == 'rename' || actions[i] == 'folder')
                continue; // handled elsewhere
            appendActionIcon(co, n, actions[i], clr, CE.STRTAB.lookup('view.hint.'+actions[i]), actions[i]);
        }
    }
    
    function isOwnedSvc(svc) {
        if(!svc || !svc.deviceid || !svc.serviceid)
            return false;
        
        var realSvc = g_svcmap[svc.deviceid + ':_:' + svc.serviceid];
        return (realSvc != null);
    }
    
    function isOwnedFile(file) {
        return isOwnedSvc(CE.CEUI.getCurSvc(file));
    }

    function appendContentRow(tbody, idx, co, go) {
        var gid = getGid('celist', co.id, co.file);

        var tr, td;
        var cn = 'odd';
        if(idx % 2 == 0) {
            cn = 'even';
        }
        tr = CE.dce('tr', gid, 'item '+cn);

        // Icon
        td = CE.dce('td', null, 'icon');
        appendContentIcon(td, co, false, go);
        if(co.onEvent) {
            CE.CEU.attachEvent(td, 'mousedown', co);
            CE.CEU.attachEvent(td, 'mouseup', co);
        }
        tr.appendChild(td);
        
        var nameDiv, nameSpan, renLink;
        var actions, hasRename;
        if(go.actions) {
            actions   = getActionTypesForFile(co.file, co.slidedir);
            hasRename = (actions.indexOf('rename') != -1);
        }

        if(isMusicListing()) {
            var properties = (co.file.properties && (typeof co.file.properties != 'string')) ? co.file.properties : {};
            properties.title  = properties.title   ? properties.title   : co.name;
            properties.album  = properties.album   ? properties.album   : '';
            properties.artist = properties.artist  ? properties.artist  : '';
            properties.genre  = properties.genre   ? properties.genre   : '';
            
            // Title
            td = CE.dce('td', null, 'name');
            nameDiv  = td.appendChild(CE.dce('div',null,hasRename?'name':null));
            nameSpan = nameDiv.appendChild(CE.dce('span',null,hasRename?'name':null));
            nameSpan.appendChild(CE.dctn(CE.trims(properties.title, 50)));
            if(hasRename) {
                renLink = nameSpan.appendChild(CE.dce('div',null,'renlink'));
                renLink.title = CE.STRTAB.lookup('view.hint.rename');
            }
            td.title = properties.title;
            if(co.onEvent) {
                CE.CEU.attachEvent(td, 'mousedown', co);
                CE.CEU.attachEvent(td, 'mouseup', co);
            }
            tr.appendChild(td);

            // Artist
            td = CE.dce('td', null, 'artist');
            td.appendChild(CE.dctn(CE.trims(properties.artist, 25)));
            td.title = properties.artist;
            if(co.onEvent) {
                CE.CEU.attachEvent(td, 'mousedown', co);
                CE.CEU.attachEvent(td, 'mouseup', co);
            }
            tr.appendChild(td);

            // Album
            td = CE.dce('td', null, 'album');
            td.appendChild(CE.dctn(CE.trims(properties.album, 25)));
            td.title = properties.album;
            if(co.onEvent) {
                CE.CEU.attachEvent(td, 'mousedown', co);
                CE.CEU.attachEvent(td, 'mouseup', co);
            }
            tr.appendChild(td);

            // Genre
            td = CE.dce('td', null, 'genre');
            td.appendChild(CE.dctn(CE.trims(properties.genre, 10)));
            td.title = properties.genre;
            if(co.onEvent) {
                CE.CEU.attachEvent(td, 'mousedown', co);
                CE.CEU.attachEvent(td, 'mouseup', co);
            }
            tr.appendChild(td);
        } else {
            // Name
            var name = CE.CEUI.getDispFn(co.file);
            td = CE.dce('td', null, 'name');
            nameDiv  = td.appendChild(CE.dce('div',null,hasRename?'name':null));
            nameSpan = nameDiv.appendChild(CE.dce('span',null,hasRename?'name':null));
            nameSpan.appendChild(CE.dctn(CE.trims(name, 50)));
            if(hasRename) {
                renLink = nameSpan.appendChild(CE.dce('div',null,'renlink'));
                renLink.title = CE.STRTAB.lookup('view.hint.rename');
            }
            td.title = name;
    
            if(co.onEvent) {
                CE.CEU.attachEvent(td, 'mousedown', co);
                CE.CEU.attachEvent(td, 'mouseup', co);
            }
            tr.appendChild(td);

            // Size
            td = CE.dce('td', null, 'size');
            if(co.file) {
                if(CE.CEUI.isDescendableFile(co.file)) {
                    td.appendChild(CE.STRTAB.lookupel("view.dirsize"));
                } else {
                    td.appendChild(CE.dctn(co.file.size));
                }
            }
            if(co.onEvent) {
                CE.CEU.attachEvent(td, 'mousedown', co);
                CE.CEU.attachEvent(td, 'mouseup', co);
            }
            tr.appendChild(td);
    
            // Date
            td = CE.dce('td', null, 'date');
            if(co.file && co.file.mtime) {
                var date = new Date();
                date.setTime(co.file.mtime);
                td.appendChild(CE.dctn(CE.CEU.getCalendarDate(date)));
            }
            if(co.onEvent) {
                CE.CEU.attachEvent(td, 'mousedown', co);
                CE.CEU.attachEvent(td, 'mouseup', co);
            }
            tr.appendChild(td);
    
            // Type
            td = CE.dce('td', null, 'type');
            if(co.file) {
                if(CE.CEUI.isDescendableFile(co.file)) {
                    td.appendChild(CE.STRTAB.lookupel("view.directory."+co.file.type));
                } else {
                    // if its too long we want the ellipsis in the beginning..
                    if(co.file.mimetype) {
                        var m = co.file.mimetype;
    
                        var f = getNameForMime(m);
                        if(f) {
                            td.appendChild(CE.dctn(f));
                        } else {
                            td.appendChild(CE.dctn(CE.trims(m, 20, true)));
                        }
                    }
                }
            }
            if(co.onEvent) {
                CE.CEU.attachEvent(td, 'mousedown', co);
                CE.CEU.attachEvent(td, 'mouseup', co);
            }
            tr.appendChild(td);
        }

        // Actions
        if(go.actions) {
            if(actions.indexOf('zoom') != -1 || actions.indexOf('zoomfolder') != -1) {
                CE.aCN(tr, 'zoomable');
            }
            var renEvent;
            if(hasRename) {
                var renEvtId = (co.file.deviceid?co.file.deviceid:'')+(co.file.serviceid?co.file.serviceid:'')+co.file.fileid;
                g_renameEvts[renEvtId] = {onEvent:CE.CEUI.onRenameStart, 'file':co.file, 'el':nameDiv};
                renEvent = g_renameEvts[renEvtId];
                CE.CEU.attachEvent(renLink, 'click', renEvent);
            }
            td = CE.dce('td', null, 'actions');
            var n = CE.dce('div');
            n.className = 'itemactions';
            appendActionIcons(n, co, '-dark', actions);
            if(actions.indexOf('folder') != -1)
                appendActionIcon(co, n, 'folder', '-dark', CE.STRTAB.lookup("view.hint.folder"));
                
            td.appendChild(n); n = null;
            tr.appendChild(td);
        }
        tbody.appendChild(tr);

        if(renEvent && CE.CEUI.g_newfolder == co.file.name) {
            renEvent.onEvent(renEvent); // trigger rename of new folder
            CE.CEUI.g_newfolder = null;
        }
    }

    function getFullClassName(cname, go) {

        if(go && go.viewsize && go.viewsize == 'small') {
            return cname + '-sm';
        } if(go && go.viewsize && go.viewsize == 'medium') {
            return cname + '-md';
        } else if(go && go.grd == 'cesrc_list') {
            return cname + '-md';
        } else if(go && go.slidedir) {
            return cname;
        } else if(g_viewsize == 'medium') {
            return cname + '-md';
        } else if(g_viewsize == 'small') {
            return cname + '-sm';
        }
        return cname;
    }

    function appendContentItem(grd, co, go, dispNum) {
        var gi, n;
        gi = CE.dce('div');
        gi.className = getFullClassName('item', go);
        var isSrc = (go && go.grd == 'cesrc_list');
        var grdid = isSrc ? 'cesrc_list' : 'celist';
        var gid   = getGid(grdid, co.id, co.file);
        gi.id = gid;

        // Selection checkbox
        if(CE.CEU.isLoggedIn() && !co.slidedir) {
            var check   = gi.appendChild(CE.dce('div',null,'selectcheck'));
            check.title = CE.STRTAB.lookup('view.selectcheck');
            CE.CEU.attachEvent(check, 'click', {onEvent:onSelectFile, 'co':co, 'gid':gid, 'grd':grdid});
        }

        // Icon
        appendContentIcon(gi, co, true, go);
        
        // Name
        var dispName = CE.CEUI.getDispFn(co.file);
        if(isMusicListing() && co.file.properties && co.file.properties.title)
            dispName = co.file.properties.title;
        if(dispNum)
            dispName = dispNum + '. ' + dispName;
        n = CE.dce('div');
        n.className = getFullClassName('itemname', go);
        var spn = n.appendChild(CE.dce('span'));
        spn.appendChild(CE.dctn(CE.CEU.trimstring(dispName, 25)));
        n.title = dispName;

        gi.appendChild(n);

        // Actions
        var renEvent;
        if(go.actions) {
            var actions = getActionTypesForFile(co.file, co.slidedir);
            if(actions.indexOf('zoom') != -1 || actions.indexOf('zoomfolder') != -1) {
                CE.aCN(gi, 'zoomable');
            }
            if(actions.indexOf('rename') != -1) {
                CE.aCN(n, 'renameable');
                var renEvtId = (co.file.deviceid?co.file.deviceid:'')+(co.file.serviceid?co.file.serviceid:'')+co.file.fileid;
                g_renameEvts[renEvtId] = {onEvent:CE.CEUI.onRenameStart, 'file':co.file, 'el':n};
                renEvent = g_renameEvts[renEvtId];
                CE.CEU.attachEvent(spn, 'click',   renEvent);
                CE.CEU.attachEvent(spn, 'mouseup', {onEvent:function(c,e,t){if((e.which&&e.which!=1)||(e.button&&e.button!=1))onEventContent(co,e,t);}});
            }
            
            n = CE.dce('div');
            n.className = getFullClassName('itemactions', go);
            appendActionIcons(n, co, null, actions);
            gi.appendChild(n); n = null;
            
            if(actions.indexOf('folder') != -1) {
                // Search results include a "display parent folder" link
                n = CE.dce('div', null, getFullClassName('itemsearchactions',go));
                CE.CEU.attachEvent(n, 'mouseout', co);
                appendActionIcon(co, n, 'folder', null, CE.STRTAB.lookup("view.hint.folder"));
                gi.appendChild(n);
            }

            if(canDoEditTypeActions()) {
                if(CE.CEU.user && CE.CEU.user.flags && CE.CEU.user.flags.indexOf("transcode") != -1) {
                    if(TRANSCODE_ENABLED && TRANSCODABLE_MIME_TYPES[co.file.mimetype] && !co.file.stream) {
                        var tm = NaN;

                        if(co.file.xcstamp) {
                            tm = parseInt(co.file.xcstamp);
                        }
                        if(isNaN(tm)) {
                            var d = CE.dce("div", "transcode_" + co.id, getFullClassName("item-transcode"))
                            var i = CE.dci(CE.STRTAB.lookup("imgbase")+"transcode-icon.png");
                            i.title = CE.STRTAB.lookup("view.hint.transcode");
                            d.appendChild(i);
                            CE.CEU.attachEvent(d, "click", {onEvent: startTranscode, co: co });
                            gi.appendChild(d);
                        }
                    }
                }
            }
        }
        grd.appendChild(gi);

        if(renEvent && CE.CEUI.g_newfolder == co.file.name) {
            renEvent.onEvent(renEvent); // trigger rename of new folder
            CE.CEUI.g_newfolder = null;
        }

        return gi;
    }
    
    function onSelectFile(evt, e, t) {
        var checks = CE.getByClass(CE.CEU.$(evt.gid), 'div', 'selectcheck');
        if(checks.length > 0) {
            var check = checks[0];
            if(CE.hCN(check,'selected')) {
                CE.rCN(check, 'selected');
                delete g_selfiles[evt.grd][evt.gid];
            } else {
                CE.aCN(check, 'selected');
                g_selfiles[evt.grd][evt.gid] = evt.co.file;
            }
        }
    }
    
    this.onRenameStart = function(evt, e, t, newName) {
        if(showReadOnlyError(CE.STRTAB.lookup('view.rename.title'), CE.STRTAB.lookupel("view.onreadonlydrive")))
            return;

        var edit = evt.el.appendChild(CE.dce('input','cerenamefield','rename'));
        edit.type  = 'text';
        edit.value = newName ? newName : evt.file.name;
        edit.select();
        
        var eventInfo = {onEvent:onRenameApply, 'file':evt.file, 'el':edit, 'renevt':evt};
        CE.CEU.attachEvent(edit, 'blur',    eventInfo);
        CE.CEU.attachEvent(edit, 'keydown', {onEvent:function(p,e,t) {
            if(e.keyCode == 27) { //esc
                CE.CEU.releaseAllEvents(edit, 'blur',    false);
                CE.CEU.releaseAllEvents(edit, 'keydown', false);
                evt.el.removeChild(edit);
            } else if(e.keyCode == 13) { //enter
                eventInfo.onEvent(eventInfo);
            }
        }});
    };
    
    function onRenameApply(evt) {
        if(evt.el && evt.el.parentNode) {
            var div     = evt.el.parentNode;
            var newName = CE.CEU.stripBadFnChars(evt.el.value);
            CE.CEU.releaseAllEvents(evt.el, 'blur',    false);
            CE.CEU.releaseAllEvents(evt.el, 'keydown', false);
            div.removeChild(evt.el);
            if(newName == evt.file.name || newName == '')
                return;
                
            CE.CEU.showLoadingAni(true);
            
            var doIt = function() {
                var svc  = CE.CEUI.getCurSvc(evt.file);
                var args = ['deviceid',  svc ? svc.deviceid  : '',
                            'serviceid', svc ? svc.serviceid : '',
                            'filename',  newName,
                            'name',      newName];
                if(evt.file.fileid && evt.file.fileid != '-1')
                    args.push('fileid', evt.file.fileid);
                if(evt.file.album && evt.file.album.albumtype == FILE_TYPE_SLIDEALBUM)
                    args.push('albumid', evt.file.album.albumid);
                else if(CE.CEUI.g_curalbum)
                    args.push('albumid', CE.CEUI.g_curalbum.albumid);
    
                var method = (CE.CEUI.g_curalbum && CE.CEUI.g_curalbum.albumtype == FILE_TYPE_SLIDEALBUM)
                           ? 'renameAlbumFile' : ((evt.file.album && evt.file.album.albumtype == FILE_TYPE_SLIDEALBUM)
                                               ? 'renameAlbum' : 'moveFile');
                CE.CEU.svc.asyncRPC('POST', method, args,
                function(r) {
                    // Success
                    CE.CEU.showLoadingAni(false);
                    if(evt.file.album && evt.file.album.albumtype == FILE_TYPE_SLIDEALBUM && r.album)
                        g_albummap[r.album.albumid] = r.album;
                    if(CE.hCN('cetool_sizep','selected'))
                        CE.CEUI.g_lastRename = newName;
                    if(evt.file.albumid)
                        CE.CEUI.reloadAll();
                    else
                        CE.CEUI.reloadContentCWD();
                },
                onRenameFail, {'file':evt.file, 'newname':newName, 'renevt':evt.renevt});
            };

            var sh = findShare(evt.file);
            if(sh && (!evt.file.album || evt.file.album.albumtype != FILE_TYPE_SLIDEALBUM)) {
                // This was a shared folder so first remove the share association
                var shroot = sh.root ? sh.root : sh;
                shareDisableNotify(shroot.serviceid, shroot.deviceid, sh.albumid, function(){
                    CE.CEU.svc.asyncRPC("POST", "deleteAlbum", ["albumid", sh.albumid], function(){
                        doIt();
                        reloadSidebar(true, 'cemyalb_albums');
                    });
                });
            } else {
                doIt();
            }
        }
    }
    
    function onRenameFail(r, d) {
        CE.CEU.showLoadingAni(false);
        
        // Pop a message explaining why the rename failed
        var svc  = CE.CEUI.getCurSvc(d.file);
        var args = ['deviceid',  svc ? svc.deviceid  : '',
                    'serviceid', svc ? svc.serviceid : '',
                    'filename',  d.newname,
                    'parentid',  d.file.parentid ? d.file.parentid : '0'];
        CE.CEU.svc.asyncRPC('POST', 'getFile', args,
        function() {
            CE.CEUI.showHint('ren1', CE.STRTAB.lookupel('view.error.exists'), true);
        },
        function() {
            CE.CEUI.showHint('ren2', CE.dctn(r['HB-EXCEPTION'].message), true);
        });
        
        // Start the rename again
        if(!d.renevt.lastAttempt || d.renevt.lastAttempt != d.newname) {
            d.renevt.lastAttempt = d.newname;
            d.renevt.onEvent(d.renevt, null, null, d.newname);
        }
    }

    // Do the usual populate content business, but lets turn on the bits
    // that need it as well, as reloadSidebar is no longer the only 
    // possible entry point.
    function populateContentCheckInit(r, go) {
        if(!g_inited) {
            g_initops--;
            if(g_initops <= 0) {
                initOpDone();
            }
        }
        if(r.album) {
            that.g_curalbum = r.album;
            if(go) go.album = r.album;
        }
        return populateContent(r, go);
    }

    function doPrevLink(eh) {
        if(g_curpageoffset > 0) {
            g_curpageoffset--;
            reloadContent();        
        }
    }

    function doPageLink(eh) {
        g_curpageoffset = eh.po;
        reloadContent();
    }

    function doNextLink(eh) {
        g_curpageoffset++;
        reloadContent();        
    }

    function doSrcPrevLink(eh) {
        var pid  = null;
        var file = null;
        if(g_srcpageoffset > 0)
            g_srcpageoffset--;
        if(g_srcpath.length > 0) {
            pid  = g_srcpath[g_srcpath.length-1].id;
            file = g_srcpath[g_srcpath.length-1].file;
        } else if(g_srcsvc && g_srcsvc.fileid && !g_lastScrit['cesrc_list']) {
            pid = g_srcsvc.fileid;
        }
        reloadContent(pid, {viewmode:2, nopreview: true, pid:'', 'svc':g_srcsvc, 'scrit':g_lastScrit['cesrc_list'],
                            grd:'cesrc_list', pageoffset: g_srcpageoffset}, file);
    }

    function doSrcPageLink(eh) {
        var pid  = null;
        var file = null;
        g_srcpageoffset = eh.po;
        if(g_srcpath.length > 0) {
            pid  = g_srcpath[g_srcpath.length-1].id;
            file = g_srcpath[g_srcpath.length-1].file;
        } else if(g_srcsvc && g_srcsvc.fileid && !g_lastScrit['cesrc_list']) {
            pid = g_srcsvc.fileid;
        }
        reloadContent(pid, {viewmode:2, nopreview: true, pid:'', 'svc':g_srcsvc, 'scrit':g_lastScrit['cesrc_list'],
                            grd:'cesrc_list', pageoffset: g_srcpageoffset}, file);
    }

    function doSrcNextLink(eh) {
        var pid  = null;
        var file = null;
        g_srcpageoffset ++;
        if(g_srcpath.length > 0) {
            pid  = g_srcpath[g_srcpath.length-1].id;
            file = g_srcpath[g_srcpath.length-1].file;
        } else if(g_srcsvc && g_srcsvc.fileid && !g_lastScrit['cesrc_list']) {
            pid = g_srcsvc.fileid;
        }
        reloadContent(pid, {viewmode:2, nopreview: true, pid:'', 'svc':g_srcsvc, 'scrit':g_lastScrit['cesrc_list'],
                            grd:'cesrc_list', pageoffset: g_srcpageoffset}, file);
    }

    function figureOutPagination(r, go) {
        var numPerPage = NPERPAGE;
        var pagdiv = null;
        var po = parseInt(r.pageoffset);
        var i = 0, s = 0, e = 0, tc = NaN, f = NaN, flen = 0;
        var a = null;

        if(go.grd=='cesrc_list') {
            pagdiv = CE.CEU.$("cesrcpagination");
        } else {
            pagdiv = CE.CEU.$("cepagination");
            if(CE.CEUI.g_isSlideAlb) numPerPage = NPERPAGE_SLIDEALB;
        }
        CE.rac(pagdiv);

        // if we don't know what page we are even on, then NM DUDE!
        if(isNaN(po)) {
            return;
        }
        // if we got no files back then NM DUDE!
        if(r.files) {
            flen = r.files.length;
        }
        // if we got less files back than we can fit on a page then we are all good too.
        if(po == 0) {
            if(r.origcount) {
                if(r.origcount < numPerPage)
                    return;
            } else {
                if(flen < numPerPage)
                    return;
            }
        }
        if(r.totalcount) {
            tc = parseInt(r.totalcount);
        }
        s = po - 10;
        var sExtra = 10;
        if(s < 0) {
            s = 0;
            sExtra = po;
        }
        e = po + 10;
        var eExtra = 10;
        if(!isNaN(tc)) {
            if(po == 0 && tc == numPerPage)
                return; // exactly one page
            f = Math.ceil(tc / numPerPage);
            if(e > f) {
                e = f;
                eExtra = e - po;
            }
        }
        while((e-s) > 10) {
            if(sExtra > eExtra) {
                --sExtra;
                ++s;
            } else {
                --eExtra;
                --e;
            }
        }
        if(po > 0) {
            if(go.grd=='cesrc_list') {
                a = CE.dca({ onEvent: doSrcPrevLink }, null, 'cepage-link-pn');
            } else {
                a = CE.dca({ onEvent: doPrevLink }, null, 'cepage-link-pn');
            }
            a.appendChild(CE.STRTAB.lookupel("view.prev"));
            pagdiv.appendChild(a);
        }
        for(i = s; i < e; i++) {
            if(i == po) {
                a = CE.dce("span", null, 'cepage-link');
            } else {
                if(go.grd=='cesrc_list') {
                    a = CE.dca({ onEvent: doSrcPageLink, 'po': i}, null, 'cepage-link');
                } else {                
                    a = CE.dca({ onEvent: doPageLink, 'po': i }, null, 'cepage-link');
                }
            }
            a.appendChild(CE.dctn("" + (i+1)));
            pagdiv.appendChild(a);
        }

        if(po < (e-1)) {
            if(go.grd=='cesrc_list') {
                a = CE.dca({ onEvent: doSrcNextLink }, null, 'cepage-link-pn');
            } else {
                a = CE.dca({ onEvent: doNextLink }, null, 'cepage-link-pn');
            }
            a.appendChild(CE.STRTAB.lookupel("view.next"));
            pagdiv.appendChild(a);
        }
    }

    function updateSortCriteria(sortcrit, skipsel, skipprefs, skiprl) {
        g_sortcrit                = sortcrit;
        g_searchResults['celist'] = null;

        if(!skipsel) {
            var sel  = CE.CEU.$("cesortselect");
            var opts = CE.getByClass(sel, 'div', 'ceopt');
            for(var i = 0; i < opts.length; ++i) {
                var item = opts[i];
                if(i == 0) {
                    CE.CEU.checkOpt(item);
                } else {
                    var spn = item.getElementsByTagName('span');
                    var val = (spn.length > 0 && spn[0].innerHTML) ? spn[0].innerHTML : '';
                    if(val.indexOf(sortcrit) != -1)
                        CE.CEU.checkOpt(item);
                }
            }
        }
        if(g_cursearch)
            CE.aCN('cesortselect_size', 'hidden');
        else
            CE.rCN('cesortselect_size', 'hidden');

        // we don't save the sorting in 'search' mode
        if(!g_cursearch && !skipprefs) {
            updateUserViewPrefs();
        }
        if(!skiprl) {
            reloadContent();
        }
    }
    
    this.isNothingAvailable = function() {
        if((CE.CEU.$('nothing') && !CE.hCN('nothing', 'hidden')) ||
           (!CE.hCN('ceempty', 'hidden') && (!CE.hCN('albumdriveerror', 'hidden') ||
                                             !CE.hCN('driveerror', 'hidden') ||
                                             !CE.hCN('drives', 'hidden'))))
            return true;
    }
    
    this.toggleFlexiOptions = function(suffix) {
        if(!suffix) suffix = '';
        
        if(CE.hCN('ceflexiopts'+suffix, 'hidden')) {
            CE.rCN('ceflexiopts'+suffix, 'hidden');
        } else {
            CE.aCN('ceflexiopts'+suffix, 'hidden');
        }
    };

    this.toggleSortOptions = function() {
        if(CE.hCN('cesortselect', 'hidden')) {
            CE.CEU.$("cetoolbar").style.zIndex = 11;
            CE.rCN('cesortselect', 'hidden');
            CE.CEU.$('cetool_sort').blur();
        } else {
            CE.CEU.$("cetoolbar").style.zIndex = 1;
            CE.aCN('cesortselect', 'hidden');
        }
    };

    this.onSortOrderChange = function(sortOrder) {
        if(CE.CEUI.isNothingAvailable())
            return;
        updateSortCriteria(sortOrder, false);
        CE.aCN('cesortselect', 'hidden');
    }

    function onSortHeaderClick(e) {
        switch(e.sortcrit) {
        case 'name':
            if(g_sortcrit == "+name") {
                updateSortCriteria("-name");
            } else {
                updateSortCriteria("+name");
            }
            break;
        case 'size':
            if(g_sortcrit == "+size") {
                updateSortCriteria("-size");
            } else { 
                updateSortCriteria("+size");
            }
            break;
        case 'date':
            if(g_sortcrit == "+mtime") {
                updateSortCriteria("-mtime");
            } else { 
                updateSortCriteria("+mtime");
            }
            break;
        case 'type':
            if(g_sortcrit == "+mimetype") {
                updateSortCriteria("-mimetype");
            } else { 
                updateSortCriteria("+mimetype");
            }
            break;
        }
    }

    function addSortHeader(td, field, val, upval, dnval) {
        if(CE.hCN('cetool_sort', 'cedisabled')) {
            td.appendChild(CE.dctn(field));
        } else {
            if(val) {
                var a = CE.dca({onEvent:onSortHeaderClick, sortcrit: val});
                a.appendChild(CE.dctn(field));
                td.appendChild(a);
                if(g_sortcrit == upval) {
                    td.appendChild(CE.dci(CE.STRTAB.lookup("imgbase")+"up.png", null, "cesortarrow"));
                } else if(g_sortcrit == dnval) {
                    td.appendChild(CE.dci(CE.STRTAB.lookup("imgbase")+"dn.png", null, "cesortarrow"));
                }
            } else {
                td.appendChild(CE.dctn(field));
            }
        }
    }
    
    this.populateContentWithSlideshow = function(el, files, go, fullwin, startPaused, trueImgOnly, startIndex, isSlideAlb, hideControls) {
        // Build the list of files to include in the slideshow
        var data    = {};
        var selItem = 0;
        for(var i=0; i<files.length; ++i) {
            var nf = files[i];
            
            var urlPrefix   = CE.CEUI.getDataStreamPrefix(nf, null);
            var thumb       = CE.CEUI.getFileThumbnail(nf, "?fixed");
            var thumbUrl    = thumb.url;
            var thumbShare  = thumb.share  ? CE.STRTAB.lookup("imgbase")+"share-icon.png" : null;
            var imageUrl;
            var fullResImageUrl = null;

            if(CE.CEUI.isImageFile(nf)) {
                fullResImageUrl = urlPrefix + nf.fileid + "/" + encodeURIComponent(nf.name);
                if(nf.preview)
                    imageUrl = urlPrefix + nf.preview + "/" + encodeURIComponent(nf.name);
                else
                    imageUrl = fullResImageUrl;
                if(fullResImageUrl == imageUrl)
                    fullResImageUrl = null;
            } else if(!nf.thumbnail) {
                if(CE.CEUI.isDescendableFile(nf)) {
                    imageUrl = thumbUrl+"&"+Math.floor(Math.random()*1000)+"&f="+encodeURIComponent(nf.name);
                } else if(nf.mimetype && MIME_NAME_MAP[nf.mimetype]) {
                    imageUrl = CE.STRTAB.lookup("imgbase")+MIME_NAME_MAP[nf.mimetype][1]+"?"+Math.floor(Math.random()*1000)+"&f="+encodeURIComponent(nf.name);
                } else {
                    imageUrl = CE.STRTAB.lookup("imgbase")+"page.png?"+Math.floor(Math.random()*1000)+"&f="+encodeURIComponent(nf.name);
                }
            } else {
                imageUrl = urlPrefix + nf.thumbnail + "/" + encodeURIComponent(nf.name);
            }
            
            if(that.g_lastRename && that.g_lastRename == nf.name)
                selItem = i;
            
            var extraIcon = null;
            var dispFunc  = null;
            if(CE.CEUI.isVideoFile(nf)) {
                dispFunc = showMediaInSlideshow;
                imageUrl = CE.STRTAB.lookup("imgbase") + "blank.gif?" + Math.floor(Math.random()*1000) + "&f=" + encodeURIComponent(nf.name);
            } else if(CE.CEUI.isAudioFile(nf)) {
                dispFunc = showMediaInSlideshow;
            }

            var co = 
                {onAction:  onActionContent,
                 onDisplay: dispFunc,
                 id:        nf.fileid,
                 go:        go,
                 name:      nf.name,
                 file:      nf
                }
            
            var dirFunc = null;
            if(CE.CEUI.isDescendableFile(nf))
                dirFunc = function(cof){onEventContent(cof, {'type':'click'});};

            // No full res for now
            fullResImageUrl = null;
            
            var caption = CE.CEUI.getDispFn(nf);
            if(isMusicListing() && nf.properties && nf.properties.title)
                caption = nf.properties.title;

            var actions     = getActionTypesForFile(nf, isSlideAlb);
            var hasRename   = (actions.indexOf('rename') != -1);
            var hasEdit     = hasTrashAction(actions);
            var hasDownload = (actions.indexOf('download') != -1);
            var hasShare    = (actions.indexOf('share') != -1);
            
            if(nf.cachepad) {
                if(imageUrl) {
                    imageUrl += (imageUrl.indexOf('?') == -1) ? '?' : '&';
                    imageUrl += nf.cachepad;
                }
                if(thumbUrl) {
                    thumbUrl += (thumbUrl.indexOf('?') == -1) ? '?' : '&';
                    thumbUrl += nf.cachepad;
                }
            }

            data[imageUrl] = { fullres:fullResImageUrl, 'caption':caption, thumbnail:thumbUrl, thumbnailFolder:thumb.folder, thumbnailShare:thumbShare, extraaction:co, hasshare:hasShare, hasdownload:hasDownload, hasedit:hasEdit, hasrename:hasRename, dirfunc:dirFunc, extraicon:extraIcon, trueimg:CE.CEUI.isImageFile(nf) };
        }
        
        // Display the slideshow
        var width      = el.offsetWidth;
        var height     = el.offsetHeight - 110;
        var thumbPos   = null;

        var showDiv = CE.dce('div', "show_div", '');
        
        if(!fullwin) {
            el.appendChild(showDiv);
        } else {
            var cemain = CE.CEU.$('cemain');
            cemain.parentNode.insertBefore(showDiv, cemain);
            width  = window.innerWidth ? window.innerWidth : ((document.documentElement&&document.documentElement.clientWidth)?document.documentElement.clientWidth:document.body.clientWidth);
            height = window.innerHeight ? window.innerHeight : ((document.documentElement&&document.documentElement.clientHeight)?document.documentElement.clientHeight:document.body.clientHeight);
            
            showDiv.style.position   = 'absolute';
            showDiv.style.top        = '0px';
            showDiv.style.left       = '0px';
            showDiv.style.width      = width + 'px';
            showDiv.style.height     = height + 'px';
            showDiv.style.background = '#000000';
            
            var thumbTop  = 0;
            var thumbLeft = 0;
            var elem = el;
            while(elem != null) {
                thumbTop  += elem.offsetTop;
                thumbLeft += elem.offsetLeft;
                elem = elem.offsetParent;
            }
            thumbTop += el.offsetHeight - 110;
            thumbPos  = {'top':thumbTop+'px', 'left':thumbLeft+'px', 'width':el.offsetWidth+'px', 'height':115+'px'};
            
            CE.aCN('cemain', 'cefullwinslid');
        }
        
        if(startIndex)
            selItem = startIndex;

        g_slideshowObj = new Slideshow('show_div', data, {loader:null, slide:selItem, controller:true, extracontrol:true, 'width':width, 'height':height, thumbnails:true, thumbsize:50, overlap:false, resize:'length', thumbclass:'thumbnails-extra', canedit:true, canshare:true, thumbpos:thumbPos, thumbfade:fullwin, paused:startPaused?true:false, trueimgonly:trueImgOnly, hidecontrols:hideControls});
        
        that.g_lastRename = null;
    };
    
    this.showFullScreenPreview = function() {
        var files    = g_curpage['celist'];
        var curIndex = 0;
        var paused   = true;
        if(g_slideshowObj) {
            curIndex = g_slideshowObj.getCurSlide();
            paused   = g_slideshowObj.isPaused();
        }
        
        CE.aCN('cetools_normal',         'hidden');
        CE.aCN('cebuttons_preview',      'hidden');
        CE.rCN('cetools_fullscreenprev', 'hidden');

        CE.CEUI.clearContent('celist');
        var celist   = CE.CEU.$('celist');
        var slideDiv = CE.dce('div');
        slideDiv.style.width = '100%';
        slideDiv.style.height = (CE.CEU.$('cecontent').offsetHeight - 17) + 'px';
        slideDiv.style.paddingTop = '5px';
        celist.appendChild(slideDiv);
        CE.CEUI.populateContentWithSlideshow(slideDiv, files, null, true, paused, true, curIndex);
    };
    
    function hasHtml5Audio() {
        if(CE.CEUI.g_options['disablehtml5audio'] == '1')
            return false;
        var a = document.createElement('audio');
        if(a && a.canPlayType) {
            var can = a.canPlayType('audio/mp3');
            if(can == 'probably' || can == 'maybe')
                return true;
            can = a.canPlayType('audio/mpeg');
            if(can == 'probably' || can == 'maybe')
                return true;
        }
        return false;
    }
    
    function AudioTrayPlayer() {
        var that         = this;
        var m_trayPlayer = null;
        var m_butPause   = null;
        var m_butMute    = null;
        var m_posNib     = null;
        var m_volNib     = null;
        var m_playlist   = [];
        var m_index      = 0;
        
        var m_dragCtrl    = null;
        var m_mouseStartX = 0;
        var m_posStartX   = 0;
        
        function onDragStart(d, evt)
        {
            if(d.ctrl == m_posNib && (m_trayPlayer.duration == 0 || m_trayPlayer.duration == NaN))
                return; // invalid duration, so don't let them set position

            m_dragCtrl = d.ctrl;
            
            if((!evt.which || evt.which == 1) && (!evt.button || evt.button == 1)) {
                m_posStartX   = m_dragCtrl.offsetLeft;
                m_mouseStartX = evt.clientX;
                CE.CEUI.addMouseHandler(onDragMove);
                m_dragCtrl.ondragstart = function() { return false; };
                document.onselectstart = function () { return false; };
                m_dragCtrl.focus();
            }
        }
        this.onDragStop = function()
        {
            if(m_dragCtrl) {
                CE.CEUI.removeMouseHandler(onDragMove);
                document.onselectstart = null;
                m_dragCtrl.focus();
                m_dragCtrl = null;
                updatePos();
                return true;
            } else {
                return false;
            }
        }
        function onDragMove(evt)
        {
            if(!evt) evt = window.event;
            
            var newX = m_posStartX + (evt.clientX - m_mouseStartX);
            if(newX < 0)
                newX = 0;
            if(newX > (m_dragCtrl.parentNode.offsetWidth-2-m_dragCtrl.offsetWidth))
                newX = m_dragCtrl.parentNode.offsetWidth-2-m_dragCtrl.offsetWidth;
            m_dragCtrl.style.left = newX + 'px';
            m_dragCtrl.focus();
            
            var fraction = newX / (m_dragCtrl.parentNode.offsetWidth-2-m_dragCtrl.offsetWidth);

            if(m_dragCtrl == m_posNib && m_trayPlayer.duration != 0 && m_trayPlayer.duration != NaN) {
                m_trayPlayer.currentTime = parseInt(m_trayPlayer.duration * fraction);
            } else if(m_dragCtrl == m_volNib) {
                m_trayPlayer.volume = fraction;
            }
        }

        function updatePos() {
            if(m_trayPlayer.duration != 0 && m_trayPlayer.duration != NaN && m_dragCtrl != m_posNib) {
                var pos = m_trayPlayer.currentTime / m_trayPlayer.duration;
                m_posNib.style.left = parseInt(((m_posNib.parentNode.offsetWidth-2) - m_posNib.offsetWidth) * pos) + 'px';
                
                var time = new Date(2010, 0, 1, 0, 0, parseInt(m_trayPlayer.currentTime));
                m_posNib.title = CE.STRTAB.lookup('view.audio.pos', time.format('MM:ss'));
                m_posNib.parentNode.title = m_posNib.title;
            }
            
            if(m_dragCtrl != m_volNib) {
                m_volNib.style.left = parseInt(((m_volNib.parentNode.offsetWidth-2) - m_volNib.offsetWidth) * m_trayPlayer.volume) + 'px';
                if(m_trayPlayer.muted) {
                    CE.aCN(m_butMute, 'on');
                } else {
                    CE.rCN(m_butMute, 'on');
                }

                m_volNib.title = CE.STRTAB.lookup('view.audio.vol', parseInt(m_trayPlayer.volume*100));
                m_volNib.parentNode.title = m_volNib.title;
            }
        }
        
        function updateButs() {
            if(m_trayPlayer.paused || m_trayPlayer.ended) {
                CE.aCN(m_butPause, 'play');
                CE.rCN(m_butPause, 'pause');
            } else {
                CE.aCN(m_butPause, 'pause');
                CE.rCN(m_butPause, 'play');
            }
        }
        
        function prev() {
            if(m_playlist.length > 0) {
                --m_index;
                if(m_index < 0)
                    m_index = 0;
                m_trayPlayer.src = m_playlist[m_index].filename;
                m_trayPlayer.load();
            }
        }
    
        function next(evt) {
            updateButs();
            if(m_playlist.length > 0) {
                ++m_index;
                if(m_index >= m_playlist.length) {
                    m_index = 0;
                    if(!evt || !evt.force)
                        return; // end of playlist    
                }
                m_trayPlayer.src = m_playlist[m_index].filename;
                m_trayPlayer.load();
            }
        }
        
        function mute() {
            if(CE.hCN(m_butMute, 'on')) {
                CE.rCN(m_butMute, 'on');
                m_trayPlayer.muted = false;
            } else {
                CE.aCN(m_butMute, 'on');
                m_trayPlayer.muted = true;
            }
        }
    
        this.pause = function(evt) {
            if((m_trayPlayer.paused || m_trayPlayer.ended) && (!evt || !evt.force)) {
                m_trayPlayer.play();
            } else {
                m_trayPlayer.pause();
            }
        };

        this.queue = function(playlist) {
            m_playlist = playlist;
        };
        
        this.play = function(trackIndex) {
            if(typeof(trackIndex) != 'undefined') {
                m_index = trackIndex;
                m_trayPlayer.src = m_playlist[m_index].filename;
                m_trayPlayer.load();
            } else {
                m_trayPlayer.play();
            }
        };
        
        this.show = function(parent) {
            m_trayPlayer = parent.appendChild(CE.dce('audio','audio_tray_player'));
            var controls = parent.appendChild(CE.dce('div',null,'audio_tray_controls'));
            
            var butPrev = controls.appendChild(CE.dce('div',null,'but prev'));
            butPrev.title = CE.STRTAB.lookup('view.audio.prev');
            CE.CEU.attachEvent(butPrev, 'click', {onEvent:prev});

            var butNext = controls.appendChild(CE.dce('div',null,'but next'));
            butNext.title = CE.STRTAB.lookup('view.audio.next');
            CE.CEU.attachEvent(butNext, 'click', {onEvent:next, force:true});

            m_butPause = controls.appendChild(CE.dce('div',null,'but pause'));
            m_butPause.title = CE.STRTAB.lookup('view.audio.pause');
            CE.CEU.attachEvent(m_butPause, 'click', {onEvent:that.pause});

            var posSlider = controls.appendChild(CE.dce('div',null,'pos_slider'));
            m_posNib = posSlider.appendChild(CE.dce('div',null,'pos_nib'));
            CE.CEU.attachEvent(m_posNib, 'mousedown', {onEvent:onDragStart, ctrl:m_posNib});
            CE.CEU.attachEvent(m_posNib, 'mouseup',   {onEvent:that.onDragStop});

            var volSlider = controls.appendChild(CE.dce('div',null,'vol_slider'));
            m_volNib = volSlider.appendChild(CE.dce('div',null,'vol_nib'));
            CE.CEU.attachEvent(m_volNib, 'mousedown', {onEvent:onDragStart, ctrl:m_volNib});
            CE.CEU.attachEvent(m_volNib, 'mouseup',   {onEvent:that.onDragStop});

            m_butMute = controls.appendChild(CE.dce('div',null,'mute'));
            m_butMute.title = CE.STRTAB.lookup('view.audio.mute');
            CE.CEU.attachEvent(m_butMute, 'click', {onEvent:mute});

            m_trayPlayer.addEventListener('canplay', function() {
                m_trayPlayer.play();
                updatePos();
            }, false);
            m_trayPlayer.addEventListener('ended', next, false);
            m_trayPlayer.addEventListener('pause', updateButs, false);
            m_trayPlayer.addEventListener('play', updateButs, false);
            //m_trayPlayer.addEventListener('timeupdate', updatePos, false);
            setInterval(updatePos, 1000);
        };
    }

    function playAudioInTray(files, curFile) {
        var fileNum   = 0;
        var playlist = '<playlist>';
        var playlistArray = [];
        var trackIndex    = 0;
        
        for(var i=0; i < files.length; ++i) {
            nf = files[i];
            if(!CE.CEUI.isAudioFile(nf))
                continue;
            if(curFile == i)
                trackIndex = fileNum;
            ++fileNum;
                
            playlist += '<item>';
    
            var urlPrefix = CE.CEUI.getDataStreamPrefix(nf, null);
            var mediaUrl  = urlPrefix + nf.fileid + "/" + encodeURIComponent(nf.name);
            var thumbUrl  = '';
            if(nf.thumbnail)
                thumbUrl = urlPrefix + nf.thumbnail + "/" + encodeURIComponent(nf.name);
            else if(nf.preview)
                thumbUrl = urlPrefix + nf.preview + "/" + encodeURIComponent(nf.name);
            
            playlistArray.push({
                title:    CE.CEU.padInt(fileNum,5) + ' ' + CE.CEUI.getDispFn(nf),
                filename: mediaUrl,
                image:    thumbUrl});

            playlist += '<title>' + CE.CEU.padInt(fileNum,5) + ' ' + CE.CEU.xmlEncode(CE.CEUI.getDispFn(nf)) + '</title>';
            playlist += '<filename>' + CE.CEU.xmlEncode(mediaUrl) + '</filename>';
            if(thumbUrl.length > 0)
                playlist += '<image>' + CE.CEU.xmlEncode(thumbUrl) + '</image>';
            
            playlist += '</item>';

        }
        
        playlist += '</playlist>';
        
        // Create the tray player if it isn't already there
        if(CE.hCN('audio_tray_holder', 'hidden')) {
            CE.rCN('audio_tray_holder', 'hidden');
            CE.aCN('cesidebar',         'ceaudiotray');
            var height = 42;
            var width  = 100;
            
            if(hasHtml5Audio()) {
                g_audioPlayer = new AudioTrayPlayer();
                g_audioPlayer.show(CE.CEU.$('audio_tray'));
            } else {
                var regCode = "NjZSVFlUUCUyQ2YlN0ZVNXolM0M4JTdCciUzQm93NzdRMjFKbyU4MVElMjlO";         // *.pogoplug.com
                if(document.domain.toLowerCase().indexOf("cloudengines.com") != -1)
                  regCode = "NDYyV1plMllyJTdFSE0lM0FiN0YlM0IlNDBMUSU3QjFSRCUzQUV5Y1NhUCU1RVFRJTdC"; // *.cloudengines.com
                
                var wimpyPlayer = new Object();
                wimpyPlayer.wimpySwf=CE.STRTAB.lookup("wimpydir")+"rave.swf";
                wimpyPlayer.wimpyWidth=width;
                wimpyPlayer.wimpyHeight=height;
                wimpyPlayer.wimpyReg=regCode;
                wimpyPlayer.wimpySkin=CE.STRTAB.lookup("wimpydir")+"skins/tray-player/skin_tray-player-smalltray.xml";
                wimpyPlayer.startPlayingOnload="yes";
                wimpyPlayer.autoAdvance="yes";
                wimpyPlayer.setAspectRatio="maintain";
                makeWimpyPlayer(wimpyPlayer, "audio_tray", null, "wimpy_tray");
            }
            
            CE.CEU.$("audio_tray_holder").style.zIndex = 10; // temporarily lift up the tray to ensure immediate start of audio
        }

        // Play the audio
        if(g_audioPlayer) {
            // HTML5
            g_audioPlayer.queue(playlistArray);
            g_audioPlayer.play(trackIndex);
        } else {
            // Wimpy
            var interval = setInterval(function(){
                if(wimpy_amReady_ask("wimpy_tray")) {
                    clearInterval(interval);
                    CE.CEU.$("audio_tray_holder").style.zIndex = 5; // started at higher zindex to ensure immediate start
                    wimpy_clearPlaylist("wimpy_tray");
                    wimpy_appendPlaylist(playlist, false, "wimpy_tray");
                    wimpy_gotoTrack(trackIndex+1, "wimpy_tray");
                    wimpy_play("wimpy_tray");
                }
            }, 200);
        }
    }
    
    this.pauseAudioTray = function() {
        if(g_audioPlayer) {
            // HTML5
            g_audioPlayer.pause({force:true});
        } else {
            // Wimpy
            if(!CE.hCN('audio_tray_holder', 'hidden')) {
                if(wimpy_amReady_ask("wimpy_tray")) {
                    wimpy_pause("wimpy_tray");
                }
            }
        }
    };
    
    this.minAudioTray = function() {
        CE.rCN('cesidebar',         'ceaudiotray');
        CE.aCN('audio_tray',        'cemin');
        CE.aCN('audio_tray_holder', 'cemin');
        CE.aCN('audio_tray_min',    'hidden');
        CE.rCN('audio_tray_max',    'hidden');
    };
    
    this.maxAudioTray = function() {
        CE.rCN('audio_tray',        'cemin');
        CE.rCN('audio_tray_holder', 'cemin');
        CE.rCN('audio_tray_min',    'hidden');
        CE.aCN('audio_tray_max',    'hidden');
        CE.aCN('cesidebar',         'ceaudiotray');
    };

    this.refreshFileStream = function(file, svc, cb) {
        // This is a video with a stream that has already been queued for full transcoding; check if it's done
        var args = ['deviceid',  svc.deviceid,
                    'serviceid', svc.serviceid,
                    'fileid',    file.fileid];
                    
        CE.CEU.svc.asyncRPC('POST', "getFile", args, function(r) {
            if(r.file) {
                file.streamRefreshed = true;
                file.stream          = r.file.stream;
                file.streamtype      = r.file.streamtype;
            }
            if(cb) {
                cb();
            }
        }, cb);
    };
    
    this.isTranscodeQueued = function(file, svc) {
        var key = 'tq-' + b64_md5(svc.deviceid + ':' + svc.serviceid + ':' + file.fileid);
        var val = CE.CEU.getCookie(key);
        return (val && val == '1');
    };
    this.setTranscodeQueued = function(file, svc) {
        var key = 'tq-' + b64_md5(svc.deviceid + ':' + svc.serviceid + ':' + file.fileid);
        CE.CEU.setCookieTemp(key, '1');
    };
    
    function showMediaInSlideshow(el, nf, slideobj, iconUrl, startPaused) {
        if(CE.CEUI.isVideoFile(nf)) {
            CE.CEUI.pauseAudioTray();
            
            var urlPrefix = CE.CEUI.getDataStreamPrefix(nf, null);
            var mediaUrl  = urlPrefix + nf.fileid + "/" + encodeURIComponent(nf.name);
            var thumbUrl  = '';
            if(nf.thumbnail)
                thumbUrl = urlPrefix + nf.thumbnail + "/" + encodeURIComponent(nf.name);
            else if(nf.preview)
                thumbUrl = urlPrefix + nf.preview + "/" + encodeURIComponent(nf.name);
    
            var showDiv = CE.dce('div', "slideshow_extra_div", '');
            showDiv.style.zIndex = 500;
            el.appendChild(showDiv);

            var svc = CE.CEUI.getCurSvc(nf);
            if(nf.stream && svc.xcodeStream != "never") {
                mediaUrl = urlPrefix + nf.stream + "/" + encodeURIComponent(nf.name) + ".mp4";
                if(nf.streamtype != 'full' && svc.xcodeStream == 'always') {
                    if(CE.CEUI.isTranscodeQueued(nf,svc) && !nf.streamRefreshed) {
                        CE.CEUI.refreshFileStream(nf, svc, function(){showMediaInSlideshow(el, nf, slideobj, iconUrl, startPaused);});
                        return;
                    } else if(!CE.CEUI.isTranscodeQueued(nf,svc)) {
                        // Queue the full transcode when the preview is done
                        CE.CEUI.g_videoSlideTranscode = true;
                        CE.CEUI.g_videoTranscodeFile  = nf;
                    }
                    nf.streamRefreshed = false;
                } else {
                    CE.CEUI.g_videoSlideTranscode = false;
                }
            }
            
            CE.CEUI.createVideoEl(showDiv, 'slide_flv', mediaUrl, el.offsetWidth, el.offsetHeight, !startPaused, false,
                                  CE.CEUI.onSlidePreviewVideoComplete, true, 'opaque', thumbUrl);
        } else if(!startPaused) {
            // Play audio in the tray
            var files = [];
            var index = 0;
            var pos   = 0;
            for(var i = 0; i < g_curpage['celist'].length; ++i) {
                if(CE.CEUI.isAudioFile(g_curpage['celist'][i])) {
                    files.push(g_curpage['celist'][i]);
                    if(g_curpage['celist'][i].fileid == nf.fileid)
                        index = pos;
                    ++pos;
                }
            }
            playAudioInTray(files, index);
        }
    }

    function saveSlideAlbOrder(cb) {
        if(g_slideAlbImages.length == 0 && g_slideAlbMusic.length == 0) {
            if(cb) cb();
            return;
        }
        
        // Update the album files with the correct ordering
        var appendFiles = function(outFiles, inFiles, baseIndex) {
            for(var i = 0; i < inFiles.length; ++i) {
                var file = [];
                file.push('albumindex',    baseIndex + ((i+1)*100));
                file.push('deviceid',      inFiles[i].deviceid);
                file.push('serviceid',     inFiles[i].serviceid);
                file.push('fileid',        inFiles[i].fileid);
                file.push('filename',      inFiles[i].filename);
                if(inFiles[i].mimetype)    file.push('mimetype',  inFiles[i].mimetype);
                if(inFiles[i].mtime)       file.push('mtime',     inFiles[i].mtime);
                if(inFiles[i].size)        file.push('size',      inFiles[i].size);
                if(inFiles[i].type)        file.push('type',      inFiles[i].type);
                if(inFiles[i].thumbnail)   file.push('thumbnail', inFiles[i].thumbnail);
                if(inFiles[i].preview)     file.push('preview',   inFiles[i].preview);
                if(inFiles[i].stream)      file.push('stream',    inFiles[i].stream);
                outFiles.push(file);
                
                // Since we're updating the album, sync up our in-memory file index
                inFiles[i].albumindex = baseIndex + ((i+1)*100);
            }
        }
        
        var files = [];
        appendFiles(files, g_slideAlbImages, 0);
        appendFiles(files, g_slideAlbMusic,  1000000);
        
        CE.CEU.svc.asyncRPC("POST", "updateAlbumFiles", ['albumid',that.g_curalbum.albumid, 'files',files], cb, onGenericFailure);
    }
    
    function onSlideAlbDropInsertFile(newFile, dropIndex, cb) {
        var list = CE.CEUI.isAudioFile(newFile) ? g_slideAlbMusic : g_slideAlbImages;
        
        var svc = CE.CEUI.getCurSvc(newFile);
        if(!newFile.deviceid)  newFile.deviceid = svc.deviceid;
        if(!newFile.serviceid) newFile.serviceid = svc.serviceid;

        // Get the new album index and insert into our in-memory list
        if(dropIndex >= 0 && dropIndex < list.length) {
            var prevIndex, nextIndex;
            if((dropIndex-1) >= 0) {
                prevIndex = parseInt(list[dropIndex-1].albumindex);
                nextIndex = parseInt(list[dropIndex].albumindex);
                newFile.albumindex = prevIndex + Math.floor((nextIndex-prevIndex)/2);
            } else {
                prevIndex = 0;
                nextIndex = parseInt(list[dropIndex].albumindex);
                newFile.albumindex = Math.floor(nextIndex / 2);
            }
            if(newFile.albumindex <= prevIndex || newFile.albumindex >= nextIndex) {
                // No room to add this new file into the correct order; re-space the file ordering and try again
                return saveSlideAlbOrder(function() {
                    onSlideAlbDropInsertFile(newFile, dropIndex, cb);
                });
            }
            list.splice(dropIndex, 0, newFile);
        } else {
            newFile.albumindex = (list.length > 0) ? parseInt(list[list.length-1].albumindex)+100 : 100;
            list.push(newFile);
        }
        
        // Insert this file into the actual album
        var args = ['name',       newFile.name,
                    'albumindex', newFile.albumindex,
                    'albumid',    that.g_curalbum.albumid,
                    'deviceid',   svc.deviceid,
                    'serviceid',  svc.serviceid,
                    'fileid',     newFile.fileid,
                    'filename',   newFile.filename,
                    'type',       newFile.type, 
                    'fsize',      newFile.size,
                    'mime',       newFile.mimetype,
                    'mtime',      newFile.mtime,
                    'thumbnail',  newFile.thumbnail,
                    'preview',    newFile.preview,
                    'stream',     newFile.stream];

        CE.CEU.svc.asyncRPC("POST", "addAlbumFile", args, cb, onGenericFailure);
    }

    function onSlideAlbDropFolder(r, d, t, cb) {
        var i = 0;
        var metaPrompted, metaPromptDeny;
        
        var insertNextFile = function() {
            CE.CEU.showLoadingAni(true);
            if(r.files && r.files.length > i) {
                if(CE.CEUI.isAudioFile(r.files[i]) || CE.CEUI.isImageFile(r.files[i]) || CE.CEUI.isVideoFile(r.files[i])) {
                    if(!r.files[i].svc)
                        r.files[i].svc = d.svc;
                    ++i;
                    onSlideAlbDropInsertFile(r.files[i-1], -1, insertNextFile);
                } else if(CE.CEUI.isDescendableFile(r.files[i]) && (!d.subf || !CE.CEUI.isDescendableMetaFile(r.files[i]))) {
                    var descendInsert = function() {
                        CE.CEU.showLoadingAni(true);
                        var svc  = r.files[i].svc ? r.files[i].svc  : d.svc
                        var args = ['deviceid',  svc.deviceid,
                                    'serviceid', svc.serviceid,
                                    'maxcount',  NPERPAGE_SLIDEALB,
                                    'parentid',  r.files[i].fileid];
                        CE.CEU.svc.asyncRPC('POST', 'listFiles', args, 
                        function(r1) {
                            onSlideAlbDropFolder(r1, {'drag':d.drag, 'evt':d.evt, 'tel':d.tel, 'svc':svc, 'subf':true}, null, insertNextFile);
                        },
                        insertNextFile);
                        ++i;
                        return true;
                    };
                    if(CE.CEUI.isDescendableMetaFile(r.files[i]) && !metaPrompted) {
                        metaPrompted = true;
                        CE.CEU.showLoadingAni(false);
                        CE.CEU.promptYesNo(CE.STRTAB.lookup('drag.filegrp.slide.title'),
                                           CE.STRTAB.lookup('drag.filegrp.slide.msg'), descendInsert,               //yes
                                           function() {metaPromptDeny=true; ++i; insertNextFile(); return true;});  //no
                    } else if(CE.CEUI.isDescendableMetaFile(r.files[i]) && metaPrompted && metaPromptDeny) {
                        ++i;
                        insertNextFile();
                    } else {
                        descendInsert();
                    }
                } else {
                    ++i;
                    insertNextFile();
                }
            } else if(cb) {
                cb();
            } else {
                // All files have been inserted; refresh display
                populateSlideAlbEditRow(CE.CEU.$('slidedir_music'),  'music',  g_slideAlbMusic);
                populateSlideAlbEditRow(CE.CEU.$('slidedir_images'), 'images', g_slideAlbImages);
                CE.CEU.showLoadingAni(false);
            }
        };

        // Insert all the slideshow album-compatible files in this folder
        insertNextFile();
    }
    
    function onSlideAlbDrop(drag, evt, tel) {
        var srcFile = CE.CEU.shallowCopy(drag.src.file);
        
        if(drag.src.go.grd == 'cesrc_list') {
            var gid = getGid(drag.src.go.grd, drag.src.id, srcFile);
            var sel = CE.getByClass(CE.CEU.$(gid), 'div', 'selectcheck');
            if(sel.length > 0 && CE.hCN(sel[0],'selected')) {
                // Multiple files are selected; copy them all
                var files = [];
                for(var sid in g_selfiles[drag.src.go.grd]) {
                    files.push(g_selfiles[drag.src.go.grd][sid]);
                }
                onSlideAlbDropFolder({'files':files}, {'drag':drag, 'evt':evt, 'tel':tel, 'svc':drag.src.go.svc});
                return;
            } else if(CE.CEUI.isDescendableFile(srcFile)) {
                onSlideAlbDropFolder({'files':[srcFile]}, {'drag':drag, 'evt':evt, 'tel':tel, 'svc':drag.src.go.svc});
                return;
            }
        }
        
        if(tel.id.indexOf('slidedir_place_') == -1) {
            // We really want the placeholder before the file item
            tel = tel.previousSibling;
            if(!tel || tel.id.indexOf('slidedir_place_') == -1) {
                // File being dragged onto random space from Copy Files pane; just copy
                if(!CE.CEUI.isAudioFile(srcFile) && !CE.CEUI.isImageFile(srcFile) && !CE.CEUI.isVideoFile(srcFile))
                    return;
                if(drag && drag.src && drag.src.slidedir)
                    return; // already in the slideshow
                return onSlideAlbDropInsertFile(srcFile, -1, function(){
                    populateSlideAlbEditRow(CE.CEU.$('slidedir_music'),  'music',  g_slideAlbMusic);
                    populateSlideAlbEditRow(CE.CEU.$('slidedir_images'), 'images', g_slideAlbImages);
                });
            }
        }
        
        var isAudio  = (tel.id.indexOf('_music_') != -1);
        var indexPos = tel.id.lastIndexOf('_');
        if(indexPos == -1)
            return; // not a valid drop placeholder
        var dropIndex = parseInt(tel.id.substr(indexPos+1));

        if(drag && drag.src && srcFile) {
            // Make sure the user is dropping a supported filetype on the right row
            if(CE.CEUI.isAudioFile(srcFile) != isAudio)
                return;
            if(!CE.CEUI.isAudioFile(srcFile) && !CE.CEUI.isImageFile(srcFile) && !CE.CEUI.isVideoFile(srcFile))
                return;
            
            if(!drag.src.go || !drag.src.go.slidedir) {
                // Dragging from the Copy Files pane
                return onSlideAlbDropInsertFile(srcFile, dropIndex, function(){
                    if(isAudio)
                        populateSlideAlbEditRow(CE.CEU.$('slidedir_music'), 'music', g_slideAlbMusic);
                    else
                        populateSlideAlbEditRow(CE.CEU.$('slidedir_images'), 'images', g_slideAlbImages);
                });
            } else {
                // Dragging another item that's already in this slideshow album
                // Stick the new file into the right position in the ordering
                var files    = isAudio ? g_slideAlbMusic : g_slideAlbImages;
                var newFiles = [];
                for(var i = 0; i < files.length; ++i) {
                    if(i == dropIndex)
                        newFiles.push(srcFile);
                    if(srcFile.filename == files[i].filename && srcFile.albumindex == files[i].albumindex)
                        continue;
                    newFiles.push(files[i]);
                }
                if(dropIndex >= files.length)
                    newFiles.push(srcFile);
                
                if(isAudio)
                    g_slideAlbMusic = newFiles;
                else
                    g_slideAlbImages = newFiles;

                // Re-display the rows
                if(isAudio)
                    populateSlideAlbEditRow(CE.CEU.$('slidedir_music'), 'music', newFiles);
                else
                    populateSlideAlbEditRow(CE.CEU.$('slidedir_images'), 'images', newFiles);
                saveSlideAlbOrder();
            }
        }
    }

    function appendSlideAlbPlaceholder(el, type, num) {
        var placeholder = CE.dce('div', 'slidedir_place_'+type+'_'+num, 'slidedir_placeholder');
        placeholder.oncedrop = onSlideAlbDrop;
        el.appendChild(placeholder);
    }
    
    function populateSlideAlbEditRow(rowDiv, type, files) {
       // Maintain scroll position
        var scrollPos = rowDiv.scrollLeft;
        CE.rac(rowDiv);
        
        var imageDiv = CE.dce('div');
        imageDiv.style.width = ((files.length * (170+24)) + 24) + 'px';
        rowDiv.appendChild(imageDiv);
        
        for(var i = 0; i < files.length; ++i) {
            appendSlideAlbPlaceholder(imageDiv, type, i);
            var co = {onEvent: onEventContent,
                      onAction: onActionContent,
                      file:files[i],
                      id:files[i].fileid,
                      name:files[i].filename,
                      slidedir:true,
                      go:{slidedir:true, actions:true}};
            
            var item = appendContentItem(imageDiv, co, co.go, i+1);
            item.oncedrop = onSlideAlbDrop;
        }
        appendSlideAlbPlaceholder(imageDiv, type, i);
        
        if(files.length > 0)
            CE.aCN('slidedir_emptyhint', 'hidden');
        
        rowDiv.scrollLeft = scrollPos;
    }
    
    function populateContentWithSlideAlbEdit(el, go) {
        CE.rCN('cetools_slide_write', 'hidden');
        
        // Display the image/video files
        var rowDiv = CE.dce('div', 'slidedir_images', 'slidedir_edit_row');
        populateSlideAlbEditRow(rowDiv, 'images', g_slideAlbImages);
        var outDiv = CE.dce('div', null, 'slidedir_edit_row_photocont');
        outDiv.style.marginBottom = '10px';
        outDiv.appendChild(rowDiv);
        el.appendChild(outDiv);
        
        if(g_slideAlbImages.length == 0 && g_slideAlbMusic.length == 0) {
            ((outDiv.appendChild(CE.dce('div','slidedir_emptyhint'))).appendChild(CE.dce('div',null,'cehintbubble')))
                .innerHTML = CE.STRTAB.lookup('slidedir.emptyhint');
        }
        
        // Display the audio files
        rowDiv = CE.dce('div', 'slidedir_music', 'slidedir_edit_row');
        populateSlideAlbEditRow(rowDiv, 'music', g_slideAlbMusic);
        outDiv = CE.dce('div', null, 'slidedir_edit_row_audiocont');
        outDiv.appendChild(rowDiv);
        el.appendChild(outDiv);
        
        // If there are no files, pop the add content pane automatically
        if(g_slideAlbImages.length < 1 && g_slideAlbMusic.length < 1 && !go.metashare) {
            CE.CEUI.addContent(true);
        }
        if(go.metashare)
            delete go.metashare;
    }
    
    function populateContentWithSlideAlbView(el, go) {
        CE.aCN('cetools_slide_write', 'hidden');
        
        // Play the music in the background
        if(g_slideAlbMusic.length > 0)
            playAudioInTray(g_slideAlbMusic, 0);
        
        // Show the slideshow
        if(g_slideAlbImages.length > 0)
            CE.CEUI.populateContentWithSlideshow(el, g_slideAlbImages, go, true, false, false, null, true);
        else
            showEmptyContentDiv("slideshow");
    }
    
    this.onSlideAlbEdit = function() {
        g_slideAlbMode = 'edit';
        CE.aCN('ceslidedir_edit', 'selected');
        CE.rCN('ceslidedir_view', 'selected');
        CE.CEUI.reloadContentCWD();
    }
    
    this.onSlideAlbView = function() {
        CE.CEUI.addContent(false);
        g_slideAlbMode = 'view';
        CE.rCN('ceslidedir_edit', 'selected');
        CE.aCN('ceslidedir_view', 'selected');
        CE.CEUI.reloadContentCWD();
    }

    function populateContentWithSlideAlbConfig(el, go) {
        if(that.g_curalbum && g_albummap[that.g_curalbum.albumid]) {
            if(!g_slideAlbMode)
                g_slideAlbMode = (that.g_curalbum.ownerid == CE.CEU.user.userid) ? 'edit' : 'view';
            
            CE.rCN('ceslidedir_edit', 'selected');
            CE.rCN('ceslidedir_view', 'selected');
            CE.aCN((g_slideAlbMode=='edit')?'ceslidedir_edit':'ceslidedir_view', 'selected');
            
            var slideDiv = CE.dce('div');
            slideDiv.style.width = '100%';
            slideDiv.style.paddingTop = '5px';
            el.appendChild(slideDiv);
            
            if(g_slideAlbMode == 'edit') {
                slideDiv.oncedrop = onSlideAlbDrop;
                populateContentWithSlideAlbEdit(slideDiv, go);
            } else {
                slideDiv.style.height = (CE.CEU.$('cecontent').offsetHeight - 17) + 'px';
                slideDiv.oncedrop = function(){};
                populateContentWithSlideAlbView(slideDiv, go);
            }
        } else {
            var slideDiv = CE.dce('div');
            slideDiv.style.width = '100%';
            slideDiv.style.height = (CE.CEU.$('cecontent').offsetHeight - 12) + 'px';
            el.appendChild(slideDiv);
            slideDiv.oncedrop = function(){};
            
            populateContentWithSlideAlbView(slideDiv, go);
        }

        // Hide loading anim
        CE.CEU.showLoadingAni(false);
    }
    
    function populateContentWithSlideAlb(el, files, go) {
        // Re-establish the order of the files and group into images/music
        g_slideAlbImages = [];
        g_slideAlbMusic  = [];
        
        mainloop:
        for(var i = 0; i < files.length; ++i) {
            var orderedList;
            if(CE.CEUI.isImageFile(files[i]) || CE.CEUI.isVideoFile(files[i]))
                orderedList = g_slideAlbImages;
            else if(CE.CEUI.isAudioFile(files[i]))
                orderedList = g_slideAlbMusic;
            else
                continue;
                
            // Insert this file in the correct position in the list
            for(var j = 0; j < orderedList.length; ++j) {
                if(parseInt(files[i].albumindex) < parseInt(orderedList[j].albumindex)) {
                    orderedList.splice(j, 0, files[i]);
                    continue mainloop;
                }
            }
            orderedList.push(files[i]);
        }
        
        populateContentWithSlideAlbConfig(el, go);
    }
    
    function isMusicListing(grd) {
        var search = (grd == 'cesrc_list') ? g_cursrcsearch : g_cursearch;
        return (search && (search == 'cesearch_fleximusic' || search == 'cesearch_albums' ||
                search == 'cesearch_artists' || search == 'cesearch_genres' || search == 'cesearch_audio'));
    }
    
    function isPhotoListing(grd) {
        var search = (grd == 'cesrc_list') ? g_cursrcsearch : g_cursearch;
        return (search && (search == 'cesearch_imagetime' || search == 'cesearch_image'));
    }
    
    function isPhotoTimelineListing() {
        return (g_cursearch && g_cursearch == 'cesearch_imagetime');
    }
    
    function isMovieListing(grd) {
        var search = (grd == 'cesrc_list') ? g_cursrcsearch : g_cursearch;
        return (search && (search == 'cesearch_movietime' || search == 'cesearch_video'));
    }

    function getCurShare() {
        if(g_curpath && g_curpath.length > 0) {
            var cur = g_curpath[g_curpath.length-1];
            if(cur.file && cur.file.deviceid && cur.file.serviceid && cur.file.fileid) {
                var maps = [g_sharemap, g_albummap];
                for(var m = 0; m < maps.length; ++m) {
                    var map = maps[m];
                    for(var i in map) {
                        var shareFiles = map[i].files;
                        if(!shareFiles && map[i].root)
                            shareFiles = [map[i].root];
                        if(!shareFiles)
                            continue;
                        for(var iSf = 0; iSf < shareFiles.length; ++iSf) {
                            if(shareFiles[iSf].fileid == cur.file.fileid && shareFiles[iSf].deviceid == cur.file.deviceid &&
                               shareFiles[iSf].serviceid == cur.file.serviceid) {
                                return map[i];
                            }
                        }
                    }
                }
            }
        }
        return that.g_curalbum;
    }
    
    function enableDisableToolbarButtons(disable) {
        var noPerms, notOwner, filtered, readOnly, nothing, hasSvc, nonCuzSearch, canSort, validSyncSource;
        
        if(!disable) {
            var svc = that.g_cursvc;
            if(!svc && that.g_curalbum && that.g_curalbum.root)
                svc = g_svcmap[that.g_curalbum.root.deviceid + ":_:" + that.g_curalbum.root.serviceid];
            if(!svc && that.g_curalbum)
                svc = that.g_curalbum.root;

            noPerms  = (that.g_curalbum && CE.CEU.user && that.g_curalbum.ownerid != CE.CEU.user.userid && that.g_curalbum.perms == 0);
            notOwner = (!that.g_cursvc && (!that.g_curalbum || (CE.CEU.user && that.g_curalbum.ownerid != CE.CEU.user.userid)));
            filtered = CE.CEUI.isFilteredListing();
            readOnly = (svc && svc.readonly);
            nothing  = CE.CEUI.isNothingAvailable();
            canSort  = (svc && svc.type.indexOf('xce:plugfs:cloud:gdocs') == -1); // hack until gdocs supports sort
            validSyncSource = (svc && svc.type.indexOf('xce:plugfs') == 0 && hasFeature('foldersync',svc.deviceid));

            for(var s in g_svcmap) {
                if(g_svcmap[s].online && g_svcmap[s].online == '1') {
                    hasSvc = true;
                    break;
                }
            }
            
            if(g_wasSearch) {
                var folder = getCurrentFolder();
                if(!((g_curpath && g_curpath.length > 0 && g_curpath[g_curpath.length-1].file &&
                   g_curpath[g_curpath.length-1].file.cousins && g_curpath[g_curpath.length-1].file.cousins.length > 0) ||
                   (folder && folder.cousins))) {
                    nonCuzSearch = true;
                }
            }
        }
        
        if(disable || noPerms || filtered || readOnly || nothing) {
            CE.aCN('cetool_upload',    'cedisabled');
            CE.aCN('cetool_otheracts', 'cedisabled');
        } else {
            CE.rCN('cetool_upload',    'cedisabled');
            CE.rCN('cetool_otheracts', 'cedisabled');
        }

        if(disable || !hasSvc) {
            CE.aCN('cetool_newslidedir', 'cedisabled');
        } else {
            CE.rCN('cetool_newslidedir', 'cedisabled');
        }
        
        if(disable || disallowedShareWarning(true) || nonCuzSearch || nothing || notOwner) {
            CE.aCN('cetool_share',      'cedisabled');
            CE.aCN('cetool_shareslide', 'cedisabled');
        } else {
            CE.rCN('cetool_share',      'cedisabled');
            CE.rCN('cetool_shareslide', 'cedisabled');
        }
        
        if(disable || !validSyncSource || notOwner || filtered || nothing) {
            CE.aCN('cetool_sync', 'cedisabled');
        } else {
            CE.rCN('cetool_sync', 'cedisabled');
        }
        
        if(!canSort) {
            CE.aCN('cetool_sort', 'cedisabled');
        } else {
            CE.rCN('cetool_sort', 'cedisabled');
        }
    }
    
    this.toggleOtherActs = function() {
        if(CE.hCN('cetool_otheracts_pop', 'hidden')) {
            CE.CEU.$("cetoolbar").style.zIndex = 11;
            CE.rCN('cetool_otheracts_pop', 'hidden');
            CE.CEU.$('cetool_otheracts').blur();
        } else {
            CE.CEU.$("cetoolbar").style.zIndex = 1;
            CE.aCN('cetool_otheracts_pop', 'hidden');
        }
    };
    
    function populateContentBySource(r, go) {
        var groups = r.files;
        var found  = false;
        
        var sources = [];
        for(var s in groups) {
            sources.push({key:s, name:groups[s][0].svc.name});
        }
        sources.sort(function(a, b) {
            var an = a.name.toLowerCase();
            var bn = b.name.toLowerCase();
            if(an == bn)
                return 0;
            else if(an < bn)
                return -1;
            else
                return 1;
        });
        
        for(var i = 0; i < sources.length; ++i) {
            if(found)
                CE.CEU.$(go.grd).appendChild(CE.dce('div',null,'cebysourcelabel_sep'));
            found = true;
            r.files = groups[sources[i].key];
            
            (CE.CEU.$(go.grd).appendChild(CE.dce('div',null,'cebysourcelabel'))).appendChild(CE.dctn(r.files[0].svc.name));
            var el = CE.CEU.$(go.grd).appendChild(CE.dce('div'));
            
            populateContent(r, go, null, el);
        }
        
        if(!found) {
            r.files = [];
            populateContent(r, go);
        }
    }
    
    function selectAllFiles() {
        var checks    = CE.getByClass(CE.CEU.$('celist'), 'div', 'selectcheck');
        var notAllSel = false;
        for(var i = 0; i < checks.length; ++i) {
            if(!CE.hCN(checks[i],'selected')) {
                notAllSel = true;
                break;
            }
        }
        for(var i = 0; i < checks.length; ++i) {
            var evt = CE.CEU.getEventObj(checks[i], 'click');
            if(evt) {
                if(notAllSel && !CE.hCN(checks[i],'selected')) {
                    evt.onEvent(evt);
                } else if(!notAllSel && CE.hCN(checks[i],'selected')) {
                    evt.onEvent(evt);
                }
            }
        }
    }

    function populateContent(r, go, x, popdiv) {
        CE.CEDBG.println('CEUI: Populating Content: '+CE.CEDBG.serialize(go));
        var el = popdiv ? popdiv : CE.CEU.$(go.grd);
        
        if(go.grd == 'celist') {
            if(g_jumpToShare) {
                // Instead of showing this content, load up the share from the clicked link
                var shareId = g_jumpToShare;
                g_jumpToShare = null;
                activateAlbum(shareId, true);
                return;
            }
            
            CE.CEUI.closeSyncPane();
            
            if(go.shared) {
                var share = null;
                if(go.pid) {
                    share = findFolderShare( {fileid: go.pid});
                }
                if(!share && go.album) {
                    share = g_albummap[go.album.albumid];
                }
                if(!CE.CEUI.g_isSlideAlb || go.metashare)
                    openShareWindow(share);
                delete go.shared;
            } else {
                closeShareWindow();
            }
            
            // Show the breadcrumbs
            CE.rCN("cebc", "hidden");

            var writeTools = CE.CEU.$('cetools_write');
            if(writeTools) writeTools.style.visibility = 'visible';

            CE.rCN('cetools_normal',         'hidden');
            CE.aCN('cetools_fullscreenprev', 'hidden');
            CE.aCN('cetools_print',          'hidden');

            enableDisableToolbarButtons();
            closeTray(true);

            if(CE.CEUI.g_isSlideAlb) {
                if(CE.CEU.$('cesrcsvc_hidden'))
                    CE.CEU.$('cesrcsvc_hidden').appendChild(CE.CEU.$('cesrcsvc_sharefilter'));
                var modes = CE.CEU.$('cetools_slidedirmodes');
                if(modes)
                    CE.aCN('cetools_viewmodes', 'hidden');
                else
                    CE.CEU.$('cetools_viewmodes').style.visibility = 'hidden';
                if(modes) modes.style.display = 'inline';
                CE.aCN('cetools_nonslide', 'hidden');
                CE.rCN('cetools_slide', 'hidden');
                if(that.g_curalbum && !g_albummap[that.g_curalbum.albumid])
                    if(writeTools) writeTools.style.visibility = 'hidden';
                figureOutPagination(r, go);
                populateContentWithSlideAlb(el, r.files, go);
                return;
            } else {
                if(CE.CEU.$('cesrcsvc_myfiles'))
                    CE.CEU.$('cesrcsvc_myfiles').appendChild(CE.CEU.$('cesrcsvc_sharefilter'));
                g_slideAlbMode = null;
                var modes = CE.CEU.$('cetools_slidedirmodes');
                CE.rCN('cetools_viewmodes', 'hidden');
                CE.CEU.$('cetools_viewmodes').style.visibility = 'visible';
                if(modes) modes.style.display = 'none';
                CE.rCN('cetools_nonslide', 'hidden');
                CE.rCN('cetools_slide_write', 'hidden');
                CE.aCN('cetools_slide', 'hidden');
            }
        }

        var search   = g_cursearch;
        var viewMode = g_viewmode;
        var path     = g_curpath;
        var postfix  = '';
        
        if(go.grd == 'cesrc_list') {
            search   = g_cursrcsearch;
            viewMode = 0;
            path     = g_srcpath;
            postfix  = 'src';
        }

        if(el) {
            var tbody = null;
            // Make sure we are visible (switched out of empty album case)
            // even if there are no files we still want to to show the breadcrumb
            if(go.grd=='celist')
                showEmptyContentDiv(null);
            if(r && r.files) {
                var scrit = go.scrit || g_sortcrit;
                resortFiles(r.files, scrit);
                g_curpage[go.grd] = r.files;
            }
            
            if((!path || path.length < 1) && isMusicListing(go.grd)) {
                CE.rCN('cebuttons_fleximusic'+postfix, 'hidden');
                if(search == 'cesearch_albums')
                    CE.CEU.checkOpt('cebuttons_fleximusic_albums'+postfix);
                else if(search == 'cesearch_audio')
                    CE.CEU.checkOpt('cebuttons_fleximusic_songs'+postfix);
                else if(search == 'cesearch_artists')
                    CE.CEU.checkOpt('cebuttons_fleximusic_artists'+postfix);
                else if(search == 'cesearch_genres')
                    CE.CEU.checkOpt('cebuttons_fleximusic_genres'+postfix);
            } else {
                CE.aCN('cebuttons_fleximusic'+postfix, 'hidden');
            }
            
            if((!path || path.length < 1) && isPhotoListing(go.grd)) {
                CE.rCN('ceflexidrop'+postfix, 'hidden');
                CE.rCN('cebuttons_flexiimages'+postfix, 'hidden');
                if(!CE.hCN('cebuttons_grpmode','hidden'))
                    CE.rCN('ceflexiopts-sep', 'hidden');
                if(search == 'cesearch_imagetime')
                    CE.CEU.checkOpt('cebuttons_flexiimages_imagetime'+postfix);
                else if(search == 'cesearch_image')
                    CE.CEU.checkOpt('cebuttons_flexiimages_image'+postfix);
            } else {
                CE.aCN('cebuttons_flexiimages'+postfix, 'hidden');
            }

            if((!path || path.length < 1) && isMovieListing(go.grd)) {
                CE.rCN('ceflexidrop'+postfix, 'hidden');
                CE.rCN('cebuttons_fleximovies'+postfix, 'hidden');
                if(!CE.hCN('cebuttons_grpmode','hidden'))
                    CE.rCN('ceflexiopts-sep', 'hidden');
                if(search == 'cesearch_movietime')
                    CE.CEU.checkOpt('cebuttons_fleximovies_movietime'+postfix);
                else if(search == 'cesearch_video')
                    CE.CEU.checkOpt('cebuttons_fleximovies_video'+postfix);
            } else {
                CE.aCN('cebuttons_fleximovies'+postfix, 'hidden');
            }

            figureOutPagination(r, go);
            bcShowFittingCrumbsOnly();
            if(r && r.files && r.files.length) {
                if(CE.hCN('cetool_sizep','selected') && go.grd != 'cesrc_list') {
                    CE.rCN('cebuttons_preview', 'hidden');
                    var slideDiv = CE.dce('div');
                    el.appendChild(slideDiv);
                    slideDiv.style.height = (CE.CEU.$('cecontent').offsetHeight - 24) + 'px';
                    CE.CEUI.populateContentWithSlideshow(slideDiv, r.files, go, null, true, true);
                } else {
                    // don't build the table headers in list view unless we know we have some content. 
                    if(go.viewmode==1) {
                        var div   = CE.dce('div', 'celist_lv');
                        var table = CE.dce('table', 'celist_table');
                        tbody = CE.dce('tbody', 'celist_tbody');
                        var tr    = CE.dce('tr', null, 'head');
                        
                        var td = CE.dce('td', null, 'head icon');
                        if(CE.CEU.isLoggedIn()) {
                            var check = td.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'check-header.png'));
                            check.title = CE.STRTAB.lookup('view.check.all');
                            CE.CEU.attachEvent(check, 'click', {onEvent:selectAllFiles});
                        }
                        tr.appendChild(td);
                        
                        if(g_cursearch && isMusicListing()) {
                            td = CE.dce('td', null, 'head name');
                            addSortHeader(td, CE.STRTAB.lookup('view.col.name'), 'name', '+name', '-name');
                            tr.appendChild(td);

                            td = CE.dce('td', null, 'head artist');
                            addSortHeader(td, CE.STRTAB.lookup('view.col.artist')); //, 'artist', '+artist', '-artist');
                            tr.appendChild(td);

                            td = CE.dce('td', null, 'head album');
                            addSortHeader(td, CE.STRTAB.lookup('view.col.album')); //, 'album', '+album', '-album');
                            tr.appendChild(td);

                            td = CE.dce('td', null, 'head genre');
                            addSortHeader(td, CE.STRTAB.lookup('view.col.genre')); //, 'genre', '+genre', '-genre');
                            tr.appendChild(td);
                        } else {
                            if(g_cursearch && isPhotoTimelineListing()) {
                                td = CE.dce('td', null, 'head name');
                                addSortHeader(td, CE.STRTAB.lookup('view.col.name'), 'name', '+name', '-name');
                                tr.appendChild(td);
                            } else {
                                td = CE.dce('td', null, 'head name');
                                addSortHeader(td, CE.STRTAB.lookup('view.col.filename'), 'name', '+name', '-name');
                                tr.appendChild(td);
                            }
                            
                            td = CE.dce('td', null, 'head size');
                            addSortHeader(td, CE.STRTAB.lookup('view.col.size'), 'size', '+size', '-size');
                            tr.appendChild(td);
                            
                            td = CE.dce('td', null, 'head date');
                            addSortHeader(td, CE.STRTAB.lookup('view.col.date'), 'date', '+mtime', '-mtime');
                            tr.appendChild(td);
                            
                            td = CE.dce('td', null, 'head type');
                            addSortHeader(td, CE.STRTAB.lookup('view.col.type'), 'type', '+mimetype', '-mimetype');
                            tr.appendChild(td);
                        }
                        
                        td = CE.dce('td', null, 'head actions');
                        td.appendChild(CE.STRTAB.lookupel('view.col.actions'));
                        tr.appendChild(td);

                        tbody.appendChild(tr);
                        table.appendChild(tbody);
                        div.appendChild(table);
                        el.appendChild(div);
                    }
                    CE.CEDBG.println('CEUI: Content Set Page Size: '+r.files.length);
                    for(var i=0; i<r.files.length;i++) {
                        var id;
                        if(r.files[i].fileid) {
                            id = r.files[i].fileid;
                        } else {
                            CE.CEDBG.println('CEUI: no id for file: ['+r.files[i]+']');
                            continue;
                        }
                        
                        // if we are descending down a path from an album, the children
                        // are going to be on the same machine as their parents. 
                        if(go.album) {
                            if(!r.files[i].deviceid) {
                                if(g_curpath && g_curpath.length > 0) {
                                    if(g_curpath[g_curpath.length - 1].file) {
                                        if(g_curpath[g_curpath.length - 1].file.deviceid) {
                                            r.files[i].deviceid = g_curpath[g_curpath.length - 1].file.deviceid;
                                        }
                                        if(g_curpath[g_curpath.length - 1].file.deviceid) {
                                            r.files[i].serviceid = g_curpath[g_curpath.length - 1].file.serviceid;
                                        }
                                    }
                                }
                            }
                        }
                        if(go.grd == 'cesrc_list' && !r.files[i].deviceid && go.svc && go.svc.deviceid) {
                            r.files[i].deviceid  = go.svc.deviceid;
                            r.files[i].serviceid = go.svc.serviceid;
                        }
    
                        var co = 
                            {onEvent: onEventContent, 
                             onAction: onActionContent,
                             id: id,
                             go: go,
                             name: r.files[i].name,
                             file: r.files[i]
                            }
                        if(co.go.svc && r.files[i].svc)
                            co.go.svc = r.files[i].svc;
                            
                        if(go.viewmode==1) {
                            appendContentRow(tbody, i, co, go);
                        } else {
                            appendContentItem(el, co, go);
                        }
                    }
                    var ac = CE.dce('div');
                    ac.className = 'afterclear';
                    el.appendChild(ac); ac = null;
                }
            } else if(go.album) {
                if(go.grd=='celist') {
                    if(!g_cursearch && g_curpath.length == 0) {
                        if(g_albummap[go.album.albumid]) {
                            showEmptyContentDiv("albumowned");
                        } else {
                           showEmptyContentDiv("albumshared");
                        }
                    } else if(g_cursearch == 'cesearch_albums') {
                        showEmptyContentDiv("albums");
                    } else if(g_cursearch == 'cesearch_audio') {
                        showEmptyContentDiv("songs");
                    } else if(g_cursearch == 'cesearch_artists') {
                        showEmptyContentDiv("artists");
                    } else if(g_cursearch == 'cesearch_genres') {
                        showEmptyContentDiv("genres");
                    } else if(g_cursearch == 'cesearch_playlists') {
                        showEmptyContentDiv("playlists");
                    } else if(g_cursearch == 'cesearch_slidedirs') {
                        showEmptyContentDiv("slidedirs");
                    } else if(g_cursearch == 'cesearch_video' || g_cursearch == 'cesearch_movietime') {
                        showEmptyContentDiv("movies");
                    } else if(g_cursearch == 'cesearch_image' || g_cursearch == 'cesearch_imagetime') {
                        showEmptyContentDiv("photos");
                    } else if(g_cursearch && g_cursearch != '') {
                        showEmptyContentDiv("searchres");
                    } else {
                        showEmptyContentDiv("folder");
                    }
                }
            } else {
                if(go.grd=='celist') {
                    if(r.pageoffset > 0) {
                        g_curpageoffset = r.pageoffset - 1;
                        reloadContent();
                    } else if(g_cursearch == 'cesearch_albums') {
                        showEmptyContentDiv("albums");
                    } else if(g_cursearch == 'cesearch_audio') {
                        showEmptyContentDiv("songs");
                    } else if(g_cursearch == 'cesearch_artists') {
                        showEmptyContentDiv("artists");
                    } else if(g_cursearch == 'cesearch_genres') {
                        showEmptyContentDiv("genres");
                    } else if(g_cursearch == 'cesearch_playlists') {
                        showEmptyContentDiv("playlists");
                    } else if(g_cursearch == 'cesearch_slidedirs') {
                        showEmptyContentDiv("slidedirs");
                    } else if(g_cursearch == 'cesearch_video' || g_cursearch == 'cesearch_movietime') {
                        showEmptyContentDiv("movies");
                    } else if(g_cursearch == 'cesearch_image' || g_cursearch == 'cesearch_imagetime') {
                        showEmptyContentDiv("photos");
                    } else if(g_cursearch && g_cursearch == 'celibshares_share') {
                        showEmptyContentDiv("nosharedwithme");
                    } else if(g_cursearch && g_cursearch == 'celibshares_album') {
                        showEmptyContentDiv("nofilesishare");
                    } else if(g_cursearch && g_cursearch != '') {
                        showEmptyContentDiv("searchres");
                    } else {
                        showEmptyContentDiv("folder");
                    }
                }
            }
            // Hide loading graphic
            //CE.aCN(go.grd+'_loading','hidden');
            CE.CEU.showLoadingAni(false);
            el = null;

            // Its cool for us to upload to this album...
            var enableUpload = false;
            if(that.g_cursvc) {
                enableUpload = true;
            } else if(that.g_curalbum && CE.CEU.user && 
                      (that.g_curalbum.ownerid == CE.CEU.user.userid || 
                       that.g_curalbum.perms != "0")) {
                enableUpload = true;
            }
            if(enableUpload) {
                //CE.rCN("cetool_upload", "hidden");
            } else {
                //CE.aCN("cetool_upload", "hidden");
            }

        }
    }

    function downloadfile(file) {
        trackEvent('Download', null, file);
        
        var dlpath = that.getDataStreamPrefix(file, null);

        dlpath += file.fileid + "/dl/" + encodeURIComponent(file.name);
        if(CE.CEUI.isDescendableFile(file)) {
            dlpath += ".zip";
        }
        if(frames && frames['secretiframe']) {
            var dlframe = frames['secretiframe'];
            CE.CEDBG.println('CEUI: Setting secret iframe href to ' + dlpath);
            dlframe.location.href = dlpath;
        } else {
            var dlframe = CE.CEU.$('secretiframe');
            if(dlframe) {
                dlframe.src = dlpath;
            }
        } 
    }

    function addFolderToShareSuccess(r, d) {
        // lets do this without upsetting current focus
        reloadSidebar(true, 'cemyalb_albums');
        if(d.jump) {
            if(d.co.file.svc) {
                if(d.co.file.svc.albumid) {
                    that.g_cursvc   = null;
                    that.g_curalbum = d.co.file.svc;
                } else {
                    that.g_cursvc   = d.co.file.svc;
                    that.g_curalbum = null;
                }
                if(d.co.go && d.co.file.svc)
                    d.co.go.svc = d.co.file.svc;
            }
            pushContentStack(d.co);
        }
    }

    function createShareFromFile(co) {
        if(co.file.album && co.file.album.albumtype == FILE_TYPE_SLIDEALBUM) {
            // Already shared; add in the metashare
            setOption('slideshare_'+co.file.album.albumid, true);
            shbyid('ceshare_secure').checked = CE.CEUI.g_options['secureshares']?true:false;
            CE.CEUI.setAlbumSecurity(function() {
                activateAlbum(co.file.album.albumid, false, true);
            });
            return;
        }

        var filesToShare = [co.file];
        if(co.file.cousins)
            for(var i = 0; i < co.file.cousins.length; ++i)
                filesToShare.push(co.file.cousins[i]); // want to share all cousins
        if(filesToShare.length < 1)
            return;
        if(filesToShare.length > 0 && findFolderShare(filesToShare[0])) {
            if(co.go) co.go.shared = true;
            if(co.file.svc && co.file.svc.albumid) {
                that.g_cursvc   = null;
                if(!co.file.svc.ownerid && g_albummap[co.file.svc.albumid])
                    that.g_curalbum = g_albummap[co.file.svc.albumid];
                else
                    that.g_curalbum = co.file.svc;
            }
            return pushContentStack(co);
        }
        if(filesToShare.length > 0 && findFileShare(filesToShare[0])) {
            return openShareWindow(findFileShare(filesToShare[0]));
        }

        var isFolder = CE.CEUI.isDescendableFile(filesToShare[0]);
        var jump     = true;

        // Create the album that will host the shared folders
        trackEvent('Share', 'Create', filesToShare[0]);
        
        CE.CEU.svc.asyncRPC("POST", "createAlbum",
                            ['name',      CE.CEUI.getDispFn(filesToShare[0]),
                             'type',      filesToShare[0].type,
                             'albumtype', isFolder ? filesToShare[0].type : FILE_TYPE_FILEALBUM,
                             'secure',    CE.CEUI.g_options['secureshares']?'1':'0'],
        function(r){
            shbyid('ceshare_secure').checked = CE.CEUI.g_options['secureshares']?true:false;
            // Add the folders to this new album
            for(var i = 0; i < filesToShare.length; ++i) {
                var svc = CE.CEUI.getCurSvc(filesToShare[i]);
                if(!svc)
                    continue;
                if(co.go)
                    co.go.shared = true;
                var args = ['name',      CE.CEUI.getDispFn(filesToShare[i]),
                            'albumid',   r.album.albumid,
                            'deviceid',  svc.deviceid,
                            'serviceid', svc.serviceid,
                            'fileid',    filesToShare[i].fileid,
                            'filename',  filesToShare[i].name,
                            'type',      filesToShare[i].type, 
                            'fsize',     filesToShare[i].size,
                            'mime',      filesToShare[i].mimetype,
                            'mtime',     filesToShare[i].mtime,
                            'thumbnail', filesToShare[i].thumbnail,
                            'stream',    filesToShare[i].stream,
                            'preview',   filesToShare[i].preview];
                var svcKey = svc.deviceid + ':_:' + svc.serviceid + ':_:' + filesToShare[i].fileid;
                filesToShare[i].albumid = r.album.albumid;
                if(isFolder && g_sharedfolders[svcKey])
                    continue;

                CE.CEU.svc.asyncRPC("POST", "addAlbumFile", args,
                function(ar, d) {
                    if(CE.CEUI.isDescendableFile(d.file)) {
                        g_sharedfolders[d.svckey] = d.file; // to get invitation list
                        addFolderToShareSuccess(ar, d);
                    } else {
                        reloadSidebar(true, 'cemyalb_albums', function() {
                            openShareWindow(g_albummap[r.album.albumid]);
                            // Add the share icon to the file's displayed thumbnail
                            var item = CE.CEU.$('cecnt_'+d.file.fileid);
                            if(!item && d.file.deviceid && d.file.serviceid)
                                item = CE.CEU.$('cecnt_'+d.file.fileid+'_'+d.file.deviceid+'_'+d.file.serviceid);
                            if(item) {
                                var icons      = CE.getByClass(item, 'div', getFullClassName('itemicon'));
                                var shareicons = CE.getByClass(item, 'img', getFullClassName('shareicon'));
                                if(shareicons.length < 1 && icons.length > 0) {
                                    icons[0].appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'share-icon.png', null, getFullClassName('shareicon')));
                                }
                            }
                        });
                    }
                },
                onGenericFailure, {'co':co, 'jump':jump, 'file':filesToShare[i], 'svckey':svcKey});
                jump = false;
            }
        }, onGenericFailure);
    }

    function onCopyFileSuccess(r, d) {
        CE.CEU.showLoadingAni(false);
        if(r.progressid) {
            if(!g_xferprogress) g_xferprogress = new XferProgressDialog();
            g_xferprogress.show(r.progressid, d.srcSvc,
            function(success) {
                if(success && !CE.hCN('dragmenubut1_move','hidden') && (isOwnedSvc(d.srcSvc) || (d.srcSvc.perms && d.srcSvc.perms != '0'))) {
                    // Since we're in Move mode, delete the source files
                    var args = ['deviceid',  d.srcSvc.deviceid,
                                'serviceid', d.srcSvc.serviceid,
                                'fileid',    d.srcFile.fileid,
                                'recurse',   '1'];
                    if(d.srcSvc.albumid)
                        args.push('albumid', d.srcSvc.albumid);
                    CE.CEU.svc.asyncRPC('POST', 'removeFile', args, function() {
                        // Refresh source and dest listings
                        that.reloadContentCWD();
                        refreshCopyFilesPane();
                        if(d.cb)
                            d.cb();
                    },
                    function() {
                        // Refresh source and dest listings
                        that.reloadContentCWD();
                        refreshCopyFilesPane();
                        if(d.cb)
                            d.cb();
                    });
                } else {
                    var mainSvc = CE.CEUI.getCurSvc();
                    if(d.dstSvc.deviceid == mainSvc.deviceid && d.dstSvc.serviceid == mainSvc.serviceid) {
                        var mainFolderId;
                        if(g_curpath.length > 0 && g_curpath[g_curpath.length-1].file)
                            mainFolderId = g_curpath[g_curpath.length-1].file.fileid;
                        if(!mainFolderId)
                            mainFolderId = mainSvc.fileid;
                        if(!mainFolderId)
                            mainFolderId = '0';
                        if(mainFolderId == d.dstFolder) {
                            // Refresh main listing
                            that.reloadContentCWD();
                        }
                    }
                    
                    var srcSvc = g_srcsvc;
                    if(!CE.hCN('cesrctray','hidden') && g_srcsvc && d.dstSvc.deviceid == g_srcsvc.deviceid && d.dstSvc.serviceid == g_srcsvc.serviceid) {
                        var srcFolderId;
                        if(g_srcpath && g_srcpath.length > 0 && g_srcpath[g_srcpath.length-1].file)
                            srcFolderId = g_srcpath[g_srcpath.length-1].file.fileid;
                        if(!srcFolderId)
                            srcFolderId = g_srcsvc.fileid;
                        if(!srcFolderId)
                            srcFolderId = '0';
                        if(srcFolderId == d.dstFolder) {
                            // Refresh src listing
                            refreshCopyFilesPane();
                        }
                    }

                    if(d.cb)
                        d.cb();
                }
            });

        }
    }
    

    function onCopyFileFailure(r, d) {
        CE.CEU.showLoadingAni(false);
        if(d.cb) d.cb();
    }
    
    function onYesOverwriteDrop(data) {
        // User has chosen to overwrite, so copy the file
        var args =  [ "deviceid", data.srcSvc.deviceid, 
            "serviceid", data.srcSvc.serviceid, 
            "fileid", data.srcFile.fileid,
            "dstdeviceid", data.dstSvc.deviceid,
            "dstserviceid", data.dstSvc.serviceid,
            "dstfileid", data.dstFolder ];

        CE.CEU.showLoadingAni(true);
        CE.CEU.svc.asyncRPC("POST", "copyFile", args, onCopyFileSuccess, onCopyFileFailure, data);
        return true;
    }

    function onNoDontOverwriteDrop(data) {
        if(data.cb)
            data.cb();
        return true;
    }
    
    function checkDroppedFileExistsSuccess(r, data) {
        // File alreay exists at destination
        if(data.opt['over'] == 'yes') {
            // Yes to all
            onYesOverwriteDrop(data);
        } else if(data.opt['over'] == 'no') {
            // No to all
            onNoDontOverwriteDrop(data);
        } else {
            // Prompt the user to confirm that they wish to overwrite
            var msg = CE.dce('div');
            (msg.appendChild(CE.dce('div',null,'extramarginbottom')))
                .appendChild(CE.dctn(CE.STRTAB.lookup('view.overwrite.prompt',CE.CEUI.getDispFn(data.srcFile))));
            var checkDiv = msg.appendChild(CE.dce('div'));
            var check    = CE.dce('input', 'apply_all', 'cecheck');
            check.type = 'checkbox';
            checkDiv.appendChild(check);
            (checkDiv.appendChild(CE.dcl('apply_all'))).appendChild(CE.STRTAB.lookupel('view.overwrite.applyall'));
            
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.overwrite.title'), msg,
                [{name:'yes',label:CE.STRTAB.lookup("view.yes"),callback:
                function() {
                    if(check.checked) data.opt['over'] = 'yes';
                    onYesOverwriteDrop(data);
                    return true;
                }}, 
                {name:'no',label:CE.STRTAB.lookup("view.no"),callback:
                function() {
                    if(check.checked) data.opt['over'] = 'no';
                    onNoDontOverwriteDrop(data);
                    return true;
                }}]);
            dlg.show();
        }
    }
    
    function checkDroppedFileExistsFailure(r, data) {
        // File does not yet exist at destination, so copy it without hesitation
        var args =  [ "deviceid", data.srcSvc.deviceid, 
            "serviceid", data.srcSvc.serviceid, 
            "fileid", data.srcFile.fileid,
            "dstdeviceid", data.dstSvc.deviceid,
            "dstserviceid", data.dstSvc.serviceid,
            "dstfileid", data.dstFolder ];

        CE.CEU.showLoadingAni(true);
        CE.CEU.svc.asyncRPC("POST", "copyFile", args, onCopyFileSuccess, onCopyFileFailure, data);
    }
    
    function copyFiles(sourceSvc, sourceFiles, destParentId, destSvc, destPath) {
        var iFile = 0;
        var opt   = {};
        
        var copyFile = function() {
            if(iFile >= sourceFiles.length)
                return;
            var sourceFile = sourceFiles[iFile];
            ++iFile;
            if(CE.CEUI.isDescendableFile(sourceFile) && sourceFile.type != FILE_TYPE_DIRECTORY)
                return copyFile(); // don't try to copy meta-folders

            sourceSvc = sourceFile.svc ? sourceFile.svc : sourceSvc;
            if(!sourceSvc && sourceFile.serviceid && sourceFile.deviceid)
                sourceSvc = {serviceid:sourceFile.serviceid, deviceid:sourceFile.deviceid};
            if(!sourceSvc || !destSvc)
                return copyFile();

            if(sourceSvc.deviceid == destSvc.deviceid && sourceSvc.serviceid == destSvc.serviceid) {
                if(sourceFile.fileid == destSvc.fileid) {
                    return CE.CEU.showMessage(CE.STRTAB.lookup('view.copycaps'), CE.STRTAB.lookup('view.copy.norecurse'));
                }

                for(var i = 0; i < destPath.length; ++i) {
                    var bco = destPath[i];
                    if(bco && bco.file && sourceFile.fileid == bco.file.fileid) {
                        return CE.CEU.showMessage(CE.STRTAB.lookup('view.copycaps'), CE.STRTAB.lookup('view.copy.norecurse'));
                    }
                }
            }
            // Check whether the dropped file already exists at the destination
            var args = ['deviceid',  destSvc.deviceid,
                        'serviceid', destSvc.serviceid,
                        'parentid',  destParentId,
                        'filename',  sourceFile.name];
            CE.CEU.svc.asyncRPC('POST', "getFile", args, checkDroppedFileExistsSuccess, checkDroppedFileExistsFailure,
                                {'srcSvc':sourceSvc, 'srcFile':sourceFile, 'dstSvc':destSvc, 'dstFolder':destParentId,
                                 'cb':copyFile, 'opt':opt});
        };
        
        copyFile();
    }
    
    function getDropFolder(dropEl, grd) {
        if(!g_curpage[grd])
            return null;
            
        while(dropEl && dropEl.id != 'cemain') {
            var prefLen = !dropEl.id      ? -1 : dropEl.id.indexOf('_');
            var prefix  = (prefLen == -1) ? '' : dropEl.id.substr(0, prefLen);

            if(prefix == 'cecnt' || prefix == 'cesrccnt') {
                var fileid = dropEl.id.substr(prefLen+1);
                for(var i = 0; i < g_curpage[grd].length; ++i) {
                    if(g_curpage[grd][i].fileid == fileid.substr(0,g_curpage[grd][i].fileid.length)) {
                        if(g_curpage[grd][i].type == FILE_TYPE_DIRECTORY)
                            return g_curpage[grd][i];
                        else
                            return null;
                    }
                }
                return null;
            }

            dropEl = dropEl.parentNode;
        }
        
        return null;
    }
    
    function getMetaFolderFiles(metaFolder, metaFolderSvc, cb) {
        // Get all the files in the passed meta-folder (exclude sub meta-folders); passes resulting array to callback
        var files = [];
        
        metaFolderSvc = metaFolder.svc ? metaFolder.svc : metaFolderSvc;
        if(!metaFolderSvc && metaFolder.serviceid && metaFolder.deviceid)
            metaFolderSvc = {serviceid:metaFolder.serviceid, deviceid:metaFolder.deviceid};
        if(!metaFolderSvc)
            return cb(files);
        
        var page = 0;
        var numPerPage = 1000;
        
        var getNextPage = function() {
            CE.CEU.svc.asyncRPC('POST', 'listFiles', ['deviceid',metaFolderSvc.deviceid, 'serviceid',metaFolderSvc.serviceid,
                                'pageoffset',page, 'parentid',metaFolder.fileid, 'maxcount',numPerPage],
            function(r) {
                if(r.files) {
                    for(var i = 0; i < r.files.length; ++i) {
                        if(CE.CEUI.isDescendableMetaFile(r.files[i])) // exclude sub meta-folders
                            continue;
                        files.push(r.files[i]);
                        files[files.length-1].svc = metaFolderSvc;
                    }
                }
                if(!r.files || r.files.length < numPerPage) {
                    cb(files); // done
                } else {
                    ++page;
                    getNextPage();
                }
            },
            onGenericFailure);
        };
        getNextPage();
    }

    function onDropFiles(drag, evt, tel) {
        // Source
        var sourceSvc;
        var sourceGrd;
        var sourceFolderId, sourcePath;
        if(drag && drag.src && drag.src.go && drag.src.go.grd == 'cesrc_list') {
            sourceSvc     = g_srcsvc;
            sourceGrd     = 'cesrc_list';
            sourcePath    = g_srcpath;
        } else {
            sourceSvc     = that.g_cursvc;
            if(!sourceSvc && that.g_curalbum)
                sourceSvc = that.g_curalbum.root;
            if(!sourceSvc && drag && drag.src && drag.src.file && drag.src.file.svc)
                sourceSvc = drag.src.file.svc.root ? drag.src.file.svc.root : drag.src.file.svc;
            if(!sourceSvc)
                return false;
            sourceGrd     = 'celist';
            sourcePath    = g_curpath;
        }
        if(!sourceFolderId && sourcePath.length > 0) {
            var bco = sourcePath[sourcePath.length-1];
            if(bco && bco.file) {
                sourceFolderId = bco.file.fileid;
            }
        }
        if(!sourceFolderId && sourceSvc)
            sourceFolderId = sourceSvc.fileid;
        if(!sourceFolderId)
            sourceFolderId = '0';
        
        // Destination
        var destSvc, destPath, destGrd, noPerms;
        if(CE.CEU.hasAncestor(evt.target,'cesrctray')) {
            destSvc  = g_srcsvc;
            destPath = g_srcpath;
            destGrd  = 'cesrc_list';
        } else {
            destSvc = that.g_cursvc;
            if(!destSvc && that.g_curalbum) {
                destSvc = that.g_curalbum.root;
                noPerms = (CE.CEU.user && that.g_curalbum.ownerid != CE.CEU.user.userid && that.g_curalbum.perms == 0);
            }
            destPath = g_curpath;
            destGrd  = 'celist';
        }
        var destFolder = getDropFolder(evt.target, destGrd);
        if(!destSvc && destFolder && destFolder.svc) {
            if(destFolder.svc.root) {
                destSvc = destFolder.svc.root;
                noPerms = (CE.CEU.user && destFolder.svc.ownerid && destFolder.svc.ownerid != CE.CEU.user.userid && destFolder.svc.perms == 0);
            } else {
                destSvc = destFolder.svc;
            }
        }
        if(!destSvc || noPerms)
            return false;
        var destFolderId = destFolder ? destFolder.fileid : null;
        if(!destFolderId && destPath.length > 0) {
            var bco = destPath[destPath.length-1];
            if(bco && bco.file) {
                destFolderId = bco.file.fileid;
            }
        }
        if(!destFolderId && destSvc)
            destFolderId = destSvc.fileid;
        if(!destFolderId)
            destFolderId = '0';
        
        if(sourceSvc == destSvc && sourceFolderId == destFolderId)
            return;
        
        if(destSvc && destSvc.ownerid && destSvc.ownerid != CE.CEU.user.userid) {
            if(destSvc.perms == '0') {
                CE.CEU.showMessage(CE.STRTAB.lookup('view.copycaps'), CE.STRTAB.lookup('view.copy.nowrite'));
                return;
            }
        }

        if(drag && drag.src && drag.src.file) {
            var tmpSourceFiles = [];
            var gid = getGid(sourceGrd, drag.src.id, drag.src.file);
            var sel = CE.getByClass(CE.CEU.$(gid), 'div', 'selectcheck');
            if(sel.length > 0 && CE.hCN(sel[0],'selected')) {
                // Multiple files are selected; copy them all
                for(var sid in g_selfiles[sourceGrd]) {
                    if(sourceSvc != destSvc || g_selfiles[sourceGrd][sid].fileid != destFolderId) { // don't copy a folder to itself
                        tmpSourceFiles.push(g_selfiles[sourceGrd][sid]);
                    }
                }
            } else {
                if(sourceSvc != destSvc || drag.src.file.fileid != destFolderId) { // don't copy a folder to itself
                    tmpSourceFiles.push(drag.src.file);
                }
            }
            
            var sourceFiles = [];
            var iSourceFile = 0;
            var metaPrompted = false;
            
            var getNextSourceFile = function() {
                if(iSourceFile >= tmpSourceFiles.length) {
                    return copyFiles(sourceSvc, sourceFiles, destFolderId, destSvc, destPath); // done; copy the files
                }
                
                if(CE.CEUI.isDescendableMetaFile(tmpSourceFiles[iSourceFile])) {
                    var appendMetaFolderFiles = function() {
                        getMetaFolderFiles(tmpSourceFiles[iSourceFile], sourceSvc,
                        function(groupFiles) {
                            sourceFiles = sourceFiles.concat(groupFiles);
                            ++iSourceFile;
                            getNextSourceFile();
                        });
                        return true;
                    };
                    if(!metaPrompted) {
                        metaPrompted = true;
                        CE.CEU.promptYesNo(CE.STRTAB.lookup('drag.filegrp.copy.title'),
                                           CE.STRTAB.lookup('drag.filegrp.copy.msg'), appendMetaFolderFiles);
                    } else {
                        appendMetaFolderFiles();
                    }
                } else {
                    sourceFiles.push(tmpSourceFiles[iSourceFile]);
                    ++iSourceFile;
                    getNextSourceFile();
                }
            };
            getNextSourceFile();
        }

        return false;
    }
    
    function handleTrashAlbumFile(co) {
        // Prompt the user
        var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('ceui.slidedir.delete.title'), CE.STRTAB.lookup('ceui.slidedir.delete.msg',CE.CEUI.getDispFn(co.file)),
            [{name:'yes',label:CE.STRTAB.lookup("view.yes"),callback:function(){
                // Remove the file from the album
                CE.CEU.svc.asyncRPC('POST', "removeAlbumFile",
                    ['albumid',that.g_curalbum.albumid, 'deviceid',co.file.deviceid, 'serviceid',co.file.serviceid,
                     'fileid',co.file.fileid, 'albumindex',(co.file.albumindex?co.file.albumindex:'')],
                    function(){
                        // Refresh the listing
                        CE.CEUI.reloadContentCWD();
                    }, onGenericFailure);
                return true;
            }}, 
            {name:'no',label:CE.STRTAB.lookup("view.no")}]);
        dlg.show();
    }

    function handleTrashAction(co) {
        if(co.file.svc && co.file.svc.albumid && g_albummap[co.file.svc.albumid]) {
            // They may be trying to remove an album in the Files I Share listing
            var album = g_albummap[co.file.svc.albumid];
            if(album.root && album.root.fileid == co.file.fileid) {
                onDeleteFolderShareClick({albumid:co.file.svc.albumid}, null, null, CE.CEUI.isSlideAlbListing());
                return;
            } else if(album.files) {
                for(var i = 0; i < album.files.length; ++i) {
                    if(album.files[i].fileid == co.file.fileid) {
                        onDeleteFolderShareClick({albumid:co.file.svc.albumid}, null, null, CE.CEUI.isSlideAlbListing());
                        return;
                    }
                }
            }
        } else if(co.file.album && g_albummap[co.file.album.albumid]) {
            onDeleteFolderShareClick({albumid:co.file.album.albumid}, null, null, CE.CEUI.isSlideAlbListing());
            return;
        } else if(co.file.svc && co.file.svc.albumid && g_sharemap[co.file.svc.albumid]) {
            // They may be trying to remove a share in the Shared with me listing
            var share = g_sharemap[co.file.svc.albumid];
            if(share.root && share.root.fileid == co.file.fileid) {
                onRemoveFolderShareClick({album:share});
                return;
            } else if(share.files) {
                for(var i = 0; i < share.files.length; ++i) {
                    if(share.files[i].fileid == co.file.fileid) {
                        onRemoveFolderShareClick({album:share});
                        return;
                    }
                }
            }
        } else if(co.file.album && g_sharemap[co.file.album.albumid]) {
            onRemoveFolderShareClick({album:co.file.album});
            return;
        }
        
        if(co.go.grd) {
            for(var gid in g_selfiles[co.go.grd]) {
                var file = g_selfiles[co.go.grd][gid];
                if(co.file.fileid == file.fileid) {
                    // Clicking delete on one file in a selection; prompt to delete selection
                    return CE.CEUI.deleteSelected();
                }
            }
        }
        
        if(that.g_curalbum && that.g_curalbum.albumtype == FILE_TYPE_SLIDEALBUM) {
            return handleTrashAlbumFile(co);
        }

        if(showReadOnlyError(CE.STRTAB.lookup('view.delete'), CE.STRTAB.lookupel("view.onreadonlydrive"))) {
            return;
        }

        if(!g_deleteconfirm) {
            g_deleteconfirm = new DeleteConfirmDialog();
        }
        g_deleteconfirm.show(co.file);
    }

    function disallowedShareWarning(noShow) {
        var dvc = null;

        if(that.g_cursvc) {
            dvc = that.g_cursvc.device;
        } else if(that.g_curalbum && that.g_curalbum.root && that.g_curalbum.root.deviceid ) {
            var svc = g_svcmap[that.g_curalbum.root.deviceid + ":_:" + that.g_curalbum.root.serviceid];
            if(svc) {
                dvc = svc.device;
            }
        }
        if(dvc && dvc.sku && dvc.sku.terms != 0) {
            if(dvc.sku.terms & 1) {
                var now = new Date();
                if((dvc.valstart + dvc.valend) < now.getTime()) {
                    if(!noShow) CE.rCN("cetrialexpired", "hidden");
                    return true;
                } 
                if(!g_showedtrialwarning) {
                    if((dvc.valstart + dvc.valend) < (now.getTime()+(4 * 24 * 60 * 60 * 1000))) {
                        if(!noShow) {
                            g_showedtrialwarning = true;
                            CE.rCN('cetrialwarn', "hidden");
                        }
                        return true;
                    }
                }
            }        
        }
        return false;
    }
    
    function handleParentFolderAction(co) {
        var folders = [];
        var svc     = CE.CEUI.getCurSvc(co.file);
        
        if(svc.deviceid && svc.serviceid && g_svcmap[svc.deviceid+':_:'+svc.serviceid])
            svc = g_svcmap[svc.deviceid+':_:'+svc.serviceid]; // get the real service and not just the album...
        
        var postOp = function(){
            if(folders.length) {
                // Get the root breadcrumb
                g_cursearch = null;
                g_curscrit  = null;
                if(folders[0].fileid == 0 && svc.id && g_svcmap[svc.id]) {
                    CE.CEUI.g_cursvc   = g_svcmap[svc.id];
                    CE.CEUI.g_curalbum = null;
                    updateSidebarState(svc.id);
                } else if(folders[0].fileid != 0 && svc.fileid) {
                    CE.CEUI.g_cursvc = null;
                    for(var aid in g_sharemap) {
                        if(g_sharemap[aid].root.fileid == svc.fileid) {
                            CE.CEUI.g_curalbum = g_sharemap[aid];
                            updateSidebarState(CE.CEUI.g_curalbum.albumid);
                            break;
                        }
                    }
                }
                bcFlush();
                // Build the breadcrumb list for the folder
                for(var i = 1; i < folders.length; ++i) {
                    var co = {id:folders[i].fileid, name:folders[i].filename, file:folders[i]};
                    bcAppend(co);
                }
                CE.rCN("cebc", "hidden");
                // Load the folder
                CE.CEUI.reloadContentCWD();
            }
        };

        var getParentFolder = function(parentId) {
            if(svc.fileid && svc.fileid == parentId) {
                folders.unshift(svc);
                postOp();
                return;
            }
            var args = [];
            args.push('deviceid',  svc.deviceid);
            args.push('serviceid', svc.serviceid);
            args.push('fileid',    parentId);
            CE.CEU.svc.asyncRPC("POST", "getFile", args, function(r){
                folders.unshift(r.file);
                if(r.file.fileid == 0 || (svc.fileid && r.file.fileid == svc.fileid))
                    postOp();
                else
                    getParentFolder(r.file.parentid);
            },
            postOp);
        };
        
        // Get the chain of parent folders and jump to the real folder
        getParentFolder(co.file.parentid);
    }

    function onActionContent(co, action, evt, tel) {
        CE.CEDBG.println('CEUI: onActionContent: ['+action+']');
        closeContextMenu();
        closeHoverPreviewVideo();
        switch(action) {
        case 'zoom':
        case 'zoomfolder':
            return onEventContent(co, evt, tel);
        case 'download': 
            downloadfile(co.file);
            break;
        case 'print':
            onShowPrintDoc({f:co.file});
            break;
        case 'share':
            if(!disallowedShareWarning()) {
                createShareFromFile(co);
            }
            break;
        case 'trash':
        case 'trashshare':
        case 'trashmyshare':
        case 'trashslidealb':
        case 'trashslide':
            handleTrashAction(co);
            break;
        case 'folder':
            handleParentFolderAction(co);
            break;
        case 'rename':
            var renEvtId = (co.file.deviceid?co.file.deviceid:'')+(co.file.serviceid?co.file.serviceid:'')+co.file.fileid;
            if(g_renameEvts[renEvtId]) g_renameEvts[renEvtId].onEvent(g_renameEvts[renEvtId]);
            break;
        }
        return false;
    }

    this.isImageFile = function(file) {
        if(file.mimetype) {
             return (file.mimetype.indexOf("image") != -1 && file.mimetype.indexOf("bmp") == -1);
        } else if(file.name) {
            var ext = file.name.substr(file.name.length-4);
            if(ext) {
                ext = ext.toLowerCase();
                if(ext.match(".png") || ext.match(".jpg") || ext.match("jpeg") || ext.match(".gif")) {
                    return true;
                }
            }
        }
        return false;
    }
    
    this.isDocFile = function(file) {
        if(file.mimetype) {
            for(var i = 0; i < DOC_MIME_TYPES.length; ++i) {
                if(file.mimetype == DOC_MIME_TYPES[i])
                    return true;
            }
        }
        var name = file.filename || file.name;
        for(var i = 0; i < DOC_EXTENSIONS.length; ++i) {
            if(name.length > DOC_EXTENSIONS[i].length) {
                if(name.substr(name.length-DOC_EXTENSIONS[i].length-1) == ('.'+DOC_EXTENSIONS[i])) {
                    return true;
                }
            }
        }
        return false;
    };

    this.isVideoFile = function(file) {
        if(file.mimetype) {
            if(file.mimetype.substr(0,6) == 'video/' && file.stream) {
                if(CE.CEUI.getCurSvc(file).xcodeStream != 'never')
                    return true;
            }
            for(var i = 0; i < FLASH_VIDEO_MIME_TYPES.length; ++i) {
                if(FLASH_VIDEO_MIME_TYPES[i] == file.mimetype) {
                    return true;
                }
            }
        }
        return false;
    }

    this.isAudioFile = function(file) {
        if(file.mimetype) {
            for(var i = 0; i < FLASH_AUDIO_MIME_TYPES.length; ++i) {
                if(FLASH_AUDIO_MIME_TYPES[i] == file.mimetype) {
                    return true;
                }
            }
        }
        return false;
    }
    
    function onVideoComplete(hideCb, nextCb) {
        setTimeout(function() {
            if(hideCb)
                hideCb();
            
            // Queue the video for transcoding
            var svc = CE.CEUI.getCurSvc(CE.CEUI.g_videoTranscodeFile);
            CE.CEUI.setTranscodeQueued(CE.CEUI.g_videoTranscodeFile, svc);

            CE.CEU.svc.asyncRPC('POST', 'featureCommand', ['feature','xcode', 'command','addQueue', 'deviceid',svc.deviceid,
                                'serviceid',svc.serviceid, 'fileid',CE.CEUI.g_videoTranscodeFile.fileid, 'tasktype','stream', 'priority','-999']);

            var msg = CE.dce('span');
            msg.appendChild(CE.STRTAB.lookupel('cexcode.queued.msg1'));
            msg.appendChild(CE.dca({onEvent:function(){window.location.replace('settings.html#jd0hZpEM3gX6kc5lEJnm'); return false;}})).appendChild(CE.STRTAB.lookupel('cexcode.queued.msg2'));
            msg.appendChild(CE.STRTAB.lookupel('cexcode.queued.msg3'));

            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('cexcode.queued.title'), msg,
                                        [{name:'ok',label:CE.STRTAB.lookup('button.ok'), callback:nextCb}]);
            dlg.show();
            
            // Display the hint that we're processing media
            checkMediaProcessing();
        }, 200);
    }
    
    this.onSlidePreviewVideoComplete = function() {
        if(CE.CEUI.g_videoSlideTranscode && CE.CEUI.g_videoTranscodeFile) {
            that.g_videoSlideTranscode = false;
            onVideoComplete(null, function(){
                if(g_slideshowObj)
                    g_slideshowObj.pause(false, true);
                return true;
            });
        } else {
            if(g_slideshowObj)
                g_slideshowObj.pause(false, true);
        }
    };

    this.onPreviewVideoComplete = function() {
        if(that.g_videoPrevTranscode && CE.CEUI.g_videoTranscodeFile) {
            that.g_videoPrevTranscode = false;
            
            onVideoComplete(function(){
                if(g_preview)
                    g_preview.hide();
            });
        }
    };

    this.onHoverPreviewVideoComplete = function() {
        if(g_videoHoverTranscode && CE.CEUI.g_videoTranscodeFile) {
            g_videoHoverTranscode = false;
            
            onVideoComplete(function(){
                closeHoverPreviewVideo();
            });
        }
    };
    
    this.hasVideoSupport = function() {
        if(g_videoSupport) {
            return g_videoSupport;
        }
        
        if(CE.CEUI.g_options['disablehtml5video'] != '1') {
            var v = document.createElement('video');
            if(!!v.canPlayType) {
                var canPlay = v.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
                if(canPlay == 'maybe' || canPlay == 'probably') {
                    g_videoSupport = 'html5';
                    return g_videoSupport; // supports html 5 video
                }
            }
        }
        
        // Flash support?
        var version = CE.CEU.getFlashVersion();
        if(!(version[0] < 9 || (version[0] == 9 && version[1] < 1 && version[2] < 115))) {
            g_videoSupport = 'flash';
            return g_videoSupport;
        }
        
        g_videoSupport = 'none';
        return g_videoSupport;
    };
    
    this.createVideoEl = function(parent, id, videoUrl, width, height, autoPlay, muted, endEvent, controls, wmode, image) {
        if(CE.CEUI.hasVideoSupport() == 'none') {
            // Output placeholder error text
            (parent.appendChild(CE.dce('div',null,'flashupgrade_cont'))).appendChild(CE.CEU.getFlashUpgradeHTML());
            return;
        }
        
        if(g_videoSupport == 'flash') {
            var so = new SWFObject(CE.STRTAB.lookup('flashbase')+'flvplayer.swf', id, width, height, '8');
            so.addParam('allowfulqualitylscreen', 'high');
            so.addParam('allowfullscreen', 'true');
            so.addVariable('enablejs', 'true');
            so.addVariable('javascriptid', id);
            so.addVariable('file', videoUrl);
            so.addVariable('displayheight', height);
            so.addVariable('autostart', autoPlay ? 'true' : 'false');
            if(controls)
                so.addVariable('skin', CE.STRTAB.lookup('flashbase')+'stijl.swf');
            else
                so.addVariable('controlbar', 'none');
            so.addVariable('mute', muted?'true':'false');
            if(wmode)
                so.addParam('wmode', wmode);
            if(image)
                so.addVariable('image', image);
            so.write(parent);
            return CE.CEU.$(id);
        } else {
            var vid = parent.appendChild(CE.dce('video', id, 'cevideo'));
            if(controls)
                vid.controls = 'true';
            vid.setAttribute('width',  width);
            vid.setAttribute('height', height);
            vid.setAttribute('src',    videoUrl);
            if(endEvent)
                vid.addEventListener('ended', endEvent, true);
            if(muted) {
                vid.addEventListener('canplay', function() {
                    vid.muted = true;
                }, false);
            }
            if(autoPlay)
                vid.play();
            return vid;
        }
    };
    
    function onHoverPreviewVideo(co) {
        if(CE.CEUI.g_options['disablehoverprev'] == '1' || navigator.platform == 'iPad')
            return;
        if(CE.CEUI.hasVideoSupport() == 'none')
            return;

        var svc = CE.CEUI.getCurSvc(co.file);
        var url;
        if(co.file.stream && svc.xcodeStream != 'never') {
            url = CE.CEUI.getDataStreamPrefix(co.file) + co.file.stream + "/" + encodeURIComponent(co.file.name) + ".mp4";
            if(co.file.streamtype != 'full' && svc.xcodeStream == 'always') {
                if(CE.CEUI.isTranscodeQueued(co.file,svc) && !co.file.streamRefreshed) {
                    CE.CEUI.refreshFileStream(co.file, svc, function(){onHoverPreviewVideo(co);});
                    return;
                } else if(!CE.CEUI.isTranscodeQueued(co.file,svc)) {
                    // Queue the full transcode after the preview
                    g_videoHoverTranscode = true;
                    CE.CEUI.g_videoTranscodeFile = co.file;
                }
                co.file.streamRefreshed = false;
            } else {
                g_videoHoverTranscode = false;
            }
        } else {
            url = CE.CEUI.getDataStreamPrefix(co.file) + co.file.fileid + "/" + encodeURIComponent(co.file.name);
        }
        
        var outerDivId = (co.go && co.go.grd == 'cesrc_list') ? 'cesrccnt_'+co.id : 'cecnt_'+co.id;
        if(co.file.deviceid && co.file.serviceid)    outerDivId += '_' + co.file.deviceid + '_' + co.file.serviceid;
        var outerDiv = CE.CEU.$(outerDivId);
        
        if(outerDiv) {
            var iconDiv = null;
            var divs    = outerDiv.getElementsByTagName("div");
            for(var i = 0; i < divs.length; ++i) {
                if(divs[i].className.substr(0,8) == "itemicon") {
                    iconDiv = divs[i];
                    break;
                }
            }
            if(iconDiv) {
                var videoDiv = CE.dce('div', 'itemvideoprev', 'itemvideoprev');
                iconDiv.appendChild(videoDiv);
                var vid = CE.CEUI.createVideoEl(videoDiv, 'hover_flv', url, videoDiv.offsetWidth, videoDiv.offsetHeight,
                                                true, true, CE.CEUI.onHoverPreviewVideoComplete, false, 'transparent');

                CE.CEU.attachEvent(videoDiv, 'mouseout', co);
                CE.CEU.attachEvent(vid,      'mouseout', co);
                CE.CEU.attachEvent(vid,      'mouseup',  co);
            }
        }
    }
    
    function closeHoverPreviewVideo(disable) {
        if(disable)
            CE.CEUI.g_videoPrevDisabled = true;
        
        clearTimeout(g_videoHoverTimer);
        g_videoHoverTimer = null;
        
        var prevDiv = CE.CEU.$("itemvideoprev");
        if(prevDiv) {
            CE.rac(prevDiv);
            prevDiv.parentNode.removeChild(prevDiv);
        }
    }
    
    this.cancelPrintJobs = function() {
        if(CE.CEU.structlen(g_selfiles['celist']) == 0)
            return;
        
        CE.CEU.promptYesNo(CE.STRTAB.lookup('print.queue.cancelsel.title'), CE.STRTAB.lookup('print.queue.cancelsel.msg'),
        function() {
            CE.CEU.showLoadingAni(true);
            var count = 0;
            
            for(var gid in g_selfiles['celist']) {
                var job  = g_selfiles['celist'][gid];
                var args = ['deviceid',job.svc.deviceid, 'serviceid',job.svc.serviceid, 'jobid',job.jobid];
                
                ++count;
                CE.CEU.svc.asyncRPC('PRINT', 'cancelJob', args, function(){--count;},
                                    function(r,d,m){--count; onGenericFailure(r,d,m);});
            }
            
            CE.CEU.poll(1000, function(){return (count==0);},
            function() {
                CE.CEU.showLoadingAni(false);
                CE.CEUI.reloadContentCWD();
            });
            
            return true;
        });
    };
    
    this.reprintPrintJobs = function() {
        if(CE.CEU.structlen(g_selfiles['celist']) == 0)
            return;
        
        CE.CEU.promptYesNo(CE.STRTAB.lookup('print.queue.reprintsel.title'), CE.STRTAB.lookup('print.queue.reprintsel.msg'),
        function() {
            CE.CEU.showLoadingAni(true);
            var count = 0;
            
            for(var gid in g_selfiles['celist']) {
                var job  = g_selfiles['celist'][gid];
                var args = ['deviceid',job.svc.deviceid, 'serviceid',job.svc.serviceid, 'jobid',job.jobid];
                
                ++count;
                CE.CEU.svc.asyncRPC('PRINT', 'reprintJob', args, function(){--count;},
                                    function(r,d,m){--count; onGenericFailure(r,d,m);});
            }
            
            CE.CEU.poll(1000, function(){return (count==0);},
            function() {
                CE.CEU.showLoadingAni(false);
                CE.CEUI.reloadContentCWD();
            });
            
            return true;
        });
    };
    
    function onPrintDoc(d) {
        var printer = g_svcmap[ d.printers.options[d.printers.selectedIndex].value ];
        var fileSvc = CE.CEUI.getCurSvc(d.f);
        var fileUrl = 'pp://' + fileSvc.deviceid + ':' + fileSvc.serviceid + ':' + d.f.fileid;
        var args    = ['deviceid',          printer.deviceid,
                       'serviceid',         printer.serviceid,
                       'filedurl',          fileUrl,
                       'numcopies',         (d.numCopies.value != '') ? parseInt(d.numCopies.value) : '1',
                       'collate',           d.colCheck.checked ? '1' : '0',
                       'pageorientation',   CE.hCN(d.orientPortrait,'selected') ? '3' : '4',
                       'fittopage',         d.fitCheck.checked ? '1' : '0',
                       'scaling',           parseInt(d.scale)];
        
        if(d.pageRadioFrom.checked && pageFrom.value != '' && pageTo.value != '') {
            args.push('pagerange', parseInt(pageFrom.value) + ':' + parseInt(pageTo.value));
        }
        
        CE.CEU.svc.asyncRPC('PRINT', 'printFile', args, null, onGenericFailure);
        
        return true;
    }
    
    function onShowPrintDoc(d) {
        var d = {
            f: d.f,
            printers: null,
            numCopies: null,
            colCheck: null,
            pageRadioAll: null,
            pageRadioFrom: null,
            pageFrom: null,
            pageTo: null,
            papers: null,
            papersLabel: null,
            orientPortrait: null,
            orientLandscape: null,
            scale: null,
            fitCheck: null
        };
        
        var content = CE.dce('div');
        var table   = (content.appendChild(CE.dce('table',null,'ceprinttable'))).appendChild(CE.dce('tbody'));
        
        // Printer
        var tr = table.appendChild(CE.dce('tr'));
        (tr.appendChild(CE.dce('td',null,'cerightalign'))).appendChild(CE.STRTAB.lookupel('print.dlg.printer'));
        d.printers = (tr.appendChild(CE.dce('td'))).appendChild(CE.dce('select'));
        for(var s in g_svcmap) {
            if(g_svcmap[s].type.indexOf(':printer') != -1) {
                d.printers.appendChild(CE.dco(g_svcmap[s].name,s));
            }
        }
        
        // Copies
        tr = table.appendChild(CE.dce('tr'));
        (tr.appendChild(CE.dce('td',null,'cerightalign'))).appendChild(CE.STRTAB.lookupel('print.dlg.copies'));
        var td = tr.appendChild(CE.dce('td'));
        d.numCopies = CE.dce('input');
        d.numCopies.type = 'text';
        d.numCopies.value = '1';
        d.numCopies.size = 4;
        td.appendChild(d.numCopies);
        d.colCheck = CE.dce('input','colcheck','ceprintcheck');
        d.colCheck.type = 'checkbox';
        d.colCheck.checked = true;
        td.appendChild(d.colCheck);
        (td.appendChild(CE.dcl('colcheck'))).appendChild(CE.STRTAB.lookupel('print.dlg.collated'));
        
        // Pages
        tr = table.appendChild(CE.dce('tr'));
        (tr.appendChild(CE.dce('td',null,'cerightalign'))).appendChild(CE.STRTAB.lookupel('print.dlg.pages'));
        td = tr.appendChild(CE.dce('td'));
        var tmpdiv = td.appendChild(CE.dce('div'));
        d.pageRadioAll = CE.dce('input','pageradioall','ceprintcheck');
        d.pageRadioAll.type = 'radio';
        d.pageRadioAll.name = 'pageopt';
        d.pageRadioAll.checked = true;
        tmpdiv.appendChild(d.pageRadioAll);
        (tmpdiv.appendChild(CE.dcl('pageradioall'))).appendChild(CE.STRTAB.lookupel('print.dlg.pagesall'));
        tmpdiv = td.appendChild(CE.dce('div'));
        d.pageRadioFrom = CE.dce('input','pageradiofrom','ceprintcheck');
        d.pageRadioFrom.type = 'radio';
        d.pageRadioFrom.name = 'pageopt';
        tmpdiv.appendChild(d.pageRadioFrom);
        (tmpdiv.appendChild(CE.dcl('pageradiofrom'))).appendChild(CE.STRTAB.lookupel('print.dlg.pagesfrom'));
        d.pageFrom = CE.dce('input');
        d.pageFrom.type = 'text';
        d.pageFrom.value = '1';
        d.pageFrom.size = 4;
        tmpdiv.appendChild(d.pageFrom);
        tmpdiv.appendChild(CE.STRTAB.lookupel('print.dlg.pagesto'));
        d.pageTo = CE.dce('input');
        d.pageTo.type = 'text';
        d.pageTo.value = '1';
        d.pageTo.size = 4;
        tmpdiv.appendChild(d.pageTo);
        
        // Paper Size
        tr = table.appendChild(CE.dce('tr'));
        (tr.appendChild(CE.dce('td',null,'cerightalign'))).appendChild(CE.STRTAB.lookupel('print.dlg.paper'));
        td = tr.appendChild(CE.dce('td'));
        d.papers = td.appendChild(CE.dce('select'));
        d.papers.appendChild(CE.dco('US Letter'));
        td.appendChild(CE.dctn(' '));
        d.papersLabel = td.appendChild(CE.dce('span'))
        d.papersLabel.appendChild(CE.STRTAB.lookupel('print.dlg.paper.0.size'));
        
        // Orientation
        tr = table.appendChild(CE.dce('tr'));
        (tr.appendChild(CE.dce('td',null,'cerightalign'))).appendChild(CE.STRTAB.lookupel('print.dlg.orientation'));
        td = tr.appendChild(CE.dce('td',null,'ceopt-group'));
        d.orientPortrait = td.appendChild(CE.dca({onEvent:function(){CE.CEU.checkOpt('orient_portrait');}},'orient_portrait','cebutton ceopt ceprintbut selected'));
        d.orientPortrait.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'orientation-portrait.png'));
        td.appendChild(CE.dctn(' '));
        d.orientLandscape = td.appendChild(CE.dca({onEvent:function(){CE.CEU.checkOpt('orient_landscape');}},'orient_landscape','cebutton ceopt ceprintbut'));
        d.orientLandscape.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'orientation-landscape.png'));
        td.appendChild(CE.STRTAB.lookupel('print.dlg.orientation.scale'));
        d.scale = CE.dce('input');
        d.scale.type = 'text';
        d.scale.value = '100';
        d.scale.size = 3;
        td.appendChild(d.scale);
        td.appendChild(CE.STRTAB.lookupelh('print.dlg.orientation.scale.post'));

        td = (table.appendChild(CE.dce('tr'))).appendChild(CE.dce('td'));
        td.colSpan = 2;
        td.appendChild(CE.dce('hr'));
        
        // Scale to fit
        tr = table.appendChild(CE.dce('tr'));
        td = tr.appendChild(CE.dce('td'));
        td = tr.appendChild(CE.dce('td'));
        d.fitCheck = CE.dce('input','fitcheck','ceprintcheck');
        d.fitCheck.type = 'checkbox';
        d.fitCheck.checked = true;
        td.appendChild(d.fitCheck);
        (td.appendChild(CE.dcl('fitcheck'))).appendChild(CE.STRTAB.lookupel('print.dlg.scalefit'));
        
        
        var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('button.print'), content, 
                            [{name:'print', label:CE.STRTAB.lookup('button.print'), callback:function(){return onPrintDoc(d);}},
                             {name:'cancel', label:CE.STRTAB.lookup('button.cancel'), callback:function(){dlg.hide();}}]);
        dlg.show();
    }
    
    function hasParent(el, parentIds) {
        while(el) {
            for(var i = 0; i < parentIds.length; ++i) {
                if(el.id == parentIds[i])
                    return true;
            }
            if(el.id == 'cemain')
                return false;
            el = el.parentNode;
        }
        return false;
    }
    
    function chooseDragOpt(e) {
        if(CE.CEU.$('cedragmenu'))
            CE.CEU.$('cedragmenu').parentNode.removeChild(CE.CEU.$('cedragmenu'));
        
        for(var i = 1; i <= 2; ++i) {
            CE.aCN('dragmenubut'+i+'_copy', 'hidden');
            CE.aCN('dragmsg'    +i+'_copy', 'hidden');
            CE.aCN('dragmenubut'+i+'_move', 'hidden');
            CE.aCN('dragmsg'    +i+'_move', 'hidden');
            
            CE.rCN('dragmenubut'+i+'_'+e.opt, 'hidden');
            CE.rCN('dragmsg'    +i+'_'+e.opt, 'hidden');
        }

    	e.cancelBubble = true;
    	if (e.stopPropagation) e.stopPropagation();
    	return true;
    }
    
    this.showDragMenu = function(e, butnum) {
        if(!e)         e        = window.event;
        if(!e.target)  e.target = e.srcElement;
        closeContextMenu(e);
        if(CE.CEU.$('cedragmenu')) {
            if(hasParent(CE.CEU.$('cedragmenu'),['dragmenubut'+butnum]) && !hasParent(e.target,['cedragmenu'])) {
                CE.CEU.$('cedragmenu').parentNode.removeChild(CE.CEU.$('cedragmenu'));
                return;
            } else if(!hasParent(e.target,['cedragmenu'])) {
                CE.CEU.$('cedragmenu').parentNode.removeChild(CE.CEU.$('cedragmenu'));
            } else {
                return;
            }
        }
        if(e.target && (CE.hCN(e.target,'ceopt') || CE.hCN(e.target,'ceopt-radio')))
            return;
        
        var opts = CE.CEU.$('dragmenubut'+butnum).appendChild(CE.dce('div','cedragmenu','ceopts'));
        (opts.appendChild(CE.dce('div',null,'ceopttitle'))).appendChild(CE.STRTAB.lookupel('view.dragmenu.title'));
        
        var opt = opts.appendChild(CE.dce('div',null,'ceopt ' + (CE.hCN('dragmenubut1_copy','hidden')?'':'selected')));
        opt.appendChild(CE.dce('div',null,'ceopt-radio'));
        opt.appendChild(CE.STRTAB.lookupel('view.dragmenu.copy'));
        CE.CEU.attachEvent(opt, 'click', {onEvent:chooseDragOpt,opt:'copy'});

        opt = opts.appendChild(CE.dce('div',null,'ceopt ' + (CE.hCN('dragmenubut1_move','hidden')?'':'selected')));
        opt.appendChild(CE.dce('div',null,'ceopt-radio'));
        opt.appendChild(CE.STRTAB.lookupel('view.dragmenu.move'));
        CE.CEU.attachEvent(opt, 'click', {onEvent:chooseDragOpt,opt:'move'});
        
        CE.CEU.$('dragmenubut'+butnum).blur();
    };
    
    function closeContextMenu(ev) {
        var menu = CE.CEU.$('cecontextmenu');
        if(menu)
            menu.parentNode.removeChild(menu);
        
        if(ev && ev.target) {
            if(!hasParent(ev.target,['cemylib_ss_opts','cesearch_dropper'])) {
                CE.aCN('cemylib_ss_opts', 'hidden');
            }
            if(!hasParent(ev.target,['cesortselect','cetool_sort'])) {
                CE.aCN('cesortselect', 'hidden');
                CE.CEU.$("cetoolbar").style.zIndex = 1;
            }
            if(!hasParent(ev.target,['cetool_otheracts_pop','cetool_otheracts'])) {
                CE.aCN('cetool_otheracts_pop', 'hidden');
                CE.CEU.$("cetoolbar").style.zIndex = 1;
            }
            if(!hasParent(ev.target,['ceflexidrop_but'])) {
                CE.aCN('ceflexiopts', 'hidden');
            }
            if(!hasParent(ev.target,['ceflexidropsrc_but'])) {
                CE.aCN('ceflexioptssrc', 'hidden');
            }
            if(CE.CEU.$('cedragmenu') && !hasParent(ev.target,['dragmenubut1','dragmenubut2'])) {
                CE.CEU.$('cedragmenu').parentNode.removeChild(CE.CEU.$('cedragmenu'));
            }
        }
    }
    
    function showContextMenu(co, evt) {
        closeContextMenu(evt);
        if(co.go.grd != 'celist')
            return;
            
        var menu = document.body.appendChild(CE.dce('div', 'cecontextmenu'));
        if(evt.pageX || evt.pageY) {
            menu.style.left = evt.pageX + 'px';
            menu.style.top  = evt.pageY + 'px';
        } else {
            menu.style.left = (document.documentElement.scrollLeft + evt.clientX) + 'px';
            menu.style.top  = (document.documentElement.scrollTop  + evt.clientY) + 'px';
        }
        
        if(CE.CEU.structlen(g_selfiles[co.go.grd]) == 0) {
            var actions = getActionTypesForFile(co.file, co.slidedir);
            for(var i = 0; i < actions.length; ++i) {
                (function(i){
                    var item = menu.appendChild(CE.dce('div',null,'cecontextitem'));
                    item.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-'+actions[i]+'-dark-small.png'));
                    item.appendChild(CE.STRTAB.lookupel('view.hint.'+actions[i]));
        
                    var e = {onEvent: function(eh,evt,tel) {
                                co.onAction(co, actions[i], evt, tel);
                            	evt.cancelBubble = true;
                            	if (evt.stopPropagation) evt.stopPropagation();
                             }, 
                             co: co,
                             action: actions[i]};
                    CE.CEU.attachEvent(item, 'mouseup', e);
                })(i);
            }
        } else {
            var file  = CE.CEU.structhead(g_selfiles[co.go.grd]);
            var trash = getTrashActionForFiles(g_selfiles[co.go.grd]);
            if(trash) {
                var item = menu.appendChild(CE.dce('div',null,'cecontextitem'));
                item.appendChild(CE.dci(CE.STRTAB.lookup('imgbase')+'icon-'+trash+'-dark-small.png'));
                item.appendChild(CE.STRTAB.lookupel('view.delsel.'+trash+'.title'));
                CE.CEU.attachEvent(item, 'mouseup', {onEvent:CE.CEUI.deleteSelected});
                
            }
        }
    }
    
    function isToolbarButtonValid(butEl) {
        if(CE.hCN(butEl,'cedisabled') || CE.hCN(butEl,'selected'))
            return false;
        
        while(butEl) {
            if(CE.hCN(butEl,'hidden'))
                return false;
            if(butEl.style.visibility && butEl.style.visibility.indexOf('hidden') != -1)
                return false;
            if(butEl.style.display && butEl.style.display.indexOf('none') != -1)
                return false;
            butEl = butEl.parentNode;
            if(butEl.id == 'cetoolbar')
                break;
        }
        
        return true;
    }
    
    function appendContextMenuItem(baseEl, menu) {
        var item = menu.appendChild(CE.dce('div',null,'cecontextitem noicon'));
        
        var text = baseEl.innerText || baseEl.textContent;
        item.appendChild(CE.dctn(text));
        
        CE.CEU.attachEvent(item, 'mouseup', {onEvent:
        function(co, evt) {
            evt = CE.CEU.shallowCopy(evt);
            evt.target = baseEl;
            baseEl.onclick(evt);
        }});
    }
    
    function showDefaultContextMenu(co, evt) {
        if((evt.which && evt.which != 1) || (evt.button && evt.button != 1)) {
            closeContextMenu(evt);
            cancelSelRect();
    
            var menu = document.body.appendChild(CE.dce('div', 'cecontextmenu'));
            var top, left;
            if(evt.pageX || evt.pageY) {
                top  = evt.pageY;
                left = evt.pageX;
            } else {
                top  = document.documentElement.scrollTop  + evt.clientY;
                left = document.documentElement.scrollLeft + evt.clientX;
            }
            
            var buttons = CE.CEU.$('cetoolbar').getElementsByTagName('a');
            var num     = 0;
            if(buttons && buttons.length) {
                for(var i = 0; i < buttons.length; ++i) {
                    if(!isToolbarButtonValid(buttons[i]))
                        continue;
                    
                    var opts = CE.getByClass(buttons[i], 'div', 'ceopt');    
                    for(var o = 0; o < opts.length; ++o) {
                        if(!CE.hCN(opts[o],'inmenu'))
                            continue;
                        appendContextMenuItem(opts[o], menu);
                        ++num;
                    }
                    
                    if(!buttons[i].onclick)
                        continue;
                    if(!CE.hCN(buttons[i],'inmenu'))
                        continue;
                        
                    appendContextMenuItem(buttons[i], menu);
                    ++num;
                }
            }
    
            if(num > 0) {
                var main = CE.CEU.$('cemain');
                if((left+menu.offsetWidth) > main.offsetWidth)
                    left = (main.offsetWidth-menu.offsetWidth);
                if((top+menu.offsetHeight) > main.offsetHeight)
                    top = (main.offsetHeight-menu.offsetHeight);
                menu.style.top  = top + 'px';
                menu.style.left = left + 'px';
                
            	evt.cancelBubble = true;
            	if (evt.stopPropagation) evt.stopPropagation();
            } else {
                closeContextMenu();
            }
        }
    }

    function onEventContent(co, evt, tel) {
        //CE.CEDBG.println('CEUI: onEventContent('+evt.type+'): co: '+co.name+' ('+co.id+')');
        // We may receive mousedown, mouseup, and click events here...
        switch(evt.type) {
        case 'mouseup':
            if(g_selrect && g_selrect.touched)
                break;
            closeHoverPreviewVideo();
            // mouseup may be a click if we didn't drag
            if(g_diddrag) {
                break;
            }
            if((evt.which && evt.which != 1) || (evt.button && evt.button != 1)) {
                showContextMenu(co, evt);
            	evt.cancelBubble = true;
            	if (evt.stopPropagation) evt.stopPropagation();
                break;
            }
            // fall through...
        case 'click':
            closeContextMenu();
            if(g_curdrag) {
                g_curdrag = null;
            }
            if(co.file) {
                if(CE.CEUI.isDescendableFile(co.file)) {
                    if(co.file.type == FILE_TYPE_SLIDEALBUM && co.file.album && co.go && co.go.grd && co.go.grd == "celist") {
                        // They clicked on a slideshow album folder
                        that.g_cursvc   = null;
                        that.g_curalbum = co.file.album;
                        co.go.album     = co.file.album;
                        pushContentStack(co);
                        //activateAlbum(co.file.album.albumid, g_sharemap[co.file.album.albumid]?true:false);
                    } else if(co.file.type == FILE_TYPE_SLIDEALBUM && co.file.album && co.go && co.go.grd && co.go.grd == "cesrc_list") {
                        // They clicked on a slideshow album folder in the copy pane
                        g_srcsvc = null;
                        co.go.album = co.file.album;
                        pushContentStack(co);
                    } else {
                        // They clicked on a directory, so we need to descend
                        if(co.file.svc) {
                            if(co.go && co.go.grd && co.go.grd == "cesrc_list") {
                                g_srcsvc        = co.file.svc;
                            } else if(co.file.svc.albumid) {
                                if(CE.CEUI.isFilesIShareListing()) {
                                    // Clicking on an album in Files I Share; show the sharing pane when descending
                                    return onActionContent(co, 'share', evt, tel);
                                } else {
                                    that.g_cursvc   = null;
                                    that.g_curalbum = co.file.svc;
                                }
                            } else if(!that.g_curalbum || CE.CEU.isLoggedIn()) {
                                that.g_cursvc   = co.file.svc;
                                that.g_curalbum = null;
                            }
                            if(co.go && co.file.svc)
                                co.go.svc = co.file.svc;
                        }
                        pushContentStack(co);
                    }
                } else if(!co.go || !co.go.nopreview) {
                    // if it was a broken link...
                    if(co.broken) break;
                    if(CE.CEUI.isFilesIShareListing()) {
                        // Clicking on a single-file album in Files I Share; show the sharing pane before previewing
                        onActionContent(co, 'share', evt, tel);
                    }
                    if(canDoPreview(co.file) && g_curpage['celist']) {
                        trackEvent('Open', null, co.file);
                        for(var i=0; i<g_curpage['celist'].length; i++) {
                            if(g_curpage['celist'][i].fileid == co.id) {
                                if(CE.CEUI.isImageFile(g_curpage['celist'][i])) {
                                    closeHoverPreviewVideo(true);
                                    g_slideshow.showPreview(evt, g_curpage['celist'], i);
                                } else if(CE.CEUI.isVideoFile(g_curpage['celist'][i])) {
                                    closeHoverPreviewVideo(true);
                                    g_preview.showPreview(evt, g_curpage['celist'], i);
                                } else if(CE.CEUI.isAudioFile(g_curpage['celist'][i])) {
                                    playAudioInTray(g_curpage['celist'], i);
                                } else if(CE.CEUI.isDocFile(g_curpage['celist'][i])) {
                                    var docprev = new DocumentPreview(g_curpage['celist'][i]);
                                    docprev.show();
                                } else {
                                    closeHoverPreviewVideo(true);
                                    g_preview.showPreview(evt, g_curpage['celist'], i);
                                }
                                break;
                            }
                        }
                    }
                }
            }
            break;
        case 'mousedown':
            closeContextMenu();
            // OK, we may be starting a drag operation
            if((!evt.which || evt.which == 1) && (!evt.button || evt.button == 1))
                startDrag(co, evt, tel);
            break;
        case 'mouseover':
            if(!CE.CEUI.g_videoPrevDisabled && !g_videoHoverTimer && CE.CEUI.isVideoFile(co.file)) {
                g_videoHoverTimer = setTimeout(function() {
                    onHoverPreviewVideo(co);
                }, 1);
            }
            break;
        case 'mouseout':
            if(g_videoHoverTimer) {
                var outerDivId = (co.go && co.go.grd == 'cesrc_list') ? 'cesrccnt_'+co.id : 'cecnt_'+co.id;
                if(co.file.deviceid && co.file.serviceid)    outerDivId += '_' + co.file.deviceid + '_' + co.file.serviceid;
                var outerDiv = CE.CEU.$(outerDivId);
                var iconDiv  = null;
                var divs     = outerDiv.getElementsByTagName("div");
                for(var i = 0; i < divs.length; ++i) {
                    if(divs[i].className.substr(0,8) == "itemicon") {
                        iconDiv = divs[i];
                        break;
                    }
                }
                if(iconDiv) {
                    var itemPos = CE.CEU.getAbsPos(iconDiv);
                    if(evt.clientX < itemPos.x || evt.clientY < itemPos.y ||
                       evt.clientX > (itemPos.x+iconDiv.offsetWidth-2) ||
                       evt.clientY > (itemPos.y+iconDiv.offsetHeight-2)) {
                        closeHoverPreviewVideo();
                    }
                }
            }
            break;
        }
        return false;
    }

    function pushContentStack(co) {
        trackEvent('Open', null, co.file);
        // Ugh... Hack...
        if(co.go && co.go.grd != 'celist') {
            if(co.go.grd == 'cesrc_list') {
                bcSrcAppend(co);           
                co.go.pageoffset = 0;
            }
            reloadContent(co.id, co.go, co.file);
        } else {
            // Add the object to the breadcrumb
            bcAppend(co);
            // Load the first page contained in this object
            g_curpageoffset = 0;
            if(co.go)
                co.go.pageoffset = 0;
            reloadContent(co.id, co.go, co.file);
        }
    }

    function onFileFailureMessagesSuccess(r)  {
        if(r.messages) {
            var msgbody = CE.CEU.$("driveerrormsgs");
            var messages = r.messages;
            var lastcode = 0;

            CE.rac(msgbody);
            for(var curmsgnum = messages.length; curmsgnum > 0; curmsgnum --) {
                var div = CE.dce("div");
                if(lastcode != messages[curmsgnum-1].msgcode) {
                    appendMessageToElem(div, messages, curmsgnum);
                    msgbody.appendChild(div);
                }
                lastcode = messages[curmsgnum-1].msgcode;
            }
        }
    }

    function onFileFailureMessagesFailure(r) {

        // do nothing. its too late.
    }

    function onListFilesFailure(r, data, method) {
        CE.CEU.showLoadingAni(false);

        if(!CE.CEU.isLoggedIn()  &&
           window.location.pathname.indexOf('album.html') == -1 &&
           window.location.pathname.indexOf('/share/') == -1) {
            //alert('LOGGING OUT -- LIST FILES FAILURE!');
            window.location.replace("index.html#logout");
        }
        var e = r["HB-EXCEPTION"];
        if(e && e.ecode) {
            switch(e.ecode) {
            case 815:
            case 816:
                CE.rCN("cepayment", "hidden");
                return true;
            }
        }
        if(data && data.grd == 'celist') {
            var args = [];

            if(that.g_curalbum) {
                showEmptyContentDiv("albumdriveerror");             
                
                args.push("deviceid");
                args.push(that.g_curalbum.root.deviceid);
                args.push("serviceid");
                args.push(that.g_curalbum.root.serviceid);
         
            } else if(that.g_cursvc) {
                if(that.g_cursvc.readonly && method=="searchFiles") {
                } else {
                    showEmptyContentDiv("driveerror");
                    args.push("deviceid");
                    args.push(that.g_cursvc.deviceid);
                    args.push("serviceid");
                    args.push(that.g_cursvc.serviceid);
                }
            } else if(window.location.pathname.indexOf('album.html') == -1 &&
                      window.location.pathname.indexOf('/share/') == -1) {
                showEmptyContentDiv("albumdriveerror");             
            }
            if(args.length == 4) {

                args.push("cmd");
                args.push("getmessages");
                CE.CEU.svc.asyncRPC('POST', 'sendServiceCommand', args,
                                    onFileFailureMessagesSuccess, onFileFailureMessagesFailure);
            }
        }
        //return onGenericFailure(r, data, method);
    }
    
    function resortFiles(files, sortType) {
        if(sortType == '+name') {
            files.sort(function(a, b) {
                var aname = a.filename || a.name;
                var bname = b.filename || b.name;
                return natcompare(aname.toLowerCase(), bname.toLowerCase());
            });
        } else if(sortType == '-name') {
            files.sort(function(a, b) {
                var aname = a.filename || a.name;
                var bname = b.filename || b.name;
                return natcompare(bname.toLowerCase(), aname.toLowerCase());
            });
        }
    }
    
    function searchServiceComplete(r, d) {
        var svcKey  = d.deviceid + ':_:' + d.serviceid + (d.aid ? (':'+d.aid) : '');
        var results = g_searchResults[d.go.grd][svcKey];
        CE.CEDBG.println("searchServiceComplete: " + svcKey);
        
        if(r.files && r.files.length > 0) {
            resortFiles(r.files, d.sortcrit);
            // Remember the files returned by this service
            for(var i = 0; i < r.files.length; ++i) {
                r.files[i].deviceid  = d.deviceid;
                r.files[i].serviceid = d.serviceid;
                r.files[i].svc       = d.svc;
                if(!r.files[i].svc && d.aid)
                    r.files[i].svc = g_sharemap[d.aid].root ? g_sharemap[d.aid].root : (g_sharemap[d.aid].files ? g_sharemap[d.aid].files[0] : null);
                if(!r.files[i].svc)
                    r.files[i].svc = g_svcmap[svcKey];
                results.files.push(r.files[i]);
            }
        } else {
            // Silently ignore services that fail searching
            results.eofhit  = true;
        }
        
        if(r.files && r.files.length < d.numRequested)
            results.eofhit  = true;

        results.totalcount = r.totalcount;
        results.pending    = false;
    }
    
    function searchService(go, pageOffset, sortCrit, maxCount, deviceId, serviceId, pid, aid, cuzFile) {
        var svcKey  = deviceId + ':_:' + serviceId + (aid ? (':'+aid) : '');
        var results = g_searchResults[go.grd][svcKey];
        if(!results) {
            g_searchResults[go.grd][svcKey] = {};
            results = g_searchResults[go.grd][svcKey];
            results.offset = 0;
            results.files  = [];
        }

        if(!results.eofhit) {
            if(results.files.length < ((pageOffset+1)*maxCount)) {
                results.pending = true;
                
                // Get everything we don't have, up until and including the page the user wants to see
                var startPageOffset = parseInt(results.files.length / maxCount);
                var numToGet        = (pageOffset-startPageOffset+1) * maxCount;

                var args = [];
                args.push('deviceid',   deviceId);
                args.push('serviceid',  serviceId);
                args.push('offset',     startPageOffset*maxCount);
                args.push('sortcrit',   sortCrit);
                args.push('maxcount',   numToGet);

                if(that.g_sharetoken)
                    args.push("sharetoken", that.g_sharetoken);
                
                if(cuzFile) {
                    // Cousin listing
                    args.push('parentid', aid);
                    CE.CEU.svc.asyncRPC('POST', 'listFiles', args, searchServiceComplete, searchServiceComplete,
                                        {'deviceid':deviceId, 'serviceid':serviceId, 'numRequested':numToGet,
                                         'go':go, 'aid':aid, 'svc':cuzFile.svc, 'sortcrit':sortCrit});
                } else {
                    // Real search
                    args.push('searchcrit', go.scrit);
                    if(pid)
                        args.push('parentid', pid);
                    CE.CEU.svc.asyncRPC('POST', 'searchFiles', args, searchServiceComplete, searchServiceComplete,
                                        {'deviceid':deviceId, 'serviceid':serviceId, 'numRequested':numToGet,
                                         'go':go, 'aid':aid, 'sortcrit':sortCrit});
                }
            }
        }
    }
    
    function FileCmp(f1, f2, sortCrit) {
        if(sortCrit == '+name') {
            return natcompare(f1.filename.toLowerCase(), f2.filename.toLowerCase());
        } else
        if(sortCrit == '-name') {
            return natcompare(f2.filename.toLowerCase(), f1.filename.toLowerCase());
        } else
        if(sortCrit == '+size') {
            return ((parseInt(f1.size) == parseInt(f2.size)) ? 0 : ((parseInt(f1.size) > parseInt(f2.size)) ? 1 : -1));
        } else
        if(sortCrit == '-size') {
            return ((parseInt(f1.size) == parseInt(f2.size)) ? 0 : ((parseInt(f1.size) > parseInt(f2.size)) ? -1 : 1));
        } else
        if(sortCrit == '+mtime') {
            return ((parseInt(f1.mtime) == parseInt(f2.mtime)) ? 0 : ((parseInt(f1.mtime) > parseInt(f2.mtime)) ? 1 : -1));
        } else
        if(sortCrit == '-mtime') {
            return ((parseInt(f1.mtime) == parseInt(f2.mtime)) ? 0 : ((parseInt(f1.mtime) > parseInt(f2.mtime)) ? -1 : 1));
        } else
        if(sortCrit == '+mimetype') {
            return ((f1.mimetype == f2.mimetype) ? 0 : ((f1.mimetype > f2.mimetype) ? 1 : -1));
        } else
        if(sortCrit == '-mimetype') {
            return ((f1.mimetype == f2.mimetype) ? 0 : ((f1.mimetype > f2.mimetype) ? -1 : 1));
        }
        
        // Default to filename
        return ((f1.filename.toLowerCase() == f2.filename.toLowerCase()) ? 0 : ((f1.filename.toLowerCase() > f2.filename.toLowerCase()) ? 1 : -1));
    }
    
    function getNextSearchPageItem(sortCrit, go) {
        var minSvc = null;
        for(var s in g_searchResults[go.grd]) {
            if(g_searchResults[go.grd][s].files.length > g_searchResults[go.grd][s].offset) {
                if(!minSvc) {
                    minSvc = g_searchResults[go.grd][s];
                } else {
                    if(FileCmp(g_searchResults[go.grd][s].files[g_searchResults[go.grd][s].offset],minSvc.files[minSvc.offset],sortCrit) < 0)
                        minSvc = g_searchResults[go.grd][s];
                }
            }
        }
        
        if(minSvc) {
            ++minSvc.offset;
            return minSvc.files[minSvc.offset-1];
        } else {
            return null;
        }
    }
    
    function getSearchPage(pageOffset, maxCount, sortCrit, go, groupBySource) {
        var r = {};
        r.pageoffset = pageOffset;
        r.origcount  = 0;
        r.files      = groupBySource ? {} : [];
        
        if(g_searchLastPage[go.grd] != pageOffset-1) {
            // This is not the page following the previously viewed search page, so we have to get our bearings
            for(var s in g_searchResults[go.grd])
                g_searchResults[go.grd][s].offset = 0;
            for(var i = 0; i < (pageOffset*maxCount); ++i)
                getNextSearchPageItem(sortCrit, go); //skip
        }
        
        // Get the files for this page
        grabfiles:
        for(var i = 0; i < maxCount; ++i) {
            var file = getNextSearchPageItem(sortCrit, go);
            if(!file)
                break;
            var rfiles = r.files;
            if(groupBySource) {
                if(!r.files[file.svc.deviceid+':'+file.svc.serviceid])
                    r.files[file.svc.deviceid+':'+file.svc.serviceid] = [];
                rfiles = r.files[file.svc.deviceid+':'+file.svc.serviceid];
            }
            ++r.origcount;
            if(CE.CEUI.isDescendableMetaFile(file)) {
                for(var j = 0; j < rfiles.length; ++j) {
                    if(file.type == rfiles[j].type && file.name == rfiles[j].name) {
                        // Consolidate this meta-folder with its cousin(s)
                        var share = g_sharedfolders[rfiles[j].svc.deviceid+':_:'+rfiles[j].svc.serviceid+':_:'+rfiles[j].fileid];
                        if(share) {
                            if(!rfiles[j].cousins)
                                rfiles[j].cousins = [];
                            rfiles[j].cousins.push(file);
                        } else {
                            // If one of the cousins is shared, that one should be the root cousin
                            file.cousins = [];
                            if(rfiles[j].cousins)
                                for(var iCuz = 0; iCuz < rfiles[j].cousins.length; ++iCuz)
                                    file.cousins.push(rfiles[j].cousins[iCuz]);
                            rfiles[j].cousins = null;
                            file.cousins.push(rfiles[j]);
                            rfiles[j] = file;
                        }
                        --i; // cousin doesn't count
                        continue grabfiles;
                    }
                }
            }
            rfiles.push(file);
        }
        
        // Figure out the totalcount of files if all results have one
        r.totalcount = 0;
        for(var s in g_searchResults[go.grd]) {
            if(g_searchResults[go.grd][s].totalcount && g_searchResults[go.grd][s].totalcount != '') {
                r.totalcount += parseInt(g_searchResults[go.grd][s].totalcount);
            } else if(g_searchResults[go.grd][s].files && g_searchResults[go.grd][s].files.length) {
                r.totalcount = null;
                break;
            }
        }
        
        g_searchLastPage[go.grd] = pageOffset;
        return r;
    }
    
    function reloadSearchContent(go, pageOffset, sortCrit, maxCount, cuzFile) {
        if(go.grd == 'celist')
            g_wasSearch = true;
        if(cuzFile)
            go.scrit = cuzFile.fileid;

        if(!g_searchResults[go.grd] || g_lastScrit[go.grd] != go.scrit) {
            g_searchResults[go.grd]  = {}
            g_searchLastPage[go.grd] = 0;
            g_lastScrit[go.grd]      = go.scrit;
        }
        
        if(cuzFile) {
            // Not a real search; we're just conglomerating multiple cousin listings
            searchService(go, pageOffset, sortCrit, maxCount, cuzFile.svc.deviceid, cuzFile.svc.serviceid,
                          null, cuzFile.fileid, cuzFile);
            for(var i = 0; i < cuzFile.cousins.length; ++i) {
                searchService(go, pageOffset, sortCrit, maxCount,
                              cuzFile.cousins[i].svc.deviceid, cuzFile.cousins[i].svc.serviceid,
                              null, cuzFile.cousins[i].fileid, cuzFile.cousins[i]);
            }
        } else {
            // Search through all available services; we'll clump the results together when we're done
            var searched = {};
            
            if(go.grd != 'celist' || go.stype != 'cesearch_name' || CE.hCN('cemylib_ss_opts_my','selected')
                                                                 || CE.hCN('cemylib_ss_opts_all','selected')) {
                for(var s in g_svcmap) {
                    if(g_svcmap[s] && g_svcmap[s].deviceid && g_svcmap[s].serviceid) {
                        var devid = g_svcmap[s].deviceid;
                        var svcid = g_svcmap[s].serviceid;
                        if(!searched[devid+':'+svcid]) {
                            searched[devid+':'+svcid] = true;
                            searchService(go, pageOffset, sortCrit, maxCount, devid, svcid);
                        }
                    }
                }
            }
            
            if(go.grd == 'celist' && (CE.hCN('cemylib_ss_opts_shared','selected') || CE.hCN('cemylib_ss_opts_all','selected'))) {
                for(var a in g_sharemap) {
                    if(g_sharemap[a] && g_sharemap[a].root && g_sharemap[a].root.deviceid && g_sharemap[a].root.serviceid) {
                        var devid = g_sharemap[a].root.deviceid;
                        var svcid = g_sharemap[a].root.serviceid;
                        var pid   = g_sharemap[a].root.fileid;
                        if(!searched[devid+':'+svcid] && !searched[devid+':'+svcid+':'+pid]) {
                            searched[devid+':'+svcid+':'+pid] = true;
                            searchService(go, pageOffset, sortCrit, maxCount, devid, svcid, pid, a);
                        }
                    } else if(g_sharemap[a] && g_sharemap[a].files) {
                        for(var ir = 0; ir < g_sharemap[a].files.length; ++ir) {
                            if(g_sharemap[a].files[ir].deviceid && g_sharemap[a].files[ir].serviceid) {
                                var devid = g_sharemap[a].files[ir].deviceid;
                                var svcid = g_sharemap[a].files[ir].serviceid;
                                var pid   = g_sharemap[a].files[ir].fileid;
                                if(!searched[devid+':'+svcid] && !searched[devid+':'+svcid+':'+pid]) {
                                    searched[devid+':'+svcid+':'+pid] = true;
                                    searchService(go, pageOffset, sortCrit, maxCount, devid, svcid, pid, a);
                                }
                            }
                        }
                    }
                }
            }
        }
        
        // Wait for all pending requests to complete
        var interval = setInterval(function(){
            if(!g_searchResults || !g_searchResults[go.grd]) {
                clearInterval(interval);
                return; //interrupted
            }
            
            for(var s in g_searchResults[go.grd])
                if(g_searchResults[go.grd][s].pending)
                    return;
            clearInterval(interval);
            
            if(go.grd == 'celist' && !CE.hCN('cetool_sizep','selected')) {
                CE.rCN('ceflexidrop',       'hidden');
                CE.rCN('cebuttons_grpmode', 'hidden');
            }

            // We have our results
            if(go.grd != 'celist' || !CE.hCN('cebuttons_grpmode_bysource','selected') || CE.hCN('cetool_sizep','selected')) {
                // Conglomerate
                populateContent(getSearchPage(pageOffset,maxCount,sortCrit,go), go);
            } else {
                // Group by source
                populateContentBySource(getSearchPage(pageOffset,maxCount,sortCrit,go,true), go);
            }
        }, 500);
    }
    
    this.clearContent = function(grd) {
        closeOldHints();

        showEmptyContentDiv(null);
        g_selfiles[grd] = {};
        g_curpage[grd]  = null;

        if(grd == 'celist') {
            CE.aCN('ceflexidrop',           'hidden');
            CE.aCN('ceflexiopts-sep',       'hidden');
            CE.aCN('cebuttons_fleximusic',  'hidden');
            CE.aCN('cebuttons_flexiimages', 'hidden');
            CE.aCN('cebuttons_fleximovies', 'hidden');
            CE.aCN('cebuttons_grpmode',     'hidden');
            
            // Cleanup document preview
            var docprev = CE.CEU.$('cedocprev');
            if(docprev) {
                CE.rac(docprev);
                docprev.parentNode.removeChild(docprev);
            }

            // Cleanup last viewed slideshow
            var slideFlv = CE.CEU.$('slide_flv');
            if(slideFlv) {
                CE.rac(slideFlv);
                slideFlv.parentNode.removeChild(slideFlv);
            }
            var slideExtra = CE.CEU.$('slideshow_extra_div');
            if(slideExtra) {
                CE.rac(slideExtra);
                slideExtra.parentNode.removeChild(slideExtra);
            }
            if(g_slideshowObj) {
                g_slideshowObj.destroy();
                g_slideshowObj = null;
                var slideDiv = CE.CEU.$('show_div');
                if(slideDiv) {
                    CE.rac(slideDiv);
                    slideDiv.parentNode.removeChild(slideDiv);
                    CE.rCN('cemain', 'cefullwinslid');
                }
                if(CE.CEUI.g_isSlideAlb)
                    CE.CEUI.pauseAudioTray();
            }

            CE.CEUI.g_isSlideAlb = false;
            g_wasSearch          = false;
            g_renameEvts         = {};
            
            showEmptyContentDiv(null);
        } else {
            CE.aCN('ceflexidropsrc',           'hidden');
            CE.aCN('cebuttons_fleximusicsrc',  'hidden');
            CE.aCN('cebuttons_flexiimagessrc', 'hidden');
            CE.aCN('cebuttons_fleximoviessrc', 'hidden');
        }

        var el = CE.CEU.$(grd);
        if(el) {
            CE.CEU.releaseAllEvents(el, 'mousedown', false);
            CE.CEU.releaseAllEvents(el, 'mousemove', false);
            CE.CEU.releaseAllEvents(el, 'mouseover', false);
            CE.CEU.releaseAllEvents(el, 'mouseout',  false);
            CE.CEU.releaseAllEvents(el, 'click',     true);
        }
    }

    function reloadContent(pid, go, file) {
        // Crud to accommodate different ways this function is called
        if(!go)
            go = {viewmode: g_viewmode, actions: true, scrit:g_curscrit, stype:g_cursearch};
        if(go.viewmode === undefined)
            go.viewmode = g_viewmode;
        if(!pid)
            pid = go.pid;
        if(!go.grd)
            go.grd = 'celist';

        if(pid === undefined || pid === null) {
            // If not specified, get pid from breadcrumb
            if(go.grd == 'cesrc_list') {
                if(!go.scrit && g_srcpath && g_srcpath.length>0) {
                    pid = g_srcpath[g_srcpath.length-1].id;
                    if(!file)
                        file = g_srcpath[g_srcpath.length-1].file;
                }
            } else {
                if(g_curpath && g_curpath.length>0) {
                    pid = g_curpath[g_curpath.length-1].id;
                    if(!file)
                        file = g_curpath[g_curpath.length-1].file;
                }
            }
        }
        go.pid = pid;
        // Decide what they are loading...
        if(!go.svc && !go.album) {
            if(that.g_cursvc) {
                go.svc = that.g_cursvc;
            } else if(that.g_curalbum) {
                go.album = that.g_curalbum;
                if(go.album.albumtype != FILE_TYPE_SLIDEALBUM) {
                    if(!file && go.album.root && go.album.root.cousins) {
                        file = go.album.root;
                    } else if(!file && go.album.files) {
                        file         = CE.CEU.shallowCopy(go.album.files[0]);
                        file.svc     = { deviceid:file.deviceid, serviceid:file.serviceid };
                        file.cousins = [];
                        for(var i = 1; i < go.album.files.length; ++i) {
                            go.album.files[i].svc = { deviceid:go.album.files[i].deviceid, serviceid:go.album.files[i].serviceid };
                            file.cousins.push(go.album.files[i]);
                        }
                    }
                }
            }
        }

        var maxCount   = NPERPAGE;
        var pageoffset = g_curpageoffset;
        if(go.pageoffset)
            pageoffset = go.pageoffset;
        
        CE.CEUI.clearContent(go.grd);
        
        if(go.grd == 'celist') {
            // Are we loading a slideshow album
            if(go.album && go.album.albumtype == FILE_TYPE_SLIDEALBUM && (!go.pid || go.pid == '-1') && !go.scrit) {
                CE.CEUI.g_isSlideAlb = true;
                maxCount             = NPERPAGE_SLIDEALB;
            }
        }

        // End of crud

        var el = CE.CEU.$(go.grd);
        if(el) {
            // put a loading graphic up...
            CE.CEU.showLoadingAni(true);

            if(go.album && go.album.albumtype == FILE_TYPE_FILEALBUM)
                return populateContent({'files':[go.album.root]}, go);
            if(go.grd == 'celist' && !pid && g_cursearch && (g_cursearch == 'celibshares_album' || g_cursearch == 'celibshares_share'))
                return CE.CEUI.showSharedFolders(g_cursearch.substr(12), go.grd, go.viewmode);
            if(go.grd == 'cesrc_list' && !pid && g_cursrcsearch && (g_cursrcsearch == 'celibshares_album' || g_cursrcsearch == 'celibshares_share'))
                return CE.CEUI.showSharedFolders(g_cursrcsearch.substr(12), go.grd, go.viewmode);
            if(go.grd == 'celist' && !pid && g_cursearch && g_cursearch == 'cesearch_slidedirs')
                return showSlideAlbums(go.grd, go.viewmode, false);
            if(go.grd == 'cesrc_list' && !pid && g_cursrcsearch && g_cursrcsearch == 'cesearch_slidedirs')
                return showSlideAlbums(go.grd, go.viewmode, false);
            if(!pid && go.scrit && go.scrit != "")
                return reloadSearchContent(go, pageoffset, g_sortcrit, maxCount);
            else if(file && file.cousins)
                return reloadSearchContent(go, pageoffset, g_sortcrit, maxCount, file);
            else
                g_searchResults[go.grd] = null;
            
            if(go.svc) {
                if(go.svc.type && go.svc.type.indexOf("xce:printer")==0) {
                    showPrintQueue(go.svc);
                    return;
                }
                // iterate the service
                var args = 
                    ['deviceid', go.svc.deviceid,
                     'serviceid', go.svc.serviceid,
                     'pageoffset', pageoffset,
                     'sortcrit', g_sortcrit,
                     'maxcount', maxCount ];
                if(pid) {
                    args.push('parentid');
                    args.push(pid);
                    var shareid = args[1] + ":_:" + args[3] + ":_:" + pid;
                    if(g_sharedfolders[shareid]) {
                        //args.push('listshares');
                        //args.push('1');
                    }
                }
                CE.CEU.svc.asyncRPC('POST', 'listFiles', args,
                                    populateContent, onListFilesFailure, go);
            } else if(go.album) {
                if(pid && pid != '-1' && g_curpath && g_curpath.length>0 && g_curpath[0].file) {
                    // iterate a child of the album
                    var args = 
                        ['pageoffset', pageoffset,
                         'parentid', pid,
                         'sortcrit', g_sortcrit,
                         'maxcount', maxCount];

                    if(go.album && go.album.root) {
                        args.push('deviceid');
                        args.push(go.album.root.deviceid);
                        args.push('serviceid');
                        args.push(go.album.root.serviceid);
                    }
                    if(that.g_sharetoken) {
                        args.push("sharetoken");
                        args.push(that.g_sharetoken);
                    }
                    CE.CEU.svc.asyncRPC('POST', 'listFiles', args,
                                        populateContent, onListFilesFailure, go);
                } else {
                    // iterate the root of the album
                    if(go.album.root && go.album.albumtype != FILE_TYPE_SLIDEALBUM) {
                        var args = 
                            ['deviceid', go.album.root.deviceid,
                             'serviceid', go.album.root.serviceid,
                             'pageoffset', pageoffset,
                             'parentid', go.album.root.fileid,
                             'sortcrit', g_sortcrit,
                             'maxcount', maxCount];
                        
                        if(that.g_sharetoken) {
                            args.push("sharetoken");
                            args.push(that.g_sharetoken);
                        }
                        var shareid = go.album.root.deviceid + ":_:" + go.album.root.serviceid + ":_:" + go.album.root.fileid;
                        if(g_sharedfolders[shareid]) {
                            //args.push('listshares');
                            //args.push('1');
                        }

                        CE.CEU.svc.asyncRPC('POST', 'listFiles', args,
                                            populateContent, onListFilesFailure, go);
                    } else {
                        var args = ['albumid',    go.album.albumid,
                                    'pageoffset', pageoffset,
                                    'maxcount',   maxCount];
                        if(that.g_sharetoken)
                            args.push('sharetoken', that.g_sharetoken);

                        CE.CEU.svc.asyncRPC('POST', 'listAlbumFiles', args,
                                            function(r,d){fixAlbumFiles(r,d,populateContent);},
                                            onListFilesFailure, go);
                    }
                }
            } else if(that.g_sharetoken) {
                var args = ['sharetoken', that.g_sharetoken, 'pageoffset', pageoffset];
                if(go.pid)
                    args.push('parentid', go.pid);

                if(pageoffset > 0) {
                    args.push('maxcount', maxCount);
                    CE.CEU.svc.asyncRPC('POST', 'listFiles', args, populateContent, onListFilesFailure, go);
                } else {
                    // User is viewing this from a public viewing url
                    go.maxcount = maxCount;
                    args.push('maxcount',   NPERPAGE_SLIDEALB);
                    CE.CEU.svc.asyncRPC('POST', 'listFiles', args,
                        function(r,d) {
                            var pageFiles = [];
                            for(var i = 0; i < r.files.length; ++i) {
                                if(d.maxcount > 0) {
                                    --d.maxcount;
                                    pageFiles.push(r.files[i]);
                                }
                            }
                            r.files = pageFiles;
                            populateContent(r, d);
                        },
                        onListFilesFailure,
                        go);
                }
            } else {
                CE.CEU.showLoadingAni(false);
            }
        }
        el = null;
    }

    function tryOOBE() {
        if(!CE.CEU.user) return false;
        
        var hasPlug = false;
        var ret = false;
        for(var s in g_svcmap) {
            hasPlug = true;
            break;
        }
        if(!hasPlug) {
            for(var d in g_orphans) {
                hasPlug = true;
                break;
            }
        }
        if(hasPlug) {
            if(!CE.CEU.user.flags || CE.CEU.user.flags.search("sawoobe") == -1) {
                var closeCb = function() {
                    var neverCheck = CE.CEU.$('cewelcomehints_never');
                    var neverAgain = neverCheck ? neverCheck.checked : false;
                    if(neverAgain) {
                        if(CE.CEU.user.flags) {
                            CE.CEU.user.flags += ",sawoobe";
                        } else {
                            CE.CEU.user.flags = "sawoobe";
                        }
                        CE.CEU.svc.asyncRPC("POST", "updateUser", [ "flags", "+sawoobe" ]);
                    }
                    if(!that.g_cursvc && !that.g_curalbum) {
                        activateSomething(false);
                    }
                    checkPopDriveApp(true);
                    checkMediaProcessing(true);
                    return true;
                };
                
                // Display the Welcome help
                var content = CE.dce('div', 'cewelcomemsg');
                content.innerHTML = CE.STRTAB.lookup('hint.welcome.msg.html');
                
                var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('hint.welcome.title'), content, 
                                            [{name:'close', label:CE.STRTAB.lookup('view.close'), callback:closeCb}],
                                            null, null, 'CEUDialog_hint2', null, null, closeCb);
                dlg.show();
                ret = true;
            }
        }
        
        return ret;
    }

    function setViewPrefsFromUser(user) {
        if(user) {
            if(user.flags) {
                var idx = -1;
                idx = user.flags.indexOf("lv");
                if(idx > 0) {
                    g_viewmode = 1;
                    NPERPAGE = 32;
                } else {
                    g_viewmode = 0;
                    
                    idx = user.flags.indexOf("gv=");
                    if(idx > 0) {
                        var c = user.flags.charAt(idx+3);
                        
                        switch(c) {
                        case 's':
                            g_viewsize = "small";
                            NPERPAGE = 128;
                            break;
                        case 'm':
                            g_viewsize = "medium";
                            NPERPAGE = 64;
                            break;
                        case 'l':
                            g_viewsize = "large";
                            NPERPAGE = 32;
                            break;
                        case 'p':
                            g_viewsize = "photo";
                            NPERPAGE = NPERPAGE_SLIDESHOW;
                            break;
                        }
                    }
                }
                idx = user.flags.indexOf("sort=");
                if(idx >= 0) {
                    var sortcrit = user.flags.substr(idx+5);
                    if(sortcrit.indexOf("+name")==0)  {
                        sortcrit = "+name";
                    } else if(sortcrit.indexOf("-name")==0) {
                        sortcrit = "-name";
                    } else if(sortcrit.indexOf("+mtime")==0)  {
                        sortcrit = "+mtime";
                    } else if(sortcrit.indexOf("-mtime")==0) {
                        sortcrit = "-mtime";
                    } else if(sortcrit.indexOf("+size")==0)  {
                        sortcrit = "+size";
                    } else if(sortcrit.indexOf("-size")==0) {
                        sortcrit = "-size";
                    } else if(sortcrit.indexOf("+mimetype")==0)  {
                        sortcrit = "+mimetype";
                    } else if(sortcrit.indexOf("-mimetype")==0) {
                        sortcrit = "-mimetype";
                    } else {
                        sortcrit = "none";
                    }
                    updateSortCriteria(sortcrit, false, true, true);
                }
            } else {
                g_viewsize = "large";
                g_viewmode = 0;
            }
        }
        switch(g_viewsize) {
        case "small":
            CE.aCN("cetool_sizes", "selected");
            CE.rCN("cetool_sizem", "selected");
            CE.rCN("cetool_sizel", "selected");
            CE.rCN("cetool_sizep", "selected");
            CE.aCN("cetool_size_selbut", "selected");
            break;
        case "medium":
            CE.rCN("cetool_sizes", "selected");
            CE.aCN("cetool_sizem", "selected");
            CE.rCN("cetool_sizel", "selected");
            CE.rCN("cetool_sizep", "selected");
            CE.aCN("cetool_size_selbut", "selected");
            break;
        case "large":
            CE.rCN("cetool_sizes", "selected");
            CE.rCN("cetool_sizem", "selected");
            CE.aCN("cetool_sizel", "selected");            
            CE.rCN("cetool_sizep", "selected");
            CE.aCN("cetool_size_selbut", "selected");
            break;
        case "photo":
            CE.rCN("cetool_sizes", "selected");
            CE.rCN("cetool_sizem", "selected");
            CE.rCN("cetool_sizel", "selected");            
            CE.aCN("cetool_sizep", "selected");
            CE.rCN("cetool_size_selbut", "selected");
            break;
        }
        if(g_viewmode == 1) {
            CE.rCN("cetool_gview", "selected");
            CE.aCN("cetool_lview", "selected");

            CE.rCN("cetool_sizes", "selected");
            CE.rCN("cetool_sizem", "selected");
            CE.rCN("cetool_sizel", "selected");            
            CE.rCN("cetool_sizep", "selected");            
            CE.rCN("cetool_size_selbut", "selected");
        } else {            
            CE.aCN("cetool_gview", "selected");
            CE.rCN("cetool_lview", "selected");
        }
    }

    function onUserChange(evt, user) {
        switch(evt) {
        case 'success':
            CE.CEUI.g_options = {};
            if(user.options) {
                for(var i = 0; i < user.options.length; ++i)
                    CE.CEUI.g_options[user.options[i].name] = user.options[i].value;
            }
            if(CE.CEUI.g_options['fullsecure']) {
                if(document.location.protocol != 'https:') {
                    document.location.protocol = 'https';
                    return;
                }
            } else {
                if(document.location.protocol != 'http:') {
                    document.location.protocol = 'http';
                    return;
                }
            }
            g_printSupport = (user.flags && user.flags.indexOf('print') != -1);
            

            
            setViewPrefsFromUser(user);
            that.reloadAll(function() {
                if(CE.CEU.structlen(g_albummap) == 0 && CE.CEU.structlen(g_sharemap) == 0 && CE.CEU.structlen(g_devmap) == 0) {
                    CE.aCN('cesearch_entry',   'cedisabled');
                    CE.aCN('cesearch_dropper', 'cedisabled');
                } else {
                    checkPopDriveApp();
                    checkMediaProcessing();
                }
            });
            break;
        case 'failure':
            // this should also clear the valtoken
            CE.CEU.logoutUser();
            window.location.replace('index.html#logout');
            break;
        }
    }

    function devicePoll(r) {
        var date = new Date();
        var now = date.getTime();
        var grd = CE.CEU.$("cemylib_devices");
        for(var i = 0; i < r.devices.length; i++) {
            var d = r.devices[i];
            var nosvcs = true;
            
            if(r.devices[i].services) {
                var allsvcoffline = true;

                for(var m = 0; m < d.services.length; ++m) {
                    if(d.services[m].online && d.services[m].online == '1') {
                        allsvcoffline = false;
                    }
                }

                for(var j = 0; j < d.services.length; j++) {
                    var s = d.services[j];
                    var k = d.deviceid + ':_:' + s.serviceid;

                    s.lastheard = now;
                    g_svcmap[k] = s;
                    s.id = k;
                    if(!s.name)
                        s.name = CE.STRTAB.lookup("view.svc.unnamed");

                    s.device = d;
                    
                    // append will check if the id is already in the dom and update it accordingly.
                    // reduces complication here...
                    if(allsvcoffline || (s.online && s.online == '1')) {
                        appendSidebarItem(grd, {onEvent: onClickSidebar, 
                                                id: k,
                                                name: s.name ? s.name : CE.STRTAB.lookup("view.svc.unnamed"),
                                                type: 'service',
                                                device: d,
                                                service: s,
                                                owner: d.owner,
                                                disabled: !s.online || s.online == "0"
                                               });
                    }
                    nosvcs = false;
                    
                    updateSvcXcodeMode(k, d.deviceid, s.serviceid);
                }
            }
            if(nosvcs) {
                appendSidebarItem(grd, { id: d.deviceid,
                                         name: CE.STRTAB.lookup("view.device.nodrive"),
                                         device: d,
                                         service: null,
                                         owner: d.owner,
                                         disabled: false });
            }
        }

        for(var k in g_svcmap) {
            var s = g_svcmap[k];

            // we've got someone in our map we haven't heard from.
            // disable the side bar item.
            if(!s.lastheard || s.lastheard != now) {
                appendSidebarItem(grd, {onEvent: onClickSidebar, 
                                        id: k,
                                        name: s.name ? s.name : CE.STRTAB.lookup("view.svc.unnamed"),
                                        type: 'service',
                                        device: s.device,
                                        service: s,
                                        owner: d.owner,
                                        disabled: true
                                       });

            }
        }
        populateSourceServiceList();
        pollComplete();
    }

    function ownerAlbumPoll(r) {

        pollComplete();
    }


    function shareAlbumPoll(r) {


        pollComplete();
    }

    function pollComplete() {
        if(--g_pollops == 0) {
            CE.rTimer(pollInterval, UI_POLL_INTERVAL);
        }
    }

    function pollInterval() {
        // don't do anything if we are mid an rpc
        if(CE.CEU.svc && !CE.CEU.svc.g_lastRequest) {
            CE.CEDBG.println("Polling for updated Sidebar state...");
            CE.CEU.svc.asyncRPC('POST', 'listDevices',      null, devicePoll);
            //CE.CEU.svc.asyncRPC('POST', 'listOwnerAlbums',  null, ownerAlbumPoll);
            //CE.CEU.svc.asyncRPC("POST", "listSharedAlbums", null, shareAlbumPoll);
            checkMediaProcessing();
            g_pollops ++;
        } else {
            CE.CEDBG.println("Wanted to poll devices in the middle of an RPC. Waiting 5 minutes...");
            CE.rTimer(pollInterval, UI_POLL_INTERVAL);
        }
    }
    
    function showAlbumLoginRequired() {
        var loginval = CE.CEU.getSearchParam("canlogin");
        if(loginval && (loginval.indexOf("Y")==0 || loginval.indexOf("y")==0 ||
                        loginval.indexOf("T")==0 || loginval.indexOf("t")==0 || loginval.indexOf("1")==0)) {
            CE.CEUI.loginFromAlbum(true);
        } else {
            CE.CEUI.signupFromAlbum(true);
        }
    }
    
    function showAlbumLoginHint() {
        var loginval = CE.CEU.getSearchParam("canlogin");
        if(loginval) {
            if((loginval.indexOf("Y")==0 || 
                loginval.indexOf("y")==0 || 
                loginval.indexOf("T")==0 ||                            
                loginval.indexOf("t")==0 ||     
                loginval.indexOf("1")==0)) {
                
                CE.CEUI.closeHint('ceneedpass');

                var content = CE.dce('span');
                content.appendChild(CE.STRTAB.lookupel('share.morefeaturessign'));
                (content.appendChild(CE.dca({onEvent:function(){CE.CEUI.loginFromAlbum();}}))).appendChild(CE.STRTAB.lookupel('share.clickherecaps'));
                content.appendChild(CE.STRTAB.lookupel('share.tosignin'));
                CE.CEUI.showHint('celoginok', content, false, null, true, true);
            } else {
                CE.CEUI.closeHint('celoginok');

                var content = CE.dce('span');
                content.appendChild(CE.STRTAB.lookupel('share.coolfeatures'));
                (content.appendChild(CE.dca({onEvent:function(){CE.CEUI.signupFromAlbum();}}))).appendChild(CE.STRTAB.lookupel('share.clickherecaps'));
                content.appendChild(CE.STRTAB.lookupel('share.createact2'));
                CE.CEUI.showHint('ceneedpass', content, false, null, true, true);
            }
        } else {
            CE.CEUI.closeHint('celoginok');
            CE.CEUI.closeHint('ceneedpass');
        }
    }

    function getAlbumForShareTokenSuccess(r) {
        if(r) {
            if(r.album) {
                if(r.album.secure == '1' && (CE.CEU.getSearchParam('sharetoken') || !r.album.publicid)) {
                    showAlbumLoginRequired();
                } else {
                    showAlbumLoginHint();
                    initOpDone();
                    that.g_curalbum = r.album;
                    bcFlush();
                    loadInitialAlbumFiles(that.g_curalbum);
                    
                    if(that.g_curalbum.perms && that.g_curalbum.perms != '0') {
                        if(CE.CEUI.isHintShowing('celoginok')) {
                            CE.CEUI.closeHint('celoginok');
    
                            var content = CE.dce('span');
                            content.appendChild(CE.STRTAB.lookupel('share.uploadpermission'));
                            (content.appendChild(CE.dca({onEvent:function(){CE.CEUI.loginFromAlbum();}}))).appendChild(CE.STRTAB.lookupel('share.clickherecaps'));
                            content.appendChild(CE.STRTAB.lookupel('share.tosignin'));
                            CE.CEUI.showHint('cealbumsigninmsg', content, false, null, true);
                        } else {
                            CE.CEUI.closeHint('ceneedpass');
    
                            var content = CE.dce('span');
                            content.appendChild(CE.STRTAB.lookupel('share.uploadpermission'));
                            (content.appendChild(CE.dca({onEvent:function(){CE.CEUI.signupFromAlbum();}}))).appendChild(CE.STRTAB.lookupel('share.clickherecaps'));
                            content.appendChild(CE.STRTAB.lookupel('share.createact'));
                            CE.CEUI.showHint('cealbumpasswordmsg', content, false, null, true);
                        }
                    }
                }
            }
            if(r.flags) {
                if(r.flags & FS_FLAGS_SORTABLE) {
                    CE.rCN('cetool_sort', 'cedisabled');
                } 
            } 
        }
    }

    function tryToListFilesAnyway() {
        var go = { viewmode: g_viewmode, grd: 'celist', actions: true };

        bcFlush();
        initOpDone();
        reloadContent(null, go);
    }

    function getAlbumForShareToken(sharetoken) {

        CE.CEU.svc.asyncRPC("POST", "getAlbum", [ 'sharetoken', sharetoken ], getAlbumForShareTokenSuccess,
                            tryToListFilesAnyway);
    }

    function showReadOnlyError(title, msg) {
        var svc = that.g_cursvc;
        if(!svc && that.g_curalbum && that.g_curalbum.root) {
            svc = g_svcmap[that.g_curalbum.root.deviceid + ":_:" + that.g_curalbum.root.serviceid];
        } 
        if(!svc && that.g_curalbum) {
            svc = that.g_curalbum.root;
        }
        if(svc && svc.readonly) {
            var dlg = new CE.CEU.Dialog(title, msg, [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
            dlg.show();
            dlg = null;
            return true;    
        }
        return false;
    }

    this.closeBrowserWarning = function() {
        CE.aCN("cewarn", "hidden");
    }

    function showBrowserWarning()  {
        BrowserDetect.init();

        switch(BrowserDetect.browser) {
        case "Explorer":
            if(BrowserDetect.version == "7" || BrowserDetect.version == "8") { 
                return false;
            }
            break;
        case "Safari":
            return false;
        case "Firefox":
            if(Math.floor(BrowserDetect.version) == 3) {
                return false;
            }
            break;
        case "Chrome":
            return false;
        }
        CE.rCN("cewarn", "hidden");
        return false;
    }

    function fixupIE6Dom() {
        var hldr = CE.CEU.$("ie6holder");
        if(hldr) {
            CE.rCN("cesrctray", "cesrctray-holder");
            CE.rCN("cesrctray_bg", "cesrctray-bg");
            CE.rCN("cesrctray_fg", "cesrctray-fg");

            CE.aCN("cesrctray", "cesrctray-bg-ie6");
        }
    }
    
    this.signupFromAlbum = function(noCancel) {
        CE.CEU.svc.asyncRPC('POST', 'getUser', ['sharetoken', CE.CEU.getSearchParam('sharetoken')],
        function(r) {
            var infoDiv = CE.dce('div', null, 'extramarginbottom16');
            if(noCancel) {
                infoDiv.appendChild(CE.STRTAB.lookupel('signup.required'));
            }
            var email = (r && r.user && r.user.email) ? r.user.email : '';
            var inputDlg = CE.CEU.showMultiInputDialog("album.password.title",
                                        [{inputtype:"div", div:infoDiv},
                                         {message:"index.youremail", inputcls:"field", inputid:"emailro", inputtype:"text", disabled:true, dflt:email, twoline:true},
                                         {message:"password.create", inputcls:"field", inputid:"password1", inputtype:"password", dflt:'', twoline:true, focus:true},
                                         {message:"password.confirm", inputcls:"field", inputid:"password2", inputtype:"password", dflt:'', twoline:true},
                                         {message:"signup.termsmsg", inputcls:"", inputid:"termscheck", inputtype:"checkbox", dflt:CE.STRTAB.lookup('signup.terms'), lnk:'http://www.pogoplug.com/terms/', twoline:true}],
                                        "button.ok",
                                        function()
            {
                if(!CE.CEU.$('termscheck').checked) {
                    CE.rCN('ceinputdlg_error', 'hidden');
                    CE.CEU.$('ceinputdlg_error').innerHTML = '';
                    CE.CEU.$('ceinputdlg_error').appendChild(CE.dctn(CE.STRTAB.lookup('activate.mustagree')));
                    return false;
                }
                
                trackEvent('Register', 'Sharee');
                
                CE.CEU.updatePassword(CE.CEU.getSearchParam("token"), CE.CEU.getSearchParam("sharetoken"), 
                                      CE.CEU.$('password1'), 
                                      CE.CEU.$('password2') ,
                                      'ceinputdlg_error', 
                function(x,v,z) {
                    if(x=='success') {
                        var sharetoken = CE.CEU.getSearchParam('sharetoken')
                        var urlextra   = '';
                        if(sharetoken)
                            urlextra = '?sharetoken=' + encodeURIComponent(sharetoken);

                        if(z)
                            window.location = 'view.html' + urlextra + '#' + z;
                        else
                            window.location = 'view.html' + urlextra;
                        inputDlg.hide();
                        inputDlg=null;
                    }
                });
                return false;
            }, null, noCancel);
        },
        onGenericFailure);
        
        return false;
    }
    
    function loginFromAlbumUser(r, d) {
        var nocancel = (d && d.nocancel);
        var email = '';
        if(r && r.user && r.user.email)
            email = r.user.email;
        
        var infoDiv = CE.dce('div');
        if(nocancel) {
            infoDiv.appendChild(CE.STRTAB.lookupel('signin.required'));
        }
        var forgotDiv  = infoDiv.appendChild(CE.dce('div', null, 'cesmallright'));
        var forgotLink = forgotDiv.appendChild(CE.dca({onEvent:function(){setTimeout(function(){window.location=CE.STRTAB.lookup('index.forgotpassword.url');},200);}}));
        forgotLink.appendChild(CE.STRTAB.lookupel('index.forgotpassword'));
        
        CE.CEU.showMultiInputDialog("signin.title",
                                    [{inputtype:"div", div:infoDiv},
                                     {message:"index.youremail", inputcls:"field", inputid:"signin_user", inputtype:"text", dflt:email, twoline:true},
                                     {message:"index.yourpassword", inputcls:"field", inputid:"signin_pass", inputtype:"password", dflt:'', twoline:true, focus:true}],
                                    "button.ok",
                                    function()
        {
            CE.CEU.loginUser(CE.CEU.$('signin_user'), CE.CEU.$('signin_pass'), 
                             'celogin_errmsg',
                             function(x,v){
                                if(x=='success') {
                                    var sharetoken = CE.CEU.getSearchParam('sharetoken')
                                    if(!sharetoken)
                                        window.location.replace('view.html');
                                    else
                                        window.location.replace('view.html?sharetoken=' + encodeURIComponent(sharetoken));
                                    return;
                                } else {
                                    CE.rCN('ceinputdlg_error', 'hidden');
                                    CE.CEU.$('ceinputdlg_error').innerHTML = '';
                                    CE.CEU.$('ceinputdlg_error').appendChild(CE.STRTAB.lookupel('ceu.usererror'));
                                }
                             });
            return false;
        }, null, nocancel);
    }
    
    this.loginFromAlbum = function(noCancel) {
        if(!that.g_sharetoken) {
            loginFromAlbumUser(null, {nocancel:noCancel});
        } else {
            CE.CEU.svc.asyncRPC('POST', 'getUser', ['sharetoken', that.g_sharetoken],
                                loginFromAlbumUser, loginFromAlbumUser, {nocancel:noCancel});
        }
        return false;
    }
    
    function initAlbum() {
//         if(!window.location.search) {
//             window.location.replace("view.html");
//             return;
//         } else {
            showBrowserWarning();

        // NOTE: There is a new way to get here now...  Look at the location to see where we are...
        // NOTE:    /album.html?... is the old way
        // NOTE:    /share/<shareid|sharetoken>/ is the new way
        var idx = window.location.pathname.indexOf('/share/');
        if(idx==0) {
            var str = window.location.pathname.slice(idx+7);
            idx = str.indexOf('/');
            if(idx != -1) {
                str = str.slice(0, idx);
            } 
            that.g_sharetoken = str;
        } else {
            // if there is no side bar then we kind of need the sharetoken to 
            // display anything.
            that.g_sharetoken = CE.CEU.getSearchParam("sharetoken");
        }

            // If we got a sharetoken on the url line then kill any valtoken we may have had. 
            var date = new Date();
            date.setTime(date.getTime()+((-1)*24*60*60*1000));
            var ck = 'valtoken=;expires='+date.toGMTString()+';path=/';
            document.cookie = ck;

            if(that.g_sharetoken) {
                if(!g_inited) {
                    g_initops++;
                }
                getAlbumForShareToken(that.g_sharetoken);
            } else {
                //alert('NO SHARE TOKEN!');
                window.location.replace("view.html");
                return;
            }
            var expiry = CE.CEU.getSearchParam("expiry");
            if(expiry) {
                var e = CE.CEU.$("ceexpiry");
                if(e) {
                    if(expiry == "0") {
                        e.innnerHTML = CE.STRTAB.lookup("view.expiry.todayonly");
                    } else if(expiry == "1") {
                        e.innerHTML = CE.STRTAB.lookup("view.expiry.onemoreday");
                    } else {
                        e.innerHTML = expiry + CE.STRTAB.lookup("view.expiry.moredays");
                    }
                }
            }
            if(that.g_sharetoken) return;
//         }

        // Lets see if the valtoken was set on the url line...
        var vt = CE.CEU.getSearchParam("valtoken");
        if(vt) {
            // set a cookie for the valtoken...
            // TODO: Needs to expire at some point
            document.cookie = 'valtoken='+vt+';path=/';
        }

        // Ensure our user cookie is set
        if(!CE.CEU.getValtoken()) {
            // We aren't logged in, so bail back to the home screen...
            //alert('LOGGING OUT -- initAlbum!');
            return window.location.replace('index.html#logout');
        }

        var version = CE.STRTAB.lookup('version.ui');
        if(version && version.indexOf('DEBUG')>0) {
            CE.CEDBG.enable(true);
        }

        // Ensure our user is valid and get his details
        // NOTE: This is asynchronous and will redirect the page
        // NOTE: if a string (url) or call a callback function if
        // NOTE: we pass a function to the third argument

        CE.CEU.$('cecontent').oncedrop = onDropFiles;
        CE.CEU.$('cesrctray').oncedrop = onDropFiles;
        var contentDivs = ['cecontent', 'cecontent_edge_top', 'cecontent_edge_bottom', 'cecontent_corner_top_left', 'cecontent_corner_top_right', 'cecontent_corner_bot_left', 'cecontent_corner_bot_right'];
        for(var i = 0; i < contentDivs.length; ++i)
            CE.CEU.attachEvent(CE.CEU.$(contentDivs[i]), 'mousedown', {onEvent:onStartSelRect, grd:'celist'});
        CE.CEU.attachEvent(CE.CEU.$('cecontent'),   'mouseup',   {onEvent:showDefaultContextMenu});
        CE.CEU.attachEvent(CE.CEU.$('cesrcscroll'), 'mousedown', {onEvent:onStartSelRect, grd:'cesrc_list'});

        CE.CEU.getUser('ceusername', null, onUserChange);
        CE.rTimer(pollInterval, UI_POLL_INTERVAL);
    }
    
    function initAlbumTokenUser(r, currentUserMail) {
        var shareToken = CE.CEU.getSearchParam("sharetoken");
        
        if(r && r.user && r.user.email && shareToken) {
            var tokenUserMail = r.user.email.toLowerCase();
            if(tokenUserMail == currentUserMail) {
                // User is logged in and trying to view an album that was shared to them
                //   Bring them to the full view.html UI with the shared album selected
                window.location.replace("view.html?sharetoken="+shareToken);
                return;
            }
        }
        
        initAlbum();
    }
    
    function initAlbumCurUser(r) {
        var shareToken = CE.CEU.getSearchParam("sharetoken");
        if(!shareToken || !r.user || !r.user.email) {
            initAlbum();
        } else {
            CE.CEU.svc.asyncRPC("POST", "getUser", ["sharetoken", shareToken], initAlbumTokenUser, initAlbum, r.user.email.toLowerCase());
        }
    }
    
    this.showLocaleDialog = function() {
        var locales = CE.dce('div', null, 'celocalelist');
        
        var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('title.locale'), locales,
                                    [{name:'close', label:CE.STRTAB.lookup('ceui.close')}],
                                    null, false, 'CEUDialog_wide1', null, true);

        CE.CEU.populateLocaleList(locales, "rPadded", true, dlg, 5);

        dlg.show();
        dlg = null;
    }

    function setOption(name, value, cb) {
        CE.CEUI.g_options[name] = value;
        CE.CEU.svc.asyncRPC("POST", "setOption", ['name',name,'value',value], cb, onGenericFailure);
    }

    function checkPopDriveApp(force) {
        if(!force && CE.CEU.$('cewelcomemsg')) {
            return false;
        } else if(!CE.CEU.user.flags || CE.CEU.user.flags.search("didlmsg") == -1) {
            var span = CE.dce('span');
            span.appendChild(CE.STRTAB.lookupel('view.looklikea'));
            var a = span.appendChild(CE.dca({onEvent:CE.CEU.showDlHint}));
            a.appendChild(CE.STRTAB.lookupel('view.desktopdrive'));
            span.appendChild(CE.STRTAB.lookupel('view.desktopdrive2'));
            CE.CEUI.showHint('driveapp', span, false, function(){
                if(CE.CEU.user.flags)
                    CE.CEU.user.flags += ",didlmsg";
                else
                    CE.CEU.user.flags = "didlmsg";
                CE.CEU.svc.asyncRPC("POST", "updateUser", [ "flags", "+didlmsg" ]);
            });
            return true;
        } else {
            return false;
        }
    }
    
    function checkMediaProcessing(force) {
        if(!force && CE.CEU.$('cewelcomemsg'))
            return;

        if(g_checkMediaProcessingTimer) {
            clearTimeout(g_checkMediaProcessingTimer);
            g_checkMediaProcessingTimer = null;
        }
        
        var deviceids = [];
        for(var d in g_devmap) deviceids.push(d);
        
        var checkNextDevice = function() {
            if(deviceids.length < 1) {
                // We're not processing media stuff, so close that hint if it's open
                CE.CEUI.closeHint('media');
                return;
            }
            var did = deviceids.pop();
            CE.CEU.svc.asyncRPC('POST', 'featureCommand', ['feature','xcode', 'command','listQueue', 'deviceid',did],
                                function(r) {
                                    if(r.currtask) {
                                        // We're processing. Pop a hint, and check again in 20 seconds
                                        var hint = CE.dce('span');
                                        hint.appendChild(CE.STRTAB.lookupel('view.cexcode.processing1'));
                                        hint.appendChild(CE.dca({onEvent:function(){window.location.replace('settings.html#jd0hZpEM3gX6kc5lEJnm'); return false;}})).appendChild(CE.STRTAB.lookupel('view.cexcode.processing2'));
                                        hint.appendChild(CE.STRTAB.lookupel('view.cexcode.processing3'));
                                        CE.CEUI.showHint('media', hint, null, null, true);
                                        g_checkMediaProcessingTimer = setTimeout(checkMediaProcessing, 20000);
                                    } else {
                                        checkNextDevice();
                                    }
                                }, checkNextDevice);
        };
        
        // Go through all the devices and pop a hint if any of them is processing media
        checkNextDevice();
    }

    //----------------------------------------------------------------------------------------
    // Public Functions
    this.init = function() {

        CE.CEU.init();
        fixupIE6Dom();
        
        // Modify text directionality for certain elements when rtl is called for
        var dir = CE.STRTAB.lookup('lang.dir');
        if(dir != '' && document.styleSheets && document.styleSheets.length) {
            var modstyles = ['.cehintbubble', '.cedir', '.CEUDialog div.CEUDialog_hdr'];
            for(var i = 0; i < document.styleSheets.length; ++i) {
                var rules = document.styleSheets[i].cssRules || document.styleSheets[i].rules;
                if(rules) {
                    for(var j = 0; j < rules.length; ++j) {
                        for(var k = 0; k < modstyles.length; ++k) {
                            if(rules[j].selectorText == modstyles[k]) {
                                rules[j].style['direction'] = dir;
                            }
                        }
                    }
                }
            }
        }
        
        // stupid firefox doesn't believe me if i stick it in the html....
        var e = CE.CEU.$("cesearch_entry");
        if(e) e.value = CE.STRTAB.lookup('view.search');

        if(!CE.CEU.$("cesidebar")) {
            if(CE.CEU.isLoggedIn())
                CE.CEU.svc.getUser(initAlbumCurUser, initAlbum);
            else
                initAlbum();
            return;
        }
        
        // Lets see if the valtoken was set on the url line...
        var vt = CE.CEU.getSearchParam("valtoken");
        if(vt) {
            // set a cookie for the valtoken...
            // TODO: Needs to expire at some point
            document.cookie = 'valtoken='+vt+';path=/';
        }

        // Ensure our user cookie is set
        if(!CE.CEU.getValtoken()) {
            // We aren't logged in, so bail back to the home screen...
            //alert('LOGGING OUT -- init!');
            return window.location.replace('index.html#logout');
        }

        var version = CE.STRTAB.lookup('version.ui');
        if(version && version.indexOf('DEBUG')>0) {
            CE.CEDBG.enable(true);
        }

        // Ensure our user is valid and get his details
        // NOTE: This is asynchronous and will redirect the page
        // NOTE: if a string (url) or call a callback function if
        // NOTE: we pass a function to the third argument

        CE.CEU.$('cecontent').oncedrop = onDropFiles;
        CE.CEU.$('cesrctray').oncedrop = onDropFiles;
        var contentDivs = ['cecontent', 'cecontent_edge_top', 'cecontent_edge_bottom', 'cecontent_corner_top_left', 'cecontent_corner_top_right', 'cecontent_corner_bot_left', 'cecontent_corner_bot_right'];
        for(var i = 0; i < contentDivs.length; ++i)
            CE.CEU.attachEvent(CE.CEU.$(contentDivs[i]), 'mousedown', {onEvent:onStartSelRect, grd:'celist'});
        CE.CEU.attachEvent(CE.CEU.$('cecontent'),   'mouseup',   {onEvent:showDefaultContextMenu});
        CE.CEU.attachEvent(CE.CEU.$('cesrcscroll'), 'mousedown', {onEvent:onStartSelRect, grd:'cesrc_list'});
        
        var finishInit = function() {
            CE.CEU.getUser('ceusername', null, onUserChange);
            CE.rTimer(pollInterval, UI_POLL_INTERVAL);
        };

        var sharetoken = CE.CEU.getSearchParam("sharetoken");
        if(sharetoken) {
            // Sharetoken was specified; figure out what share we should view before displaying
            CE.CEU.svc.asyncRPC("POST", "getAlbum", ['sharetoken', sharetoken],
            function(r){
                if(r && r.album && r.album.shareid)
                    g_jumpToShare = r.album.albumid;
                finishInit();
            },
            finishInit);
        } else {
            finishInit();
        }
    };
    
    //----------------------------------------------------------------------------------------
    // Public Event handlers from HTML
    function onStartSelRect(d, e) {
        if(!g_trayResizeId && !CE.CEU.$('cedocprev')) {
            closeContextMenu();
            
            if(g_selrect && g_selrect.div)
                g_selrect.div.parentNode.removeChild(g_selrect.div);
    
            g_selrect = {grd:d.grd, startX: e.clientX, startY: e.clientY};
            g_selrect.touched = false;
            g_selrect.fileids = {};
    
            document.body.ondragstart   = function()  { return false; };
            document.body.onselectstart = function () { return false; };
            CE.CEU.$(d.grd).style.MozUserSelect = 'none';
            CE.CEU.$(d.grd).parentNode.style.MozUserSelect = 'none';
        }
    }
    
    function updateContentSize() {
        var tray    = getTrayEl();
        var toolbar = CE.CEU.$('cetoolbar');
        
        if(!CE.hCN(toolbar,'hidden')) {
            if(!tray) {
                CE.CEU.$('cecontent').style.bottom = toolbar.offsetHeight + 5 + 'px';
            } else {
                CE.CEU.$('cecontent').style.bottom = toolbar.offsetHeight + tray.offsetHeight + 'px';
            }
        }
    }
    
    this.addMouseHandler = function(f) {
        g_mousedisp = f;
        //CE.CEDBG.println("Setting Mouse Event Dispatch: "+g_mousedisp);
    }
    this.removeMouseHandler = function(f) {
        if(g_mousedisp == f) {
            //CE.CEDBG.println("Removing Mouse Event Dispatch: "+g_mousedisp);
            g_mousedisp = null;
        }
    }
  



    
    this.getCurSvc = function(file) {
        if(file && file.svc) {
            return file.svc;
        } else if(file && file.serviceid && file.deviceid) {
            if(CE.CEUI.g_cursvc && CE.CEUI.g_cursvc.serviceid == file.serviceid && CE.CEUI.g_cursvc.deviceid == file.deviceid) {
                return CE.CEUI.g_cursvc;
            } else if(CE.CEUI.g_curalbum && CE.CEUI.g_curalbum.root && CE.CEUI.g_curalbum.root.serviceid == file.serviceid &&
                    CE.CEUI.g_curalbum.root.deviceid == file.deviceid) {
                return CE.CEUI.g_curalbum.root;
            } else {
                return {serviceid:file.serviceid, deviceid:file.deviceid};
            }
        } else if(CE.CEUI.g_cursvc) {
            return CE.CEUI.g_cursvc;
        } else if(CE.CEUI.g_curalbum && CE.CEUI.g_curalbum.root) {
            return CE.CEUI.g_curalbum.root;
        }
        return null;
    }
    
    this.isDescendableFile = function(file) {
        if(file.type == FILE_TYPE_DIRECTORY || file.type == FILE_TYPE_ALBUM ||
           file.type == FILE_TYPE_ARTIST || file.type == FILE_TYPE_GENRE || file.type == FILE_TYPE_IMGTIMELINE ||
           file.type == FILE_TYPE_MOVIETIMELINE || file.type == FILE_TYPE_SLIDEALBUM) {
            return true;
        }
        return false;
    }
    
    this.isDescendableMetaFile = function(file) {
        if(file.type == FILE_TYPE_ALBUM || file.type == FILE_TYPE_ARTIST ||
           file.type == FILE_TYPE_GENRE || file.type == FILE_TYPE_IMGTIMELINE ||
           file.type == FILE_TYPE_MOVIETIMELINE) {
            return true;
        }
        return false;
    }

    this.getFileThumbnail = function(file, fixedIconPostfix) {
        if(!fixedIconPostfix)
            fixedIconPostfix = "";
        
        var result   = {};
        var svc      = CE.CEUI.getCurSvc(file);
        var shareid  = svc ? (svc.deviceid+':_:'+svc.serviceid+':_:'+file.fileid) : "";
            
        if(file.thumbnail) {
            result.url    = CE.CEUI.getDataStreamPrefix(file) + file.thumbnail + "/" + encodeURIComponent(file.name) + "?" + encodeURIComponent(file.mtime);
            result.share  = (g_sharedfolders[shareid] || g_sharedfiles[shareid]) ? true : (file.album && g_albummap[file.album.albumid]);
            result.folder = CE.CEUI.isDescendableFile(file);
        } else if(CE.CEUI.isDescendableFile(file)) {
            var sharestr = g_sharedfolders[shareid] ? "-share" : ((file.album && g_albummap[file.album.albumid])?"-share":"");
            if(file.type == FILE_TYPE_ALBUM) {
                result.url = CE.STRTAB.lookup("imgbase") + "folder" + sharestr + "-album.png" + fixedIconPostfix;
            } else if(file.type == FILE_TYPE_ARTIST) {
                result.url = CE.STRTAB.lookup("imgbase") + "folder" + sharestr + "-artist.png" + fixedIconPostfix;
            } else if(file.type == FILE_TYPE_GENRE) {
                result.url = CE.STRTAB.lookup("imgbase") + "folder" + sharestr + "-genre.png" + fixedIconPostfix;
            } else if(file.type == FILE_TYPE_SLIDEALBUM) {
                sharestr = (file.album && CE.CEUI.g_options['slideshare_'+file.album.albumid]) ? "-share" : "";
                result.url = CE.STRTAB.lookup("imgbase") + "folder" + sharestr + "-slidedir.png" + fixedIconPostfix;
            } else {
                result.url = CE.STRTAB.lookup("imgbase") + "folder" + sharestr + ".png" + fixedIconPostfix;
            }
        } else {
            var icon = getIconForMime(file.mimetype);
            if(icon)
                result.url = CE.STRTAB.lookup("imgbase") + icon + fixedIconPostfix;
            else
                result.url = CE.STRTAB.lookup("imgbase") + "page.png" + fixedIconPostfix;
            result.share  = (g_sharedfolders[shareid] || g_sharedfiles[shareid]) ? true : (file.album && g_albummap[file.album.albumid]);
        }
        
        return result;
    }
    
    this.getDispFn = function(file) {
        var filename = '-';
        var filetype = null;
        if(typeof file == 'string') {
            filename = file;
        } else if(file && file.name) {
            filename = file.name;
            filetype = file.type;
        }
        
        if((filetype == FILE_TYPE_IMGTIMELINE || filetype == FILE_TYPE_MOVIETIMELINE) &&
           filename.length == 7 && filename.indexOf('-') == 4) {
            return CE.STRTAB.lookup('ceu.monthfull'+(parseInt(filename.substr(5),10)-1)) + ', ' + filename.substr(0,4);
        } else {
            return filename;
        }
    }
    
    this.showSearchOptions = function() {
        var optDiv = CE.CEU.$('cemylib_ss_opts');
        if(CE.hCN(optDiv,'hidden')) {
            CE.rCN(optDiv, 'hidden');
        } else {
            CE.aCN(optDiv, 'hidden');
        }
    };
    
    this.selSearchOption = function() {
        CE.aCN('cemylib_ss_opts', 'hidden');
        g_curscrit                = '';
        g_searchResults['celist'] = null;
    };

    function onNameSearchTimer() {
        g_searchkeytimer = null;

        var txt  = CE.CEU.$("cesearch_entry");
        var crit = CE.CEU.stripBadFnChars(txt.value);
        
        if(crit.length > 0) {
            var newScrit = 'name contains "' + crit + '"';
            if(g_curscrit != newScrit) {
                g_curscrit                = newScrit;
                g_searchResults['celist'] = null;
            }

            g_curpageoffset = 0;
            bcFlush();
            reloadContent();
        }
    }

    this.nameSearchX = function() {
        if(g_cursearch == "cesearch_name") {
            CE.CEUI.applySearch("cesearch_name", null, true);
        }
        var e = CE.CEU.$("cesearch_entry");
        if(e) e.value = CE.STRTAB.lookup("view.search");
        
        g_curscrit                = null;
        g_searchResults['celist'] = null;
        g_curpageoffset = 0;
        bcFlush();
        reloadContent();
    };

    this.nameSearchKey = function(e) {
        var l = CE.CEU.$("cesearch_entry");
        if(l.value && l.value.length > 0) {
            var keynum;

            if(g_cursearch != "cesearch_name") {
                CE.CEUI.applySearch("cesearch_name", null, true);
            }

            if(window.event) {
                keynum = e.keyCode;
            } else if(e.which) {
                keynum = e.which;
            }
            if(g_searchkeytimer) {
                CE.uTimer(g_searchkeytimer);
                g_searchkeytimer = null;
            }
            if(keynum == 13) { // enter key
                return onNameSearchTimer();
            } else {
                g_searchkeytimer = CE.rTimer(onNameSearchTimer, 500);
            }
        }
    };

    this.nameSearchFocus = function() {
        if(g_cursearch!="cesearch_name") {
            var e = CE.CEU.$("cesearch_entry");
            if(e) e.value = "";
        }
    };
    
    this.nameSearchBlur = function() {
    };

    this.nameSearch = function() {
    };
    
    this.getScrit = function(scritId) {
        var d = new Date();
        
        switch(scritId) {
            case 'cesearch_today':
                return 'ctime>="'+(d.getTime()-(1*24*60*60*1000))+'"';
            case 'cesearch_week':
                return 'ctime>="'+(d.getTime()-(7*24*60*60*1000))+'"';
            case 'cesearch_month':
                return 'ctime>="'+(d.getTime()-(30*24*60*60*1000))+'"';
            case 'cesearch_video':
                return 'mediatype="video"';
            case 'cesearch_movietime':
                return 'type="' + FILE_TYPE_MOVIETIMELINE + '"';
            case 'cesearch_image':
                return 'mediatype="image"';
            case 'cesearch_imagetime':
                return 'type="' + FILE_TYPE_IMGTIMELINE + '"';
            case 'cesearch_slidedirs':
                return '';//TODO
            case 'cesearch_playlists':
                return 'name contains ".m3u"'; // TODO: All playlists
            case 'cesearch_artists':
                return 'type="' + FILE_TYPE_ARTIST + '"';
            case 'cesearch_audio':
                return 'mediatype="audio"';
            case 'cesearch_albums':
                return 'type="' + FILE_TYPE_ALBUM + '"';
            case 'cesearch_genres':
                return 'type="' + FILE_TYPE_GENRE + '"';
            case 'cesearch_fleximusic':
                return (g_viewmode==1) ? 'mediatype="audio"' : ('type="' + FILE_TYPE_ALBUM + '"');
        }
        
        return '';
    }

    this.getScritName = function(scritId) {
        var d = new Date();
        
        switch(scritId) {
            case 'cesearch_today':
                return CE.STRTAB.lookup('view.today');
            case 'cesearch_week':
                return CE.STRTAB.lookup('view.lastweek');
            case 'cesearch_month':
                return CE.STRTAB.lookup('view.lastmonth');
            case 'cesearch_video':
                return CE.STRTAB.lookup('view.allmovies');
            case 'cesearch_movietime':
                return CE.STRTAB.lookup('view.movietime2');
            case 'cesearch_image':
                return CE.STRTAB.lookup('view.allphotos');
            case 'cesearch_imagetime':
                return CE.STRTAB.lookup('view.phototime');
            case 'cesearch_slidedirs':
                return CE.STRTAB.lookup('view.slideshows');
            case 'cesearch_playlists':
                return CE.STRTAB.lookup('view.playlists');
            case 'cesearch_artists':
                return CE.STRTAB.lookup('view.artists');
            case 'cesearch_audio':
                return CE.STRTAB.lookup('view.songs');
            case 'cesearch_albums':
                return CE.STRTAB.lookup('view.albums');
            case 'cesearch_genres':
                return CE.STRTAB.lookup('view.genres');
            case 'celibshares_album':
                return CE.STRTAB.lookup('view.filesishare');
            case 'celibshares_share':
                return CE.STRTAB.lookup('view.sharedwithme');
            case 'cesearch_fleximusic':
                return (g_viewmode==1) ? CE.STRTAB.lookup('view.songs') : CE.STRTAB.lookup('view.albums');
        }
        
        return CE.STRTAB.lookup("view.search");
    }

    this.applySearch = function(sn, sc, skiprefresh) {
        CE.rCN('cecontent_controls', 'hidden');
        
        // Disable tray pane
        closeTray(null, null, true);

        // Disable prior selected search
        if(g_cursearch) {
            if(CE.CEU.$(g_cursearch))
                CE.rCN(CE.CEU.$(g_cursearch).parentNode, 'active');
            if(g_cursearch=="cesearch_name") {
                var e = CE.CEU.$("cesearch_entry");
                if(e) e.value = CE.STRTAB.lookup("view.search");
            }
        }
        g_curscrit                = null;
        g_searchResults['celist'] = null;
        
        that.g_curalbum = null;
        that.g_cursvc   = null;
        
        // Apply selected search
        if(sn == 'cesearch_fleximusic') {
            sn = (g_viewmode == 1) ? 'cesearch_audio' : 'cesearch_albums';
        }
        updateSidebarState('none');
        g_cursearch = sn;
        if(sn) {
            if(CE.CEU.$(sn)) {
                CE.aCN(CE.CEU.$(sn).parentNode, 'active');
                updateMainDropState(sn);
            } else if(sn == 'cesearch_image') {
                CE.aCN(CE.CEU.$('cesearch_imagetime').parentNode, 'active');
                updateMainDropState('cesearch_imagetime');
            } else if(sn == 'cesearch_movietime') {
                CE.aCN(CE.CEU.$('cesearch_video').parentNode, 'active');
                updateMainDropState('cesearch_movietime');
            } else if(sn == 'cesearch_artists' || sn == 'cesearch_audio' || sn == 'cesearch_albums' || sn == 'cesearch_genres') {
                CE.aCN(CE.CEU.$('cesearch_fleximusic').parentNode, 'active');
                updateMainDropState('cesearch_albums');
            }
            if(sc) {
                g_curscrit  = sc;
            } else {
                // They want the default search criteria...
                g_curscrit = CE.CEUI.getScrit(sn);
            }
        }

        if(!g_cursearch) {
            if(CE.CEU.user) {
                setViewPrefsFromUser(CE.CEU.user);
            }
            if(!that.g_curalbum && !that.g_cursvc && sn) {
                activateSomething(false);
                return;
            }
        } else {
            if(g_sortcrit == "+size" || g_sortcrit == "-size") {
                updateSortCriteria("none", false, true, true);
            } else {
                updateSortCriteria(g_sortcrit, true, true, true);
            }
        }
        if(!skiprefresh) {
            // Reload our content state
            g_curpageoffset = 0;
            bcFlush();
            reloadContent();
        }
    };
    
    this.setGroupMode = function(mode) {
        CE.CEU.checkOpt('cebuttons_grpmode_'+mode);

        g_searchResults['celist'] = null;
        CE.CEUI.reloadContentCWD();
    };
    
    this.toggleSidebar = function(gh, gb) {
        var h = CE.CEU.$(gh);
        var b = CE.CEU.$(gb);
        if(h) {
            if(h.className.indexOf('closed')==-1) {
                CE.aCN(h, 'closed');
                if(b) b.style.display = 'none';
            } else {
                CE.rCN(h, 'closed');
                if(b) b.style.display = 'block';
            }
        }
        // Always return false to stop click...
        return false;
    };
    
    this.isFilteredListing = function() {
        // Is the currently viewed content the result of a search or a music/timeline filter?
        if(g_wasSearch)
            return true;
        if(!g_curpath || g_curpath.length < 1)
            return false;
        return CE.CEUI.isDescendableMetaFile(g_curpath[g_curpath.length-1].file);
    };
    
    this.isTopLevelFilteredListing = function() {
        // Is the currently viewed content a top-level Movies/Photos/Music/Today/etc listing?
        return (g_wasSearch && (!g_curpath || g_curpath.length < 1))
    };
    
    this.isFilesIShareListing = function() {
        return (g_wasSearch && g_cursearch == 'celibshares_album' && (!g_curpath || g_curpath.length == 0));
    };

    this.isSharedWithMeListing = function() {
        return (g_wasSearch && g_cursearch == 'celibshares_share' && (!g_curpath || g_curpath.length == 0));
    };

    this.isSharedFoldersListing = function() {
        return (g_wasSearch && (g_cursearch == 'celibshares_album' || g_cursearch == 'celibshares_share' ||
                                g_cursearch == 'cesearch_slidedirs') && (!g_curpath || g_curpath.length == 0));
    };
    
    this.isSlideAlbListing = function() {
        return (g_wasSearch && g_cursearch == 'cesearch_slidedirs' && (!g_curpath || g_curpath.length == 0));
    };

    function appendShareFolders(outFiles, shareMap, slideOnly, grd) {
        for(var k in shareMap) {
            if(shareMap[k].albumtype == FILE_TYPE_SLIDEALBUM &&
               (slideOnly || shareMap==g_sharemap || CE.CEUI.g_options['slideshare_'+shareMap[k].albumid]) &&
               (grd == 'celist' || !that.g_curalbum || that.g_curalbum.albumid != shareMap[k].albumid) &&
               (grd == 'celist' || !that.g_curalbum || that.g_curalbum.albumtype != FILE_TYPE_SLIDEALBUM || shareMap[k].ownerid == CE.CEU.user.userid)) {
                outFiles.push({fileid:'-1', name:shareMap[k].name, album:shareMap[k], type:FILE_TYPE_SLIDEALBUM});
            } else if(shareMap[k].albumtype != FILE_TYPE_SLIDEALBUM && !slideOnly && shareMap[k].root) {
                var file = CE.CEU.shallowCopy(shareMap[k].root);
                file.svc = shareMap[k];
                outFiles.push(file);
            } else if(shareMap[k].albumtype != FILE_TYPE_SLIDEALBUM && !slideOnly && shareMap[k].files) {
                var file     = CE.CEU.shallowCopy(shareMap[k].files[0]);
                file.svc     = { albumid:shareMap[k].albumid, deviceid:file.deviceid, serviceid:file.serviceid };
                file.cousins = [];
                for(var i = 1; i < shareMap[k].files.length; ++i) {
                    var cuz = CE.CEU.shallowCopy(shareMap[k].files[i]);
                    cuz.svc = { deviceid:cuz.deviceid, serviceid:cuz.serviceid };
                    file.cousins.push(cuz);
                }
                outFiles.push(file);
            }
        }
    }
    
    function populateContentWithSharedFolders(files, grd, viewMode, searchType) {
        CE.CEUI.clearContent(grd);
        
        var totalCount = files.length;
        var pageOffset = g_curpageoffset;

        if(grd == 'celist') {
            if(files.length > NPERPAGE) {
                files.splice(0, NPERPAGE*pageOffset);
                if(files.length > NPERPAGE)
                    files.splice(NPERPAGE, files.length-NPERPAGE);
            }
            
            updateSidebarState(searchType);
            g_cursearch = searchType;
            g_wasSearch = true;
            bcFlush();
        } else {
            g_cursrcsearch = searchType;
            bcSrcFlush();
        }

        var go = { viewmode:viewMode?viewMode:g_viewmode, 'grd':grd, actions:(grd=='celist') };
        populateContent({'files':files, 'totalcount':totalCount, 'pageoffset':pageOffset}, go);
    }
    
    this.showSharedFolders = function(type, grd, viewMode) {
        CE.rCN('cecontent_controls', 'hidden');
        
        var shareMap = eval('g_'+type+'map');
        
        // Show each of the shared folders
        var files = [];
        appendShareFolders(files, shareMap, false, grd);

        populateContentWithSharedFolders(files, grd, viewMode, 'celibshares_'+type);
        return false;
    };

    function showSlideAlbums(grd, viewMode, includeNonOwners) {
        var files = [];
        appendShareFolders(files, g_albummap, true, grd);
        
        if(includeNonOwners)
            appendShareFolders(files, g_sharemap, true, grd);

        populateContentWithSharedFolders(files, grd, viewMode, 'cesearch_slidedirs');
    }
    
    function calculateCurrentPage(orig, nw) {
        g_curpageoffset = Math.floor((orig * g_curpageoffset)/ nw);
        NPERPAGE = nw;
    }

    function onUserPrefsUpdated(r) {

        if(CE.CEU.user && r.user) {
            CE.CEU.user.flags = r.user.flags;
        }
    }


    function updateUserViewPrefs() {
        var flags = null;
        if(g_viewmode==0) {
            if(!g_viewsize) {
                g_viewsize = "large";
            }
            flags = "gv="+g_viewsize.charAt(0);
        } else {
            flags = "lv";
        }
        if(g_sortcrit) {
            flags += ",sort=" + g_sortcrit;
        } else {
            flags += ",sort=none";
        }
        CE.CEU.svc.asyncRPC("POST", "updateUser", ["flags", flags], onUserPrefsUpdated);
    }

    this.setViewSize = function(sz) {
        if(CE.CEUI.isNothingAvailable())
            return;
        if(sz == 'photo') {
            var hasFiles = false;
            if(g_curpage['celist']) {
                for(var i = 0; i < g_curpage['celist'].length; ++i) {
                    if(!CE.CEUI.isDescendableFile(g_curpage['celist'][i])) {
                        hasFiles = true;
                        break;
                    }
                }
            }
            if(!hasFiles) {
                CE.CEU.showMessage(CE.STRTAB.lookup('view.preview.nofiles.title'), CE.STRTAB.lookup('view.preview.nofiles'));
                return;
            }
        }
        g_viewsize = sz;
        switch(g_viewsize) {
        case 'small':
            g_viewmode = 0;
            CE.aCN("cetool_sizes", "selected");
            CE.rCN("cetool_sizem", "selected");
            CE.rCN("cetool_sizel", "selected");
            CE.rCN('cetool_lview', 'selected');
            CE.rCN('cetool_sizep', 'selected');
            CE.aCN("cetool_size_selbut", "selected");

            calculateCurrentPage(NPERPAGE, 128);
            break;
        case 'medium':
            g_viewmode = 0;
            CE.rCN("cetool_sizes", "selected");
            CE.aCN("cetool_sizem", "selected");
            CE.rCN("cetool_sizel", "selected");
            CE.rCN('cetool_lview', 'selected');
            CE.rCN('cetool_sizep', 'selected');
            CE.aCN("cetool_size_selbut", "selected");

            calculateCurrentPage(NPERPAGE, 64);
            break;
        case 'large':
            g_viewmode = 0;
            CE.rCN("cetool_sizes", "selected");
            CE.rCN("cetool_sizem", "selected");
            CE.aCN("cetool_sizel", "selected");
            CE.rCN('cetool_lview', 'selected');
            CE.rCN('cetool_sizep', 'selected');
            CE.aCN("cetool_size_selbut", "selected");

            calculateCurrentPage(NPERPAGE, 32);
            break;
        case 'list':
            g_viewmode = 1;
            CE.rCN("cetool_sizes", "selected");
            CE.rCN("cetool_sizem", "selected");
            CE.rCN("cetool_sizel", "selected");
            CE.aCN('cetool_lview', 'selected');
            CE.rCN('cetool_sizep', 'selected');
            CE.rCN("cetool_size_selbut", "selected");
            NPERPAGE = 32;
            break;
        case 'photo':
            g_viewmode = 0;
            CE.rCN("cetool_sizes", "selected");
            CE.rCN("cetool_sizem", "selected");
            CE.rCN("cetool_sizel", "selected");
            CE.rCN('cetool_lview', 'selected');
            CE.aCN('cetool_sizep', 'selected');
            CE.rCN("cetool_size_selbut", "selected");
            calculateCurrentPage(NPERPAGE, NPERPAGE_SLIDESHOW);
            break;
        }
        updateUserViewPrefs();
        reloadContent();
    }

    this.setViewMode = function(mode) {
        var tool_gv = CE.CEU.$('cetool_gview');
        var tool_lv = CE.CEU.$('cetool_lview');
        if(mode==1 || mode=='list') {
            if(g_viewmode==1) {
                // short circuit if we are already in list view
                return false;
            }
            g_viewmode = 1;
        } else {
            if(g_viewmode==0) {
                // short circuit if we are already in gridview
                return false;
            }
            g_viewmode = 0;
        }
        if(tool_gv) {
            if(g_viewmode==0) {
               CE.aCN(tool_gv, 'selected');
            } else {
                CE.rCN(tool_gv, 'selected');
            }
        }
        if(tool_lv) {
            if(g_viewmode==1) {
                CE.aCN(tool_lv, 'selected');
            } else {
                CE.rCN(tool_lv, 'selected');
            }
        }
        if(g_viewmode ==1) {
            CE.rCN("cetool_sizes", "selected");
            CE.rCN("cetool_sizem", "selected");
            CE.rCN("cetool_sizel", "selected");
            CE.rCN("cetool_sizep", "selected");
            CE.rCN("cetool_size_selbut", "selected");
        }

        updateUserViewPrefs();
        reloadContent();
        return false;
    };
    
    this.onMouseOverSizeSel = function() {
        CE.CEU.$('cetool_size_selbut').style.opacity        = '0.8';
        CE.CEU.$('cetool_size_selbut').style.filter         = 'alpha(opacity=80)';
        CE.CEU.$('cetool_size_selbut').style['-ms-filter']  = 'alpha(opacity=80)';
        
        CE.rCN('cetool_size_pop', 'hidden');
        CE.CEU.$("cetoolbar").style.zIndex = 11;
    };

    this.onMouseOutSizeSel = function() {
        CE.CEU.$('cetool_size_selbut').style.opacity        = '1';
        CE.CEU.$('cetool_size_selbut').style.filter         = 'alpha(opacity=100)';
        CE.CEU.$('cetool_size_selbut').style['-ms-filter']  = 'alpha(opacity=100)';
        
        CE.aCN('cetool_size_pop', 'hidden');
        CE.CEU.$("cetoolbar").style.zIndex = 1;
    };

    this.reloadAll = function(cb) {
        reloadSidebar(null, null, cb);
    };

    this.reloadContentCWD = function() {
        reloadContent();
    };

    this.uploadFiles = function() { 
        var file = null;

        if(that.g_curalbum && CE.CEU.user && that.g_curalbum.ownerid != CE.CEU.user.userid && that.g_curalbum.perms == 0) {
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('ceui.upload'), CE.STRTAB.lookup("view.upload.noperms"), 
                [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
            dlg.show();
            dlg = null;
            return;    
        }

        if(CE.CEUI.isFilteredListing()) {
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('ceui.upload'), CE.STRTAB.lookup("view.upload.nofilter"),
                [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
            dlg.show();
            dlg = null;
            return;    
        }  

        if(showReadOnlyError(CE.STRTAB.lookup('ceui.upload'), CE.STRTAB.lookup("view.upload.readonly"))) {
            return;
        }

        if(CE.CEUI.isNothingAvailable()) {
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('ceui.upload'), CE.STRTAB.lookup("view.upload.notavail"),
                [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
            dlg.show();
            dlg = null;
            return;
        }

        file = getCurrentFolder();


        var version = CE.CEU.getFlashVersion();
        CE.CEDBG.println("Flash Version: " + version.join());
        if(version[0] < 9 || (version[0] == 9 && version[1] < 1 && version[2] < 115)) {
            g_uploadfiles = new CE.CEUI.FileUploadDialog();
        }
        else {
            g_uploadfiles = new CE.CEUI.FlashFileUploadDialog();
        }
        g_uploadfiles.show(file);
        return false;
    };

    this.createDir = function() {
        if(showReadOnlyError(CE.STRTAB.lookup('ceui.createdir.title'), CE.STRTAB.lookup('view.createdir.readonly'))) {
            return;
        }
        if(that.g_curalbum && CE.CEU.user && that.g_curalbum.ownerid != CE.CEU.user.userid && that.g_curalbum.perms == 0) {
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('ceui.createdir.title'), CE.STRTAB.lookup("view.createdir.noperms"),
                [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
            dlg.show();
            dlg = null;
            return;    
        }
        if(CE.CEUI.isFilteredListing()) {
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('ceui.createdir.title'), CE.STRTAB.lookup('view.createdir.nofilter'),
                [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
            dlg.show();
            dlg = null;
            return;
        }
        if(CE.CEUI.isNothingAvailable()) {
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('ceui.createdir.title'), CE.STRTAB.lookup("view.createdir.notavail"),
                [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
            dlg.show();
            dlg = null;
            return;
        }

        var parentId = '0';
        if(g_curpath && g_curpath.length > 0)
            parentId = g_curpath[g_curpath.length-1].file.fileid;
        else if(CE.CEUI.g_curalbum && CE.CEUI.g_curalbum.root)
            parentId = CE.CEUI.g_curalbum.root.fileid;
        var svc  = CE.CEUI.g_cursvc ? CE.CEUI.g_cursvc : CE.CEUI.g_curalbum.root;
        var args = ['deviceid',  svc.deviceid,
                    'serviceid', svc.serviceid,
                    'parentid',  parentId,
                    'type',      FILE_TYPE_DIRECTORY,
                    'filename',  CE.STRTAB.lookup('ceui.createdir.defname')];
        var tryNum = 0;
        
        var doIt = function() {
            CE.CEU.svc.asyncRPC('POST', 'createFile', args,
            function() {
                // Success; trigger renaming of the new folder
                CE.CEUI.g_newfolder = args[args.length-1];
                CE.CEUI.reloadContentCWD();
            },
            function(r, d, m) {
                // Fail
                if(r.httpStatus == 500 && r['HB-EXCEPTION'] && r['HB-EXCEPTION'].ecode == 500) {
                    // Already exists; try another name
                    ++tryNum;
                    args[args.length-1] = CE.STRTAB.lookup('ceui.createdir.defname') + ' (' + tryNum + ')';
                    doIt();
                } else {
                    onGenericFailure(r, d, m);
                }
            });
        };
        doIt();

        return false;
    };
    
    this.createSlideAlbum = function() {
        var hasSvc = false;
        for(var s in g_svcmap) {
            if(g_svcmap[s].online && g_svcmap[s].online == '1') {
                hasSvc = true;
                break;
            }
        }
        if(!hasSvc) {
            CE.CEU.showMessage(CE.STRTAB.lookup('ceui.createslidedir.title'), CE.STRTAB.lookup('ceui.createslidedir.nosvc'));
            return;
        }
        
        CE.CEU.showMultiInputDialog("ceui.createslidedir.title",
                                    [{message:"view.enterslidedirname", inputcls:"std-field-wide", inputid:"slidename", inputtype:"text", twoline:true}],
                                    "button.ok", function() {
            // Make sure the name they entered is valid
            var name = CE.CEU.stripBadFnChars(CE.CEU.$('slidename').value);
            if(name.length < 1) {
                CE.rCN('ceinputdlg_error', 'hidden');
                CE.rac(CE.CEU.$('ceinputdlg_error'));
                CE.CEU.$('ceinputdlg_error').appendChild(CE.STRTAB.lookupel('view.invalidslidedirname'));
                return false;
            }
            
            // Create the new slideshow album
            CE.CEU.svc.asyncRPC("POST", "createAlbum", ['name',name, 'albumtype',FILE_TYPE_SLIDEALBUM],
            function(r){
                // Show the new slideshow album
                reloadSidebar(true, 'cemyalb_albums', function(){
                    activateAlbum(r.album.albumid, false);
                });
            }, onGenericFailure);
            
            return true;
        });
    };
    
    function refreshCopyFilesPane() {
        if(CE.hCN('cesrctray', 'hidden'))
            return;
        
        if(g_srcpath.length > 0) {
            g_srcpath[g_srcpath.length-1].onEvent(g_srcpath[g_srcpath.length-1]);
        } else {
            that.srcSvcSelected();
        }
    }
    
    function setDefaultSrcPath() {
        g_srcsvc                       = null;
        g_searchResults['cesrc_list']  = null;
        g_searchLastPage['cesrc_list'] = null;
        g_lastScrit['cesrc_list']      = null;
        g_cursrcsearch                 = null;
        bcSrcFlush();

        if(CE.CEUI.g_isSlideAlb) {
            // Start with photos filter if adding to slideshow album
            CE.CEU.$('cesrcsvc').selectedIndex = 1;
            that.srcSvcSelected();
            return;
        }
        
        if(CE.CEUI.g_cursvc) {
            // Set service
            var iFound = -1;
            for(var i = 0; i < CE.CEU.$('cesrcsvc').options.length; ++i) {
                if(CE.CEU.$('cesrcsvc').options[i].value == CE.CEUI.g_cursvc.id) {
                    iFound = i;
                    break;
                }
            }
            if(iFound != -1) {
                CE.CEU.$('cesrcsvc').selectedIndex = iFound;
                g_srcsvc = CE.CEUI.g_cursvc;
                // Set path
                for(var i = 0; i < g_curpath.length; ++i) {
                    var co = {file: CE.CEU.shallowCopy(g_curpath[i].file),
                              id:   g_curpath[i].id,
                              name: g_curpath[i].name,
                              go:   {actions:false, grd:'cesrc_list', nopreview:true, pid:null, viewmode:2, svc:g_srcsvc}};
                    bcSrcAppend(co);
                }
                reloadContent(null, {viewmode:2, nopreview: true, pid:null, 'svc':g_srcsvc, grd:'cesrc_list', scrit:null});
                return;
            }
        }
        
        if(CE.CEUI.g_curalbum) {
            // Set album
            var iFound = -1;
            for(var i = 0; i < CE.CEU.$('cesrcsvc').options.length; ++i) {
                if(CE.CEUI.g_curalbum.ownerid == CE.CEU.user.userid) {
                    if(CE.CEU.$('cesrcsvc').options[i].value == 'filter:celibshares_album') {
                        iFound = i;
                        break;
                    }
                } else {
                    if(CE.CEU.$('cesrcsvc').options[i].value == 'filter:celibshares_share') {
                        iFound = i;
                        break;
                    }
                }
            }
            if(iFound != -1) {
                CE.CEU.$('cesrcsvc').selectedIndex = iFound;
                g_cursrcsearch = CE.CEU.$('cesrcsvc').options[iFound].value.substr(7); 
                // Set path
                for(var i = 0; i < g_curpath.length; ++i) {
                    var co = {file: CE.CEU.shallowCopy(g_curpath[i].file),
                              id:   g_curpath[i].id,
                              name: g_curpath[i].name,
                              go:   {actions:false, grd:'cesrc_list', nopreview:true, pid:null, viewmode:2, svc:CE.CEUI.g_curalbum}};
                    bcSrcAppend(co);
                }
                reloadContent(null, {viewmode:2, nopreview: true, pid:null, 'svc':CE.CEUI.g_curalbum, grd:'cesrc_list', scrit:null});
                return;
            }
        }
        
        // Fallback to photos filter
        CE.CEU.$('cesrcsvc').selectedIndex = 1;
        that.srcSvcSelected();
    }
    
    this.addContent = function(toggle) {
        if(toggle===undefined) {
          toggle = CE.hCN('cesrctray', 'hidden');
        }
        if(toggle) {
            if(CE.CEUI.isFilteredListing()) {
                var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.copy.title'), CE.STRTAB.lookup("view.copy.nofilter"),
                    [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
                dlg.show();
                dlg = null;
                return;
            }
            if(CE.CEUI.isNothingAvailable()) {
                var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.copy.title'), CE.STRTAB.lookup("view.copy.notavail"),
                    [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
                dlg.show();
                dlg = null;
                return;
            }
            if(showReadOnlyError('Copy Files', "Can't copy files into a read only filesystem")) {
                return;
            }
            if(that.g_curalbum && that.g_curalbum.perms == '0' && that.g_curalbum.ownerid != CE.CEU.user.userid) {
                var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.copy.title'), CE.STRTAB.lookup('view.copy.nowrite'),
                    [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
                dlg.show();
                dlg = null;
                return;
            }
            
            closeTray(null, true);
            if(CE.CEUI.g_isSlideAlb) {
                CE.aCN('cesrctray_hint_folder', 'hidden');
                CE.rCN('cesrctray_hint_slide',  'hidden');
            } else {
                CE.rCN('cesrctray_hint_folder', 'hidden');
                CE.aCN('cesrctray_hint_slide',  'hidden');
            }
            CE.rCN('cesrctray', 'hidden');
            updateContentSize();
            
            if(!CE.CEUI.g_options['cesrctray_hint']) {
                CE.rCN('cesrctray_hint_noautop', 'hidden');
                CE.rCN('cesrctray_hint',         'hidden');
            }

            setDefaultSrcPath();
            
            CE.aCN('cesrctophintto_cont',     'hidden');
            CE.aCN('cesrctophinttofrom_cont', 'hidden');
            CE.aCN('cesrctophintfrom',        'hidden');
            CE.aCN('cesrctophintfromto',      'hidden');
            if(CE.CEUI.g_isSlideAlb) {
                CE.rCN('cesrctophintto_cont', 'hidden');
                CE.rCN('cesrctophintfrom',    'hidden');
            } else {
                CE.rCN('cesrctophinttofrom_cont', 'hidden');
                CE.rCN('cesrctophintfromto',      'hidden');
            }
        } else {
            CE.aCN('cesrctray',  'hidden');
            CE.aCN('cesrctophintto_cont',     'hidden');
            CE.aCN('cesrctophinttofrom_cont', 'hidden');
            updateContentSize();
        }
    };
    
    this.deleteSelected = function() {
        var type = CE.CEUI.isSlideAlbListing() ? 'delselslide'
                 : (CE.CEUI.isSharedWithMeListing() ? 'delselshare'
                                                    : (CE.CEUI.isFilesIShareListing() ? 'stopselshare' : 'delsel'));
        var checks = CE.getByClass(CE.CEU.$('celist'), 'div', 'selectcheck');
        var files  = [];
        for(var i = 0; i < checks.length; ++i) {
            if(CE.hCN(checks[i],'selected')) {
                var evt = CE.CEU.getEventObj(checks[i], 'click');
                files.push(evt.co.file);
            }
        }
        
        if(files.length == 0) {
            return CE.CEU.showMessage(CE.STRTAB.lookup('view.delsel.nosel.title'), CE.STRTAB.lookup('view.delsel.nosel'));
        }
        
        var action  = getTrashAction(getActionTypesForFile(files[0]));
        var message = CE.dce('div', null, 'confirmlist');
        message.appendChild(CE.STRTAB.lookupel('view.'+type+'.confirm'));
        var listDiv = message.appendChild(CE.dce('ul'));
        for(var i = 0; i < files.length; ++i) {
            (listDiv.appendChild(CE.dce('li'))).appendChild(CE.dctn(files[i].name));
        }
        
        CE.CEU.promptYesNo(CE.STRTAB.lookup('view.delsel.'+action+'.title'), message,
        function() {
            CE.CEU.showLoadingAni(true);
            var pending = files.length;
            if(CE.CEUI.isSlideAlbListing() || CE.CEUI.isFilesIShareListing()) {
                var iAlb = 0;
                var delNextAlb = function() {
                    if(iAlb >= files.length)
                        return;
                    var aid = (files[iAlb].album && files[iAlb].album.albumid) ? files[iAlb].album.albumid : files[iAlb].svc.albumid;
                    onDeleteFolderShareClick({albumid:aid}, null, null, CE.CEUI.isSlideAlbListing(),
                                             true, true, function() {--pending; ++iAlb; delNextAlb();});
                };
                delNextAlb();
            } else if(CE.CEUI.isSharedWithMeListing()) {
                var iShare = 0;
                var delNextShare = function() {
                    if(iShare >= files.length)
                        return;
                    var share = g_sharemap[files[iShare].svc.albumid];
                    onRemoveFolderShareClick({album:share}, true, true, function() {--pending; ++iShare; delNextShare();});
                };
                delNextShare();
            } else {
                for(var i = 0; i < files.length; ++i) {
                    trackEvent('Delete', null, files[i]);
                    var svc  = CE.CEUI.getCurSvc(files[i]);
                    var args = ['deviceid',  svc.deviceid,
                                'serviceid', svc.serviceid,
                                'fileid',    files[i].fileid,
                                'recurse',   '1'];
                    if(svc.albumid)
                        args.push('albumid', svc.albumid);
                    CE.CEU.svc.asyncRPC('POST', 'removeFile', args, function() {--pending;},
                    function(r, d) {
                        --pending;
                        return CE.CEU.showMessage(CE.STRTAB.lookup('view.delsel.'+action+'.title'), CE.STRTAB.lookup('view.delsel.error', d.file.name));
                    },
                    {'file':files[i]});
                }
            }
            
            var interval = setInterval(function() {
                if(pending == 0) {
                    clearInterval(interval);
                    CE.CEU.showLoadingAni(false);
                    if(!CE.CEUI.isSharedFoldersListing()) {
                        CE.CEUI.reloadContentCWD();
                    } else {
                        reloadSidebar(null, null, function(){
                            CE.CEUI.reloadContentCWD();
                        });
                    }
                }
            }, 100);
            
            return true;
        });
    };
    
    this.getAlbum = function(albumId) {
        // Get the album with the specified ID
        for(var albumKey in g_sharedfolders)
            if(g_sharedfolders[albumKey].albumid == albumId)
                return g_sharedfolders[albumKey];

        if(g_albummap[albumId])
            return g_albummap[albumId];
        
        return null;
    }
    
    this.getCurShareFiles = function() {
        if(g_shareedit) {
            if(g_shareedit.root) {
                return [g_shareedit.root];
            } else if(g_shareedit.files) {
                return g_shareedit.files;
            }
        }
        
        var folders = [];

        // Get the current folder
        if(g_curpath.length > 0 && g_curpath[g_curpath.length-1].file) {
            folders.push(g_curpath[g_curpath.length-1].file);
        } else if(that.g_curalbum) {
            if(that.g_curalbum.albumtype == FILE_TYPE_SLIDEALBUM)
                folders.push({fileid:'-1', name:that.g_curalbum.name, album:that.g_curalbum, type:FILE_TYPE_SLIDEALBUM});
            else if(that.g_curalbum.root)
                folders.push(that.g_curalbum.root);
        } else if(that.g_cursvc) {
            var svcFolder = { deviceid: that.g_cursvc.deviceid, serviceid: that.g_cursvc.serviceid,
                              fileid: '0', name: that.g_cursvc.name, type: FILE_TYPE_DIRECTORY,
                              fsize: 0, mime: '', mtime: '', thumbnail: '', preview: '', isSvcRoot:true };
            folders.push(svcFolder);
        }
        
        // If there are any associated cousins, include them as well
        if(folders.length > 0 && folders[0].cousins) {
            for(var i = 0; i < folders[0].cousins.length; ++i) {
                folders.push(folders[0].cousins[i]);
            }
        }
        
        return folders;
    };
    
    this.inviteEmail = function(emailList) {
        if(!shbyid("ceshare_check").checked) {
            // First turn on sharing
            shbyid("ceshare_check").checked = true;
            shareFolder(false, function(){that.inviteEmail(emailList)});
            return false;
        }

        if(!g_shareedit)
            return;
            
        var el = shbyid('ceshare_email');
        if(!el && !emailList) {
            CE.CEU.notifyError(CE.STRTAB.lookup("view.error.invalidemail"));
            return;
        }
        
        // Parse the list of e-mail addresses to add
        var em = emailList ? emailList : el.value.toLowerCase();
        var ar = em.split(/;|,/);
        var toadd = [];

        for(var q = 0; q < ar.length; ++q) {
            // first trim the string...
            ar[q] = ar[q].replace(/^\s*/, "").replace(/\s*$/, "");

            if(CE.CEU.validateEmail(ar[q])) {
                var skip = false;
                if(g_curshares) {
                    for(var i = 0; i < g_curshares.length; ++i) {
                        if(g_curshares[i] && g_curshares[i].user && g_curshares[i].user.email) {
                            if(g_curshares[i].user.email.toLowerCase() == ar[q]) {
                                skip = true;
                                break;
                            }
                        }
                    }
                }
                if(!skip)
                    toadd.push(ar[q]);
            }
        }
        CE.CEU.showLoadingAni(true);
        
        var args = ['albumid',  g_shareedit.albumid,
                    'email',    toadd.join(';')];
        if(!CE.hCN(shbyid("ceshare_messagediv"), "hidden")) {
            var ta = shbyid("ceshare_message");
            if(ta.value) {
                args.push("message");
                args.push(ta.value);
            }
        }
        
        var refresh = false;
        for(var q = 0; q < ar.length; ++q) {
            if(isEmailOfCurrentUser(ar[q])) {
                refresh = true;
                break;
            }
        }
        g_contacts = null;
        CE.CEU.svc.asyncRPC("POST", "addAlbumShare", args, onInviteSuccess, onGenericFailure, {'a':g_shareedit, rl:refresh});
        
        trackEvent('Share', 'Email', g_shareedit.files ? g_shareedit.files[0] : g_shareedit.root);

        return false;
    };
    
    function getLastContact(strContactList) {
        var mailPrefix = strContactList.toLowerCase();
        var comma      = mailPrefix.lastIndexOf(',');
        if(comma != -1)
            mailPrefix = mailPrefix.substr(comma+1);
        while(mailPrefix.length > 0 && mailPrefix.charAt(0) == ' ')
            mailPrefix = mailPrefix.substr(1);
        while(mailPrefix.length > 0 && mailPrefix.charAt(mailPrefix.length-1) == ' ')
            mailPrefix = mailPrefix.substr(0, mailPrefix.length-1);
        return mailPrefix;
    }
    
    function onShareInviteKeyDown(p, e, t) {
        var suggestions = shbyid('ceshare_suggest');
        
        var getActive = function(list) {
            for(var i = 0; i < list.length; ++i) {
                if(CE.hCN(list[i], 'active'))
                    return i;
            }
            return -1;
        };
        
        if(suggestions && suggestions.style.display == 'block') {
            var items = CE.getByClass(suggestions, 'div', 'ceshare_suggest_item');
            var i     = getActive(items);
            // Down arrow
            if(e.keyCode == 40) {
                if(i != -1) {
                    CE.rCN(items[i], 'active');
                    i = (i == (items.length-1)) ? 0 : i+1;
                    CE.aCN(items[i], 'active');
                } else if(items.length > 0) {
                    CE.aCN(items[0], 'active');
                }
                return;
            }
            // Up arrow
            if(e.keyCode == 38) {
                if(i != -1) {
                    CE.rCN(items[i], 'active');
                    i = (i == 0) ? items.length-1 : i-1;
                    CE.aCN(items[i], 'active');
                } else if(items.length > 0) {
                    CE.aCN(items[items.length-1], 'active');
                }
                return;
            }
            // Enter
            if(e.keyCode == 13 && i != -1) {
                onClickContactSuggestion({contact: items[i].firstChild.nodeValue});
                return;
            }
        }
        
        // Enter
        if(e.keyCode == 13) {
            CE.CEUI.inviteEmail();
        }
    }
    
    function showContactSuggestions(evt, e) {
        // Ignore up/down/enter
        if(e.keyCode == 40 || e.keyCode == 38 || e.keyCode == 13)
            return;
            
        // Get the list of available contacts, if we don't yet have them
        if(!g_contacts) {
            CE.CEU.svc.asyncRPC("POST", "getContacts", [], function(r,d){
                if(r && r.contacts)
                    g_contacts = r.contacts;
                else
                    g_contacts = [];
                showContactSuggestions(d.evt, d.e);
            }, function(){g_contacts=[];}, {'evt':evt, 'e':e});
            return;
        }
        
        // Get the last (partial) e-mail address that was typed
        var mailPrefix = getLastContact(shbyid('ceshare_email').value);
        if(mailPrefix.length < 1)
            return;
        
        // Display matching contacts
        var good = false;
        var sugg = shbyid('ceshare_suggest');
        CE.rac(sugg);
        for(var i = 0; i < g_contacts.length; ++i) {
            if(g_contacts[i].substr(0,mailPrefix.length) != mailPrefix || g_contacts[i] == mailPrefix)
                continue;
            good = true;
            var item = CE.dce('div', null, 'ceshare_suggest_item');
            item.appendChild(CE.dctn(g_contacts[i]));
            CE.CEU.attachEvent(item, 'click', {onEvent:onClickContactSuggestion, contact:g_contacts[i]});
            sugg.appendChild(item);
        }
        
        sugg.style.display = good ? 'block' : 'none';
    }
    
    function onClickContactSuggestion(evt) {
        shbyid('ceshare_suggest').style.display = 'none';
        
        var emailField  = shbyid('ceshare_email');
        var mailPrefix  = getLastContact(emailField.value);
        var prefixIndex = emailField.value.toLowerCase().lastIndexOf(mailPrefix);
        
        if(prefixIndex != -1) {
            emailField.value = emailField.value.substr(0,prefixIndex) + evt.contact + ', ';
        } else {
            emailField.value = emailField.value + ', ' + evt.contact + ', ';
        }
        
        emailField.focus();
        emailField.selectionStart = emailField.value.length;
        emailField.selectionEnd   = emailField.value.length;
    }
    
    this.onEmailInviteFocus = function() {
        if(shbyid('ceshare_email').value == CE.STRTAB.lookup('view.enteraddrs')) {
            shbyid('ceshare_email').value = '';
            CE.aCN(shbyid('ceshare_email'), 'email_focus');
        }
    };
    
    this.onEmailInviteBlur = function() {
        if(shbyid('ceshare_email').value == '') {
            shbyid('ceshare_email').value = CE.STRTAB.lookup('view.enteraddrs');
            CE.rCN(shbyid('ceshare_email'), 'email_focus');
        }
        // Close the suggestions popup
        setTimeout(function(){
            shbyid('ceshare_suggest').style.display = 'none';
        }, 500);
    };

    this.handlePageNext = function() {
        if(g_curpagetotal > 0) {
            if(((g_curpageoffset * NPERPAGE) + NPERPAGE) < g_curpagetotal) {
                g_curpageoffset++;
                reloadContent();    
            }            
        } else if(CE.hCN("ceempty", "hidden") || CE.hCN("page", "hidden")) {
            g_curpageoffset++;
            reloadContent();
        }
        return false;
    };

    this.handlePagePrev = function() {
        if(g_curpageoffset > 0) {
            g_curpageoffset--;
            reloadContent();
        }
        return false;
    };

    this.handleTrayNext = function() {
        if(((g_srcpageoffset * NPERPAGE) + NPERPAGE) < g_srcpagetotal) {
            g_srcpageoffset++;
            var pid = null;
            if(g_srcpath.length > 0) {
                pid = g_srcpath[g_srcpath.length-1].id;
            }
            reloadContent(pid, {viewmode:2, nopreview: true, pid:'', 'svc':g_srcsvc, grd:'cesrc_list', pageoffset: g_srcpageoffset});
        }
        return false;
    };

    this.handleTrayPrev = function() {
        if(g_srcpageoffset > 0) {
            g_srcpageoffset--;
            var pid = null;
            if(g_srcpath.length > 0) {
                pid = g_srcpath[g_srcpath.length-1].id;
            }
            reloadContent(pid, {viewmode:2, nopreview: true, pid:'', 'svc':g_srcsvc, grd:'cesrc_list', pageoffset: g_srcpageoffset});

        }
        return false;
    };
    
    this.onTrayResizeMove = function(evt) {
        if(g_trayResizeId) {
            var newHeight     = g_trayResizeHeight + (g_trayResizePos - evt.clientY);
            var browserHeight = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight;
            if(newHeight < 70)
                newHeight = 70;
            else if(newHeight > (browserHeight-150))
                newHeight = (browserHeight-150);
            
            CE.CEU.$(g_trayResizeId).style.height = newHeight + 'px';
            CE.CEU.$(g_trayResizeId).focus();
            updateContentSize();
        }
        return true;
    }
    
    this.onTrayResizeStart = function(evt, id, handleid) {
        if(!g_trayResizeId) {
            CE.CEDBG.println('tray-v-start');
            if(!evt) evt = window.event;
            
            g_trayResizeId     = id;
            g_trayResizeHeight = CE.CEU.$(g_trayResizeId).offsetHeight;
            g_trayResizePos    = evt.clientY;
            CE.CEU.$(g_trayResizeId).focus();
            CE.CEUI.addMouseHandler(CE.CEUI.onTrayResizeMove);
            
            document.body.ondragstart   = function()  { return false; };
            document.body.onselectstart = function () { return false; };
            CE.CEU.$(handleid).ondragstart = function() { return false; };
            CE.CEU.$('cecontent').style.MozUserSelect = 'none';
            CE.CEU.$('celist').style.MozUserSelect = 'none';
            CE.CEU.$('cesrc_pad').style.MozUserSelect = 'none';
            CE.CEU.$('cesrc_list').style.MozUserSelect = 'none';
            CE.CEU.$('cesidebar').style.MozUserSelect = 'none';
        }
    };
    
    this.onTrayResizeStop = function() {
        if(g_trayResizeId) {
            CE.CEDBG.println('tray-v-stop');
            CE.CEUI.removeMouseHandler(CE.CEUI.onTrayResizeMove);
            CE.CEU.$(g_trayResizeId).focus();
            g_trayResizeId = null;
            document.body.ondragstart   = null;
            document.body.onselectstart = null;
            CE.CEU.$('cecontent').style.MozUserSelect = null;
            CE.CEU.$('celist').style.MozUserSelect = null;
            CE.CEU.$('cesrc_pad').style.MozUserSelect = null;
            CE.CEU.$('cesrc_list').style.MozUserSelect = null;
            CE.CEU.$('cesidebar').style.MozUserSelect = null;
        }
    };
    
    this.mainDropSelected = function() {
        var option = CE.CEU.$('cemaindrop').options[CE.CEU.$('cemaindrop').selectedIndex];
        var value  = option.value;
        if(value.indexOf('filter:') != -1) {
            CE.CEUI.applySearch(value.substr(7));
        } else {
            var li = CE.CEU.$(value);
            if(li) {
                var evt = CE.CEU.getEventObj(li, 'click');
                if(evt) {
                    evt.onEvent(evt);
                }
            }
        }
    };

    this.srcSvcSelected = function(searchId) {
        var sel    = CE.CEU.$("cesrcsvc");
        var pid    = null;
        var filter = "";
        
        g_srcsvc                       = null;
        g_searchResults['cesrc_list']  = null;
        g_searchLastPage['cesrc_list'] = null;
        g_lastScrit['cesrc_list']      = null;
        g_cursrcsearch                 = null;

        if(searchId) {
            g_cursrcsearch = searchId;
            filter         = CE.CEUI.getScrit(searchId);
        } else if(sel) {
            if(sel.options[sel.selectedIndex]) {
                var selValue = sel.options[sel.selectedIndex].value;
                if(selValue.substr(0,7) == "filter:") {
                    g_cursrcsearch = selValue.substr(7);
                    filter         = CE.CEUI.getScrit(selValue.substr(7));
                } else {
                    g_srcsvc = g_svcmap[selValue];
                    if(!g_srcsvc) {
                        var share = g_sharemap[sel.options[sel.selectedIndex].value]; 
                        if(share && share.root) {
                            g_srcsvc = share.root;
                            pid = share.root.fileid;
                        }
                    }
                }
            }
        }
        if(g_srcsvc || g_cursrcsearch != null) {
            bcSrcFlush();
            reloadContent(null, {viewmode:2, nopreview: true, pid:pid, 'svc':g_srcsvc, grd:'cesrc_list', scrit:filter});
        }
    };

    this.removeAlbum = function() {
        if(!g_deleteconfirm) {
            g_deleteconfirm = new DeleteConfirmDialog();
        }
        g_deleteconfirm.show(that.g_curalbum, true);
        return false;
    }

    function onUnshareFolderSuccess(share) {
        if(share) {
            if(that.g_curalbum && share.albumid == that.g_curalbum.albumid) {
                reloadSidebar();
            } else {
                reloadSidebar(true, 'cemyalb_albums');
                // populate a null result merely clears the table.
                populateInvitationList(null);
                
                var iconid = '';
                if(share.root)
                    iconid = share.root.deviceid +  ':_:' + share.root.serviceid +  ':_:' + share.root.fileid + ":_:di";
                else if(share.files)
                    iconid = share.files[0].deviceid +  ':_:' + share.files[0].serviceid +  ':_:' + share.files[0].fileid + ":_:di";
                var ico = CE.CEU.$(iconid);
                
                if(ico && ico.parentNode) {
                    ico.parentNode.removeChild(ico);
                }
                populateRSSShareSection();
                bcCheckShare();
            }
        }
    }
    
    function onShareFolderSuccess(album, cb) {
        var isLast   = false;
        var lastCall = function() {
            if(!isLast) {
                isLast = true;
            } else {
                if(cb) cb();
            }
        };
        
        enableMoreOptions(album, lastCall);
        reloadSidebar(true, 'cemyalb_albums', lastCall);
        
        bcCheckShare();
        // don't push content stack or anything. we are already looking where we want to.
    }

    function onYesDoShareDrive(cb) {
        that.shareFolder(true, cb);
        return true;
    }

    function onNoDontShareDrive() {
        var c = shbyid("ceshare_check");
        c.checked = false;
        return true;
    }

    function enableMoreOptionsCheckBoxes(share, div) {
        if(!div) {
            div = CE.CEU.$('ceshare_page_1');
        }        
        if(div) {
            for(var ch = div.firstChild; ch != null; ch = ch.nextSibling) {
                if(ch.type && ch.type == 'checkbox') {
                    ch.disabled = share == null;
                } else if(ch.childNodes && ch.childNodes.length > 0) {
                    enableMoreOptionsCheckBoxes(share, ch);
                }
            }
        }
    }

    function onGotAlbumRoot(r, cb) {
        var autonotify = false;
        var lastPost   = 0;
        g_mailNotifMsg = '';

        if(r && r.file && r.file.properties) {
            for(var k in r.file.properties) {
                if(k.indexOf("mailnotify") == 0) {
                    if(r.file.properties[k].indexOf('0')!=0) {
                        autonotify = true;
                    }
                } else if(k.indexOf("mailnotif_sent") == 0) {
                    lastPost = parseInt(r.file.properties[k]);
                } else if(k.indexOf("mailnotif_msg") == 0) {
                    g_mailNotifMsg = r.file.properties[k];
                }
            }
        }
        var check = shbyid("ceshare-check-repost");
        if(check) check.checked = autonotify;
        
        if(autonotify && lastPost > 0) {
            var date = new Date(lastPost*1000);
            CE.CEU.$("mailnotif_lastpubdate").innerHTML = date.format('mmmm d, yyyy "at" h:MMTT');
            CE.CEU.$("mailnotif_lastpub").style.display = 'inline';
        } else {
            CE.CEU.$("mailnotif_lastpubdate").innerHTML = '';
            CE.CEU.$("mailnotif_lastpub").style.display = 'none';
        }
        
        if(cb)
            cb();
    }
    
    function enableMoreOptions(share, cb) {
        var c = shbyid("cesharerss_check");
        if(share && share.feedid && share.feedid.length > 0) {
            CE.rCN(shbyid("ceshare_rssbody"), "hidden");
            c.checked = true;
        } else {
            CE.aCN(shbyid("ceshare_rssbody"), "hidden");
            c.checked = false;
        }
        populateRSSShareSection(share);

        c = shbyid("ceshare-check-publicurl");
        if(c) {
            if(share && share.publicid && share.publicid.length > 0) {
                CE.rCN(shbyid("ceshare_puburlbody"), "hidden");
                c.checked = true;
            } else {
                CE.aCN(shbyid("ceshare_puburlbody"), "hidden");
                c.checked = false;
            }
        }
        showPublicURLSection(share);
        
        // Check if social network notification is enabled for this share
        var root = (share && share.root) ? share.root : ((share && share.files) ? share.files[0] : null);
        if(root) {
            displayShareWindowSocialOpts('twitter',  root.fileid, root.deviceid, root.serviceid);
            displayShareWindowSocialOpts('facebook', root.fileid, root.deviceid, root.serviceid);
            displayShareWindowSocialOpts('myspace',  root.fileid, root.deviceid, root.serviceid);
        } else if(share) {
            var svc = (that.g_curalbum && that.g_curalbum.root) ? that.g_curalbum.root : that.g_cursvc;
            if(svc) {
                displayShareWindowSocialOpts('twitter',  svc.fileid, svc.deviceid, svc.serviceid, true);
                displayShareWindowSocialOpts('facebook', svc.fileid, svc.deviceid, svc.serviceid, true);
                displayShareWindowSocialOpts('myspace',  svc.fileid, svc.deviceid, svc.serviceid, true);
            }
        } else {
            updateShareWindowSocialOpt('twitter',  '0', 0);
            updateShareWindowSocialOpt('facebook', '0', 0);
            updateShareWindowSocialOpt('myspace',  '0', 0);
        }

        if(root) {
            CE.CEU.svc.asyncRPC("POST", "getFile", [ "deviceid", root.deviceid, "serviceid", root.serviceid, "fileid", root.fileid, "allprops", "1" ],
                                onGotAlbumRoot, null, cb);
        } else {
            shbyid("ceshare-check-repost").checked = false;
        }
    }
    
    function shareDisableNotify(serviceId, deviceId, albumId, cb, d) {
        // Silently disable social network & mail notification for this share, then execute the supplied callback
        var args = ['deviceid',deviceId, 'serviceid',serviceId, 'albumid',albumId, 'socialnotif','0',
                    'network','twitter', 'network1','facebook', 'network2','myspace',
                    'text','', 'msg','', 'url','', 'title','', 'urllabel','', 'autonotify','0'];
        
        CE.CEU.svc.asyncRPC('POST', 'updateAlbumShare', args, cb, cb, d);
    }
    
    function shareFolderEnable(justdoit, folders, cb) {
        if(folders[0].isSvcRoot && !justdoit) {
            // If they're trying to share a drive root, prompt them first
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.sharefiles'), CE.STRTAB.lookupel("view.share.drive"),
                [{name:'yes',label:CE.STRTAB.lookup("view.yes"),callback:function(){return onYesDoShareDrive(cb);}}, 
                {name:'no',label:CE.STRTAB.lookup("view.no"),callback:onNoDontShareDrive}], null, null, null, null, null,
                onNoDontShareDrive);
            dlg.show(); dlg = null;
            return;
        }
        
        CE.rCN(shbyid('ceshare_listsectionhint'), 'hidden');
        CE.aCN('ceshare_hint_noshare', 'hidden');
        CE.rCN('ceshare_hint_share', 'hidden');
        
        if(that.g_curalbum && that.g_curalbum.albumtype == FILE_TYPE_SLIDEALBUM) {
            // Already shared; add in the metashare
            setOption('slideshare_'+that.g_curalbum.albumid, true);
            g_shareedit = that.g_curalbum;
            shbyid('ceshare_secure').checked = CE.CEUI.g_options['secureshares']?true:false;
            CE.CEUI.setAlbumSecurity(cb);
            return;
        }
        
        CE.CEU.showLoadingAni(true);
        
        if(folders[0].fileid == '0') {
            CE.aCN(shbyid("ceshare_folderlabel"), "hidden");
            CE.rCN(shbyid("ceshare_drivelabel"),  "hidden");
            CE.aCN(shbyid("ceshare_filelabel"),   "hidden");
        }  else {
            CE.rCN(shbyid("ceshare_folderlabel"), "hidden");
            CE.aCN(shbyid("ceshare_drivelabel"),  "hidden");
            CE.aCN(shbyid("ceshare_filelabel"),   "hidden");
        }

        // Create the album that will host the shared folder(s)
        trackEvent('Share', 'Create', folders[0]);
        var complete = 0;
        var album    = null;
        CE.CEU.svc.asyncRPC("POST", "createAlbum",
                            ['name',CE.CEUI.getDispFn(folders[0]), 'albumtype',folders[0].type, 'type',folders[0].type,
                             'secure',CE.CEUI.g_options['secureshares']?'1':'0'],
        function(r){
            shbyid('ceshare_secure').checked = CE.CEUI.g_options['secureshares']?true:false;
            album = CE.CEU.shallowCopy(r.album);
            // Add the folders to this new album
            for(var i = 0; i < folders.length; ++i) {
                var f    = CE.CEU.shallowCopy(folders[i]);
                var svc  = CE.CEUI.getCurSvc(f);
                var args = ['name',      CE.CEUI.getDispFn(f),
                            'albumid',   r.album.albumid,
                            'deviceid',  svc.deviceid,
                            'serviceid', svc.serviceid,
                            'fileid',    f.fileid,
                            'filename',  f.name,
                            'type',      f.type, 
                            'fsize',     f.size,
                            'mime',      f.mimetype,
                            'mtime',     f.mtime,
                            'thumbnail', f.thumbnail,
                            'preview',   f.preview ];
                
                // Need to add it to sharedfolders immediately to get the invitation list
                f.albumid = r.album.albumid;
                g_sharedfolders[svc.deviceid + ':_:' + svc.serviceid + ':_:' + f.fileid] = f;
                CE.CEU.svc.asyncRPC("POST", "addAlbumFile", args, function(){++complete;}, onGenericFailure);
                
                f.deviceid  = svc.deviceid;
                f.serviceid = svc.serviceid;
                if(i == 0 && folders.length == 1) {
                    album.root = f;
                } else {
                    if(i == 0) album.files = [];
                    album.files.push(f);
                }
            }
        }, onGenericFailure);

        // Wait for operations on all folders to complete
        var interval = setInterval(function() {
            if(complete == folders.length) {
                clearInterval(interval);
                CE.CEU.showLoadingAni(false);
                onShareFolderSuccess(album, function() {
                    g_shareedit = g_albummap[album.albumid];
                    if(cb) cb();
                });
            }
        }, 100);
    }
    
    function shareFolderDisable(justdoit, folders, cb) {
        if(!justdoit) {
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.share.disable.title'), CE.STRTAB.lookupel("view.share.disable.msg"),
                [{name:'yes',label:CE.STRTAB.lookup("view.yes"),callback:function(){shareFolder(true,cb);return true;}}, 
                {name:'no',label:CE.STRTAB.lookup("view.no"),callback:function(){shbyid("ceshare_check").checked=true;return true;}}]);
            dlg.show(); dlg = null;
            return;
        }
        
        if(that.g_curalbum && that.g_curalbum.albumtype == FILE_TYPE_SLIDEALBUM) {
            // Disable the metashare
            removeAllInvitations(that.g_curalbum.albumid);
            setOption('slideshare_'+that.g_curalbum.albumid, false);
            return;
        }

        CE.CEU.showLoadingAni(true);
        CE.aCN(shbyid('ceshare_listsectionhint'), 'hidden');
        enableMoreOptions(null);
        
        // Disable share notifications for the folder(s) hosted by this album
        var pending = 0;
        if(g_shareedit.albumtype != FILE_TYPE_FILEALBUM) {
            for(var i = 0; i < folders.length; ++i) {
                var svc = CE.CEUI.getCurSvc(folders[i]);
                delete g_sharedfolders[svc.deviceid +  ':_:' + svc.serviceid +  ':_:' + folders[i].fileid];
                ++pending;
                shareDisableNotify(svc.serviceid, svc.deviceid, g_shareedit.albumid, function(){--pending;});
            }
        }
        
        // Wait for completion and delete the actual album
        var interval = setInterval(function() {
            if(pending == 0) {
                clearInterval(interval);
                CE.CEU.svc.asyncRPC("POST", "deleteAlbum", ['albumid', g_shareedit.albumid], function() {
                    CE.CEU.showLoadingAni(false);
                    if(g_shareedit.albumtype != FILE_TYPE_FILEALBUM) {
                        onUnshareFolderSuccess(g_shareedit);
                        g_shareedit = null;
                    } else {
                        reloadSidebar(true, 'cemyalb_albums', function() {
                            g_shareedit = null;
                            CE.CEUI.reloadContentCWD();
                        });
                    }
                }, onGenericFailure);
            }
        }, 100);
    }

    this.shareFolder = function(justdoit, cb) {
        if(!disallowedShareWarning()) {
            return shareFolder(justdoit, cb);
        }
    }
    
    function shareFolder(justdoit, cb) {
        var c       = shbyid("ceshare_check");
        var folders = CE.CEUI.getCurShareFiles();
        if(!c)                 return;
        if(folders.length < 1) return;
        
        if(c.checked) {
            shareFolderEnable(justdoit, folders, cb);
        } else {
            shareFolderDisable(justdoit, folders, cb);
        }
    };

    function displayShareWindowSocialOpts(network, fileId, deviceId, serviceId, off) {
        if(hasFeature(network, deviceId)) {
            // This device supports posting to this social network
            CE.CEU.$(network+"_shareopts").style.display = 'block';
            
            // Update the "Post to social network" notification checkbox
            CE.CEU.svc.asyncRPC('POST', 'featureCommand', 
                                ['deviceid', deviceId, 'serviceid', serviceId,
                                'feature', network, 'command', 'has_notify', 'fileid', fileId],
                                function(r) {
                                    if(off)
                                        r.notify = 0;
                                    updateShareWindowSocialOpt(network, r.notify, r.lastpost ? parseInt(r.lastpost) : 0);
                                });
        } else {
            CE.CEU.$(network+"_shareopts").style.display = 'none';
        }
    }
    
    function updateShareWindowSocialOpt(network, notify, lastPost) {
        CE.CEU.$(network+"_check").checked     = (notify == '1');
        CE.CEU.$(network+"_authcheck").checked = (notify == '1');
        CE.CEU.$(network+"_editnotif").style.display = (notify == '1') ? 'inline' : 'none';

        if(lastPost > 0) {
            var date = new Date(lastPost*1000);
            CE.CEU.$(network+"_lastpubdate").innerHTML = date.format('mmmm d, yyyy "at" h:MMTT');
            CE.CEU.$(network+"_lastpub").style.display = 'inline';
        } else {
            CE.CEU.$(network+"_lastpubdate").innerHTML = '';
            CE.CEU.$(network+"_lastpub").style.display = 'none';
        }

        updateShareWindowSocialOptsVisibility(network);
    }

    function updateShareWindowSocialOptsVisibility(network) {
        var hasLastPub = (CE.CEU.$(network+"_lastpubdate").innerHTML != '');
        var pubViewing = shbyid("ceshare-check-publicurl").checked;
        
        if(hasLastPub && pubViewing) {
            CE.CEU.$(network+"_repost").style.display  = 'block';
        } else {
            CE.CEU.$(network+"_repost").style.display  = 'none';
        }
    }
    
    this.setAlbumSecurity = function(cb) {
        if(!shbyid('ceshare_check').checked) {
            // First turn on sharing
            shbyid('ceshare_check').checked = true;
            shareFolder(false, function(){that.setAlbumSecurity(cb);});
            return;
        }

        var share    = g_shareedit || findFolderShare(getCurrentFolder());
        var security = shbyid('ceshare_secure').checked ? '1' : '0';

        if(share) {
            CE.CEU.svc.asyncRPC('POST', 'updateAlbumShare', ['albumid', share.albumid, 'secure', security],
            function() {
                share.secure = security;
                if(cb) cb();
            },
            onGenericFailure);
        }
    };
    
    function openShareWindow(share) {
        if(!share && that.g_curalbum && that.g_curalbum.ownerid != CE.CEU.user.userid) {
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.share.title'), CE.STRTAB.lookupel('view.share.notown'),
                [{name:'ok',label:CE.STRTAB.lookup('view.ok')}]);
            dlg.show(); dlg = null;
            return;
        }
        g_shareedit = share;
        populateInvitationList();

        closeTray(null, true);
        CE.rCN(shbyid('ceshare'), 'hidden');
        updateContentSize();
        
        CE.CEUI.selectShareTab(0);
        shbyid('ceshare_email').value = CE.STRTAB.lookup('view.enteraddrs');
        shbyid('ceshare_email').blur();
        CE.rCN(shbyid('ceshare_email'), 'email_focus');
        shbyid('ceshare_secure').checked = false;

        var isSlide = (share && share.albumtype == FILE_TYPE_SLIDEALBUM);
        var isFile  = (share && share.albumtype == FILE_TYPE_FILEALBUM);
        var c       = shbyid("ceshare_check");
        
        if(isSlide) {
            c.checked = CE.CEUI.g_options['slideshare_'+share.albumid] ? true : false;
        } else {
            c.checked = share != null;
        }
        
        if(c.checked) {
            CE.aCN(shbyid('ceshare_listsectionhint'), 'hidden');
        } else {
            CE.rCN(shbyid('ceshare_listsectionhint'), 'hidden');
            CE.rCN('ceshare_hint_noshare', 'hidden');
            CE.aCN('ceshare_hint_share', 'hidden');
        }
        
        if(isSlide || isFile) {
            CE.aCN('twitter_repost_mask', 'hidden');
            CE.aCN('facebook_repost_mask','hidden');
            CE.aCN('myspace_repost_mask', 'hidden');
            CE.aCN('rss_shareopts',       'hidden');
            CE.aCN('repost_shareopts',    'hidden');
        } else {
            CE.rCN('twitter_repost_mask', 'hidden');
            CE.rCN('facebook_repost_mask','hidden');
            CE.rCN('myspace_repost_mask', 'hidden');
            CE.rCN('rss_shareopts',       'hidden');
            CE.rCN('repost_shareopts',    'hidden');
        }

        if(share) {
            shbyid('ceshare_secure').checked = (share.secure == '1');
            reloadInvitationList(share);
        }
        var l = shbyid("ceshare_checklabel");
        if(l) {
            CE.rac(l);
            
            var f = null;
            if(share && share.root && !isSlide) {
                f = share.root;
            }
            if(!f) {
                 f = getCurrentFolder();
            } 
            if(f) {
                if(CE.CEUI.isDescendableMetaFile(f))
                    CE.aCN('rss_shareopts', 'hidden');
                    
                if(f.name) {
                    c.disabled = false;
                    CE.CEUI.g_shareTypeStr = CE.STRTAB.lookup('view.folder');
                    if(isSlide) {
                        l.appendChild(CE.STRTAB.lookupel('view.sharethisslideshow', CE.CEUI.getDispFn(f)));
                        CE.CEUI.g_shareTypeStr = CE.STRTAB.lookup('view.slideshow');
                    } else if(isFile) {
                        CE.CEUI.g_shareTypeStr = CE.STRTAB.lookup('view.file');
                    } else if(f.fileid == "0") {
                        CE.CEUI.g_shareTypeStr = CE.STRTAB.lookup('view.drive');
                    } else {
                        CE.CEUI.g_shareTypeStr = CE.STRTAB.lookup('view.folder');
                    }
                    if(!isSlide) {
                        l.appendChild(CE.STRTAB.lookupel('view.sharethis', CE.CEUI.g_shareTypeStr, CE.CEUI.getDispFn(f)));
                    }
                    CE.rac(shbyid('ceshare_listsectionhinttype'));
                    CE.rac(shbyid('ceshare_listsectionhinttypeb'));
                    CE.rac(shbyid('ceshare_listsectionhinttypec'));
                    (shbyid('ceshare_listsectionhinttype')).appendChild(CE.dctn(CE.CEUI.g_shareTypeStr));
                    (shbyid('ceshare_listsectionhinttypeb')).appendChild(CE.dctn(CE.CEUI.g_shareTypeStr));
                    (shbyid('ceshare_listsectionhinttypec')).appendChild(CE.dctn(CE.CEUI.g_shareTypeStr));
                }
                if(isFile) {
                    CE.aCN(shbyid("ceshare_folderlabel"), "hidden");
                    CE.aCN(shbyid("ceshare_drivelabel"),  "hidden");
                    CE.rCN(shbyid("ceshare_filelabel"),   "hidden");
                } else if(f.fileid == '0') {
                    CE.aCN(shbyid("ceshare_folderlabel"), "hidden");
                    CE.rCN(shbyid("ceshare_drivelabel"),  "hidden");
                    CE.aCN(shbyid("ceshare_filelabel"),   "hidden");
                }  else {
                    CE.rCN(shbyid("ceshare_folderlabel"), "hidden");
                    CE.aCN(shbyid("ceshare_drivelabel"),  "hidden");
                    CE.aCN(shbyid("ceshare_filelabel"),   "hidden");
                }
            }
        }
        enableMoreOptions(share);
        populateRSSShareSection(share);
    }

    function closeShareWindow() {
        populateInvitationList();
        CE.aCN(shbyid('ceshare'), 'hidden');
        updateContentSize();
    }
    
    this.selectShareTab = function(selectedTab) {
        var tabIndex = 0;
        var tab;
        while(tab = CE.CEU.$('ceshare_tab_'+tabIndex)) {
            var tabPage = CE.CEU.$('ceshare_page_'+tabIndex);
            
            if(tabIndex == selectedTab) {
                CE.aCN(tab,     'active');
                CE.rCN(tabPage, 'hidden');
            } else {
                CE.rCN(tab,     'active');
                CE.aCN(tabPage, 'hidden');
            }
            
            ++tabIndex;
        }
    }

    this.exitShareMode = function() {
        closeShareWindow();
    }

    function getCurrentFolder() {
        var folder = null;
        
        if(g_curpath && g_curpath.length && g_curpath.length > 0) {
            var co = g_curpath[g_curpath.length-1];
            if(co) {
                folder = co.file;
            }
        }
        if(!folder) {
            if(that.g_curalbum) {
                if(that.g_curalbum.albumtype == FILE_TYPE_SLIDEALBUM) {
                    folder = {fileid:"-1", album:that.g_curalbum, name:that.g_curalbum.name, type:FILE_TYPE_SLIDEALBUM};
                } else {
                    folder = that.g_curalbum.root;
                }
            }
        }
        if(!folder) {
            folder = { fileid: "0"  };
            if(that.g_cursvc) {
                folder.name = that.g_cursvc.name;
            }
        }
        return folder;
    }

    // Returns the file id of the current folder
    this.getCurrentFolderID = function() {
        var folder = getCurrentFolder();

        return folder.fileid;
    }

    this.getCurrentAlbum = function() {
        return CE.CEUI.g_curalbum;
    }

    function populateRSSShareSection(share) {
        
        var mydiv = shbyid("cerss_display");
        var drss = CE.dce('textarea');
        drss.readOnly = true;
       
        var rssc = shbyid("cesharerss_check");
        if(share && share.feedid) {
            if(rssc) rssc.checked = true;

            var url = 'http://' + window.location.host + '/feeds/' + share.feedid;

            CE.rCN(shbyid("ceshare_rssbody"), "hidden");
            
            var d = shbyid("cerss_link");
            if(d) {
                CE.rac(d);
                var a = CE.dce('a');
                a.appendChild(CE.STRTAB.lookupel('view.rss.inbrowser'));
                a.target = "_blank";
                a.href = url;
                d.appendChild(a);
            }
            drss.appendChild(CE.dctn(url));
        } else {
            if(rssc) rssc.checked = false;

            var d = shbyid("cerss_link");
            if(d) {
                CE.rac(d);
                d.appendChild(CE.STRTAB.lookupel('view.rss.inbrowser'));

            }
            CE.aCN(shbyid("ceshare_rssbody"), "hidden");
        }
        CE.rac(mydiv);
        mydiv.appendChild(drss);
    }

    function onEnterRSSModeSuccess(r, data) {
        if(data && r && r.url) {
            var idx = r.url.indexOf("feeds/");
            var feedid = r.url.substr(idx+6);
            data.feedid = feedid;

            populateRSSShareSection(data);
        }
    }
    function onEnterRSSModeFailure(r, data) {
        var c = shbyid("cesharerss_check");
        c.checked = false;

        populateRSSShareSection();
    }

    function onExitRSSModeSuccess(r, data) {
        if(data && data.feedid) {
            delete data.feedid;
           
            populateRSSShareSection(data);
        }
    }

    function onExitRSSModeFailure(r, data) {
        populateRSSShareSection();
    }

    this.onShareMessageKeyUp = function(e) {
        var rc = shbyid("ceshare_remchars");
        if(rc) { 
            var ta = shbyid("ceshare_message");
            if(ta) {
                if(ta.value.length >= MESSAGE_MAX_CHARS) {
                    ta.value = ta.value.substr(0, MESSAGE_MAX_CHARS);
                }
                CE.rac(rc);
                rc.appendChild(CE.dctn("" + (MESSAGE_MAX_CHARS - ta.value.length)));
            }
        }
        return true;
    }

    this.shareMessageToggle = function() {
        var lnk = shbyid("ceshare_messagelink");
        
        if(CE.hCN(shbyid("ceshare_messagediv"), "hidden")) {
            CE.rCN(shbyid("ceshare_messagediv"), "hidden");

            var rc = shbyid("ceshare_remchars");
            if(rc) {
                CE.rac(rc);

                var ta = shbyid("ceshare_message");
                if(ta) {
                    rc.appendChild(CE.dctn("" + (MESSAGE_MAX_CHARS - ta.value.length)));
                }
            }
            CE.rac(lnk);
            lnk.appendChild(CE.dci(CE.STRTAB.lookup("imgbase")+'icon-message.png'));
            lnk.appendChild(CE.dctn(' ' + CE.STRTAB.lookup('view.removethismsg')));
        } else {
            CE.aCN(shbyid("ceshare_messagediv"), "hidden");
            var ta = CE.CEU.$(shbyid("ceshare_message"));
            if(ta) ta.value = "";
            
            var rc = shbyid("ceshare_remchars");
            CE.rac(rc);
            rc.appendChild(CE.dctn("" + MESSAGE_MAX_CHARS));

            CE.rac(lnk);
            lnk.appendChild(CE.dci(CE.STRTAB.lookup("imgbase")+'icon-message.png'));
            lnk.appendChild(CE.dctn(' ' + CE.STRTAB.lookup('view.addamsg')));
        }
    }

    this.enterShareMode = function() {
        if(!disallowedShareWarning()) {
            return enterShareMode();
        }
    }
    
    function shbyid(id) {
        return CE.CEU.$(id + "1");
    }            

    function enterShareMode() {
        if(!CE.hCN(shbyid('ceshare'), 'hidden')) {
            closeShareWindow();
            return false;
        }

        var folder   = getCurrentFolder();
        var isCousin = false;
        if((g_curpath && g_curpath.length > 0 && g_curpath[g_curpath.length-1].file &&
           g_curpath[g_curpath.length-1].file.cousins && g_curpath[g_curpath.length-1].file.cousins.length > 0) ||
           (folder && folder.cousins)) {
            isCousin = true;
        }
        
        if(g_wasSearch && !isCousin) {
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.share.title'), CE.STRTAB.lookup('view.share.nofilter'),
                [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
            dlg.show();
            dlg = null;
            return;
        }
        if(CE.CEUI.isNothingAvailable()) {
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.share.title'), CE.STRTAB.lookup("view.share.notavail"),
                [{name:'ok',label:CE.STRTAB.lookup('button.ok')}]);
            dlg.show();
            dlg = null;
            return;
        }

        var share = findFolderShare(folder);
        openShareWindow(share);
        return false;
    };

    this.enterRSSMode = function(seriously, turnon, justdoit) {
        var rsscheck = shbyid("cesharerss_check");

        if(!shbyid("ceshare_check").checked && (rsscheck.checked || seriously)) {
            // First turn on sharing
            shbyid("ceshare_check").checked = true;
            shareFolder(false, function(){that.enterRSSMode(seriously, true);});
            return;
        }

        var folder = getCurrentFolder();
        var share = findFolderShare(folder);

        if(seriously || turnon) {
            rsscheck.checked = true;
        }
        if(!share) {
            var msg = null;
            if(folder.fileid == '0') {
                msg = CE.STRTAB.lookup("view.rss.sharedrivefirst");
            } else { 
                msg = CE.STRTAB.lookup("view.rss.sharefolderfirst");
            }
            var dlg = new CE.CEU.Dialog("RSS", msg, [{name: 'ok', label: CE.STRTAB.lookup('ceui.ok')}]);
            dlg.show(); dlg = null;
            rsscheck.checked = false;
            return;
        }
        if(folder) {
            if(!seriously && rsscheck.checked && folder.fileid == '0') {
                msg = CE.STRTAB.lookup('view.rss.entiredrive');
                var dlg = new CE.CEU.Dialog("RSS", msg, [{name: 'ok', label: CE.STRTAB.lookup('ceui.ok'), callback: function() { that.enterRSSMode(true); return true;}}, 
                                                         {name: 'cancel', label: CE.STRTAB.lookup('ceui.cancel')}]);
                dlg.show(); dlg = null;
                rsscheck.checked = false;
                return;
            }
            if(share) {
                if(rsscheck.checked) {
                    var args = ['albumid', share.albumid];
                    trackEvent('Share', 'RSS', share.files ? share.files[0] : share.root);
                    CE.CEU.svc.asyncRPC("POST", "generateRSSURL", args, onEnterRSSModeSuccess, onEnterRSSModeFailure, share);
                } else {
                    if(!justdoit) {
                        var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.rss.disable.title'), CE.STRTAB.lookupel("view.rss.disable.msg"),
                            [{name:'yes',label:CE.STRTAB.lookup("view.yes"),callback:function(){that.enterRSSMode(seriously,turnon,true);return true;}}, 
                            {name:'no',label:CE.STRTAB.lookup("view.no"),callback:function(){rsscheck.checked=true;return true;}}]);
                        dlg.show(); dlg = null;
                        return;
                    }
                    if(share.feedid) {
                        CE.CEU.svc.asyncRPC("POST", "removeFolderShare", ['shareid', share.feedid, 'sharetype', '1' ],
                                            onExitRSSModeSuccess, onExitRSSModeFailure, share);
                    }
                }
            }
        }
    };

    function showPublicURLSection(share) {
        var mydiv = shbyid("ceshare_puburldisplay");
        if(mydiv) {
            CE.rac(mydiv);

            if(share && share.publicid) {
                var url = 'http://' + window.location.host + '/share/' + share.publicid + '/';
                var ta = CE.dce('textarea', 'puburlta');
                ta.readOnly = true;
                ta.appendChild(CE.dctn(url));
                mydiv.appendChild(ta);
                
                CE.rCN(shbyid("ceshare_puburlbody"), "hidden");
                
                var a = shbyid("ceshare_puburllink");
                if(a) {
                    a.href = url;
                }
            } else {
                CE.aCN(shbyid("ceshare_puburlbody"), "hidden");

            }
        }
    }

    function onGeneratePublicURLSuccess(r, d) {
        if(d.share && r && r.url) {
            var idx = r.url.indexOf("share/");
            var publicid = r.url.substr(idx+6);
            d.share.publicid = publicid;
        }
        showPublicURLSection(d.share);
        
        // Update display of social networking options
        updateShareWindowSocialOptsVisibility('twitter');
        updateShareWindowSocialOptsVisibility('facebook');
        updateShareWindowSocialOptsVisibility('myspace');
        
        // If there was another option that triggered enabling the public URL, complete it now
        if(d.nextOpt)
            d.nextOpt();
    } 

    function onExitPublicURLSuccess(r, share) {
        share.publicid = null;
        showPublicURLSection(share);
    }

    function openSyncPane() {
        closeTray(null, true);
        CE.rCN('cesync', 'hidden');
        updateContentSize();
        
        CE.CEU.showLoadingAni(true);
        refreshSyncPane();

        if(!CE.CEUI.g_options['cesync_hint']) {
            CE.rCN('cesync_hint_noautop', 'hidden');
            CE.rCN('cesync_hint',         'hidden');
        }
    }
    
    function refreshSyncPane() {
        var folder = getCurrentFolder();
        if(!folder)
            return;
        var svc = CE.CEUI.getCurSvc(folder);

        CE.aCN('cesync_none', 'hidden');
        CE.rac('cesync_table');
        CE.CEU.showLoadingAni(true);
        
        for(var s in g_svcmap) {
            g_svcmap[s].isValidSyncSource = (g_svcmap[s].type.indexOf('xce:plugfs') == 0 &&
                                             hasFeature('foldersync',g_svcmap[s].deviceid));
        }
        
        var config = new CE.CEU.ActiveCopyConfig(g_svcmap, g_sharemap, onGenericFailure);
        
        var table = config.appendSyncTableHead(CE.CEU.$('cesync_table'));
        config.appendSyncTableRows(table, refreshSyncPane, svc, folder.fileid,
            function() {
                // Nothing setup; show tip instead of table
                CE.rac('cesync_table');
                CE.rCN('cesync_none', 'hidden');
                CE.CEU.showLoadingAni(false);
            },
            function() {
                CE.CEU.showLoadingAni(false);
            });
    }
    
    this.chooseSyncDest = function(evt) {
        for(var s in g_svcmap) {
            g_svcmap[s].isValidSyncSource = (g_svcmap[s].type.indexOf('xce:plugfs') == 0 &&
                                             hasFeature('foldersync',g_svcmap[s].deviceid));
        }

        var config = new CE.CEU.ActiveCopyConfig(g_svcmap, g_sharemap, onGenericFailure);
        config.chooseSyncDest(evt, null, null, getCurrentFolder().fileid, CE.CEUI.getCurSvc(), refreshSyncPane);
    };
    
    this.closeSyncPane = function() {
        CE.aCN('cesync', 'hidden');
        CE.rac('cesync_table');
        updateContentSize();
    };

    this.toggleSyncPane = function() {
        if(CE.hCN('cesync','hidden'))
            openSyncPane();
        else
            that.closeSyncPane();
    };
    
    this.showSyncConfig = function() {
        CE.CEUI.clearContent('celist');
        CE.CEUI.g_cursvc   = null;
        CE.CEUI.g_curalbum = null;

        var modes = CE.CEU.$('cetools_slidedirmodes');
        CE.rCN('cetools_viewmodes', 'hidden');
        CE.CEU.$('cetools_viewmodes').style.visibility = 'visible';
        if(modes) modes.style.display = 'none';
        CE.rCN('cetools_nonslide', 'hidden');
        CE.rCN('cetools_slide_write', 'hidden');
        CE.aCN('cetools_slide', 'hidden');
        CE.aCN('cetools_print', 'hidden');

        enableDisableToolbarButtons(true);
        closeTray();
        CE.aCN('cecontent_controls', 'hidden');
        updateSidebarState('none');
        CE.aCN(CE.CEU.$('cesearch_sync').parentNode, 'active');
        
        var div = CE.CEU.$('celist').appendChild(CE.dce('div',null,'padding6'));
        var syncDiv = div.appendChild(CE.dce('div'));

        for(var s in g_svcmap) {
            g_svcmap[s].isValidSyncSource = (g_svcmap[s].type.indexOf('xce:plugfs') == 0 &&
                                             hasFeature('foldersync',g_svcmap[s].deviceid));
        }

        var config = new CE.CEU.ActiveCopyConfig(g_svcmap, g_sharemap, onGenericFailure, syncDiv);
        config.showConfig();
    };

    function closeTray(onlyIfInvalid, noContentResize, leaveSrc) {
        if(!CE.hCN(shbyid('ceshare'),'hidden') && (!onlyIfInvalid || CE.hCN('cetool_share','cedisabled'))) {
            CE.aCN(shbyid('ceshare'), 'hidden');
            if(!noContentResize)
                updateContentSize();
        }
        
        if(!CE.hCN('cesrctray','hidden')) {
            var trayMismatch = ((!CE.CEUI.g_isSlideAlb && !CE.hCN('cesrctray_hint_slide','hidden')) ||
                                (CE.CEUI.g_isSlideAlb && CE.hCN('cesrctray_hint_slide','hidden')));
            if(!onlyIfInvalid || trayMismatch) {
                if(!leaveSrc || trayMismatch) {
                    CE.aCN('cesrctray',               'hidden');
                    CE.aCN('cesrctophintto_cont',     'hidden');
                    CE.aCN('cesrctophinttofrom_cont', 'hidden');
                    if(!noContentResize)
                        updateContentSize();
                }
            }
        }

        if(!CE.hCN('cesync','hidden') && (!onlyIfInvalid || CE.hCN('cetool_sync','cedisabled'))) {
            CE.aCN('cesync', 'hidden');
            CE.rac('cesync_table');
            if(!noContentResize)
                updateContentSize();
        }
    }
    
    function getTrayEl() {
        var tray = shbyid('ceshare');
        if(!CE.hCN(tray,'hidden'))
            return tray;
        tray = CE.CEU.$('cesrctray');
        if(!CE.hCN(tray,'hidden'))
            return tray;
        tray = CE.CEU.$('cesync');
        if(!CE.hCN(tray,'hidden'))
            return tray;
        return null;
    }
    
    this.onHintOver = function(name) {
        if(CE.CEUI.g_options[name]) {
            CE.aCN(name+'_noautop', 'hidden');
            CE.rCN(name,            'hidden');
        }
    };
    
    this.onHintOut = function(name) {
        if(CE.CEUI.g_options[name]) {
            CE.aCN(name, 'hidden');
        }
    };

    this.onHintNoAuto = function(name) {
        var checked = CE.CEU.$(name+'_noauto').checked;
        setOption(name, checked);
        if(checked) {
            CE.aCN(name, 'hidden');
        }
    };

    //----------------------------------------------------------------------------------------
    // Common social-networking functions
    function showSocialMsg(socialNetwork, messageType, cb) {
        var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.puburl.'+socialNetwork+'.title'), CE.STRTAB.lookupel('view.puburl.'+socialNetwork+'.'+messageType), 
                                    [{name:'ok',label:CE.STRTAB.lookup('button.ok'),callback:function(){if(cb)cb();return true;}}]);
        dlg.show();
        dlg = null;
        return true;
    }
    
    function socialGetRawTokens(network, svc, tokens, cb) {
        if(tokens['fetched']) {
            if(cb) cb();
            return;
        }
        CE.CEU.svc.asyncRPC('POST', 'featureCommand', ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
            'feature', network, 'command', 'get_raw_tokens'], function(r){
                for(var name in r) {
                    if(name.substr(0,4) == 'hbpf')
                        tokens[name] = r[name];
                }
                tokens['fetched'] = true;
                if(cb) cb();
            }, onGenericFailure);
    }
    
    function socialEnsureAllAuthorized(network, cb) {
        // We're alread authorized for the first root, but if this album has multiple roots
        //   we need to make sure that they are all authorized as well by copying tokens
        var folders = CE.CEUI.getCurShareFiles();
        if(folders.length < 2) {
            if(cb) cb();
            return;
        }
        
        var tokens   = {};
        var complete = 1;
        for(var i = 1; i < folders.length; ++i) {
            (function(i) {
                var svc = CE.CEUI.getCurSvc(folders[i]);
                CE.CEU.svc.asyncRPC('POST', 'featureCommand', ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                    'feature', network, 'command', 'is_authorized'], function(){++complete;},
                function() {
                    // Get the tokens from the already authorized plug
                    socialGetRawTokens(network, CE.CEUI.getCurSvc(folders[0]), tokens, function(){
                        // Copy the tokens to the unauthorized plug
                        var args = ['deviceid',svc.deviceid, 'serviceid',svc.serviceid, 'feature',network, 'command','set_raw_tokens'];
                        for(var name in tokens)
                            args.push(name, tokens[name]);
                        CE.CEU.svc.asyncRPC('POST', 'featureCommand', args, function(){++complete;}, onGenericFailure);
                    });
                });
            })(i);
        }

        // Wait until all plugs have been authorized
        var interval = setInterval(function() {
            if(complete == folders.length) {
                clearInterval(interval);
                if(cb) cb();
            }
        }, 100);
    }

    function socialPostStatus(svc, network) {
        if(network == 'facebook') {
            // Facebook is a special case
            return facebookPostLink(svc);
        } else {
            // Prompt the user for some text for their status
            CE.CEU.showMultiInputDialog("view.puburl."+network+".title",
                                        [{message:"view.puburl."+network+".msg", inputcls:"std-field-wide", inputid:"status_text", inputtype:"text", dflt:CE.STRTAB.lookup("view.puburl."+network+".dflttxt"), twoline:true}],
                                        "button.ok", function()
            {
                var statusText = CE.CEU.$("status_text").value;
                if(!CE.CEU.$(network+"_check").checked) {
                    socialEnsureAllAuthorized(network, function() {
                        socialSetNotifyMsg(svc, network, statusText); // enable notification of changes
                    });
                }
                
                var statusText = statusText + ' ' + CE.CEU.$("puburlta").value;
                var fileId     = g_shareedit.root ? g_shareedit.root.fileid : (g_shareedit.files ? g_shareedit.files[0].fileid : -1);
                
                trackEvent('Share', network, g_shareedit ? (g_shareedit.files ? g_shareedit.files[0] : g_shareedit.root) : null);
                
                CE.CEU.svc.asyncRPC('POST', 'featureCommand', 
                                    ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                                    'feature', network, 'command', 'post', "text", statusText, "fileid", fileId],
                                    function(){
                                        CE.CEU.$(network+"_lastpubdate").innerHTML = (new Date()).format('mmmm d, yyyy "at" h:MMTT');
                                        CE.CEU.$(network+"_lastpub").style.display = 'inline';
                                        CE.CEU.$(network+"_repost").style.display  = 'block';
                                        return showSocialMsg(network,'posted');
                                    },
                                    function(){return showSocialMsg(network,'error');}, svc);
                return true;
            });
            
            // Maximum length for text
            var url = CE.CEU.$("puburlta").value;
            var maxLen = 140 - url.length - 1;
            CE.CEU.$("status_text").maxLength = maxLen;
        }
    }
    
    this.postToSocialAuthorized = function(network) {
        var svc = g_shareedit.root ? g_shareedit.root : (g_shareedit.files ? g_shareedit.files[0] : (g_shareedit.serviceid ? g_shareedit : CE.CEUI.getCurSvc()));
        if(!shbyid("ceshare-check-publicurl").checked) {
            shbyid("ceshare-check-publicurl").checked = true;
            that.enablePublicURL(false,function(){
                showSocialMsg(network, 'pubviewable', function(){
                    socialPostStatus(svc, network);
                });
            });
        } else {
            socialPostStatus(svc,network);
        }
    }

    this.postToSocial = function(network) {
        if(!shbyid("ceshare_check").checked) {
            // First turn on sharing
            shbyid("ceshare_check").checked = true;
            shareFolder(false, function(){that.postToSocial(network);});
            return false;
        }

        // If we're not already authorized, we'll need to get authorization before posting
        var svc = g_shareedit.root ? g_shareedit.root : (g_shareedit.files ? g_shareedit.files[0] : (g_shareedit.serviceid ? g_shareedit : CE.CEUI.getCurSvc()));
        CE.CEU.svc.asyncRPC('POST', 'featureCommand', 
                            ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                            'feature', network, 'command', 'is_authorized'],
                            function(){that.postToSocialAuthorized(network);return true;},
                            function(r,d,m){
                                if(!CE.CEU.isLoggedIn())
                                    return onGenericFailure(r, d, m);
                                if(network == 'twitter')
                                    that.twitterGetReqToken(function(){that.postToSocialAuthorized(network);return true;});
                                else if(network == 'myspace')
                                    that.myspaceGetReqToken(function(){that.postToSocialAuthorized(network);return true;});
                                else if(network == 'facebook')
                                    that.facebookGetAuth(function(){that.postToSocialAuthorized(network);return true;});
                            }, svc);
        return false;
    }
    
    function socialSetNotifyMsg(svc, network, text) {
        if(CE.hCN(network+'_repost_mask','hidden'))
            return true; // no notifications for this share
        
        var statusText = text + ' ' + CE.CEU.$("puburlta").value;
        
        CE.CEU.svc.asyncRPC('POST', 'updateAlbumShare', 
                            ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                            'albumid', g_shareedit.albumid, 'socialnotif', '1', 'network', network,
                            "text", statusText],
                            function(){CE.CEU.$(network+"_check").checked = true;
                                       CE.CEU.$(network+"_authcheck").checked = true;
                                       CE.CEU.$(network+"_editnotif").style.display = 'inline';
                                       return true;},
                            function(){return showSocialMsg(network,'notify.error');}, svc);
        return true;
    }

    function socialSetNotify(svc, network, defaultText) {
        socialEnsureAllAuthorized(network, function(){
            if(network == 'facebook') {
                // Facebook is a special case
                return facebookSetNotify(svc, defaultText);
            } else {
                if(!defaultText || defaultText == '')
                    defaultText = CE.STRTAB.lookup("view.puburl."+network+".notify.dflttxt");
                
                // Prompt the user for some text for their notification
                CE.CEU.showMultiInputDialog("view.puburl."+network+".title",
                                            [{message:"view.puburl."+network+".notify.msg", inputcls:"std-field-wide", inputid:"status_text", inputtype:"text", dflt:defaultText, twoline:true}],
                                            "button.ok", function(){return socialSetNotifyMsg(svc,network,CE.CEU.$("status_text").value);});
                // Maximum length for text
                var url = CE.CEU.$("puburlta").value;
                var maxLen = 140 - url.length - 1;
                CE.CEU.$("status_text").maxLength = maxLen;
            }
        });
    }
    
    this.notifySocialAuthorized = function(network, defaultText) {
        var svc = g_shareedit.root ? g_shareedit.root : (g_shareedit.files ? g_shareedit.files[0] : (g_shareedit.serviceid ? g_shareedit : CE.CEUI.getCurSvc()));
        if(!shbyid("ceshare-check-publicurl").checked) {
            shbyid("ceshare-check-publicurl").checked = true;
            that.enablePublicURL(false,function(){
                showSocialMsg(network, 'pubviewable', function(){
                    socialSetNotify(svc, network, defaultText);
                });
            });
        } else {
            socialSetNotify(svc, network, defaultText);
        }
    }
    
    this.notifySocial = function(network, turnon) {
        if(CE.hCN(network+'_repost_mask','hidden'))
            return; // no notifications for this share

        if(!shbyid("ceshare_check").checked && CE.CEU.$(network+"_check").checked) {
            // First turn on sharing
            shbyid("ceshare_check").checked = true;
            shareFolder(false, function(){that.notifySocial(network,true);});
            return;
        }
        
        if(turnon)
            CE.CEU.$(network+"_check").checked = true;

        CE.CEU.$(network+"_authcheck").checked = CE.CEU.$(network+"_check").checked;

        var svc    = g_shareedit.root ? g_shareedit.root : (g_shareedit.files ? g_shareedit.files[0] : (g_shareedit.serviceid ? g_shareedit : CE.CEUI.getCurSvc()));
        var fileId = g_shareedit.root ? g_shareedit.root.fileid : (g_shareedit.files ? g_shareedit.files[0].fileid : -1);

        if(!CE.CEU.$(network+"_check").checked) {
            // Disable notification
            CE.CEU.$(network+"_editnotif").style.display = 'none';
            CE.CEU.svc.asyncRPC('POST', 'updateAlbumShare', 
                                ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                                'albumid', g_shareedit.albumid, 'socialnotif', '0', 'network', network,
                                "text", "", "msg", "", "url", "", "title", "", "urllabel", ""],
                                function(){
                                    updateShareWindowSocialOptsVisibility(network);
                                },
                                function(r,d,m){
                                    if(!CE.CEU.isLoggedIn())
                                        return onGenericFailure(r, d, m);
                                    else
                                        return showSocialMsg(network,'notify.error');
                                }, svc);
        } else {
            // If we're not already authorized, we'll need to get authorization before allowing notification updates
            CE.CEU.$(network+"_editnotif").style.display = 'inline';
            CE.CEU.svc.asyncRPC('POST', 'featureCommand', 
                                ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                                'feature', network, 'command', 'is_authorized', 'fileid', fileId],
                                function(r){return that.notifySocialAuthorized(network, r.notify_text);},
                                function(r,d,m){
                                    if(!CE.CEU.isLoggedIn())
                                        return onGenericFailure(r, d, m);
                                    if(network == 'twitter')
                                        that.twitterGetReqToken(function(){return that.notifySocialAuthorized(network);});
                                    else if(network == 'myspace')
                                        that.myspaceGetReqToken(function(){return that.notifySocialAuthorized(network);});
                                    else if(network == 'facebook')
                                        that.facebookGetAuth(function(){return that.notifySocialAuthorized(network);});
                                }, svc);
        }
    }
    
    //----------------------------------------------------------------------------------------
    // Twitter authentication
    this.onTwitterReqTokenSuccess = function(r, nextFunc) {
        var svc = g_shareedit.root ? g_shareedit.root : (g_shareedit.files ? g_shareedit.files[0] : (g_shareedit.serviceid ? g_shareedit : CE.CEUI.getCurSvc()));
        // Popup a window at twitter.com so that they can authorize us
        var url = "http://twitter.com/oauth/authorize?oauth_token=" + encodeURIComponent(r.token);
        window.open(url, "twit_auth", 'width=800,height=450,status=0,toolbar=0,location=0,menubar=0,directories=0,resizable=1,scrollbars=1');

        CE.CEU.showMultiInputDialog("view.puburl.twitter.title",
                                    [{message:"view.puburl.twitter.auth", inputcls:"std-field-wide", inputid:"twitpin", inputtype:"text", dflt:'', twoline:true}],
                                    "button.ok",
                                    function()
        {
            var pin = CE.CEU.$("twitpin").value;
            
            CE.CEU.svc.asyncRPC('POST', 'featureCommand', 
                                ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                                'feature', 'twitter', 'command', 'get_access_token', 'pin', pin],
                                nextFunc,
                                function(){return showSocialMsg('twitter','errorauth');}, svc);
            return true;
        });
    }    
    
    this.twitterGetReqToken = function(nextFunc) {
        var svc = g_shareedit.root ? g_shareedit.root : (g_shareedit.files ? g_shareedit.files[0] : (g_shareedit.serviceid ? g_shareedit : CE.CEUI.getCurSvc()));
        // Warn them that we're about to auth
        var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.puburl.twitter.title'), CE.STRTAB.lookupel("view.puburl.twitter.authing"), 
                                    [{name:'ok',label:CE.STRTAB.lookup('button.ok'),callback:function()
        {
            // Pop a blank window in anticipation of success; if we do it in the callback, it'll be blocked by the browser
            window.open("", "twit_auth", 'width=800,height=450,status=0,toolbar=0,location=0,menubar=0,directories=0,resizable=1,scrollbars=1');
            CE.CEU.svc.asyncRPC('POST', 'featureCommand', 
                                ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                                'feature', 'twitter', 'command', 'get_req_token'],
                                function(r){that.onTwitterReqTokenSuccess(r,nextFunc);},
                                function(){window.open("http://"+window.location.host+"/twitter_auth.html","twit_auth");
                                           return showSocialMsg('twitter','errorauth');});
            
            return true;
        }}]);
        dlg.show();
        dlg = null;
    }

    //----------------------------------------------------------------------------------------
    // MySpace authentication
    this.onMyspaceReqTokenSuccess = function(r, nextFunc) {
        var svc = g_shareedit.root ? g_shareedit.root : (g_shareedit.files ? g_shareedit.files[0] : (g_shareedit.serviceid ? g_shareedit : CE.CEUI.getCurSvc()));
        // Popup a window at myspace.com so that they can authorize us
        //var callbackUrl = "http://my.pogoplug.com/myspace_auth.html";
        ///var i = document.URL.indexOf("//");
        //if(i != -1) {
        //    i = document.URL.indexOf("/", i+3);
        //    if(i != -1) {
        //        callbackUrl = document.URL.substr(0,i) + "/myspace_auth.html";
        //    }
        //}
        var callbackUrl = "http://" + window.location.host + "/mysqpace_auth.html";
        var url = "http://api.myspace.com/authorize?oauth_token=" + encodeURIComponent(r.token) + "&oauth_callback=" + encodeURIComponent(callbackUrl);
        var win = window.open(url, "myspace_auth", 'width=800,height=450,status=0,toolbar=0,location=0,menubar=0,directories=0,resizable=1,scrollbars=1');
        
        var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.puburl.myspace.title'), CE.STRTAB.lookupel("view.puburl.myspace.authclose"), 
                                    [{name:'ok',label:CE.STRTAB.lookup('button.ok'),callback:function() {
            CE.CEU.svc.asyncRPC('POST', 'featureCommand', 
                                ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                                'feature', 'myspace', 'command', 'get_access_token'],
                                nextFunc,
                                function(){return showSocialMsg('myspace','errorauth');}, svc);
            return true;
        }}]);
        dlg.show();
        dlg = null;
    }
    
    this.myspaceGetReqToken = function(nextFunc) {
        var svc = g_shareedit.root ? g_shareedit.root : (g_shareedit.files ? g_shareedit.files[0] : (g_shareedit.serviceid ? g_shareedit : CE.CEUI.getCurSvc()));
        // Warn them that we're about to auth
        var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.puburl.myspace.title'), CE.STRTAB.lookupel("view.puburl.myspace.authing"), 
                                    [{name:'ok',label:CE.STRTAB.lookup('button.ok'),callback:function()
        {
            // Pop a blank window in anticipation of success; if we do it in the callback, it'll be blocked by the browser
            window.open("", "myspace_auth", 'width=800,height=450,status=0,toolbar=0,location=0,menubar=0,directories=0,resizable=1,scrollbars=1');
            CE.CEU.svc.asyncRPC('POST', 'featureCommand', 
                                ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                                'feature', 'myspace', 'command', 'get_req_token'],
                                function(r){that.onMyspaceReqTokenSuccess(r,nextFunc);},
                                function(){return showSocialMsg('myspace','errorauth');}, svc);
            
            return true;
        }}]);
        dlg.show();
        dlg = null;
    }

    //----------------------------------------------------------------------------------------
    // Facebook authentication & posting
    function fbRequireSession(cb) {
        var clearFbTimeouts = function(numToClear) {
            var lastTimeoutId = window.setTimeout(function(){});
            // clear timeout from the line above, and facebook's pending timeouts
            for(var i = 0; i <= numToClear; i++) {
                window.clearTimeout(lastTimeoutId - i);
            }
        };
        FB.Connect.requireSession(function() {
            // Apparently we need to do some resetting-timeout magic to clear a timeout set by facebook
            clearFbTimeouts(5);
            if(cb) cb();
        }, null, true);
    }
    
    this.facebookGetAuthReady = function(nextFunc) {
        var appKey = "fad96babe81e9b5c4c9887efc50d4027"; // *.pogoplug.com
        var cloud  = "0";
        
        if(document.domain.toLowerCase().indexOf("cloudengines.com") != -1) {
            cloud  = "1";
            appKey = "6b5588a479c3e87895760dd4a3a02c11"; // *.cloudengines.com
        }
        
        FB.init(appKey, "fb_xd_receiver.html");
        
        var doIt = function() {
            fbRequireSession(
            function() {
                FB.Connect.showPermissionDialog("offline_access,publish_stream",
                function() {
                    FB.Facebook.get_sessionState().waitUntilReady(
                    function() {
                        CE.CEU.showLoadingAni(false);
                        // We've been authorized, so store the facebook session_key for later use
                        var sr  = FB.Facebook.apiClient.get_session();
                        var svc = g_shareedit.root ? g_shareedit.root : (g_shareedit.files ? g_shareedit.files[0] : (g_shareedit.serviceid ? g_shareedit : CE.CEUI.getCurSvc()));
                        CE.CEU.svc.asyncRPC('POST', 'featureCommand', 
                                            ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                                            'feature', 'facebook', 'command', 'set_authorized', "session_key", sr.session_key, "server", cloud],
                                            nextFunc,
                                            function(){return showSocialMsg('facebook','autherror');}, svc);
                    });
                });
            });
        }

        FB.ensureInit(function() {
            var uid = FB.Connect.get_loggedInUser();
            if(uid) {
                FB.Connect.forceSessionRefresh();
                setTimeout(doIt, 4000);
            } else {
                doIt();
            }
        });
    };

    this.facebookGetAuth = function(nextFunc) {
        CE.CEU.showLoadingAni(true);
        if(CE.CEU.$("FB_HiddenContainer")) {
            that.facebookGetAuthReady(nextFunc);
        } else {
            // Need to create this element before dynamically loading the connect script, or all hell breaks loose
            var fbHiddenDiv = document.createElement("div");
            fbHiddenDiv.id = "FB_HiddenContainer";
            fbHiddenDiv.style.position = "absolute";
            fbHiddenDiv.style.left = "-10000px";
            fbHiddenDiv.style.top = "-10000px";
            fbHiddenDiv.style.width = "0px";
            fbHiddenDiv.style.height = "0px";
            document.body.insertBefore(fbHiddenDiv, window.document.body.firstChild);

            var script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = 'http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php';
            document.body.appendChild(script);
            
            // Wait until the script has loaded
            var interval = setInterval(function() {
                if(typeof FB != 'undefined' && typeof FB.init != 'undefined' && typeof FB.ensureInit != 'undefined') {
                    clearInterval(interval);
                    that.facebookGetAuthReady(nextFunc);
                }
            }, 50);
        }
    }
    
    function facebookPostLink(svc) {
        // Prompt the user for some text for their post
        CE.CEU.showMultiInputDialog("view.puburl.facebook.title",
                                    [{message:"view.puburl.facebook.msg", inputcls:"std-field-wide", inputid:"post_text", inputtype:"text", dflt:CE.STRTAB.lookup("view.puburl.facebook.dflttxt"), twoline:true}],
                                    "button.ok", function()
        {
            var postText = CE.CEU.$("post_text").value;
            if(!CE.CEU.$("facebook_check").checked) {
                socialEnsureAllAuthorized('facebook', function() {
                    facebookSetNotifyMsg(svc, postText); // enable notification of changes
                });
            }

            var fileId = g_shareedit.root ? g_shareedit.root.fileid : (g_shareedit.files ? g_shareedit.files[0].fileid : -1);

            trackEvent('Share', 'facebook', g_shareedit ? (g_shareedit.files ? g_shareedit.files[0] : g_shareedit.root) : null);
            
            CE.CEU.svc.asyncRPC('POST', 'featureCommand', 
                                ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                                'feature', 'facebook', 'command', 'post',
                                "msg", postText, "url", CE.CEU.$("puburlta").value, "fileid", fileId,
                                "title", CE.STRTAB.lookup("view.puburl.facebook.share.title"), "urllabel", CE.STRTAB.lookup("view.puburl.facebook.share.link")],
                                function(){
                                    CE.CEU.$("facebook_lastpubdate").innerHTML = (new Date()).format('mmmm d, yyyy "at" h:MMTT')
                                    CE.CEU.$("facebook_lastpub").style.display = 'inline';
                                    CE.CEU.$("facebook_repost").style.display  = 'block';
                                    return showSocialMsg('facebook','posted');
                                },
                                function(){return showSocialMsg('facebook','error');}, svc);
            return true;
        });
    }
    
    function facebookSetNotifyMsg(svc, text) {
        if(CE.hCN('facebook_repost_mask','hidden'))
            return true; // no notifications for this share

        CE.CEU.svc.asyncRPC('POST', 'updateAlbumShare', 
                            ['deviceid', svc.deviceid, 'serviceid', svc.serviceid,
                            'albumid', g_shareedit.albumid, 'socialnotif', '1', 'network', 'facebook',
                            "msg", text, "url", CE.CEU.$("puburlta").value,
                            "title", CE.STRTAB.lookup("view.puburl.facebook.notify.title"),
                            "urllabel", CE.STRTAB.lookup("view.puburl.facebook.share.link")],
                            function(){CE.CEU.$("facebook_check").checked = true;
                                       CE.CEU.$("facebook_authcheck").checked = true;
                                       CE.CEU.$("facebook_editnotif").style.display = 'inline';
                                       return true;},
                            function(){return showSocialMsg('facebook','notify.error');}, svc);
        return true;
    }

    function facebookSetNotify(svc, defaultText) {
        // Prompt the user for some text for their notification
        if(!defaultText || defaultText == '')
            defaultText = CE.STRTAB.lookup("view.puburl.facebook.notify.dflttxt");

        CE.CEU.showMultiInputDialog("view.puburl.facebook.title",
                                    [{message:"view.puburl.facebook.notify.msg", inputcls:"std-field-wide", inputid:"post_text", inputtype:"text", dflt:defaultText, twoline:true}],
                                    "button.ok", function(){return facebookSetNotifyMsg(svc,CE.CEU.$("post_text").value);});
    }
    
    function disableSocialNotification(network) {
        if(CE.CEU.$(network+"_shareopts").style.display != 'none') {
            CE.CEU.$(network+"_check").checked = false;
            CE.CEU.$(network+"_authcheck").checked = false;
            CE.CEU.$(network+"_editnotif").style.display = 'none';
            CE.CEUI.notifySocial(network);
        }
    }
    
    this.onClickAuthCheck = function(network) {
        var cbMain = CE.CEU.$(network+"_authcheck");
        var cbSub  = CE.CEU.$(network+"_check");
        if(cbMain.checked) {
            if(CE.CEU.$(network+"_repost").style.display == 'block') {
                cbSub.checked = true;
                CE.CEUI.notifySocial(network);
            } else {
                CE.CEUI.postToSocial(network);
            }
        } else {
            cbSub.checked = false;
            CE.CEUI.notifySocial(network);
        }
        return true;
    }

    this.enablePublicURL = function(seriously, nextOpt, turnon, justdoit) {
        var cb = shbyid("ceshare-check-publicurl");
        
        if(!shbyid("ceshare_check").checked && (cb.checked || seriously)) {
            // First turn on sharing
            shbyid("ceshare_check").checked = true;
            shareFolder(false, function(){that.enablePublicURL(seriously, nextOpt, true);});
            return;
        }
        if(!g_shareedit)
            return;
        var fileId = g_shareedit.files ? g_shareedit.files[0].fileid : (g_shareedit.root ? g_shareedit.root.fileid : null);

        if(seriously || turnon) {
            cb.checked = true;
        }
        if(fileId) {
            if(!seriously && cb.checked && fileId == '0') {
                msg = CE.STRTAB.lookup('view.puburl.entiredrive');
                var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup("view.puburl.entiredrive.title"), msg, 
                    [{name: 'ok', label: CE.STRTAB.lookup('ceui.ok'), callback: function() { that.enablePublicURL(true,nextOpt); return true;}}, 
                     {name: 'cancel', label: CE.STRTAB.lookup('ceui.cancel')}]);
                dlg.show(); dlg = null;
                cb.checked = false;
                return;
            }
            if(g_shareedit) {
                if(cb.checked) {
                    trackEvent('Share', 'Public', g_shareedit.files ? g_shareedit.files[0] : g_shareedit.root);
                    CE.CEU.svc.asyncRPC("POST", "generatePublicURL", ['albumid',g_shareedit.albumid],
                                        onGeneratePublicURLSuccess, onGenericFailure,
                                        {'share':g_shareedit,'nextOpt':nextOpt});
                } else {
                    if(!justdoit) {
                        var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.puburl.disable.title'), CE.STRTAB.lookupel("view.puburl.disable.msg"),
                            [{name:'yes',label:CE.STRTAB.lookup("view.yes"),callback:function(){that.enablePublicURL(seriously,nextOpt,turnon,true);return true;}}, 
                            {name:'no',label:CE.STRTAB.lookup("view.no"),callback:function(){cb.checked=true;return true;}}]);
                        dlg.show(); dlg = null;
                        return;
                    }
                    if(g_shareedit.publicid) {
                        CE.CEU.svc.asyncRPC("POST", "removeFolderShare", ['shareid', g_shareedit.publicid, 'sharetype', '2'],
                                            onExitPublicURLSuccess, onGenericFailure, g_shareedit);
                    }
                    // Since we're disabling the public URL, disable notification of public updates
                    if(g_shareedit.albumtype != FILE_TYPE_FILEALBUM) {
                        disableSocialNotification('twitter');
                        disableSocialNotification('facebook');
                        disableSocialNotification('myspace');
                    }
                }
            }
       }
    };

    function onEnableShareRepostSuccess(r, share) {
        if(!shbyid("ceshare-check-repost").checked) {
            CE.CEU.$("mailnotif_lastpubdate").innerHTML = '';
            CE.CEU.$("mailnotif_lastpub").style.display = 'none';
        }
    } 

    function onEnableShareRepostFailure(r,  share) {
        var cb = shbyid("ceshare-check-repost");
        cb.checked = false;
        CE.CEU.$("mailnotif_lastpubdate").innerHTML = '';
        CE.CEU.$("mailnotif_lastpub").style.display = 'none';
    }

    this.enableShareRepost = function(turnon) {
        var cb = shbyid("ceshare-check-repost");

        if(!shbyid("ceshare_check").checked && cb.checked) {
            // First turn on sharing
            shbyid("ceshare_check").checked = true;
            shareFolder(false, function(){that.enableShareRepost(true);});
            return;
        }
        
        if(turnon)
            cb.checked = true;

        var folder = getCurrentFolder();
        var share = findFolderShare(folder);

        if(share) {
            var args =  [ "albumid", share.albumid, "autonotify" ];
            if(cb.checked) {
                args.push("1");
            } else {
                args.push("0");
            }
            CE.CEU.svc.asyncRPC("POST", "updateAlbumShare", args, onEnableShareRepostSuccess, onEnableShareRepostFailure, share);
        }
    };
    
    this.enableShareRepostMsg = function() {
        if(!shbyid("ceshare_check").checked) {
            // First turn on sharing
            shbyid("ceshare_check").checked = true;
            shareFolder(false, function(){that.enableShareRepostMsg();});
            return;
        }
        
        // Prompt the user for some text for the notification e-mail
        CE.CEU.showMultiInputDialog("view.repost.edit.title",
                                    [{message:"view.repost.edit.msg", inputcls:"std-field-wide", inputid:"notifmsg", dflt:g_mailNotifMsg, inputtype:"text", twoline:true}],
                                    "button.ok", function()
        {
            shbyid("ceshare-check-repost").checked = true;
            var folder = getCurrentFolder();
            var share  = findFolderShare(folder);
            if(share) {
                g_mailNotifMsg = CE.CEU.$("notifmsg").value;
                
                var args =  [ "albumid",        share.albumid,
                              "autonotify",     "1",
                              "autonotify_msg", g_mailNotifMsg ];
                CE.CEU.svc.asyncRPC("POST", "updateAlbumShare", args, onEnableShareRepostSuccess, onEnableShareRepostFailure, share);
            }
            return true;
        });
    }

    function onStartTrialSuccess(r) {
        reloadSidebar();
    }

    function onStartTrialFailure(r) {
        
        // do error dialog.
        
    }

    this.startTrial = function() {
        CE.aCN("cepayment", "hidden");
        if(that.g_cursvc && that.g_cursvc.device && that.g_cursvc.device.deviceid) {
            CE.CEU.svc.asyncRPC("POST", "updateDevice", [ "deviceid", that.g_cursvc.device.deviceid, "terms", "1" ], onStartTrialSuccess, onStartTrialFailure);
        }
    };

    this.gotoBilling = function() {
        if(that.g_cursvc && that.g_cursvc.device && that.g_cursvc.device.deviceid) {
            return window.location.replace("/billing/" + that.g_cursvc.device.deviceid + "?return=view.html&mode=setup");
        }
    };

    this.showWarningMessages = function() {
        CE.aCN("cereadonly", "hidden");

        if(!g_warnmessage) {
            g_warnmessage = new WarnMessageDialog();  
       }
        if(that.g_cursvc) {
            var eh = { 
                'service': that.g_cursvc, 
                'device': that.g_cursvc.device, 
                'onHide': updateServiceMessageCount, 
                'name': that.g_cursvc.name
            };
            g_warnmessage.show(eh);
        }
        return false;
    }

    this.cancelAsyncOp = function() {
        CE.CEU.svc.cancelLastRPC();
        CE.CEU.showLoadingAni(false);
        return false
    }
    
    this.hideShowHints = function() {
        if(CE.hCN('cehint', 'hidden')) {
            revealHint(true, true);
        } else {
            hideHint();
        }
    };
    
    function onHideHint(evt) {
        if(g_hints.length == 1) {
            hideHint();
        } else {
            for(var i = 0; i < g_hints.length; ++i) {
                if(g_hints[i].id == evt.id) {
                    g_hints[i].hidden = true;
                    refreshHintDisplay();
                    break;
                }
            }
        }
    }
    
    function redraw(el) {
        CE.aCN(el, 'cerefresh');
        setTimeout(function() {
            CE.rCN(el, 'cerefresh');
        }, 0);
    }
    
    function refreshHintDisplay(fast) {
        if(g_hints.length < 1) {
            hideHint();
            return;
        }
        
        if(g_hints_cur >= g_hints.length)
            g_hints_cur = g_hints.length - 1;
        
        var list = CE.CEU.$('cehint_list');
        CE.rac(list);
        var excs = ' cetop';
        
        for(var i = 0; i < g_hints.length; ++i) {
            if(g_hints[i].hidden)
                continue;
            var hint = list.appendChild(CE.dce('div',null,'cehint_hint'+excs));
            excs = '';
            hint.appendChild(g_hints[i].msg);
            
            var buts = hint.appendChild(CE.dce('div',null,'cehint_buts'));
            (buts.appendChild(CE.dca({onEvent:onHideHint,id:g_hints[i].id},null,'cebutton')))
                .appendChild(CE.STRTAB.lookupel('view.cehint.hide'));

            if(!g_hints[i].hideclose) {
                var closeFunc = function(evt) {
                    CE.CEUI.closeHint(evt.id);
                };

                (buts.appendChild(CE.dca({onEvent:closeFunc, id:g_hints[i].id},null,'cebutton')))
                    .appendChild(CE.STRTAB.lookupel('view.cehint.close'));
            }
        }
        
        revealHint(fast);
    }
    
    this.hideHints = function() {
        for(var i = g_hints.length-1; i >= 0; --i) {
            g_hints[i].hidden = true;
        }
        refreshHintDisplay(true);
    };
    
    this.deleteHints = function() {
        for(var i = g_hints.length-1; i >= 0; --i) {
            CE.CEUI.closeHint(g_hints[i].id);
        }
    };
    
    function updateHintRevealer() {
        if(g_hints.length < 1) {
            // No hints to show
            CE.aCN('cehint_revealer_num',     'hidden');
            CE.aCN('cehint_revealer_img_yes', 'hidden');
            CE.rCN('cehint_revealer_img_no',  'hidden');
        } else {
            // There are hints to show
            CE.rac('cehint_revealer_num');
            CE.CEU.$('cehint_revealer_num').appendChild(CE.dctn(g_hints.length));
            CE.rCN('cehint_revealer_num',     'hidden');
            CE.rCN('cehint_revealer_img_yes', 'hidden');
            CE.aCN('cehint_revealer_img_no',  'hidden');
            if(CE.hCN('cehint','hidden')) {
                CE.CEU.$('cehint_revealer_img_yes').title = CE.STRTAB.lookup('view.hint.showall');
                CE.CEU.$('cehint_revealer_num').title = CE.STRTAB.lookup('view.hint.showall');
            } else {
                CE.CEU.$('cehint_revealer_img_yes').title = CE.STRTAB.lookup('view.hint.hideall');
                CE.CEU.$('cehint_revealer_num').title = CE.STRTAB.lookup('view.hint.hideall');
            }
        }
    }
    
    function revealHint(fast, unhide) {
        if(g_hints_hider != null) {
            clearTimeout(g_hints_hider);
            g_hints_hider = null;
        }
        
        if(unhide) {
            for(var i = 0; i < g_hints.length; ++i) {
                g_hints[i].hidden = false;
            }
            return refreshHintDisplay(fast);
        }

        g_hints_hiding  = false;
        g_hints_showing = true;
        
        CE.rCN('cehint', 'hidden');
        
        var interval;
        var doStep = function() {
            if(!g_hints_showing) {
                if(interval) clearInterval(interval);
            } else {
                var fullHeight = CE.CEU.$('cehint_list').offsetHeight;
                var curHeight = CE.CEU.$('cehint').style.height;
                if(curHeight == '')
                    curHeight = CE.CEU.$('cehint').offsetHeight;
                else
                    curHeight = parseInt(curHeight);
                var diff = (curHeight < fullHeight) ? 3 : -3;
                curHeight += diff;
                if((diff > 0 && curHeight >= fullHeight) || (diff < 0 && curHeight <= fullHeight)) {
                    if(interval) clearInterval(interval);
                    curHeight = fullHeight;
                    g_hints_showing = false;
                    CE.CEUI.autoHintHider();
                    var hiddenHints = false;
                    for(var i = 0; i < g_hints.length; ++i) {
                        if(g_hints[i].hidden) {
                            hiddenHints = true;
                            break;
                        }
                    }

                    CE.CEU.$('cemain_pad').style.marginTop = curHeight-1 + 'px';
                    CE.CEU.$('cehint').style.height        = curHeight + 'px';
                    
                    if(fullHeight <= 1) {
                        CE.CEU.$('cemain_pad').style.marginTop = '0px';
                        CE.aCN('cehint', 'hidden');
                    }
                    updateHintRevealer();
                    redraw(CE.CEU.$('cehint'));
                } else {
                    CE.CEU.$('cemain_pad').style.marginTop = curHeight-1 + 'px';
                    CE.CEU.$('cehint').style.height        = curHeight + 'px';
                }
            }
        };

        if(fast) {
            doStep();
            interval = setInterval(doStep, 50);
        } else {
            interval = setInterval(doStep, 50);
        }
    }
    
    function hideHint() {
        if(g_hints_hider != null) {
            clearTimeout(g_hints_hider);
            g_hints_hider = null;
        }
        
        if(CE.hCN('cehint', 'hidden')) {
            g_hints_hiding  = false;
            g_hints_showing = false;
            return;
        }

        g_hints_hiding  = true;
        g_hints_showing = false;
        
        var interval = setInterval(
            function() {
                var curHeight = CE.CEU.$('cehint').style.height;
                if(curHeight == '')
                    curHeight = CE.CEU.$('cehint').offsetHeight;
                else
                    curHeight = parseInt(curHeight);
                var newHeight = curHeight - 3;

                if(!g_hints_hiding) {
                    clearInterval(interval);
                } else if(curHeight == 0 || newHeight <= 0) {
                    clearInterval(interval);
                    CE.CEU.$('cemain_pad').style.marginTop = '0px';
                    CE.aCN('cehint', 'hidden');
                    g_hints_hiding = false;
                    updateHintRevealer();
                } else {
                    CE.CEU.$('cehint').style.height        = newHeight + 'px';
                    CE.CEU.$('cemain_pad').style.marginTop = newHeight-1 + 'px';
                }
            },
            50);
    }
    
    this.autoHintHider = function() {
        if(g_hints_hider != null) {
            clearTimeout(g_hints_hider);
            g_hints_hider = null;
        }
        
        var noAutoHide = false;
        for(var i = 0; i < g_hints.length; ++i) {
            if(g_hints[i].noautohide) {
                noAutoHide = true;
                break;
            }
        }
        
        if(!noAutoHide) {
            g_hints_hider = setTimeout(
            function() {
                g_hints_hider = null;
                hideHint();
            },
            15000);
        }
    };
    
    function closeOldHints() {
        for(var i = 0; i < g_hints_close.length; ++i) {
            CE.CEUI.closeHint(g_hints_close[i]);
        }
        g_hints_close = [];
    }
    
    this.showHint = function(hintId, contentNode, closeOnContentReload, closeCb, hideClose, noAutoHide) {
        // First check if this hint is already popped, in which case just switch to it
        for(var i = 0; i < g_hints.length; ++i) {
            if(g_hints[i].id == hintId) {
                if(g_hints_cur != i) {
                    g_hints_cur = i;
                    refreshHintDisplay();
                }
                return;
            }
        }
        
        g_hints.unshift({id:hintId, msg:contentNode, closecb:closeCb, hideclose:hideClose, noautohide:noAutoHide});
        g_hints_cur = 0;
        
        if(closeOnContentReload)
            g_hints_close.push(hintId);
        
        refreshHintDisplay();
    };
    
    this.isHintShowing = function(hintId) {
        for(var i = 0; i < g_hints.length; ++i) {
            if(g_hints[i].id == hintId)
                return true;
        }
        return false;
    };
    
    this.showNextHint = function() {
        ++g_hints_cur;
        if(g_hints_cur >= g_hints.length)
            g_hints_cur = 0;
        refreshHintDisplay();
    };
    
    this.closeHint = function(hintId) {
        for(var i = 0; i < g_hints.length; ++i) {
            if(g_hints[i].id == hintId) {
                if(g_hints[i].closecb)
                    g_hints[i].closecb();
                g_hints.splice(i, 1);
                break;
            }
        }

        refreshHintDisplay();
    };
    
    this.getDataStreamPrefix = function(file, svc, alb, skipsecureapi) {
        var url;

        // there is a case where file is actually the album.root object itself, in which case 
        // it has the apiurl on it. this would appear to be the single-file-shared case.
        if(file && file.apiurl) {
            url = file.apiurl;
        } else if(file && file.serviceid) {
            var s = g_svcmap[file.deviceid + ":_:" + file.serviceid];
            if(s) {
                url = s.apiurl;
            } 
        } else if(svc) {
            url = svc.apiurl;
        } else if(that.g_cursvc) {
            url = that.g_cursvc.apiurl;
        } else if(alb && alb.root) {
            if(alb.root.apiurl) {
                url = alb.root.apiurl;
            } else  if(alb.root.deviceid && alb.root.serviceid) {
                var s = g_svcmap[alb.root.deviceid + ":_:" + alb.root.serviceid];
                if(s) {
                    url = s.apiurl;
                } 
            }
        } else if(that.g_curalbum && that.g_curalbum.root) {
            if(that.g_curalbum.root.apiurl) {
                url = that.g_curalbum.root.apiurl;
            } else if(that.g_curalbum.root.deviceid && that.g_curalbum.root.serviceid) {
                var s = g_svcmap[that.g_curalbum.root.deviceid + ":_:" + that.g_curalbum.root.serviceid];
                if(s) {
                    url = s.apiurl;
                } 
            }
        }
        if(window.location.protocol.indexOf("https") == 0) {
            // the apiurl should still be reachable via https...
            // john.
            if(url) {
                if(url.toLowerCase().indexOf("https")!=0) {
                    url = null;
                } else if(skipsecureapi) {
                    url = null;
                }
            }
        }
        if(url) {
            var i = url.lastIndexOf("/api");
            if(url.length - 4 == i) {
                url = url.slice(0, i);
                url += "/files/";
                
                // we got an api url, but its not a direct-to-plug url...
                if(url.lastIndexOf("/svc/files/")>0) {
                    // we are supposed to add the device/serviceid to the end regardless...
                    // john.
                    var thing = file;
                    if(!thing || !thing.deviceid) {
                        thing = svc;
                    }
                    if(!thing) {
                        if(alb && alb.root) {
                            thing = alb.root;
                        }
                    }
                    if(!thing || !thing.deviceid) {
                        thing = that.g_cursvc;
                    }
                    if(!thing && (that.g_curalbum)) {
                        if(that.g_curalbum.root) {
                            thing = that.g_curalbum.root;
                        } else {
                            thing = that.g_curalbum;
                        }
                    }
                    if(thing) {
                        if(!CE.CEU.getCookie("valtoken")) {
                            if(that.g_sharetoken.length > 40) {
                                url += that.g_sharetoken;
                                url += "/";
                            } else {
                                // the sharetoken does not have the user info encoded in it.
                                // we can't go direct-to-pm unless it is there.
                                url = null;
                            }
                        }
                        if(url) {
                            if(thing.deviceid) {
                                url += thing.deviceid;
                                url += "/";
                            }
                            if(thing.serviceid) {
                                url += thing.serviceid;
                                url += "/";
                            }
                        }
                    }
                }        
            }
        }
        if(url == null) {
            if(window.location.pathname.indexOf('/share/') == 0) {
                url = "/share/" + that.g_sharetoken + "/";
            } else {
            // this is the old way. have to go through the main site
            if(alb && alb.albumid) {
                url = "/svc/albums/";
                if(that.g_sharetoken) {
                    url += that.g_sharetoken + "/";
                }
                url += alb.albumid + '/';
            } else {
                url = "/svc/files/";
                if(that.g_sharetoken) {
                    url += that.g_sharetoken + "/";
                }
            }
            if(file && file.serviceid) {
                url += file.deviceid+'/'+file.serviceid+'/'
            } else if(svc) {
                url += svc.deviceid+'/'+svc.serviceid+'/'
            } else if(alb && alb.root) {
                url += alb.root.deviceid + '/' + alb.root.serviceid + '/';
            } else if(that.g_curalbum && that.g_curalbum.root) {
                url += that.g_curalbum.root.deviceid + '/' + that.g_curalbum.root.serviceid + '/';                
            } else if(that.g_cursvc) {
                url += that.g_cursvc.deviceid + '/' + that.g_cursvc.serviceid + '/';
            } else {
                if(that.g_sharetoken) {
                    url = "/share/" + that.g_sharetoken + "/";
                }
            }
            }
        }
        return url;
    };

    this.getDeviceID = function() {
        var deviceID = that.g_cursvc.deviceid;
        return deviceID;
    };

    this.getServiceID = function () {
        var serviceID = that.g_cursvc.serviceid;
        return serviceID;
    };

    this.fileUploaderFlashLoaded = function() {
        g_uploadfiles.flashLoaded();
    };

    this.closeFileUploader = function() {
        g_uploadfiles.hide();
    };

}();


//-----------------------------------------------------
// CE.CEUI.PreviewRibbon - class
// Helper class providing the 'ribbon' of thumbnails
// that are presented inside the preview pane's top tool drop-down
CE.CEUI.PreviewRibbon = function(ribbondiv, imgdiv, tabdiv, mti) {
    //----------------------------------------------------------------------------------------
    // Private variables
    var that = this;
    var m_ribbondiv = ribbondiv;
    var m_imgdiv = imgdiv;
    var m_tabdiv = tabdiv;
    var m_mti = mti;
    var m_showing = false;
    var m_animstep = 0.0;
    var m_animtimer = null;
    var m_animstate;

    var m_slidetimer = null;
    var m_slidestate = 0;

    var m_upfx;

    m_imgdiv.className = 'sqpvribbon';
    m_imgdiv.style.top = ''+(-150)+'px';
    m_imgdiv.style.height = ''+150+'px';

    //----------------------------------------------------------------------------------------
    // Private Functions
//         function onTN(img, pv, tw, th) {
//             var w = img.width;
//             var h = img.height;
//             if(tw < w) {
//                 var r = tw / w;
//                 w *= r;
//                 h *= r;
//             }
//             if(th < h) {
//                 var r = th / h;
//                 w *= r;
//                 h *= r;
//             }
//             img.style.width = ''+w+'px';
//             img.style.height = ''+h+'px';
//             pv.appendChild(img);
//         }
    function onMouseMove(e, pos) {
        var op = m_ribbondiv.offsetParent;
        var ox = 0;//m_ribbondiv.offsetLeft;
        var oy = 0;//m_ribbondiv.offsetTop;
        var w = m_ribbondiv.clientWidth;
        var h = m_ribbondiv.clientHeight;
        var ns = m_slidestate;
        while(op) {
            ox += op.offsetLeft;
            oy += op.offsetTop;
            op = op.offsetParent;
        }
        if(ox <= pos.x && pos.x < ox+w &&
           oy <= pos.y && pos.y < oy+h) {
            if(ox <= pos.x && pos.x <= ox+100) {
                var of = pos.x-ox
                if(of <= 25) {
                    ns = -3;
                } else if(of <= 66) {
                    ns = -2;
                } else if(of <= 100) {
                    ns = -1;
                }
            } else if(ox+w-100 <= pos.x && pos.x <= ox+w) {
                var of = pos.x-ox-w+100
                ns = of / 33;
//                 if(of <= 25) {
//                     ns = 1;
//                 } else if(of <= 66) {
//                     ns = 2;
//                 } else if(of <= 100) {
//                     ns = 3;
//                 }
            } else if(m_slidestate) {
                ns = 0;
            }
        } else if(m_slidestate) {
            ns = 0;
        }
        if(m_slidestate != ns) {
            m_slidestate = ns;
            if(m_slidetimer) {
                CE.uTimer(m_slidetimer);
                m_slidetimer = null;
            }
            CE.CEDBG.println("Ribbon Slide State: "+m_slidestate+
                             " X: "+e.clientX+" Y: "+e.clientY+
                             " (OFF: "+ox+","+oy+" -- "+w+","+h+")");
            slideRibbon();
        }
        // Continue event processing...
        return true;
    }

    function onTNClick(p, e, t) {
        if(p && p.file) {
            if(m_mti) m_mti(p.idx, p.file);
        }
    }
    function slideRibbon() {
        m_slidetimer = null;
        if(m_slidestate!=0) {
            var ls = m_imgdiv.style.left;
            var l = 0;
            if(ls) {
                l = parseInt(ls.replace(/(\d+).*$/, "$1"));
            }
            var w = m_ribbondiv.clientWidth;
            var wr = m_imgdiv.clientWidth;
            var x = l;
            x = (-10*m_slidestate)+l;

            if(x<=0 && x>=(w-wr)) {
//                 CE.CEDBG.println(" -- Sliding Ribbon: "+m_slidestate+" L: "+l+
//                                  " -> "+x+" w:"+w+" wr:"+ wr+" xx: "+(w-wr));
                m_imgdiv.style.left = ''+x+'px';
                again = true;
                m_slidetimer = CE.rTimer(slideRibbon, 43);
            }
        }
    }
    function animShow() {
        // TODO: Use wall-clock to smooth this out...
        m_animstep = m_animstep + 40.0/1000.0 / 0.25;
        var offx = 0;

        if(m_animstep < 1.0) {
            offx = 150 * m_animstep;
            m_animtimer = CE.rTimer(animShow, 40);
        } else {
            offx = 150;
            m_animtimer = null;
        }
        if(m_animstate == 'hide') offx = 150-offx;
        m_ribbondiv.style.height = ''+offx+'px';
        m_imgdiv.style.top = ''+(offx-150)+'px';
        m_tabdiv.style.top = ''+offx+'px';
    }
    function setShowing(showing) {
        m_showing = showing;
        if(m_showing) {
            CE.CEUI.addMouseHandler(onMouseMove);
        } else {
            CE.CEUI.removeMouseHandler(onMouseMove);
        }
    }
    
    // Public methods
    this.setFiles = function(upfx, fileset, cidx) {
        if(upfx) m_upfx = upfx;
        if(fileset) {
            m_imgdiv.style.left = '0px';
            CE.rac(m_imgdiv);
            var ni;
            var cnt = 0;
            for(ni=0;ni<fileset.length;ni++) {
                var nf = fileset[ni];
                var url = null;
                if(nf.thumbnail) {
                    url=m_upfx + nf.thumbnail + "/" + encodeURIComponent(nf.name);
                } else if(nf.preview) {
                    url=m_upfx + nf.preview + "/" + encodeURIComponent(nf.name);
                } else if(nf.name) {
                    var ext = nf.name.substr(nf.name.length-4);
                    if(ext) {
                        ext = ext.toLowerCase();
                        if(ext.match(".png") || ext.match(".jpg") || ext.match("jpeg") || ext.match(".gif") || ext.match(".bmp")) {
                            url = m_upfx + nf.fileid + "/" + encodeURIComponent(nf.name);
                        }
                    }                    
                }
                if(url) {
                    var i = new Image();
                    i.className = 'sqpvribbonimg'
                    i.src = url;
                    i.alt = CE.CEUI.getDispFn(nf);
                    CE.CEU.attachEvent(i, 'click',
                                       {onEvent: onTNClick, idx: ni, file: nf});
                    m_imgdiv.appendChild(i);
                    cnt++;
                    // (function(i,p){i.onload= function() {onTN(i, p, 126, 126);};})(i,m_imgdiv);
                }
            }
            m_imgdiv.style.width  = ''+(150*cnt)+'px';
        }
    }
    this.showNow = function(doshow) { 
        m_animstep = 1.0;
        if(doshow && !m_showing) {
            setShowing(true);
            animShow();
        } else if(!doshow && m_showing) {
            setShowing(false);
            animShow();
        } else if(m_animtimer) {
            // finish the in-progress animation
            animShow();
        }
        // Always unregister the animation
        if(m_animtimer) {
            CE.uTimer(m_animtimer);
            m_animtimer = null;
        }
        return m_showing;
    }
    this.animateShow = function(doshow) { 
        if(doshow && !m_showing) {
            m_animstep = 0.0;
            m_animstate = 'show';
            m_animtimer = CE.rTimer(animShow, 40);
            setShowing(true);
        } else if(!doshow && m_showing) {
            m_animstep = 0.0;
            m_animstate = 'hide';
            m_animtimer = CE.rTimer(animShow, 40);
            setShowing(false);
        }
        return m_showing; 
    };
}

//-----------------------------------------------------
// CE.CEUI.SlideshowPane - class
// CLASS: Slideshow of package contents. 
CE.CEUI.SlideshowPane = function() {
    //----------------------------------------------------------------------------------------
    // Private variables
    var that = this;
    var m_showing = false;
    var m_animstep = 0.0;
    var m_animtimer = null;
    var m_animstate;
    var m_szxtarget, m_szytarget;
    var m_szxstart, m_szystart;
    var m_szxoff, m_szyoff;
    var m_vw, m_vh;
    var m_show = null;
    var m_showstart = 0;
    var m_files = [];
    var m_data = {};

    // DOM Elements we own
    var m_sproot       = CE.dce('div', null, 'sqslideshow');
    var m_hidediv      = CE.dce('div', null, 'sqslideshow_hide');
    var m_slideshowDiv = null;

    // Close image
    var cbubble = CE.dce("img", "sqslideshow_close", "hidden");
    cbubble.src = CE.STRTAB.lookup("imgbase")+"corner-close.png";
    CE.CEU.attachEvent(cbubble, "click", {onEvent: doClose});
    m_sproot.appendChild(cbubble);

    // Full screen
    cbubble = CE.dce("img", "sqslideshow_fullscreen", "hidden");
    cbubble.src   = CE.STRTAB.lookup("imgbase")+"corner-fullscreen.png";
    cbubble.title = CE.STRTAB.lookup('view.fullscreen');
    CE.CEU.attachEvent(cbubble, "click", {onEvent: doFullscreen});
    m_sproot.appendChild(cbubble);

    //----------------------------------------------------------------------------------------
    // Private Functions
    //   -- Tool Event Callbacks

    function doClose() {
        that.hide();
    }

    function evtHide() {
        that.hide();
    }
    m_hidediv.onclick = evtHide;

    function cleanup() {
        if(m_show) {
            m_show.destroy();
            m_show = null;
        }
        if(m_animtimer) {
            CE.uTimer(m_animtimer);
            m_animtimer = null;
        }
        m_animstate = null;
        m_animstep  = 0.0;
        m_szxoff    = 0;
        m_szyoff    = 0;
        m_showstart = 0;
        m_files     = [];
        m_data      = {};

        if(m_slideshowDiv) {
            CE.CEU.releaseAllEvents(m_slideshowDiv, 'click', true);
            CE.rac(m_slideshowDiv);
            m_slideshowDiv.parentNode.removeChild(m_slideshowDiv);
            m_slideshowDiv = null;
        }
    }
    
    function doFullscreen() {
        var files    = m_files;
        var curIndex = m_show.getCurSlide();
        var paused   = m_show.isPaused();
        that.hide();
        
        CE.CEUI.clearContent('celist');
        var celist   = CE.CEU.$('celist');
        var slideDiv = CE.dce('div');
        slideDiv.style.width = '100%';
        slideDiv.style.height = (CE.CEU.$('cecontent').offsetHeight - 17) + 'px';
        slideDiv.style.paddingTop = '5px';
        celist.appendChild(slideDiv);
        CE.CEUI.populateContentWithSlideshow(slideDiv, files, null, true, paused, false, curIndex);

        CE.aCN('cetools_normal',         'hidden');
        CE.rCN('cetools_fullscreenprev', 'hidden');
    }
    
    function position(szx, szy, offx, offy) {
        if(!offx) offx = 0;
        if(!offy) offy = 0;
        // the more standards compliant browsers (mozilla/netscape/opera/IE7) use 
        // window.innerWidth and window.innerHeight
        if (typeof window.innerWidth != 'undefined') {
            m_vw = window.innerWidth,
            m_vh = window.innerHeight
        }
        // IE6 in standards compliant mode
        else if (typeof document.documentElement != 'undefined'
                 && typeof document.documentElement.clientWidth != 'undefined' 
                 && document.documentElement.clientWidth != 0)  {
            m_vw = document.documentElement.clientWidth;
            m_vh = document.documentElement.clientHeight;
        }

        var px = Math.round(((m_vw - szx)/2 + offx));
        var py = Math.round(((m_vh - szy)/2 + offy));
        szx = Math.round(szx);
        szy = Math.round(szy);

        if(py<12) {
            py = 12;
        }
        if(px<12) {
            px = 12;
        }

         //CE.CEDBG.println("CEV: Position Slideshow VP: {"+m_vw+","+m_vh+"} GEOM: "+
         //                 szx+"x"+szy+"+"+px+"+"+py);

        var l = CE.CEU.$('sqslideshow_close');
        if(l) {
            CE.rCN('sqslideshow_close', 'hidden');
            l.style.left = '' + (px - 12) + 'px';
            l.style.top  = '' + (py - 12) + 'px';
        }
        var r = CE.CEU.$('sqslideshow_fullscreen');
        if(r) {
            CE.rCN('sqslideshow_fullscreen', 'hidden');
            r.style.left = '' + (px + szx - 1) + 'px';
            r.style.top  = '' + (py + szy - 5) + 'px';
        }

        m_sproot.style.width = ''+szx+'px';
        m_sproot.style.height = ''+szy+'px';
        m_sproot.style.left = ''+px+'px';
        m_sproot.style.top = ''+py+'px';
    }

    function animShow() {
        // TODO: Use wall-clock to smooth this out...
        m_animstep = m_animstep + 40.0/1000.0 / 0.25;

        if(m_animstep < 1.0) {
            var offx = 0;
            var offy = 0;
            if(m_szxoff>0) {
                offx = (1-m_animstep) * (m_szxoff-m_vw/2);
            }
            if(m_szyoff>0) {
                offy = (1-m_animstep) * (m_szyoff-m_vh/2);
            }
            position(m_szxstart + (m_animstep * (m_szxtarget-m_szxstart)), 
                     m_szystart + (m_animstep * (m_szytarget-m_szystart)),
                     offx, offy);
            m_animtimer = CE.rTimer(animShow, 40);
        } else {
            position(m_szxtarget, m_szytarget);
            m_animtimer = null;
        }
    }

    this.show = function (evt) {
        if(!m_showing) {
            m_showing = true;
            
            if(!m_slideshowDiv) {
                m_slideshowDiv = CE.dce('div', "slideprev_div", 'sqfileslideshow');
                m_sproot.appendChild(m_slideshowDiv);
            }

            // start at 126x126
            if(evt) {
                // start at the mouse position rather than centered...
                m_szxoff   = evt.clientX;
                m_szyoff   = evt.clientY;
            } else {
                m_szxoff   = 0;
                m_szyoff   = 0;
            }
            m_szxstart = 126;
            m_szystart = 126;
            position(m_szxstart,m_szystart, m_szxoff, m_szyoff);

            document.body.appendChild(m_hidediv);
            document.body.appendChild(m_sproot);
            m_show = new Slideshow('slideprev_div', m_data, {loader:null, controller:true, width:640, height:480, thumbnails:true, thumbsize:50, paused:true, overlap:false, resize:'length', slide:m_showstart});

            m_animstep = 0.0;
            m_animstate = 'show';
            m_animtimer = CE.rTimer(animShow, 40);
        }
    };

    this.hide = function () {
        CE.aCN("sqslideshow_close",      "hidden");
        CE.aCN("sqslideshow_fullscreen", "hidden");
        if(m_showing) {
            document.body.removeChild(m_sproot);
            document.body.removeChild(m_hidediv);
            m_showing = false;
        }
        cleanup();
        CE.CEUI.g_videoPrevDisabled = false;
    };

    this.showPreview = function(evt, pg, idx) {
        if(m_showing) return;

        if(pg) {
            cleanup();
            var count = 0;

            // Build the list of files to include in the slideshow
            for(var i=0; i<pg.length; ++i) {
                var nf = pg[i];
                if(nf.stream || nf.type == 1 || !CE.CEUI.isImageFile(nf))
                    continue;
                if(i == idx)
                    m_showstart = count;
                ++count;
                var urlPrefix = CE.CEUI.getDataStreamPrefix(nf, null);

                var imageUrl = urlPrefix + nf.fileid + "/" + encodeURIComponent(nf.name);
                if(nf.preview)
                    imageUrl = urlPrefix + nf.preview + "/" + encodeURIComponent(nf.name);
                
                var thumbUrl = imageUrl;
                if(nf.thumbnail)
                    thumbUrl = urlPrefix + nf.thumbnail + "/" + encodeURIComponent(nf.name);

                m_files.push(nf);
                m_data[imageUrl] = { caption:CE.CEUI.getDispFn(nf), thumbnail:thumbUrl };
            }

            m_szxtarget = 640;
            m_szytarget = 480;
        }

        that.show(evt);
    };
}

//-----------------------------------------------------
// CE.CEUI.PreviewPane - class
// CLASS: Single file preview of package contents. 
// Has a big window for previewing the item, and a ribbon across the top
// for viewing more items from the same folder. 
CE.CEUI.PreviewPane = function() {
    //----------------------------------------------------------------------------------------
    // Private variables
    var that = this;
    var m_page = null; // page of items this preview pane will display in it's ribbon
    var m_pindex = -1;
    var m_file = null; // current file being shown in the main preview area
    var m_stream = null;
    var m_upfx = null; // URL prefix derived from current service

    var m_showing = false;
    var m_toolsshowing = false;
    var m_toolhidetimer = null;
    // NOTE: Testing, set this to big positive number, but make sure it is zero for production
    var m_toolhidedisable = 0;
    var m_tool = {};
    var m_sstimer = null;
    var m_animstep = 0.0;
    var m_animtimer = null;
    var m_animstate;
    var m_szxtarget, m_szytarget;
    var m_szxstart, m_szystart;
    var m_szxoff, m_szyoff;
    var m_noresize = true;
    var m_aspect;
    var m_currimg;
    var m_currfullimg;
    var m_previmg;
    var m_nextimg;
    var m_ribbon = null;
    var m_vw, m_vh;

    //   -- DOM Elements we own
    var m_pproot     = CE.dce('div', null, 'sqpreview');
    var m_hidediv    = CE.dce('div', null, 'sqpreview_hide');
    var m_previewDiv = CE.dce('div', null, 'sqfilepreview');

    m_pproot.appendChild(m_previewDiv);

    // Close image
    var cbubble = CE.dce("img", "sqpvtool_close", "hidden");
    cbubble.src = CE.STRTAB.lookup("imgbase")+"corner-close.png";
    CE.CEU.attachEvent(cbubble, "click", {onEvent: doClose});
    document.body.appendChild(cbubble);

    // Full screen
    if(navigator.platform != 'iPad' && CE.CEUI.hasVideoSupport() != 'flash') {
        cbubble = CE.dce("img", "sqpvtool_fullscreen", "hidden");
        cbubble.src   = CE.STRTAB.lookup("imgbase")+"corner-fullscreen.png";
        cbubble.title = CE.STRTAB.lookup('view.fullscreen');
        CE.CEU.attachEvent(cbubble, "click", {onEvent: doFullscreen});
        document.body.appendChild(cbubble);
    }

    // Build our tools
    var m_toolspec = {
        'play': {type:'img', events:{'click': onPlayClick} },
        'prev': {type:'img', events:{'click': onPrevClick} },
        'next': {type:'img', events:{'click': onNextClick} },
        'info': {type:'div'},
        'ribbon': {type:'div', events:{'mouseover': onRibbonMouseOver, 'mouseout': onRibbonMouseOut} },
        'tab': {type:'div', events:{'mouseover': onTabMouseOver, 'mouseout': onTabMouseOut} },
        'ribbonimg': {type:'div', events:{'mouseover': onRibbonMouseOver, 'mouseout': onRibbonMouseOut} }
    };
    (function() {
        for(var key in m_toolspec) {
            if(m_toolspec[key].type=='img') {
                m_tool[key] = CE.dce('img');
                // TODO: we should fix the tool to support GIF for broken IE
                m_tool[key].src = CE.STRTAB.lookup("imgbase")+'tools_'+key+'.png';
            } else {
                m_tool[key] = CE.dce(m_toolspec[key].type);
            } 
            m_tool[key].className = 'sqpreviewtool';
            m_tool[key].id = 'sqpvtool_'+key;
            m_tool[key].style.display = 'none';
            if(m_toolspec[key].events) {
                for(var evt in m_toolspec[key].events) {
                    CE.CEU.attachEvent(m_tool[key], evt,
                                       {onEvent: m_toolspec[key].events[evt], tool: key});
                }
            }
            m_pproot.appendChild(m_tool[key]);
        }
    })();
    m_ribbon = new CE.CEUI.PreviewRibbon(m_tool['ribbon'], m_tool['ribbonimg'], m_tool['tab'], 
                                         moveToImage);
    //----------------------------------------------------------------------------------------
    // Private Functions
    //   -- Tool Event Callbacks


    function areYouAnImage(nf) {

        if(nf.preview) return true;

        var ext = nf.name.substr(nf.name.length-4);
        if(ext) {
            ext = ext.toLowerCase();
            if(ext.match(".png") || ext.match(".jpg") || ext.match("jpeg") || ext.match(".gif") || ext.match(".bmp")) {
                return true;
            }
        }                    
        return false
    }

    function moveToImage(ni,nf) {
        m_pindex = ni;
        m_file = m_page[m_pindex];

        // TODO: animate this transition...
        m_currimg = new Image();
        // Reset target size
        if(m_noresize) {
            m_szxtarget = 640;
            m_szytarget = 480;
            m_currimg.szxtarget = 640;
            m_currimg.szytarget = 480;
        } else {
            m_szxtarget = 640;
            m_szytarget = 640;
            m_currimg.szxtarget = 640;
            m_currimg.szytarget = 640;
        }
        m_currimg.onload = function() {onImageLoad(this);};
        CE.CEU.attachEvent(m_currimg, 'click', {onEvent: onImageClick});
        CE.CEU.attachEvent(m_currimg, 'mousemove', {onEvent: onImageMouseMove});
        if(m_file.preview) {
            m_currimg.src=m_upfx + m_file.preview + "/" + encodeURIComponent(m_file.name);
        } else {
            m_currimg.src=m_upfx + m_file.fileid + "/" + encodeURIComponent(m_file.name);
        }
        m_ribbon.setFiles(m_upfx, null, m_pindex);
    }

    function doClose() {
        that.hide();
    }
    function doPrev() {
        // TODO: accelerate by preloading...
        if(m_page && m_page) {
            var ni;
            var nf = null;
            // TODO: We need to support a better back-to-server window expansion...
            for(ni=m_pindex-1;ni>=0;ni--) {
                if(areYouAnImage(m_page[ni])) {
                    nf = m_page[ni];
                    break;
                }
            }
            if(!nf) {
                // Start over from beginning as we need to loop...
                for(ni=(m_page.length-1);ni>m_pindex;ni--) {
                    if(areYouAnImage(m_page[ni])) {
                        nf = m_page[ni];
                        break;
                    }                        
                }
            }
            if(nf) {
                //CE.CEDBG.println("Found prev Image["+ni+"]: "+nf.name+" -- "+nf.preview);
                moveToImage(ni, nf);
            }
        }
    }

    function doNext() {
        // TODO: accelerate by preloading...
        if(m_page && m_page) {
            var ni;
            var nf = null;
            // TODO: We need to support a better back-to-server window expansion...
            for(ni=m_pindex+1;ni<m_page.length;ni++) {
                if(areYouAnImage(m_page[ni])) {
                    nf = m_page[ni];
                    break;
                }
            }
            if(!nf) {
                // Start over from beginning as we need to loop...
                for(ni=0;ni<m_pindex;ni++) {
                    if(areYouAnImage(m_page[ni])) {
                        nf = m_page[ni];
                        break;
                    }
                }
            }
            if(nf) {
                //CE.CEDBG.println("Found next image["+ni+"]: "+nf.name+" -- "+nf.preview);
                moveToImage(ni, nf);
            }
        }
    }

    function onPrevClick() {
        doPrev();
        startHideTimer();
    }

    function onNextClick() {
        doNext();
        startHideTimer();
    }

    function ssStep() {
        doNext();
        m_sstimer = CE.rTimer(ssStep, 3000);
    }

    function startSS() {
        if(!m_sstimer) {
            showTools(false);
            m_tool['play'].src = CE.STRTAB.lookup("imgbase")+'tools_pause.png';
            ssStep();
        }
    }

    function stopSS() {
        if(m_sstimer) {
            CE.uTimer(m_sstimer);
            m_sstimer = null;
            m_tool['play'].src = CE.STRTAB.lookup("imgbase")+'tools_play.png';
        }
        startHideTimer();
    }

    function onPlayClick() {
        if(m_sstimer) {
            stopSS();
        } else {
            startSS();
        }
    }

    // This code was older fallback timer based check...
    //       function onRibbonOutCheck() {
    //           if(m_toolhidedisable<=0) {
    //               m_ribbon.animateShow(false);
    //           }
    //       }
    function onTabMouseOver(p, e, t) {
        m_ribbon.animateShow(true);
        onRibbonMouseOver(p, e, t);
    }

    function onTabMouseOut(p, e, t) {
        onRibbonMouseOut(p, e, t);
    }

    function onRibbonMouseOver(p, e, t) {
        // we need to detect the case of mouse out ribbon entering tab, 
        // and also the case where we are entering one of our children...
        // NOTE: toElement is to support broken IE...
        var r = e.relatedTarget?e.relatedTarget:e.fromElement;
        if(r) {
            if(r != m_tool['tab'] && r != m_tool['ribbon']) {
                // Check for OK Classes too
                if(r.className == 'sqpvribbon' || r.className == 'sqpvribbonimg') {
                    // Ignore our OK classes
                } else {
                    // TODO: There are cases where this can get out-of-sync, we should
                    // TODO: just set to true/false
                    m_toolhidedisable ++;
                    CE.CEDBG.println("Mouse Over Ribbon... (TO: "+r.nodeName+(r.id?"#"+r.id:"")+
                                     (r.className?"."+r.className:"")+") Disable Hiding: "+
                                     m_toolhidedisable);
                }
            }
        }
    }

    function onRibbonMouseOut(p, e, t) {
        // we need to detect the case of mouse out ribbon entering tab, 
        // and also the case where we are entering one of our children...
        // NOTE: toElement is to support broken IE...
        var r = e.relatedTarget?e.relatedTarget:e.toElement;
        if(r) {
            if(r != m_tool['tab'] && r != m_tool['ribbon']) {
                // Check for OK Classes too
                if(r.className == 'sqpvribbon' || r.className == 'sqpvribbonimg') {
                    // Ignore our OK classes
                } else {
                    // TODO: There are cases where this can get out-of-sync, we should
                    // TODO: just set to true/false
                    m_toolhidedisable --;
                    CE.CEDBG.println("Mouse Out Ribbon... (TO: "+r.nodeName+(r.id?"#"+r.id:"")+
                                     (r.className?"."+r.className:"")+") Hiding: "+
                                     m_toolhidedisable);
                    m_ribbon.animateShow(false);
                }
            }
        }
        // This code was older fallback timer based check...
        //m_ribbonchecktimer = CE.rTimer(onRibbonOutCheck, 50);
    }

    function evtHide() {
        that.hide();
    }
    m_hidediv.onclick = evtHide;

    function getFlashVersion() {
        var version = [ 0, 0, 0 ];

        if(navigator.plugins && navigator.mimeTypes.length){
            var x = navigator.plugins["Shockwave Flash"];
            if(x && x.description) {
                version = x.description.replace(/([a-zA-Z]|\s)+/, "").replace(/(\s+r|\s+b[0-9]+)/, ".").split(".")
            }
            // 	    } else if (navigator.userAgent && navigator.userAgent.indexOf("Windows CE") >= 0){ // if Windows CE
            // 		var axo = 1;
            // 		var counter = 3;
            // 		while(axo) {
            // 		    try {
            // 			counter++;
            // 			axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+ counter);
            //                         version = [ counter, 0, 0 ];
            // 		    } catch (e) {
            // 			axo = null;
            // 		    }
            // 		}
            //                 axo = null;
        } else {
            var axo = null;
            try {
                axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
            } catch(e) {
                try {
                    axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
                    version = [6, 0, 21];
                    axo.AllowScriptAccess = "always";
                } catch(e) {
                    if (version[0] == 6) {
                        axo = null;
                        return version;
                    }
                }
                // 		    try {
                // 			axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
                // 		    } catch(e) {}
            }
            if (axo != null) {
                version = axo.GetVariable("$version").split(" ")[1].split(",");
            }
            axo = null;
        }
        return version;
    }

    function createVideo(url, piurl, div) {
        CE.CEUI.createVideoEl(div, 'prev_flv', url, 640, 480, true, false,
                              CE.CEUI.onPreviewVideoComplete, true, null, piurl);
    }

    function cleanup() {
        if(m_animtimer) {
            CE.uTimer(m_animtimer);
            m_animtimer = null;
        }
        m_animstate = null;
        m_animstep = 0.0;
        m_currimg = null;
        m_currfullimg = null;
        m_nextimg = null;
        m_szxoff   = 0;
        m_szyoff   = 0;

        if(navigator.plugins) {
            var el = m_previewDiv.firstChild;
            while(el) {
                if(el.tagName == 'embed') {
                    el.sendEvent('stop');
                }
                el=el.nextSibling;
            }
        } else {
            // Arg, we need to force flash to clean up a little better on IE or it leaks
            var el = m_previewDiv.firstChild;
            while(el) {
                if(el.tagName == 'object') {
                    el.sendEvent('stop');
                    el.style.display = 'none';
                    for(var x in el) {
                        if(typeof(el[x]) == 'function') {
                            CE.CEDBG.println('-- Killing Object Function: '+x);
                            el[x] = null;
                        }
                    }
                }
                el=el.nextSibling;
            }
            el = null;
        }
        CE.CEU.releaseAllEvents(m_previewDiv, 'click', true);
    }
    
    function doFullscreen() {
        var files    = [m_file];
        var curIndex = 0;
        var paused   = false;
        that.hide();
        
        CE.CEUI.clearContent('celist');
        var celist   = CE.CEU.$('celist');
        var slideDiv = CE.dce('div');
        slideDiv.style.width = '100%';
        slideDiv.style.height = (CE.CEU.$('cecontent').offsetHeight - 17) + 'px';
        slideDiv.style.paddingTop = '5px';
        celist.appendChild(slideDiv);
        CE.CEUI.populateContentWithSlideshow(slideDiv, files, null, true, paused, false, curIndex, null, true);

        CE.aCN('cetools_normal',         'hidden');
        CE.rCN('cetools_fullscreenprev', 'hidden');
    }
    
    function position(szx, szy, offx, offy) {
        if(!offx) offx = 0;
        if(!offy) offy = 0;
        // the more standards compliant browsers (mozilla/netscape/opera/IE7) use 
        // window.innerWidth and window.innerHeight
        if (typeof window.innerWidth != 'undefined') {
            m_vw = window.innerWidth,
            m_vh = window.innerHeight
        }
        // IE6 in standards compliant mode
        else if (typeof document.documentElement != 'undefined'
                 && typeof document.documentElement.clientWidth != 'undefined' 
                 && document.documentElement.clientWidth != 0)  {
            m_vw = document.documentElement.clientWidth;
            m_vh = document.documentElement.clientHeight;
        }

        var px = Math.round(((m_vw - szx)/2 + offx));
        var py = Math.round(((m_vh - szy)/2 + offy));
        szx = Math.round(szx);
        szy = Math.round(szy);

        if(py<12) {
            py = 12;
        }
        if(px<12) {
            px = 12;
        }

//         CE.CEDBG.println("CEV: Position Preview VP: {"+m_vw+","+m_vh+"} GEOM: "+
//                          szx+"x"+szy+"+"+px+"+"+py);

        var l = CE.CEU.$('sqpvtool_close');
        if(l) {
            CE.rCN('sqpvtool_close', 'hidden');
            l.style.left = '' + (px - 12) + 'px';
            l.style.top  = '' + (py - 12) + 'px';
            CE.CEDBG.println('Close: ' + l.style.left + ' - ' + l.style.top);
        }
        var r = CE.CEU.$('sqpvtool_fullscreen');
        if(r) {
            CE.rCN('sqpvtool_fullscreen', 'hidden');
            r.style.left = '' + (px + szx - 1) + 'px';
            r.style.top  = '' + (py + szy - 5) + 'px';
            CE.CEDBG.println('Full: ' + r.style.left + ' - ' + r.style.top);
        }

        m_pproot.style.width = ''+szx+'px';
        m_pproot.style.height = ''+szy+'px';
        m_pproot.style.left = ''+px+'px';
        m_pproot.style.top = ''+py+'px';
    }

    function startHideTimer() {
        if(m_toolhidetimer) {
            CE.uTimer(m_toolhidetimer);
            m_toolhidetimer = null;
        }
        m_toolhidetimer = CE.rTimer(hideTools, 2000);
    }
    function hideTools() {
        m_toolhidetimer = null;
        if(m_toolhidedisable<=0) {
            m_ribbon.animateShow(false);
            showTools(false);
            m_toolhidedisable = 0;
        } else {
            startHideTimer();
        }
    }
    function showTools(show) {
        if(show) {
            if(!m_toolsshowing) {
                //CE.CEDBG.println('-- Showing tools...');
                for (var key in m_tool) {
                    if(m_tool[key]) {
                        //CE.CEDBG.println('-- Showing tool "'+key+'"...');
                        m_tool[key].style.display= 'block';
                    }
                }
                m_toolsshowing = true;
            }
            startHideTimer();
        } else {
            if(m_toolhidetimer) {
                CE.uTimer(m_toolhidetimer);
                m_toolhidetimer = null;
            }
            if(m_toolsshowing) {
                //CE.CEDBG.println('-- Hiding tools...');
                for (var key in m_tool) {
                    if(m_tool[key]) {
                        m_tool[key].style.display= 'none';
                    }
                }
                m_toolsshowing = false;
            }
        }
    }

    function attachTools(showNow) {
        if(showNow) {
            showTools(true);
        }
    }

    function animShow() {
        // TODO: Use wall-clock to smooth this out...
        m_animstep = m_animstep + 40.0/1000.0 / 0.25;

        if(m_animstep < 1.0) {
            var offx = 0;
            var offy = 0;
            if(m_szxoff>0) {
                offx = (1-m_animstep) * (m_szxoff-m_vw/2);
            }
            if(m_szyoff>0) {
                offy = (1-m_animstep) * (m_szyoff-m_vh/2);
            }
            position(m_szxstart + (m_animstep * (m_szxtarget-m_szxstart)), 
                     m_szystart + (m_animstep * (m_szytarget-m_szystart)),
                     offx, offy);
            m_animtimer = CE.rTimer(animShow, 40);
        } else {
            position(m_szxtarget, m_szytarget);
            attachTools(false);
            m_animtimer = null;
        }
    }

    function onImageLoad(img) { 
        var w = img.width;
        var h = img.height;
        var ow = w;
        var oh = h;
        var aspect = w/h;

        //             CE.CEDBG.println("CEV: -- ASPECT CORRECT: was    ("+w+" x "+h+")");
        //             CE.CEDBG.println("CEV: -- ASPECT CORRECT: parent ("+m_previewDiv.clientWidth+" x "+m_previewDiv.clientHeight+")");
        if(img.szxtarget) {
            //              CE.CEDBG.println("CEV: -- TARGET WIDTH: "+img.szxtarget);
            if(w > img.szxtarget) {
                var o = w;
                var p = img.szxtarget;
                var r = p / o;
                //                  CE.CEDBG.println("CEV: -- ASPECT CORRECT: ratio: ("+p+" / "+o+" -- "+r+")");
                w *= r;
                h *= r;
                aspect = w/h;
            }
        }
        if(img.szytarget) {
            //              CE.CEDBG.println("CEV: -- TARGET HEIGHT: "+img.szytarget);
            if(h > img.szytarget) {
                var o = h;
                var p = img.szytarget;
                var r = p / o;
                //                  CE.CEDBG.println("CEV: -- ASPECT CORRECT: ratio: ("+p+" / "+o+" -- "+r+")");
                w *= r;
                h *= r;
                aspect = w/h;
            }
        }
        //             CE.CEDBG.println("CEV: -- ASPECT CORRECT: now    ("+w+" x "+h+")");
        //           img.width = w;
        //           img.height = h;

        if(m_noresize) {
            CE.CEDBG.println("CEV: -- ASPECT POSITION: was ("+ow+" x "+oh+") now ("+w+" x "+h+"): "+
                             "left: "+((m_szxtarget-w)/2)+" top: "+((m_szytarget-h)/2));

            img.style.position = 'relative';
            img.style.width  = ''+w+'px';
            img.style.height = ''+h+'px';
            // NOTE: We don't need to move left because we are text-align: center
            //img.style.left   = ''+((m_szxtarget-w)/2)+'px';
            img.style.top    = ''+((m_szytarget-h)/2)+'px';
        }

        if((m_animstate == 'show' && m_currimg == img) ||
           (m_animstate == 'zoom' && m_currfullimg == img)) {
            m_aspect = aspect;
            if(m_noresize) {
            } else {
                m_szxtarget = w;
                m_szytarget = h;
            }
            if(m_animtimer) {
                CE.uTimer(m_animtimer);
                m_animtimer = null;
            }
            animShow();
            CE.rac(m_previewDiv);
            m_previewDiv.appendChild(img);

            if(m_animstate == 'show') {
                if(m_tool['info']) {
                    //CE.CEDBG.println("CEV: -- Populating Info: "+m_file.name+" ("+m_file.comment+")");
                    CE.rac(m_tool['info']);
                    // File name
                    var n = CE.dce('div');
                    n.className = 'sqpreview_name';
                    n.appendChild(CE.dctn(CE.trims(CE.CEUI.getDispFn(m_file), 50)));
                    m_tool['info'].appendChild(n); n = null;
                    // File comments
                    if(m_file.comment) {
                        n = CE.dce('div');
                        n.className = 'sqpreview_comment';
                        n.appendChild(CE.dctn(m_file.comment));
                        m_tool['info'].appendChild(n); n = null;
                    }
                }
            } else if(m_animstate == 'zoom') {
                CE.CEDBG.println("CEV: -- FULL ZOOM:     ("+w+" x "+h+")");
            }
        }
    }

    function onImageMouseMove(h,e,tel) { 
        showTools(true);
    }

    function onImageClick(h,e,tel) { 
        if(m_animtimer) {
            CE.uTimer(m_animtimer);
            m_animstep = 1.0;
            m_szxoff   = 0;
            m_szyoff   = 0;
            animShow();
            if(m_animstate == 'show') {
                attachTools(true);
            }
        } else if(m_animstate == 'show') {
            if(!m_toolsshowing) {
                showTools(true);
            } else {
                showTools(false);
            }
            // start at current and zoom to fullscreen
            //               m_szxoff   = 0;
            //               m_szyoff   = 0;
            //               m_szxstart = m_szxtarget;
            //               m_szystart = m_szytarget;
            //               position(m_szxstart,m_szystart);

            //               m_currfullimg = new Image();
            //               m_szxtarget = m_vw;
            //               m_szytarget = m_vh;
            //               m_currfullimg.src= m_upfx + m_file.fileid + "/"+m_file.name;
            //               m_currfullimg.onload = function() {onImageLoad(this);};
            //               CE.CEU.attachEvent(m_currfullimg, 'click', {onEvent: onImageClick});
            
            //               m_animstep = 0.0;
            //               m_animstate = 'zoom';
            //               m_animtimer = CE.rTimer(animShow, 40);
        } else {
            that.hide();
        }
    }
    
    this.show = function (evt) {
        if(!m_showing) {
            m_showing = true;

            // start at 126x126
            if(evt) {
                // start at the mouse position rather than centered...
                m_szxoff   = evt.clientX;
                m_szyoff   = evt.clientY;
            } else {
                m_szxoff   = 0;
                m_szyoff   = 0;
            }
            m_szxstart = 126;
            m_szystart = 126;
            position(m_szxstart,m_szystart, m_szxoff, m_szyoff);

            document.body.appendChild(m_hidediv);
            document.body.appendChild(m_pproot);

            m_animstep = 0.0;
            m_animstate = 'show';
            m_animtimer = CE.rTimer(animShow, 40);
        }
    };

    this.hide = function () {
        stopSS();
        CE.aCN("sqpvtool_close",      "hidden");
        CE.aCN("sqpvtool_fullscreen", "hidden");
        if(m_showing) {
            showTools(false);
            document.body.removeChild(m_pproot);
            document.body.removeChild(m_hidediv);
            m_showing = false;
        }
        cleanup();
        CE.CEUI.g_videoPrevDisabled = false;
    };

    this.showPreview = function(evt, pg, idx) {
        if(m_showing) return;

        if(pg) {
            m_page = pg;
            m_pindex = idx;
            m_file = m_page[m_pindex];
            // rebuild URL prefix
            m_upfx = CE.CEUI.getDataStreamPrefix(m_file, null);
            m_ribbon.setFiles(m_upfx, pg, idx);

            cleanup();

            if(m_file) {
                var path     = m_upfx + m_file.fileid + "/";
                var preview  = null;
                var useFlash = CE.CEUI.isVideoFile(m_file) || CE.CEUI.isAudioFile(m_file);

                if(m_file.type == 1) {
                    path += "rss/";
                    useFlash = true;
                } else if(m_file.mimetype) {
                    if(0==m_file.mimetype.indexOf("image")) {
                        useFlash = false;
                    }
                } else {
                    var ext = path.substr(path.length-4);
                    if(ext) {
                        ext = ext.toLowerCase();
                        if(ext.match(".png") || ext.match(".jpg") || ext.match("jpeg") || ext.match(".gif")) {
                            useFlash = false;
                        }
                    }
                }
                path += encodeURIComponent(m_file.name);
                
                var svc = CE.CEUI.getCurSvc(m_file);
                if(m_file.stream && CE.CEUI.isVideoFile(m_file) && svc.xcodeStream != 'never') {
                    path = m_upfx + m_file.stream + "/" + encodeURIComponent(m_file.name) + ".mp4";
                    if(m_file.streamtype != 'full' && svc.xcodeStream == 'always') {
                        if(CE.CEUI.isTranscodeQueued(m_file,svc) && !m_file.streamRefreshed) {
                            CE.CEUI.refreshFileStream(m_file, svc, function(){that.showPreview(evt, pg, idx);});
                            return;
                        } else if(!CE.CEUI.isTranscodeQueued(m_file,svc)) {
                            // Queue the full transcode when the preview is done
                            CE.CEUI.g_videoPrevTranscode = true;
                            CE.CEUI.g_videoTranscodeFile = m_file;
                        }
                        m_file.streamRefreshed = false;
                    } else {
                        CE.CEUI.g_videoPrevTranscode = false;
                    }
                }
                
                if(m_file.preview)
                    preview = m_upfx + m_file.preview + "/" + encodeURIComponent(m_file.name);
                
                if(useFlash) {
                    createVideo(path, preview, m_previewDiv);
                    // Strongly set target size for video
                    m_szxtarget = 640;
                    m_szytarget = 480;
                } else {
                    m_currimg = new Image();
                    // Reset target size
                    if(m_noresize) {
                        m_szxtarget = 640;
                        m_szytarget = 480;
                        m_currimg.szxtarget = 640;
                        m_currimg.szytarget = 480;
                    } else {
                        m_szxtarget = 640;
                        m_szytarget = 640;
                        m_currimg.szxtarget = 640;
                        m_currimg.szytarget = 640;
                    }
                    m_currimg.onload = function() {onImageLoad(this);};
                    CE.CEU.attachEvent(m_currimg, 'click', {onEvent: onImageClick});
                    CE.CEU.attachEvent(m_currimg, 'mousemove', {onEvent: onImageMouseMove});
                    m_currimg.src= preview ? preview : path;
                }
            }
        }
        that.show(evt);
    };
}

CE.CEUI.FlashFileUploadDialog = function() {
    var that = this;

    var mShowing = false;

    //   -- DOM Elements we own
    var mUproot     = CE.dce('div', null, 'squpload');
    var mHideDiv    = CE.dce('div', null, 'squpload_hide');
    var mUploadDiv  = CE.dce('div', null, 'sqfileupload');

    mUproot.appendChild(mUploadDiv);

    // Disregard the argument 'file'; it exists for function signature parity with FileUploadDialog.
    this.show = function (file) {
        if(!mShowing) {
            mShowing = true;
            document.body.appendChild(mHideDiv);
            document.body.appendChild(mUproot);
            var version = CE.CEU.getFlashVersion();
        
            CE.CEDBG.println("Flash Version: " + version.join());
            if(version[0] < 9 || (version[0] == 9 && version[1] < 1 && version[2] < 115)) {
                mUploadDiv.appendChild(CE.CEU.getFlashUpgradeHTML());
            }
            else {
                var html;
                if(navigator.appName != "Microsoft Internet Explorer") {
                    html = '<embed type="application/x-shockwave-flash" src="' + CE.STRTAB.lookup("flashbase") + 'FileUploader.swf" name="FileUploader" wmode="transparent" allowScriptAccess="sameDomain" width="100%" height="100%"/>';
                } else {
                    html = '<object id="FileUploader" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="https://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="100%" height="100%" >' + 
                        '<param name="wmode" value="transparent" />' +
                        '<param name="movie" value="'+CE.STRTAB.lookup("flashbase")+'FileUploader.swf" />' +
                        '<param name="allowScriptAccess" value="sameDomain"/>' +
                        '</object>';
                }
                mUploadDiv.innerHTML = html;
            }
            // Now center the div in the screen
            mUproot.style.left = Math.floor(mHideDiv.offsetWidth/2 - mUproot.offsetWidth/2) + 'px';
            mUproot.style.top  = Math.floor(mHideDiv.offsetHeight/2 - mUproot.offsetHeight/2) + 'px';
        }
    };

    this.hide = function () {
        if(mShowing) {
            document.body.removeChild(mUproot);
            document.body.removeChild(mHideDiv);
            mShowing = false;
        }
        cleanup();
    };

    this.flashLoaded = function () {
        // Now that the flash is loaded, resize the containing div to the dimensions of the app (they will differ with locale)
        var flashApp = CE.CEU.getFlashMovieElement("FileUploader");

        var newWidth = flashApp.getFlashFileUploaderWidth();
        var newHeight = flashApp.getFlashFileUploaderHeight();

        mUproot.style.width = '' + newWidth + 'px';
        mUproot.style.height = '' + newHeight + 'px';

        // Now center the div in the screen
        mUproot.style.left = Math.floor(mHideDiv.offsetWidth/2 - mUproot.offsetWidth/2) + 'px';
        mUproot.style.top  = Math.floor(mHideDiv.offsetHeight/2 - mUproot.offsetHeight/2) + 'px';
    };

    function cleanup() {
        if(navigator.plugins) {
            var element = mUploadDiv.firstChild;
            while(element) {
                if(element.tagName == 'embed') {
                    element.sendEvent('stop');
                }
                if (element)
                    element=element.nextSibling;
            }
        } else {
            // Arg, we need to force flash to clean up a little better on IE or it leaks
            var element = mUploadDiv.firstChild;
            while(element) {
                if(element.tagName == 'object') {
                    element.sendEvent('stop');
                    element.style.display = 'none';
                    for(var x in element) {
                        if(typeof(element[x]) == 'function') {
                            CE.CEDBG.println('-- Killing Object Function: '+x);
                            element[x] = null;
                        }
                    }
                }
                element=element.nextSibling;
            }
            element = null;
        }
        CE.CEU.releaseAllEvents(mUploadDiv, 'click', true);
    };
    
}  
CE.CEUI.FileUploadDialog = function() {
    var that = this;
    
    var m_folder = null;
    var m_lastFormNumber = 0;
    var m_pane = CE.dce('div');
    var m_fileulpb = null;
    var m_uploadDialog = new CE.CEU.Dialog(CE.STRTAB.lookup('view.upload.title'), m_pane);
    var m_uploadDialogDisabled = false;
    var m_curfile = null;
    
    function onRemoveFileInput(ev) {
        var div = CE.CEU.$("addFileForm");
        var form = CE.CEU.$("form" + ev.fn);
        if(!form) {
            form = document.forms["form" + ev.fn];
        }
        if(!form) {
            // ie seems to not have an associative array here. 
            form = document.forms[ev.fn];
        }

        if(div) div.removeChild(form);
    }

    function addFileInput(div) {
        var form = CE.dce("form");

        form.id = form.name = "form" + m_lastFormNumber;
        form.method = "post";
        form.enctype = form.encoding = "multipart/form-data";
        //form.action =  CE.CEU.svc.serviceurl + "rest/uploadFile";
        form.target = "secretiframe";
        
        var input = CE.dce("input");
        input.type = "file";
        input.name = "fileinput" + m_lastFormNumber;
        input.className = 'uploadfile';
        input.id = input.name;

        form.appendChild(input);

        var a = CE.dce("a");
        a.href = "#";
        a.style.paddingLeft = "4px";
        a.appendChild(CE.STRTAB.lookupel('view.delete'));
        CE.CEU.attachEvent(a, 'click', {onEvent: onRemoveFileInput, fn: m_lastFormNumber });

        form.appendChild(a);
        
        m_lastFormNumber ++;
        div.appendChild(form);
    }
    
    function onAddFileClick() {
        var div = CE.CEU.$("addFileForm");
        if(div) addFileInput(div);
    }

    function onUploadFileDialogClose() {
        if(m_uploadDialog) {
            m_uploadDialog.hide();
        }
    }

    function onUploadFileComplete() {
        // its done. if user wants to cancel now, he will have to delete
        // manually.
        m_curfile = null;

        if(!g_onframeloaded) {
            // extra precaution
            return;
        }
        // make sure the progres bar undesterstands that we are done. there is a
        // slight disconnect between our out of band uploadProgres RPC's and 
        // the frames onload callback. we are going to trust the frame though.
        if(m_fileulpb) {
            m_fileulpb.uploadcompleted();
        }
        for(m_nextFormNumber ++; m_nextFormNumber < m_lastFormNumber; m_nextFormNumber ++) {
            var input = CE.CEU.$("fileinput" + m_nextFormNumber);
            if(input && input.value) {
                createFile(input);
                return;
            }       
        }
        // So we are really done. set frame src to null to avoid unpleasant repost dialogs. 
        var fr = CE.CEU.$("secretiframe");
        // be sure to set load handler to null first, otherwise we will gete into a 
        // recursive state. 
        g_onframeloaded = null;
        if(fr) fr.src = null;
        // clean up my divs. 
        CE.rac(CE.CEU.$("addFileForm"));
        if(m_uploadDialog) {
            m_uploadDialog.hide();
        }
        //window.location.replace(window.location);
        CE.CEUI.reloadContentCWD();
    }

    function uploadFile(form, file) {
        form.action = CE.CEUI.getDataStreamPrefix(m_folder, null, CE.CEUI.g_curalbum);
        form.action += file.fileid + "/";

        var frame = CE.CEU.$("secretiframe");
        if(frame) {
           g_onframeloaded = onUploadFileComplete;
        }
        if(m_fileulpb) m_fileulpb.setfile(file, m_folder);
        form.submit();
    }

    function onCreateFileSuccess(result) {
        if(result.file) {
            m_curfile = result.file;
            var form = CE.CEU.$("form" + m_nextFormNumber);
            if(!form) {
                form = document.forms["form" + m_nextFormNumber];
            }
            if(!form) {
                // ie seems to not have an associative array here. 
                form = document.forms[m_nextFormNumber-1];
            }
            if(form) {
                uploadFile(form, result.file);
            }
        }
    }
    
    function onCreateFileFailure(r, ignore, method) {
        var div = CE.CEU.$("fileAddForms");
        if(div) {
            var f = CE.CEU.$("secretiframe");
            if(f) f.onload = null;
            cleanse(div);
        }
        var reason = null;
        if(r && r['HB-EXCEPTION']) {
            reason = r['HB-EXCEPTION'];
        }
        if(reason) {
            CE.CEDBG.println('CEUI: RPC EXCEPTION ('+method+'): '+CE.CEDBG.serialize(reason));
        } else {
            CE.CEDBG.println('CEUI: RPC ERROR ('+method+'): '+CE.CEDBG.serialize(r));
        }
        if(reason && reason.ecode == 606) {
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.upload.title'), CE.STRTAB.lookup('view.upload.noperms'),
                [ { name: 'ok', label: CE.STRTAB.lookup('view.ok') } ]);
            dlg.show(); dlg = null;
        }
        m_uploadDialog.hide(); m_uploadDialog = null;
        //CE.CEU.notifyError(r, 'RPC', method);
        //showServerErrorDialog(result, "createFile");
    }
    
    function extractFilename(inputValue)
    {
        var fn = inputValue;
        var idx = fn.lastIndexOf("\\");
        if(idx >= 0) {
            fn = fn.substr(idx+1);
        } else {
            idx = fn.lastIndexOf("/");
            if(idx >= 0) {
                fn = fn.substr(idx+1);
            }
        }
        return CE.CEU.stripBadFnChars(fn);
    }    

    function createFile(input) {
        var args = [];
        var fn = extractFilename(input.value);

        if(CE.CEUI.g_cursvc) {
            args.push("deviceid");
            args.push(CE.CEUI.g_cursvc.deviceid);
            args.push("serviceid");
            args.push(CE.CEUI.g_cursvc.serviceid);
        } if(CE.CEUI.g_curalbum && CE.CEUI.g_curalbum.root) {

            args.push("deviceid");
            args.push(CE.CEUI.g_curalbum.root.deviceid);

            args.push("serviceid");
            args.push(CE.CEUI.g_curalbum.root.serviceid);

            args.push("albumid");
            args.push(CE.CEUI.g_curalbum.albumid);
        }
        
        if(m_folder) {
            args.push("parentid");
            args.push(m_folder.fileid);
        }
        args.push("filename");
        args.push(fn);
        args.push("type");
        args.push("0");

        var d = new Date();
        args.push("mtime");
        args.push("" + d.getTime());
        args.push("ctime");
        args.push("" + d.getTime());

        CE.CEU.svc.asyncRPC("POST", "createFile", args, onCreateFileSuccess, onCreateFileFailure);
    }

    function onUploadFilesClick() {
        if(m_uploadDialogDisabled) {
            return;
        }
        m_uploadDialogDisabled = true;
        m_nextFormNumber = 0;
        validateOverwrite();
    }
    
    function validateOverwrite()
    {
        while(m_nextFormNumber < m_lastFormNumber) {
            var input = CE.CEU.$("fileinput" + m_nextFormNumber);
            if(input && input.value) {
                // check whether this file already exists
                var svc = CE.CEUI.g_cursvc;
                if(!svc)
                    svc = CE.CEUI.g_curalbum.root;
                
                var args = [];
                
                args.push("deviceid");
                args.push(svc.deviceid);
                args.push("serviceid");
                args.push(svc.serviceid);
                
                args.push("parentid");                
                if(m_folder)
                    args.push(m_folder.fileid);
                else
                    args.push("0");

                args.push("filename");
                args.push(extractFilename(input.value));
            
                CE.CEU.svc.asyncRPC('POST', "getFile", args, checkFileExistsSuccess, checkFileExistsFailure);
                return;
            }
            m_nextFormNumber ++;
        }
        
        // all files have been validated for overwrite, so start the upload
        m_uploadDialogDisabled = false;
        uploadFiles();
    }
    
    function checkFileExistsSuccess(r) {
        if(r.file) {
            var dlg = new CE.CEU.Dialog(CE.STRTAB.lookup('view.overwrite.title'), CE.STRTAB.lookup('view.overwrite.prompt',CE.CEUI.getDispFn(r.file)),
                [{name:'yes',label:CE.STRTAB.lookup("view.yes"),callback:onYesOverwrite}, 
                {name:'no',label:CE.STRTAB.lookup("view.no"),callback:onNoDontOverwrite}]);
            dlg.show(); dlg = null;
            return;
        }

        // validate the next one
        m_nextFormNumber ++;
        validateOverwrite();
    }

    function checkFileExistsFailure(r) {
        // file doesn't exist; validate the next one
        m_nextFormNumber ++;
        validateOverwrite();
    }

    function onYesOverwrite() {
        // they want to overwrite; validate the next one
        m_nextFormNumber ++;
        validateOverwrite();
        return true;
    }

    function onNoDontOverwrite() {
        // they do not want to overwrite; return to the upload dialog
        return true;
    }

    function uploadFiles() {
        m_nextFormNumber = 0;
        while(m_nextFormNumber < m_lastFormNumber) {
            var input = CE.CEU.$("fileinput" + m_nextFormNumber);
            if(input && input.value) {
                CE.aCN("addFilesClicker", "hidden");
                CE.aCN("addFileForm", "hidden");
                createFile(input);

                var pbdiv = CE.dce("div");
                pbdiv.id = "fileulpb";
                
                var uldgdiv = CE.CEU.$("uldgcontent");
                uldgdiv.appendChild(pbdiv);

               m_fileulpb = new FileUploadPB("fileulpb");
                m_uploadDialog.removeButton("upload");             
                return;
            }
            m_nextFormNumber ++;
        }
    }    

    function onCancelUploadClick() {
        if(m_uploadDialogDisabled) {
            return;
        }
        if(m_fileulpb) {
            m_fileulpb.setfile(null, null);
        }
        var frame = CE.CEU.$("secretiframe");
        if(frame) {
            g_onframeloaded = null;
            frame.src = null;
        }
        if(m_curfile) {
            var args = [];
            if(CE.CEUI.g_cursvc) {
                args.push("deviceid");
                args.push(CE.CEUI.g_cursvc.deviceid);
                args.push("serviceid");
                args.push(CE.CEUI.g_cursvc.serviceid);
            } else if(m_folder) {
                if(m_folder.deviceid) {
                    args.push("deviceid");
                    args.push(m_folder.deviceid);
                }
                if(m_folder.serviceid) {
                    args.push("serviceid");
                    args.push(m_folder.serviceid);
                }
            }
            args.push("fileid");
            args.push(m_curfile.fileid);
            CE.CEU.svc.asyncRPC("POST", "removeFile", args);
        }            
        CE.rac(CE.CEU.$("addFileForm"));
        return true;
    }

    (function() {
        m_pane.id = "uldgcontent";
        var sprdiv = CE.dce("div", "superUploadDiv");
        var adddiv = CE.dce("div", "addFileForm");
        
        sprdiv.appendChild(adddiv);
        
        var addfiles = CE.dca({onEvent: onAddFileClick}, "addFilesClicker");
        addfiles.appendChild(CE.dctn(CE.STRTAB.lookup("ceui.addfiles")));
        sprdiv.appendChild(addfiles);
        m_pane.appendChild(sprdiv);
        
        m_lastFormNumber = 0;
        addFileInput(adddiv);

        m_uploadDialog.addButton('upload',  CE.STRTAB.lookup('ceui.upload'), onUploadFilesClick);
        m_uploadDialog.addButton('cancelupload', CE.STRTAB.lookup('ceui.cancel'), onCancelUploadClick);

    })();
    
    this.show = function (file) { 
        m_folder = file;
        m_uploadDialog.show();
    };

    function FileUploadPB(rootid) {
        var that = this;

        var m_rootid = null;
        var m_bgid = null;
        var m_fgid = null;
        var m_nameid = null;
        var m_offid = null;
        var m_lenid = null;
        var m_rateid = null;
        var m_remid = null;
        var m_file = null;

        var m_stime = null;
        var m_utime = null;
        var m_lastinc = 0;
        var m_lastlen = 0;

        var m_timeoutid = null;
        var m_uploadcompleted = false;
        var m_failcount = 0;

        function getCurrentTime() {
            var d = new Date();
            return d.getTime();
        }

        function onUploadProgressSuccess(result) {
            that.update(result.offset, result.length);
            // lets make damn sure we hit 100% if the upload completed.
            if(m_uploadcompleted) {
                if(m_fgid) {
                    var fg = CE.CEU.$(m_fgid);
                    if(fg) fg.style.width = "100%";
                }
            }
            if(!m_uploadcompleted) {
                m_timeoutid = CE.rTimer(uploadProgressTimer, 1000);
            }
        }

        function onUploadProgressFailure(result) {
            // we maybe got a failure because the upload completed. lets not give 
            // anyone a hard time about that. 
            if(m_uploadcompleted) {
                if(m_fgid) {
                    var fg = CE.CEU.$(m_fgid);
                    if(fg) fg.style.width = "100%";
                }
            }
            //m_timeoutid = registerTimer(uploadProgressTimer, FILE_UPLOAD_PROGRESS_TIMEOUT);
        }

        function doUploadProgressRPC() {
            var args = [];

            if(CE.CEUI.g_curalbum && CE.CEUI.g_curalbum.root) {
                args.push("deviceid");
                args.push(CE.CEUI.g_curalbum.root.deviceid);

                args.push("serviceid");
                args.push(CE.CEUI.g_curalbum.root.serviceid);
            } else if(CE.CEUI.g_cursvc) {
                args.push("deviceid");
                args.push(CE.CEUI.g_cursvc.deviceid);

                args.push("serviceid");          
                args.push(CE.CEUI.g_cursvc.serviceid);
            } 
            args.push("fileid");
            args.push(m_file.fileid);

            CE.CEU.svc.asyncRPC("POST", "uploadProgress", args, onUploadProgressSuccess, onUploadProgressFailure);
        }

        function uploadProgressTimer() {
            m_timeoutid = null;
            if(m_file) {
                doUploadProgressRPC();
            }
        }

        function cae(type, id, cls, pid) {
            var e = CE.dce(type, id, cls);
            if(pid) {
                var p = CE.CEU.$(pid);
                p.appendChild(e);
            }
            return e;
        }

        var KILOBYTE = (1024);
        var MEGABYTE = (1024 * 1024);
        var GIGABYTE = (1024 * 1024 * 1024);
        
        // divide totalbytes by KILOBIT to get # of kilobits
        var KILOBIT = 128;
        var MEGABIT = (1024 * 128);

        function createReadableSizeString(value) {
            var size = '';
            var suffix = '';
            
            if((value / GIGABYTE) > .8) {
                size = ((Math.round((value / GIGABYTE) * 10)) / 10);
                suffix = CE.STRTAB.lookup('ceui.gigabytes');
            } else if((value / MEGABYTE) > .8) {
                size = ((Math.round((value / MEGABYTE) * 10)) / 10); 
                suffix = CE.STRTAB.lookup('ceui.megabytes');
            } else {
                size = ((Math.round((value / KILOBYTE) * 10))  / 10); 
                suffix = CE.STRTAB.lookup('ceui.kilobytes');
            } 
            
            if(size == Infinity || isNaN(size)) {
                size = '';
                suffix = '';
            }
            return size + suffix;
        }

        function createReadableBitrateString(value) {
            var rate = '';
            
            if((value / MEGABIT) > 1) {
                rate = (Math.round((value / MEGABIT) * 10) / 10) + CE.STRTAB.lookup('ceui.megabits');
            } else if((value / KILOBIT) > 1) {
                rate = (Math.round((value / KILOBIT) * 10) / 10) + CE.STRTAB.lookup('ceui.kilobits');
            } else {
                rate = value + CE.STRTAB.lookup('ceui.bits');
            }
            return rate;
        }
        
        function createReadableTimeString(value) {
            if(value < 60) {
                var seconds = Math.round(value);
                if(seconds == 1) {
                    return CE.STRTAB.lookup('ceui.second', seconds);
                } else {
                    return CE.STRTAB.lookup('ceui.seconds', seconds);
                }
            } else if((value / 60) < 60) {
                var minutes = Math.round(value / 60);
                if(minutes == 1) {
                    return CE.STRTAB.lookup('ceui.minute', minutes);
                } else {
                    return CE.STRTAB.lookup('ceui.minutes', minutes);
                }
            } else {
                var hours = Math.round(value / 3600);
                if(hours == 1) {
                    return CE.STRTAB.lookup('ceui.hour', hours);
                } else {
                    return CE.STRTAB.lookup('ceui.hours', hours);
                }
            }
            return 0;
        }


        this.uploadcompleted = function() {
            m_uploadcompleted = true;
            if(m_lastlen == 0) {
                if(m_file) {
                    doUploadProgressRPC();
                } 
            } else if(m_fgid) {
                var fg = CE.CEU.$(m_fgid);
                if(fg) {
                    fg.style.width = "100%";
                }     
            }
        };

        this.setfile = function(file, folder) {
            m_uploadcompleted = false;
            m_file = file;
            m_folder = folder;
            var l = null;
            if(m_fgid) {
                l = CE.CEU.$(m_fgid);
                if(l) l.style.width = "0%";
            }
            if(m_nameid) {
                l = CE.CEU.$(m_nameid);
                if(l) l.innerHTML = "";
            }
            if(m_offid) {
                l = CE.CEU.$(m_offid);
                if(l) l.innerHTML = "";
            }
            if(m_lenid) {
                l = CE.CEU.$(m_lenid);
                if(l) l.innerHTML = "";
            }
            if(m_rateid) {
                l = CE.CEU.$(m_lenid);
                if(l) l.innerHTML = "";
            }
            if(m_remid) {
                l = CE.CEU.$(m_lenid);
                if(l) l.innerHTML = "";
            }
            if(m_file) { 
                m_stime = m_utime = getCurrentTime();
                if(m_nameid) {
                    var nm = CE.CEU.$(m_nameid);
                    if(nm) {
                        var n = m_file.name;
                        if(n) {
                            if(n.length > 50) {
                                n = n.substr(0, 49) + "...";
                            }
                            nm.innerHTML = n;
                        }
                    }
                }
                doUploadProgressRPC();
            }
        }

        this.update = function(inc, len) {
            var frac = inc / len;
            var percent = 0;

            if(isNaN(frac) || (frac < 0) || (frac > 1)) {
            } else {
                percent = Math.floor(frac * 100);
            }
            if(m_fgid) {
                var fg = CE.CEU.$(m_fgid);
                if(fg) {
                    fg.style.width = "" + percent + "%";
                }
            }      
            if(m_offid) {
                var off = CE.CEU.$(m_offid);
                if(off) {
                    off.innerHTML = createReadableSizeString(inc);
                }
            }
            if(m_lenid) {
                var lendiv = CE.CEU.$(m_lenid);
                if(lendiv) {
                    lendiv.innerHTML = createReadableSizeString(len);
                }
            }
            if(m_rateid) {
                var ratediv = CE.CEU.$(m_rateid);
                if(ratediv) {
                    var now = getCurrentTime();
                    var dif = now - m_utime;
                    m_utime = now;

                    var bytes = inc - m_lastinc;
                    m_lastinc = inc;

                    if(bytes > 0 && dif > 0) {
                        var Bps = bytes / (dif/1000);
                        if(isNaN(Bps) || Bps == Infinity) {
                        } else {
                                   
                            ratediv.innerHTML = createReadableSizeString(Bps);

                            if(m_remid) {
                                var remdiv = CE.CEU.$(m_remid);

                                if(remdiv) {
                                    var rem = len - inc;
                                    
                                    rem = rem / Bps;

                                    if(isNaN(rem) || rem == Infinity) {
                                    } else {
                                        remdiv.innerHTML = createReadableTimeString(rem);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            m_lastlen = len;
        };

        this.init = function(elid) {
            m_rootid = elid;

            var rootel = CE.CEU.$(m_rootid);
            if(!rootel) return;

            var div = cae("div", m_rootid + "_holder", "cepbholder", rootel);
            var el = cae("div", m_rootid + "_pbn", "cepbname", div);
            m_nameid = el.id;
            el = cae("div", m_rootid + "_pboff", "cepboff", div);
            m_offid = el.id;
            el = cae("div", m_rootid + "_pbof", "cepbof", div);
            el.appendChild(CE.dctn(CE.STRTAB.lookup("ceui.of")));
            el = cae("div", m_rootid + "_pblen", "cepblen", div);
            m_lenid = el.id;
            el = cae("div", m_rootid + "_pbrate", "cepbrate", div);
            m_rateid = el.id;
            el = cae("div", m_rootid + "_pbrem", "cepbrem", div);
            m_remid = el.id;
            el = cae("div", m_rootid + "_pbbg", "cepbb", rootel);
            m_bgid = el.id;
            el = cae("div", rootid + "_pbfg", "cepbf", el);
            m_fgid = el.id;
        };
        this.init(rootid);
    }
};
