You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@roller.apache.org by sn...@apache.org on 2006/10/08 21:54:16 UTC

svn commit: r454197 [14/29] - in /incubator/roller/trunk/web: WEB-INF/classes/ roller-ui/authoring/editors/ roller-ui/authoring/editors/xinha/ roller-ui/authoring/editors/xinha/conf/ roller-ui/authoring/editors/xinha/contrib/ roller-ui/authoring/editor...

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/GetHtml/get-html.js
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/GetHtml/get-html.js?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/GetHtml/get-html.js (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/GetHtml/get-html.js Sun Oct  8 12:53:13 2006
@@ -0,0 +1,209 @@
+/**
+  * Based on XML_Utility functions submitted by troels_kn.
+  * credit also to adios, who helped with reg exps:
+  * http://www.sitepoint.com/forums/showthread.php?t=201052
+  * 
+  * A replacement for HTMLArea.getHTML
+  *
+  * Features:
+  *   - Generates XHTML code
+  *   - Much faster than HTMLArea.getHTML
+  *   - Eliminates the hacks to accomodate browser quirks
+  *   - Returns correct code for Flash objects and scripts
+  *   - Formats html in an indented, readable format in html mode
+  *   - Preserves script and pre formatting
+  *   - Preserves formatting in comments
+  *   - Removes contenteditable from body tag in full-page mode
+  *   - Supports only7BitPrintablesInURLs config option
+  *   - Supports htmlRemoveTags config option
+  */
+  
+function GetHtml(editor) {
+    this.editor = editor;
+}
+
+GetHtml._pluginInfo = {
+	name          : "GetHtml",
+	version       : "1.0",
+	developer     : "Nelson Bright",
+	developer_url : "http://www.brightworkweb.com/",
+	license       : "htmlArea"
+};
+
+HTMLArea.RegExpCache = [
+/*00*/  new RegExp().compile(/<\s*\/?([^\s\/>]+)[\s*\/>]/gi),//lowercase tags
+/*01*/  new RegExp().compile(/(\S*\s*=\s*)?_moz[^=>]*(=\s*[^>]*)?/gi),//strip _moz attributes
+/*02*/  new RegExp().compile(/\s*=\s*(([^'"][^>\s]*)([>\s])|"([^"]+)"|'([^']+)')/g),// find attributes
+/*03*/  new RegExp().compile(/\/>/g),//strip singlet terminators
+/*04*/ // new RegExp().compile(/<(br|hr|img|input|link|meta|param|embed)([^>]*)>/g),//terminate singlet tags
+/*04*/  new RegExp().compile(/<(br|hr|img|input|link|meta|param|embed|area)((\s*\S*="[^"]*")*)>/g),//terminate singlet tags
+/*05*/  new RegExp().compile(/(checked|compact|declare|defer|disabled|ismap|multiple|no(href|resize|shade|wrap)|readonly|selected)([\s>])/gi),//expand singlet attributes
+/*06*/  new RegExp().compile(/(="[^']*)'([^'"]*")/),//check quote nesting
+/*07*/  new RegExp().compile(/&(?=[^<]*>)/g),//expand query ampersands
+/*08*/  new RegExp().compile(/<\s+/g),//strip tagstart whitespace
+/*09*/  new RegExp().compile(/\s+(\/)?>/g),//trim whitespace
+/*10*/  new RegExp().compile(/\s{2,}/g),//trim extra whitespace
+/*11*/  new RegExp().compile(/\s+([^=\s]+)(="[^"]+")/g),// lowercase attribute names
+/*12*/  new RegExp().compile(/(\S*\s*=\s*)?contenteditable[^=>]*(=\s*[^>\s\/]*)?/gi),//strip contenteditable
+/*13*/  new RegExp().compile(/((href|src)=")([^\s]*)"/g), //find href and src for stripBaseHref()
+/*14*/  new RegExp().compile(/<\/?(div|p|h[1-6]|table|tr|td|th|ul|ol|li|blockquote|object|br|hr|img|embed|param|pre|script|html|head|body|meta|link|title|area|input|form|textarea|select|option)[^>]*>/g),
+/*15*/  new RegExp().compile(/<\/(div|p|h[1-6]|table|tr|ul|ol|blockquote|object|html|head|body|script|form|select)( [^>]*)?>/g),//blocklevel closing tag
+/*16*/  new RegExp().compile(/<(div|p|h[1-6]|table|tr|ul|ol|blockquote|object|html|head|body|script|form|select)( [^>]*)?>/g),//blocklevel opening tag
+/*17*/  new RegExp().compile(/<(td|th|li|option|br|hr|img|embed|param|pre|meta|link|title|area|input|textarea)[^>]*>/g),//singlet tag or output on 1 line
+/*18*/  new RegExp().compile(/(^|<\/(pre|script)>)(\s|[^\s])*?(<(pre|script)[^>]*>|$)/g),//find content NOT inside pre and script tags
+/*19*/  new RegExp().compile(/(<pre[^>]*>)(\s|[^\s])*?(<\/pre>)/g),//find content inside pre tags
+/*20*/  new RegExp().compile(/(^|<!--(\s|\S)*?-->)((\s|\S)*?)(?=<!--(\s|\S)*?-->|$)/g),//find content NOT inside comments
+/*21*/  new RegExp().compile(/\S*=""/g), //find empty attributes
+/*22*/  new RegExp().compile(/<!--[\s\S]*?-->|<\?[\s\S]*?\?>|<\/?\w[^>]*>/g), //find all tags, including comments and php
+/*23*/  new RegExp().compile(/(^|<\/script>)(\s|[^\s])*?(<script[^>]*>|$)/g) //find content NOT inside script tags
+];
+
+/** 
+  * Cleans HTML into wellformed xhtml
+  */
+HTMLArea.prototype.cleanHTML = function(sHtml) {
+	var c = HTMLArea.RegExpCache;
+	sHtml = sHtml.
+		replace(c[0], function(str) { return str.toLowerCase(); } ).//lowercase tags/attribute names
+		replace(c[1], ' ').//strip _moz attributes
+		replace(c[12], ' ').//strip contenteditable
+		replace(c[2], '="$2$4$5"$3').//add attribute quotes
+		replace(c[21], ' ').//strip empty attributes
+		replace(c[11], function(str, p1, p2) { return ' '+p1.toLowerCase()+p2; }).//lowercase attribute names
+		replace(c[3], '>').//strip singlet terminators
+		replace(c[9], '$1>').//trim whitespace
+		replace(c[5], '$1="$1"$3').//expand singlet attributes
+		replace(c[4], '<$1$2 />').//terminate singlet tags
+		replace(c[6], '$1$2').//check quote nesting
+	//	replace(c[7], '&amp;').//expand query ampersands
+		replace(c[8], '<').//strip tagstart whitespace
+		replace(c[10], ' ');//trim extra whitespace
+	if(HTMLArea.is_ie && c[13].test(sHtml)) {
+		sHtml = sHtml.replace(c[13],'$1'+this.stripBaseURL(RegExp.$3)+'"');
+	}
+	if(this.config.only7BitPrintablesInURLs && c[13].test(sHtml)) {
+	  sHtml = sHtml.replace(c[13], '$1'+RegExp.$3.replace(/([^!-~]+)/g,function(chr){return escape(chr);})+'"');
+	}
+	return sHtml;
+};
+
+/**
+  * Prettyfies html by inserting linebreaks before tags, and indenting blocklevel tags
+  */
+HTMLArea.indent = function(s, sindentChar) {
+	HTMLArea.__nindent = 0;
+	HTMLArea.__sindent = "";
+	HTMLArea.__sindentChar = (typeof sindentChar == "undefined") ? "  " : sindentChar;
+	var c = HTMLArea.RegExpCache;
+	if(HTMLArea.is_gecko) { //moz changes returns into <br> inside <pre> tags
+		s = s.replace(c[19], function(str){return str.replace(/<br \/>/g,"\n")});
+	}
+	s = s.replace(c[18], function(strn) { //skip pre and script tags
+	  strn = strn.replace(c[20], function(st,$1,$2,$3) { //exclude comments
+		string = $3.replace(/[\n\r]/gi, " ").replace(/\s+/gi," ").replace(c[14], function(str) {
+			if (str.match(c[16])) {
+				var s = "\n" + HTMLArea.__sindent + str;
+				// blocklevel openingtag - increase indent
+				HTMLArea.__sindent += HTMLArea.__sindentChar;
+				++HTMLArea.__nindent;
+				return s;
+			} else if (str.match(c[15])) {
+				// blocklevel closingtag - decrease indent
+				--HTMLArea.__nindent;
+				HTMLArea.__sindent = "";
+				for (var i=HTMLArea.__nindent;i>0;--i) {
+					HTMLArea.__sindent += HTMLArea.__sindentChar;
+				}
+				return "\n" + HTMLArea.__sindent + str;
+			} else if (str.match(c[17])) {
+				// singlet tag
+				return "\n" + HTMLArea.__sindent + str;
+			}
+			return str; // this won't actually happen
+		});
+		return $1 + string;
+	  });return strn;
+    });
+    //final cleanup
+    s = s.replace(/^\s*/,'').//strip leading whitespace
+        replace(/ +\n/g,'\n').//strip spaces at end of lines
+        replace(/[\r\n]+<\/script>/g,'\n</script>');//strip returns added into scripts
+    return s;
+};
+
+HTMLArea.getHTML = function(root, outputRoot, editor) {
+	var html = "";
+	var c = HTMLArea.RegExpCache;
+
+	if(root.nodeType == 11) {//document fragment
+	    //we can't get innerHTML from the root (type 11) node, so we 
+	    //copy all the child nodes into a new div and get innerHTML from the div
+	    var div = document.createElement("div");
+	    var temp = root.insertBefore(div,root.firstChild);
+	    for (j = temp.nextSibling; j; j = j.nextSibling) { 
+	    		temp.appendChild(j.cloneNode(true));
+	    }
+		html += temp.innerHTML.replace(c[23], function(strn) { //skip content inside script tags
+			strn = strn.replace(c[22], function(tag){
+				if(/^<[!\?]/.test(tag)) return tag; //skip comments and php tags
+				else return editor.cleanHTML(tag)});
+			return strn;
+		});
+
+	} else {
+
+		var root_tag = (root.nodeType == 1) ? root.tagName.toLowerCase() : ''; 
+		if (outputRoot) { //only happens with <html> tag in fullpage mode
+			html += "<" + root_tag;
+			var attrs = root.attributes; // strangely, this doesn't work in moz
+			for (i = 0; i < attrs.length; ++i) {
+				var a = attrs.item(i);
+				if (!a.specified) {
+				  continue;
+				}
+				var name = a.nodeName.toLowerCase();
+				var value = a.nodeValue;
+				html += " " + name + '="' + value + '"';
+			}
+			html += ">";
+		}
+		if(root_tag == "html") {
+			innerhtml = editor._doc.documentElement.innerHTML;
+		} else {
+			innerhtml = root.innerHTML;
+		}
+		//pass tags to cleanHTML() one at a time
+		//includes support for htmlRemoveTags config option
+		html += innerhtml.replace(c[23], function(strn) { //skip content inside script tags
+			strn = strn.replace(c[22], function(tag){
+				if(/^<[!\?]/.test(tag)) return tag; //skip comments and php tags
+				else if(!(editor.config.htmlRemoveTags && editor.config.htmlRemoveTags.test(tag.replace(/<([^\s>\/]+)/,'$1'))))
+					return editor.cleanHTML(tag);
+				else return ''});
+			return strn;
+		});
+		//IE drops  all </li> tags in a list except the last one
+		if(HTMLArea.is_ie) {
+			html = html.replace(/<li( [^>]*)?>/g,'</li><li$1>').
+				replace(/(<(ul|ol)[^>]*>)[\s\n]*<\/li>/g, '$1').
+				replace(/<\/li>([\s\n]*<\/li>)+/g, '<\/li>');
+		}
+		if(HTMLArea.is_gecko)
+			html = html.replace(/<br \/>\n$/, ''); //strip trailing <br> added by moz
+		if (outputRoot) {
+			html += "</" + root_tag + ">";
+		}
+		html = HTMLArea.indent(html);
+	};
+//	html = HTMLArea.htmlEncode(html);
+
+	return html;
+};
+
+//overload outwardHtml() to handle onclick suppression
+HTMLArea.prototype._origOutwardHtml = HTMLArea.prototype.outwardHtml;
+HTMLArea.prototype.outwardHtml = function(html) {
+	html = html.replace("onclick=\"try{if(document.designMode && document.designMode == 'on') return false;}catch(e){} window.open(", "onclick=\"window.open(");
+	html = this._origOutwardHtml(html);
+	return html;
+};

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/horizontal-rule.js
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/horizontal-rule.js?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/horizontal-rule.js (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/horizontal-rule.js Sun Oct  8 12:53:13 2006
@@ -0,0 +1,102 @@
+
+HorizontalRule._pluginInfo = {
+	name          : "HorizontalRule",
+	version       : "1.0",
+	developer     : "Nelson Bright",
+	developer_url : "http://www.brightworkweb.com/",
+	c_owner       : "Nelson Bright",
+	sponsor       : "BrightWork, Inc.",
+	sponsor_url   : "http://www.brightworkweb.com/",
+	license       : "htmlArea"
+};
+
+function HorizontalRule(editor) {
+    this.editor = editor;
+
+    var cfg = editor.config;
+	var toolbar = cfg.toolbar;
+	var self = this;
+        
+	cfg.registerButton({
+		id       : "edithorizontalrule",
+		tooltip  : this._lc("Insert/edit horizontal rule"),
+	//	image    : editor.imgURL("ed_hr.gif", "HorizontalRule"),
+		image    : [_editor_url + "images/ed_buttons_main.gif",6,0],
+		textMode : false,
+		action   : function(editor) {
+						self.buttonPress(editor);
+				   }
+	});
+
+	cfg.addToolbarElement("edithorizontalrule","inserthorizontalrule",0);
+}
+
+HorizontalRule.prototype._lc = function(string) {
+    return HTMLArea._lc(string, 'HorizontalRule');
+};
+
+HorizontalRule.prototype.buttonPress = function(editor) {
+	this.editor = editor;
+	this._editHorizontalRule();
+};
+
+HorizontalRule.prototype._editHorizontalRule = function(rule) {
+	editor = this.editor;
+	var sel = editor._getSelection();
+	var range = editor._createRange(sel);
+  var outparam = null;
+  if (typeof rule == "undefined") {
+    rule = editor.getParentElement();
+    if (rule && !/^hr$/i.test(rule.tagName))
+      rule = null;
+  }
+  if (rule) outparam = {
+    f_size        : rule.size,
+    f_width       : /%/.test(rule.width)? rule.width.substring(0,rule.width.length-1):rule.width,
+    f_widthUnit   : /%/.test(rule.width)?"%":"px",
+    f_color       : rule.color,
+    f_noshade     : rule.noShade,
+    f_align       : rule.align
+  };
+
+	editor._popupDialog("plugin://HorizontalRule/edit_horizontal_rule.html", function(param) {
+		if (!param) {	// user pressed Cancel
+			return false;
+		}
+		var hr = rule;
+		if (!hr) {
+		  var hrule = "<hr";
+			for (var field in param) {
+				var value = param[field];
+				if(value == "") continue;
+				switch (field) { 
+				case "f_width" :
+				if(param["f_widthUnit"]=="%")hrule += " width='" + value+"%'";
+					else hrule += " width='" + value +"'"; break;
+				case "f_size" :
+				hrule += " size='" + value +"'"; break;
+				case "f_align" :
+				hrule += " align='" + value +"'"; break;
+				case "f_color" :
+				hrule += " color='" + value +"'"; break;
+				case "f_noshade" :
+				hrule += (value)? " noshade":""; break;
+				}
+			}
+			hrule += ">";
+			editor.insertHTML(hrule);
+		} else {
+			for (var field in param) {
+			  var value = param[field];
+			  switch (field) {
+				  case "f_size"    : hr.size  = value; break;
+				  case "f_width"   : hr.width   = (param["f_widthUnit"]=="%")?value+"%":value; break;
+				  case "f_align"   : hr.align   = value; break;
+				  case "f_color"   : hr.color   = value; break;
+				  case "f_noshade" : hr.noShade = value; break;
+			  }
+			}
+		}
+	}, outparam);
+};
+	
\ No newline at end of file

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/de.js
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/de.js?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/de.js (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/de.js Sun Oct  8 12:53:13 2006
@@ -0,0 +1,21 @@
+// I18N constants
+// LANG: "de", ENCODING: UTF-8
+// translated: Udo Schmal (gocher), http://www.schaffrath-neuemedien.de/, udo.schmal@t-online.de
+{
+  "Insert/edit horizontal rule": "horizontale Linie einfügen/bearbeiten",
+  "Horizontal Rule": "Horizontale Linie",
+  "Layout": "Gestaltung",
+  "Width:": "Breite:",
+  "percent": "Prozent",
+  "pixels": "Pixel",
+  "Height:": "Höhe:",
+  "Alignment:": "Ausrichtung:",
+  "Left": "links",
+  "Center": "zentriert",
+  "Right": "rechts",
+  "Style": "Stil",
+  "Color:": "Farbe",
+  "No shading": "keine Schattierung",
+  "Note:": "Anmerkung",
+  "To select an existing horizontal rule, a double-click may be needed.": "Um eine horizontale Linie auszuwählen kann ein Doppelklick erforderlich sein."
+};

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/fr.js
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/fr.js?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/fr.js (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/fr.js Sun Oct  8 12:53:13 2006
@@ -0,0 +1,20 @@
+// I18N constants
+// LANG: "fr", ENCODING: UTF-8
+{
+  "Insert/edit horizontal rule": "Insérer une règle horizontale",
+  "Horizontal Rule": "Règle horizontale",
+  "Layout": "Layout",
+  "Width:": "Largeur",
+  "percent": "pourcent",
+  "pixels": "pixels",
+  "Height:": "Hauteur",
+  "Alignment:": "Alignement",
+  "Left": "Gauche",
+  "Center": "Centre",
+  "Right": "Droite",
+  "Style": "Style",
+  "Color:": "Couleur",
+  "No shading": "Pas d'ombre",
+  "Note:": "Note",
+  "To select an existing horizontal rule, a double-click may be needed.": "Pour sélectionner une règle horizontale, un double-clic peut être nécessaire."
+};
\ No newline at end of file

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/nb.js
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/nb.js?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/nb.js (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/nb.js Sun Oct  8 12:53:13 2006
@@ -0,0 +1,21 @@
+// I18N constants
+// LANG: "nb", ENCODING: UTF-8
+// translated: Kim Steinhaug, http://www.steinhaug.com/, kim@steinhaug.com
+{
+  "Insert/edit horizontal rule": "Sett inn/ rediger horisontal linje",
+  "Horizontal Rule": "Horisontal linje",
+  "Layout": "Oppsett",
+  "Width:": "Bredde:",
+  "percent": "prosent",
+  "pixels": "Piksel",
+  "Height:": "Høyde:",
+  "Alignment:": "Justering:",
+  "Left": "Venstre",
+  "Center": "Sentrert",
+  "Right": "Høyre",
+  "Style": "Stil",
+  "Color:": "Farge",
+  "No shading": "Ingen skygge",
+  "Note:": "Notat",
+  "To select an existing horizontal rule, a double-click may be needed.": "For å velge en horisontal linje kan det hende du må dobbeltklikke."
+};

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/no.js
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/no.js?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/no.js (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/lang/no.js Sun Oct  8 12:53:13 2006
@@ -0,0 +1,21 @@
+// I18N constants
+// LANG: "de", ENCODING: UTF-8
+// translated: Kim Steinhaug, http://www.steinhaug.com/, kim@steinhaug.com
+{
+  "Insert/edit horizontal rule": "Sett inn/ rediger horisontal linje",
+  "Horizontal Rule": "Horisontal linje",
+  "Layout": "Oppsett",
+  "Width:": "Bredde:",
+  "percent": "prosent",
+  "pixels": "Piksel",
+  "Height:": "Høyde:",
+  "Alignment:": "Justering:",
+  "Left": "Venstre",
+  "Center": "Sentrert",
+  "Right": "Høyre",
+  "Style": "Stil",
+  "Color:": "Farge",
+  "No shading": "Ingen skygge",
+  "Note:": "Notat",
+  "To select an existing horizontal rule, a double-click may be needed.": "For å velge en horisontal linje kan det hende du må dobbeltklikke."
+};

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/popups/edit_horizontal_rule.html
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/popups/edit_horizontal_rule.html?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/popups/edit_horizontal_rule.html (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HorizontalRule/popups/edit_horizontal_rule.html Sun Oct  8 12:53:13 2006
@@ -0,0 +1,159 @@
+<html>
+<head>
+  <title>Insert/Edit Horizontal Rule</title>
+  <link rel="stylesheet" type="text/css" href="../../../popups/popup.css" />
+  <script type="text/javascript" src="../../../popups/popup.js"></script>
+
+<script type="text/javascript">
+editor = window.opener.editor;
+
+function Init() {
+	__dlg_translate("HorizontalRule");
+	__dlg_init();
+	window.resizeTo(300,320);
+	var params = window.dialogArguments;
+	if(params) {
+		document.getElementById("f_size").value = params.f_size;
+		document.getElementById("f_width").value = params.f_width;
+		document.getElementById("f_widthUnit").value = params.f_widthUnit;
+		document.getElementById("f_align").value = params.f_align;
+		document.getElementById("f_color").value = params.f_color;
+		document.getElementById("hrpv").style.backgroundColor = params.f_color;
+		document.getElementById("f_noshade").checked = params.f_noshade;
+	}
+	document.getElementById("f_width").focus();
+}
+
+function onOK() {
+  var fields = ["f_size", "f_width", "f_widthUnit", "f_align", "f_color", "f_noshade"];
+  var param = new Object();
+  for (var i in fields) {
+    var id = fields[i];
+	var el = document.getElementById(id);
+    param[id] = (el.type == "checkbox") ? el.checked : el.value;
+  }
+  __dlg_close(param);
+  return false;
+}
+
+function onCancel() {
+  __dlg_close(null);
+  return false;
+}
+
+function selectColor(id1,id2,color) {
+	Dialog(editor.popupURL("select_color.html"), function(color){
+		if (color) {
+			document.getElementById(id1).style.backgroundColor="#"+color;
+			document.getElementById(id2).value="#"+color;
+		}
+	}, color);
+}
+function Dialog(url, action, init) {
+	Dialog.openModal(url, action, init);
+}
+Dialog.openModal = function(url, action, init) {
+	var dlg2 = window.open(url, "hadialog2",
+		  "toolbar=no,menubar=no,personalbar=no,width=10,height=10," +
+		  "scrollbars=no,resizable=yes,modal=yes,dependable=yes");
+	Dialog._modal = dlg2;
+	Dialog._arguments = init;
+	Dialog._return = function (val) {
+		if (val && action) {
+			action(val);
+		}
+		Dialog._modal = null;
+	};
+};
+
+</script>
+
+<style type="text/css">
+.buttonColor {
+  padding: 1px;
+  cursor: default;
+  border: 1px solid;
+  border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+
+.buttonColor-hilite {
+  border-color: #000;
+}
+
+.buttonColor .chooser {
+  height: 0.6em;
+  border: 1px solid;
+  padding: 0px 1em;
+  border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
+}
+
+.buttonColor .nocolor {
+  padding: 0px;
+  height: 0.6em;
+  border: 1px solid;
+  border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
+}
+.buttonColor .nocolor-hilite { background-color: #fff; color: #f00; }
+</style>
+<style type="text/css">
+@import url(../../../popups/popupstyle.css);
+</style>
+
+</head>
+
+<body class="dialog" onload="Init()">
+<div class="title">Horizontal Rule</div>
+<form action="" method="get">
+  <fieldset>
+  <legend>Layout</legend>
+    <div class="fr">Width:</div>
+    <input style="margin-right: 0.5em;" name="f_width" id="f_width" size="5" type="text">
+    <select style="margin-right: 0.5em;" name="f_widthUnit" id="f_widthUnit">
+      <option value="%">percent</option>
+      <option value="px">pixels</option>
+    </select>
+    <p />
+    <div class="fr">Height:</div>
+    <input style="margin-right: 0.5em;" name="f_size" id="f_size" size="5" type="text"> <span>pixels</span>
+    <p />
+    <div class="fr">Alignment:</div>
+    <select name="f_align" id="f_align">
+      <option value="left">Left</option>
+      <option value="center">Center</option>
+      <option value="right">Right</option>
+    </select>
+    <p />
+  </fieldset>
+  <fieldset>
+  <legend>Style</legend>
+    <div class="fr">Color:</div>
+    <table cellpadding="2" cellspacing="0" id="hrbtn" class="buttonColor">
+    <tr>
+      <td class="chooser" id="hrpv"
+          onMouseOver="document.getElementById('hrbtn').style.borderColor='black'"
+          onMouseOut="document.getElementById('hrbtn').style.borderColor='ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight'"
+          onClick="selectColor('hrpv','f_color',document.getElementById('f_color').value)">&nbsp;</td>
+      <td class="nocolor" id="hrclr"
+          onMouseOver="document.getElementById('hrclr').style.color='#f00'"
+          onMouseOut="document.getElementById('hrclr').style.color='#000'"
+          onClick="document.getElementById('f_color').value='';	document.getElementById('hrpv').style.backgroundColor=''">&#x00d7;</td>
+    </tr>
+    </table>
+    <p />
+    <div class="fr"> </div>
+    <input type="hidden" name="f_color" id="f_color">
+    <input type="checkbox" name="f_noshade" id="f_noshade" value="noshade">
+    <span>No shading</span>
+    <p />
+  </fieldset>
+  <fieldset>
+	  <legend>Note:</legend>
+    <span>To select an existing horizontal rule, a double-click may be needed.</span>
+  </fieldset>
+<div id="buttons">
+  <button type="submit" name="ok" onclick="return onOK();">OK</button>
+  <button type="button" name="cancel" onclick="return onCancel();">Cancel</button>
+</div>
+</form>
+</body>
+</html>
\ No newline at end of file

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/README
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/README?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/README (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/README Sun Oct  8 12:53:13 2006
@@ -0,0 +1,104 @@
+// Plugin for htmlArea to run code through the server's HTML Tidy
+// By Adam Wright, for The University of Western Australia
+//
+//   Email:      zeno@ucc.gu.uwa.edu.au
+//   Homepage:   http://blog.hipikat.org/
+//
+// Distributed under the same terms as HTMLArea itself.
+// This notice MUST stay intact for use (see license.txt).
+//
+// Version: 0.5
+// Released to the outside world: 04/03/04
+
+
+HtmlTidy is a plugin for the popular cross-browser TTY WYSIWYG editor,
+htmlArea (http://www.interactivetools.com/products/htmlarea/). HtmlTidy
+basically queries HTML Tidy (http://tidy.sourceforge.net/) on the
+server side, getting it to make-html-nice, instead of relying on masses
+of javascript, which the client would have to download.
+
+Hi, this is a quick explanation of how to install HtmlTidy. Much better
+documentation is probably required, and you're welcome to write it :)
+
+
+* The HtmlTidy directory you should have found this file in should
+  include the following:
+
+  - README
+        This file, providing help installing the plugin.
+
+  - html-tidy-config.cfg
+        This file contains the configuration options HTML Tidy uses to
+        clean html, and can be modified to suit your organizations
+        requirements.
+
+  - html-tidy-logic.php
+        This is the php script, which is queried with dirty html and is
+        responsible for invoking HTML Tidy, getting nice new html and
+        returning it to the client.
+
+  - html-tidy.js
+        The main htmlArea plugin, providing functionality to tidy html
+        through the htmlArea interface.
+
+  - htmlarea.js.onmode_event.diff
+        At the time of publishing, an extra event handler was required
+        inside the main htmlarea.js file. htmlarea.js may be patched
+        against this file to make the changes reuquired, but be aware
+        that the event handler may either now be in the core or
+        htmlarea.js may have changed enough to invalidate the patch.
+	
+	UPDATE: now it exists in the official htmlarea.js; applying
+	this patch is thus no longer necessary.
+
+  - img/html-tidy.gif
+        The HtmlTidy icon, for the htmlArea toolbar. Created by Dan
+        Petty for The University of Western Australia.
+
+  - lang/en.js
+        English language file. Add your own language files here and
+        please contribute back into the htmlArea community!
+
+  The HtmlArea directory should be extracted to your htmlarea/plugins/
+  directory.
+
+
+* Make sure the onMode event handler mentioned above, regarding
+  htmlarea.js.onmode_event.diff, exists in your htmlarea.js
+
+
+* html-tidy-logic.php should be executable, and your web server should
+  be configured to execute php scripts in the directory
+  html-tidy-logic.php exists in.
+
+
+* HTML Tidy needs to be installed on your server, and 'tidy' should be
+  an alias to it, lying in the PATH known to the user executing such
+  web scripts.
+
+
+* In your htmlArea configuration, do something like this:
+
+    HTMLArea.loadPlugin("HtmlTidy");
+
+    editor = new HTMLArea("doc");
+    editor.registerPlugin("HtmlTidy");
+
+
+* Then, in your htmlArea toolbar configuration, use:
+
+    - "HT-html-tidy"
+        This will create the 'tidy broom' icon on the toolbar, which
+        will attempt to tidy html source when clicked, and;
+
+    - "HT-auto-tidy"
+        This will create an "Auto Tidy" / "Don't Tidy" dropdown, to
+        select whether the source should be tidied automatically when
+        entering source view. On by default, if you'd like it otherwise
+        you can do so programatically after generating the toolbar :)
+        (Or just hack it to be otherwise...)
+    
+
+Thank you.
+
+Any bugs you find can be emailed to zeno@ucc.gu.uwa.edu.au

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy-config.cfg
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy-config.cfg?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy-config.cfg (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy-config.cfg Sun Oct  8 12:53:13 2006
@@ -0,0 +1,29 @@
+// Default configuration file for the htmlArea, HtmlTidy plugin
+// By Adam Wright, for The University of Western Australia
+//
+// Evertything you always wanted to know about HTML Tidy *
+// can be found at http://tidy.sourceforge.net/, and a
+// quick reference to the configuration options exists at
+// http://tidy.sourceforge.net/docs/quickref.html
+//
+// * But were afraid to ask
+//
+// Distributed under the same terms as HTMLArea itself.
+// This notice MUST stay intact for use (see license.txt).
+
+word-2000: yes
+clean: yes
+drop-font-tags: no
+doctype: auto
+drop-empty-paras: yes
+drop-proprietary-attributes: yes
+enclose-block-text: yes
+enclose-text: yes
+escape-cdata: yes
+logical-emphasis: yes
+indent: auto
+indent-spaces: 2
+break-before-br: yes
+output-xhtml: yes
+
+force-output: yes

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy-logic.php
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy-logic.php?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy-logic.php (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy-logic.php Sun Oct  8 12:53:13 2006
@@ -0,0 +1,79 @@
+<?php 
+##
+##  Plugin for htmlArea, to run code through the server's HTML Tidy
+##   By Adam Wright, for The University of Western Australia
+##    This is the server-side script, which dirty code is run through.
+##
+##  Distributed under the same terms as HTMLArea itself.
+##  This notice MUST stay intact for use (see license.txt).
+##
+
+	// Get the original source
+	$source = $_POST['htisource_name'];
+	$source = stripslashes($source);
+  $cwd = str_replace("\\","/",getcwd())."/";
+  
+	// Open a tidy process - I hope it's installed!
+	$descriptorspec = array(
+		0 => array("pipe", "r"),
+		1 => array("pipe", "w")
+	);
+	$process = @proc_open("tidy -utf8 -config {$cwd}html-tidy-config.cfg", $descriptorspec, $pipes);
+
+
+	// Make sure the program started and we got the hooks...
+	// Either way, get some source code into $source
+	if (is_resource($process)) {
+
+		// Feed untidy source into the stdin
+		fwrite($pipes[0], $source);
+		fclose($pipes[0]);
+
+		// Read clean source out to the browser
+		while (!feof($pipes[1])) {
+			//echo fgets($pipes[1], 1024);
+			$newsrc .= fgets($pipes[1], 1024);
+		}
+		fclose($pipes[1]);
+
+		// Clean up after ourselves
+		proc_close($process);
+
+	} else {
+    /* Use tidy if it's available from PECL */
+    if( function_exists('tidy_parse_string') )
+    {
+      $tempsrc = tidy_parse_string($source);
+      tidy_clean_repair();
+      $newsrc = tidy_get_output();
+    }
+    else
+    {
+      // Better give them back what they came with, so they don't lose it all...
+      $newsrc = "<body>\n" .$source. "\n</body>";
+    }
+	}
+
+	// Split our source into an array by lines
+	$srcLines = preg_split("/\n/",$newsrc,-1,PREG_SPLIT_NO_EMPTY);
+
+	// Get only the lines between the body tags
+	$startLn = 0;
+	while ( strpos( $srcLines[$startLn++], '<body' ) === false && $startLn < sizeof($srcLines) );
+	$endLn = $startLn;
+	while ( strpos( $srcLines[$endLn++], '</body' ) === false && $endLn < sizeof($srcLines) );
+
+	$srcLines = array_slice( $srcLines, $startLn, ($endLn - $startLn - 1) );
+
+	// Create a set of javascript code to compile a new source string
+	foreach ($srcLines as $line) {
+		$jsMakeSrc .= "\tns += '" . str_replace("'","\'",$line) . "\\n';\n";
+	}
+if(!sizeof($srcLines)) {
+    echo "alert(HTMLArea._lc('Tidy failed.  Check your HTML for syntax errors.', 'HtmlTidy'));\n";
+} else {
+?>
+var ns="";
+<?php echo $jsMakeSrc; ?>
+editor.setHTML(ns);
+<? } ?>
\ No newline at end of file

Propchange: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy-logic.php
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy.js
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy.js?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy.js (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/html-tidy.js Sun Oct  8 12:53:13 2006
@@ -0,0 +1,105 @@
+// Plugin for htmlArea to run code through the server's HTML Tidy
+// By Adam Wright, for The University of Western Australia
+//
+// Distributed under the same terms as HTMLArea itself.
+// This notice MUST stay intact for use (see license.txt).
+
+function HtmlTidy(editor) {
+	this.editor = editor;
+
+	var cfg = editor.config;
+	var bl = HtmlTidy.btnList;
+	var self = this;
+
+	this.onMode = this.__onMode;
+
+	// register the toolbar buttons provided by this plugin
+	var toolbar = [];
+	for (var i = 0; i < bl.length; ++i) {
+		var btn = bl[i];
+		if (btn == "html-tidy") {
+			var id = "HT-html-tidy";
+			cfg.registerButton(id, this._lc("HTML Tidy"), editor.imgURL(btn[0] + ".gif", "HtmlTidy"), true,
+					   function(editor, id) {
+						   // dispatch button press event
+						   self.buttonPress(editor, id);
+					   }, btn[1]);
+			toolbar.push(id);
+		} else if (btn == "html-auto-tidy") {
+            var btnTxt = [this._lc("Auto-Tidy"), this._lc("Don't Tidy")];
+            var optionItems = new Object();
+            optionItems[btnTxt[0]] = "auto";
+            optionItems[btnTxt[1]] = "noauto";
+			var ht_class = {
+				id	: "HT-auto-tidy",
+				options	: optionItems,
+				action	: function (editor) { self.__onSelect(editor, this); },
+				refresh	: function (editor) { },
+				context	: "body"
+			};
+			cfg.registerDropdown(ht_class);
+		}
+	}
+
+	for (var i in toolbar) {
+		cfg.toolbar[0].push(toolbar[i]);
+	}
+}
+
+HtmlTidy._pluginInfo = {
+	name          : "HtmlTidy",
+	version       : "1.0",
+	developer     : "Adam Wright",
+	developer_url : "http://blog.hipikat.org/",
+	sponsor       : "The University of Western Australia",
+	sponsor_url   : "http://www.uwa.edu.au/",
+	license       : "htmlArea"
+};
+
+HtmlTidy.prototype._lc = function(string) {
+    return HTMLArea._lc(string, 'HtmlTidy');
+};
+
+HtmlTidy.prototype.__onSelect = function(editor, obj) {
+	// Get the toolbar element object
+	var elem = editor._toolbarObjects[obj.id].element;
+
+	// Set our onMode event appropriately
+	if (elem.value == "auto")
+		this.onMode = this.__onMode;
+	else
+		this.onMode = null;
+};
+
+HtmlTidy.prototype.__onMode = function(mode) {
+	if ( mode == "textmode" ) {
+		this.buttonPress(this.editor, "HT-html-tidy");
+	}
+};
+
+HtmlTidy.btnList = [
+		    null, // separator
+		    ["html-tidy"],
+		    ["html-auto-tidy"]
+];
+
+HtmlTidy.prototype.buttonPress = function(editor, id) {
+
+	switch (id)
+  {
+    case "HT-html-tidy":
+    {
+      var oldhtml = editor.getHTML();
+      if(oldhtml=="") break; //don't clean empty text
+      // Ask the server for some nice new html, based on the old...
+      HTMLArea._postback(_editor_url + 'plugins/HtmlTidy/html-tidy-logic.php', {'htisource_name' : oldhtml},
+                            function(javascriptResponse) { eval(javascriptResponse) });
+    }
+		break;
+	}
+};
+
+HtmlTidy.prototype.processTidied = function(newSrc) {
+	editor = this.editor;
+	editor.setHTML(newSrc);
+};
\ No newline at end of file

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/img/html-tidy.gif
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/img/html-tidy.gif?view=auto&rev=454197
==============================================================================
Binary file - no diff available.

Propchange: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/img/html-tidy.gif
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/de.js
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/de.js?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/de.js (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/de.js Sun Oct  8 12:53:13 2006
@@ -0,0 +1,7 @@
+// I18N constants
+// LANG: "de", ENCODING: UTF-8
+// Author: Raimund Meyer ray@ray-of-light.org
+{
+  "HTML Tidy": "HTML Tidy",
+	"Tidy failed.  Check your HTML for syntax errors.": "Tidy fehlgeschlagen. Prüfen Sie den HTML Code nach Syntax-Fehlern."  
+};

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/fr.js
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/fr.js?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/fr.js (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/fr.js Sun Oct  8 12:53:13 2006
@@ -0,0 +1,8 @@
+// I18N constants
+// LANG: "fr", ENCODING: UTF-8
+{
+  "HTML Tidy": "HTML Tidy",
+  "Auto-Tidy": "Tidy automatique",
+  "Don't Tidy": "Tidy non utilisé",
+  "Tidy failed.  Check your HTML for syntax errors.": "Tidy a échoué. Vérifiez votre HTML for des erreurs de syntaxe"
+};
\ No newline at end of file

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/nb.js
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/nb.js?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/nb.js (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/nb.js Sun Oct  8 12:53:13 2006
@@ -0,0 +1,7 @@
+// I18N constants
+// LANG: "nb", ENCODING: UTF-8
+// translated: Kim Steinhaug, http://www.steinhaug.com/, kim@steinhaug.com
+{
+  "HTML Tidy": "HTML Tidy",
+  "Tidy failed.  Check your HTML for syntax errors.": "Tidy feilet. Sjekk HTML koden for syntaksfeil."
+};
\ No newline at end of file

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/nl.js
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/nl.js?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/nl.js (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/nl.js Sun Oct  8 12:53:13 2006
@@ -0,0 +1,5 @@
+// I18N constants
+// LANG: "nl", ENCODING: UTF-8
+{
+  "HT-html-tidy": "HTML opschonen"
+};
\ No newline at end of file

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/no.js
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/no.js?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/no.js (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/HtmlTidy/lang/no.js Sun Oct  8 12:53:13 2006
@@ -0,0 +1,7 @@
+// I18N constants
+// LANG: "no", ENCODING: UTF-8
+// translated: Kim Steinhaug, http://www.steinhaug.com/, kim@steinhaug.com
+{
+  "HTML Tidy": "HTML Tidy",
+  "Tidy failed.  Check your HTML for syntax errors.": "Tidy feilet. Sjekk HTML koden for syntaksfeil."
+};
\ No newline at end of file

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/Files.php
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/Files.php?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/Files.php (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/Files.php Sun Oct  8 12:53:13 2006
@@ -0,0 +1,269 @@
+<?php
+/**
+ * File Utilities.
+ * @author $Author: Wei Zhuo $
+ * @version $Id: Files.php 26 2004-03-31 02:35:21Z Wei Zhuo $
+ * @package ImageManager
+ */
+
+define('FILE_ERROR_NO_SOURCE', 100);
+define('FILE_ERROR_COPY_FAILED', 101);
+define('FILE_ERROR_DST_DIR_FAILED', 102);
+define('FILE_COPY_OK', 103);
+
+/**
+ * File Utilities
+ * @author $Author: Wei Zhuo $
+ * @version $Id: Files.php 26 2004-03-31 02:35:21Z Wei Zhuo $
+ * @package ImageManager
+ * @subpackage files
+ */
+class Files 
+{
+	
+	/**
+	 * Copy a file from source to destination. If unique == true, then if
+	 * the destination exists, it will be renamed by appending an increamenting 
+	 * counting number.
+	 * @param string $source where the file is from, full path to the files required
+	 * @param string $destination_file name of the new file, just the filename
+	 * @param string $destination_dir where the files, just the destination dir,
+	 * e.g., /www/html/gallery/
+	 * @param boolean $unique create unique destination file if true.
+	 * @return string the new copied filename, else error if anything goes bad.
+	 */
+	function copyFile($source, $destination_dir, $destination_file, $unique=true) 
+	{
+		if(!(file_exists($source) && is_file($source))) 
+			return FILE_ERROR_NO_SOURCE;
+
+		$destination_dir = Files::fixPath($destination_dir);
+
+		if(!is_dir($destination_dir)) 
+			Return FILE_ERROR_DST_DIR_FAILED;
+
+		$filename = Files::escape($destination_file);
+
+		if($unique) 
+		{
+			$dotIndex = strrpos($destination_file, '.');
+			$ext = '';
+			if(is_int($dotIndex)) 
+			{
+				$ext = substr($destination_file, $dotIndex);
+				$base = substr($destination_file, 0, $dotIndex);
+			}
+			$counter = 0;
+			while(is_file($destination_dir.$filename)) 
+			{
+				$counter++;
+				$filename = $base.'_'.$counter.$ext;
+			}
+		}
+
+		if (!copy($source, $destination_dir.$filename))
+			return FILE_ERROR_COPY_FAILED;
+		
+		//verify that it copied, new file must exists
+		if (is_file($destination_dir.$filename))
+			Return $filename;
+		else
+			return FILE_ERROR_COPY_FAILED;
+	}
+
+	/**
+	 * Create a new folder.
+	 * @param string $newFolder specifiy the full path of the new folder.
+	 * @return boolean true if the new folder is created, false otherwise.
+	 */
+	function createFolder($newFolder) 
+	{
+		mkdir ($newFolder, 0777);
+		return chmod($newFolder, 0777);
+	}
+
+
+	/**
+	 * Escape the filenames, any non-word characters will be
+	 * replaced by an underscore.
+	 * @param string $filename the orginal filename
+	 * @return string the escaped safe filename
+	 */
+	function escape($filename)
+	{
+		Return preg_replace('/[^\w\._]/', '_', $filename);
+	}
+
+	/**
+	 * Delete a file.
+	 * @param string $file file to be deleted
+	 * @return boolean true if deleted, false otherwise.
+	 */
+	function delFile($file) 
+	{
+		if(is_file($file)) 
+			Return unlink($file);
+		else
+			Return false;
+	}
+
+	/**
+	 * Delete folder(s), can delete recursively.
+	 * @param string $folder the folder to be deleted.
+	 * @param boolean $recursive if true, all files and sub-directories
+	 * are delete. If false, tries to delete the folder, can throw
+	 * error if the directory is not empty.
+	 * @return boolean true if deleted.
+	 */
+	function delFolder($folder, $recursive=false) 
+	{
+		$deleted = true;
+		if($recursive) 
+		{
+			$d = dir($folder);
+			while (false !== ($entry = $d->read())) 
+			{
+				if ($entry != '.' && $entry != '..')
+				{
+					$obj = Files::fixPath($folder).$entry;
+					//var_dump($obj);
+					if (is_file($obj))
+					{
+						$deleted &= Files::delFile($obj);					
+					}
+					else if(is_dir($obj))
+					{
+						$deleted &= Files::delFolder($obj, $recursive);
+					}
+					
+				}
+			}
+			$d->close();
+
+		}
+
+		//$folder= $folder.'/thumbs';
+		//var_dump($folder);
+		if(is_dir($folder)) 
+			$deleted &= rmdir($folder);
+		else
+			$deleted &= false;
+
+		Return $deleted;
+	}
+
+	/**
+	 * Append a / to the path if required.
+	 * @param string $path the path
+	 * @return string path with trailing /
+	 */
+	function fixPath($path) 
+	{
+		//append a slash to the path if it doesn't exists.
+		if(!(substr($path,-1) == '/'))
+			$path .= '/';
+		Return $path;
+	}
+
+	/**
+	 * Concat two paths together. Basically $pathA+$pathB
+	 * @param string $pathA path one
+	 * @param string $pathB path two
+	 * @return string a trailing slash combinded path.
+	 */
+	function makePath($pathA, $pathB) 
+	{
+		$pathA = Files::fixPath($pathA);
+		if(substr($pathB,0,1)=='/')
+			$pathB = substr($pathB,1);
+		Return Files::fixPath($pathA.$pathB);
+	}
+
+	/**
+	 * Similar to makePath, but the second parameter
+	 * is not only a path, it may contain say a file ending.
+	 * @param string $pathA the leading path
+	 * @param string $pathB the ending path with file
+	 * @return string combined file path.
+	 */
+	function makeFile($pathA, $pathB) 
+	{		
+		$pathA = Files::fixPath($pathA);
+		if(substr($pathB,0,1)=='/')
+			$pathB = substr($pathB,1);
+		
+		Return $pathA.$pathB;
+	}
+
+	
+	/**
+	 * Format the file size, limits to Mb.
+	 * @param int $size the raw filesize
+	 * @return string formated file size.
+	 */
+	function formatSize($size) 
+	{
+		if($size < 1024) 
+			return $size.' bytes';	
+		else if($size >= 1024 && $size < 1024*1024) 
+			return sprintf('%01.2f',$size/1024.0).' KB';
+		else
+			return sprintf('%01.2f',$size/(1024.0*1024)).' MB';
+	}
+
+	/**
+	 * Returns size of a directory, with all file & subdirectory
+	 * sizes added up
+	 * @param string dir path
+	 * @return int
+	 */
+	function dirSize($dirName = '.')
+	{
+		$dir  = dir($dirName);
+		$size = 0;
+
+		while ($file = $dir->read()) {
+			if ($file != '.' && $file != '..')
+			{
+				if (is_dir("$dirName$file"))
+				{
+					$size += Files::dirSize($dirName . '/' . $file);
+				}
+				else
+				{
+					$size += filesize($dirName . '/' . $file);
+				}
+			}
+		}
+		$dir->close();
+		return $size;
+	}
+	
+	/**
+	 * Renames file, preserving its directory and extension
+	 * @param string $oldPath path to the old existing file
+	 * @param string new filename (just the name, without path or extension)
+	 * @author Krzysztof Kotowicz <ko...@webworkers.pl>
+	 */
+	function renameFile($oldPath, $newName) {
+
+		if(!(file_exists($oldPath) && is_file($oldPath)))
+			return FILE_ERROR_NO_SOURCE;
+
+		$oldFileParts = pathinfo($oldPath);
+
+		$newPath = $oldFileParts['dirname'] . '/'
+				   . $newName
+				   . (!empty($oldFileParts['extension']) ? '.' . $oldFileParts['extension'] : '');
+
+		if (file_exists($newPath))
+			return false;
+
+		if (!rename($oldPath, $newPath))
+			return FILE_ERROR_COPY_FAILED;
+
+	}
+
+}
+
+?>

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/GD.php
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/GD.php?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/GD.php (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/GD.php Sun Oct  8 12:53:13 2006
@@ -0,0 +1,503 @@
+<?php
+/***********************************************************************
+** Title.........:  GD Driver
+** Version.......:  1.0
+** Author........:  Xiang Wei ZHUO <we...@zhuo.org>
+** Filename......:  GD.php
+** Last changed..:  30 Aug 2003 
+** Notes.........:  Orginal is from PEAR
+**/
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Peter Bowyer <pe...@mapledesign.co.uk>                      |
+// |          Alan Knowles <al...@akbkhome.com>                            |
+// +----------------------------------------------------------------------+
+//
+//    Usage :
+//    $img    = new Image_Transform_GD();
+//    $angle  = -78;
+//    $img->load('magick.png');
+//
+//    if($img->rotate($angle,array('autoresize'=>true,'color_mask'=>array(255,0,0)))){
+//        $img->addText(array('text'=>"Rotation $angle",'x'=>0,'y'=>100,'font'=>'/usr/share/fonts/default/TrueType/cogb____.ttf'));
+//        $img->display();
+//    } else {
+//        echo "Error";
+//    }
+//
+//
+// $Id: GD.php 26 2004-03-31 02:35:21Z Wei Zhuo $
+//
+// Image Transformation interface using the GD library
+//
+
+require_once "Transform.php";
+
+Class Image_Transform_Driver_GD extends Image_Transform
+{
+    /**
+     * Holds the image file for manipulation
+     */
+    var $imageHandle = '';
+
+    /**
+     * Holds the original image file
+     */
+    var $old_image = '';
+
+    /**
+     * Check settings
+     *
+     * @return mixed true or  or a PEAR error object on error
+     *
+     * @see PEAR::isError()
+     */
+    function Image_Transform_GD()
+    {
+        return;
+    } // End function Image
+
+    /**
+     * Load image
+     *
+     * @param string filename
+     *
+     * @return mixed none or a PEAR error object on error
+     * @see PEAR::isError()
+     */
+    function load($image)
+    {
+        $this->uid = md5($_SERVER['REMOTE_ADDR']);
+        $this->image = $image;
+        $this->_get_image_details($image);
+        $functionName = 'ImageCreateFrom' . $this->type;
+		if(function_exists($functionName))
+		{
+			$this->imageHandle = $functionName($this->image);
+		}
+    } // End load
+
+    /**
+     * addText
+     *
+     * @param   array   options     Array contains options
+     *                              array(
+     *                                  'text'  The string to draw
+     *                                  'x'     Horizontal position
+     *                                  'y'     Vertical Position
+     *                                  'Color' Font color
+     *                                  'font'  Font to be used
+     *                                  'size'  Size of the fonts in pixel
+     *                                  'resize_first'  Tell if the image has to be resized
+     *                                                  before drawing the text
+     *                              )
+     *
+     * @return none
+     * @see PEAR::isError()
+     */
+    function addText($params)
+    {
+        $default_params = array(
+                                'text' => 'This is Text',
+                                'x' => 10,
+                                'y' => 20,
+                                'color' => array(255,0,0),
+                                'font' => 'Arial.ttf',
+                                'size' => '12',
+                                'angle' => 0,
+                                'resize_first' => false // Carry out the scaling of the image before annotation?  Not used for GD
+                                );
+        $params = array_merge($default_params, $params);
+        extract($params);
+
+        if( !is_array($color) ){
+            if ($color[0]=='#'){
+                $this->colorhex2colorarray( $color );
+            } else {
+                include_once('Image/Transform/Driver/ColorsDefs.php');
+                $color = isset($colornames[$color])?$colornames[$color]:false;
+            }
+        }
+
+        $c = imagecolorresolve ($this->imageHandle, $color[0], $color[1], $color[2]);
+
+        if ('ttf' == substr($font, -3)) {
+            ImageTTFText($this->imageHandle, $size, $angle, $x, $y, $c, $font, $text);
+        } else {
+            ImagePSText($this->imageHandle, $size, $angle, $x, $y, $c, $font, $text);
+        }
+        return true;
+    } // End addText
+
+
+    /**
+     * Rotate image by the given angle
+     * Uses a fast rotation algorythm for custom angles
+     * or lines copy for multiple of 90 degrees
+     *
+     * @param int       $angle      Rotation angle
+     * @param array     $options    array(  'autoresize'=>true|false,
+     *                                      'color_mask'=>array(r,g,b), named color or #rrggbb
+     *                                   )
+     * @author Pierre-Alain Joye
+     * @return mixed none or a PEAR error object on error
+     * @see PEAR::isError()
+     */
+    function rotate($angle, $options=null)
+    {
+        if(function_exists('imagerotate')) {
+            $white = imagecolorallocate ($this->imageHandle, 255, 255, 255);
+			$this->imageHandle = imagerotate($this->imageHandle, $angle, $white);
+            return true;
+        }
+
+        if ( $options==null ){
+            $autoresize = true;
+            $color_mask = array(255,255,0);
+        } else {
+            extract( $options );
+        }
+
+        while ($angle <= -45) {
+            $angle  += 360;
+        }
+        while ($angle > 270) {
+            $angle  -= 360;
+        }
+
+        $t      = deg2rad($angle);
+
+        if( !is_array($color_mask) ){
+            if ($color[0]=='#'){
+                $this->colorhex2colorarray( $color_mask );
+            } else {
+                include_once('Image/Transform/Driver/ColorDefs.php');
+                $color = isset($colornames[$color_mask])?$colornames[$color_mask]:false;
+            }
+        }
+
+        // Do not round it, too much lost of quality
+        $cosT   = cos($t);
+        $sinT   = sin($t);
+
+        $img    =& $this->imageHandle;
+
+        $width  = $max_x  = $this->img_x;
+        $height = $max_y  = $this->img_y;
+        $min_y  = 0;
+        $min_x  = 0;
+
+        $x1     = round($max_x/2,0);
+        $y1     = round($max_y/2,0);
+
+        if ( $autoresize ){
+            $t      = abs($t);
+            $a      = round($angle,0);
+            switch((int)($angle)){
+                case 0:
+                        $width2     = $width;
+                        $height2    = $height;
+                    break;
+                case 90:
+                        $width2     = $height;
+                        $height2    = $width;
+                    break;
+                case 180:
+                        $width2     = $width;
+                        $height2    = $height;
+                    break;
+                case 270:
+                        $width2     = $height;
+                        $height2    = $width;
+                    break;
+                default:
+                    $width2     = (int)(abs(sin($t) * $height + cos($t) * $width));
+                    $height2    = (int)(abs(cos($t) * $height+sin($t) * $width));
+            }
+
+            $width2     -= $width2%2;
+            $height2    -= $height2%2;
+
+            $d_width    = abs($width - $width2);
+            $d_height   = abs($height - $height2);
+            $x_offset   = $d_width/2;
+            $y_offset   = $d_height/2;
+            $min_x2     = -abs($x_offset);
+            $min_y2     = -abs($y_offset);
+            $max_x2     = $width2;
+            $max_y2     = $height2;
+        }
+
+        $img2   = @imagecreate($width2,$height2);
+
+        if ( !is_resource($img2) ){
+            return false;/*PEAR::raiseError('Cannot create buffer for the rotataion.',
+                                null, PEAR_ERROR_TRIGGER, E_USER_NOTICE);*/
+        }
+
+        $this->img_x = $width2;
+        $this->img_y = $height2;
+
+
+        imagepalettecopy($img2,$img);
+
+        $mask   = imagecolorresolve($img2,$color_mask[0],$color_mask[1],$color_mask[2]);
+
+        // use simple lines copy for axes angles
+        switch((int)($angle)){
+            case 0:
+                imagefill ($img2, 0, 0,$mask);
+                for ($y=0; $y < $max_y; $y++) {
+                    for ($x = $min_x; $x < $max_x; $x++){
+                        $c  = @imagecolorat ( $img, $x, $y);
+                        imagesetpixel($img2,$x+$x_offset,$y+$y_offset,$c);
+                    }
+                }
+                break;
+            case 90:
+                imagefill ($img2, 0, 0,$mask);
+                for ($x = $min_x; $x < $max_x; $x++){
+                    for ($y=$min_y; $y < $max_y; $y++) {
+                        $c  = imagecolorat ( $img, $x, $y);
+                        imagesetpixel($img2,$max_y-$y-1,$x,$c);
+                    }
+                }
+                break;
+            case 180:
+                imagefill ($img2, 0, 0,$mask);
+                for ($y=0; $y < $max_y; $y++) {
+                    for ($x = $min_x; $x < $max_x; $x++){
+                        $c  = @imagecolorat ( $img, $x, $y);
+                        imagesetpixel($img2, $max_x2-$x-1, $max_y2-$y-1, $c);
+                    }
+                }
+                break;
+            case 270:
+                imagefill ($img2, 0, 0,$mask);
+                for ($y=0; $y < $max_y; $y++) {
+                    for ($x = $max_x; $x >= $min_x; $x--){
+                        $c  = @imagecolorat ( $img, $x, $y);
+                        imagesetpixel($img2,$y,$max_x-$x-1,$c);
+                    }
+                }
+                break;
+            // simple reverse rotation algo
+            default:
+                $i=0;
+                for ($y = $min_y2; $y < $max_y2; $y++){
+
+                    // Algebra :)
+                    $x2 = round((($min_x2-$x1) * $cosT) + (($y-$y1) * $sinT + $x1),0);
+                    $y2 = round((($y-$y1) * $cosT - ($min_x2-$x1) * $sinT + $y1),0);
+
+                    for ($x = $min_x2; $x < $max_x2; $x++){
+
+                        // Check if we are out of original bounces, if we are
+                        // use the default color mask
+                        if ( $x2>=0 && $x2<$max_x && $y2>=0 && $y2<$max_y ){
+                            $c  = imagecolorat ( $img, $x2, $y2);
+                        } else {
+                            $c  = $mask;
+                        }
+                        imagesetpixel($img2,$x+$x_offset,$y+$y_offset,$c);
+
+                        // round verboten!
+                        $x2  += $cosT;
+                        $y2  -= $sinT;
+                    }
+                }
+                break;
+        }
+        $this->old_image    = $this->imageHandle;
+        $this->imageHandle  =  $img2;
+        return true;
+    }
+
+
+   /**
+    * Resize Action
+    *
+    * For GD 2.01+ the new copyresampled function is used
+    * It uses a bicubic interpolation algorithm to get far
+    * better result.
+    *
+    * @param int  $new_x new width
+    * @param int  $new_y new height
+    *
+    * @return true on success or pear error
+    * @see PEAR::isError()
+    */
+    function _resize($new_x, $new_y) {
+        if ($this->resized === true) {
+            return false; /*PEAR::raiseError('You have already resized the image without saving it.  Your previous resizing will be overwritten', null, PEAR_ERROR_TRIGGER, E_USER_NOTICE);*/
+        }
+        if(function_exists('ImageCreateTrueColor')){
+            $new_img =ImageCreateTrueColor($new_x,$new_y);
+        } else {
+            $new_img =ImageCreate($new_x,$new_y);
+        }
+        if(function_exists('ImageCopyResampled')){
+            ImageCopyResampled($new_img, $this->imageHandle, 0, 0, 0, 0, $new_x, $new_y, $this->img_x, $this->img_y);
+        } else {
+            ImageCopyResized($new_img, $this->imageHandle, 0, 0, 0, 0, $new_x, $new_y, $this->img_x, $this->img_y);
+        }
+        $this->old_image = $this->imageHandle;
+        $this->imageHandle = $new_img;
+        $this->resized = true;
+
+        $this->new_x = $new_x;
+        $this->new_y = $new_y;
+        return true;
+    }
+
+    /**
+     * Crop the image
+     *
+     * @param int $crop_x left column of the image
+     * @param int $crop_y top row of the image
+     * @param int $crop_width new cropped image width
+     * @param int $crop_height new cropped image height
+     */
+    function crop($new_x, $new_y, $new_width, $new_height) 
+    {
+        if(function_exists('ImageCreateTrueColor')){
+            $new_img =ImageCreateTrueColor($new_width,$new_height);
+        } else {
+            $new_img =ImageCreate($new_width,$new_height);
+        }
+        if(function_exists('ImageCopyResampled')){
+            ImageCopyResampled($new_img, $this->imageHandle, 0, 0, $new_x, $new_y,$new_width,$new_height,$new_width,$new_height);
+        } else {
+            ImageCopyResized($new_img, $this->imageHandle, 0, 0, $new_x, $new_y, $new_width,$new_height,$new_width,$new_height);
+        }
+        $this->old_image = $this->imageHandle;
+        $this->imageHandle = $new_img;
+        $this->resized = true;
+
+        $this->new_x = $new_x;
+        $this->new_y = $new_y;
+        return true;
+    }
+   
+    /**
+     * Flip the image horizontally or vertically
+     *
+     * @param boolean $horizontal true if horizontal flip, vertical otherwise
+     */
+    function flip($horizontal)
+    {
+        if(!$horizontal) {
+            $this->rotate(180);
+        }
+
+        $width = imagesx($this->imageHandle); 
+        $height = imagesy($this->imageHandle); 
+
+        for ($j = 0; $j < $height; $j++) { 
+                $left = 0; 
+                $right = $width-1; 
+
+
+                while ($left < $right) { 
+                    //echo " j:".$j." l:".$left." r:".$right."\n<br>";
+                    $t = imagecolorat($this->imageHandle, $left, $j); 
+                    imagesetpixel($this->imageHandle, $left, $j, imagecolorat($this->imageHandle, $right, $j)); 
+                    imagesetpixel($this->imageHandle, $right, $j, $t); 
+                    $left++; $right--; 
+                } 
+            
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Adjust the image gamma
+     *
+     * @param float $outputgamma
+     *
+     * @return none
+     */
+    function gamma($outputgamma=1.0) {
+        ImageGammaCorrect($this->imageHandle, 1.0, $outputgamma);
+    }
+
+    /**
+     * Save the image file
+     *
+     * @param string  $filename the name of the file to write to
+     * @param int     $quality  output DPI, default is 85
+     * @param string  $types    define the output format, default
+     *                          is the current used format
+     *
+     * @return none
+     */
+    function save($filename, $type = '', $quality = 85)
+    {
+		$type           = $type==''? $this->type : $type;
+		$functionName   = 'image' . $type;
+
+		if(function_exists($functionName))
+		{
+			$this->old_image = $this->imageHandle;
+			if($type=='jpeg')
+				$functionName($this->imageHandle, $filename, $quality);
+			else
+				$functionName($this->imageHandle, $filename);
+			$this->imageHandle = $this->old_image;
+			$this->resized = false;
+		}
+    } // End save
+
+
+    /**
+     * Display image without saving and lose changes
+     *
+     * @param string type (JPG,PNG...);
+     * @param int quality 75
+     *
+     * @return none
+     */
+    function display($type = '', $quality = 75)
+    {
+        if ($type != '') {
+            $this->type = $type;
+        }
+        $functionName = 'Image' . $this->type;
+		if(function_exists($functionName))
+		{
+			header('Content-type: image/' . strtolower($this->type));
+			$functionName($this->imageHandle, '', $quality);
+			$this->imageHandle = $this->old_image;
+			$this->resized = false;
+			ImageDestroy($this->old_image);
+			$this->free();
+		}
+    }
+
+    /**
+     * Destroy image handle
+     *
+     * @return none
+     */
+    function free()
+    {
+        if ($this->imageHandle){
+            ImageDestroy($this->imageHandle);
+        }
+    }
+
+} // End class ImageGD
+?>

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/IM.php
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/IM.php?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/IM.php (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/IM.php Sun Oct  8 12:53:13 2006
@@ -0,0 +1,235 @@
+<?php
+
+/***********************************************************************
+** Title.........:  ImageMagick Driver
+** Version.......:  1.0
+** Author........:  Xiang Wei ZHUO <we...@zhuo.org>
+** Filename......:  IM.php
+** Last changed..:  30 Aug 2003 
+** Notes.........:  Orginal is from PEAR
+**/
+
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Peter Bowyer <pe...@mapledesign.co.uk>                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: IM.php 27 2004-04-01 08:31:57Z Wei Zhuo $
+//
+// Image Transformation interface using command line ImageMagick
+//
+
+require_once "Transform.php";
+
+Class Image_Transform_Driver_IM extends Image_Transform
+{
+    /**
+     * associative array commands to be executed
+     * @var array
+     */
+    var $command = array();
+
+    /**
+     *
+     *
+     */
+    function Image_Transform_Driver_IM()
+    {
+        return true;
+    } // End Image_IM
+
+    /**
+     * Load image
+     *
+     * @param string filename
+     *
+     * @return mixed none or a PEAR error object on error
+     * @see PEAR::isError()
+     */
+    function load($image)
+    {
+
+        $this->uid = md5($_SERVER['REMOTE_ADDR']);
+        /*if (!file_exists($image)) {
+            return PEAR::raiseError('The image file ' . $image . ' does\'t exist', true);
+        }*/
+        $this->image = $image;
+        $this->_get_image_details($image);
+    } // End load
+
+    /**
+     * Resize Action
+     *
+     * @param int   new_x   new width
+     * @param int   new_y   new height
+     *
+     * @return none
+     * @see PEAR::isError()
+     */
+    function _resize($new_x, $new_y)
+    {
+        /*if (isset($this->command['resize'])) {
+            return PEAR::raiseError("You cannot scale or resize an image more than once without calling save or display", true);
+        }*/
+        $this->command['resize'] = "-geometry ${new_x}x${new_y}!";
+
+        $this->new_x = $new_x;
+        $this->new_y = $new_y;
+    } // End resize
+
+    /**
+     * Crop the image
+     *
+     * @param int $crop_x left column of the image
+     * @param int $crop_y top row of the image
+     * @param int $crop_width new cropped image width
+     * @param int $crop_height new cropped image height
+     */
+    function crop($crop_x, $crop_y, $crop_width, $crop_height) 
+    {
+        $this->command['crop'] = "-crop {$crop_width}x{$crop_height}+{$crop_x}+{$crop_y}";
+    }
+
+    /**
+     * Flip the image horizontally or vertically
+     *
+     * @param boolean $horizontal true if horizontal flip, vertical otherwise
+     */
+    function flip($horizontal) 
+    {
+        if($horizontal)
+            $this->command['flop'] = "-flop";
+        else
+            $this->command['flip'] = "-flip";
+    }
+    /**
+     * rotate
+     *
+     * @param   int     angle   rotation angle
+     * @param   array   options no option allowed
+     *
+     */
+    function rotate($angle, $options=null)
+    {
+        if ('-' == $angle{0}) {
+            $angle = 360 - substr($angle, 1);
+        }
+         $this->command['rotate'] = "-rotate $angle";
+    } // End rotate
+
+    /**
+     * addText
+     *
+     * @param   array   options     Array contains options
+     *                              array(
+     *                                  'text'  The string to draw
+     *                                  'x'     Horizontal position
+     *                                  'y'     Vertical Position
+     *                                  'Color' Font color
+     *                                  'font'  Font to be used
+     *                                  'size'  Size of the fonts in pixel
+     *                                  'resize_first'  Tell if the image has to be resized
+     *                                                  before drawing the text
+     *                              )
+     *
+     * @return none
+     * @see PEAR::isError()
+     */
+    function addText($params)
+    {
+        $default_params = array(
+                                'text' => 'This is Text',
+                                'x' => 10,
+                                'y' => 20,
+                                'color' => 'red',
+                                'font' => 'Arial.ttf',
+                                'resize_first' => false // Carry out the scaling of the image before annotation?
+                                );
+         $params = array_merge($default_params, $params);
+         extract($params);
+         if (true === $resize_first) {
+             // Set the key so that this will be the last item in the array
+            $key = 'ztext';
+         } else {
+            $key = 'text';
+         }
+         $this->command[$key] = "-font $font -fill $color -draw 'text $x,$y \"$text\"'";
+         // Producing error: gs: not found gs: not found convert: Postscript delegate failed [No such file or directory].
+    } // End addText
+
+    /**
+     * Adjust the image gamma
+     *
+     * @param float $outputgamma
+     *
+     * @return none
+     */
+    function gamma($outputgamma=1.0) {
+        $this->command['gamma'] = "-gamma $outputgamma";
+    }
+
+    /**
+     * Save the image file
+     *
+     * @param string  $filename the name of the file to write to
+     * @param quality $quality  image dpi, default=75
+     * @param string  $type     (JPG,PNG...)
+     *
+     * @return none
+     */
+    function save($filename, $type='', $quality = 85)
+    {
+        $type == '' ? $this->type : $type;
+        $cmd = '' . IMAGE_TRANSFORM_LIB_PATH . 'convert ';
+		$cmd .= implode(' ', $this->command) . " -quality $quality ";
+		$cmd .= '"'.($this->image) . '" "' . ($filename) . '"';
+        
+        //$cmd = str_replace('/', '\\', $cmd);
+		//echo($cmd.'<br>');
+        exec($cmd,$retval);
+		//error_log('IM '.print_r($retval,true));
+    } // End save
+
+    /**
+     * Display image without saving and lose changes
+     *
+     * @param string type (JPG,PNG...);
+     * @param int quality 75
+     *
+     * @return none
+     */
+    function display($type = '', $quality = 75)
+    {
+        if ($type == '') {
+            header('Content-type: image/' . $this->type);
+            passthru(IMAGE_TRANSFORM_LIB_PATH . 'convert ' . implode(' ', $this->command) . " -quality $quality "  . escapeshellarg($this->image) . ' ' . strtoupper($this->type) . ":-");
+        } else {
+            header('Content-type: image/' . $type);
+            passthru(IMAGE_TRANSFORM_LIB_PATH . 'convert ' . implode(' ', $this->command) . " -quality $quality "  . escapeshellarg($this->image) . ' ' . strtoupper($type) . ":-");
+        }
+    }
+
+
+    /**
+     * Destroy image handle
+     *
+     * @return none
+     */
+    function free()
+    {
+        return true;
+    }
+
+} // End class ImageIM
+?>

Added: incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/ImageEditor.php
URL: http://svn.apache.org/viewvc/incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/ImageEditor.php?view=auto&rev=454197
==============================================================================
--- incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/ImageEditor.php (added)
+++ incubator/roller/trunk/web/roller-ui/authoring/editors/xinha/plugins/ImageManager/Classes/ImageEditor.php Sun Oct  8 12:53:13 2006
@@ -0,0 +1,449 @@
+<?php
+/**
+ * Image Editor. Editing tools, crop, rotate, scale and save.
+ * @author $Author: Wei Zhuo $
+ * @version $Id: ImageEditor.php 27 2004-04-01 08:31:57Z Wei Zhuo $
+ * @package ImageManager
+ */
+
+require_once('Transform.php');
+
+/**
+ * Handles the basic image editing capbabilities.
+ * @author $Author: Wei Zhuo $
+ * @version $Id: ImageEditor.php 27 2004-04-01 08:31:57Z Wei Zhuo $
+ * @package ImageManager
+ * @subpackage Editor
+ */
+class ImageEditor 
+{
+	/**
+	 * ImageManager instance.
+	 */
+	var $manager;
+
+	/**
+	 * user based on IP address
+	 */
+	var $_uid;
+
+	/**
+	 * tmp file storage time.
+	 */
+	var $lapse_time =900; //15 mins
+
+	var $filesaved = 0;
+
+	/**
+	 * Create a new ImageEditor instance. Editing requires a 
+	 * tmp file, which is saved in the current directory where the
+	 * image is edited. The tmp file is assigned by md5 hash of the
+	 * user IP address. This hashed is used as an ID for cleaning up
+	 * the tmp files. In addition, any tmp files older than the
+	 * the specified period will be deleted.
+	 * @param ImageManager $manager the image manager, we need this
+	 * for some file and path handling functions.
+	 */
+	function ImageEditor($manager) 
+	{
+		$this->manager = $manager;
+		$this->_uid = md5($_SERVER['REMOTE_ADDR']);
+	}
+	
+	/**
+	 * Did we save a file?
+	 * @return int 1 if the file was saved sucessfully, 
+	 * 0 no save operation, -1 file save error.
+	 */
+	function isFileSaved() 
+	{
+		Return $this->filesaved;
+	}
+
+	/**
+	 * Process the image, if not action, just display the image.
+	 * @return array with image information, empty array if not an image.
+	 * <code>array('src'=>'url of the image', 'dimensions'=>'width="xx" height="yy"',
+	 * 'file'=>'image file, relative', 'fullpath'=>'full path to the image');</code>
+	 */
+	function processImage() 
+	{
+		if(isset($_GET['img']))
+			$relative = rawurldecode($_GET['img']);
+		else
+			Return array();
+		
+		//$relative = '/Series2004NoteFront.jpg';
+
+		$imgURL = $this->manager->getFileURL($relative);
+		$fullpath = $this->manager->getFullPath($relative);
+		
+		$imgInfo = @getImageSize($fullpath);
+		if(!is_array($imgInfo))
+			Return array();
+
+		$action = $this->getAction();
+
+		if(!is_null($action))
+		{
+			$image = $this->processAction($action, $relative, $fullpath);
+		}
+		else
+		{
+			$image['src'] = $imgURL;
+			$image['dimensions'] = $imgInfo[3];
+			$image['file'] = $relative;
+			$image['fullpath'] = $fullpath;
+		}
+
+		Return $image;
+	}
+
+	/**
+	 * Process the actions, crop, scale(resize), rotate, flip, and save.
+	 * When ever an action is performed, the result is save into a
+	 * temporary image file, see createUnique on the filename specs.
+	 * It does not return the saved file, alway returning the tmp file.
+	 * @param string $action, should be 'crop', 'scale', 'rotate','flip', or 'save'
+	 * @param string $relative the relative image filename
+	 * @param string $fullpath the fullpath to the image file
+	 * @return array with image information
+	 * <code>array('src'=>'url of the image', 'dimensions'=>'width="xx" height="yy"',
+	 * 'file'=>'image file, relative', 'fullpath'=>'full path to the image');</code>
+	 */
+	function processAction($action, $relative, $fullpath) 
+	{
+		$params = '';
+		
+		if(isset($_GET['params']))
+			$params = $_GET['params'];
+
+		$values =  explode(',',$params,4);
+		$saveFile = $this->getSaveFileName($values[0]);
+
+		$img = Image_Transform::factory(IMAGE_CLASS);
+		$img->load($fullpath);
+
+		switch ($action) 
+		{
+			case 'crop':
+				$img->crop(intval($values[0]),intval($values[1]),
+							intval($values[2]),intval($values[3]));
+			break;
+			case 'scale':
+				$img->resize(intval($values[0]),intval($values[1]));
+				break;
+			case 'rotate':
+				$img->rotate(floatval($values[0]));
+				break;
+			case 'flip':
+				if ($values[0] == 'hoz')
+					$img->flip(true);
+				else if($values[0] == 'ver') 
+					$img->flip(false);
+				break;
+			case 'save':
+				if(!is_null($saveFile))
+				{
+					$quality = intval($values[1]);
+		            if($quality <0) $quality = 85;
+					$newSaveFile = $this->makeRelative($relative, $saveFile);
+					$newSaveFile = $this->getUniqueFilename($newSaveFile);
+					
+					//get unique filename just returns the filename, so
+					//we need to make the relative path once more.
+					$newSaveFile = $this->makeRelative($relative, $newSaveFile);
+          $image['saveFile'] = $newSaveFile;
+					$newSaveFullpath = $this->manager->getFullPath($newSaveFile);
+					$img->save($newSaveFullpath, $values[0], $quality);
+					if(is_file($newSaveFullpath))
+						$this->filesaved = 1;
+					else
+						$this->filesaved = -1;
+				}
+				break;
+		}
+		
+		//create the tmp image file
+		$filename = $this->createUnique($fullpath);
+		$newRelative = $this->makeRelative($relative, $filename);
+		$newFullpath = $this->manager->getFullPath($newRelative);
+		$newURL = $this->manager->getFileURL($newRelative);
+		
+		//save the file.
+		$img->save($newFullpath);
+		$img->free();
+
+		//get the image information
+		$imgInfo = @getimagesize($newFullpath);
+
+		$image['src'] = $newURL;
+    $image['width'] = $imgInfo[0];
+    $image['height'] = $imgInfo[1];
+		$image['dimensions'] = $imgInfo[3];
+		$image['file'] = $newRelative;
+		$image['fullpath'] = $newFullpath;
+
+		Return $image;
+	
+	}
+
+	/**
+	 * Get the file name base on the save name
+	 * and the save type.
+	 * @param string $type image type, 'jpeg', 'png', or 'gif'
+	 * @return string the filename according to save type
+	 */
+	function getSaveFileName($type) 
+	{
+		if(!isset($_GET['file']))
+			Return null;
+
+		$filename = Files::escape(rawurldecode($_GET['file']));
+		$index = strrpos($filename,'.');
+		$base = substr($filename,0,$index);
+		$ext = strtolower(substr($filename,$index+1,strlen($filename)));
+
+		if($type == 'jpeg' && !($ext=='jpeg' || $ext=='jpg'))
+		{
+			Return $base.'.jpeg';
+		}
+		if($type=='png' && $ext != 'png')
+			Return $base.'.png';
+		if($type=='gif' && $ext != 'gif')
+			Return $base.'.gif';
+
+		Return $filename;
+	}
+
+	/**
+	 * Get the default save file name, used by editor.php.
+	 * @return string a suggestive filename, this should be unique
+	 */
+	function getDefaultSaveFile() 
+	{
+		if(isset($_GET['img']))
+			$relative = rawurldecode($_GET['img']);
+		else
+			Return null;
+
+		Return $this->getUniqueFilename($relative);
+	}
+
+	/**
+	 * Get a unique filename. If the file exists, the filename
+	 * base is appended with an increasing integer.
+	 * @param string $relative the relative filename to the base_dir
+	 * @return string a unique filename in the current path
+	 */
+	function getUniqueFilename($relative) 
+	{
+		$fullpath = $this->manager->getFullPath($relative);
+		
+		$pathinfo = pathinfo($fullpath);
+
+		$path = Files::fixPath($pathinfo['dirname']);
+		$file = Files::escape($pathinfo['basename']);
+		
+		$filename = $file;
+
+		$dotIndex = strrpos($file, '.');
+		$ext = '';
+
+		if(is_int($dotIndex)) 
+		{
+			$ext = substr($file, $dotIndex);
+			$base = substr($file, 0, $dotIndex);
+		}
+
+		$counter = 0;
+		while(is_file($path.$filename)) 
+		{
+			$counter++;
+			$filename = $base.'_'.$counter.$ext;
+		}
+		
+		Return $filename;
+		
+	}
+
+	/**
+	 * Specifiy the original relative path, a new filename
+	 * and return the new filename with relative path.
+	 * i.e. $pathA (-filename) + $file
+	 * @param string $pathA the relative file
+	 * @param string $file the new filename
+	 * @return string relative path with the new filename
+	 */
+	function makeRelative($pathA, $file) 
+	{
+		$index = strrpos($pathA,'/');
+		if(!is_int($index))
+			Return $file;
+
+		$path = substr($pathA, 0, $index);
+		Return Files::fixPath($path).$file;
+	}
+
+	/**
+	 * Get the action GET parameter
+	 * @return string action parameter
+	 */
+	function getAction() 
+	{
+		$action = null;
+		if(isset($_GET['action']))
+			$action = $_GET['action'];
+		Return $action;
+	}
+
+	/**
+	 * Generate a unique string based on md5(microtime()).
+	 * Well not so uniqe, as it is limited to 6 characters
+	 * @return string unique string.
+	 */
+    function uniqueStr()
+    {
+      return substr(md5(microtime()),0,6);
+    }
+
+	/**
+	 * Create unique tmp image file name.
+	 * The filename is based on the tmp file prefix
+	 * specified in config.inc.php plus 
+	 * the UID (basically a md5 of the remote IP)
+	 * and some random 6 character string.
+	 * This function also calls to clean up the tmp files.
+	 * @param string $file the fullpath to a file
+	 * @return string a unique filename for that path
+	 * NOTE: it only returns the filename, path no included.
+	 */
+	function createUnique($file) 
+	{
+		$pathinfo = pathinfo($file);
+		$path = Files::fixPath($pathinfo['dirname']);
+		$imgType = $this->getImageType($file);
+
+		$unique_str = $this->manager->getTmpPrefix().$this->_uid.'_'.$this->uniqueStr().".".$imgType;
+
+	   //make sure the the unique temp file does not exists
+        while (file_exists($path.$unique_str))
+        {
+            $unique_str = $this->manager->getTmpPrefix().$this->_uid.'_'.$this->uniqueStr().".".$imgType;
+        }
+
+		$this->cleanUp($path,$pathinfo['basename']);
+
+		Return $unique_str;
+	}
+
+	/**
+	 * Delete any tmp image files.
+	 * @param string $path the full path 
+	 * where the clean should take place.
+	 */
+	function cleanUp($path,$file) 
+	{
+		$path = Files::fixPath($path);
+
+		if(!is_dir($path))
+			Return false;
+
+		$d = @dir($path);
+		
+		$tmp = $this->manager->getTmpPrefix();
+		$tmpLen = strlen($tmp);
+
+		$prefix = $tmp.$this->_uid;
+		$len = strlen($prefix);
+
+		while (false !== ($entry = $d->read())) 
+		{
+			//echo $entry."<br>";
+			if(is_file($path.$entry) && $this->manager->isTmpFile($entry))
+			{
+				if(substr($entry,0,$len)==$prefix && $entry != $file)
+					Files::delFile($path.$entry);
+				else if(substr($entry,0,$tmpLen)==$tmp && $entry != $file)
+				{
+					if(filemtime($path.$entry)+$this->lapse_time < time())
+						Files::delFile($path.$entry);
+				}
+			}
+		}
+		$d->close();
+	}
+
+	/**
+	 * Get the image type base on an image file.
+	 * @param string $file the full path to the image file.
+	 * @return string of either 'gif', 'jpeg', 'png' or 'bmp'
+	 * otherwise it will return null.
+	 */
+	function getImageType($file) 
+	{
+		$imageInfo = @getImageSize($file);
+
+		if(!is_array($imageInfo))
+			Return null;
+
+		switch($imageInfo[2]) 
+		{
+			case 1:
+				Return 'gif';
+			case 2:
+				Return 'jpeg';
+			case 3:
+				Return 'png';
+			case 6:
+				Return 'bmp';
+		}
+
+		Return null;
+	}
+
+	/**
+	 * Check if the specified image can be edit by GD
+	 * mainly to check that GD can read and save GIFs
+	 * @return int 0 if it is not a GIF file, 1 is GIF is editable, -1 if not editable.
+	 */
+	function isGDEditable() 
+	{
+		if(isset($_GET['img']))
+			$relative = rawurldecode($_GET['img']);
+		else
+			Return 0;
+		if(IMAGE_CLASS != 'GD')
+			Return 0;
+
+		$fullpath = $this->manager->getFullPath($relative);
+
+		$type = $this->getImageType($fullpath);
+		if($type != 'gif')
+			Return 0;
+
+		if(function_exists('ImageCreateFrom'+$type)
+			&& function_exists('image'+$type))
+			Return 1;
+		else
+			Return -1;
+	}
+
+	/**
+	 * Check if GIF can be edit by GD.
+	 * @return int 0 if it is not using the GD library, 1 is GIF is editable, -1 if not editable.
+	 */
+	function isGDGIFAble() 
+	{
+		if(IMAGE_CLASS != 'GD')
+			Return 0;
+
+		if(function_exists('ImageCreateFromGif')
+			&& function_exists('imagegif'))
+			Return 1;
+		else
+			Return -1;
+	}
+}
+
+?>