You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by br...@apache.org on 2010/02/28 22:12:41 UTC

svn commit: r917294 [2/3] - in /incubator/jspwiki/trunk: ./ etc/ini/ src/WebContent/WEB-INF/classes/templates/ src/WebContent/scripts/ src/WebContent/scripts/wysiwyg/ src/WebContent/scripts/wysiwyg/images/ src/WebContent/templates/default/ src/WebConte...

Modified: incubator/jspwiki/trunk/src/WebContent/scripts/mootools-more.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/mootools-more.js?rev=917294&r1=917293&r2=917294&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/mootools-more.js (original)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/mootools-more.js Sun Feb 28 21:12:40 2010
@@ -1,18 +1,51 @@
 //MooTools More, <http://mootools.net/more>. Copyright (c) 2006-2009 Aaron Newton <http://clientcide.com/>, Valerio Proietti <http://mad4milk.net> & the MooTools team <http://mootools.net/developers>, MIT Style License.
 
+/*
+---
+
+script: More.js
+
+description: MooTools More
+
+license: MIT-style license
+
+authors:
+- Guillermo Rauch
+- Thomas Aylott
+- Scott Kyle
+
+requires:
+- core:1.2.4/MooTools
+
+provides: [MooTools.More]
+
+...
+*/
+
 MooTools.More = {
-	'version': '1.2.3.1'
+	'version': '1.2.4.4',
+	'build': '6f6057dc645fdb7547689183b2311063bd653ddf'
 };
 
 /*
-Script: Fx.Elements.js
-	Effect to change any number of CSS properties of any number of Elements.
+---
+
+script: Fx.Elements.js
+
+description: Effect to change any number of CSS properties of any number of Elements.
 
-	License:
-		MIT-style license.
+license: MIT-style license
 
-	Authors:
-		Valerio Proietti
+authors:
+- Valerio Proietti
+
+requires:
+- core:1.2.4/Fx.CSS
+- /MooTools.More
+
+provides: [Fx.Elements]
+
+...
 */
 
 Fx.Elements = new Class({
@@ -58,42 +91,58 @@
 });
 
 /*
-Script: Fx.Accordion.js
-	An Fx.Elements extension which allows you to easily create accordion type controls.
+---
+
+script: Fx.Accordion.js
 
-	License:
-		MIT-style license.
+description: An Fx.Elements extension which allows you to easily create accordion type controls.
 
-	Authors:
-		Valerio Proietti
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+
+requires:
+- core:1.2.4/Element.Event
+- /Fx.Elements
+
+provides: [Fx.Accordion]
+
+...
 */
 
-var Accordion = Fx.Accordion = new Class({
+Fx.Accordion = new Class({
 
 	Extends: Fx.Elements,
 
 	options: {/*
 		onActive: $empty(toggler, section),
-		onBackground: $empty(toggler, section),*/
+		onBackground: $empty(toggler, section),
+		fixedHeight: false,
+		fixedWidth: false,
+		*/
 		display: 0,
 		show: false,
 		height: true,
 		width: false,
 		opacity: true,
-		fixedHeight: false,
-		fixedWidth: false,
-		wait: false,
 		alwaysHide: false,
 		trigger: 'click',
-		initialDisplayFx: true
+		initialDisplayFx: true,
+		returnHeightToAuto: true
 	},
 
 	initialize: function(){
-		var params = Array.link(arguments, {'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined});
+		var params = Array.link(arguments, {
+			'container': Element.type, //deprecated
+			'options': Object.type,
+			'togglers': $defined,
+			'elements': $defined
+		});
 		this.parent(params.elements, params.options);
 		this.togglers = $$(params.togglers);
-		this.container = document.id(params.container);
 		this.previous = -1;
+		this.internalChain = new Chain();
 		if (this.options.alwaysHide) this.options.wait = true;
 		if ($chk(this.options.show)){
 			this.options.display = false;
@@ -115,7 +164,9 @@
 				for (var fx in this.effects) el.setStyle(fx, 0);
 			}
 		}, this);
-		if ($chk(this.options.display)) this.display(this.options.display, this.options.initialDisplayFx);
+		if ($chk(this.options.display) || this.options.initialDisplayFx === false) this.display(this.options.display, this.options.initialDisplayFx);
+		if (this.options.fixedHeight !== false) this.options.returnHeightToAuto = false;
+		this.addEvent('complete', this.internalChain.callChain.bind(this.internalChain));
 	},
 
 	addSection: function(toggler, element){
@@ -125,7 +176,9 @@
 		this.togglers.include(toggler);
 		this.elements.include(element);
 		var idx = this.togglers.indexOf(toggler);
-		toggler.addEvent(this.options.trigger, this.display.bind(this, idx));
+		var displayer = this.display.bind(this, idx);
+		toggler.store('accordion:display', displayer);
+		toggler.addEvent(this.options.trigger, displayer);
 		if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
 		if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
 		element.fullOpacity = 1;
@@ -138,34 +191,104 @@
 		return this;
 	},
 
+	detach: function(){
+		this.togglers.each(function(toggler) {
+			toggler.removeEvent(this.options.trigger, toggler.retrieve('accordion:display'));
+		}, this);
+	},
+
 	display: function(index, useFx){
+		if (!this.check(index, useFx)) return this;
 		useFx = $pick(useFx, true);
+		if (this.options.returnHeightToAuto){
+			var prev = this.elements[this.previous];
+			if (prev && !this.selfHidden){
+				for (var fx in this.effects){
+					prev.setStyle(fx, prev[this.effects[fx]]);
+				}
+			}
+		}
 		index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
 		if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
 		this.previous = index;
 		var obj = {};
 		this.elements.each(function(el, i){
 			obj[i] = {};
-			var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));
+			var hide;
+			if (i != index){
+				hide = true;
+			} else if (this.options.alwaysHide && ((el.offsetHeight > 0 && this.options.height) || el.offsetWidth > 0 && this.options.width)){
+				hide = true;
+				this.selfHidden = true;
+			}
 			this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
 			for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
 		}, this);
+		this.internalChain.chain(function(){
+			if (this.options.returnHeightToAuto && !this.selfHidden){
+				var el = this.elements[index];
+				if (el) el.setStyle('height', 'auto');
+			};
+		}.bind(this));
 		return useFx ? this.start(obj) : this.set(obj);
 	}
 
 });
 
 /*
-Script: Drag.js
-	The base Drag Class. Can be used to drag and resize Elements using mouse events.
+	Compatibility with 1.2.0
+*/
+var Accordion = new Class({
+
+	Extends: Fx.Accordion,
+
+	initialize: function(){
+		this.parent.apply(this, arguments);
+		var params = Array.link(arguments, {'container': Element.type});
+		this.container = params.container;
+	},
 
-	License:
-		MIT-style license.
+	addSection: function(toggler, element, pos){
+		toggler = document.id(toggler);
+		element = document.id(element);
+		var test = this.togglers.contains(toggler);
+		var len = this.togglers.length;
+		if (len && (!test || pos)){
+			pos = $pick(pos, len - 1);
+			toggler.inject(this.togglers[pos], 'before');
+			element.inject(toggler, 'after');
+		} else if (this.container && !test){
+			toggler.inject(this.container);
+			element.inject(this.container);
+		}
+		return this.parent.apply(this, arguments);
+	}
+
+});
+
+/*
+---
+
+script: Drag.js
+
+description: The base Drag Class. Can be used to drag and resize Elements using mouse events.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+- Tom Occhinno
+- Jan Kassens
+
+requires:
+- core:1.2.4/Events
+- core:1.2.4/Options
+- core:1.2.4/Element.Event
+- core:1.2.4/Element.Style
+- /MooTools.More
+
+provides: [Drag]
 
-	Authors:
-		Valerio Proietti
-		Tom Occhinno
-		Jan Kassens
 */
 
 var Drag = new Class({
@@ -187,6 +310,7 @@
 		handle: false,
 		invert: false,
 		preventDefault: false,
+		stopPropagation: false,
 		modifiers: {x: 'left', y: 'top'}
 	},
 
@@ -224,7 +348,9 @@
 	},
 
 	start: function(event){
+		if (event.rightClick) return;
 		if (this.options.preventDefault) event.preventDefault();
+		if (this.options.stopPropagation) event.stopPropagation();
 		this.mouse.start = event.page;
 		this.fireEvent('beforeStart', this.element);
 		var limit = this.options.limit;
@@ -274,8 +400,11 @@
 				}
 			}
 			if (this.options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % this.options.grid[z]);
-			if (this.options.style) this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
-			else this.element[this.options.modifiers[z]] = this.value.now[z];
+			if (this.options.style) {
+				this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
+			} else {
+				this.element[this.options.modifiers[z]] = this.value.now[z];
+			}
 		}
 		this.fireEvent('drag', [this.element, event]);
 	},
@@ -312,16 +441,29 @@
 
 
 /*
-Script: Drag.Move.js
-	A Drag extension that provides support for the constraining of draggables to containers and droppables.
+---
+
+script: Drag.Move.js
+
+description: A Drag extension that provides support for the constraining of draggables to containers and droppables.
+
+license: MIT-style license
 
-	License:
-		MIT-style license.
+authors:
+- Valerio Proietti
+- Tom Occhinno
+- Jan Kassens
+- Aaron Newton
+- Scott Kyle
 
-	Authors:
-		Valerio Proietti
-		Tom Occhinno
-		Jan Kassens*/
+requires:
+- core:1.2.4/Element.Dimensions
+- /Drag
+
+provides: [Drag.Move]
+
+...
+*/
 
 Drag.Move = new Class({
 
@@ -340,14 +482,20 @@
 
 	initialize: function(element, options){
 		this.parent(element, options);
+		element = this.element;
+		
 		this.droppables = $$(this.options.droppables);
 		this.container = document.id(this.options.container);
-		if (this.container && $type(this.container) != 'element') this.container = document.id(this.container.getDocument().body);
-
-		var position = this.element.getStyle('position');
-		if (position=='static') position = 'absolute';
-		if ([this.element.getStyle('left'), this.element.getStyle('top')].contains('auto')) this.element.position(this.element.getPosition(this.element.offsetParent));
-		this.element.setStyle('position', position);
+		
+		if (this.container && $type(this.container) != 'element')
+			this.container = document.id(this.container.getDocument().body);
+		
+		var styles = element.getStyles('left', 'top', 'position');
+		if (styles.left == 'auto' || styles.top == 'auto')
+			element.setPosition(element.getPosition(element.getOffsetParent()));
+		
+		if (styles.position == 'static')
+			element.setStyle('position', 'absolute');
 
 		this.addEvent('start', this.checkDroppables, true);
 
@@ -355,42 +503,81 @@
 	},
 
 	start: function(event){
-		if (this.container){
-			var ccoo = this.container.getCoordinates(this.element.getOffsetParent()), cbs = {}, ems = {};
-
-			['top', 'right', 'bottom', 'left'].each(function(pad){
-				cbs[pad] = this.container.getStyle('border-' + pad).toInt();
-				ems[pad] = this.element.getStyle('margin-' + pad).toInt();
-			}, this);
-
-			var width = this.element.offsetWidth + ems.left + ems.right;
-			var height = this.element.offsetHeight + ems.top + ems.bottom;
-
-			if (this.options.includeMargins) {
-				$each(ems, function(value, key) {
-					ems[key] = 0;
-				});
-			}
-			if (this.container == this.element.getOffsetParent()) {
-				this.options.limit = {
-					x: [0 - ems.left, ccoo.right - cbs.left - cbs.right - width + ems.right],
-					y: [0 - ems.top, ccoo.bottom - cbs.top - cbs.bottom - height + ems.bottom]
-				};
-			} else {
-				this.options.limit = {
-					x: [ccoo.left + cbs.left - ems.left, ccoo.right - cbs.right - width + ems.right],
-					y: [ccoo.top + cbs.top - ems.top, ccoo.bottom - cbs.bottom - height + ems.bottom]
-				};
-			}
-
-		}
+		if (this.container) this.options.limit = this.calculateLimit();
+		
 		if (this.options.precalculate){
-			this.positions = this.droppables.map(function(el) {
+			this.positions = this.droppables.map(function(el){
 				return el.getCoordinates();
 			});
 		}
+		
 		this.parent(event);
 	},
+	
+	calculateLimit: function(){
+		var offsetParent = this.element.getOffsetParent(),
+			containerCoordinates = this.container.getCoordinates(offsetParent),
+			containerBorder = {},
+			elementMargin = {},
+			elementBorder = {},
+			containerMargin = {},
+			offsetParentPadding = {};
+
+		['top', 'right', 'bottom', 'left'].each(function(pad){
+			containerBorder[pad] = this.container.getStyle('border-' + pad).toInt();
+			elementBorder[pad] = this.element.getStyle('border-' + pad).toInt();
+			elementMargin[pad] = this.element.getStyle('margin-' + pad).toInt();
+			containerMargin[pad] = this.container.getStyle('margin-' + pad).toInt();
+			offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt();
+		}, this);
+
+		var width = this.element.offsetWidth + elementMargin.left + elementMargin.right,
+			height = this.element.offsetHeight + elementMargin.top + elementMargin.bottom,
+			left = 0,
+			top = 0,
+			right = containerCoordinates.right - containerBorder.right - width,
+			bottom = containerCoordinates.bottom - containerBorder.bottom - height;
+
+		if (this.options.includeMargins){
+			left += elementMargin.left;
+			top += elementMargin.top;
+		} else {
+			right += elementMargin.right;
+			bottom += elementMargin.bottom;
+		}
+		
+		if (this.element.getStyle('position') == 'relative'){
+			var coords = this.element.getCoordinates(offsetParent);
+			coords.left -= this.element.getStyle('left').toInt();
+			coords.top -= this.element.getStyle('top').toInt();
+			
+			left += containerBorder.left - coords.left;
+			top += containerBorder.top - coords.top;
+			right += elementMargin.left - coords.left;
+			bottom += elementMargin.top - coords.top;
+			
+			if (this.container != offsetParent){
+				left += containerMargin.left + offsetParentPadding.left;
+				top += (Browser.Engine.trident4 ? 0 : containerMargin.top) + offsetParentPadding.top;
+			}
+		} else {
+			left -= elementMargin.left;
+			top -= elementMargin.top;
+			
+			if (this.container == offsetParent){
+				right -= containerBorder.left;
+				bottom -= containerBorder.top;
+			} else {
+				left += containerCoordinates.left + containerBorder.left;
+				top += containerCoordinates.top + containerBorder.top;
+			}
+		}
+		
+		return {
+			x: [left, right],
+			y: [top, bottom]
+		};
+	},
 
 	checkAgainst: function(el, i){
 		el = (this.positions) ? this.positions[i] : el.getCoordinates();
@@ -433,14 +620,28 @@
 
 
 /*
-Script: Color.js
-	Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
+---
+
+script: Color.js
+
+description: Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
 
-	License:
-		MIT-style license.
+requires:
+- core:1.2.4/Array
+- core:1.2.4/String
+- core:1.2.4/Number
+- core:1.2.4/Hash
+- core:1.2.4/Function
+- core:1.2.4/$util
 
-	Authors:
-		Valerio Proietti
+provides: [Color]
+
+...
 */
 
 var Color = new Native({
@@ -518,15 +719,16 @@
 Array.implement({
 
 	rgbToHsb: function(){
-		var red = this[0], green = this[1], blue = this[2];
-		var hue, saturation, brightness;
-		var max = Math.max(red, green, blue), min = Math.min(red, green, blue);
+		var red = this[0],
+				green = this[1],
+				blue = this[2],
+				hue = 0;
+		var max = Math.max(red, green, blue),
+				min = Math.min(red, green, blue);
 		var delta = max - min;
-		brightness = max / 255;
-		saturation = (max != 0) ? delta / max : 0;
-		if (saturation == 0){
-			hue = 0;
-		} else {
+		var brightness = max / 255,
+				saturation = (max != 0) ? delta / max : 0;
+		if(saturation != 0) {
 			var rr = (max - red) / delta;
 			var gr = (max - green) / delta;
 			var br = (max - blue) / delta;
@@ -579,15 +781,26 @@
 
 
 /*
-Script: Hash.Cookie.js
-	Class for creating, reading, and deleting Cookies in JSON format.
+---
+
+script: Hash.Cookie.js
+
+description: Class for creating, reading, and deleting Cookies in JSON format.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+- Aaron Newton
+
+requires:
+- core:1.2.4/Cookie
+- core:1.2.4/JSON
+- /MooTools.More
 
-	License:
-		MIT-style license.
+provides: [Hash.Cookie]
 
-	Authors:
-		Valerio Proietti
-		Aaron Newton
+...
 */
 
 Hash.Cookie = new Class({
@@ -627,55 +840,77 @@
 });
 
 /*
-Script: Tips.js
-	Class for creating nice tips that follow the mouse cursor when hovering an element.
+---
 
-	License:
-		MIT-style license.
+script: Tips.js
 
-	Authors:
-		Valerio Proietti
-		Christoph Pojer
+description: Class for creating nice tips that follow the mouse cursor when hovering an element.
+
+license: MIT-style license
+
+authors:
+- Valerio Proietti
+- Christoph Pojer
+
+requires:
+- core:1.2.4/Options
+- core:1.2.4/Events
+- core:1.2.4/Element.Event
+- core:1.2.4/Element.Style
+- core:1.2.4/Element.Dimensions
+- /MooTools.More
+
+provides: [Tips]
+
+...
 */
 
-var Tips = new Class({
+(function(){
+
+var read = function(option, element){
+	return (option) ? ($type(option) == 'function' ? option(element) : element.get(option)) : '';
+};
+
+this.Tips = new Class({
 
 	Implements: [Events, Options],
 
 	options: {
-		onShow: function(tip){
-			tip.setStyle('visibility', 'visible');
+		/*
+		onAttach: $empty(element),
+		onDetach: $empty(element),
+		*/
+		onShow: function(){
+			this.tip.setStyle('display', 'block');
 		},
-		onHide: function(tip){
-			tip.setStyle('visibility', 'hidden');
+		onHide: function(){
+			this.tip.setStyle('display', 'none');
 		},
 		title: 'title',
-		text: function(el){
-			return el.get('rel') || el.get('href');
+		text: function(element){
+			return element.get('rel') || element.get('href');
 		},
 		showDelay: 100,
 		hideDelay: 100,
-		className: null,
+		className: 'tip-wrap',
 		offset: {x: 16, y: 16},
+		windowPadding: {x:0, y:0},
 		fixed: false
 	},
 
 	initialize: function(){
 		var params = Array.link(arguments, {options: Object.type, elements: $defined});
-		if (params.options && params.options.offsets) params.options.offset = params.options.offsets;
 		this.setOptions(params.options);
-		this.container = new Element('div', {'class': 'tip'});
-		this.tip = this.getTip();
-
 		if (params.elements) this.attach(params.elements);
+		this.container = new Element('div', {'class': 'tip'});
 	},
 
-	getTip: function(){
-		return new Element('div', {
+	toElement: function(){
+		if (this.tip) return this.tip;
+
+		return this.tip = new Element('div', {
 			'class': this.options.className,
 			styles: {
-				visibility: 'hidden',
-				display: 'none',
 				position: 'absolute',
 				top: 0,
 				left: 0
@@ -688,81 +923,90 @@
 	},
 
 	attach: function(elements){
-		var read = function(option, element){
-			if (option == null) return '';
-			return $type(option) == 'function' ? option(element) : element.get(option);
-		};
 		$$(elements).each(function(element){
-			var title = read(this.options.title, element);
+			var title = read(this.options.title, element),
+				text = read(this.options.text, element);
+			
 			element.erase('title').store('tip:native', title).retrieve('tip:title', title);
-			element.retrieve('tip:text', read(this.options.text, element));
-
+			element.retrieve('tip:text', text);
+			this.fireEvent('attach', [element]);
+			
 			var events = ['enter', 'leave'];
 			if (!this.options.fixed) events.push('move');
-
+			
 			events.each(function(value){
-				element.addEvent('mouse' + value, element.retrieve('tip:' + value, this['element' + value.capitalize()].bindWithEvent(this, element)));
+				var event = element.retrieve('tip:' + value);
+				if (!event) event = this['element' + value.capitalize()].bindWithEvent(this, element);
+				
+				element.store('tip:' + value, event).addEvent('mouse' + value, event);
 			}, this);
 		}, this);
-
+		
 		return this;
 	},
 
 	detach: function(elements){
 		$$(elements).each(function(element){
 			['enter', 'leave', 'move'].each(function(value){
-				element.removeEvent('mouse' + value, element.retrieve('tip:' + value) || $empty);
+				element.removeEvent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value);
 			});
-
-			element.eliminate('tip:enter').eliminate('tip:leave').eliminate('tip:move');
-
-			if ($type(this.options.title) == 'string' && this.options.title == 'title'){
+			
+			this.fireEvent('detach', [element]);
+			
+			if (this.options.title == 'title'){ // This is necessary to check if we can revert the title
 				var original = element.retrieve('tip:native');
 				if (original) element.set('title', original);
 			}
 		}, this);
-
+		
 		return this;
 	},
 
 	elementEnter: function(event, element){
-		$A(this.container.childNodes).each(Element.dispose);
-
+		this.container.empty();
+		
 		['title', 'text'].each(function(value){
 			var content = element.retrieve('tip:' + value);
-			if (!content) return;
-
-			this[value + 'Element'] = new Element('div', {'class': 'tip-' + value}).inject(this.container);
-			this.fill(this[value + 'Element'], content);
+			if (content) this.fill(new Element('div', {'class': 'tip-' + value}).inject(this.container), content);
 		}, this);
-
-		this.timer = $clear(this.timer);
-		this.timer = this.show.delay(this.options.showDelay, this, element);
-		this.tip.setStyle('display', 'block');
-		this.position((!this.options.fixed) ? event : {page: element.getPosition()});
+		
+		$clear(this.timer);
+		this.timer = (function(){
+			this.show(this, element);
+			this.position((this.options.fixed) ? {page: element.getPosition()} : event);
+		}).delay(this.options.showDelay, this);
 	},
 
 	elementLeave: function(event, element){
 		$clear(this.timer);
-		this.tip.setStyle('display', 'none');
 		this.timer = this.hide.delay(this.options.hideDelay, this, element);
+		this.fireForParent(event, element);
 	},
 
-	elementMove: function(event){
+	fireForParent: function(event, element){
+		element = element.getParent();
+		if (!element || element == document.body) return;
+		if (element.retrieve('tip:enter')) element.fireEvent('mouseenter', event);
+		else this.fireForParent(event, element);
+	},
+
+	elementMove: function(event, element){
 		this.position(event);
 	},
 
 	position: function(event){
+		if (!this.tip) document.id(this);
+
 		var size = window.getSize(), scroll = window.getScroll(),
 			tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight},
 			props = {x: 'left', y: 'top'},
 			obj = {};
-
+		
 		for (var z in props){
 			obj[props[z]] = event.page[z] + this.options.offset[z];
-			if ((obj[props[z]] + tip[z] - scroll[z]) > size[z]) obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z];
+			if ((obj[props[z]] + tip[z] - scroll[z]) > size[z] - this.options.windowPadding[z]) obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z];
 		}
-
+		
 		this.tip.setStyles(obj);
 	},
 
@@ -771,12 +1015,16 @@
 		else element.adopt(contents);
 	},
 
-	show: function(el){
-		this.fireEvent('show', [this.tip, el]);
+	show: function(element){
+		if (!this.tip) document.id(this);
+		this.fireEvent('show', [this.tip, element]);
 	},
 
-	hide: function(el){
-		this.fireEvent('hide', [this.tip, el]);
+	hide: function(element){
+		if (!this.tip) document.id(this);
+		this.fireEvent('hide', [this.tip, element]);
 	}
 
-});
\ No newline at end of file
+});
+
+})();
\ No newline at end of file

Added: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.Extras.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.Extras.js?rev=917294&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.Extras.js (added)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.Extras.js Sun Feb 28 21:12:40 2010
@@ -0,0 +1,61 @@
+/*
+Script: MooEditable.Extras.js
+	Extends MooEditable to include more (simple) toolbar buttons.
+
+License:
+	MIT-style license.
+*/
+
+MooEditable.Actions.extend({
+
+	formatBlock: {
+		title: 'Block Formatting',
+		type: 'menu-list',
+		options: {
+			list: [
+				{text: 'Paragraph', value: 'p'},
+				{text: 'Heading 1', value: 'h1'},
+				{text: 'Heading 2', value: 'h2'},
+				{text: 'Heading 3', value: 'h3'}
+			]
+		},
+		states: {
+			tags: ['p', 'h1', 'h2', 'h3']
+		},
+		command: function(menulist, name){
+			var argument = '<' + name + '>';
+			this.execute('formatBlock', false, argument);
+			this.focus();
+		}
+	},
+	
+	justifyleft:{
+		title: 'Align Left',
+		states: {
+			css: {'text-align': 'left'}
+		}
+	},
+	
+	justifyright:{
+		title: 'Align Right',
+		states: {
+			css: {'text-align': 'right'}
+		}
+	},
+	
+	justifycenter:{
+		title: 'Align Center',
+		states: {
+			tags: ['center'],
+			css: {'text-align': 'center'}
+		}
+	},
+	
+	justifyfull:{
+		title: 'Align Justify',
+		states: {
+			css: {'text-align': 'justify'}
+		}
+	}
+
+});

Propchange: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.Extras.js
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.Group.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.Group.js?rev=917294&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.Group.js (added)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.Group.js Sun Feb 28 21:12:40 2010
@@ -0,0 +1,55 @@
+/*
+Script: MooEditable.Group.js
+	A MooEditable extension for having multiple MooEditable instances on a page controlled by one toolbar.
+
+License:
+	MIT-style license.
+*/
+
+MooEditable.Group = new Class({
+
+	Implements: [Options],
+	
+	options: {
+		actions: 'bold italic underline strikethrough | insertunorderedlist insertorderedlist indent outdent | undo redo | createlink unlink | urlimage | toggleview'
+	},
+    
+	initialize: function(toolbarEl, options){
+		this.setOptions(options);
+		this.actions = this.options.actions.clean().split(' ');
+		var self = this;
+		this.toolbar = new MooEditable.UI.Toolbar({
+			onItemAction: function(){
+				var args = $splat(arguments);
+				var item = args[0];
+				if (!self.activeEditor) return;
+				self.activeEditor.focus();
+				self.activeEditor.action(item.name, args);
+				if (self.activeEditor.mode == 'iframe') self.activeEditor.checkStates();
+			}
+		}).render(this.actions);
+		document.id(toolbarEl).adopt(this.toolbar);
+	},
+
+	add: function(textarea, options){
+		return this.activeEditor = new MooEditable.Group.Item(textarea, this, $merge({toolbar: false}, this.options, options));
+	}
+	
+});
+
+
+MooEditable.Group.Item = new Class({
+
+	Extends: MooEditable,
+
+	initialize: function(textarea, group, options){
+		this.group = group;
+		this.parent(textarea, options);
+		var focus = function(){
+			this.group.activeEditor = this;
+		}.bind(this);
+		this.textarea.addEvent('focus', focus);
+		this.win.addEvent('focus', focus);
+	}
+
+});
\ No newline at end of file

Propchange: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.Group.js
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.UI.ButtonOverlay.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.UI.ButtonOverlay.js?rev=917294&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.UI.ButtonOverlay.js (added)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.UI.ButtonOverlay.js Sun Feb 28 21:12:40 2010
@@ -0,0 +1,95 @@
+/*
+Script: MooEditable.UI.ButtonOverlay.js
+	UI Class to create a button element with a popup overlay.
+
+License:
+	MIT-style license.
+
+Copyright:
+	Copyright (c) 2007-2009 [Lim Chee Aun](http://cheeaun.com).
+*/
+
+MooEditable.UI.ButtonOverlay = new Class({
+
+	Extends: MooEditable.UI.Button,
+
+	options: {
+		/*
+		onOpenOverlay: $empty,
+		onCloseOverlay: $empty,
+		*/
+		overlayHTML: '',
+		overlayClass: '',
+		overlaySize: {x: 150, y: 'auto'},
+		overlayContentClass: ''
+	},
+
+	initialize: function(options){
+		this.parent(options);
+		this.render();
+		this.el.addClass('mooeditable-ui-buttonOverlay');
+		this.renderOverlay(this.options.overlayHTML);
+	},
+	
+	renderOverlay: function(html){
+		var self = this;
+		this.overlay = new Element('div', {
+			'class': 'mooeditable-ui-button-overlay ' + self.name + '-overlay ' + self.options.overlayClass,
+			html: '<div class="overlay-content ' + self.options.overlayContentClass + '">' + html + '</div>',
+			tabindex: 0,
+			styles: {
+				left: '-999em',
+				position: 'absolute',
+				width: self.options.overlaySize.x,
+				height: self.options.overlaySize.y
+			},
+			events: {
+				mousedown: self.clickOverlay.bind(self),
+				focus: self.openOverlay.bind(self),
+				blur: self.closeOverlay.bind(self)
+			}
+		}).inject(document.body).store('MooEditable.UI.ButtonOverlay', this);
+		this.overlayVisible = false;
+	},
+	
+	openOverlay: function(){
+		if (this.overlayVisible) return;
+		this.overlayVisible = true;
+		this.activate();
+		this.fireEvent('openOverlay', this);
+		return this;
+	},
+	
+	closeOverlay: function(){
+		if (!this.overlayVisible) return;
+		this.overlay.setStyle('left', '-999em');
+		this.overlayVisible = false;
+		this.deactivate();
+		this.fireEvent('closeOverlay', this);
+		return this;
+	},
+	
+	clickOverlay: function(e){
+		if (e.target == this.overlay || e.target.parentNode == this.overlay) return;
+		e.preventDefault();
+		this.action(e);
+		this.overlay.blur();
+	},
+	
+	click: function(e){
+		e.preventDefault();
+		if (this.disabled) return;
+		if (this.overlayVisible){
+			this.overlay.blur();
+			return;
+		} else {
+			var coords = this.el.getCoordinates();
+			this.overlay.setStyles({
+				top: coords.bottom,
+				left: coords.left
+			});
+			this.overlay.focus();
+		}
+	}
+	
+});
\ No newline at end of file

Propchange: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.UI.ButtonOverlay.js
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.UI.MenuList.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.UI.MenuList.js?rev=917294&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.UI.MenuList.js (added)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.UI.MenuList.js Sun Feb 28 21:12:40 2010
@@ -0,0 +1,109 @@
+/*
+Script: MooEditable.UI.MenuList.js
+	UI Class to create a menu list (select) element.
+
+License:
+	MIT-style license.
+
+Copyright:
+	Copyright (c) 2007-2009 [Lim Chee Aun](http://cheeaun.com).
+*/
+
+MooEditable.UI.MenuList = new Class({
+
+	Implements: [Events, Options],
+
+	options: {
+		/*
+		onAction: $empty,
+		*/
+		title: '',
+		name: '',
+		'class': '',
+		list: []
+	},
+
+	initialize: function(options){
+		this.setOptions(options);
+		this.name = this.options.name;
+		this.render();
+	},
+	
+	toElement: function(){
+		return this.el;
+	},
+	
+	render: function(){
+		var self = this;
+		var html = '';
+		this.options.list.each(function(item){
+			html += '<option value="{value}">{text}</option>'.substitute(item);
+		});
+		this.el = new Element('select', {
+			'class': self.options['class'],
+			title: self.options.title,
+			html: html,
+			events: {
+				change: self.change.bind(self)
+			}
+		});
+		
+		this.disabled = false;
+
+		// add hover effect for IE
+		if (Browser.Engine.trident) this.el.addEvents({
+			mouseenter: function(e){ this.addClass('hover'); },
+			mouseleave: function(e){ this.removeClass('hover'); }
+		});
+		
+		return this;
+	},
+	
+	change: function(e){
+		e.preventDefault();
+		if (this.disabled) return;
+		var name = e.target.value;
+		this.action(name);
+	},
+	
+	action: function(){
+		this.fireEvent('action', [this].concat($A(arguments)));
+	},
+	
+	enable: function(){
+		if (!this.disabled) return;
+		this.disabled = false;
+		this.el.set('disabled', false).removeClass('disabled').set({
+			disabled: false,
+			opacity: 1
+		});
+		return this;
+	},
+	
+	disable: function(){
+		if (this.disabled) return;
+		this.disabled = true;
+		this.el.set('disabled', true).addClass('disabled').set({
+			disabled: true,
+			opacity: 0.4
+		});
+		return this;
+	},
+	
+	activate: function(value){
+		if (this.disabled) return;
+		var index = 0;
+		if (value) this.options.list.each(function(item, i){
+			if (item.value == value) index = i;
+		});
+		this.el.selectedIndex = index;
+		return this;
+	},
+	
+	deactivate: function(){
+		this.el.selectedIndex = 0;
+		this.el.removeClass('onActive');
+		return this;
+	}
+	
+});
\ No newline at end of file

Propchange: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.UI.MenuList.js
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.js?rev=917294&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.js (added)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.js Sun Feb 28 21:12:40 2010
@@ -0,0 +1,1203 @@
+/*
+Script: MooEditable.js
+	Class for creating a WYSIWYG editor, for contentEditable-capable browsers.
+
+License:
+	MIT-style license.
+
+Copyright:
+	Copyright (c) 2007-2009 [Lim Chee Aun](http://cheeaun.com).
+	
+Build: %build%
+
+Credits:
+	- Code inspired by Stefan's work [Safari Supports Content Editing!](http://www.xs4all.nl/~hhijdra/stefan/ContentEditable.html) from [safari gets contentEditable](http://walkah.net/blog/walkah/safari-gets-contenteditable)
+	- Main reference from Peter-Paul Koch's [execCommand compatibility](http://www.quirksmode.org/dom/execCommand.html)
+	- Some ideas and code inspired by [TinyMCE](http://tinymce.moxiecode.com/)
+	- Some functions inspired by Inviz's [Most tiny wysiwyg you ever seen](http://forum.mootools.net/viewtopic.php?id=746), [mooWyg (Most tiny WYSIWYG 2.0)](http://forum.mootools.net/viewtopic.php?id=5740)
+	- Some regex from Cameron Adams's [widgEditor](http://widgeditor.googlecode.com/)
+	- Some code from Juan M Martinez's [jwysiwyg](http://jwysiwyg.googlecode.com/)
+	- Some reference from MoxieForge's [PunyMCE](http://punymce.googlecode.com/)
+	- IE support referring Robert Bredlau's [Rich Text Editing](http://www.rbredlau.com/drupal/node/6)
+	- Tango icons from the [Tango Desktop Project](http://tango.freedesktop.org/)
+	- Additional Tango icons from Jimmacs' [Tango OpenOffice](http://www.gnome-look.org/content/show.php/Tango+OpenOffice?content=54799)
+*/
+
+var MooEditable = new Class({
+
+	Implements: [Events, Options],
+
+	options: {
+		toolbar: true,
+		cleanup: true,
+		paragraphise: true,
+		xhtml : true,
+		semantics : true,
+		actions: 'bold italic underline strikethrough | insertunorderedlist insertorderedlist indent outdent | undo redo | createlink unlink | urlimage | toggleview',
+		handleSubmit: true,
+		handleLabel: true,
+		baseCSS: 'html{ height: 100%; cursor: text } body{ font-family: sans-serif; border: 0; }',
+		extraCSS: '',
+		externalCSS: '',
+		html: '<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><style>{BASECSS} {EXTRACSS}</style>{EXTERNALCSS}</head><body>{CONTENT}</body></html>'
+	},
+
+	initialize: function(el, options){
+		this.setOptions(options);
+		this.textarea = document.id(el);
+		this.textarea.store('MooEditable', this);
+		this.actions = this.options.actions.clean().split(' ');
+		this.keys = {};
+		this.dialogs = {};
+		this.actions.each(function(action){
+			var act = MooEditable.Actions[action];
+			if (!act) return;
+			if (act.options){
+				var key = act.options.shortcut;
+				if (key) this.keys[key] = action;
+			}
+			if (act.dialogs){
+				$each(act.dialogs, function(dialog, name){
+					dialog = dialog.attempt(this);
+					dialog.name = action + ':' + name;
+					if ($type(this.dialogs[action]) != 'object') this.dialogs[action] = {};
+					this.dialogs[action][name] = dialog;
+				}, this);
+			}
+			if (act.events){
+				$each(act.events, function(fn, event){
+					this.addEvent(event, fn);
+				}, this);
+			}
+		}.bind(this));
+		this.render();
+	},
+	
+	toElement: function(){
+		return this.textarea;
+	},
+	
+	render: function(){
+		var self = this;
+		
+		// Dimensions
+		var dimensions = this.textarea.getSize();
+		
+		// Build the container
+		this.container = new Element('div', {
+			id: (this.textarea.id) ? this.textarea.id + '-mooeditable-container' : null,
+			'class': 'mooeditable-container',
+			styles: {
+				width: dimensions.x
+			}
+		});
+
+		// Override all textarea styles
+		this.textarea.addClass('mooeditable-textarea').setStyle('height', dimensions.y);
+		
+		// Build the iframe
+		this.iframe = new IFrame({
+			'class': 'mooeditable-iframe',
+			styles: {
+				height: dimensions.y
+			}
+		});
+		
+		this.toolbar = new MooEditable.UI.Toolbar({
+			onItemAction: function(){
+				var args = $splat(arguments);
+				var item = args[0];
+				self.action(item.name, args);
+			}
+		});
+		this.attach.delay(1, this);
+		
+		// Update the event for textarea's corresponding labels
+		if (this.options.handleLabel && this.textarea.id) $$('label[for="'+this.textarea.id+'"]').addEvent('click', function(e){
+			if (self.mode != 'iframe') return;
+			e.preventDefault();
+			self.focus();
+		});
+
+		// Update & cleanup content before submit
+		if (this.options.handleSubmit){
+			this.form = this.textarea.getParent('form');
+			if (!this.form) return;
+			this.form.addEvent('submit', function(){
+				if (self.mode == 'iframe') self.saveContent();
+			});
+		}
+		
+		this.fireEvent('render', this);
+	},
+
+	attach: function(){
+		var self = this;
+
+		// Assign view mode
+		this.mode = 'iframe';
+		
+		// Editor iframe state
+		this.editorDisabled = false;
+
+		// Put textarea inside container
+		this.container.wraps(this.textarea);
+
+		this.textarea.setStyle('display', 'none');
+		
+		this.iframe.setStyle('display', '').inject(this.textarea, 'before');
+		
+		$each(this.dialogs, function(action, name){
+			$each(action, function(dialog){
+				document.id(dialog).inject(self.iframe, 'before');
+				var range;
+				dialog.addEvents({
+					open: function(){
+						range = self.selection.getRange();
+						self.editorDisabled = true;
+						self.toolbar.disable(name);
+						self.fireEvent('dialogOpen', this);
+					},
+					close: function(){
+						self.toolbar.enable();
+						self.editorDisabled = false;
+						self.focus();
+						if (range) self.selection.setRange(range);
+						self.fireEvent('dialogClose', this);
+					}
+				});
+			});
+		});
+
+		// contentWindow and document references
+		this.win = this.iframe.contentWindow;
+		this.doc = this.win.document;
+
+		// Build the content of iframe
+		var docHTML = this.options.html.substitute({
+			BASECSS: this.options.baseCSS,
+			EXTRACSS: this.options.extraCSS,
+			EXTERNALCSS: (this.options.externalCSS) ? '<link rel="stylesheet" href="' + this.options.externalCSS + '">': '',
+			CONTENT: this.cleanup(this.textarea.get('value'))
+		});
+		this.doc.open();
+		this.doc.write(docHTML);
+		this.doc.close();
+
+		// Turn on Design Mode
+		// IE fired load event twice if designMode is set
+		(Browser.Engine.trident) ? this.doc.body.contentEditable = true : this.doc.designMode = 'On';
+
+		// Mootoolize window, document and body
+		if (!this.win.$family) new Window(this.win);
+		if (!this.doc.$family) new Document(this.doc);
+		document.id(this.doc.body);
+
+		// Bind all events
+		this.doc.addEvents({
+			mouseup: this.editorMouseUp.bind(this),
+			mousedown: this.editorMouseDown.bind(this),
+			contextmenu: this.editorContextMenu.bind(this),
+			click: this.editorClick.bind(this),
+			dbllick: this.editorDoubleClick.bind(this),
+			keypress: this.editorKeyPress.bind(this),
+			keyup: this.editorKeyUp.bind(this),
+			keydown: this.editorKeyDown.bind(this)
+		});
+		this.textarea.addEvent('keypress', this.textarea.retrieve('mooeditable:textareaKeyListener', this.keyListener.bind(this)));
+		
+		// Fix window focus event not firing on Firefox 2
+		if (Browser.Engine.gecko && Browser.Engine.version == 18) this.doc.addEvent('focus', function(){
+			self.win.fireEvent('focus').focus();
+		});
+
+		// styleWithCSS, not supported in IE and Opera
+		if (!(/trident|presto/i).test(Browser.Engine.name)){
+			var styleCSS = function(){
+				self.execute('styleWithCSS', false, false);
+				self.doc.removeEvent('focus', styleCSS);
+			};
+			this.win.addEvent('focus', styleCSS);
+		}
+
+		if (this.options.toolbar){
+			document.id(this.toolbar).inject(this.container, 'top');
+			this.toolbar.render(this.actions);
+		}
+
+		this.selection = new MooEditable.Selection(this.win);
+		
+		this.fireEvent('attach', this);
+		
+		return this;
+	},
+	
+	detach: function(){
+		this.saveContent();
+		this.textarea.setStyle('display', '').removeClass('mooeditable-textarea').inject(this.container, 'before');
+		this.textarea.removeEvent('keypress', this.textarea.retrieve('mooeditable:textareaKeyListener'));
+		this.container.dispose();
+		this.fireEvent('detach', this);
+		return this;
+	},
+	
+	editorMouseUp: function(e){
+		if (this.editorDisabled){
+			e.stop();
+			return;
+		}
+		
+		if (this.options.toolbar) this.checkStates();
+		
+		this.fireEvent('editorMouseUp', e);
+	},
+	
+	editorMouseDown: function(e){
+		if (this.editorDisabled){
+			e.stop();
+			return;
+		}
+		
+		this.fireEvent('editorMouseDown', e);
+	},
+	
+	editorContextMenu: function(e){
+		if (this.editorDisabled){
+			e.stop();
+			return;
+		}
+		
+		this.fireEvent('editorContextMenu', e);
+	},
+	
+	editorClick: function(e){
+		// make images selectable and draggable in Safari
+		if (Browser.Engine.webkit){
+			var el = e.target;
+			if (el.get('tag') == 'img'){
+				this.selection.selectNode(el);
+			}
+		}
+		
+		this.fireEvent('editorClick', e);
+	},
+	
+	editorDoubleClick: function(e){
+		this.fireEvent('editorDoubleClick', e);
+	},
+	
+	editorKeyPress: function(e){
+		if (this.editorDisabled){
+			e.stop();
+			return;
+		}
+		
+		this.keyListener(e);
+		
+		this.fireEvent('editorKeyPress', e);
+	},
+	
+	editorKeyUp: function(e){
+		if (this.editorDisabled){
+			e.stop();
+			return;
+		}
+		
+		if (this.options.toolbar) this.checkStates();
+		
+		this.fireEvent('editorKeyUp', e);
+	},
+	
+	editorKeyDown: function(e){
+		if (this.editorDisabled){
+			e.stop();
+			return;
+		}
+		
+		if (e.key == 'enter'){
+			if (this.options.paragraphise){
+				if (e.shift && Browser.Engine.webkit){
+					var s = this.selection;
+					var r = s.getRange();
+					
+					// Insert BR element
+					var br = this.doc.createElement('br');
+					r.insertNode(br);
+					
+					// Place caret after BR
+					r.setStartAfter(br);
+					r.setEndAfter(br);
+					s.setRange(r);
+					
+					// Could not place caret after BR then insert an nbsp entity and move the caret
+					if (s.getSelection().focusNode == br.previousSibling){
+						var nbsp = this.doc.createTextNode('\u00a0');
+						var p = br.parentNode;
+						var ns = br.nextSibling;
+						(ns) ? p.insertBefore(nbsp, ns) : p.appendChild(nbsp);
+						s.selectNode(nbsp);
+						s.collapse(1);
+					}
+					
+					// Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117
+					this.win.scrollTo(0, Element.getOffsets(s.getRange().startContainer).y);
+					
+					e.preventDefault();
+				} else if (Browser.Engine.gecko || Browser.Engine.webkit){
+					var node = this.selection.getNode();
+					var blockEls = /^(H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD)$/;
+					var isBlock = node.getParents().include(node).some(function(el){
+						return el.nodeName.test(blockEls);
+					});
+					if (!isBlock) this.execute('insertparagraph');
+				}
+			} else {
+				if (Browser.Engine.trident){
+					var r = this.selection.getRange();
+					var node = this.selection.getNode();
+					if (r && node.get('tag') != 'li'){
+						this.selection.insertContent('<br>');
+						this.selection.collapse(false);
+					}
+					e.preventDefault();
+				}
+			}
+		}
+		
+		this.fireEvent('editorKeyDown', e);
+	},
+	
+	keyListener: function(e){
+		var key = (Browser.Platform.mac) ? e.meta : e.control;
+		if (!key || !this.keys[e.key]) return;
+		e.preventDefault();
+		var item = this.toolbar.getItem(this.keys[e.key]);
+		item.action(e);
+	},
+
+	focus: function(){
+		// needs the delay to get focus working
+		(function(){ 
+			(this.mode == 'iframe' ? this.win : this.textarea).focus();
+			this.fireEvent('focus', this);
+		}).bind(this).delay(10);
+		return this;
+	},
+
+	action: function(command, args){
+		var action = MooEditable.Actions[command];
+		if (action.command && $type(action.command) == 'function'){
+			action.command.run(args, this);
+		} else {
+			this.focus();
+			this.execute(command, false, args);
+			if (this.mode == 'iframe') this.checkStates();
+		}
+	},
+
+	execute: function(command, param1, param2){
+		if (this.busy) return;
+		this.busy = true;
+		this.doc.execCommand(command, param1, param2);
+		this.saveContent();
+		this.busy = false;
+		return false;
+	},
+
+	toggleView: function(){
+		this.fireEvent('beforeToggleView', this);
+		if (this.mode == 'textarea'){
+			this.mode = 'iframe';
+			this.iframe.setStyle('display', '');
+			this.setContent(this.textarea.value);
+			this.textarea.setStyle('display', 'none');
+		} else {
+			this.saveContent();
+			this.mode = 'textarea';
+			this.textarea.setStyle('display', '');
+			this.iframe.setStyle('display', 'none');
+		}
+		this.fireEvent('toggleView', this);
+		this.focus();
+		return this;
+	},
+
+	getContent: function(){
+		return this.cleanup(this.doc.body.get('html'));
+	},
+
+	setContent: function(newContent){
+		this.doc.body.set('html', newContent);
+		return this;
+	},
+
+	saveContent: function(){
+		if (this.mode == 'iframe') this.textarea.set('value', this.getContent());
+		return this;
+	},
+
+	checkStates: function(){
+		this.actions.each(function(action){
+			var item = this.toolbar.getItem(action);
+			if (!item) return;
+			item.deactivate();
+
+			var states = MooEditable.Actions[action]['states'];
+			if (!states) return;
+			
+			var el = this.selection.getNode();
+			if (!el) return;
+			
+			// custom checkState
+			if ($type(states) == 'function'){
+				states.attempt(el, item);
+				return;
+			}
+			
+			try{
+				if (this.doc.queryCommandState(action)){
+					item.activate();
+					return;
+				}
+			} catch(e) {}
+			
+			if (states.tags){
+				do {
+					if ($type(el) != 'element') break;
+					var tag = el.tagName.toLowerCase();
+					if (states.tags.contains(tag)){
+						item.activate(tag);
+						break;
+					}
+				}
+				while (el = el.parentNode);
+			}
+
+			if (states.css){
+				var blockEls = /^(H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD)$/;
+				do {
+					if ($type(el) != 'element') break;
+					var found = false;
+					for (var prop in states.css){
+						var css = states.css[prop];
+						if (document.id(el).getStyle(prop).contains(css)){
+							item.activate(css);
+							found = true;
+						}
+					}
+					if (found || el.tagName.test(blockEls)) break;
+				}
+				while (el = el.parentNode);
+			}
+		}.bind(this));
+	},
+
+	cleanup: function(source){
+		if (!this.options.cleanup) return source.trim();
+		
+		do {
+			var oSource = source;
+
+			// Webkit cleanup
+			source = source.replace(/<br class\="webkit-block-placeholder">/gi, "<br />");
+			source = source.replace(/<span class="Apple-style-span">(.*)<\/span>/gi, '$1');
+			source = source.replace(/ class="Apple-style-span"/gi, '');
+			source = source.replace(/<span style="">/gi, '');
+
+			// Remove padded paragraphs
+			source = source.replace(/<p>\s*<br ?\/?>\s*<\/p>/gi, '<p>\u00a0</p>');
+			source = source.replace(/<p>(&nbsp;|\s)*<\/p>/gi, '<p>\u00a0</p>');
+			if (!this.options.semantics){
+				source = source.replace(/\s*<br ?\/?>\s*<\/p>/gi, '</p>');
+			}
+
+			// Replace improper BRs (only if XHTML : true)
+			if (this.options.xhtml){
+				source = source.replace(/<br>/gi, "<br />");
+			}
+
+			if (this.options.semantics){
+				//remove divs from <li>
+				if (Browser.Engine.trident){
+					source = source.replace(/<li>\s*<div>(.+?)<\/div><\/li>/g, '<li>$1</li>');
+				}
+				//remove stupid apple divs
+				if (Browser.Engine.webkit){
+					source = source.replace(/^([\w\s]+.*?)<div>/i, '<p>$1</p><div>');
+					source = source.replace(/<div>(.+?)<\/div>/ig, '<p>$1</p>');
+				}
+
+				//<p> tags around a list will get moved to after the list
+				if (['gecko', 'presto', 'webkit'].contains(Browser.Engine.name)){
+					//not working properly in safari?
+					source = source.replace(/<p>[\s\n]*(<(?:ul|ol)>.*?<\/(?:ul|ol)>)(.*?)<\/p>/ig, '$1<p>$2</p>');
+					source = source.replace(/<\/(ol|ul)>\s*(?!<(?:p|ol|ul|img).*?>)((?:<[^>]*>)?\w.*)$/g, '</$1><p>$2</p>');
+				}
+
+				source = source.replace(/<br[^>]*><\/p>/g, '</p>');			//remove <br>'s that end a paragraph here.
+				source = source.replace(/<p>\s*(<img[^>]+>)\s*<\/p>/ig, '$1\n'); 	//if a <p> only contains <img>, remove the <p> tags
+
+				//format the source
+				source = source.replace(/<p([^>]*)>(.*?)<\/p>(?!\n)/g, '<p$1>$2</p>\n');  	//break after paragraphs
+				source = source.replace(/<\/(ul|ol|p)>(?!\n)/g, '</$1>\n'); 			//break after </p></ol></ul> tags
+				source = source.replace(/><li>/g, '>\n\t<li>'); 				//break and indent <li>
+				source = source.replace(/([^\n])<\/(ol|ul)>/g, '$1\n</$2>');  			//break before </ol></ul> tags
+				source = source.replace(/([^\n])<img/ig, '$1\n<img'); 				//move images to their own line
+				source = source.replace(/^\s*$/g, '');						//delete empty lines in the source code (not working in opera)
+			}
+
+			// Remove leading and trailing BRs
+			source = source.replace(/<br ?\/?>$/gi, '');
+			source = source.replace(/^<br ?\/?>/gi, '');
+
+			// Remove useless BRs
+			source = source.replace(/><br ?\/?>/gi, '>');
+
+			// Remove BRs right before the end of blocks
+			source = source.replace(/<br ?\/?>\s*<\/(h1|h2|h3|h4|h5|h6|li|p)/gi, '</$1');
+
+			// Semantic conversion
+			source = source.replace(/<span style="font-weight: bold;">(.*)<\/span>/gi, '<strong>$1</strong>');
+			source = source.replace(/<span style="font-style: italic;">(.*)<\/span>/gi, '<em>$1</em>');
+			source = source.replace(/<b\b[^>]*>(.*?)<\/b[^>]*>/gi, '<strong>$1</strong>');
+			source = source.replace(/<i\b[^>]*>(.*?)<\/i[^>]*>/gi, '<em>$1</em>');
+			source = source.replace(/<u\b[^>]*>(.*?)<\/u[^>]*>/gi, '<span style="text-decoration: underline;">$1</span>');
+
+			// Replace uppercase element names with lowercase
+			source = source.replace(/<[^> ]*/g, function(match){return match.toLowerCase();});
+
+			// Replace uppercase attribute names with lowercase
+			source = source.replace(/<[^>]*>/g, function(match){
+				   match = match.replace(/ [^=]+=/g, function(match2){return match2.toLowerCase();});
+				   return match;
+			});
+
+			// Put quotes around unquoted attributes
+			source = source.replace(/<[^>]*>/g, function(match){
+				   match = match.replace(/( [^=]+=)([^"][^ >]*)/g, "$1\"$2\"");
+				   return match;
+			});
+
+			//make img tags xhtml compatable
+			//           if (this.options.xhtml){
+			//                source = source.replace(/(<(?:img|input)[^/>]*)>/g, '$1 />');
+			//           }
+
+			//remove double <p> tags and empty <p> tags
+			source = source.replace(/<p>(?:\s*)<p>/g, '<p>');
+			source = source.replace(/<\/p>\s*<\/p>/g, '</p>');
+			source = source.replace(/<p>\W*<\/p>/g, '');
+
+			// Final trim
+			source = source.trim();
+		}
+		while (source != oSource);
+
+		return source;
+	}
+
+});
+
+MooEditable.Selection = new Class({
+
+	initialize: function(win){
+		this.win = win;
+	},
+
+	getSelection: function(){
+		this.win.focus();
+		return (this.win.getSelection) ? this.win.getSelection() : this.win.document.selection;
+	},
+
+	getRange: function(){
+		var s = this.getSelection();
+
+		if (!s) return null;
+
+		try {
+			return s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : null);
+		} catch(e) {
+			// IE bug when used in frameset
+			return this.doc.body.createTextRange();
+		}
+	},
+
+	setRange: function(range){
+		if (range.select){
+			$try(function(){
+				range.select();
+			});
+		} else {
+			var s = this.getSelection();
+			if (s.addRange){
+				s.removeAllRanges();
+				s.addRange(range);
+			}
+		}
+	},
+
+	selectNode: function(node, collapse){
+		var r = this.getRange();
+		var s = this.getSelection();
+
+		if (r.moveToElementText){
+			$try(function(){
+				r.moveToElementText(node);
+				r.select();
+			});
+		} else if (s.addRange){
+			collapse ? r.selectNodeContents(node) : r.selectNode(node);
+			s.removeAllRanges();
+			s.addRange(r);
+		} else {
+			s.setBaseAndExtent(node, 0, node, 1);
+		}
+
+		return node;
+	},
+
+	isCollapsed: function(){
+		var r = this.getRange();
+		if (r.item) return false;
+		return r.boundingWidth == 0 || this.getSelection().isCollapsed;
+	},
+
+	collapse: function(toStart){
+		var r = this.getRange();
+		var s = this.getSelection();
+
+		if (r.select){
+			r.collapse(toStart);
+			r.select();
+		} else {
+			toStart ? s.collapseToStart() : s.collapseToEnd();
+		}
+	},
+
+	getContent: function(){
+		var r = this.getRange();
+		var body = new Element('body');
+
+		if (this.isCollapsed()) return '';
+
+		if (r.cloneContents){
+			body.appendChild(r.cloneContents());
+		} else if ($defined(r.item) || $defined(r.htmlText)){
+			body.set('html', r.item ? r.item(0).outerHTML : r.htmlText);
+		} else {
+			body.set('html', r.toString());
+		}
+
+		var content = body.get('html');
+		return content;
+	},
+
+	getText : function(){
+		var r = this.getRange();
+		var s = this.getSelection();
+
+		return this.isCollapsed() ? '' : r.text || s.toString();
+	},
+
+	getNode: function(){
+		var r = this.getRange();
+
+		if (!Browser.Engine.trident){
+			var el = null;
+
+			if (r){
+				el = r.commonAncestorContainer;
+
+				// Handle selection a image or other control like element such as anchors
+				if (!r.collapsed)
+					if (r.startContainer == r.endContainer)
+						if (r.startOffset - r.endOffset < 2)
+							if (r.startContainer.hasChildNodes())
+								el = r.startContainer.childNodes[r.startOffset];
+
+				while ($type(el) != 'element') el = el.parentNode;
+			}
+
+			return document.id(el);
+		}
+
+		return document.id(r.item ? r.item(0) : r.parentElement());
+	},
+
+	insertContent: function(content){
+		if (Browser.Engine.trident){
+			var r = this.getRange();
+			r.pasteHTML(content);
+			r.collapse(false);
+			r.select();
+		} else {
+			this.win.document.execCommand('insertHTML', false, content);
+		}
+	}
+
+});
+
+MooEditable.UI = {};
+
+MooEditable.UI.Toolbar= new Class({
+
+	Implements: [Events, Options],
+
+	options: {
+		/*
+		onItemAction: $empty,
+		*/
+		'class': ''
+	},
+    
+	initialize: function(options){
+		this.setOptions(options);
+		this.el = new Element('div', {'class': 'mooeditable-ui-toolbar ' + this.options['class']});
+		this.items = {};
+		this.content = null;
+	},
+	
+	toElement: function(){
+		return this.el;
+	},
+	
+	render: function(actions){
+		if (this.content){
+			this.el.adopt(this.content);
+		} else {
+			this.content = actions.map(function(action){
+				return (action == '|') ? this.addSeparator() : this.addItem(action);
+			}.bind(this));
+		}
+		return this;
+	},
+	
+	addItem: function(action){
+		var self = this;
+		var act = MooEditable.Actions[action];
+		if (!act) return;
+		var type = act.type || 'button';
+		var options = act.options || {};
+		var item = new MooEditable.UI[type.camelCase().capitalize()]($extend(options, {
+			name: action,
+			'class': action + '-item toolbar-item',
+			title: act.title,
+			onAction: self.itemAction.bind(self)
+		}));
+		this.items[action] = item;
+		document.id(item).inject(this.el);
+		return item;
+	},
+	
+	getItem: function(action){
+		return this.items[action];
+	},
+	
+	addSeparator: function(){
+		return new Element('span', {'class': 'toolbar-separator'}).inject(this.el);
+	},
+	
+	itemAction: function(){
+		this.fireEvent('itemAction', arguments);
+	},
+
+	disable: function(except){
+		$each(this.items, function(item){
+			(item.name == except) ? item.activate() : item.deactivate().disable();
+		});
+		return this;
+	},
+
+	enable: function(){
+		$each(this.items, function(item){
+			item.enable();
+		});
+		return this;
+	},
+	
+	show: function(){
+		this.el.setStyle('display', '');
+		return this;
+	},
+	
+	hide: function(){
+		this.el.setStyle('display', 'none');
+		return this;
+	}
+	
+});
+
+MooEditable.UI.Button = new Class({
+
+	Implements: [Events, Options],
+
+	options: {
+		/*
+		onAction: $empty,
+		*/
+		title: '',
+		name: '',
+		text: 'Button',
+		'class': '',
+		shortcut: '',
+		mode: 'icon'
+	},
+
+	initialize: function(options){
+		this.setOptions(options);
+		this.name = this.options.name;
+		this.render();
+	},
+	
+	toElement: function(){
+		return this.el;
+	},
+	
+	render: function(){
+		var self = this;
+		var key = (Browser.Platform.mac) ? 'Cmd' : 'Ctrl';
+		var shortcut = (this.options.shortcut) ? ' ( ' + key + '+' + this.options.shortcut.toUpperCase() + ' )' : '';
+		var text = this.options.title || name;
+		var title = text + shortcut;
+		this.el = new Element('button', {
+			'class': 'mooeditable-ui-button ' + self.options['class'],
+			title: title,
+			html: '<span class="button-icon"></span><span class="button-text">' + text + '</span>',
+			events: {
+				click: self.click.bind(self),
+				mousedown: function(e){ e.preventDefault(); }
+			}
+		});
+		if (this.options.mode != 'icon') this.el.addClass('mooeditable-ui-button-' + this.options.mode);
+		
+		this.active = false;
+		this.disabled = false;
+
+		// add hover effect for IE
+		if (Browser.Engine.trident) this.el.addEvents({
+			mouseenter: function(e){ this.addClass('hover'); },
+			mouseleave: function(e){ this.removeClass('hover'); }
+		});
+		
+		return this;
+	},
+	
+	click: function(e){
+		e.preventDefault();
+		if (this.disabled) return;
+		this.action(e);
+	},
+	
+	action: function(){
+		this.fireEvent('action', [this].concat($A(arguments)));
+	},
+	
+	enable: function(){
+		if (this.active) this.el.removeClass('onActive');
+		if (!this.disabled) return;
+		this.disabled = false;
+		this.el.removeClass('disabled').set({
+			disabled: false,
+			opacity: 1
+		});
+		return this;
+	},
+	
+	disable: function(){
+		if (this.disabled) return;
+		this.disabled = true;
+		this.el.addClass('disabled').set({
+			disabled: true,
+			opacity: 0.4
+		});
+		return this;
+	},
+	
+	activate: function(){
+		if (this.disabled) return;
+		this.active = true;
+		this.el.addClass('onActive');
+		return this;
+	},
+	
+	deactivate: function(){
+		this.active = false;
+		this.el.removeClass('onActive');
+		return this;
+	}
+	
+});
+
+MooEditable.UI.Dialog = new Class({
+
+	Implements: [Events, Options],
+
+	options:{
+		/*
+		onOpen: $empty,
+		onClose: $empty,
+		*/
+		'class': '',
+		contentClass: ''
+	},
+
+	initialize: function(html, options){
+		this.setOptions(options);
+		this.html = html;
+		
+		var self = this;
+		this.el = new Element('div', {
+			'class': 'mooeditable-ui-dialog ' + self.options['class'],
+			html: '<div class="dialog-content ' + self.options.contentClass + '">' + html + '</div>',
+			styles: {
+				'display': 'none'
+			},
+			events: {
+				click: self.click.bind(self)
+			}
+		});
+	},
+	
+	toElement: function(){
+		return this.el;
+	},
+	
+	click: function(){
+		this.fireEvent('click', arguments);
+		return this;
+	},
+	
+	open: function(){
+		this.el.setStyle('display', '');
+		this.fireEvent('open', this);
+		return this;
+	},
+	
+	close: function(){
+		this.el.setStyle('display', 'none');
+		this.fireEvent('close', this);
+		return this;
+	}
+
+});
+
+MooEditable.UI.AlertDialog = function(alertText){
+	if (!alertText) return;
+	var html = alertText + ' <button class="dialog-ok-button">OK</button>';
+	return new MooEditable.UI.Dialog(html, {
+		'class': 'mooeditable-alert-dialog',
+		onOpen: function(){
+			var button = this.el.getElement('.dialog-ok-button');
+			(function(){
+				button.focus();
+			}).delay(10);
+		},
+		onClick: function(e){
+			e.preventDefault();
+			if (e.target.tagName.toLowerCase() != 'button') return;
+			if (document.id(e.target).hasClass('dialog-ok-button')) this.close();
+		}
+	});
+};
+
+MooEditable.UI.PromptDialog = function(questionText, answerText, fn){
+	if (!questionText) return;
+	var html = '<label class="dialog-label">' + questionText
+		+ ' <input type="text" class="text dialog-input" value="' + answerText + '">'
+		+ '</label> <button class="dialog-button dialog-ok-button">OK</button>'
+		+ '<button class="dialog-button dialog-cancel-button">Cancel</button>';
+	return new MooEditable.UI.Dialog(html, {
+		'class': 'mooeditable-prompt-dialog',
+		onOpen: function(){
+			var input = this.el.getElement('.dialog-input');
+			(function(){
+				input.focus();
+				input.select();
+			}).delay(10);
+		},
+		onClick: function(e){
+			e.preventDefault();
+			if (e.target.tagName.toLowerCase() != 'button') return;
+			var button = document.id(e.target);
+			var input = this.el.getElement('.dialog-input');
+			if (button.hasClass('dialog-cancel-button')){
+				input.set('value', answerText);
+				this.close();
+			} else if (button.hasClass('dialog-ok-button')){
+				var answer = input.get('value');
+				input.set('value', answerText);
+				this.close();
+				if (fn) fn.attempt(answer, this);
+			}
+		}
+	});
+};
+
+MooEditable.Actions = new Hash({
+
+	bold: {
+		title: 'Bold',
+		options: {
+			shortcut: 'b'
+		},
+		states: {
+			tags: ['b', 'strong'],
+			css: {'font-weight': 'bold'}
+		}
+	},
+	
+	italic: {
+		title: 'Italic',
+		options: {
+			shortcut: 'i'
+		},
+		states: {
+			tags: ['i', 'em'],
+			css: {'font-style': 'italic'}
+		}
+	},
+	
+	underline: {
+		title: 'Underline',
+		options: {
+			shortcut: 'u'
+		},
+		states: {
+			tags: ['u'],
+			css: {'text-decoration': 'underline'}
+		}
+	},
+	
+	strikethrough: {
+		title: 'Strikethrough',
+		options: {
+			shortcut: 's'
+		},
+		states: {
+			tags: ['s', 'strike'],
+			css: {'text-decoration': 'line-through'}
+		}
+	},
+	
+	insertunorderedlist: {
+		title: 'Unordered List',
+		states: {
+			tags: ['ul']
+		}
+	},
+	
+	insertorderedlist: {
+		title: 'Ordered List',
+		states: {
+			tags: ['ol']
+		}
+	},
+	
+	indent: {
+		title: 'Indent',
+		states: {
+			tags: ['blockquote']
+		}
+	},
+	
+	outdent: {
+		title: 'Outdent'
+	},
+	
+	undo: {
+		title: 'Undo',
+		options: {
+			shortcut: 'z'
+		}
+	},
+	
+	redo: {
+		title: 'Redo',
+		options: {
+			shortcut: 'y'
+		}
+	},
+	
+	unlink: {
+		title: 'Remove Hyperlink'
+	},
+
+	createlink: {
+		title: 'Add Hyperlink',
+		options: {
+			shortcut: 'l'
+		},
+		states: {
+			tags: ['a']
+		},
+		dialogs: {
+			alert: MooEditable.UI.AlertDialog.pass('Please select the text you wish to hyperlink.'),
+			prompt: function(editor){
+				return MooEditable.UI.PromptDialog('Enter URL', 'http://', function(url){
+					editor.execute('createlink', false, url.trim());
+				});
+			}
+		},
+		command: function(){
+			if (this.selection.isCollapsed()){
+				this.dialogs.createlink.alert.open();
+			} else {
+				var text = this.selection.getText();
+				var url = /^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i;
+				var prompt = this.dialogs.createlink.prompt;
+				if (url.test(text)) prompt.el.getElement('.mooeditable-dialog-input').set('value', text);
+				prompt.open();
+			}
+		}
+	},
+
+	urlimage: {
+		title: 'Add Image',
+		options: {
+			shortcut: 'm'
+		},
+		dialogs: {
+			prompt: function(editor){
+				return MooEditable.UI.PromptDialog('Enter image URL', 'http://', function(url){
+					editor.execute("insertimage", false, url.trim());
+				});
+			}
+		},
+		command: function(){
+			this.dialogs.urlimage.prompt.open();
+		}
+	},
+
+	toggleview: {
+		title: 'Toggle View',
+		command: function(){
+			(this.mode == 'textarea') ? this.toolbar.enable() : this.toolbar.disable('toggleview');
+			this.toggleView();
+		}
+	}
+
+});
+
+Element.Properties.mooeditable = {
+
+	set: function(options){
+		return this.eliminate('mooeditable').store('mooeditable:options', options);
+	},
+
+	get: function(options){
+		if (options || !this.retrieve('mooeditable')){
+			if (options || !this.retrieve('mooeditable:options')) this.set('mooeditable', options);
+			this.store('mooeditable', new MooEditable(this, this.retrieve('mooeditable:options')));
+		}
+		return this.retrieve('mooeditable');
+	}
+
+};
+
+Element.implement({
+
+	mooEditable: function(options){
+		return this.get('mooeditable', options);
+	}
+
+});
\ No newline at end of file

Propchange: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.js
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditableSilkTheme.css
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditableSilkTheme.css?rev=917294&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditableSilkTheme.css (added)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditableSilkTheme.css Sun Feb 28 21:12:40 2010
@@ -0,0 +1,73 @@
+/*
+ A change to the original MooEditable style
+ 
+ * Famfamfam Silk Iconset
+ * Darker gray color for mouse over selections 
+ * Rounded corners for buttons and overlays (only supported on WebKit and FireFox) 
+ 
+ Usage:
+        Include MooEditable.SilkTheme.css to overwrite the default settings
+        
+ 		<link rel="stylesheet" type="text/css" href="../../Assets/MooEditable/MooEditable.css">
+		<link rel="stylesheet" type="text/css" href="../../Assets/MooEditable/MooEditable.Extras.css">
+		<link rel="stylesheet" type="text/css" href="../../Assets/MooEditable/MooEditable.SilkTheme.css">
+ 
+ 
+ Author: Olivier Refalo
+ */
+
+
+.mooeditable-ui-toolbar .mooeditable-ui-button .button-icon{
+	background: transparent url(images/mooeditable-toolbarbuttons-silk.png) no-repeat 0 -8px;
+}
+
+.mooeditable-ui-toolbar .bold-item .button-icon{ background-position: 0 -16px; } 
+.mooeditable-ui-toolbar .createlink-item .button-icon{ background-position: 0 -32px; } 
+.mooeditable-ui-toolbar .indent-item .button-icon{ background-position: 0 -48px; } 
+.mooeditable-ui-toolbar .insertorderedlist-item .button-icon{ background-position: 0 -64px; } 
+.mooeditable-ui-toolbar .insertunorderedlist-item .button-icon{ background-position: 0 -80px; } 
+.mooeditable-ui-toolbar .italic-item .button-icon{ background-position: 0 -96px; } 
+.mooeditable-ui-toolbar .outdent-item .button-icon{ background-position: 0 -176px; } 
+.mooeditable-ui-toolbar .redo-item .button-icon{ background-position: 0 -208px; } 
+.mooeditable-ui-toolbar .strikethrough-item .button-icon{ background-position: 0 -224px; } 
+.mooeditable-ui-toolbar .toggleview-item .button-icon{ background-position: 0 -240px; } 
+.mooeditable-ui-toolbar .underline-item .button-icon{ background-position: 0 -256px; } 
+.mooeditable-ui-toolbar .undo-item .button-icon{ background-position: 0 -272px; } 
+.mooeditable-ui-toolbar .unlink-item .button-icon{ background-position: 0 -288px; } 
+.mooeditable-ui-toolbar .urlimage-item .button-icon{ background-position: 0 -304px; } 
+.mooeditable-ui-toolbar .forecolor-item .button-icon{background-position: 0 -192px;}
+.mooeditable-ui-toolbar .smiley-item .button-icon{
+	background: transparent url(Smiley/smile.png) no-repeat top left;
+}
+
+.mooeditable-ui-toolbar .justifycenter-item .button-icon,
+.mooeditable-ui-toolbar .justifyright-item .button-icon,
+.mooeditable-ui-toolbar .justifyfull-item .button-icon,
+.mooeditable-ui-toolbar .justifyleft-item .button-icon{
+	background-image: url(images/mooeditable-toolbarbuttons-silk.png);
+}
+.mooeditable-ui-toolbar .justifycenter-item .button-icon{ background-position: 0 -112px; } 
+.mooeditable-ui-toolbar .justifyfull-item .button-icon{ background-position: 0 -128px; } 
+.mooeditable-ui-toolbar .justifyleft-item .button-icon{ background-position: 0 -144px; } 
+.mooeditable-ui-toolbar .justifyright-item .button-icon{ background-position: 0 -160px; } 
+
+.mooeditable-ui-toolbar .mooeditable-ui-button:hover,
+.mooeditable-ui-toolbar .mooeditable-ui-button.hover{
+	-webkit-border-radius: 3px;
+	-moz-border-radius: 3px;
+	border-radius: 3px;
+}
+
+.mooeditable-ui-button-overlay{
+	-webkit-border-radius: 5px;
+	-moz-border-radius: 5px;
+	border-radius: 5px;
+}
+
+.mooeditable-ui-toolbar .mooeditable-ui-button.active,
+.mooeditable-ui-toolbar .mooeditable-ui-button.onActive{
+	background-color: #bbb;
+	-webkit-border-radius: 3px;
+	-moz-border-radius: 3px;
+	border-radius: 3px;
+}

Propchange: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditableSilkTheme.css
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/images/mooeditable-toolbarbuttons-silk.png
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/images/mooeditable-toolbarbuttons-silk.png?rev=917294&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/images/mooeditable-toolbarbuttons-silk.png
------------------------------------------------------------------------------
    svn:executable = *

Propchange: incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/images/mooeditable-toolbarbuttons-silk.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/jspwiki/trunk/src/WebContent/templates/default/AttachmentTab.jsp
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/templates/default/AttachmentTab.jsp?rev=917294&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/templates/default/AttachmentTab.jsp (added)
+++ incubator/jspwiki/trunk/src/WebContent/templates/default/AttachmentTab.jsp Sun Feb 28 21:12:40 2010
@@ -0,0 +1,159 @@
+<%--
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+--%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
+<%@ taglib uri="http://jakarta.apache.org/jspwiki.tld" prefix="wiki" %>
+<%@ taglib uri="http://stripes.sourceforge.net/stripes.tld" prefix="s" %>
+<%@ page import="org.apache.wiki.*" %>
+<%@ page import="javax.servlet.jsp.jstl.fmt.*" %>
+<%@ page import="org.apache.wiki.action.WikiContextFactory" %>
+<%@ page import="org.apache.wiki.util.TextUtil" %>
+<%@ page import="org.apache.wiki.api.WikiPage" %>
+<%@ page import="org.apache.wiki.content.jcr.JCRWikiPage" %>
+<%@ page import="java.text.SimpleDateFormat" %>
+<%@ page errorPage="${templates['Error.jsp']}" %>
+<%
+  int MAXATTACHNAMELENGTH = 30;
+  WikiContext c = WikiContextFactory.findContext( pageContext );
+  String progressId = c.getEngine().getProgressManager().getNewProgressIdentifier();
+
+  int attCount = c.getEngine().getAttachmentManager().listAttachments(c.getPage()).size();
+  String attTitle = LocaleSupport.getLocalizedMessage(pageContext, "attach.tab");
+  if( attCount != 0 ) attTitle += " (" + attCount + ")";
+%>
+<wiki:TabbedSection defaultTab="attach">
+
+  <wiki:Tab id="pagecontent" titleKey="view.tab" accesskey="v" url="Wiki.jsp?page=${wikiActionBean.page.name}"/>
+
+  <wiki:Tab id="attach" title="<%= attTitle %>" accesskey="a">
+    <div id="addattachment">
+      <h3><fmt:message key="attach.add" /></h3>
+      <wiki:Permission permission="upload">
+        <s:form beanclass="org.apache.wiki.action.AttachmentActionBean" class="wikiform" id="uploadform" acceptcharset="UTF-8">
+          <s:param name="progressid" value="<%=progressId%>" />
+          <s:param name="page" value="${wikiActionBean.page.name}" />
+          <table>
+          <tr>
+            <td colspan="2"><div class="formhelp"><fmt:message key="attach.add.info" /></div></td>
+          </tr>
+          <tr>
+            <td colspan="2"><s:errors field="newAttachments" /></td>
+          </tr>
+          <tr>
+            <td ><s:label for="attachfile" name="attach.add.selectfile" /></td>
+            <td><s:file name="newAttachments[0]" id="attachfile0" /></td>
+          </tr>
+          <tr>
+            <td><s:label for="attachnote" name="attach.add.changenote" /></td>
+            <td><s:text name="changenote" id="attachnote" maxlength="80" size="60" /></td>
+          </tr>
+
+          <tr>
+            <td></td>
+            <td>
+              <%-- FIXME localisation nok --%>
+              <s:submit name="upload" id="upload" />
+              <div id="progressbar"><div class="ajaxprogress"></div></div>
+            </td>
+          </tr>
+
+          </table>
+        </s:form>
+      </wiki:Permission>
+      <wiki:Permission permission="!upload">
+        <div class="formhelp"><fmt:message key="attach.add.permission" /></div>
+      </wiki:Permission>
+    </div>
+
+    <wiki:HasAttachments>
+
+      <h3><fmt:message key="attach.list" /></h3>
+
+      <%--<small><fmt:message key="attach.listsubtitle"/></small>--%>
+
+      <wiki:Permission permission="delete">
+        <%-- hidden delete form --%>
+      </wiki:Permission>
+
+      <div class="zebra-table">
+        <div class="slimbox-image sortable">
+          <table class="wikitable">
+            <tr>
+              <th><fmt:message key="info.attachment.type" /></th>
+              <th><fmt:message key="info.attachment.name" /></th>
+              <th><fmt:message key="info.size" /></th>
+              <th><fmt:message key="info.version" /></th>
+              <th><fmt:message key="info.date" /></th>
+              <th><fmt:message key="info.author" /></th>
+              <wiki:Permission permission="delete"><th><fmt:message key="info.actions" /></th></wiki:Permission>
+              <th class="changenote"><fmt:message key="info.changenote" /></th>
+            </tr>
+
+            <wiki:AttachmentsIterator id="att">
+    <%
+      String name = att.getFileName();
+      String sname = name;
+      if( sname.length() > MAXATTACHNAMELENGTH ) sname = sname.substring(0,MAXATTACHNAMELENGTH) + "...";
+
+      String mimetype = att.getContentType().replace('/','-');
+
+      java.util.Date modified = new SimpleDateFormat(JCRWikiPage.DATEFORMAT_ISO8601_2000).parse(att.getAttribute(JCRWikiPage.ATTR_CREATED).toString());
+    %>
+              <tr>
+              	<%-- The 'title' attribute is used to sort empty cells --%>
+                <td title="<%= att.getContentType() %>"><div class="mime <%= mimetype %>" /></td>
+                <td><wiki:LinkTo title="<%= name %>"><%= sname %></wiki:LinkTo></td>
+                <td style="white-space:nowrap;text-align:right;">
+                  <fmt:formatNumber value='<%=Double.toString(att.getSize()/1000.0)%>' maxFractionDigits='1' minFractionDigits='1' />&nbsp;<fmt:message key="info.kilobytes" />
+                </td>
+                <td style="text-align:center;">
+                  <a href="<wiki:PageInfoLink format='url' />" title="<fmt:message key='attach.moreinfo.title' />"><wiki:PageVersion/></a>
+                </td>
+          	    <td jspwiki:sortvalue="<%= modified.getTime() %>">
+            	    <fmt:formatDate value="<%= modified %>" pattern="${prefs.TimeFormat}" timeZone="${prefs.TimeZone}" />
+            	</td>
+                <td style="white-space:nowrap;" ><wiki:Author/></td>
+                <wiki:Permission permission="delete">
+                  <td>
+                    <input type="button" value="<fmt:message key='attach.delete' />" src="<wiki:Link format='url' context='<%=WikiContext.DELETE%>' />" onclick="$('deleteForm').setProperty('action',this.src); $('delete-all').click();" />
+                  </td>
+                </wiki:Permission>
+                <td class="changenote">
+<%
+        //FIXME: should probably also become a JCRWikiPage attriute.
+        String changeNote = TextUtil.replaceEntities((String)att.getAttribute(WikiPage.CHANGENOTE));
+        if( changeNote != null ) {
+        %><%=changeNote%><%
+        }
+%>
+                </td>
+              </tr>
+            </wiki:AttachmentsIterator>
+
+          </table>
+        </div>
+      </div>
+
+    </wiki:HasAttachments>
+  </wiki:Tab>
+
+  <wiki:Tab id="info" titleKey="info.tab" url="PageInfo.jsp?page=${wikiActionBean.page.name}" accesskey="i" />
+
+</wiki:TabbedSection>

Added: incubator/jspwiki/trunk/src/WebContent/templates/default/DefaultLayout.jsp
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/templates/default/DefaultLayout.jsp?rev=917294&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/templates/default/DefaultLayout.jsp (added)
+++ incubator/jspwiki/trunk/src/WebContent/templates/default/DefaultLayout.jsp Sun Feb 28 21:12:40 2010
@@ -0,0 +1,195 @@
+<%--
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+--%>
+<%@ taglib uri="http://jakarta.apache.org/jspwiki.tld" prefix="wiki" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
+<%@ taglib uri="http://stripes.sourceforge.net/stripes.tld" prefix="s" %>
+<%@ page import="org.apache.wiki.WikiContext" %>
+<%@ page import="org.apache.wiki.action.WikiContextFactory" %>
+<%--
+     This file contains the default layout used by all JSPWiki 3 pages.
+     The default layout contains the HTML doctype declaration, header,
+     and page layout. It can be customized in the following ways:
+
+     1) Top-level JSPs can define default components, as defined by
+        Stripes <s:layout-component name="foo"> elements. Named components
+        that can be overridden include:
+
+          headTitle           : The HTML page title, which will be rendered
+                                in the <title> element. Default=wiki: pagename
+          stylesheet          : Link tags to external stylesheets. Default=blank
+          inlinecss           : Inline stylesheets. Default=blank
+          script              : JavaScript <script> elements. Default=blank
+          jslocalizedstrings  : Localized scripts for JavaScript
+                                functions. Default=blank
+          jsfunction          : JavaScript functions. Default=blank
+          headMetaRobots      : Search engine options. Default=noindex,nofollow
+          content             : The page contents. Default=blank
+
+     2) DefaultLayout injects additional JSPs that are meant to be
+        customized. These include:
+
+          localheader.jsp     : A "local header" that can contain company logos
+                                or other markup. Default=blank
+
+--%>
+<s:layout-definition>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>
+    <%--
+
+         Title: by default, use the "view page" title
+    --%>
+    <s:layout-component name="headTitle">
+      <fmt:message key="view.title.view">
+        <fmt:param><wiki:Variable var="ApplicationName" /></fmt:param>
+        <fmt:param><wiki:PageName/></fmt:param>
+      </fmt:message>
+    </s:layout-component>
+    </title>
+    <%--
+
+         CSS stylesheets
+    --%>
+    <link rel="stylesheet" media="screen, projection, print" type="text/css" href="<wiki:Link format='url' templatefile='jspwiki.css' />" />
+    <%-- put this at the top, to avoid double load when not yet cached --%>
+	<%--
+    <link rel="stylesheet" type="text/css" media="print" href="<wiki:Link format='url' templatefile='jspwiki_print.css' />" />
+    <link rel="alternate stylesheet" type="text/css" href="<wiki:Link format='url' templatefile='jspwiki_print.css' />" title="Print friendly" />
+    --%>
+    <link rel="alternate stylesheet" type="text/css" href="<wiki:Link format='url' templatefile='jspwiki.css' />" title="Standard" />
+    <s:layout-component name="stylesheet" />
+    <s:layout-component name="inlinecss" />
+    <%--
+
+         Links to favicon and common pages
+    --%>
+    <link rel="search" href="<wiki:LinkTo format='url' page='FindPage' />" title='Search ${wikiEngine.applicationName}' />
+    <link rel="help" href="<wiki:LinkTo format='url' page='TextFormattingRules' />" title="Help" />
+    <link rel="start" href="<wiki:LinkTo format='url' page='${wikiEngine.frontPage}' />" title="Front page" />
+    <link rel="shortcut icon" type="image/x-icon" href="<wiki:Link format='url' jsp='images/favicon.ico' />" />
+    <%-- ie6 needs next line --%>
+    <link rel="icon" type="image/x-icon" href="<wiki:Link format='url' jsp='favicon.ico' />" />
+    <%--
+
+         Support for the universal edit button
+         (www.universaleditbutton.org)
+    --%>
+    <wiki:CheckRequestContext context='view|info|diff|upload'>
+    <wiki:Permission permission="edit">
+    <wiki:PageType type="page">
+    <link rel="alternate" type="application/x-wiki" href="<wiki:EditLink format='url' />" title="<fmt:message key='actions.edit.title' />" />
+    </wiki:PageType>
+    </wiki:Permission>
+    </wiki:CheckRequestContext>
+    <%--
+
+         JavaScript
+
+    <script src="http://ajax.googleapis.com/ajax/libs/mootools/1.2.3/mootools-yui-compressed.js"></script>
+    --%>
+    <script type="text/javascript" src="<wiki:Link format='url' jsp='scripts/mootools-core.js' />"></script>
+    <script type="text/javascript" src="<wiki:Link format='url' jsp='scripts/mootools-more.js' />"></script>
+
+    <script type="text/javascript" src="<wiki:Link format='url' jsp='scripts/prettify.js' />"></script>
+    <script type="text/javascript" src="<wiki:Link format='url' jsp='scripts/jspwiki-common.js' />"></script>
+    <script type="text/javascript" src="<wiki:Link format='url' jsp='scripts/jspwiki-slimbox.js' />"></script>
+    <script type="text/javascript" src="<wiki:Link format='url' jsp='scripts/jspwiki-commonstyles.js' />"></script>
+    <s:layout-component name="script" />
+    <%--
+
+         JavaScript: localized strings and functions
+    --%>
+    <script type="text/javascript">//<![CDATA[
+    /* Localized javascript strings: LocalizedStrings[] */
+    <%-- DELETEME
+    <s:layout-component name="jslocalizedstrings" />
+    <s:layout-component name="jsfunction" />
+    --%>
+    <wiki:IncludeResources type="jslocalizedstrings"/>
+    <wiki:IncludeResources type="jsfunction"/>
+    //]]></script>
+    <%--
+
+         Skins: extra stylesheets, extra javascript
+
+    --%>
+    <c:if test='${(!empty prefs.Skin) && (prefs.Skin!="PlainVanilla") }'>
+    <link rel="stylesheet" type="text/css" media="screen, projection, print" href="<wiki:Link format='url' templatefile='skins/${prefs.Skin}/skin.css' />" />
+    <script type="text/javascript" src="<wiki:Link format='url' templatefile='skins/${prefs.Skin}/skin.js' />"></script>
+    </c:if>
+    <%--
+
+         Meta tags
+    --%>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <meta name="wikiContext" content='${wikiContext.requestContext}' />
+    <meta name="wikiBaseUrl" content='<wiki:BaseURL/>' />
+    <meta name="wikiPageUrl" content='<wiki:Link format="url"  page="#$%"/>' />
+    <meta name="wikiEditUrl" content='<wiki:EditLink format="url" />' />
+    <meta name="wikiJsonUrl" content='<%=  WikiContextFactory.findContext(pageContext).getURL( WikiContext.NONE, "JSON-RPC" ) %>' /><%--unusual pagename--%>
+    <meta name="wikiPageName" content='<wiki:Variable var="pagename" />' /><%--pagename without blanks--%>
+    <meta name="wikiUserName" content='<wiki:UserName/>' />
+    <meta name="wikiTemplateUrl" content='<wiki:Link format="url" templatefile="" />' />
+    <meta name="wikiApplicationName" content='${wikiEngine.applicationName}' />
+    <%--
+
+         Search engines: by default, page is not indexed or followed
+    --%>
+    <s:layout-component name="headMetaRobots">
+    <meta name="robots" content="noindex,nofollow" />
+    </s:layout-component>
+    <%--
+
+         RSS Feed discovery
+    --%>
+    <wiki:FeedDiscovery/>
+
+    <wiki:Include page="localheader.jsp" />
+
+
+  </head>
+
+  <body class="${wikiContext.requestContext}">
+
+    <div id="wikibody" class="${prefs.Orientation} ${prefs.Fullscreen} ">
+
+      <wiki:Include page="Header.jsp" />
+
+      <div id="content">
+        <div id="page">
+          <wiki:Include page="PageActionsTop.jsp" />
+          <s:layout-component name="content" />
+          <wiki:Include page="PageActionsBottom.jsp" />
+        </div>
+        <wiki:Include page="Favorites.jsp" />
+      </div>
+
+      <wiki:Include page="Footer.jsp" />
+
+    </div>
+  </body>
+
+</html>
+
+</s:layout-definition>