// ==UserScript==
// @name                Copy listing
// @version             1.3
// @date                2009-03-16
// @author              Ian Malpass ( ian AT etsyhacks DOT com )
// @namespace           etsy.com
// @description         Create a new listing like an existing listing
// @include             http://www.etsy.com/view_listing.php?*
// @include             http://www.etsy.com/create_listing*
// ==/UserScript==

var cc_icon = "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%0F%00%00%00%0F%08%06%00%00%00%3B%D6%95J%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%06bKGD%00%FF%00%FF%00%FF%A0%BD%A7%93%00%00%00%09pHYs%00%00%0A%F0%00%00%0A%F0%01B%AC4%98%00%00%00%07tIME%07%D9%01%15%131%0B%EC%AD%AAI%00%00%02%40IDAT(%CF%5D%93%BBkTQ%10%C6%7F%E7%DC%BB%BBww%137%0F%D4%24%C4%40D%08%0A%01%2B%11l%B4%B0%12%FD%03%14%2C-%03%A6%B1%B4R%14DA%11%ECD%C1N%D0%D2J!%A0%85%18%D4J%7C%90h%08Q%C9c%F3%D8d%F7%DE%7Bf%C6b%93%98%E4%C0%E1%CC)~%F3%7D%7C%C38%F6%9C%1B%E3%2F%0A%40U%D5%C8%F3%80%8A%11%82%92%A7%01Uk%01%E9%BDg%97%0D%C0%EF%85%E3%D8%9F%2B%25q%BD%A3V%AAw%D6%92zgWR%DF%D7%95%D4%E3BT%F7%DE%DD%8E%22%3Fx%F5%D2S%07%E0n%3Dz%15%01%A3fm%A5n%2B%9D94P%BB%3B%7C%B4%87b!%A2Z)%12y%CF%CB'%9FY%5EHi5%B3%FB%CD%F5%EC%8E%88%CE%C6%40g%1C%F9%8F%FD%07j%84%20%88(%0B%9A%F1e%E2%1BI%D1s%F2%F8%10%7D%BD5%CC%60%E8%F0~f%A6%E6%C76%1A)%C0%B5%D8%CC%A8%94%8B%5C%BCpb%DB%BA%19L%BC%FF%CA%DB%C9i~%CC%AC%90n(%E5J%C2%CF%EF%F3%24%D5%08%E7m%CCT%AF%7BS%C5Tw%80%86%9A%A1%06%1D%D5%84%FAj%C6%BB%C9_%F4%0Dws%EA%EC%08%A7%CF%1F%A3P%06%B1%94XDPUB%10%16%97%D6%115%82%82hD.P%ADD%84H%90(%90tT)%95%0B%A8%1A%22%D6%86E%043%08A%08%0AA%60p%A0%97%BE%BE%1E*%E5%02%AF'%3E%B0%BA%D4%A2%F5%DB3%3CZ%40%015%236%03%DB%FC4Si%2B%1B%ED%26%0A%95J%09%E7%1Dj%86%19%88%1A%98a%B6%A5%1C%94%90%0Bs%7F%96%C9%15ri%DFL%C0%C7%11f%BB%B3%C0%C0%01%B1%88%22*%BB%926%40%D9%F9%1A%95Z%89%83%FBj%18%9E%20%8A%A8%12%A7YN%9E%05%0C%C8%95m%CBj%20%9Bjf%C6%EC%D4_%A6%96%E7h42Z%CD%0C%EF%3D%F1F%B3E%B9%14S%2C%1582%D2%8FY%1B4%031p%B1'%13c%B6%BE8ns%FAfm-%25%F2%0E%E7%DCZ%EC%BD%B2%BA%DE%E0%E6%83%E7%C8%96%E7%CDyo%07%D9l%11%5C%98~%F8%F8%CA%A7%5D%7B%E0%1C%2B%AA%DA%BD%B4%DC%E0%3F%D9%AE%B6Br%0E%1Cn%7D%EF%12%FD%03%1D%BAs6%9BYr%C2%00%00%00%00IEND%AEB%60%82";

if ( document.location.href.indexOf( 'view_listing.php' ) > -1 ) {
    // viewing a listing - check the shop and add the link if necessary
    decorateViewListing();
} else if ( document.location.href.indexOf( 'create_listing1.php' ) > -1 ) {
    // on the first page of creating a new listing
    var match = document.location.search.match( /create_from=(\d+)/ );
    if ( match ) {
        // we have a "create from" argument - we're copying a current listing
        create_listing1( match[ 1 ] );
    }
} else if ( document.location.href.indexOf( 'create_listing2.php' ) > -1 ) {
    var match = document.location.search.match( /listing_id=(\d+)/ );
    var listing_id = match[ 1 ];
    if ( document.referrer ) {
        var match = document.referrer.match( /create_from=(\d+)/ );
        if ( match ) {
            // we've come from an item creation page that's a copy of a current listing
            // map the new listing ID to the copied item's ID for future reference
            GM_setValue( listing_id + '_from', match[ 1 ] );
            // and flag that we've set values for the first page, so we don't overwrite them
            // later if we use the "previous" button
            addSetValue( listing_id, 1 );
        }
    }
    var from = GM_getValue( listing_id + '_from' );
    // if we're copying a current listing and we haven't set values for page 2 already,
    // set the values
    if ( from && ! checkSetValue( listing_id, 2 ) ) create_listing2( listing_id, from );
} else if ( document.location.href.indexOf( 'create_listing3.php' ) > -1 ) {
    var match = document.location.search.match( /listing_id=(\d+)/ );
    var listing_id = match[ 1 ];
    if ( document.referrer ) {
        var match = document.referrer.match( /create_listing3.php/ );
        if ( match ) {
            // we've updated a shipping profile - mark that we've already set this page's values
            addSetValue( listing_id, 3 );
        }
    }
    var from = GM_getValue( listing_id + '_from' );
    if ( from ) addSetValue( listing_id, 2 ); // copying a listing, so we've set values for page 2
    // if we're copying a current listing and we haven't set values for page 3 already,
    // set the values
    if ( from && ! checkSetValue( listing_id, 3 ) ) create_listing3( listing_id, from );
} else if ( document.location.href.indexOf( 'create_listing4.php' ) > -1 ) {
    // kind a placeholder - can't update images yet
    var match = document.location.search.match( /listing_id=(\d+)/ );
    var listing_id = match[ 1 ];
    var from = GM_getValue( listing_id + '_from' );
    if ( from ) addSetValue( listing_id, 3 );
}

// utility function to manage the array of "pages we've set values for for this listing"
function addSetValue( listing_id, page ) {
    var values = GM_getValue( listing_id + '_set' );
    values = ( values == null ) ? [] : eval( values );
    values[ page ] = true;
    GM_setValue( listing_id + '_set', uneval( values ) );
}    

// utility function for querying the array of "pages we've set value for for this listing"
function checkSetValue( listing_id, page ) {
    var values = GM_getValue( listing_id + '_set' );
    values = ( values == null ) ? [] : eval( values );
    return values[ page ];
}

// check the shop, and if it's our shop, add the "copy" link
function decorateViewListing () {
    var links = document.getElementsByTagName( 'a' );
    var shopPattern = /shop.php\?user_id=(\d+)/; // find shop links

    var yourShop; // what's your shop ID?
    var thisShop; // what's the current shop's ID?

    var l = 0;
    for ( 1; l < links.length; l++ ) {
        var link = links[ l ];
        if ( link.href ) {
            var match = shopPattern.exec( link.href );
            if ( match ) {
                // we have a shop link
                if ( link.title == 'Your Shop' ) {
                    // this is the link to your shop in the top nav bar
                    yourShop = match[ 1 ];
                } else if ( link.firstChild && link.firstChild.data && link.firstChild.data == 'shop' ) {
                    // this is the link to the shop in the "seller info" box
                    thisShop = match[ 1 ];
                    // if we find this, we're done
                    break;
                }
            }
        }
    }

    if ( yourShop == thisShop ) {
        // it's our item
        // find the listing ID
        var match = window.location.search.match( /listing_id=(\d+)/ );
        var listing_id = match[ 1 ];

        // search for the "report to Etsy" link
        for ( 1; l < links.length; l++ ) {
            var link = links[ l ];
            if ( link.firstChild && link.firstChild.data && link.firstChild.data == 'Report this item to Etsy' ) {
                // found it - append the edit and delete links to that cell
                reportRow = link.parentNode;
                while ( reportRow && reportRow.nodeName != 'TR' ) reportRow = reportRow.parentNode;
                // alter the report link to become the "copy item" link
                var copyRow = reportRow.cloneNode( true );
                var copyLink = copyRow.getElementsByTagName( 'a' )[ 0 ];
                copyLink.innerHTML = 'Copy this item';
                copyLink.addEventListener( 'click', stashListing, false ); // only parse the page on click
                copyLink.style.cursor = 'pointer';
                copyLink.removeAttribute( 'href' ); // going to use an onclick handler instead
                var copyImg = copyRow.getElementsByTagName( 'img' )[ 0 ];
                copyImg.src = cc_icon;
                copyImg.alt = 'copy this item';
                copyImg.width = 15;
                copyImg.height = 15;
                // attach to document, under the "report to Etsy" link
                reportRow.parentNode.insertBefore( copyRow, reportRow.nextSibling );
                break;
            }
        }
    }
}

// does the work of parsing the page and extracting the data, then redirects to "create_listing1.php"
// with a "create_from" argument. Argument is ignored by Etsy, but flags the copy to this script.

function stashListing () {
    var match = document.location.search.match( /listing_id=(\d+)/ );
    var listing_id = match[ 1 ];
    var data = {}; // listing data object

    // find the section, if there is one
    var links = document.getElementsByTagName( 'a' );
    for ( var l = 0; l < links.length; l++ ) {
        var link = links[ l ];
        if ( link.href ) {
            var match = link.href.match( /section_id=(\d+)/ );
            if ( match ) {
                // found it - store the section ID
                data.section = match[ 1 ];
                break;
            }
        }
    }

    // item title
    var h1 = document.getElementsByTagName( 'h1' );
    data.title = h1[ 0 ].innerHTML

    // bit of a blunt approach to finding the quantity and price but it copes better with
    // sold item listings this way, which have no price or quantity to store
    var dgt = getElementsByClassName( 'dark_grey_text' );
    for ( var d = 0; d < dgt.length; d++ ) {
        if ( dgt[ d ].innerHTML.match( 'in stock' ) ) {
            // found a quantity
            costTable = dgt[ d ].parentNode;
            while ( costTable && costTable.parentNode.nodeName != 'TABLE' ) costTable = costTable.parentNode;
            if ( costTable ) {
                var tds = costTable.getElementsByTagName( 'td' );
                data.price = tds[ 2 ].innerHTML; // stash the price
                data.qty = tds[ 5 ].innerHTML;
                data.qty = data.qty.substr( 0, data.qty.length - 9 ); // stash the quantity
            }
        }
    }

    // locate the table containing the rest of the listing data
    var listingTable = h1[ 0 ].parentNode;
    while ( listingTable && listingTable.parentNode.nodeName != 'TABLE' ) listingTable = listingTable.parentNode;
    if ( ! listingTable ) return;

    tds = listingTable.getElementsByTagName( 'td' );

    // description
    data.description = toText( tds[ 4 ] );

    // tags and materials
    var trs = tds[ 7 ].getElementsByTagName( 'tr' );
    var tags = trs[ 0 ];
    var materials = trs[ 2 ];

    // extract tags names from the links
    data.tags = [];
    var links = tags.getElementsByTagName( 'a' );
    for ( var l = 0; l < links.length; l++ ) {
        data.tags.push( links[ l ].innerHTML );
    }
    // extract materials from the links
    data.materials = [];
    links = materials.getElementsByTagName( 'a' );
    for ( var l = 0; l < links.length; l++ ) {
        data.materials.push( links[ l ].innerHTML );
    }

    // ships from location
    data.shipsFrom = tds[ 22 ].innerHTML.substring( 17 );

    // shipping profile data
    var shippingTd = tds[ 25 ].getElementsByTagName( 'td' );
    data.shipping = [];
    for ( var t = 0; t < shippingTd.length; t += 2 ) {
        // ships to
        var to = shippingTd[ t ].innerHTML;
        to = to.substr( 2, to.length - 3 ); // strip the extraneous ornamentation
        // extract the costs
        var costs = shippingTd[ t + 1 ].innerHTML.match( /\$\d+\.\d{2}/g );
        for ( var c = 0; c < costs.length; c++ ) {
            costs[ c ] = costs[ c ].substr( 1 );
        }
        data.shipping.push( { location: to, costs: costs } ); // stash the shipping data
    }

    // stash the data
    GM_setValue( listing_id, uneval( data ) );

    // redirect to create_listing1 with a "create_from" argument
    document.location.href = 'http://www.etsy.com/create_listing1.php?create_from=' + listing_id;
}

// description is text and <br /> tags - store the text and replace the breaks with newlines
// made it recursive to cope with changes made by add-ons like Linkification
function toText ( node ) {
    var nodes = node.childNodes;
    var description = "";
    for ( var c = 0; c < nodes.length; c++ ) {
        if ( nodes[ c ].nodeType == 3 ) {
            description += nodes[ c ].data;
        } else if ( nodes[ c ].nodeType == 1 ) {
            if ( nodes[ c ].nodeName == 'BR' ) {
                description += '\n';
            } else {
                description += toText( nodes[ c ] );
            }
        }
    }
    return description;
}

// add values to the first page of the listing
function create_listing1( from ) {
    // find the right form
    var form;
    for ( var f = 0; f < document.forms.length; f++ ) {
        if ( document.forms[ f ].action.indexOf( 'create_listing' ) > -1 ) {
            form = document.forms[ f ];
            break;
        }
    }
    if ( ! form ) return;

    // get the data
    var data = GM_getValue( from );
    if ( ! data ) return;
    data = eval( data );

    // set the values
    if ( data.title ) {
        form.elements.namedItem( 'title' ).value = data.title;
    }        
    if ( data.description ) {
        form.elements.namedItem( 'description' ).value = data.description;
    }        
    if ( data.materials ) {
        form.elements.namedItem( 'materials' ).value = data.materials.join( ", " );
    }        
}

// add the tags on the second page of the listing process
function create_listing2( listing_id, from ) {
    if ( ! from ) return;

    // get the data
    var data = GM_getValue( from );
    if ( ! data ) return;
    data = eval( data );

    var tags = data.tags;
    var tagStr = tags.join( '.' );
    tagStr = tagStr.replace( / /g, '_' );
    document.getElementById( 'tagInput' ).value = tagStr;
    var topTag = tags.shift(); // first tag is handled differently
    topTag = topTag.replace( / /g, '_' );

    var tagLand = document.getElementById( 'tagLand' );
    if ( ! tagLand ) return;

    // deal with the first tag
    var select = tagLand.getElementsByTagName( 'select' )[ 0 ];
    for ( var c = 0; c < select.length; c++ ) {
        if ( select.options[ c ].value == topTag ) {
            // found it - select it and then call the "mainCategorySelector()" function in the main document
            select.selectedIndex = c;
            break;
        }
    }
}

// adds price, quantity, section, and shipping info
function create_listing3( listing_id, from ) {
    if ( ! from ) return;

    // get the data
    var data = GM_getValue( from );
    if ( ! data ) return;
    data = eval( data );

    // find the form
    var form;
    for ( var f = 0; f < document.forms.length; f++ ) {
        if ( document.forms[ f ].action.indexOf( 'create_listing' ) > -1 ) {
            form = document.forms[ f ];
            break;
        }
    }
    if ( ! form ) return;

    if ( data.price ) {
        form.elements.namedItem( 'price' ).value = data.price;
    }        
    if ( data.quantity ) {
        form.elements.namedItem( 'quantity' ).value = data.quantity;
    }

    if ( data.section ) {
        // we have a section - find it in the drop-down
        var select = form.elements.namedItem( 'section_pick' );
        for ( var o = 0; o < select.options.length; o++ ) {
            if ( select.options[ o ].value == data.section ) {
                select.selectedIndex = o;
                break;
            }
        }
    }
    if ( data.shipsFrom ) {
        // should always have a "ships from"
        // find it and select the value in the drop-down
        var select = form.elements.namedItem( 'ship_from_country' );
        for ( var o = 0; o < select.options.length; o++ ) {
            if ( select.options[ o ].innerHTML == data.shipsFrom ) {
                select.selectedIndex = o;
                break;
            }
        }
    }
    if ( data.shipping ) {
        // should always have *some* shipping data
        for ( var s = 0; s < data.shipping.length; s++ ) {
            var record = data.shipping[ s ];
            if ( s > 0 ) {
                // call the addDestination() function in the parent window to add an additional destination
                unsafeWindow.addDestination();
            }

            // all the shipping input elements are named the same, so we need to find the last one
            var select;
            for ( var e = form.elements.length - 1; e >= 0; e-- ) {
                if ( form.elements[ e ].name && form.elements[ e ].name == 'destination_country[]' ) {
                    select = form.elements[ e ];
                    break;
                }
            }
            // then find the country and select it in the list
            for ( var o = 0; o < select.options.length; o++ ) {
                if ( select.options[ o ].innerHTML == record.location ) {
                    select.selectedIndex = o;
                    break;
                }
            }

            // same rigamarole for finding the primary shipping price
            var primary;
            for ( var e = form.elements.length - 1; e >= 0; e-- ) {
                if ( form.elements[ e ].name && form.elements[ e ].name == 'destination_primary_shipping[]' ) {
                    primary = form.elements[ e ];
                    break;
                }
            }
            primary.value = record.costs[ 0 ];

            // and the secondary
            var secondary;
            for ( var e = form.elements.length - 1; e >= 0; e-- ) {
                if ( form.elements[ e ].name && form.elements[ e ].name == 'destination_secondary_shipping[]' ) {
                    secondary = form.elements[ e ];
                    break;
                }
            }
            // defaults to the same as the primary
            secondary.value = ( record.costs[ 1 ] == null ) ? record.costs[ 0 ] : record.costs[ 1 ];
        }
    }
}

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





