// annotator.js
//
// Copyright © 2008 by Jef Poskanzer <jef@mail.acme.com>.
// All rights reserved.


var imageUrl = document.getElementById( 'imageUrl' );
var notesUrlContainer = document.getElementById( 'notesUrlContainer' );
var notesUrl = document.getElementById( 'notesUrl' );
var tinyUrlContainer = document.getElementById( 'tinyUrlContainer' );
var tinyUrl = document.getElementById( 'tinyUrl' );
var buttonContainer = document.getElementById( 'buttonContainer' );
var imageContainer = document.getElementById( 'imageContainer' );
var image = document.getElementById( 'image' );
var instructionsContainer = document.getElementById( 'instructionsContainer' );

var notes = [];
var nNotes = 0;
var newNote = null;
var editingNote = null;


function Setup()
    {
    acme.Initialize();

    image.onmouseover = ImageMouseOverHandler;
    image.onmouseout = ImageMouseOutHandler;

    ImageUrlChanged();

    var params = GetParameters();
    for ( var n in params )
	{
	var v = params[n];
	if ( n == 'u' )
	    {
	    imageUrl.value = v;
	    ImageUrlChanged();
	    }
	else if ( n.charAt(0) == 'n' )
	    {
	    var items = v.split( '|' );
	    if ( items.length >= 5 )
		{
		var note = { x: parseInt( items[0] ), y: parseInt( items[1] ), w: parseInt( items[2] ), h: parseInt( items[3] ), t: items[4] };
		for ( i = 5; i < items.length; ++i )
		    note.t += '|' + items[i];
		notes.push( note );
		++nNotes;
		}
	    }
	}
    NotesChanged();
    }


function ImageUrlChanged()
    {
    if ( imageUrl.value == '' )
	{
	notesUrlContainer.style.display = 'none';
	tinyUrlContainer.style.display = 'none';
	buttonContainer.style.display = 'none';
	image.style.display = 'none';
	}
    else
	{
	notesUrlContainer.style.display = '';
	tinyUrlContainer.style.display = '';
	buttonContainer.style.display = '';
	image.src = imageUrl.value;
	image.style.display = '';
	}
    UnDisplayNotes();
    notes.length = 0;
    nNotes = 0;
    NotesChanged();
    CancelNewNote();
    }


function NotesChanged()
    {
    var url = window.location.protocol + '//' + window.location.hostname + window.location.pathname + '?u=' + encodeURIComponent( imageUrl.value );
    for ( var n in notes )
	if ( notes[n] != null )
	    url += '&n' + n + '=' + notes[n].x + '|' + notes[n].y + '|' + notes[n].w + '|' + notes[n].h + '|' + encodeURIComponent( notes[n].t );
    notesUrl.value = url;
    tinyUrl.value = '';
    HttpGet( '/resources/create_tinyurl.cgi?url=' + encodeURIComponent( url ), TinyUrlCallback );

    if ( nNotes == 0 )
	instructionsContainer.style.display = 'none';
    else
	instructionsContainer.style.display = '';
    }


function TinyUrlCallback( request )
    {
    tinyUrl.value = request.responseText;
    }


function EditNote( n )
    {
    if ( newNote != null )
	return;		// only one new note at a time

    editingNote = n;
    if ( editingNote == null )
	newNote = { x: 20, y: 20, w: 50, h: 50, t: 'Add your note here.' };
    else
	{
	newNote = { x: notes[editingNote].x, y: notes[editingNote].y, w: notes[editingNote].w, h: notes[editingNote].h, t: notes[editingNote].t };
	if ( notes[editingNote].div != null )
	    notes[editingNote].div.style.display = 'none';
	}

    var w = image.clientWidth;
    var h = image.clientHeight;
    if ( newNote.w > w )
	{
	newNote.x = 0;
	newNote.w = w;
	}
    else if ( newNote.x + newNote.w > w )
	newNote.x = w - newNote.w;
    if ( newNote.h > h )
	{
	newNote.y = 0;
	newNote.h = h;
	}
    else if ( newNote.y + newNote.h > h )
	newNote.y = h - newNote.h;

    newNote.div = AppendElement( imageContainer, 'div', { style: { position: 'absolute', backgroundColor: 'transparent', zIndex: '1000' } } );

    newNote.box = AppendElement( newNote.div, 'div', { style: { position: 'relative', backgroundImage: 'url(/resources/images/transpixel.gif)', cursor: 'move', marginBottom: '5px' }, onmousedown: MakeEventCaller( DragStart, 'center' ) } );
    newNote.border1 = AppendElement( newNote.box, 'div', { style: { position: 'absolute', left: '0px', top: '0px', border: '1px dashed #000000', backgroundColor: 'transparent' } } );
    newNote.border2 = AppendElement( newNote.border1, 'div', { style: { position: 'absolute', left: '0px', top: '0px', border: '1px dashed #ffffff', backgroundColor: 'transparent' } } );

    // Using a plain old div or span for the grabs doesn't work right
    // on MSIE.  Using a little table does work.  Shrug.
    var nwGrabTable = AppendElement( newNote.box, 'table', { cellSpacing: '0', cellPadding: '0', style: { position: 'absolute', left: '0px', top: '0px', border: '1px solid #000000', backgroundColor: '#ffffff', cursor: 'nw-resize' }, onmousedown: MakeEventCaller( DragStart, 'nw' ) } );
    var nwGrabTbody = AppendElement( nwGrabTable, 'tbody', { } );
    var nwGrabTr = AppendElement( nwGrabTbody, 'tr', { } );
    var nwGrabTd = AppendElement( nwGrabTr, 'td', { style: { width: '6px', height: '6px' } } );
    var neGrabTable = AppendElement( newNote.box, 'table', { cellSpacing: '0', cellPadding: '0', style: { position: 'absolute', right: '0px', top: '0px', border: '1px solid #000000', backgroundColor: '#ffffff', cursor: 'ne-resize' }, onmousedown: MakeEventCaller( DragStart, 'ne' ) } );
    var neGrabTbody = AppendElement( neGrabTable, 'tbody', { } );
    var neGrabTr = AppendElement( neGrabTbody, 'tr', { } );
    var neGrabTd = AppendElement( neGrabTr, 'td', { style: { width: '6px', height: '6px' } } );
    var swGrabTable = AppendElement( newNote.box, 'table', { cellSpacing: '0', cellPadding: '0', style: { position: 'absolute', left: '0px', bottom: '0px', border: '1px solid #000000', backgroundColor: '#ffffff', cursor: 'sw-resize' }, onmousedown: MakeEventCaller( DragStart, 'sw' ) } );
    var swGrabTbody = AppendElement( swGrabTable, 'tbody', { } );
    var swGrabTr = AppendElement( swGrabTbody, 'tr', { } );
    var swGrabTd = AppendElement( swGrabTr, 'td', { style: { width: '6px', height: '6px' } } );
    var seGrabTable = AppendElement( newNote.box, 'table', { cellSpacing: '0', cellPadding: '0', style: { position: 'absolute', right: '0px', bottom: '0px', border: '1px solid #000000', backgroundColor: '#ffffff', cursor: 'se-resize' }, onmousedown: MakeEventCaller( DragStart, 'se' ) } );
    var seGrabTbody = AppendElement( seGrabTable, 'tbody', { } );
    var seGrabTr = AppendElement( seGrabTbody, 'tr', { } );
    var seGrabTd = AppendElement( seGrabTr, 'td', { style: { width: '6px', height: '6px' } } );

    UpdateNewNote();

    newNote.text = AppendElement( newNote.div, 'textarea', { className: 'noteText', style: { position: 'relative', width: '200px', height: '5em', borderWidth: '0px', marginBottom: '5px' } } );
    newNote.text.value = newNote.t;
    newNote.text.select();

    var br = AppendElement( newNote.div, 'br', { style: { position: 'relative' } } );

    AppendElement( newNote.div, 'input', { type: 'button', value: 'Save', onclick: SaveNewNote, className: 'mainButton', style: { position: 'relative', marginRight: '5px' } } );
    AppendElement( newNote.div, 'input', { type: 'button', value: 'Cancel', onclick: CancelNewNote, className: 'otherButton', style: { position: 'relative', marginRight: '5px' } } );
    if ( editingNote != null )
	AppendElement( newNote.div, 'input', { type: 'button', value: 'Delete!', onclick: DeleteNewNote, className: 'otherButton', style: { position: 'relative' } } );
    }


function UpdateNewNote()
    {
    newNote.div.style.left = newNote.x + 'px';
    newNote.div.style.top = newNote.y + 'px';
    newNote.box.style.width = newNote.w + 'px';
    newNote.box.style.height = newNote.h + 'px';
    newNote.border1.style.width = ( newNote.w - 2 ) + 'px';
    newNote.border1.style.height = ( newNote.h - 2 ) + 'px';
    newNote.border2.style.width = ( newNote.w - 4 ) + 'px';
    newNote.border2.style.height = ( newNote.h - 4 ) + 'px';
    }


var dragging = false;
var dragPrevX, dragPrevY;

function DragStart( e, which )
    {
    e = GetEvent( e );
    e.cancelBubble = true;
    //Log( 'DragStart ' + which );
    dragPrevX = null;
    dragPrevY = null;
    var cursor;
    switch ( which )
	{
	case 'center': cursor = 'move'; break;
	case 'nw': cursor = 'nw-resize'; break;
	case 'ne': cursor = 'ne-resize'; break;
	case 'sw': cursor = 'sw-resize'; break;
	case 'se': cursor = 'se-resize'; break;
	}
    document.body.style.cursor = cursor;
    document.body.onmousemove = MakeEventCaller( Drag, which );
    document.body.onmouseup = DragStop;
    dragging = true;
    }

function DragStop()
    {
    //Log( 'DragStop' );
    dragging = false;
    document.body.style.cursor = '';
    document.body.onmousemove = null;
    document.body.onmouseup = null;
    }

function Drag( e, which )
    {
    e = GetEvent( e );
    var x = e.clientX;
    var y = e.clientY;
    if ( dragPrevX != null && dragPrevY != null )
	{
	var dx = x - dragPrevX;
	var dy = y - dragPrevY;
	var nx = newNote.x;
	var ny = newNote.y;
	var nw = newNote.w;
	var nh = newNote.h;
	switch ( which )
	    {
	    case 'center':
	    nx += dx;
	    ny += dy;
	    break;
	    case 'nw':
	    nx += dx;
	    nw -= dx;
	    ny += dy;
	    nh -= dy;
	    break;
	    case 'ne':
	    nw += dx;
	    ny += dy;
	    nh -= dy;
	    break;
	    case 'sw':
	    nx += dx;
	    nw -= dx;
	    nh += dy;
	    break;
	    case 'se':
	    nw += dx;
	    nh += dy;
	    break;
	    }
	if ( nx >= 0 && ny >= 0 &&
	     nx + nw < image.clientWidth && ny + nh < image.clientHeight )
	    {
	    newNote.x = nx;
	    newNote.y = ny;
	    newNote.w = nw;
	    newNote.h = nh;
	    UpdateNewNote();
	    }
	}
    dragPrevX = x;
    dragPrevY = y;
    }


function SaveNewNote()
    {
    if ( newNote == null )
	return;
    newNote.t = newNote.text.value;
    newNote.text = null;
    DestroyElement( newNote.div );
    newNote.div = null;
    newNote.box = null;
    newNote.border1 = null;
    newNote.border2 = null;
    newNote.text = null;
    if ( editingNote == null )
	{
	notes.push( newNote );
	++nNotes;
	}
    else
	{
	notes[editingNote].x = newNote.x;
	notes[editingNote].y = newNote.y;
	notes[editingNote].w = newNote.w;
	notes[editingNote].h = newNote.h;
	notes[editingNote].t = newNote.t;
	editingNote = null;
	}
    newNote = null;
    NotesChanged();
    DisplayNotes();
    }


function CancelNewNote()
    {
    if ( newNote == null )
	return;
    if ( newNote.div != null )
	DestroyElement( newNote.div );
    newNote = null;
    editingNote = null;
    DisplayNotes();
    }

function DeleteNewNote()
    {
    if ( newNote == null )
	return;
    if ( editingNote == null )
	return;
    if ( notes[editingNote].div != null )
	DestroyElement( notes[editingNote].div );
    notes[editingNote] = null;
    --nNotes;
    if ( nNotes == 0 )
	notes = [];
    NotesChanged();
    CancelNewNote();
    }


function ImageMouseOverHandler( e )
    {
    if ( dragging )
	return;
    e = GetEvent( e );
    var from = e.relatedTarget || e.fromElement;
    var to = window.event ? e.srcElement : e.target;
    //Log( 'ImageMouseOverHandler  ' + from.nodeName + ' -> ' + to.nodeName );
    if ( to == image && ( from == null || ! isAncestorOf( imageContainer, from ) ) )
	DisplayNotes();
    }


function ImageMouseOutHandler( e )
    {
    if ( dragging )
	return;
    e = GetEvent( e );
    var from = window.event ? e.srcElement : e.target;
    var to = e.relatedTarget || e.toElement;
    //Log( 'ImageMouseOutHandler  ' + from.nodeName + ' -> ' + to.nodeName );
    if ( from == image && ( to == null || ! isAncestorOf( imageContainer, to ) ) )
	UnDisplayNotes();
    }


function DisplayNotes()
    {
    for ( var n in notes )
	if ( notes[n] != null )
	    {
	    if ( notes[n].div == null )
		{
		notes[n].div = AppendElement( imageContainer, 'div', { style: { position: 'absolute', backgroundColor: 'transparent' } } );
		notes[n].box = AppendElement( notes[n].div, 'div', { style: { position: 'relative', margin: '1px', backgroundImage: 'url(/resources/image/transpixel.gif)' } } );
		notes[n].box.onmouseover = MakeEventCaller( NoteMouseOverHandler, n );
		notes[n].box.onmouseout = MakeEventCaller( NoteMouseOutHandler, n );
		notes[n].box.onclick = MakeEventCaller( NoteClickHandler, n );
		notes[n].border1 = AppendElement( notes[n].box, 'div', { style: { position: 'absolute', border: '1px solid #000000', backgroundColor: 'transparent' } } );
		notes[n].border2 = AppendElement( notes[n].border1, 'div', { style: { position: 'absolute', border: '1px solid #ffffff', backgroundColor: 'transparent' } } );
		notes[n].text = AppendElement( notes[n].div, 'div', { className: 'noteText', style: { position: 'relative', borderWidth: '0px', zIndex: '1000', display: 'none' } } );
		}
	    if ( n == editingNote )
		{
		notes[n].div.style.display = 'none';
		continue;
		}
	    notes[n].div.style.display = '';
	    notes[n].div.style.left = ( notes[n].x - 1 ) + 'px';
	    notes[n].div.style.top = ( notes[n].y - 1 ) + 'px';
	    notes[n].box.style.width = notes[n].w + 'px';
	    notes[n].box.style.height = notes[n].h + 'px';
	    notes[n].border1.style.width = ( notes[n].w - 2 ) + 'px';
	    notes[n].border1.style.height = ( notes[n].h - 2 ) + 'px';
	    notes[n].border2.style.width = ( notes[n].w - 4 ) + 'px';
	    notes[n].border2.style.height = ( notes[n].h - 4 ) + 'px';

	    var lines = notes[n].t.split( '\n' );
	    //var maxLen = ArrayReduce( Math.max, ArrayMap( function ( s ) { return s.length; }, lines ) );
	    //maxLen = Math.min( maxLen, 30 );
	    //notes[n].text.style.width = Math.round( maxLen * 1.2 ) + 'ex';
	    notes[n].text.innerHTML = Join( ArrayMap( QuoteHtml, lines ), '<br>' );
	    }
    }


function UnDisplayNotes()
    {
    for ( var n in notes )
	if ( notes[n] != null )
	    {
	    if ( notes[n].div != null )
		{
		DestroyElement( notes[n].div );
		notes[n].div = null;
		notes[n].box = null;
		notes[n].border1 = null;
		notes[n].border2 = null;
		notes[n].text = null;
		}
	    }
    }


function NoteMouseOverHandler( e, n )
    {
    if ( dragging )
	return;
    e = GetEvent( e );
    var from = e.relatedTarget || e.fromElement;
    var to = window.event ? e.srcElement : e.target;
    //Log( 'NoteMouseOverHandler  ' + from.nodeName + ' -> ' + to.nodeName );
    if ( ! isAncestorOf( notes[n].box, from ) && isAncestorOf( notes[n].box, to ) )
	ActivateNote( n );
    }


function NoteMouseOutHandler( e, n )
    {
    if ( dragging )
	return;
    e = GetEvent( e );
    var from = window.event ? e.srcElement : e.target;
    var to = e.relatedTarget || e.toElement;
    //Log( 'NoteMouseOutHandler  ' + from.nodeName + ' -> ' + to.nodeName );
    if ( isAncestorOf( notes[n].box, from ) && ! isAncestorOf( notes[n].box, to ) )
	DeActivateNote( n );
    }


function NoteClickHandler( e, n )
    {
    if ( dragging )
	return;
    e = GetEvent( e );
    DeActivateNote( n );
    EditNote( n );
    }


function ActivateNote( n )
    {
    notes[n].box.style.margin = '0px';
    notes[n].box.style.border = '1px solid #d4d82d';
    notes[n].box.style.marginBottom = '5px';
    notes[n].text.style.display = '';
    }


function DeActivateNote( n )
    {
    notes[n].box.style.borderWidth = '0px';
    notes[n].box.style.margin = '1px';
    notes[n].text.style.display = 'none';
    }


function isAncestorOf( ancestorNode, childNode )
    {
    var node = childNode.parentNode;
    while ( node != null )
	{
	if ( node == ancestorNode )
	    return true;
	node = node.parentNode;
	}
    return false;
    }
