/**
* Template based page editing
*  
* Copyright 2009 Andy Bennett
* 
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* 
* 	http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 
* @copyright Andy Bennett <andy@the-ocean.co.uk>
* @package TemplateEditor
* @author Andy Bennett
* @version 1.0.0
*/

var editor;

// this is an extension of the scriptaculous Ajax.InPlaceEditor (from controls.js)
var DomInPlaceEditor = Class.create();
Object.extend( DomInPlaceEditor.prototype, Ajax.InPlaceEditor.prototype );
Object.extend( DomInPlaceEditor.prototype, {

	// overload the handleFormSubmission method to fire an event with the edited data instead of doing the ajax call
	handleFormSubmission : function( e )
	{
		if(editor)
		{
			if(Prototype.Browser.IE)
			{
				this._controls.editor.value = editor.getData();
			}
			else
			{
				this._controls.editor.innerHTML = editor.getData();
			}
			
			// Destroy the editor.
			editor.destroy();
			editor = null;
		}

		// var v = $F(this._controls.editor);
		this.triggerCallback('onSubmitForm');
		this.wrapUp();
		
		document.fire('templateEditor:render');
		
		if (e) Event.stop(e);
	},
	
	leaveEditMode: function() {
		this.triggerCallback('onLeaveEditMode');
		
	    this.element.removeClassName(this.options.savingClassName);
	    this.removeForm();
	    this.leaveHover();
	    this.element.style.backgroundColor = this._originalBackground;
	    this.element.show();
	    if (this.options.externalControl)
	      this.options.externalControl.show();
	    this._saving = false;
	    this._editing = false;
	    this._oldInnerHTML = null;	    
	  },

	enterEditMode: function(e) {
		if (this._saving || this._editing) return;
		if(	$$('form.inplaceeditor-form').length > 0) return;
		this._editing = true;
		
		if (this.options.externalControl)
			this.options.externalControl.hide();
		this.element.hide();
		this.createForm();
		this.element.parentNode.insertBefore(this._form, this.element);
		if (!this.options.loadTextURL)
			this.postProcessEditField();
			
		this.triggerCallback('onEnterEditMode');
		
		if (e) Event.stop(e);
	},
	
	// overload the getText method in order to allow HTML tags to be used
	getText: function() {
		return this.element.innerHTML;
    }
	
} );

// create a new instance of the templateEditor class
var templateEdit;
function loadTemplateEditor( id )
{
	templateEdit = new templateEditor( id );
}

// templateEditor class
var templateEditor = Class.create();
templateEditor.prototype = {
	initialize : function( id, options )
	{
		this.options = Object.extend({
			useWysiwyg: true
        },options || {});
		
		$$('div.editablesection').each(function(el)
		{
			el.hide();
		});
		
		if($('template-editor')) { $('template-editor').remove(); }
		this.container = new Element('div', { id: 'template-editor'} );		// the main container that all the work happens within
		$(id).insert( { after: this.container } );

		this.id	= $(id);
		this.original = pageContent[this.id.id].clone();								// an array of objects containing the original page content data
		
		this.content = pageContent[this.id.id].clone();								// an array of objects containing the page content data
		this.curr = {};														// a temporary storage object used when a new template element is being added
		
		this.dirty = false;													// has it been edited, but not saved
		this.editing = false;												// are we currently editing?
		
		// add link to page for page templates (used by modal window)
		if($('insertLink')) { $('insertLink').remove(); }
		this.link = new Element( 'a', { 'id':'insertLink', 'href': '/pagetpl/listing'} ).hide();
		this.id.insert( this.link );
		
		// add link to page for uploader (used by modal window)
		if($('imageLink')) { $('imageLink').remove(); }
		this.imageLink = new Element( 'a', { 'id':'imageLink', 'href': '/uploads/index'} ).hide();
		this.id.insert( this.imageLink );
		
		if($('pdfLinkLink')) { $('pdfLinkLink').remove(); }
		this.pdfLinkLink = new Element( 'a', { 'id':'pdfLinkLink', 'href': '/uploads/pdfs'} ).hide();
		this.id.insert( this.pdfLinkLink );		
		
		Event.observe(window, "beforeunload", this.beforeUnload.bindAsEventListener(this), false);
		Event.observe(window, "onunload", this.beforeUnload.bindAsEventListener(this), false);
		
		document.observe('templateEditor:render', this.renderEditor.bindAsEventListener( this ), false);
		
		this.renderEditor();
	},
	
	renderView : function( update )
	{
		if(update)
		{
			this.id.update('');

			// for each page_content item in each position
			for( var i=0; i < this.content.length; i++ )
			{
				this.id.insert( this.content[i].wrapper.replace( '{content}', this.content[i].copy ) );
			}
		}
	
		$$('div.editablesection').each(function(el)
		{
			el.show();
		});
		this.container.remove();
		templateEdit = null;
	},
	
	// render the content array, with controls
	renderEditor : function()
	{
		// start fresh each time
		this.container.update('');
		this.editors = new Array();
		
		// for each page_content item in each position
		for( var i=0; i < this.content.length; i++ )
		{
			var wrapper = new Element('div', { 'class': 'editable-row surround' } );
			
			// add a delete button
			if(this.content[i].editable && this.content.length > 1)
			{
				var del = new Element('input', { type: 'button', value: 'delete', 'class': 'editable-admin' } );
				del.observe('click', this.deleteElement.bindAsEventListener( this, i ) );
				wrapper.insert( del );
			}
			
			// add an "add above" button
			var addabove = new Element('input', { type: 'button', value: 'add above', 'class': 'editable-admin'} );
			addabove.observe('click', this.addElement.bindAsEventListener( this, i, 0 ) );
			wrapper.insert( addabove );			
	
			// if this isn't the last element, show a move down button
			if(i < this.content.length-1)
			{
				var down = new Element('input', { type: 'button', value: 'down', 'class': 'editable-admin' } );
				down.observe('click', this.moveElement.bindAsEventListener( this, i, 1 ) );
				wrapper.insert( down );				
			}
			
			// if this isn't the first element, show a move up button
			if(i > 0)
			{
				var up = new Element('input', { type: 'button', value: 'up', 'class': 'editable-admin' } );
				up.observe('click', this.moveElement.bindAsEventListener( this, i, -1 ) );
				wrapper.insert( up );				
			}
			
			// get the copy from the content array, and put it within the an "editable-copy" div, inside the elements "wrapper"
			var copy = this.content[i].wrapper.replace( '{content}', '<div class="editable-copy">'+this.content[i].copy+'</div>' );
			
			// put that copy inside a new "wrapper-content" div, and put that into the wrapper
			var content = new Element('div', { 'class': 'wrapper-content' } ).update( copy );
			wrapper.insert( content );
			
			// set up the image replace links
			content.getElementsBySelector('img.replaceable').each( this.setImages.bind(this, i) );
			
			// Set up pdf replace links
			content.getElementsBySelector('a.link.replaceable').each( this.setPDFs.bind(this, i) );			
			
			// get the actual copy element
			var element = content.getElementsBySelector('.editable-copy').first();
			
			// if this is the last element, show an "add below" button
			if(i == this.content.length-1)
			{
				var addbelow = new Element('input', { type: 'button', value: 'add below', 'class': 'editable-admin'} );
				addbelow.observe('click', this.addElement.bindAsEventListener( this, i, 1 ) );
				wrapper.insert( addbelow );
			}
			
			if(this.content[i].editable && this.content[i].type != 'none')
			{
				var settings = { 
									citem: i,
									onEnterEditMode: this.editMode.bind(this, i),
									onLeaveEditMode: this.exitEditMode.bind(this, i),
									onSubmitForm: this.updateElement.bind(this, i),
									hoverClassName:'template-editor-hover'
								};
				
				if(this.content[i].type == 'wysiwyg')
				{
					settings.rows = 10;
					settings.cols = 40;
				}
				
				this.editors[i] = new DomInPlaceEditor( element, '#', settings );
			}
			
			this.container.insert( wrapper );
		}
		
		// add a save button
		var save_button = new Element('input', { 'type': 'button', 'value': 'Save', 'class': 'editable-admin' } );
		save_button.observe('click', this.saveData.bindAsEventListener(this), false);
		this.container.insert( save_button );
		
		// add a cancel button
		var cancel_button = new Element('input', { 'type': 'button', 'value': 'Cancel', 'class': 'editable-admin' } );
		cancel_button.observe('click', this.cancelEdit.bindAsEventListener(this), false);
		this.container.insert( cancel_button );
	},

	
	setImages : function( i, el )
	{
		el.observe('click', this.replaceImage.bindAsEventListener(this, i), false );
	},
	
	setPDFs : function( i, el )
	{
		el.observe('click', this.replacePDFlink.bindAsEventListener(this, i), false );
	},	
	/*
	*  before unload
	*/
	beforeUnload : function( e )
	{
		if( this.editing || this.dirty ) { 
			var msg = (this.editing)?
					"You are currently editing something, please close the editor first":
					"You've made changes but not saved them. Please click the 'Save' button if you want to keep them";
			
			if(confirm( msg ))
			{
				e.stop();
				return false;
			}
		}
	},
	
	/*
	*  exit edit mode	
	*/
	exitEditMode : function( i )
	{
		if(editor)
		{
			// Destroy the editor.
			editor.destroy();
			
			editor = null;
		}
		
		this.dirty = true;
		this.editing = false;
		$$('.editable-admin').each( function( el ) { el.show(); } );
		// this.renderEditor();
	},
	
	/*
	*  enter edit mode
	*/
	editMode : function( i )
	{	
		this.editing = true;		
		$$('.editable-admin').each( function( el ) { el.hide(); } );
		
		if(this.content[i].type == 'wysiwyg' && this.options.useWysiwyg)
		{
			var editorId = $$('.editor_field').first().identify();

			if( editor )
			{
				editor.destroy();
				editor = null;
			}
			editor = CKEDITOR.replace( editorId, { customConfig : '/staticfiles/js/ckeditor-config.js', toolbar: 'TemplateEditor' } );
		}
	},
	
	replaceImage : function( e, item )
	{
		elem = e.findElement( 'img' );
		e.stop(); 
		
		document.stopObserving('modal:opened');
		document.observe('modal:opened', this.uploadOpened.bindAsEventListener(this), false );
		
		mod = this.window_factory( this.imageLink );
		mod.open();
		
		this.curr.item = item;
		this.curr.elem = elem;
		
	},
	
	replacePDFlink : function( e, item )
	{
		elem = e.findElement( 'a' );
		e.stop(); 
		
		document.stopObserving('modal:opened');
		document.observe('modal:opened', this.uploadOpened.bindAsEventListener(this), false );
		
		mod = this.window_factory( this.pdfLinkLink );
		mod.open();
		
		this.curr.item = item;
		this.curr.elem = elem;
		
	},
	/*
	*  fired when upload modal window is opened; watches for upload form to be submitted
	*/
	uploadOpened : function()
	{
		var b = $('modal-window-iframe').contentDocument.body;
		var form = Element.extend(b).getElementsBySelector( 'form#file_upload_form' ).first();
		if(this.curr.elem.tagName == 'IMG' )
		{
			this.populateImgUploader(form);
		}
		else if(this.curr.elem.tagName == 'A' )
		{
			this.populatePdfUploader(form);
		}
		
		form.observe('submit', this.uploadStarted.bindAsEventListener(this) );
	},
	populateImgUploader: function(form)
	{
		var hh = (this.curr.elem.style.height.length == 0)?0:this.curr.elem.getHeight();
		var ww = (this.curr.elem.style.width.length == 0)?0:this.curr.elem.getWidth();

		form.getElementsBySelector('input#form_pic_alt').first().value = this.curr.elem.alt;

		form.getElementsBySelector('input#form_pic_height').first().value = hh;
		form.getElementsBySelector('span#pic_height').first().innerHTML = (hh == 0)? '(No max) ':hh;
		form.getElementsBySelector('input#form_pic_width').first().value = ww;
		form.getElementsBySelector('span#pic_width').first().innerHTML = (ww == 0)? $(this.id.id).getWidth():ww
	},
	populatePdfUploader: function(form)
	{
		var href_arr = this.curr.elem.href.split('/');
		var pdf_id = 0;
		if(href_arr.length == 1)
		{
			pdf_id = 0;
		}
		else
		{
			pdf_id = href_arr[href_arr.length - 1].replace('.pdf', '');
		}
		var soptions = form.getElementsBySelector('select#form_pdf_existing option');
		soptions.each(function(el)
		{
			if(el.value == pdf_id)
			{
				el.selected = 'selected';
				$break;
			}
		});
		form.getElementsBySelector('select#form_pdf_existing').first().value = pdf_id;
		form.getElementsBySelector('input#form_pdf_link').first().value = this.curr.elem.innerHTML;
		form.getElementsBySelector('input#form_pdf_title').first().value = this.curr.elem.title;
	},	
	/*
	*  when upload form submitted, watch to see if the upload is finished
	*/
	uploadStarted : function( e )
	{
		new PeriodicalExecuter( this.waitforfile.bind(this), 0.01 );
	},
	
	/*
	*  wait for file upload to complete, when done close modal window and replace image
	*/
	waitforfile : function(pe)
	{
		var b = $('modal-window-iframe').contentDocument.body;
		var content_json = Element.extend(b).getElementsBySelector( 'iframe#upload_target' ).first().contentWindow.document.body.innerHTML;
		if(content_json == "") 
		{
			return false;
		}
		pe.stop();
		
		Control.Modal.close();

		var content = content_json.evalJSON();
		if(this.curr.elem.tagName == 'IMG' )
		{
			this.updateImageData(content);
		}
		if(this.curr.elem.tagName == 'A')
		{
			this.updatePdfLinkData(content);
		}
		
		if( this.curr.elem.up('div.wrapper-content').down('div.editable-copy') )
		{
			this.curr.elem.up('div.wrapper-content').down('div.editable-copy').replace('{content}');
		}
		this.content[this.curr.item].wrapper = this.curr.elem.up('div.wrapper-content').innerHTML;		
		this.curr = {};
		this.renderEditor();
	},
	updateImageData: function(content)
	{
		this.curr.elem.src = content.url;
		this.curr.elem.alt = content.alt;
		
		if( content.link.length )
		{
			var link = new Element('a', { href: content.link } );
			var img = this.curr.elem.replace( link );
			link.update( img );
		}
	},
	updatePdfLinkData: function(content)
	{
	
		this.curr.elem.href = content.url;
		this.curr.elem.title = content.title_text;
		this.curr.elem.innerHTML = content.link_text;	
	},	
	
	// encode the data to json and send back to the php
	cancelEdit : function( e )
	{
		this.content = this.original.clone();
		this.renderView( false );
		document.fire('template_editor:closed');		
	},
	
	// encode the data to json and send back to the php
	saveData : function( e )
	{
		var json = this.content.toJSON();
		var pagePosition = this.id.id.split('-').last();

		new Ajax.Request('/savepage/index', {
			parameters: { json: json, page_pid: pagePid, page_position: pagePosition },
			onSuccess: this.saveSuccess.bind(this)
		});
	},
	
	saveSuccess : function( transport )
	{
		if(transport.responseText != '0')
		{
			pageContent[this.id.id] = this.content.clone();
			this.dirty = false;
			this.renderView( true );
			document.fire('template_editor:closed');			
		}		
	},
	
	// update the content of an element, called from the DomInPlaceEditor
	updateElement : function( e, editor )
	{
		var v = $F(editor._controls.editor)
		var id = editor.options.citem;
		
		this.content[id].copy = v;
		// this.renderEditor();
		// e.stop();
	},
	
	// delete an element from the array and re-render
	deleteElement: function( e, item )
	{
		if(confirm('Are you sure you want to delete this section?'))
		{
			this.content.splice( item, 1 );
			this.renderEditor();
		}
		e.stop();
	},
	
	// move an element up or down in the array and re-render
	moveElement: function( e, item, dir )
	{
		if( (item + dir) > this.content.length - 1 ) { return; }
		if( (item + dir) < 0 ) { return; }
		
		var tmp = this.content[item];
		var tmp2 = this.content[item + dir];
		
		this.content[item] = tmp2;
		this.content[item + dir] = tmp;
		
		this.renderEditor();
		
		e.stop();
	},
	
	// add a new element and re-render()
	addElement: function( e, item, dir )
	{
		document.stopObserving('modal:opened');
		document.observe('modal:opened', this.modalOpened.bindAsEventListener(this), false );
		
		mod = this.window_factory( this.link );
		mod.open();
		
		this.curr.item = item;
		this.curr.dir = dir;
		
		e.stop();
	},
	
	// when modal window is opened, add click handlers to img tags
	modalOpened : function( e )
	{
		var b = $('modal-window-iframe').contentDocument.body;
		Element.extend(b).getElementsBySelector( 'ul#insert-items li img' ).each( this.modalImages.bind(this) );
	},
	
	// each handler for images inside modal window template page
	modalImages : function( el )
	{
		el.observe( 'click', this.modalClicks.bindAsEventListener(this), false );
	},
	
	// click handler for images inside modal window template page
	modalClicks : function( e )
	{
		elem = e.findElement('img');
		elem.stopObserving('click');
		
		new Ajax.Request('/pagetpl/index/'+elem.alt, {
			onSuccess: this.requestSuccess.bind( this )
		});
		
		// close the popup window
		Control.Modal.close();
	},
	
	// ajax request success handler, inserts the data into the page
	requestSuccess : function( transport )
	{
		if(transport.responseText != '0')
		{
			element = transport.responseText.evalJSON();
			for(i=0; i<element.length; i++)
			{
				this.content.splice( this.curr.item + this.curr.dir + i, 0, element[i] );
			}

			this.curr = {};
			this.renderEditor();
		}
	},
	
	// window factory
	window_factory : function( container )
	{  	
		var window_fade = $('control_overlay');
		
		var window_header = new Element('div', { className: 'window_header' } );                                     
		var window_close = new Element('div', { className: 'window_close' } );                                    
		var window_contents = new Element('div', { className: 'window_contents', 'style':'min-height:480px;' } );
			
		var w = new Control.Modal(container, Object.extend({  
			className: 'modal',
			fade: true,
			overlayOpacity: 0.75,
			closeOnClick: window_fade,
			height:document.viewport.getHeight() - 100,
			iframe:true,
			onRemoteContentLoaded: function()
			{
				document.fire('modal:opened');
			},
			afterClose:function()
			{
				var id = this.container.id;
				this.destroy();
				if($(id))
				{
					$(id).remove();
				}
				document.fire('lw:close');
			},
			insertRemoteContentAt: window_contents
			})
		);
		
		w.container.insert(window_header);   
		window_header.insert(window_close);                                                                 
		w.container.insert(window_contents);
		
		window_close.observe('click', function(e) {
			Control.Modal.close();
		}, false);
		
		return w;
	}
}