First revision. Largely unfinished; all buttons but Image work, but missing many features. Image is partially implemented.
authorDan Fuhry <dan@enanocms.org>
Wed, 27 Oct 2010 03:18:51 -0400
changeset 0 640e15974415
child 1 960c1572df75
First revision. Largely unfinished; all buttons but Image work, but missing many features. Image is partially implemented.
GUIEditor.php
guieditor/editor.js
guieditor/icons/add.png
guieditor/icons/bold.png
guieditor/icons/extlink.png
guieditor/icons/image.png
guieditor/icons/intlink.png
guieditor/icons/italic.png
guieditor/icons/separator.png
guieditor/icons/underline.png
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GUIEditor.php	Wed Oct 27 03:18:51 2010 -0400
@@ -0,0 +1,102 @@
+<?php
+/**!info**
+{
+  "Plugin Name"  : "GUI Editor",
+  "Plugin URI"   : "http://enanocms.org/plugin/guieditor",
+  "Description"  : "Adds a toolbar to the editor interface, allowing point-and-click insertion of most formatting elements",
+  "Author"       : "Dan Fuhry",
+  "Version"      : "0.1",
+  "Author URI"   : "http://enanocms.org/",
+  "Version list" : ['0.1']
+}
+**!*/
+
+$plugins->attachHook('compile_template', 'guied_attach_script();');
+
+function guied_attach_script()
+{
+	global $template;
+	$template->add_header_js('<script type="text/javascript" src="' . scriptPath . '/plugins/guieditor/editor.js"></script>');
+}
+
+//
+// Image search autofill
+//
+
+$plugins->attachHook('autofill_json_request', 'guied_image_autofill($dataset);');
+
+function guied_image_autofill(&$dataset)
+{
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	if ( $_GET['type'] == 'guied_image' )
+	{
+		$results = perform_search($_GET['userinput'], $warnings, false, $word_list);
+		foreach ( $results as $i => $result )
+		{
+			if ( $result['namespace'] != 'File' )
+				unset($results[$i]);
+		}
+		if ( count($results) > 5 )
+		{
+			$results = array_slice($results, 0, 5);
+		}
+		foreach ( $results as $result )
+		{
+			$dataset[] = array(
+					0 => $result['page_id'],
+					'title' => str_replace(array('<highlight>', '</highlight>'), array('<b>', '</b>'), $result['page_name']),
+					'thumbnail' => makeUrlNS('Special', "DownloadFile/{$result['page_id']}", "preview&width=80&height=80"),
+					'score' => $result['score'],
+					'type' => isset($result['page_note']) ? $result['page_note'] : '',
+					'size' => $result['page_length'],
+				);
+		}
+	}
+}
+
+/**!language**
+<code>
+{
+	eng: {
+		categories: ['meta', 'guied'],
+		strings: {
+			meta: {
+				guied: 'GUI Editor',
+			},
+			guied: {
+				btn_bold: 'Bold',
+				btn_italic: 'Italic',
+				btn_underline: 'Underline',
+				btn_intlink: 'Internal link',
+				btn_extlink: 'External link',
+				btn_image: 'Image',
+				
+				sample_bold: 'Bold text',
+				sample_italic: 'Italic text',
+				sample_underline: 'Underlined text',
+				
+				intlink_title: 'Insert internal link',
+				intlink_lbl_page: 'Page:',
+				intlink_lbl_text: 'Link text:',
+				intlink_af_hint: 'Type a few letters to search.',
+				intlink_text_hint: 'If left blank, the title of the page linked to will be displayed.',
+				
+				extlink_title: 'Insert external link',
+				extlink_lbl_link: 'Link:',
+				extlink_lbl_text: 'Link text: ',
+				extlink_link_hint: 'Supported link types: http, https, irc, mailto, ftp',
+				extlink_text_hint: 'If left blank, the link URL will be displayed.',
+				
+				image_title: 'Insert image',
+				image_lbl_image: 'Image file:',
+				image_btn_upload: 'Upload a file',
+				image_lbl_caption: 'Caption:',
+				image_af_hint: 'Type a few letters to search.',
+				
+				btn_insert: 'Insert'
+			}
+		}
+	}
+}
+</code>
+**!*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/guieditor/editor.js	Wed Oct 27 03:18:51 2010 -0400
@@ -0,0 +1,401 @@
+attachHook('editor_gui_toolbar', 'guied_insert_toolbar(ta_wrapper, response.toolbar_templates);');
+
+function guied_insert_toolbar(ta_wrapper, toolbar_templates)
+{
+	// Init toolbar
+	var toolbar = '';
+	var head = new templateParser(toolbar_templates.toolbar_start);
+	var button = new templateParser(toolbar_templates.toolbar_button);
+	var label = new templateParser(toolbar_templates.toolbar_label);
+	var tail = new templateParser(toolbar_templates.toolbar_end);
+	
+	button.assign_bool({
+			show_title: true
+		});
+	
+	toolbar += head.run();
+	
+	var buttons = ['bold*', 'italic*', 'underline*', '|', 'intlink', 'extlink', 'image'];
+	
+	// Button: Bold
+	var i;
+	var hide_label = false;
+	for ( i = 0; i < buttons.length; i++ )
+	{
+		if ( buttons[i] == '|' )
+		{
+			label.assign_vars({
+					TITLE: '<img alt="|" src="' + scriptPath + '/plugins/guieditor/icons/separator.png" />'
+				});
+			toolbar += label.run();
+		}
+		else
+		{
+			if ( buttons[i].charAt(buttons[i].length - 1) == '*' )
+			{
+				hide_label = true;
+				buttons[i] = buttons[i].substr(0, buttons[i].length - 1);
+				button.assign_bool({ show_title: false });
+			}
+			button.assign_vars({
+					TITLE: $lang.get('guied_btn_' + buttons[i]),
+					IMAGE: cdnPath + '/plugins/guieditor/icons/' + buttons[i] + '.png',
+					FLAGS: 'href="#" onclick="guied_act(\'' + buttons[i] + '\'); return false;"'
+				});
+			toolbar += button.run();
+			if ( hide_label )
+			{
+				button.assign_bool({ show_title: true });
+				hide_label = false;
+			}
+		}
+	}
+	
+	// End of toolbar
+	toolbar += tail.run();
+	
+	var wrapperdiv = document.createElement('div');
+	wrapperdiv.innerHTML = toolbar;
+	ta_wrapper.appendChild(wrapperdiv);
+}
+
+function guied_act(action)
+{
+	var textarea = document.getElementById('ajaxEditArea');
+	switch(action)
+	{
+		case 'bold':
+			guied_insert_wikitext_tag(textarea, "'''", "'''", $lang.get('guied_sample_bold'));
+			break;
+		case 'italic':
+			guied_insert_wikitext_tag(textarea, "''", "''", $lang.get('guied_sample_italic'));
+			break;
+		case 'underline':
+			guied_insert_wikitext_tag(textarea, "__", "__", $lang.get('guied_sample_underline'));
+			break;
+		case 'intlink':
+			load_component('autofill');
+			var selection = guied_get_selection(textarea);
+			var il_mp = miniPrompt(function(div)
+				{
+					div.innerHTML += '<h3>' + $lang.get('guied_intlink_title') + '</h3>';
+					div.innerHTML += '<table border="0" cellspacing="5" cellpadding="0" style="width: 100%;"> \
+								<tr> \
+									<td valign="top" style="white-space: nowrap;"> \
+									' + $lang.get('guied_intlink_lbl_page') + ' \
+									</td> \
+									<td valign="top"> \
+										<input type="text" id="guied_intlink_page" class="autofill page" style="width: 100%;" /><br /> \
+										<small>' + $lang.get('guied_intlink_af_hint') + '</small> \
+									</td> \
+								</tr> \
+								<tr> \
+									<td valign="top" style="white-space: nowrap;"> \
+									' + $lang.get('guied_intlink_lbl_text') + ' \
+									</td> \
+									<td valign="top"> \
+										<input type="text" id="guied_intlink_text" style="width: 100%;" /><br /> \
+										<small>' + $lang.get('guied_intlink_text_hint') + '</small> \
+									</td> \
+								</tr>';
+					div.innerHTML += '<p style="text-align: right;"> \
+									<a class="abutton abutton_blue" onclick="guied_intlink_finish(this); return false;" href="#">' + $lang.get('guied_btn_insert') + '</a> \
+									<a class="abutton abutton_red" onclick="miniPromptDestroy(this); return false;" href="#">' + $lang.get('etc_cancel') + '</a> \
+								</p>';
+				});
+			
+			// This fixes autofill
+			il_mp.style.zIndex = getHighestZ() + 1;
+			
+			autofill_init_element(document.getElementById('guied_intlink_page'), {});
+			document.getElementById('guied_intlink_page').focus();
+			$('#guied_intlink_text').val(selection);
+			
+			break;
+		case 'extlink':
+			load_component('autofill');
+			var selection = guied_get_selection(textarea);
+			var il_mp = miniPrompt(function(div)
+				{
+					div.innerHTML += '<h3>' + $lang.get('guied_extlink_title') + '</h3>';
+					div.innerHTML += '<table border="0" cellspacing="5" cellpadding="0" style="width: 100%;"> \
+								<tr> \
+									<td valign="top" style="white-space: nowrap;"> \
+									' + $lang.get('guied_extlink_lbl_link') + ' \
+									</td> \
+									<td valign="top"> \
+										<input type="text" id="guied_extlink_link" style="width: 100%;" /><br /> \
+										<small>' + $lang.get('guied_extlink_link_hint') + '</small> \
+									</td> \
+								</tr> \
+								<tr> \
+									<td valign="top" style="white-space: nowrap;"> \
+									' + $lang.get('guied_extlink_lbl_text') + ' \
+									</td> \
+									<td valign="top"> \
+										<input type="text" id="guied_extlink_text" style="width: 100%;" /><br /> \
+										<small>' + $lang.get('guied_extlink_text_hint') + '</small> \
+									</td> \
+								</tr>';
+					div.innerHTML += '<p style="text-align: right;"> \
+									<a class="abutton abutton_blue" onclick="guied_extlink_finish(this); return false;" href="#">' + $lang.get('guied_btn_insert') + '</a> \
+									<a class="abutton abutton_red" onclick="miniPromptDestroy(this); return false;" href="#">' + $lang.get('etc_cancel') + '</a> \
+								</p>';
+				});
+			
+			document.getElementById('guied_extlink_link').focus();
+			$('#guied_extlink_text').val(selection);
+			
+			break;
+		case 'image':
+			// Fuck yeah.
+			load_component('autofill');
+			var selection = guied_get_selection(textarea);
+			var il_mp = miniPrompt(function(div)
+				{
+					div.innerHTML += '<h3>' + $lang.get('guied_image_title') + '</h3>';
+					div.innerHTML += '<table border="0" cellspacing="5" cellpadding="0" style="width: 100%;"> \
+								<tr> \
+									<td valign="top" style="white-space: nowrap;"> \
+									' + $lang.get('guied_image_lbl_image') + ' \
+									</td> \
+									<td valign="top"> \
+										<img id="guied_image_preview" src="' + cdnPath + '/images/spacer.gif" style="display: block;" /> \
+										<input type="text" id="guied_image_file" class="autofill guied_image" style="width: 100%;" onblur="guied_refresh_image();" /><br /> \
+										<small>' + $lang.get('guied_image_af_hint') + '</small> \
+										<div id="guied_upload_body" style="padding: 4px 0;"> \
+										<a class="abutton icon abutton_green" style="background-image: url(' + scriptPath + '/plugins/guieditor/icons/add.png);" href="#" onclick="guied_image_show_uploader(); return false;">' + $lang.get('guied_image_btn_upload') + '</a> \
+										</div> \
+									</td> \
+								</tr> \
+								<tr> \
+									<td valign="top" style="white-space: nowrap;"> \
+									' + $lang.get('guied_image_lbl_caption') + ' \
+									</td> \
+									<td valign="top"> \
+										<input type="text" id="guied_image_caption" style="width: 100%;" /> \
+									</td> \
+								</tr>';
+					div.innerHTML += '<p style="text-align: right;"> \
+									<a class="abutton abutton_blue" onclick="guied_image_finish(this); return false;" href="#">' + $lang.get('guied_btn_insert') + '</a> \
+									<a class="abutton abutton_red" onclick="miniPromptDestroy(this); return false;" href="#">' + $lang.get('etc_cancel') + '</a> \
+								</p>';
+				});
+			
+			// This fixes autofill
+			il_mp.style.zIndex = getHighestZ() + 1;
+			
+			autofill_init_element(document.getElementById('guied_image_file'), {});
+			document.getElementById('guied_image_file').focus();
+			$('#guied_image_caption').val(selection);
+			
+			break;
+		
+	}
+}
+
+function guied_intlink_finish(insertbtn)
+{
+	var page = $('#guied_intlink_page').val();
+	var text = $('#guied_intlink_text').val();
+	var tag = text == '' ? '[[' + page + ']]' : '[[' + page + '|' + text + ']]'; 
+	guied_replace_selection(document.getElementById('ajaxEditArea'), tag);
+	
+	miniPromptDestroy(insertbtn);
+}
+
+function guied_extlink_finish(insertbtn)
+{
+	var link = $('#guied_extlink_link').val();
+	var text = $('#guied_extlink_text').val();
+	var tag = text == '' ? '[' + link + ']' : '[' + link + ' ' + text + ']'; 
+	guied_replace_selection(document.getElementById('ajaxEditArea'), tag);
+	
+	miniPromptDestroy(insertbtn);
+}
+
+var guied_upl_oldhtml = '';
+function guied_image_show_uploader()
+{
+	var div = document.getElementById('guied_upload_body');
+	guied_upl_oldhtml = $(div).html();
+	$(div).html('<form action="' + makeUrlNS('Special', 'UploadFile') + '" target="uploadwin" onsubmit="return guied_upload_popup();" method="post" enctype="multipart/form-data">' +
+				'<p><input type="file" style="width: 100%;" name="data" id="guied_upload_file" /></p>' +
+				'<input type="hidden" value="" name="rename" />' +
+				'<input type="hidden" value="" name="comments" />' +
+				'<p><input type="submit" name="doit" value="' + $lang.get('upload_btn_upload') + '"></p>' +
+			'</form>');
+}
+
+
+function guied_upload_popup()
+{
+	window.open('about:blank', 'uploadwin', 'width=640,height=480,status=no,toolbar=no,menubar=no,location=no,resizable=yes,scrollbars=yes');
+	var filename = $('#guied_upload_file').val();
+	if ( filename == "" )
+		return false;
+	filename = filename.split(/[\/\\\\]/);
+	filename = filename[ filename.length - 1 ];
+	// sneaky little trick to make onblur get called
+	$('#guied_image_file').val(sanitize_page_id(filename)).focus();
+	setTimeout('$("#guied_upload_body").html(guied_upl_oldhtml)', 100);
+	return true;
+}
+
+function guied_refresh_image()
+{
+	$('#guied_image_preview').attr('src', makeUrlNS('Special', 'DownloadFile/' + $('#guied_image_file').val(), 'preview&width=200&height=400')).css('margin-bottom', '5px');
+}
+
+function guied_image_finish()
+{
+	// yeah... working on this.
+}
+
+// Client detection from MediaWiki
+var clientPC = navigator.userAgent.toLowerCase(); // Get client info
+var is_gecko = ((clientPC.indexOf('gecko')!=-1) && (clientPC.indexOf('spoofer')==-1)
+                && (clientPC.indexOf('khtml') == -1) && (clientPC.indexOf('netscape/7.0')==-1));
+
+// Function adapted from MediaWiki/phpBB
+function guied_insert_wikitext_tag(txtarea, tagOpen, tagClose, sampleText)
+{
+	// IE
+	if (document.selection  && !is_gecko) {
+		var theSelection = document.selection.createRange().text;
+		if (!theSelection)
+			theSelection=sampleText;
+		txtarea.focus();
+		if (theSelection.charAt(theSelection.length - 1) == " ") { // exclude ending space char, if any
+			theSelection = theSelection.substring(0, theSelection.length - 1);
+			document.selection.createRange().text = tagOpen + theSelection + tagClose + " ";
+		} else {
+			document.selection.createRange().text = tagOpen + theSelection + tagClose;
+		}
+
+	// Mozilla
+	} else if(txtarea.selectionStart || txtarea.selectionStart == '0') {
+		var replaced = false;
+		var startPos = txtarea.selectionStart;
+		var endPos = txtarea.selectionEnd;
+		if (endPos-startPos)
+			replaced = true;
+		var scrollTop = txtarea.scrollTop;
+		var myText = (txtarea.value).substring(startPos, endPos);
+		if (!myText)
+			myText=sampleText;
+		if (myText.charAt(myText.length - 1) == " ") { // exclude ending space char, if any
+			subst = tagOpen + myText.substring(0, (myText.length - 1)) + tagClose + " ";
+		} else {
+			subst = tagOpen + myText + tagClose;
+		}
+		txtarea.value = txtarea.value.substring(0, startPos) + subst +
+			txtarea.value.substring(endPos, txtarea.value.length);
+		txtarea.focus();
+		//set new selection
+		if (replaced) {
+			var cPos = startPos+(tagOpen.length+myText.length+tagClose.length);
+			txtarea.selectionStart = cPos;
+			txtarea.selectionEnd = cPos;
+		} else {
+			txtarea.selectionStart = startPos+tagOpen.length;
+			txtarea.selectionEnd = startPos+tagOpen.length+myText.length;
+		}
+		txtarea.scrollTop = scrollTop;
+
+	// All other browsers get no toolbar.
+	}
+	// reposition cursor if possible
+	if (txtarea.createTextRange)
+		txtarea.caretPos = document.selection.createRange().duplicate();
+}
+
+function guied_replace_selection(txtarea, text)
+{
+	// IE
+	if (document.selection  && !is_gecko) {
+		txtarea.focus();
+		document.selection.createRange().text = text;
+
+	// Mozilla
+	} else if(txtarea.selectionStart || txtarea.selectionStart == '0') {
+		var replaced = false;
+		var startPos = txtarea.selectionStart;
+		var endPos = txtarea.selectionEnd;
+		if (endPos-startPos)
+			replaced = true;
+		var scrollTop = txtarea.scrollTop;
+		var myText = (txtarea.value).substring(startPos, endPos);
+		if (!myText)
+			myText = '';
+		txtarea.value = txtarea.value.substring(0, startPos) + text +
+			txtarea.value.substring(endPos, txtarea.value.length);
+		txtarea.focus();
+		//set new selection
+		var cPos = startPos+text.length;
+		txtarea.selectionStart = cPos;
+		txtarea.selectionEnd = cPos;
+		txtarea.scrollTop = scrollTop;
+
+	}
+	// reposition cursor if possible
+	if (txtarea.createTextRange)
+		txtarea.caretPos = document.selection.createRange().duplicate();
+}
+
+function guied_get_selection(txtarea, sampleText)
+{
+	if ( !sampleText )
+		sampleText = '';
+	
+	// IE
+	if (document.selection  && !is_gecko) {
+		var theSelection = document.selection.createRange().text;
+		if (!theSelection)
+			theSelection=sampleText;
+		return theSelection;
+
+	// Mozilla
+	} else if(txtarea.selectionStart || txtarea.selectionStart == '0') {
+		var replaced = false;
+		var startPos = txtarea.selectionStart;
+		var endPos = txtarea.selectionEnd;
+		if (endPos-startPos)
+			replaced = true;
+		var scrollTop = txtarea.scrollTop;
+		var myText = (txtarea.value).substring(startPos, endPos);
+		if (!myText)
+			myText=sampleText;
+		return myText;
+	}
+	return sampleText;
+}
+
+//
+// Autofill schema for images
+//
+
+var autofill_schemas = autofill_schemas || {};
+autofill_schemas.guied_image = {
+	init: function(element, fillclass, params)
+	{
+		$(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass) + '&userinput=', {
+				minChars: 3,
+				formatItem: function(row, _, __)
+				{
+					var html = '<div style="float: left; margin-right: 4px;"><img alt="" src="' + row.thumbnail + '" /></div>';
+					html += row.title;
+					html += '<div style="clear: both;"></div>';
+					return html;
+				},
+				showWhenNoResults: true,
+				noResultsHTML: '<tr><td class="row1" style="font-size: smaller;">' + $lang.get('user_autofill_msg_no_suggestions') + '</td></tr>',
+				onItemSelect: function(li)
+				{
+					// $('#guied_image_file').val(li.selectValue);
+					guied_refresh_image();
+				}
+		});
+	}
+	
+};
Binary file guieditor/icons/add.png has changed
Binary file guieditor/icons/bold.png has changed
Binary file guieditor/icons/extlink.png has changed
Binary file guieditor/icons/image.png has changed
Binary file guieditor/icons/intlink.png has changed
Binary file guieditor/icons/italic.png has changed
Binary file guieditor/icons/separator.png has changed
Binary file guieditor/icons/underline.png has changed