Add Digg Control

By Daniel Einspanjer Last update Jul 19, 2007 — Installed 1,624 times. Daily Installs: 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
/* add digg control
 * version 0.9
 * Copyright (c) 2007, Daniel Einspanjer
 * License: Creative Commons "Attribution-ShareAlike 2.0"
 * http://creativecommons.org/licenses/by-sa/2.0/
 *
 * A significant portion of this code (the layout of the widget
 * and the Drag class) were copied from a nice looking greasemonkey script
 * by the name of 'book-burro' by Johan Sundström and others. Thanks guys!
 * http://userscripts.org/scripts/show/1859
 */

// ==UserScript==
// @name           Add Digg Control
// @namespace      http://www.yipyip.com/firefox/index.html
// @description    Attempts to add a digg this control to any article accessed from digg.com
// @identifier     http://userscripts.org/scripts/source/7883
// @author         Daniel Einspanjer
// @version        0.9
// @source         http://userscripts.org/scripts/source/7883.user.js
// @include        http://*digg.com/*
// @include        http://duggmirror.com/*
// @include        *#digg_link=*
// ==/UserScript==

// Version History
// 0.9 -- More digg API compatibility changes
// 0.8 -- Added the newly required pagetype variable for the dig() function. Removed unnecessary scoping block. Changed credits for Drag code
// 0.7.3 -- If UserScriptUpdates is installed, use it.
// 0.7.2 -- Heh. The update version checking didn't like 0.7a so I'm changing it to 0.7.2
// 0.7a -- Prevent Drag "sticking"; Drag function object didn't work inside anonymous namespace.
// 0.7 -- Re-arrange code; Add automatic update checking; Tweak XPath expressions to support more digg pages.
// 0.6 -- Tweak close button on widget to have a pointer cursor and to remove hash from location
// 0.5 -- Refactor functions and add code to enable ADC on duggmirror.
// 0.4 -- changed dig function to match changes from digg.com and added inclusion of js files needed by diggthis.php
// 0.3 -- update to xpath of new dig layout
// 0.2 -- changed digg it link from wrapper_full() to dig() funny that they change
//        that method just a couple of days after I start using it. :(

// ========================================================================
// set up our Drag function object for the widget
var Drag = function(){ this.init.apply( this, arguments ); };

Drag.fixE = function( e )
{
    if( typeof e == 'undefined' ) e = window.event;
    if( typeof e.layerX == 'undefined' ) e.layerX = e.offsetX;
    if( typeof e.layerY == 'undefined' ) e.layerY = e.offsetY;
    return e;
};

Drag.prototype.init = function( handle, dragdiv )
{
    this.div = dragdiv || handle;
    this.handle = handle;
    if( isNaN(parseInt(this.div.style.right )) ) this.div.style.right  = '0px';
    if( isNaN(parseInt(this.div.style.bottom)) ) this.div.style.bottom = '0px';
    this.onDragStart = function(){};
    this.onDragEnd = function(){};
    this.onDrag = function(){};
    this.onClick = function(){};
    this.mouseDown = addEventHandler( this.handle, 'mousedown', this.start, this );
};

Drag.prototype.start = function( e )
{
    this.unregister();
    // this.mouseUp = addEventHandler( this.handle, 'mouseup', this.end, this );
    e = Drag.fixE( e );
    this.started = new Date();
    var y = this.startY = parseInt(this.div.style.bottom);
    var x = this.startX = parseInt(this.div.style.right);
    this.onDragStart( x, y );
    this.lastMouseX = e.clientX;
    this.lastMouseY = e.clientY;
    this.documentMove = addEventHandler( document, 'mousemove', this.drag, this );
    this.documentStop = addEventHandler( document, 'mouseup', this.end, this );
    if( e.preventDefault ) e.preventDefault();
    return false;
};

Drag.prototype.drag = function( e )
{
    e = Drag.fixE( e );
    var ey = e.clientY;
    var ex = e.clientX;
    var y = parseInt(this.div.style.bottom);
    var x = parseInt(this.div.style.right );
    var nx = x - ex + this.lastMouseX;
    var ny = y - ey + this.lastMouseY;
    this.div.style.right	= nx + 'px';
    this.div.style.bottom	= ny + 'px';
    this.lastMouseX	= ex;
    this.lastMouseY	= ey;
    this.onDrag( nx, ny );
    if( e.preventDefault ) e.preventDefault();
    return false;
};

Drag.prototype.unregister = function()
{
    removeEventHandler( document, 'mousemove', this.documentMove );
    removeEventHandler( document, 'mouseup', this.documentStop );
}
Drag.prototype.end = function()
{
    this.unregister();
    var time = (new Date()) - this.started;
    var x = parseInt(this.div.style.right),  dx = this.startX - x;
    var y = parseInt(this.div.style.bottom), dy = this.startY - y;
    this.onDragEnd( x, y, dx, dy, time );
    if( (dx*dx + dy*dy) < (4*4) && time < 1e3 )
        this.onClick( x, y, dx, dy, time );
};

function removeEventHandler( target, eventName, eventHandler )
{
    if( target.addEventListener )
        target.removeEventListener( eventName, eventHandler, true );
    else if( target.attachEvent )
        target.detachEvent( 'on' + eventName, eventHandler );
}

function addEventHandler( target, eventName, eventHandler, scope )
{
    var f = scope ? function(){ eventHandler.apply( scope, arguments ); } : eventHandler;
    if( target.addEventListener )
        target.addEventListener( eventName, f, true );
    else if( target.attachEvent )
        target.attachEvent( 'on' + eventName, f );
    return f;
}
// end Drag code
// ========================================================================

/* First, we have to determine what type of page we are looking at.
 * ADC has four targets:
 *  1. Any page that has a hash beginning with #digg_link=
 *  2. Duggmirror pages
 *  3. The diggthis.php API page (to tweak the links)
 *  4. Digg pages that have article links
 */
var hashPart = window.location.hash.match(/#digg_link=(.*)/);

//  1. Any page that has a hash beginning with #digg_link=
if (hashPart)
{
    injectADC(hashPart[1]);
}
//  2. Duggmirror pages
else if (window.location.hostname == 'duggmirror.com')
{
    var articleURL = 'http://digg.com' + window.location.pathname
    injectADC(articleURL);
}
//  3. The diggthis.php API page (to tweak the links)
else if (window.location.pathname == '/tools/diggthis.php' && window.location.hash == '#adc_diggthis_init')
{
    /* This is where we fixup the links in the diggthis frame.
     * I do not want links in the widget blowing away the current
     * page so I change them to target _blank instead.
     * The special case is the diggIt link itself. I need it
     * to use the digg ajax method dig() instead of
     * the form submission it uses normally.
     * Note this also requires that I include the javascript files
     * that digg.com no longer sources in diggthis.php
     * I toyed with the idea of using an XHR to get the path to the
     * script files, but decided against that because I have
     * to filter the list to prevent init errors and because
     * I didn't think an additional request per ADC is worth it.
     * Unfortunately, this means if they change the requirements,
     * the ADC extension will break again. :(
     */

    // Turn off the init flag so this block only gets run once
    // (the script loads trigger GM)
    window.location.hash = '#adc_diggthis';

    document.getElementById('f1').target = '_blank';

    with( document.getElementById('diggs1') )
    {
        target = '_blank';
        title = 'Click to open the digg article for this page';
    }

    var diggItLink = document.getElementById('diglink1').firstChild;

    var newElement = document.createElement('a')
    newElement.href = "javascript:dig(1,document.forms['f1'].id.value,document.forms['f1'].digcheck.value)";
    newElement.appendChild(diggItLink.firstChild);
    diggItLink.parentNode.replaceChild(newElement, diggItLink);

    var documentHead = document.getElementsByTagName('head')[0];

    var newScriptElement = document.createElement('script');
    newScriptElement.setAttribute('type', 'text/javascript');
    newScriptElement.appendChild( document.createTextNode( "pagetype = 'permalink';" ) );
    documentHead.appendChild(newScriptElement);

    var diggScriptPath = '/js/';
    for each (var diggScriptName in ['digg.js', 'prototype.js', 'effects.js', 'jquery.js'])
    {
        var newScriptElement = document.createElement('script');
        newScriptElement.setAttribute('type', 'text/javascript');
        newScriptElement.setAttribute('src', diggScriptPath+diggScriptName);
        documentHead.appendChild(newScriptElement);
    }
}
//  4. Digg pages that have article links
else
{
    /* This xpath will only match summary pages.
     * We look for a news-body div inside a news-summary div and if we find it, we adjust the article link to have our special hash appended.
     * NOTE: If the page does any redirection, our hash will dissapear and the whole ADC will break. :(
     */
    var result = document.evaluate(
                    "/html/body/div[@id='container']/div[@id='contents']/div[@id='wrapper']/div[contains(concat(' ',@class,' '),' main ')]/div[contains(concat(' ',@class,' '),' news-summary ')]/div[contains(concat(' ',@class,' '),' news-body ')]",
                    document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

    if (result.snapshotLength > 0)
    {
        for (var i = 0; i < result.snapshotLength; i++) 
        {
            var newsBodyNode = result.snapshotItem(i);

            var articleLink = document.evaluate("h3/a",
                    newsBodyNode, null, XPathResult.FIRST_ORDERED_NODE_TYPE,
                    null).singleNodeValue;

            // Note that the digg link is absolute without the domain name. (e.g. /tech/foo)
            var diggURL = 'http://digg.com' +
                document.evaluate(
                        "div[contains(concat(' ',@class,' '),' news-details ')]/a[contains(concat(' ',@class,' '),' tool ')][contains(concat(' ',@class,' '),' comments ')]/@href",
                        newsBodyNode, null, XPathResult.STRING_TYPE, null
                        ).stringValue;

            articleLink.href += '#digg_link=' + encodeURIComponent(diggURL);
        }
    }
}

var SCRIPT = {
		name: "Add Digg Control",
		namespace: "http://www.yipyip.com",
		source: "http://userscripts.org/scripts/show/7883.user.js",			// script homepage
		identifier: "http://userscripts.org/scripts/source/7883",
		version: "0.9",								            // version
		date: (new Date(2007, 7 - 1, 19)).valueOf()            		// update date
};
try {
    var win = unsafeWindow || window.wrappedJSObject || window;
	window.addEventListener("load", function () {
		try {
			win.UserScriptUpdates.requestAutomaticUpdates(SCRIPT);
		} catch (ex) {
            // If UserScriptUpdates isn't installed, try our own version.
            try {
                if (!GM_getValue) return;
                // avoid a flood of dialogs e.g. when opening a browser with multiple tabs set to homepage and a script with * includes
                var DoS_PREVENTION_TIME = 2 * 60 * 1000;
                var isSomeoneChecking = GM_getValue('CHECKING', null);
                var now = new Date().getTime();
                GM_setValue('CHECKING', now.toString());
                if (isSomeoneChecking && (now - isSomeoneChecking) < DoS_PREVENTION_TIME) return;
                var lastChecked = GM_getValue('LAST_CHECKED', null);

                var TWO_DAYS = 2 * 24 * 60 * 60 * 1000;
                if (lastChecked && (now - lastChecked) < TWO_DAYS) return;
                GM_xmlhttpRequest({
                method: 'GET',
                url: SCRIPT.identifier,
                onload: function(result) {
                    if (!result.responseText.match(/@version\s+([\d.]+)/)) return;
                    var theOtherVersion = RegExp.$1;
                    if (theOtherVersion == SCRIPT.version) return;
                    if (window.confirm('A new version ' + theOtherVersion + ' of greasemonkey script "' + SCRIPT.name + '" is available.\nYour installed version is ' + SCRIPT.version + ' .\n\nUpdate now?\n')) {
                    GM_openInTab(SCRIPT.source);
                    }
                }
                });
                GM_setValue('LAST_CHECKED', now.toString());
            } catch (ex) {}
        }
	}, false);
} catch (ex) {}

function injectADC(articleURL)
{
    /*   ____body______________________
     *  |                              |
     *  |  ______root______________    | 
     *  | |                        |   |
     *  | | ____handle____________ |   | 
     *  | ||                      ||   |
     *  | ||title    about  close ||   | 
     *  | ||______________________||   | 
     *  | |                        |   |
     *  | | ____box_______________ |   | 
     *  | ||                      ||   | 
     *  | || _____iframe_________ ||   | 
     *  | |||                    |||   | 
     *  | |||    diggthis        |||   | 
     *  | |||____________________|||   | 
     *  | ||______________________||   | 
     *  | |________________________|   | 
     *  |______________________________| 
     */
    var root = document.createElement( 'div' );
    with( root.style )
    {
        position = 'fixed';
        top = left = '5px';
    }

    var box = document.createElement( 'div' );
    with( box.style )
    {
        position = 'relative';
        zIndex = '1000';

        backgroundColor = '#FFF299';
        color = 'black';
        border = '1px solid brown';
        padding = '0px';
        textAlign = 'left';
        font = '6pt sans-serif';
        width = '60px';

        opacity = '0.63';
        filter = 'alpha(opacity=60)';
    }

    var handle = document.createElement( 'div' );
    handle.title = 'Click title to drag';
    handle.style.padding = '5px';

    var title_span = document.createElement( 'b' );
    title_span.title = 'Add Digg Control widget';
    title_span.appendChild( document.createTextNode( 'ADC' ) );

    var about = document.createElement( 'a' );
    with( about )
    {
        appendChild( document.createTextNode( '?' ) );
        title = 'by Daniel Einspanjer (with code from book-burro)';
        target = '_blank';
        href = 'http://www.yipyip.com/firefox/index.html';
        with( style )
        {
            position = 'absolute';
            right = '15px';
            top = '2px';
            margin = '2px';
            width = '9px';
            height = '9px';
            border = '1px solid brown';
            lineHeight = '8px';
            textAlign = 'center';
            verticalAlign = 'middle';
            textDecoration = 'none';
        }
    }

    var close = document.createElement( 'span' );
    with( close )
    {
        title = 'Click to remove from this page instance';
        appendChild( document.createTextNode( 'X' ) );
        with( style )
        {
            position = 'absolute';
            right = '2px';
            top = '2px';
            margin = '2px';
            width = '9px';
            height = '9px';
            border = '1px solid brown';
            lineHeight = '8px';
            textAlign = 'center';
            cursor = 'pointer';
        }
    }
    addEventHandler( close, 'click', function(){ document.body.removeChild( root ); window.location.hash = '';} );

    var diggControlIframe = document.createElement('iframe')
    with( diggControlIframe )
    {
        id = 'diggControlIframe';
        src = 'http://digg.com/tools/diggthis.php?u='+articleURL+'#adc_diggthis_init';
        height = '82';
        width = '51';
        scrolling = 'no';
        with( style )
        {
            display = 'block';
            border = 'none';
            margin = '0 auto';
        }
    }


    handle.appendChild( title_span );
    handle.appendChild( about );
    handle.appendChild( close );
    box.appendChild( handle );
    box.appendChild( diggControlIframe );
    root.appendChild( box );
    document.body.appendChild( root );

    handle.drag = new Drag( handle, box );

}