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 [1/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...

Author: brushed
Date: Sun Feb 28 21:12:40 2010
New Revision: 917294

URL: http://svn.apache.org/viewvc?rev=917294&view=rev
Log:
v3.0.0-svn-211 : line up with latest template/stripes changes
* JSPWIKI-429:  Upgrade of Slimbox (viewer for attachments), 
* JSPWIKI-431 Multi-file upload is now supported with Stripes backend. (improved gui)
* JSPWIKI-362 - (still experimental) Added an wysiwyg editor to JSPWiki
* Fixed and simplified the %%category style to work with the stripes
* Upgrade to mootools-core to 1.2.4, mootools-more to 1.2.4.4
* Updated %%prettify style, adding line numbers to code-blocks.


Added:
    incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-slimbox.js
    incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/
    incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.Extras.js   (with props)
    incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.Group.js   (with props)
    incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.UI.ButtonOverlay.js   (with props)
    incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.UI.MenuList.js   (with props)
    incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditable.js   (with props)
    incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/MooEditableSilkTheme.css   (with props)
    incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/images/
    incubator/jspwiki/trunk/src/WebContent/scripts/wysiwyg/images/mooeditable-toolbarbuttons-silk.png   (with props)
    incubator/jspwiki/trunk/src/WebContent/templates/default/AttachmentTab.jsp
    incubator/jspwiki/trunk/src/WebContent/templates/default/DefaultLayout.jsp
    incubator/jspwiki/trunk/src/WebContent/templates/default/editors/wysiwyg.jsp
    incubator/jspwiki/trunk/src/WebContent/templates/default/images/delete.png   (with props)
Modified:
    incubator/jspwiki/trunk/ChangeLog
    incubator/jspwiki/trunk/etc/ini/jspwiki_module.xml
    incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/templates/default.properties
    incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-common.js
    incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-commonstyles.js
    incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-edit.js
    incubator/jspwiki/trunk/src/WebContent/scripts/mootools-core.js
    incubator/jspwiki/trunk/src/WebContent/scripts/mootools-more.js
    incubator/jspwiki/trunk/src/WebContent/templates/default/PageInfo.jsp
    incubator/jspwiki/trunk/src/WebContent/templates/default/jspwiki.css
    incubator/jspwiki/trunk/src/WebContent/templates/default/layout/DefaultLayout.jsp
    incubator/jspwiki/trunk/src/WebContent/templates/default/tabs/AttachmentsTab.jsp
    incubator/jspwiki/trunk/src/java/org/apache/wiki/Release.java
    incubator/jspwiki/trunk/src/java/org/apache/wiki/action/ViewActionBean.java

Modified: incubator/jspwiki/trunk/ChangeLog
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/ChangeLog?rev=917294&r1=917293&r2=917294&view=diff
==============================================================================
--- incubator/jspwiki/trunk/ChangeLog (original)
+++ incubator/jspwiki/trunk/ChangeLog Sun Feb 28 21:12:40 2010
@@ -1,3 +1,33 @@
+2010-02-28 Dirk Frederickx <br...@apache.org>
+
+        * v3.0.0-svn-211 : line up with latest template/stripes changes
+
+        * JSPWIKI-429:  Upgrade of Slimbox (viewer for attachments), 
+          and adjust it to v3.X of JSPWiki. Various media formats are now
+          supported so you can view youtube or facebook videos, other
+          wikipages, or even external web-pages.
+          Still working on a use as inline media viewer. (iso popup viewer)
+
+        * JSPWIKI-431 Multi-file upload is now supported with Stripes backend.
+          (improved gui)
+          You can add or remove files (upto 5) before you press the upload 
+          button. All attachments are uploaded with the same change note.
+
+        * JSPWIKI-362 - (still experimental) Added an wysiwyg editor to JSPWiki,
+          based on mooeditable. (MIT license) 
+          This is a light-weight editor, based on mootools. 
+          Future extensions will improve integration with JSPWiki
+          e.g. adding ajax based suggestion-menu, attachment previews, etc...
+          Interaction with Stripes back-end needs more work. 
+
+        * Fixed and simplified the %%category style to work with the stripes
+          ViewActionBean.ajaxCategories(). This replaces the AJAXCategories.jsp.
+
+        * Upgrade to mootools-core to 1.2.4, mootools-more to 1.2.4.4
+
+        * Updated %%prettify style, adding line numbers to code-blocks.
+
+
 2010-01-06 Harry Metske <me...@apache.org>
 
         * 3.0.0-svn-210

Modified: incubator/jspwiki/trunk/etc/ini/jspwiki_module.xml
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/etc/ini/jspwiki_module.xml?rev=917294&r1=917293&r2=917294&view=diff
==============================================================================
--- incubator/jspwiki/trunk/etc/ini/jspwiki_module.xml (original)
+++ incubator/jspwiki/trunk/etc/ini/jspwiki_module.xml Sun Feb 28 21:12:40 2010
@@ -46,6 +46,14 @@
       <adminBean>org.apache.wiki.ui.admin.beans.WikiWizardAdminBean</adminBean>
    </editor>
 -->
+   <editor name="Wysiwyg">
+      <author>Dirk Frederickx</author>
+      <script>scripts/wysiwyg/MooEditable.js</script>
+      <stylesheet>scripts/wysiwyg/MooEditable.css</stylesheet>
+      <path>editors/wysiwyg.jsp</path>
+      <minVersion>3.0</minVersion>
+   </editor>
+
    <plugin class="org.apache.wiki.plugin.IfPlugin">
       <author>Janne Jalkanen</author>
       <minVersion>2.4</minVersion>

Modified: incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/templates/default.properties
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/templates/default.properties?rev=917294&r1=917293&r2=917294&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/templates/default.properties (original)
+++ incubator/jspwiki/trunk/src/WebContent/WEB-INF/classes/templates/default.properties Sun Feb 28 21:12:40 2010
@@ -463,7 +463,7 @@
 javascript.filter.all=( All )
 javascript.group.validName=Please provide a valid name for the new Group
 javascript.category.title=Click to show category [{0}] ...
-javascript.slimbox.info=Image {0} of {1}
+javascript.slimbox.info=({0} of {1})
 javascript.slimbox.error=Error! There was a problem with your request. Please try again
 javascript.slimbox.directLink=Direct Link to the target
 javascript.slimbox.remoteRequest=Remote Request {0} of {1}
@@ -493,7 +493,7 @@
 ldap.bindUser.description=If your LDAP server requires a user to be bound for LDAP searches (Active Directory does, for example), enter the login name of an authenticated user here. You may need to specify the User Base for this to work.
 #Formerly named ldap.bindDNpassword.description.
 ldap.bindPassword.description=Enter the binding user's password here. It will be stored in the Keychain.
-install.userdatabase.description=Select where user profiles will be stored. The default is in an XML file in WEB-INF. However you can also use LDAP as a read-only user database. Click the "Configure LDAP..." button if you use LDAP. 
+install.userdatabase.description=Select where user profiles will be stored. The default is in an XML file in WEB-INF. However you can also use LDAP as a read-only user database. Click the "Configure LDAP..." button if you use LDAP.
 #Copied from src/WebContent/WEB-INF/classes/CoreResources.properties.. Formerly named install.jsp.intro.p1.
 install.intro.p1=Welcome!  This little JSP page is here to help you do the first difficult stage of JSPWiki installation.  If you're seeing this page, you have already installed JSPWiki correctly inside your container.
 #Copied from src/WebContent/WEB-INF/classes/CoreResources.properties.. Formerly named install.jsp.intro.p2.
@@ -524,7 +524,7 @@
 newgroup.heading.create=Create new group
 #Formerly named newgroup.members.description.
 members.description=The membership for this group. Enter each user&#8217;s name or wiki name, separated by carriage returns.
-install.approver.saveWikiPage.description=Name of the role or wiki group that can approve page changes. If users can change pages themselves without approval, leave this blank. Specify the role "SU" if you want the superuser to approve all page changes. 
+install.approver.saveWikiPage.description=Name of the role or wiki group that can approve page changes. If users can change pages themselves without approval, leave this blank. Specify the role "SU" if you want the superuser to approve all page changes.
 install.approver.createUserProfile.description=Name of the role or wiki group that can approve new wiki profiles. If users can self-enroll without approval, leave this blank. Specify the role "SU" if you want the superuser to approve all new accounts.
 attachments.zero=Attachments
 attachments.one=Attachments ({0})

Modified: incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-common.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-common.js?rev=917294&r1=917293&r2=917294&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-common.js (original)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-common.js Sun Feb 28 21:12:40 2010
@@ -30,10 +30,17 @@
 	v.2.6.0
 
 Dependencies:
-	Based on http://mootools.net/ v1.11
+	Based on http://mootools.net/ v1.2.4
+	* mootools-core.js : v1.2.4
+	* mootools-more.js : v1.2.4.4
+		including:
+		Fx.Accordion, Drag, Drag.Move, Color, Hash.Cookie, Tips
+
+
 	*	Core, Class,  Native, Element(ex. Dimensions), Window,
 	*	Effects(ex. Scroll), Drag(Base), Remote, Plugins(Hash.Cookie, Tips, Accordion)
 
+
 Core JS Routines:
 	*	[Wiki] object (page parms, UserPrefs and setting focus)
 	*	[WikiSlimbox]
@@ -48,7 +55,7 @@
 	*	[TabbedSection] including all accordion variants
 	*	[Colors], [GraphBar]
 	*	[Collapsible] list and box
-	*	[TableAdds] with %%sortable, %%tablefilter (excel like column filters) and %%zebra-table
+	*	[TablePlugin] with %%sortable, %%tablefilter (excel like column filters) and %%zebra-table
 	* 	[Popup] DOM based popup, replacing alert(), prompt(), confirm()
 
 */
@@ -82,8 +89,8 @@
 		Truncate a string to a maximum length
 
 	Arguments:
-		size - maximum length of the string
-		elips - (optional) the elips indicates when the string was truncacted (defaults to '...')
+		size - maximum length of the string, excluding the length of the elips
+		elips - (optional) the elips replaces the truncated part (defaults to '...')
 
 	Example:
 	> "this is a long string".trunc(7); //returns "this is..."
@@ -210,7 +217,7 @@
 	/*
 	Function: addHover
 		Shortcut function to add 'hover' css class to an element.
-		This allow to support hover effects on all elements, also in IE.
+		This allows to support hover effects on all elements, also in IE.
 
 	Arguments
 		clazz - (optional) hover class-name, default is {{hover}}
@@ -350,17 +357,19 @@
 			el = self.element,
 			value = el.value;
 
-		if( self.value == value ) return;
-		self.clear();
-		self.value = value;
-		self.timeout = self.callback.delay(self.options.delay, null, [el]);
+		if( self.value != value ){
+			self.clear();
+			self.value = value;
+			self.timeout = self.callback.delay(self.options.delay, null, [el]);
+		}
 	},
 	clear: function(){
 		this.timeout = $clear(this.timeout);
 	},
 	stop: function(){
-		this.element.removeEvent(this.options.event, this.listener);
-		this.clear();
+		var self = this;
+		self.element.removeEvent(self.options.event, self.listener);
+		self.clear();
 	}
 });
 
@@ -386,20 +395,20 @@
 		"javascript.imageInfo":"Image {0} of {1}"
 	}
 	(end)
-
 */
 var LocalizedStrings = LocalizedStrings || [];
 
 /*
 Function: localize
 	Localize a string; with or without parameters.
-	Uses the [LocalideString] global hash.
+	Uses the [LocalizedString] global hash.
 
 Examples:
 >	"moreInfo".localize() =="More";
 >	"imageInfo".localize(2,4); => "Image {0} of {1}" becomes "Image 2 of 4"
 
 */
+
 String.implement({
 	localize: function(){
 		var s = LocalizedStrings["javascript."+this] || "???"+this+"???",
@@ -483,6 +492,7 @@
 
 		//FIXME
 		self.makeMenuFx('morebutton', 'morepopup');
+
 		self.addEditLinks();
 
 		self.renderPage( $('page'), self.PageName);
@@ -504,17 +514,25 @@
 		SearchBox.initialize();
 
 		//fixme;
-		// HighlightWord( $('pagecontent'), self.prefs.get('PrevQuery') )
-		// self.prefs.set('PrevQuery','');
-		HighlightWord.initialize();
+		HighlightWord( $('pagecontent'), self.prefs.get('PrevQuery') )
+		self.prefs.set('PrevQuery','');
+		//HighlightWord.initialize();
+
 		self.setFocus();
 
+		//support multiple file uploads
+		new FileUpload( $('attachfile0'), {
+			max:5,
+			pattern:"0",
+			delBtn:new Element('a',{ 'class':'delete tool' })
+		});
 	},
 
 	/*
 	Function: getSections
 		Returns the list of all section headers, excluding the header of the
 		Table Of Contents.
+
 	*/
 	getSections: function(){
 		return $$('#pagecontent *[id^=section]').filter(
@@ -536,7 +554,7 @@
 		//return alert(msg); //standard js
 
 		this.dialog
-			.setBody( new Element('p').set('html',msg) )
+			.setBody( new Element('p',{ html:msg }) )
 			.setButtons({ Ok:Class.empty })
 			.show();
 	},
@@ -555,7 +573,7 @@
 		//return callback( confirm(msg) ); //standard js
 
 		this.dialog
-			.setBody( new Element('p').set('html',msg) )
+			.setBody( new Element('p', { html:msg}) )
 			.setButtons({
 				Ok:function(){ callback(true); },
 				Cancel:function(){ callback(false); }
@@ -583,7 +601,7 @@
 		var input;
 
 		this.dialog.setBody([
-				new Element('p').set('html',msg),
+				new Element('p',{ html:msg }),
 				new Element('form').adopt(
 					input = new Element('input',{
 						name:'prompt',
@@ -667,12 +685,11 @@
 		Remove all not-allowed chars from a *candidate* pagename.
 		Trim repeated whitespace, allow letters, digits and
 		following punctuation chars: ()&+,-=._$
-		Ref com.ecyrd.jspwiki.parser.MarkupParser.cleanPageName()
+		Ref. org.apache.wiki.parser.MarkupParser.cleanPageName()
 	*/
 	cleanPageName: function(p){
 
-		return p.trim().replace(/\s+/g,' ')
-				.replace(/[^A-Za-z0-9()&+,-=._$ ]/g, '');
+		return p.clean().replace(/[^A-Za-z0-9()&+,-=._$ ]/g, '');
 
 	},
 
@@ -789,26 +806,25 @@
 
 	/*
 	Property: splitbar
-		Add a toggle bar next to the main page content block, to
-		show/hide the favorites block with a click of the mouse.
-		The open/close status is saved in the Wiki.prefs cookie.
-		When the user hovers the mouse over the toggle bar, an arrow
-		image is shown to indicate the collapsable effect.
-
-	Note:
-		The toggle bar has css-id 'collapseFavs'.
-		The toggle bar gets a .hover class when the mouse hovers over it.
-		The mouse-pointer image has css-id 'collapseFavsPointer'.
-		The DOM body gets a .fav-slide class when the favorites are collapsed (hidden)
+		Add a splitbar next to the main page, to allow to toggle the
+		main page to fullscreen mode.
+		The status of the fullscreen mode is saved in the UserPreferences cookie.
+		When the user hovers the mouse over the splitbar, the cursor changes
+		to an arrow to indicate the collapsable effect.
+
+		* the splitbar has css class {{.splitbar}}.
+		* the splitbar toggles the  {{.hover}} css class (ie compat)
+		* the DOM body toggles the {{.fullscreen}} css class
 
 
 	DOM structure:
+		Based on css-only approach
 	(start code)
 	<div id='content'>
 		<div id='page'>
+			<a class='splitbar'></a> <== injected splitbar
 			<div class='tabmenu'> ... </div>
 			<div class='tabs'>
-				<div class='splitbar'></div> <== injected splitbar
 				<div id='pagecontent'> ... </div>
 				<div id='attach'> ... </div>
 				<div id='info'> ... </div>
@@ -817,110 +833,85 @@
 		<div id='favorites'> ... </div>
 	</div>
 	(end)
+
+	Alternative:
+		Alternative solution aims at showing the favorites menu on mouse-hover;
+		overlayed of the #page block.
+
+		Make favorites: position:absolute; hidden behind #page  (check width)
+		...
 	*/
 	splitbar: function(){
 
-		//inject toggle block :: can be done at server already
-		var splitbar = 'splitbar',
-			pref = 'hidefav',
-			body = $E('body'),
-			pointer = new Element('div', {'id':'splitPointer'}).inject(body),
-			pointerFn = function(e){
-				this.addClass('hover');
-				pointer.setStyles({ left:$('page').getPosition().x, top: (e.pageY || e.clientY) }).show();
-			};
+		var self = this,
+			splitbar = 'splitbar',
+			pref = 'fullscreen',
+			body = $(document.body),
+			page = $('page');
 
 		// The cookie is not saved to the server's Preferences automatically, (HTTP GET)
 		// so the body class will not be set yet.
 		// Should better move server side, for faster rendering. wf-stripes
-		body.addClass( Wiki.prefs.get( pref )||'' );
+		body.addClass( self.prefs.get( pref )||'' );
 
-		new Element('div',{
+		var split = new Element('a',{
 			'class':splitbar,
-			'events':{
-				'click': function(){
+			title:'Click to toggle the sidebar'.localize(),
+			events:{
+				click: function(){
 					body.toggleClass( pref );
-					Wiki.prefs.set( pref, body.hasClass(pref) ? pref:'' );
-				},
-				'mouseenter': pointerFn,
-				'mousemove': pointerFn,
-				'mouseleave': function(){ this.removeClass('hover'); pointer.hide() }
-			}
-		}).injectTop( $('page') );
+					self.prefs.set( pref, body.hasClass(pref) ? pref:'' );
 
-	},
-
-	addCollapsableFavs: function(){
-
-		var body = $('wikibody'),
-			$pref = 'fav-slide',
-			pref = Wiki.prefs.get('ToggleFav'),
-			tabs = $E('#page .tabs');
-
-		if( !tabs ) return;
+					if( body.hasClass(pref) ){
+						new Element('div',{'id':'dummyPage'}).injectAfter(page);
+					} else {
+						if($('dummyPage')) $('dummyPage').destroy();
+					}
 
-		// The cookie is not saved to the server's Preferences automatically,
-		// so the body class will not be set yet.
-		// Should better move server side, for faster rendering. wf-stripes
-		(pref==$pref) ? body.addClass($pref) : body.removeClass($pref);
+				},
+				mouseenter: function(e){
+					var f = $('favorites');
+					//f.injectAfter(split);
+					//f.setStyle('top', f.getParent().getCoordinates().top);
 
-		/* Be careful.
-		   The .tabs block can not be made relative, cause then the .tabmenu doesn't
-		   stack properly with the .tabs block.
-		   Therefore, insert a relative DIV container to contain the clickable vertical bar.
-		   TODO: needs probably another IE hack ;-)
-		 */
-
-		tabs = new Element('div', {
-			'styles': {
-				'position':'relative',
-				'padding': tabs.getStyle('padding') // take over padding from original .tabs
-			}
-		}).wrapContent(tabs.setStyle('padding','0'));
-
-		var pointer = new Element('div', {'id':'collapseFavsPointer'}).hide().inject(body),
-			movePointer = function(e){
-				this.addClass('hover');
-				pointer.setStyles({ left:this.getPosition().x, top: (e.pageY || e.clientY) }).show();
-			};
-
-		new Element('div', {
-			'id':'collapseFavs',
-			'events': {
-				'click': function(){
-					body.toggleClass($pref);
-					Wiki.prefs.set('ToggleFav', body.hasClass($pref) ? $pref:'' );
 				},
-				'mouseenter': movePointer,
-				'mousemove': movePointer,
-				'mouseleave': function(){
-					this.removeClass('hover');
-					pointer.hide();
+				mouseleave: function(e){
+					var f = $('favorites');
+					//f.setStyle('top','auto');
+					//page.getParent().adopt(f);
 				}
 			}
-		}).injectTop(tabs);
+		}).addHover().injectTop( page );
 
 	},
 
-	// fixme: should move server side
-	addEditLinks: function(){
-
+	/*
+	Function: addEditLinks
+		Add [[Edit] links to the section headers.
 
-//fixme: SectionEditing is not properly saved when updating userprefs !
-//alert(this.prefs.keys()+"\n"+this.prefs.values());
+	Fixme:
+		Should move server side
+	*/
+	addEditLinks: function(){
 
-		if( $("previewcontent") || !this.allowEdit || this.prefs.get('SectionEditing') != 'on') return;
+		var self = this,
+			url = self.EditUrl,
+			link = new Element('a',{
+				'class':'editsection',
+				html:'quick.edit'.localize()
+			}),
+			i = 0;
 
-		var url = this.EditUrl;
-		url = url + (url.contains('?') ? '&' : '?') + 'section=';
+		if( self.Context!="preview" && self.allowEdit && self.prefs.get('SectionEditing') ){
 
-		var aa = new Element('a',{'class':'editsection'}).set('html','quick.edit'.localize()),
-			i = 0;
+			url = url + (url.contains('?') ? '&' : '?') + 'section=';
 
-		this.getSections().each( function(el){
-			el.adopt(aa.set({'href':url + i++ }).clone());
-		});
+			self.getSections().each( function(el){
+				el.adopt(link.set({'href':url + i++ }).clone());
+			});
+		}
 
+		link.dispose();
 	},
 
 	/*
@@ -1024,65 +1015,102 @@
 
 /*
 Plugin: WikiSlimbox
+	Injects clickable slimbox links, after each target links inside a
+	%%slimbox container. The clickable slimbox link will popup
+	a modal window with a rich media viewer.
+	When multiple links are found, 'next' and 'previous' links
+	will allow to scroll through all links.
 
 Credits:
-	Inspired by Slimbox by Christophe Bleys. (see http://www.digitalia.be/software/slimbox)
+	Uses
 
 Example:
-	> %%slimbox [...] %%
-	> %%slimbox-img  [some-image.jpg] %%
-	> %%slimbox-ajax [some-page links] %%
+	> %%slimbox [any supported link] %%
+	> %%slimbox-images [links to .jpg .gif .jpeg .bmp .xxx] %%
+	> %%slimbox-media [links to  youtube, google-video, ...] %%
+	> %%slimbox-wikipage [wiki page links] %%
+	> %%slimbox-external [external links] %%
+	> %%slimbox-wikipage-external [external links] %%
+	> %%slimbox-480-300  [viewer size at 480px width x 300px height] %%
+
+DOM structure:
+	(start code)
+	<div class='"slimbox">
+		<a href="url1" class="attachment" >Image link</a>
+		<img src="url2 class="inline" />
+	</div>
+	(end)
+
+	becomes
+
+	(start code)
+	<div class='slimbox'>
+		<a href="url1" class="attachment">Image link</a>
+		<a href="url1" rel="slimbox" class="slimbox">&raquo;</a>
+
+		<a href="url2" class="attachment"/>url2</a>
+		<a href="url2" rel="slimbox" class="slimbox">&raquo;</a>
+	</div>
+	(end)
+
+	Notice how embedded images are converted back to html links. (<a>)
 */
 Wiki.registerPlugin( function(page,name){
-	var i = 0,
-		lnk = new Element('a',{'class':'slimbox'}).set('html','&raquo;');
 
+	var slimboxbtn = new Element('a',{'class':'slimboxbtn', html:'&raquo;'}),
+		options = {
+			//closeText: "<span class='accesskey'>C</span>lose".localize(),
+			//nextText: "<span class='accesskey'>P</span>revious".localize(),
+			//prevText: "<span class='accesskey'>P</span>revious".localize(),
+			//counterText: "({x} of {y})".localize()
+			closeText: "slimbox.close".localize(),
+			nextText: "slimbox.next".localize(),
+			prevText: "slimbox.previous".localize(),
+			counterText: "slimbox.info".localize("{x}","{y}"),
+			errorText: "slimbox.error".localize()
+		};
 
-	page.getElements('*[class^=slimbox]').each(function(slim){
-		var group = 'lightbox'+ i++,
-			parm = slim.className.split('-')[1] || 'img ajax',
-			filter = [];
-		if(parm.test('img')) filter.extend(['img.inline', 'a.attachment']);
-		if(parm.test('ajax')) filter.extend(['a.wikipage', 'a.external']);
-
-		slim.getElements(filter.join(',')).each(function(el){
-			var href = el.src||el.href,
-				rel = (el.className.test('inline|attachment')) ? 'img' : 'ajax';
-
-			if((rel=='img') && !href.test('(.bmp|.gif|.png|.jpg|.jpeg)(\\?.*)?$','i')) return;
-
-			lnk.clone().setProperties({
-				'href':href,
-				'rel':group+' '+rel,
-				'title':el.alt||el.get('text')
-			}).injectAfter(el);//.injectBefore(el);
-
-			//if(el.src) el.replaces(new Element('a',{
-			//	'class':'attachment',
-			//	'href':el.src
-			//}).set('html',el.alt||el.get('text')));
-
-			if(el.src) new Element('a',{
-				'class':'attachment',
-				href:el.src,
-				html:el.alt||el.get('text')
-			}).replaces(el);
+	page.getElements('div[class^=slimbox],span[class^=slimbox],a[class^=slimbox]').each(function( slim ){
 
-		});
-	});
+		var parm = slim.className.split(' ')[0],
+			filter = 'img, a',
+			elements = [], urls = [], url, href;
 
-return; //FIXME
-	//if(i) Lightbox.init();
-	/* TODO: new slimbox invocation code, based on Slimbox v1.7 */
-	$$(document.links).filter(function(el) {
-		return el.rel && el.rel.test(/^lightbox/i);
-	}).slimbox({/* Put custom options here */}, null, function(el) {
-		return (this == el) || ((this.rel.length > 8) && (this.rel == el.rel));
-	});
+		if(parm.contains('-image')) filter="img, a.attachment";
+		if(parm.contains('-external')) filter="a.external";
+		if(parm.contains('-wikipage')) filter="a.wikipage";
 
+		slim.getElements( filter ).each( function(el){
 
-})
+			url = el.src || el.href;
+			title = el.alt || el.get('text');
+
+			//skip jspwiki inserted icon for external links
+			if( el.src && el.src.test(/images\/out.png$/) ) return;
+			if( el.src && el.src.test(/images\/attachment_small.png$/) ) return;
+
+			if( el.hasClass('createpage')) return; //skip links to create new pages
+			if( el.hasClass('infolink')) return; //skip links to create new pages
+			if( (parm.contains('-image')) && !Slimbox.isImage(url) ) return;
+			if( (parm.contains('-media')) && !Slimbox.isMedia(url) ) return;
+
+			urls.push([ url, title ]);
+
+			//insert slimbox clickable button
+			elements.push( slimboxbtn.clone()
+				.set({ href:url, title:title }).injectAfter(el)
+			);
 
+			//replace embedded jspwiki images by links.
+			if(el.src)
+				new Element('a',{ 'class':'attachment', href:url, html:title }).replaces(el);
+		});
+
+		$$(elements).addEvent('click', function(){ return Slimbox.open(urls, elements.indexOf(this), options) });
+
+	});
+	slimboxbtn.destroy(); //clean-up
+})
 
 
 /*
@@ -1324,9 +1352,10 @@
 				// xss vulnerability JSPWIKI-384
 				el = el.stripScripts();
 				new Element('a',{
-					'href':'#',
+					href:'#',
+					html:el,
 					'events': {'click':function(){ q.value = el; q.form.submit(); }}
-					}).set('html',el).inject( new Element('li').inject(ul) );
+					}).inject( new Element('li').inject(ul) );
 			});
 		//}
 	},
@@ -1389,11 +1418,10 @@
 				//FIXME: stripes generates a whole web-page iso of page fragment with searchresults.
 				//var x = $E('#searchResult2',$('searchResult2'));
 				var res = $('searchResult2');
-			console.log(res.innerHTML);
 				res.replaces( res );
 				//res.replaces( res.getElement('#searchResult2') );
-				GraphBar.render($('searchResult2'));
 				Wiki.prefs.set('PrevQuery', q2);
+				new GraphBar($('searchResult2'));
 			}
 		});
 		location.hash = '#'+q2+":"+$('start').value;  /* push the query into the url history */
@@ -1426,9 +1454,25 @@
 		$('searchTarget').set('html','('+qv+') :');
 		$('searchSpin').show();
 
-    Stripes.submitFormEvent('searchForm', 'quickSearch', 'searchOutput', null);
-		$('searchSpin').hide();
-		Wiki.locatemenu( $('query'), $('searchboxMenu') );
+		Wiki.jsonrpc('search.findPages', [qv,20], function(result){
+				$('searchSpin').hide();
+				if(!result.list) return;
+				var frag = new Element('ul');
+
+				result.list.each(function(el){
+					new Element('li').adopt(
+						new Element('a',{'href':Wiki.toUrl(el.map.page), html:el.map.page }),
+						new Element('span',{'class':'small', html:" ("+el.map.score+")" })
+					).inject(frag);
+				});
+				$('searchOutput').empty().adopt(frag);
+				Wiki.locatemenu( $('query'), $('searchboxMenu') );
+		});
+
+	//fixme ??
+    //Stripes.submitFormEvent('searchForm', 'quickSearch', 'searchOutput', null);
+	//	$('searchSpin').hide();
+	//	Wiki.locatemenu( $('query'), $('searchboxMenu') );
 	} ,
 
 	/* navigate to url, after smart pagename handling */
@@ -1919,7 +1963,7 @@
 		var cookie = "";  //activate this line if you want to deactivatie cookie handling
 
 		if(!this.bullet) {
-			this.bullet = new Element('div',{'class':'collapseBullet'}).set('html','&bull;');
+			this.bullet = new Element('div',{'class':'collapseBullet', html:'&bull;' });
 		}
 
 		this.pims.push({
@@ -2084,18 +2128,17 @@
 		var legend = el.className.split('-')[1];
   		if( legend ){
   			new Element('fieldset',{'class':'commentbox'}).adopt(
-  				new Element('legend').set('html', legend.deCamelize() )
+  				new Element('legend',{ html: legend.deCamelize() })
   			).wrapContent(el).injectBefore(el);
   			el.dispose();
   		}
 	});
 
-
 });
 
 
 /*
-Class: TableAdds
+Class: TablePlugin
 	Add support for sorting, filtering and zebra-striping of tables.
 	TODO: add support for row-grouping
 	TODO: add support for check-box filtering (ref excel like)
@@ -2103,7 +2146,7 @@
 Credit:
 	Filters inspired by http://www.codeproject.com/jscript/filter.asp
 */
-var TableAdds = new Class({
+var TablePlugin = new Class({
 
 	Implements: Options,
 
@@ -2119,19 +2162,18 @@
 		}
 	},
 
-
 	initialize: function( table, options ){
 
 		if( table.rows.length < 3 ) return null;
 		table = $(table);
 
-		var self = table.TableAdds;  //max one TableAdds instance per table
+		var self = table.TablePlugin;  //max one TablePlugin instance per table
 		if( !self) {
 			this.table = table;
 			this.head = $A(table.rows[0].cells).filter(function(el){
 							return el.get('tag')=='th';
 						});
-			table.TableAdds = self = this;
+			table.TablePlugin = self = this;
 		}
 		self.setOptions(options);
 		options = self.options;
@@ -2143,7 +2185,7 @@
 					'class': 'sort',
 					title: options.title.sort,
 					events: {
-						'click': self.sort.bind(self,[i])
+						click: self.sort.bind(self,[i])
 					}
 				});
 			});
@@ -2155,8 +2197,8 @@
 				th.adopt( new Element('select',{
 					'class':'filter',
 					events: {
-						'click': function(e){ new Event(e).stopPropagation() },
-						'change': self.filter.bind(self,[i])
+						click: function(e){ new Event(e).stopPropagation() },
+						change: self.filter.bind(self,[i])
 					}
 				}) );
 			});
@@ -2226,8 +2268,6 @@
 		rows.each( function(r){ frag.appendChild(r); });
 		this.table.appendChild(frag);
 
-		//var zebra = this.zebra;
-		//if( zebra ) zebra();
 		$try( this.zebra );
 	},
 
@@ -2335,8 +2375,7 @@
 
 		},this);
 
-		var zebra = this.zebra;
-		if( zebra ) zebra();
+		$try( this.zebra );
 	},
 
 	/*
@@ -2494,14 +2533,14 @@
 });
 
 /*
-Script: TableAdds
-	Register a wiki page renderer, invoking the TableAdds class
+Script: TablePlugin
+	Register a wiki page renderer, invoking the TablePlugin class
 	where needed.
 
 Table sorting:
 	All tables inside a JSPWiki {{%%sortable}} style are retrieved and processed.
 	An onclick() handler is added to each column header which points to the
-	heart of the javascript: the [TableAdds.sort] function.
+	heart of the javascript: the [TablePlugin.sort] function.
 
 Table filtering:
 	Add excel like filter dropdowns to all tables inside a JSPWiki {{%%filtertable}} style.
@@ -2518,7 +2557,6 @@
 */
 Wiki.registerPlugin( function(page, name){
 
-
 	var title = {
 		all: "filter.all".localize(),
 		sort: "sort.click".localize(),
@@ -2526,18 +2564,18 @@
 		descending: "sort.descending".localize()
 	};
 
-	page.getElements('.sortable table').each( function(table){
-		new TableAdds(table, {sort:true, title: title});
+	page.getElements('div.sortable table').each( function(table){
+		new TablePlugin(table, {sort:true, title: title});
 	});
 
-	page.getElements('.table-filter table').each( function(table){
-		new TableAdds(table, {filter:true, title: title});
+	page.getElements('div.table-filter table').each( function(table){
+		new TablePlugin(table, {filter:true, title: title});
 	});
 
-	page.getElements('*[class^=zebra]').each( function(el){
+	page.getElements('div[class^=zebra]').each( function(el){
 		var parms = el.className.split('-').splice(1);
 		el.getElements('table').each(function(table){
-			new TableAdds(table, {zebra: parms});
+			new TablePlugin(table, {zebra: parms});
 		});
 	});
 
@@ -2547,51 +2585,66 @@
 /*
 Class: Categories
 	Turn wikipage links into AJAXed popups.
+
+DOM structure:
+(start code)
+	<span>
+		<a href=".." class="wikipage categoryLink">category-page></a>
+		<div class="categoryPopup">
+			<p><a href=".." class="wikipage categoryLink">category-page</a></p>
+			<ul>
+			  <li><a>..</a></li>
+			</ul>
+		</div>
+	</span>
+(end)
 */
-var Categories =
-{
-	render: function(page, name){
+Wiki.registerPlugin( function(page, name){
 
-		page.getElements('.category a.wikipage').each(function(link){
+	page.getElements('.category a.wikipage').each(function(link){
 
-			var page = Wiki.toPageName(link.href);
-			if(!page) return;
+		var pagename = Wiki.toPageName(link.href);
+		if(!pagename) return;
 
-			var wrap = new Element('span').injectBefore(link).adopt(link),
-				popup = new Element('div',{'class':'categoryPopup'}).inject(wrap).fade('hide');
+		var wrap = new Element('span').wraps(link),
+			popup = new Element('div',{'class':'categoryPopup'}).inject(wrap).fade('hide');
 
-			link.addClass('categoryLink')
-				.set({ href:'#', title: "category.title".localize(page) })
-				.addEvent('click', function(event){
-
-				event.stop();  //dont jump to top of page ;-)
-				new Request.HTML({
-					url:Wiki.BaseUrl + '/View.action?ajaxCategories=',
-					data: '&page=' + page,
-					update: popup,
-					onComplete: function(){
-						link.set('title', '')
-							.removeEvent('click');
-						wrap.addEvents({
-							'mouseover': function(){ popup.fade(0.9) },
-							'mouseout': function(){ popup.fade(0) }
-						});
-						popup.setStyles({
-							left: link.getPosition().x,
-							top: link.getPosition().y+16
-						}).fade(0.9).getElements('li,div.categoryTitle').addHover();
-					}
-				}).send();
+		link.set({ 'class':'categoryLink', title:'category.title'.localize(pagename) })
+			.addEvent('click', function(event){
+
+			event.stop();  //dont jump to top of page
+
+			new Request.HTML({
+				url: Wiki.BaseUrl + 'Wiki.jsp?ajaxCategories&page='+pagename,
+				update: popup,
+				onComplete: function(){
+
+					link.set('title', '').removeEvent('click');
+
+					wrap.addEvents({
+						mouseover: function(){ popup.fade(0.9) },
+						mouseout: function(){ popup.fade(0) }
+					});
+
+					var pp = link.getPosition();
+					popup.grab( new Element('p').adopt(link.clone()),'top' )
+						 .setStyles({ left: pp.x, top: pp.y+16 })
+						 .fade(0.9)
+						 .getElements('li,p').addHover();
+				}
+			}).send();
 
-			});
 		});
-	}
-}
-Wiki.registerPlugin( Categories );
+	});
+
+});
 
 
 /*
 Class: HighlightWord
+	Highlight any word or phrase of a previously search query.
+	The query can be passed in as a parameter or will be read
+	from the documents referrer url.
 
 Credit:
 	Inspired by http://www.kryogenix.org/code/browser/searchhi/
@@ -2601,61 +2654,213 @@
 	- Modified 20030227 by sgala@hisitech.com to skip words
 	  with "-" and cut %2B (+) preceding pages
 	- Refactored for JSPWiki -- now based on regexp
+
 */
-var HighlightWord =
-{
-	initialize: function (){
-		var q = Wiki.prefs.get('PrevQuery');
-		Wiki.prefs.set('PrevQuery', '');
+function HighlightWord( node, query ){
+
+	var words, reMatch,
+
+		//recursive node processing function
+		walkDomTree = function(node){
 
-		if( !q && document.referrer.test("(?:\\?|&)(?:q|query)=([^&]*)","g") ) q = RegExp.$1;
+		if( node ){
 
-		if( q ){
-			var words = decodeURIComponent(q).stripScripts(); //xss vulnerability
-			words = words.replace( /\+/g, " " );
-			words = words.replace( /\s+-\S+/g, "" );
-			words = words.replace( /([\(\[\{\\\^\$\|\)\?\*\.\+])/g, "\\$1" ); //escape metachars
-			words = words.trim().split(/\s+/).join("|");
+			//process all children
+			for( var nn=null, n = node.firstChild; n ; n = nn ){
+				// prefetch nextSibling cause the tree will be modified
+				nn = n. nextSibling;
+				walkDomTree(n);
+			}
+
+			// continue on text-nodes, not yet highlighted
+			if( node.nodeType == 3 ){
+
+				var s = $getText( node );
+
+				s = s.replace(/</g,'&lt;'); // pre text elements may contain <xml> element
+
+				if( reMatch.test( s ) ){
+
+					var tmp = new Element('span',{
+							html: s.replace( reMatch, "<span class='searchword'>$1</span>")
+						}),
+						f = document.createDocumentFragment();
 
-			this.reMatch = new RegExp( "(" + words + ")" , "gi");
+					while( tmp.firstChild ) f.appendChild( tmp.firstChild );
 
-			this.walkDomTree( $("pagecontent") );
+					node.parentNode.replaceChild( f, node );
+
+					tmp.dispose();
+				}
+			}
 		}
+	};
+
+	if( !query && document.referrer.test("(?:\\?|&)(?:q|query)=([^&]*)","g") ) query = RegExp.$1;
+
+	if( query ){
+
+		words = decodeURIComponent(query).stripScripts(); //xss vulnerability
+		words = words.replace( /\+/g, " " );
+		words = words.replace( /\s+-\S+/g, "" );
+		words = words.replace( /([\(\[\{\\\^\$\|\)\?\*\.\+])/g, "\\$1" ); //escape metachars
+		words = words.trim().split(/\s+/).join("|");
+
+		reMatch = new RegExp( "(" + words + ")" , "gi");
+
+		walkDomTree( node );
+
+	}
+
+};
+
+
+/*
+Class: FileUpload
+	Plugin to modify a basic HTML form to upload multiple files.
+
+	The script works by hiding the file input element when a file is selected,
+	then immediately replacing it with a new, empty one.
+	Although ideally the extra elements would be hidden using the CSS setting
+	'display:none', this causes Safari to ignore the element completely when
+	the form is submitted. So instead, elements are moved to a position
+	off-screen.
+	On submit, any remaining empty file input element is removed.
+
+
+Credit:
+	Inspired by MultiUpload by Stickman, http://the-stickman.com
+	Rewritten for JSPWiki.
+
+Arguments:
+	input - DOM input element
+	options - optional, see options below
+
+Options:
+	max - maximum number of files to upload, 0 means no limit.
+	pattern - pattern string to add file-number to the name and id attributes
+		of the input element. The default pattern is '{0}'
+		Eg: <input name=file{0}>  will be changed to file0, file1, file2.
+	delBtn -
+	id - (optional) Base ID attribute for all input fields, eg. file{Ø}
+		Default takes the ID of the main input element
+	name - (optional) Base name attribute for all input fields, eg. file{Ø}
+		Default takes the name of the main input element
+
+DOM Structure:
+	(start code)
+	<ul class="fileupload'>
+		<li>
+			<input type="file" disabled="" name="file0" id=""/>
+		</li>
+		<li>
+			<a class="delete"/>
+			<span>file-name-1</span>
+			<input type="file" name="file1" id="" style="position:absolute;left:-1000px;"/>
+		</li>
+		<li>
+			<a class="delete"/>
+			<span>file-name-2</span>
+			<input type="file" name="file2" id="" style="position:absolute;left:-1000px;"/>
+		</li>
+	</ul>
+	(end)
+
+Example:
+>		new FileUpload( $('uploadform'), {
+>			max:3,
+>			delBtn:new Element('a',{ 'class':'delete tool' })
+>		});
+
+*/
+var FileUpload = new Class({
+
+	Implements: [Options],
+
+	options:{
+		max: 0,
+		pattern: '{0}'
+		//delBtn:new Element('a')
 	},
 
-	/*
-	Function: walkDomTree
-		Recursive tree walk to match all text nodes
-	*/
-	walkDomTree: function( node ){
+	initialize: function(input, options){
 
-		if( !node ) return;
+		var self = this;
+
+		if( input && (input.get('tag') == 'input') && (input.get('type') == 'file') ){
+
+			self.setOptions(options);
 
-		for( var nn=null, n = node.firstChild; n ; n = nn ){
-			// prefetch nextSibling cause the tree will be modified
-			nn = n. nextSibling;
-			this.walkDomTree(n);
+			var options = self.options,
+				ul = self.ul = new Element('ul',{'class':'fileupload'}).injectAfter(input);
+
+			if( input.id ) options.id = input.id;
+			if( input.name ) options.name = input.name;
+			if( !options.delBtn ) options.delBtn = new Element('a');
+			self.addLI( input );
+
+			input.form.addEvent('submit', function(){
+				//ul.getFirst().getFirst().disabled=true;
+				//ul.getFirst().destroy();
+			});
 		}
+	},
 
-		// continue on text-nodes, not yet highlighted, with a word match
-		if( node.nodeType != 3 ) return;
-		if( node.parentNode.className == "searchword" ) return;
-		var s = node.innerText || node.textContent || '';
+	addLI: function( input ){
 
-		s = s.replace(/</g,'&lt;'); // pre text elements may contain <xml> element
+		var self = this;
 
-		if( this.reMatch.test( s ) ){
-			var tmp = new Element('span',{
-				'html': s.replace(this.reMatch,"<span class='searchword'>$1</span>")
-				}),
-				f = document.createDocumentFragment();
+		input.addEvent('change', self.add.pass(input,self) );
+		new Element('li').grab( input ).injectTop( self.ul );
+		self.setID();
+	},
+
+	setID: function(){
 
-			while( tmp.firstChild ) f.appendChild( tmp.firstChild );
+		var options = this.options,
+			pattern = options.pattern;
 
-			node.parentNode.replaceChild( f, node );
+		this.ul.getElements('input').each( function(item, index){
+
+			item.name = options.name.replace( pattern, index );
+			item.id = options.id.replace( pattern, index );
+
+		});
+	},
+
+	add: function( input ){
+
+		var self = this,
+			options = self.options,
+			max = options.max,
+			count = self.ul.getChildren().length;
+
+		if( max == 0 || count <= max ){
+
+			input
+				.setStyles({ position:'absolute', left:'-999px'}) //hide
+				.getParent().adopt(
+
+					options.delBtn.clone()
+						.addEvent('click', self.remove.pass(input, self)),
+
+					new Element('span', {
+						text: input.value.replace(/.*[\\\/]/, '')
+					})
+				);
+
+			self.addLI( new Element('input',{type:'file', disabled: count == max}) );
 		}
+	},
+
+	remove: function( input ){
+
+		input.getParent().destroy(); //remove list item
+		this.setID();
+		this.ul.getFirst().getFirst().disabled = false;
 	}
-}
+});
+
 
 /*
 Class: Dialog
@@ -2918,7 +3123,7 @@
   /*
   Function: submitFormEvent
     Submits a form to its parent ActionBean URL, using a supplied event.
-    
+
   Arguments:
     formName -  ID of the form to submit. It will be submitted to the
                 action URL supplied by the form element itself. We assume

Modified: incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-commonstyles.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-commonstyles.js?rev=917294&r1=917293&r2=917294&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-commonstyles.js (original)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-commonstyles.js Sun Feb 28 21:12:40 2010
@@ -585,7 +585,15 @@
 
 	if( els && els.length>0 ){
 
-		els.addClass('prettyprint');
+		els.addClass('prettyprint').each(function(el){
+
+			new Element('pre',{
+				'class':'prettylines',
+				html: el.innerHTML.trim().split('\n').map(function(line,i){ return i+1 }).join('\n')
+			}).injectBefore(el);
+
+		});
+
 		prettyPrint(page);
 
 	}

Modified: incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-edit.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-edit.js?rev=917294&r1=917293&r2=917294&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-edit.js (original)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-edit.js Sun Feb 28 21:12:40 2010
@@ -57,8 +57,9 @@
 		//should always run first, but seems not guaranteed on ie so let's do this for sure
 		Wiki.initialize();
 
+		var txta = $('wikiText'), //editorarea v2.8
     // Snip-editing is deliberately disabled, for now, because it causes duplication in post contents.
-		var txta = $('wikiTexts'),
+	//	var txta = $('wikiTexts'),
 			self = this,
 			snipe,
 			config,
@@ -67,6 +68,7 @@
 			tileBtns, tileFn,
  			height = prefs.get('EditorSize');
 
+		if(!txta) return;
 		/*
 			Install an onbeforeunload handler.
 
@@ -145,10 +147,10 @@
 			Initialize the configuration dialog and link it to the buttons
 		*/
 		configFn = function(){
-			snipe.set('directsnips', $('smartpairs').checked ? self.directSnippets : {})
-				.set('tabcompletion', $('tabcompletion').checked == true );
+			snipe.set('directsnips', $('smartPairs').checked ? self.directSnippets : {})
+				.set('tabcompletion', $('tabCompletion').checked == true );
 		};
-		['smartpairs', 'tabcompletion'].each( function(id){
+		['smartPairs', 'tabCompletion'].each( function(id){
 			var element = $(id);
 			if( element ){
 				element.setProperty( 'checked', prefs.get(id) || false )
@@ -266,8 +268,6 @@
 		- wiki-page or url
 		- description:title
 
-
-
 		- target: _blank --new-- window yes or no
 */
 
@@ -502,6 +502,73 @@
 		};
 
 		return result;
+	},
+
+	/*
+	Function: initializePreview
+		Initialize textarea preview functionality.
+		When #autopreview checkbox is checked, bind the
+		[refreshPreview] handler to the {{preview}} event
+		of the textarea.
+
+		Finally, send periodically the preview event.
+	*/
+	initializePreview: function( snipe ){
+
+		var livePreview = 'livePreviewOn',
+			self = this,
+			prefs = Wiki.prefs,
+			refreshFn = self.refreshPreview.bind(self);
+
+		$(livePreview)
+			.set('checked', prefs.get(livePreview) || false)
+			.addEvent('click', function(){
+				prefs.set(livePreview, this.checked);
+				refreshFn();
+			})
+			.fireEvent('click');
+
+		refreshFn.periodical(3000);
+		$(snipe).addEvent('change',refreshFn);
+    },
+
+
+	/*
+	Function: refreshPreview
+		Make AJAX call to the backend to convert the contents of the textarea
+		(wiki markup) to HTML.
+
+	*/
+	refreshPreview: function(){
+
+    	var	self = this,
+    		snipe = self.snipEditor,
+    		text = snipe.get('textarea').getValue(),
+    		page = Wiki.PageName,
+    		preview = $('livePreview'),
+    		spin = $('previewSpin');
+
+
+		if( !$('livePreviewOn').checked ){
+
+			if( self.previewcache ){
+				preview.empty();
+				self.previewcache = null;
+			}
+
+		} else if( self.previewcache != text ){
+
+			self.previewcache = text;
+
+			new Request.HTML({
+				url:Wiki.BaseUrl + "Edit.action?ajaxPreview&page=" + page,
+				data: 'wikiText=' + encodeURIComponent( text ),
+				update: preview,
+				onRequest: function(){ spin.show(); },
+				onComplete: function(){ spin.hide(); Wiki.renderPage(preview, page); }
+			}).send();
+
+		}
 	}
 }
 
@@ -2468,7 +2535,7 @@
 	*/
 	onUndo: function(e){
 
-		if(e) new Event(e).stop();
+		if(e) e.stop();
 
 		if(this.undo.length > 0){
 			this.redo.push( this.obj.getState() );
@@ -2485,7 +2552,7 @@
 	*/
 	onRedo: function(e){
 
-		if(e) new Event(e).stop();
+		if(e) e.stop();
 
 		if(this.redo.length > 0){
 			this.undo.push( this.obj.getState() );

Added: incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-slimbox.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-slimbox.js?rev=917294&view=auto
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-slimbox.js (added)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/jspwiki-slimbox.js Sun Feb 28 21:12:40 2010
@@ -0,0 +1,528 @@
+/*
+Plugin: WikiSlimbox
+	Slimbox clone, refactored for JSPWiki.
+
+	Modified for inclusion in JSPWiki
+	- DONE minimum size of image canvas
+	- DONE add maximum size of image w.r.t window size TODO
+	- DONE spacebor, down arrow, enter : next image
+	- DONE up arrow : prev image done
+	- DONE add support for external page links  => slimbox_ex
+	- DONE add support for youtube, ...
+	- add support for native <video> playback in the browser (not ie)
+
+Credits:
+	Inspired by Slimbox by Christophe Bleys.
+	(see http://www.digitalia.be/software/slimbox)
+	and the mediaboxAdvanced by John Einselen
+	(see http://iaian7.com/webcode/mediaboxAdvanced)
+
+DOM structure:
+	DOM structure of the JSPWiki Slimbox viewer.
+	(start code)
+	<div id="lbOverlay"></div>
+	<div id="lbCenter" class="or spin">
+		<div id="lbImage">
+			<!-- img or iframe element is inserted here -->
+		</div>
+	</div>
+	<div id="lbBottomContainer">
+		<div id="lbBottom">
+			<a id="lbCloseLink"/>
+			<a id="lbNextLink"/>
+			<a id="lbPrevLink"/>
+			<div id="lbCaption">
+			<div id="lbNumber">
+		</div>
+	</div>
+	(end)
+*/
+
+var Slimbox = (function() {
+
+	// Global variables, accessible to Slimbox only
+	var win = window, ie6 = Browser.Engine.trident4, options, images, activeImage = -1, activeURL, prevImage, nextImage, compatibleOverlay, middle, centerWidth, centerHeight,
+
+	// Preload images
+	preload = {}, preloadPrev = new Image(), preloadNext = new Image(),
+
+	// DOM elements
+	overlay, center, image,/* sizer,*/ closeLink, prevLink, nextLink, bottomContainer, bottom, caption, number,
+
+	// MEDIA parameters
+	media, isimage,
+
+	// Effects
+	fxOverlay, fxResize, fxImage, fxBottom;
+
+	/*
+		Initialization
+	*/
+
+	win.addEvent("domready", function() {
+
+		// Append the Slimbox HTML code at the bottom of the document
+		$(document.body).adopt(
+			$$(
+				overlay = new Element("div", {id: "lbOverlay", events: {click: close}}),
+				center = new Element("div", {id: "lbCenter"}),
+				bottomContainer = new Element("div", {id: "lbBottomContainer"})
+			).setStyle("display", "none")
+		);
+
+		//image = new Element("div", {id: "lbImage"}).injectInside(center).adopt(
+		image = new Element("div", {id: "lbImage"}).inject(center)/*.adopt(
+			sizer = new Element("div", {styles: {position: "relative"}}).adopt(
+				prevLink = new Element("a", {id: "lbPrevLink", href: "#", events: {click: previous}}),
+				nextLink = new Element("a", {id: "lbNextLink", href: "#", events: {click: next}})
+			)
+		)*/;
+
+		//bottom = new Element("div", {id: "lbBottom"}).injectInside(bottomContainer).adopt(
+		bottom = new Element("div", {id: "lbBottom"}).inject(bottomContainer).adopt(
+			caption = new Element("div", {id: "lbCaption"}),
+			number = new Element("div", {id: "lbNumber"}),
+			closeLink = new Element("a", {id: "lbCloseLink", events: {click: close}}),
+			nextLink = new Element("a", {id: "lbNextLink", events: {click: next}}),
+			prevLink = new Element("a", {id: "lbPrevLink", events: {click: previous}})
+		);
+	});
+
+
+	/*
+		Internal functions
+	*/
+
+	function position() {
+		var scroll = win.getScroll(), size = win.getSize();
+		$$(center, bottomContainer).setStyle("left", scroll.x + (size.x / 2));
+		if (compatibleOverlay) overlay.setStyles({left: scroll.x, top: scroll.y, width: size.x, height: size.y});
+	}
+
+	function setup(open) {
+		["object", ie6 ? "select" : "embed"].forEach(function(tag) {
+			Array.forEach(document.getElementsByTagName(tag), function(el) {
+				if (open) el._slimbox = el.style.visibility;
+				el.style.visibility = open ? "hidden" : el._slimbox;
+			});
+		});
+
+		overlay.style.display = open ? "" : "none";
+
+		var fn = open ? "addEvent" : "removeEvent";
+		win[fn]("scroll", position)[fn]("resize", position);
+		document[fn]("keydown", keyDown);
+	}
+
+	function keyDown(event) {
+		var code = event.code;
+		// Prevent default keyboard action (like navigating inside the page)
+		return options.closeKeys.contains(code) ? close()
+			: options.nextKeys.contains(code) ? next()
+			: options.previousKeys.contains(code) ? previous()
+			: false;
+	}
+
+	function previous() {
+		return changeImage(prevImage);
+	}
+
+	function next() {
+		return changeImage(nextImage);
+	}
+
+	function changeImage(imageIndex) {
+
+		if (imageIndex >= 0) {
+
+			activeImage = imageIndex;
+			activeURL = images[imageIndex][0];
+			//activeURL = encodeURIComponent(images[imageIndex][0]);
+
+			prevImage = (activeImage || (options.loop ? images.length : 0)) - 1;
+			nextImage = ((activeImage + 1) % images.length) || (options.loop ? 0 : -1);
+
+			stop();
+			center.className = "spin";
+			image.empty();
+
+			//preload = Slimbox.loadImage( activeURL, animateBox, options );
+
+			if( isimage = Slimbox.isImage(activeURL) ){
+
+				preload = new Image()
+				preload.onload = animateBox;
+				preload.src = activeURL;
+
+			} else if( preload = Slimbox.isMedia(activeURL) ){
+
+				//preload now contains item from the .Media repository
+				//extend it with default swiff parameters
+				preload = $extend({
+					width: options.initialWidth,
+					height: options.initialHeight,
+					params: {
+						wmode: 'opaque',
+						bgcolor: '#000000',
+						allowfullscreen: 'true',
+						allowscriptaccess: 'true'
+					}
+				}, preload[1](activeURL) );
+
+				new Swiff(preload.url, preload).inject(image);
+				animateBox();
+
+			} else {
+
+				preload = new IFrame({
+					src:activeURL,
+					width:800,
+					height:600,
+					//frameborder:0,
+					events:{
+						load:animateBox
+					}
+				}).inject(image);
+
+			}
+
+		}
+		return false;
+	}
+
+	function animateBox() {
+
+		center.className = "";
+
+		fxImage.set(0);
+
+		var size = win.getSize();
+		//image.setStyles({backgroundImage: "url(" + activeURL + ")", display: ""});
+		//if( !isimage ) image.adopt(preload);
+		image.setStyles({
+			backgroundImage: (isimage) ? 'url(' + activeURL + ')' : 'none',
+			display:'',
+			width:preload.width.toInt().limit(options.initialWidth, 0.8*size.x),
+			height:preload.height.toInt().limit(options.initialHeight, 0.9*size.y)
+		});
+		//sizer.setStyle("width", preload.width);
+		//$$(sizer, prevLink, nextLink).setStyle("height", preload.height);
+
+		caption.set("html", images[activeImage][1] || "");
+		number.set("html", (((images.length > 1) && options.counterText) || "").replace(/{x}/, activeImage + 1).replace(/{y}/, images.length));
+
+		//if (prevImage >= 0) preloadPrev.src = images[prevImage][0];
+		//if (nextImage >= 0) preloadNext.src = images[nextImage][0];
+		if (prevImage >= 0 && Slimbox.isImage(images[prevImage][0])) preloadPrev.src = images[prevImage][0];
+		if (nextImage >= 0 && Slimbox.isImage(images[nextImage][0])) preloadNext.src = images[nextImage][0];
+
+		centerWidth = image.offsetWidth;
+		centerHeight = image.offsetHeight;
+		var top = Math.max(0, middle - (centerHeight / 2)), check = 0, fn;
+		if (center.offsetHeight != centerHeight) {
+			check = fxResize.start({height: centerHeight, top: top});
+		}
+		if (center.offsetWidth != centerWidth) {
+			check = fxResize.start({width: centerWidth, marginLeft: -centerWidth/2});
+		}
+		fn = function() {
+			//bottomContainer.setStyles({width: centerWidth, top: top + centerHeight, marginLeft: -centerWidth/2, /*visibility: "hidden",*/ display: ""});
+			bottomContainer.setStyles({width: centerWidth, top: top + centerHeight, marginLeft: -centerWidth/2, display: ""});
+			fxImage.start(1);
+		};
+		if (check) {
+			fxResize.chain(fn);
+		}
+		else {
+			fn();
+		}
+	}
+
+	function animateCaption() {
+		//if (prevImage >= 0) prevLink.style.display = "";
+		//if (nextImage >= 0) nextLink.style.display = "";
+		if(prevImage >= 0) prevLink.set({title:images[prevImage][1] || "", styles:{display:""}});
+		if(nextImage >= 0) nextLink.set({title:images[nextImage][1] || "", styles:{display:""}});
+
+		//fxBottom.set(-bottom.offsetHeight).start(0);
+		fxBottom.start(1);
+		//move to animateBox
+		//bottomContainer.style.visibility = "";
+	}
+
+	function stop() {
+		preload.onload = $empty;
+		//not needed: image.empty() will stop loading, and avoid "refiring" of animateBox()
+		//preload.src = preloadPrev.src = preloadNext.src = activeURL;
+		fxResize.cancel();
+		fxImage.cancel();
+		//fxBottom.cancel();
+		fxBottom.cancel().set(0);
+		$$(prevLink, nextLink, image, bottomContainer).setStyle("display", "none");
+	}
+
+	function close() {
+		if (activeImage >= 0) {
+			stop();
+			activeImage = prevImage = nextImage = -1;
+			center.style.display = "none";
+			fxOverlay.cancel().chain(setup).start(0);
+		}
+
+		return false;
+	}
+
+
+	/*
+		API
+	*/
+
+	/* not used
+	Element.implement({
+		slimbox: function(_options, linkMapper) {
+			// The processing of a single element is similar to the processing of a collection with a single element
+			$$(this).slimbox(_options, linkMapper);
+
+			return this;
+		}
+	});
+	*/
+
+		/*
+			options:	Optional options object, see Slimbox.open()
+			linkMapper:	Optional function taking a link DOM element and an index as arguments and returning an array containing 2 elements:
+					the image URL and the image caption (may contain HTML)
+			linksFilter:	Optional function taking a link DOM element and an index as arguments and returning true if the element is part of
+					the image collection that will be shown on click, false if not. "this" refers to the element that was clicked.
+					This function must always return true when the DOM element argument is "this".
+		*/
+	/* not used
+	Elements.implement({
+		slimbox: function(_options, linkMapper, linksFilter) {
+			linkMapper = linkMapper || function(el) {
+				return [el.href, el.title];
+			};
+
+			linksFilter = linksFilter || function() {
+				return true;
+			};
+
+			var links = this;
+
+			links.removeEvents("click").addEvent("click", function() {
+				// Build the list of images that will be displayed
+				var filteredLinks = links.filter(linksFilter, this);
+				return Slimbox.open(filteredLinks.map(linkMapper), filteredLinks.indexOf(this), _options);
+			});
+
+			return links;
+		}
+	});
+	*/
+	return {
+		open: function(_images, startImage, _options) {
+			options = $extend({
+				loop: true, // Allows to navigate between first and last images
+				overlayOpacity: 0.4, // 1 is opaque, 0 is completely transparent (change the color in the CSS file)
+				overlayFadeDuration: 300, // Duration of the overlay fade-in and fade-out animations (in milliseconds)
+				resizeDuration: 240, // Duration of each of the box resize animations (in milliseconds)
+				resizeTransition: false, // false uses the mootools default transition
+				initialWidth: 400, // Initial width of the box (in pixels)
+				initialHeight: 200, // Initial height of the box (in pixels)
+				imageFadeDuration: 300, // Duration of the image fade-in animation (in milliseconds)
+				captionAnimationDuration: 300, // Duration of the caption animation (in milliseconds)
+				closeText: "Close",
+				nextText: "Next",
+				prevText: "Previous",
+				counterText: "({x} of {y})", // Translate or change as you wish, or set it to false to disable counter text for image groups
+				closeKeys: [27, 88, 67], // Array of keycodes to close Slimbox, default: Esc (27), 'x' (88), 'c' (67)
+				previousKeys: [37, 38, 80], // Array of keycodes to navigate to the previous image, default: Left arrow (37), Up arrow(38), 'p' (80)
+				nextKeys: [13, 32, 39, 40, 78] // Array of keycodes to navigate to the next image, default: Enter(13), Space(32), Right arrow (39), Down arrow(40), 'n' (78)
+			}, _options || {});
+
+			// Setup bottom labels
+			prevLink.set('html',options.prevText);
+			nextLink.set('html',options.nextText);
+			closeLink.set('html',options.closeText);
+
+			// Setup effects
+			fxOverlay = new Fx.Tween(overlay, {property: "opacity", duration: options.overlayFadeDuration});
+			fxResize = new Fx.Morph(center, $extend({duration: options.resizeDuration, link: "chain"}, options.resizeTransition ? {transition: options.resizeTransition} : {}));
+			fxImage = new Fx.Tween(image, {property: "opacity", duration: options.imageFadeDuration, onComplete: animateCaption});
+			//fxBottom = new Fx.Tween(bottom, {property: "margin-top", duration: options.captionAnimationDuration});
+			fxBottom = new Fx.Tween(bottom, {property: "opacity", duration: options.captionAnimationDuration}).set(0)
+
+			// The function is called for a single image, with URL and Title as first two arguments
+			if (typeof _images == "string") {
+				_images = [[_images, startImage]];
+				startImage = 0;
+			}
+
+			middle = win.getScrollTop() + (win.getHeight() / 2);
+			centerWidth = options.initialWidth;
+			centerHeight = options.initialHeight;
+			center.setStyles({top: Math.max(0, middle - (centerHeight / 2)), width: centerWidth, height: centerHeight, marginLeft: -centerWidth/2, display: ""});
+			compatibleOverlay = ie6 || (overlay.currentStyle && (overlay.currentStyle.position != "fixed"));
+			//solved in css _position
+			//if (compatibleOverlay) overlay.style.position = "absolute";
+			fxOverlay.set(0).start(options.overlayOpacity);
+			position();
+			setup(1);
+
+			images = _images;
+			options.loop = options.loop && (images.length > 1);
+			return changeImage(startImage);
+		},
+
+		loadImage: function( url, callbackFn, options ){
+
+			var preload;
+
+			if( isimage = this.isImage( url ) ){
+
+				preload = new Image()
+				preload.onload = callbackFn;
+				preload.src = url;
+
+			} else if( preload = this.isMedia( url ) ){
+
+				//preload now contains item from the .Media repository
+				//extend it with default swiff parameters
+				preload = $extend({
+					width: options.initialWidth,
+					height: options.initialHeight,
+					params: {
+						wmode: 'opaque',
+						bgcolor: '#000000',
+						allowfullscreen: 'true',
+						allowscriptaccess: 'true'
+					}
+				}, preload[1]( url ) );
+
+				new Swiff(preload.url, preload);
+				callbackFn();
+
+			} else {
+
+				preload = new IFrame({
+					src:activeURL,
+					width:800,
+					height:600,
+					//frameborder:0,
+					events:{
+						load:callbackFn
+					}
+				});
+
+			}
+
+			return preload;
+		},
+
+		Image: "(\.bmp|\.gif|\.png|\.jpg|\.jpeg)$",
+		isImage: function( url ){
+			return url.test(this.Image,'i')
+		},
+
+		Media: [],
+		isMedia: function(url){
+
+			for( var media = this.Media, i=0, l=media.length; i<l; i++){
+				if( url.test( media[i][0].escapeRegExp(),'i') ) return media[i];
+			}
+			return null;
+		}
+
+	};
+
+})();
+
+
+Slimbox.Media.extend([
+
+	//fixme
+	['.swf', function(url){
+		return { url:url }
+	}],
+	['facebook.com', function(url){
+  		url = 'http://www.facebook.com/v/' + url.split('v=')[1].split('&')[0];
+  		return {
+  			url:url,
+			movie:url,
+			width:320,
+			height:240,
+			classid: 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'
+		}
+	}],
+	['flickr.com', function(url){
+		url = url.split('/')[5];
+		return {
+			url: 'http://www.flickr.com/apps/video/stewart.swf',
+			classid: 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000',
+			width:500,
+			height:375,
+			params: {flashvars: 'photo_id='+url+'&amp;show_info_box=true' }
+		}
+	}],
+	['google.com/videoplay', function(url){
+		url = url.split('=')[1];
+		return {
+			url:'http://video.google.com/googleplayer.swf?docId='+url/*+'&autoplay=0'*/,
+			width:400,
+			height:326
+		}
+	}],
+	['youtube.com/watch', function(url){
+		url = url.split('v=')[1];
+		var parms =
+			(url.test(/fmt=18/i)) ? ['&ap=%2526fmt%3D18',560,345] :
+			(url.test(/fmt=22/i)) ? ['&ap=%2526fmt%3D22',640,385] :
+			/*else*/ ['&ap=%2526fmt%3D18',425,344];
+
+		return {
+			url:'http://www.youtube.com/v/'+url
+			+'&autoplay=0&fs=1'+parms[0]+'&border=0&rel=0&showinfo=1&showsearch=0&feature=player_embedded',
+			width:parms[1],
+			height:parms[2]
+		}
+	}],
+	['youtube.com/view', function(url){
+		url = url.split('p=')[1];
+		return{
+			url:'http://www.youtube.com/p/'+url
+				+'&autoplay='+options.autoplayNum+'&fs='+options.fullscreenNum+mediaFmt
+				+'&border='+options.ytBorder+'&color1=0x'+options.ytColor1+'&color2=0x'+options.ytColor2
+				+'&rel='+options.ytRel+'&showinfo='+options.ytInfo+'&showsearch='+options.ytSearch,
+			width:480,
+			height:385
+		};
+	}],
+	['metacafe.com/watch', function(url){
+		url = url.split('/')[4];
+		return {
+			url: 'http://www.metacafe.com/fplayer/'+url+'/.swf?playerVars=autoPlay=1',
+			width: 400,
+			height: 345
+		}
+	}],
+	["megavideo.com", function(url){
+		url = url.split('=')[1];
+		return {
+			url:'http://wwwstatic.megavideo.com/mv_player.swf?v='+url,
+			width:640,
+			height:360
+		}
+	}],
+	['vimeo.com', function(url){
+		url = url.split('/')[3];
+		return{
+			url:'http://www.vimeo.com/moogaloop.swf?clip_id='+url
+			 	+'&amp;server=www.vimeo.com&amp;fullscreen=1&amp;autoplay=0'
+			 	+'&amp;show_title=1&amp;show_byline=1&amp;show_portrait=1&amp;color=ffffff',
+			width:640,
+			height:360
+		};
+	}]
+
+]);
+

Modified: incubator/jspwiki/trunk/src/WebContent/scripts/mootools-core.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/WebContent/scripts/mootools-core.js?rev=917294&r1=917293&r2=917294&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/WebContent/scripts/mootools-core.js (original)
+++ incubator/jspwiki/trunk/src/WebContent/scripts/mootools-core.js Sun Feb 28 21:12:40 2010
@@ -1,6 +1,6 @@
 //MooTools, <http://mootools.net>, My Object Oriented (JavaScript) Tools. Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
 
-var MooTools={version:"1.2.3",build:"4980aa0fb74d2f6eb80bcd9f5b8e1fd6fbb8f607"};var Native=function(k){k=k||{};var a=k.name;var i=k.legacy;var b=k.protect;
+var MooTools={version:"1.2.4",build:"0d9113241a90b9cd5643b926795852a2026710d4"};var Native=function(k){k=k||{};var a=k.name;var i=k.legacy;var b=k.protect;
 var c=k.implement;var h=k.generics;var f=k.initialize;var g=k.afterImplement||function(){};var d=f||i;h=h!==false;d.constructor=Native;d.$family={name:"native"};
 if(i&&f){d.prototype=i.prototype;}d.prototype.constructor=d;if(a){var e=a.toLowerCase();d.prototype.$family={name:e};Native.typize(d,e);}var j=function(n,l,o,m){if(!b||m||!n.prototype[l]){n.prototype[l]=o;
 }if(h){Native.genericize(n,l,b);}g.call(n,l,o);return n;};d.alias=function(n,l,p){if(typeof n=="string"){var o=this.prototype[n];if((n=o)){return j(this,l,n,p);
@@ -25,11 +25,11 @@
 }if(a.nodeName){switch(a.nodeType){case 1:return"element";case 3:return(/\S/).test(a.nodeValue)?"textnode":"whitespace";}}else{if(typeof a.length=="number"){if(a.callee){return"arguments";
 }else{if(a.item){return"collection";}}}}return typeof a;}function $unlink(c){var b;switch($type(c)){case"object":b={};for(var e in c){b[e]=$unlink(c[e]);
 }break;case"hash":b=new Hash(c);break;case"array":b=[];for(var d=0,a=c.length;d<a;d++){b[d]=$unlink(c[d]);}break;default:return c;}return b;}var Browser=$merge({Engine:{name:"unknown",version:0},Platform:{name:(window.orientation!=undefined)?"ipod":(navigator.platform.match(/mac|win|linux/i)||["other"])[0].toLowerCase()},Features:{xpath:!!(document.evaluate),air:!!(window.runtime),query:!!(document.querySelector)},Plugins:{},Engines:{presto:function(){return(!window.opera)?false:((arguments.callee.caller)?960:((document.getElementsByClassName)?950:925));
-},trident:function(){return(!window.ActiveXObject)?false:((window.XMLHttpRequest)?5:4);},webkit:function(){return(navigator.taintEnabled)?false:((Browser.Features.xpath)?((Browser.Features.query)?525:420):419);
-},gecko:function(){return(document.getBoxObjectFor==undefined)?false:((document.getElementsByClassName)?19:18);}}},Browser||{});Browser.Platform[Browser.Platform.name]=true;
+},trident:function(){return(!window.ActiveXObject)?false:((window.XMLHttpRequest)?((document.querySelectorAll)?6:5):4);},webkit:function(){return(navigator.taintEnabled)?false:((Browser.Features.xpath)?((Browser.Features.query)?525:420):419);
+},gecko:function(){return(!document.getBoxObjectFor&&window.mozInnerScreenX==null)?false:((document.getElementsByClassName)?19:18);}}},Browser||{});Browser.Platform[Browser.Platform.name]=true;
 Browser.detect=function(){for(var b in this.Engines){var a=this.Engines[b]();if(a){this.Engine={name:b,version:a};this.Engine[b]=this.Engine[b+a]=true;
 break;}}return{name:b,version:a};};Browser.detect();Browser.Request=function(){return $try(function(){return new XMLHttpRequest();},function(){return new ActiveXObject("MSXML2.XMLHTTP");
-});};Browser.Features.xhr=!!(Browser.Request());Browser.Plugins.Flash=(function(){var a=($try(function(){return navigator.plugins["Shockwave Flash"].description;
+},function(){return new ActiveXObject("Microsoft.XMLHTTP");});};Browser.Features.xhr=!!(Browser.Request());Browser.Plugins.Flash=(function(){var a=($try(function(){return navigator.plugins["Shockwave Flash"].description;
 },function(){return new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version");})||"0 r0").match(/\d+/g);return{version:parseInt(a[0]||0+"."+a[1],10)||0,build:parseInt(a[2],10)||0};
 })();function $exec(b){if(!b){return b;}if(window.execScript){window.execScript(b);}else{var a=document.createElement("script");a.setAttribute("type","text/javascript");
 a[(Browser.Engine.webkit&&Browser.Engine.version<420)?"innerText":"text"]=b;document.head.appendChild(a);document.head.removeChild(a);}return b;}Native.UID=1;
@@ -117,7 +117,7 @@
 new Document(c.contentWindow.document);$extend(h.Element.prototype,Element.Prototype);}e.call(c.contentWindow,c.contentWindow.document);};var a=$try(function(){return c.contentWindow;
 });((a&&a.document.body)||window.frames[d.id])?b():c.addListener("load",b);return c;}});var Elements=new Native({initialize:function(f,b){b=$extend({ddup:true,cash:true},b);
 f=f||[];if(b.ddup||b.cash){var g={},e=[];for(var c=0,a=f.length;c<a;c++){var d=document.id(f[c],!b.cash);if(b.ddup){if(g[d.uid]){continue;}g[d.uid]=true;
-}e.push(d);}f=e;}return(b.cash)?$extend(f,this):f;}});Elements.implement({filter:function(a,b){if(!a){return this;}return new Elements(Array.filter(this,(typeof a=="string")?function(c){return c.match(a);
+}if(d){e.push(d);}}f=e;}return(b.cash)?$extend(f,this):f;}});Elements.implement({filter:function(a,b){if(!a){return this;}return new Elements(Array.filter(this,(typeof a=="string")?function(c){return c.match(a);
 }:a,b));}});Document.implement({newElement:function(a,b){if(Browser.Engine.trident&&b){["name","type","checked"].each(function(c){if(!b[c]){return;}a+=" "+c+'="'+b[c]+'"';
 if(c!="checked"){delete b[c];}});a="<"+a+">";}return document.id(this.createElement(a)).set(b);},newTextNode:function(a){return this.createTextNode(a);
 },getDocument:function(){return this;},getWindow:function(){return this.window;},id:(function(){var a={string:function(d,c,b){d=b.getElementById(d);return(d)?a.element(d,c):null;
@@ -206,13 +206,13 @@
 },getScrollSize:function(){if(b(this)){return this.getWindow().getScrollSize();}return{x:this.scrollWidth,y:this.scrollHeight};},getScroll:function(){if(b(this)){return this.getWindow().getScroll();
 }return{x:this.scrollLeft,y:this.scrollTop};},getScrolls:function(){var i=this,h={x:0,y:0};while(i&&!b(i)){h.x+=i.scrollLeft;h.y+=i.scrollTop;i=i.parentNode;
 }return h;},getOffsetParent:function(){var h=this;if(b(h)){return null;}if(!Browser.Engine.trident){return h.offsetParent;}while((h=h.parentNode)&&!b(h)){if(d(h,"position")!="static"){return h;
-}}return null;},getOffsets:function(){if(this.getBoundingClientRect){var m=this.getBoundingClientRect(),k=document.id(this.getDocument().documentElement),i=k.getScroll(),n=(d(this,"position")=="fixed");
-return{x:parseInt(m.left,10)+((n)?0:i.x)-k.clientLeft,y:parseInt(m.top,10)+((n)?0:i.y)-k.clientTop};}var j=this,h={x:0,y:0};if(b(this)){return h;}while(j&&!b(j)){h.x+=j.offsetLeft;
-h.y+=j.offsetTop;if(Browser.Engine.gecko){if(!f(j)){h.x+=c(j);h.y+=g(j);}var l=j.parentNode;if(l&&d(l,"overflow")!="visible"){h.x+=c(l);h.y+=g(l);}}else{if(j!=this&&Browser.Engine.webkit){h.x+=c(j);
-h.y+=g(j);}}j=j.offsetParent;}if(Browser.Engine.gecko&&!f(this)){h.x-=c(this);h.y-=g(this);}return h;},getPosition:function(k){if(b(this)){return{x:0,y:0};
-}var l=this.getOffsets(),i=this.getScrolls();var h={x:l.x-i.x,y:l.y-i.y};var j=(k&&(k=document.id(k)))?k.getPosition():{x:0,y:0};return{x:h.x-j.x,y:h.y-j.y};
-},getCoordinates:function(j){if(b(this)){return this.getWindow().getCoordinates();}var h=this.getPosition(j),i=this.getSize();var k={left:h.x,top:h.y,width:i.x,height:i.y};
-k.right=k.left+k.width;k.bottom=k.top+k.height;return k;},computePosition:function(h){return{left:h.x-e(this,"margin-left"),top:h.y-e(this,"margin-top")};
+}}return null;},getOffsets:function(){if(this.getBoundingClientRect){var j=this.getBoundingClientRect(),m=document.id(this.getDocument().documentElement),p=m.getScroll(),k=this.getScrolls(),i=this.getScroll(),h=(d(this,"position")=="fixed");
+return{x:j.left.toInt()+k.x-i.x+((h)?0:p.x)-m.clientLeft,y:j.top.toInt()+k.y-i.y+((h)?0:p.y)-m.clientTop};}var l=this,n={x:0,y:0};if(b(this)){return n;
+}while(l&&!b(l)){n.x+=l.offsetLeft;n.y+=l.offsetTop;if(Browser.Engine.gecko){if(!f(l)){n.x+=c(l);n.y+=g(l);}var o=l.parentNode;if(o&&d(o,"overflow")!="visible"){n.x+=c(o);
+n.y+=g(o);}}else{if(l!=this&&Browser.Engine.webkit){n.x+=c(l);n.y+=g(l);}}l=l.offsetParent;}if(Browser.Engine.gecko&&!f(this)){n.x-=c(this);n.y-=g(this);
+}return n;},getPosition:function(k){if(b(this)){return{x:0,y:0};}var l=this.getOffsets(),i=this.getScrolls();var h={x:l.x-i.x,y:l.y-i.y};var j=(k&&(k=document.id(k)))?k.getPosition():{x:0,y:0};
+return{x:h.x-j.x,y:h.y-j.y};},getCoordinates:function(j){if(b(this)){return this.getWindow().getCoordinates();}var h=this.getPosition(j),i=this.getSize();
+var k={left:h.x,top:h.y,width:i.x,height:i.y};k.right=k.left+k.width;k.bottom=k.top+k.height;return k;},computePosition:function(h){return{left:h.x-e(this,"margin-left"),top:h.y-e(this,"margin-top")};
 },setPosition:function(h){return this.setStyles(this.computePosition(h));}});Native.implement([Document,Window],{getSize:function(){if(Browser.Engine.presto||Browser.Engine.webkit){var i=this.getWindow();
 return{x:i.innerWidth,y:i.innerHeight};}var h=a(this);return{x:h.clientWidth,y:h.clientHeight};},getScroll:function(){var i=this.getWindow(),h=a(this);
 return{x:i.pageXOffset||h.scrollLeft,y:i.pageYOffset||h.scrollTop};},getScrollSize:function(){var i=a(this),h=this.getSize();return{x:Math.max(i.scrollWidth,h.x),y:Math.max(i.scrollHeight,h.y)};
@@ -247,7 +247,7 @@
 }}return h;},"+":function(c,b,a,e,d){while((b=b.nextSibling)){if(b.nodeType==1){if(Selectors.Utils.chk(b,d)&&Selectors.Filters.byTag(b,a)&&Selectors.Filters.byID(b,e)){c.push(b);
 }break;}}return c;},"~":function(c,b,a,e,d){while((b=b.nextSibling)){if(b.nodeType==1){if(!Selectors.Utils.chk(b,d)){break;}if(Selectors.Filters.byTag(b,a)&&Selectors.Filters.byID(b,e)){c.push(b);
 }}}return c;}};Selectors.Filters={byTag:function(b,a){return(a=="*"||(b.tagName&&b.tagName.toLowerCase()==a));},byID:function(a,b){return(!b||(a.id&&a.id==b));
-},byClass:function(b,a){return(b.className&&b.className.contains(a," "));},byPseudo:function(a,d,c,b){return d.call(a,c,b);},byAttribute:function(c,d,b,e){var a=Element.prototype.getProperty.call(c,d);
+},byClass:function(b,a){return(b.className&&b.className.contains&&b.className.contains(a," "));},byPseudo:function(a,d,c,b){return d.call(a,c,b);},byAttribute:function(c,d,b,e){var a=Element.prototype.getProperty.call(c,d);
 if(!a){return(b=="!=");}if(!b||e==undefined){return true;}switch(b){case"=":return(a==e);case"*=":return(a.contains(e));case"^=":return(a.substr(0,e.length)==e);
 case"$=":return(a.substr(a.length-e.length)==e);case"!=":return(a!=e);case"~=":return a.contains(e," ");case"|=":return a.contains(e,"-");}return false;
 }};Selectors.Pseudo=new Hash({checked:function(){return this.checked;},empty:function(){return !(this.innerText||this.textContent||"").length;},not:function(a){return !Element.match(this,a);
@@ -259,9 +259,10 @@
 },index:function(a){var b=this,c=0;while((b=b.previousSibling)){if(b.nodeType==1&&++c>a){return false;}}return(c==a);},even:function(b,a){return Selectors.Pseudo["nth-child"].call(this,"2n+1",a);
 },odd:function(b,a){return Selectors.Pseudo["nth-child"].call(this,"2n",a);},selected:function(){return this.selected;},enabled:function(){return(this.disabled===false);
 }});Element.Events.domready={onAdd:function(a){if(Browser.loaded){a.call(this);}}};(function(){var b=function(){if(Browser.loaded){return;}Browser.loaded=true;
-window.fireEvent("domready");document.fireEvent("domready");};if(Browser.Engine.trident){var a=document.createElement("div");(function(){($try(function(){a.doScroll();
-return document.id(a).inject(document.body).set("html","temp").dispose();}))?b():arguments.callee.delay(50);})();}else{if(Browser.Engine.webkit&&Browser.Engine.version<525){(function(){(["loaded","complete"].contains(document.readyState))?b():arguments.callee.delay(50);
-})();}else{window.addEvent("load",b);document.addEvent("DOMContentLoaded",b);}}})();var JSON=new Hash({$specialChars:{"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},$replaceChars:function(a){return JSON.$specialChars[a]||"\\u00"+Math.floor(a.charCodeAt()/16).toString(16)+(a.charCodeAt()%16).toString(16);
+window.fireEvent("domready");document.fireEvent("domready");};window.addEvent("load",b);if(Browser.Engine.trident){var a=document.createElement("div");
+(function(){($try(function(){a.doScroll();return document.id(a).inject(document.body).set("html","temp").dispose();}))?b():arguments.callee.delay(50);})();
+}else{if(Browser.Engine.webkit&&Browser.Engine.version<525){(function(){(["loaded","complete"].contains(document.readyState))?b():arguments.callee.delay(50);
+})();}else{document.addEvent("DOMContentLoaded",b);}}})();var JSON=new Hash(this.JSON&&{stringify:JSON.stringify,parse:JSON.parse}).extend({$specialChars:{"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},$replaceChars:function(a){return JSON.$specialChars[a]||"\\u00"+Math.floor(a.charCodeAt()/16).toString(16)+(a.charCodeAt()%16).toString(16);
 },encode:function(b){switch($type(b)){case"string":return'"'+b.replace(/[\x00-\x1f\\"]/g,JSON.$replaceChars)+'"';case"array":return"["+String(b.map(JSON.encode).clean())+"]";
 case"object":case"hash":var a=[];Hash.each(b,function(e,d){var c=JSON.encode(e);if(c){a.push(JSON.encode(d)+":"+c);}});return"{"+a+"}";case"number":case"boolean":return String(b);
 case false:return"null";}return null;},decode:function(string,secure){if($type(string)!="string"||!string.length){return null;}if(secure&&!(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g,"@").replace(/"[^"\\\n\r]*"/g,""))){return null;
@@ -331,7 +332,7 @@
 },failure:function(){this.onFailure();},onFailure:function(){this.fireEvent("complete").fireEvent("failure",this.xhr);},setHeader:function(a,b){this.headers.set(a,b);
 return this;},getHeader:function(a){return $try(function(){return this.xhr.getResponseHeader(a);}.bind(this));},check:function(){if(!this.running){return true;
 }switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.bind(this,arguments));return false;}return false;},send:function(k){if(!this.check(k)){return this;
-}this.running=true;var i=$type(k);if(i=="string"||i=="element"){k={data:k};}var d=this.options;k=$extend({data:d.data,url:d.url,method:d.method},k);var g=k.data,b=k.url,a=k.method.toLowerCase();
+}this.running=true;var i=$type(k);if(i=="string"||i=="element"){k={data:k};}var d=this.options;k=$extend({data:d.data,url:d.url,method:d.method},k);var g=k.data,b=String(k.url),a=k.method.toLowerCase();
 switch($type(g)){case"element":g=document.id(g).toQueryString();break;case"object":case"hash":g=Hash.toQueryString(g);}if(this.options.format){var j="format="+this.options.format;
 g=(g)?j+"&"+g:j;}if(this.options.emulation&&!["get","post"].contains(a)){var h="_method="+a;g=(g)?h+"&"+g:h;a="post";}if(this.options.urlEncoded&&a=="post"){var c=(this.options.encoding)?"; charset="+this.options.encoding:"";
 this.headers.set("Content-type","application/x-www-form-urlencoded"+c);}if(this.options.noCache){var f="noCache="+new Date().getTime();g=(g)?f+"&"+g:f;