// ==UserScript==
// @name                Unheart-reheart
// @version             1.0
// @date                2009-02-17
// @author              Ian Malpass ( ian AT etsyhacks DOT com )
// @namespace           etsy.com
// @description         Remove hearts from the shop and listing views (and add them back again)
// @include             http://www.etsy.com/shop.php?*
// @include             http://www.etsy.com/view_listing.php?*
// @include             http://www.etsy.com/favorite_sellers.php*
// @include             http://www.etsy.com/favorite_listings.php*
// ==/UserScript==

// image data for the "unheart" icons
var remove_img = {
    shop: "data:image/gif,GIF89a%10%00%10%00%E6J%00%A2%D1D%E3%E3%E3%A0%A0%A0%F2%F2%F2%F3%F3%F3%D5%D5%D5%A8%A8%A8%86%B5(%B1%B1%B1%BC%BC%BC%F7%F7%F7%A2%A2%A2%F6%F6%F6%DE%DE%DE%FC%FC%FC%CA%CA%CA%D1%D1%D1%F5%F5%F5%C0%C0%C0%B4%E3V%DF%DF%DF%DD%DD%DD%83%B2%25%F4%F4%F4%E4%E4%E4%A3%D2E%BF%BF%BF%C5%C5%C5%B2%B2%B2%A7%A7%A7%ED%ED%ED%E6%E6%E6%8A%B9%2C%C1%C1%C1%A5%D4G%E1%E1%E1%80%AF%22%9C%CB%3E%FF%FF%FF%EB%EB%EB%E8%E8%E8%C5%F4g%D6%D6%D6%E9%E9%E9%FA%FA%FA%C3%F2e%CD%CD%CD%EC%EC%EC%BA%E9%5C%C9%C9%C9%F1%F1%F1%CC%CC%CC%E2%E2%E2%CE%CE%CE%A4%A4%A4%F8%F8%F8%C9%F8k%BE%BE%BE%B9%B9%B9%FE%FE%FE%C3%C3%C3%B4%B4%B4%D2%D2%D2%EA%EA%EA%BB%BB%BB%F0%F0%F0%8E%BD0%A5%A5%A5%B0%B0%B0%E7%E7%E7%F9%F9%F9%B0%DFR%DB%DB%DB%FD%FD%FD%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00J%00%2C%00%00%00%00%10%00%10%00%00%07%9D%80JJF%3C%1D%08%23%82%89%8A%82%12!%01%14%02'%8B%8A%2C%02%1F%3F%26%051%93%89%0E%0B%01%2B%0E%3E3%9C%89%0F%09%1056%04%A5%89*%09%1BA%AD%8A%04I%B3%82%0C%3D%06%06(%B7%3A%124%05%0B%1E%AD%18%08%14%1F%0A%05D%3B%A5%09%0F%0D%1E%0C7%07%20B%22-%19%00%00J%9EH%0D2%11%0A%16G%13%130)8%DC%01%08%15%01%04%17%11%24%16%07%07%25%DA%DC.%1A%15E%03%F0%0C%0D%80L%DA%90%03B%80%01%081%1400I%C1%03%0E%1D%04%0C%11%C0A%C3%8BI%81%00%00%3B",
    item: "data:image/gif,GIF89a%10%00%10%00%E7%7F%00%C17%25%C86%23%CA6%22%CD6%22%C89%26%C9%3D%2B%A1I%3D%9ELB%A4K%3F%C9C2%BEF7%AEK%3D%95SJ%9EQG%BDK%3D%CAH7%D3F3%D2K%3A%8F%5DV%B0VJ%E3L8%CFSC%8Cgb%E5Q%3D%E1R%3F%89jf%AFaV%E6TA%E0WEsssyrqttt%80sp%EAXEvvv%E6ZH%EA%5BHyyy%EA%5DJ%9Drj%7B%7B%7B%EB_M%E7bQ%EBbP~~~%D1iZ%A3um%B9pd%EDdR%80%80%80%EBeT%EAfT%B0vm%ECgV%B5vl%83%83%83%ECjY%85%85%85%F0lZ%B6%7Bq%88%88%88%F4m%5C%D4xj%F8o%5D%C5%7Dq%FCq_%DEzk%8F%8F%8F%FFsa%FDub%F7we%FFub%91%91%91%92%92%92%FFwd%FAyf%FFyf%FF%7Bg%FF%7Di%99%99%99%FF%80k%9B%9B%9B%FF%82m%81%B0%23%9C%9C%9C%FF%84o%9E%9E%9E%A0%A0%A0%FF%8By%AA%AA%AA%AD%AD%AD%96%BFF%B4%B4%B4%A2%C6K%B6%C0d%A9%C4p%A2%CAQ%B9%B9%B9%9F%CEA%BB%BB%BB%BA%C5i%AF%CAv%A4%D3F%C0%C0%C0%AB%D3Z%B0%DFR%CD%CD%CD%CF%CF%CF%C2%DE%89%B8%E7Z%FF%C9%B5%FF%CB%C2%C6%E2%8D%BD%EC_%FF%CF%BC%C8%E4%90%CA%E4%92%D9%D9%D9%BF%EEa%DF%DF%DF%E6%E6%E6%E9%E9%E9%EE%EE%EE%F0%F0%F0%F5%F5%F5%F8%F8%F8%FD%FD%FD%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF!%FE%11Created%20with%20GIMP%00!%F9%04%01%00%00%80%00%2C%00%00%00%00%10%00%10%00%00%08%D8%00%01%01%BA%A3%05%09%953%7D%04*%5C8%26%83%86%16%3Eh%F0%E0%03%A8%8F%9A%2Ca%F6%00%AAc%A1%C2%88%23J%98%D8%E0%A2%06%C5%89%17%3BJ%E0%B12!%02%8C%20D%8E%18%E9%E0B%08%14)U%80%20%C9%E1%E0%C1%8C%1E%3F%60%16%09%D9%C4%C9M%0F7%14%24P%81C%07P%98%20%99%14%05%11eA%01%0E2j4%7DJD%89%92%25%22%D6H%20%00!%C5%8A%AC8%B6%FEPB%E2%0A%A0%24%0D%02%600aV%06Z%1D%17%DC%02%DA%13%03%81%80%0D%24L%60%91%F3%A7p%E1%3B%02%F1%B080%E0B%089%5E%C8t%89%83f%0E%1D%85z%860%00%40%C1%CD%141i%EC%C4icf%A1%9F1%1F%0C%BC%F9RfK%1A0l%E0%2C%14%98%E7%89%1A%C3%86%11%CF%DE%CD%BB%F7%EC%80%00%3B"
};

// URLs for the heart icons already on Etsy
var add_img = {
    shop: "http://www.etsy.com/images/icon_add_seller_to_favorite.gif",
    item: "http://www.etsy.com/images/icon_add_item_to_favorites.gif"
}

if ( document.location.href.indexOf( 'shop.php' ) > -1 || document.location.href.indexOf( 'view_listing.php' ) > -1 ) {
    // dealing with shop or listing heart/unheart
    // find the ID
    var url_id = document.location.search.match( /(user|listing)_id=(\d+)/ )[ 2 ];
    var id = {};

    var links = document.getElementsByTagName( 'a' );
    for ( l = 0; l < links.length; l++ ) {
        var link = links[ l ];
        if ( link.innerHTML.indexOf( 'Sign In' ) > -1 ) {
            // not signed in - fall back to standard behaviour
            break;
        } else if ( link.innerHTML.indexOf( 'Sign Out' ) > -1 ) {
            if ( document.location.href.indexOf( 'shop.php' ) > -1 ) {
                // Signed in, and on a shop page - we have all the info we need, so fix the heart links
                id.shop = url_id;
                toggle( 'shop', true );
            } else {
                // Signed in on a listing page - find out which shop we're on
                for ( l = l + 2; l < links.length; l++ ) {
                    link = links[ l ];
                    if ( link.href && link.href.indexOf( 'shop.php' ) > -1 ) {
                        // found the shop link in the breadcrumb nav - get the ID and fix the heart links
                        id.shop = link.href.match( /user_id=(\d+)/ )[ 1 ];
                        id.item = url_id;
                        toggle( 'shop', true );
                        toggle( 'item', true );
                        break;
                    }
                }
            }
            break;
        }
    }
} else {
    // we're on a "favourites" page - sellers or listings
    // find the X buttons on the page
    var dgl = getElementsByClassName( 'dark_grey_link' );
    // 0-2 are the navigation links - ignore them
    for ( var d = 3; d < dgl.length; d++ ) {
        cell = dgl[ d ];
        // find the X
        var x = cell.getElementsByTagName( 'a' )[ 0 ];
        // find the containing table
        var container = x.parentNode;
        while ( container.className != 'grey_border' ) container = container.parentNode;

        // make the "reheart" link
        var p = x.cloneNode( true );
        p.innerHTML = '+';
        p.style.display = 'none'; // hide

        // fix the reheart link's href
        var match = p.href.match( /favorite_(sellers|listings).*remove_id=(\d+)/ );
        if ( match ) {
            if ( match[ 1 ] == 'sellers' ) {
                p.href = 'http://www.etsy.com/add_favorite_shop.php?ajax=1&user_id=' + match[ 2 ];
            } else {
                p.href = 'http://www.etsy.com/add_favorite_listing.php?ajax=1&listing_id=' + match[ 2 ];
            }
        }
        // the ubiquitous "AJAX request loading" icon
        var loading = document.createElement( 'img' );
        loading.height = "16";
        loading.width = "16";
        loading.style.display = 'none';
        loading.src = "http://www.etsy.com/images/ajax-loader.gif";

        // add the loading icon and the reheart link
        cell.appendChild( p );
        cell.appendChild( loading );

        // add event listeners to intercept the clicks and do AJAX requests
        // rather than server round-trips
        x.addEventListener( 'click', genToggle( x, p, container, loading ), false );
        p.addEventListener( 'click', genToggle( p, x, container, loading ), false );
    }
}

function toggle ( type, entry ) {
    var suffix, url;
    // set up various bits we'll need to customise URLs etc., depending on what type of thing we're hearting
    if ( type == 'shop' ) {
        suffix = 'Seller';
        url_add = 'http://www.etsy.com/add_favorite_shop.php?ajax=1&user_id=' + id.shop;
        url_remove = 'http://www.etsy.com/favorite_sellers.php?remove_id=' + id.shop;
    } else {
        suffix = 'Item';
        url_add = 'http://www.etsy.com/add_favorite_listing.php?ajax=1&listing_id=' + id.item;
        url_remove = 'http://www.etsy.com/favorite_listings.php?remove_id=' + id.item;
    }
    // find the nodes we're interested in
    var loading = document.getElementById( 'ajaxLoaderIconHeart' + suffix );
    if ( loading == null ) return;
    var heart = document.getElementById( 'imgHeart' + suffix );
    if ( heart == null ) return;
    var cell = heart.parentNode.parentNode.cells[ 1 ];

    var a = document.createElement( 'a' );
    if ( ( entry && cell.innerHTML.indexOf( 'Already a favorite ' + type ) > -1 ) || ( ! entry && heart.src.indexOf( 'icon_add_' + suffix.toLowerCase() ) > -1 ) ) {
        // either we've just loaded the page and the item's already a favourite
        // or we're toggling and the item's marked as a favourite
        cell.innerHTML = '';
        a.innerHTML = 'Remove ' + suffix.toLowerCase() + ' from favorites';
        a.href = url_remove;
        heart.src = remove_img[ type ];
    } else {
        // not a favourite - replace the current "add to favorites" link
        // with our own
        cell.innerHTML = '';
        a.innerHTML = 'Add ' + suffix.toLowerCase() + ' to favorites';
        a.href = url_add;
        heart.src = add_img[ type ];
    }
    // add a click handler to the link to do the work
    a.addEventListener( 'click', genHandler( type, a, loading, heart ), false );
    // add our new link to the cell
    cell.appendChild( a );
    // hide the loading icon and show the heart icon (for when we're coming in from a heart/unheart toggle
    loading.style.display = 'none';
    heart.style.display = '';
}

function toggleFav ( node, other, container, loading ) {
    // hide the loading icon
    loading.style.display = 'none';
    // show the +/x icon (whichever is provided
    other.style.display = '';
    // toggle the opacity
    if ( container.style.opacity == '' ) {
        container.style.opacity = '0.3';
    } else {
        container.style.opacity = '';
    }
}

function genHandler( type, a, loading, heart ) {
    return function ( event ) {
        event.preventDefault(); // don't follow through on the click event
        loading.style.display = 'block'; // show the loading icon
        heart.style.display = 'none'; // hide the add/remove icon
        // send the AJAX request to heart/unheart
        // and toggle the UI when it's done
        GM_xmlhttpRequest( {
            method: 'GET',
            url: a.href,
            onload: function ( response ) { toggle( type ) }
        } );
    }
}

function genToggle( node, other, container, loading ) {
    return function ( event ) {
        event.preventDefault(); // don't follow through on the click event
        loading.style.display = ''; // show the loading icon
        node.style.display = 'none'; // hide the x or + link
        // send the AJAX request to heart/unheart
        // and toggle the UI when it's done
        GM_xmlhttpRequest( {
            method: 'GET',
            url: node.href,
            onload: function ( response ) { toggleFav( node, other, container, loading ) }
        } );
    };
}

// utility function to replicate getElementsByClassName() on older Firefoxes
function getElementsByClassName ( class, node ) {
    if ( node == null ) node = document;
    if ( node.getElementsByClassName ) {
        return node.getElementsByClassName( class );
    } else {
        var classElements = new Array();
        var els = node.getElementsByTagName( '*' );
        var elsLen = els.length;
        var pattern = new RegExp("(^|\\s)"+class+"(\\s|$)");
        for (i = 0, j = 0; i < elsLen; i++) {
            if ( pattern.test(els[i].className) ) {
                classElements[j] = els[i];
                j++;
            }
        }
        return classElements;
    }
}

