You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by aj...@apache.org on 2008/02/13 07:11:28 UTC
svn commit: r627259 [2/3] - in
/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs: applets/
applets/WiWi-INF/ applets/WiWi-INF/extensions/ error/ images/ scripts/
scripts/fckeditor/ scripts/json-rpc/ templates/ templates/default/
templates/...
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs/scripts/jspwiki-common.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs/scripts/jspwiki-common.js?rev=627259&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs/scripts/jspwiki-common.js (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs/scripts/jspwiki-common.js Tue Feb 12 22:11:16 2008
@@ -0,0 +1,1949 @@
+/**
+ ** Javascript routines to support the BrushedTemplate
+ ** Dirk Frederickx
+ ** Nov 06-Mar 07: aligned with MooTools ##
+ ** moo v1.1 selection needed:
+ ** Core, Class, Native, Element(ex. Dimensions), Window,
+ ** Effects(ex. Scroll), Drag(Base), Remote, Plugins(Hash.Cookie, Tips, Accordion)
+ **
+ ** 100 Wiki object (page parms, UserPrefs and setting focus) ##
+ ** 110 WikiSlimbox : attachment viewer ##
+ ** 114 Reflection (adds reflection to images) ##
+ ** 120 QuickLinks object ##
+ ** 130 TabbedSection object
+ ** 132 Accordion object ##
+ ** 140 SearchBox object: remember 10 most recent search topics
+ ** 150 Colors, GraphBar object: e.g. used on the findpage
+ ** 160 URL
+ **
+ ** 200 Collapsible list items
+ ** 220 RoundedCorners ffs
+ ** 230 Sortable (clever table-sort) ##
+ ** 240 Table-filter (excel like table filters ##
+ ** 250 Categories: turn wikipage link into AJAXed popup ##
+ ** 260 WikiTips ##
+ ** 270 WikiColumns ##
+ ** 280 ZebraTable: color odd/even row of a table ##
+ ** 290 HighlightWord: refactored
+ ** 295 Typography
+ ** 300 Prettify
+ **/
+
+
+/* extend mootools */
+String.extend({
+ deCamelize: function(){
+ return this.replace(/([a-z])([A-Z])/g,"$1 $2");
+ }
+});
+
+
+// get text of a dhtml node
+function $getText(el) {
+ return el.innerText || el.textContent || '';
+}
+Element.extend({
+ /* wrapper = new Element('div').injectWrapper(node); */
+ injectWrapper: function(el){
+ while( el.firstChild ) this.appendChild( el.firstChild );
+ el.appendChild( this ) ;
+ return this;
+ },
+ visible: function() {
+ var el = this;
+ while($type(el)=='element'){
+ if(el.getStyle('visibility') == 'hidden') return false;
+ if(el.getStyle('display') == 'none' ) return false;
+ el = el.getParent();
+ }
+ return true;
+ },
+ hide: function() {
+ this.style.display = 'none';
+ return this;
+ },
+ show: function() {
+ this.style.display = '';
+ return this;
+ },
+ toggle: function() {
+ this.visible() ? this.hide() : this.show();
+ return this;
+ },
+ scrollTo: function(x, y){
+ this.scrollLeft = x;
+ this.scrollTop = y;
+ },
+ /* dimensions.js */
+ getPosition: function(overflown){
+ overflown = overflown || [];
+ var el = this, left = 0, top = 0;
+ do {
+ left += el.offsetLeft || 0;
+ top += el.offsetTop || 0;
+ el = el.offsetParent;
+ } while (el);
+ overflown.each(function(element){
+ left -= element.scrollLeft || 0;
+ top -= element.scrollTop || 0;
+ });
+ return {'x': left, 'y': top};
+ }
+});
+
+var Observer = new Class({
+ initialize: function(el, fn, options){
+ this.options = Object.extend({
+ event: 'keyup',
+ delay: 300
+ }, options || {});
+ this.element = $(el);
+ this.callback = fn;
+ this.timeout = null;
+ this.listener = this.fired.bind(this);
+ this.value = this.element.getValue();
+ this.element.setProperty('autocomplete','off').addEvent(this.options.event, this.listener);
+ },
+ fired: function() {
+ if (this.value == this.element.value) return;
+ this.clear();
+ this.value = this.element.value;
+ this.timeout = this.callback.delay(this.options.delay, null, [this.element]);
+ },
+ clear: function() {
+ this.timeout = $clear(this.timeout);
+ },
+ stop: function() {
+ this.element.removeEvent(this.options.event, this.listener);
+ this.clear();
+ }
+});
+/* Observable class: observe any form element for changes */
+Element.extend({
+ observe: function(fn, options){
+ return new Observer(this, fn, options);
+ }
+});
+
+
+/* I18N Support
+ * LocalizedStrings takes form { "javascript.some.resource.key":"localised resource key {0}" }
+ * Examples:
+ * "javascript.moreInfo".localize();
+ * "javascript.imageInfo".localize(2,4); => expects "Image {0} of {1}"
+ */
+String.extend({
+ localize: function(){
+ var s = LocalizedStrings["javascript."+this], args = arguments;
+ if(!s) return("???" + this + "???");
+ return s.replace(/\{(\d)\}/g, function(m){
+ return args[m.charAt(1)] || "???"+m.charAt(1)+"???";
+ });
+ }
+});
+
+/* FIXME parse number anywhere inside a string */
+Number.REparsefloat = new RegExp( "([+-]?\\d+(:?\\.\\d+)?(:?e[-+]?\\d+)?)", "i");
+
+/** TABLE stuff **/
+function $T(el) {
+ var t = $(el);
+ return (t && t.tBodies[0]) ? $(t.tBodies[0]) : t;
+};
+
+/* FIXME */
+// find first ancestor element with tagName
+function getAncestorByTagName( node, tagName ) {
+ if( !node) return null;
+ if( node.nodeType == 1 && (node.tagName.toLowerCase() == tagName.toLowerCase())){
+ return node;
+ } else {
+ return getAncestorByTagName( node.parentNode, tagName );
+ }
+}
+
+
+/** 100 Wiki functions **/
+var Wiki = {
+
+ JSONid : 10000,
+ DELIM : '\u00A4',
+ init: function(props){
+ Object.extend(Wiki,props || {});
+ var h=location.host;
+ this.BasePath = this.BaseUrl.slice(this.BaseUrl.indexOf(h)+h.length,-1);
+ //this.ClientLanguage = navigator.language ? navigator.language : navigator.userLanguage;
+ //this.ClientTimezone = new Date().getTimezoneOffset()/60;
+ this.prefs=new Hash.Cookie('JSPWikiUserPrefs', {path:Wiki.BasePath, duration:20});
+ },
+ getUrl: function(pagename){
+ return this.PageUrl.replace(/%23%24%25/, pagename);
+ },
+ /* retrieve pagename from any wikipage url format */
+ getPageName: function(url){
+ var s = this.PageUrl.escapeRegExp().replace(/%23%24%25/, '(.+)'),
+ res = url.match(new RegExp(s));
+ return (res ? res[1] : false);
+ },
+ onPageLoad: function(){
+ this.PermissionEdit = ($E('a.edit') !== undefined); //deduct permission level
+ this.url = null;
+ this.parseLocationHash.periodical(500);
+
+ /* plain.jsp, login.jsp, prefs/profile, prefs/prefs, find */
+ ['editorarea','j_username','loginname','assertedName','query2'].some(function(el){
+ el = $(el);
+ if(el && el.visible()) { el.focus(); return true; }
+ return false;
+ });
+
+ if($('morebutton')) this.replaceMoreBox(); /* visual sugar */
+ },
+ savePrefs: function(){
+ if($('prefSkin')) this.prefs.set('SkinName', $('prefSkin').getValue());
+ if($('prefTimeZone')) this.prefs.set('TimeZone', $('prefTimeZone').getValue());
+ if($('prefTimeFormat')) this.prefs.set('DateFormat', $('prefTimeFormat').getValue());
+ if($('prefOrientation')) this.prefs.set('orientation', $('prefOrientation').getValue());
+ if($('editor')) this.prefs.set('editor', $('editor').getValue());
+ this.prefs.set('FontSize',this.PrefFontSize);
+ },
+
+ changeOrientation: function(){
+ $('wikibody').className = $('prefOrientation').getValue();
+ },
+ replaceMoreBox: function(){
+ var more = $('morebutton'),
+ popup = new Element('ul').inject(more),
+ hover = popup.effect('opacity', {wait:false}).set(0),
+ select = $('actionsMore'),
+ separator = '';
+
+ $A(select.options).each(function(o){
+ if(o.value == "") return;
+ separator='separator';
+ new Element('a',{'class':o.className,'href':o.value})
+ .setHTML(o.text).inject(new Element('li').inject(popup));
+ });
+ $('moremenu').inject(new Element('li',{'class':separator}).inject(popup));
+
+ select.getParent().hide();
+ more.show()
+ .addEvent('mouseout',(function(){ hover.start(0) }).bind(this))
+ .addEvent('mouseover',(function(){ Wiki.locatemenu(more,popup); hover.start(0.9) }).bind(this));
+ },
+
+ locatemenu: function(base,el){
+ var win = {'x': window.getWidth(), 'y': window.getHeight()},
+ scroll = {'x': window.getScrollLeft(), 'y': window.getScrollTop()},
+ corner = base.getPosition(),
+ offset = {'x': base.offsetWidth-el.offsetWidth, 'y': base.offsetHeight },
+ popup = {'x': el.offsetWidth, 'y': el.offsetHeight},
+ prop = {'x': 'left', 'y': 'top'};
+
+ for (var z in prop){
+ var pos = corner[z] + offset[z]; /*top-left corner of base */
+ if ((pos + popup[z] - scroll[z]) > win[z]) pos = win[z] - popup[z] + scroll[z];
+ el.setStyle(prop[z], pos);
+ };
+ },
+
+ parseLocationHash: function(){
+ if(this.url && this.url == location.href ) return;
+ this.url = location.href;
+ var h = location.hash;
+ if( h=="" ) return;
+ h = h.replace(/^#/,'');
+
+ var el = $(h);
+
+ while( $type(el) == 'element' ){
+ if( el.hasClass('hidetab') ){
+ TabbedSection.clickTab.apply(el);
+ } else if( el.hasClass('tab') ){
+ /* accordion -- need to find accordion object */
+ } else if( el.hasClass('collapsebody') ){
+ /* collapsible box */
+ } else if(!el.visible() ){
+ //alert('not visible'+el.id);
+ //fixme need to find the correct toggler
+ //el.show(); //eg collapsedBoxes: fixme
+ }
+ el = el.getParent();
+ }
+
+ location = location.href; /* now jump to the #hash */
+ },
+
+ /* SubmitOnce: disable all buttons to avoid double submit */
+ submitOnce: function(form){
+ window.onbeforeunload = null; /* regular exit of this page -- see jspwiki-edit.js */
+ (function(){
+ $A(form.elements).each(function(e){
+ if( (/submit|button/i).test(e.type)) e.disabled = true;
+ });
+ }).delay(10);
+ return true;
+ },
+
+ submitUpload: function(form, progress){
+ $('progressbar').setStyle('visibility','visible');
+ this.progressbar =
+ Wiki.jsonrpc.periodical(1000, this, ["progressTracker.getProgress",[progress],function(result){
+ if(!result.code) $('progressbar').getFirst().setStyle('width',result+'%').setHTML(result+'%');
+ }]);
+
+ return Wiki.submitOnce(form);
+ },
+
+ JSONid : 10000,
+ jsonrpc: function(method, params, fn) {
+ new Ajax( Wiki.JsonUrl, {
+ postBody: Json.toString({"id":Wiki.JSONid++, "method":method, "params":params}),
+ method: 'post',
+ onComplete: function(result){
+ var r = Json.evaluate(result,true);
+ if(!r) return;
+ if(r.result) fn(r.result);
+ else if(r.error) fn(r.error);
+ }
+ }).request();
+ }
+}
+
+
+/** 110 WikiSlimbox
+ ** Inspired http://www.digitalia.be/software/slimbox by Christophe Bleys
+ ** Dirk Frederickx, Mar 2007
+ ** %%slimbox [...] %%
+ ** %%slimbox-img [some-image.jpg] %%
+ ** %%slimbox-ajax [some-page links] %%
+ **/
+var WikiSlimbox = {
+
+ onPageLoad: function(){
+ var i = 0,
+ lnk = new Element('a',{'class':'slimbox'}).setHTML('»');
+
+ $$('*[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']);
+ $ES(filter.join(','),slim).each(function(el){
+ var href = el.src||el.href;
+ var 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.getText()
+ }).injectBefore(el);
+ if(el.src) el.replaceWith(new Element('a',{
+ 'class':'attachment',
+ 'href':el.src
+ }).setHTML(el.alt||el.getText()));
+ });
+ });
+ if(i) Lightbox.init();
+ //new Asset.javascript(Wiki.TemplateDir+'scripts/slimbox.js');
+ }
+}
+
+/*
+ Slimbox v1.31 - The ultimate lightweight Lightbox clone
+ by Christophe Beyls (http://www.digitalia.be) - MIT-style license.
+ Inspired by the original Lightbox v2 by Lokesh Dhakar.
+
+ Updated by Dirk Frederickx to fit JSPWiki needs
+ - minimum size of image canvas DONE
+ - add maximum size of image w.r.t window size DONE
+ - CLOSE icon -> close x text iso icon DONE
+ - <<prev, next>> links added in visible part of screen DONE
+ - add size of picture to info window DONE
+ - spacebor, down arrow, enter : next image DONE
+ - up arrow : prev image DONE
+ - allow the same picture occuring several times DONE
+ - add support for external page links => slimbox_ex DONE
+*/
+var Lightbox = {
+
+ init: function(options){
+ this.options = $extend({
+ resizeDuration: 400,
+ resizeTransition: false, /*Fx.Transitions.sineInOut,*/
+ initialWidth: 250,
+ initialHeight: 250,
+ animateCaption: true,
+ errorMessage: "slimbox.error".localize()
+ }, options || {});
+
+ this.anchors=[];
+ $each(document.links, function(el){
+ if (el.rel && el.rel.test(/^lightbox/i)){
+ el.onclick = this.click.pass(el, this);
+ this.anchors.push(el);
+ }
+ }, this);
+ this.eventKeyDown = this.keyboardListener.bindAsEventListener(this);
+ this.eventPosition = this.position.bind(this);
+
+ /* Build float panel
+ <div id="lbOverlay"></div>
+ <div id="lbCenter">
+ <div id="lbImage">
+ <!-- img or iframe element is inserted here -->
+ </div>
+ </div>
+ <div id="lbBottomContainer">
+ <div id="lbBottom">
+ <div id="lbCaption">
+ <div id="lbNumber">
+ <a id="lbCloseLink"></a>
+ <div style="clear:both;"></div>
+ </div>
+ </div>
+ */
+ this.overlay = new Element('div', {'id': 'lbOverlay'}).inject(document.body);
+
+ this.center = new Element('div', {'id': 'lbCenter', 'styles': {'width': this.options.initialWidth, 'height': this.options.initialHeight, 'marginLeft': -(this.options.initialWidth/2), 'display': 'none'}}).inject(document.body);
+ new Element('a', {'id': 'lbCloseLink', 'href':'#', 'title':'slimbox.close.title'.localize()}).inject(this.center).onclick = this.overlay.onclick = this.close.bind(this);
+ this.image = new Element('div', {'id': 'lbImage'}).inject(this.center);
+
+ this.bottomContainer = new Element('div', {'id': 'lbBottomContainer', 'styles': {'display': 'none'}}).inject(document.body);
+ this.bottom = new Element('div', {'id': 'lbBottom'}).inject(this.bottomContainer);
+ //new Element('a', {'id': 'lbCloseLink', 'href': '#', 'title':'slimbox.close.title'.localize()}).setHTML('slimbox.close'.localize()).inject(this.bottom).onclick = this.overlay.onclick = this.close.bind(this);
+ this.caption = new Element('div', {'id': 'lbCaption'}).inject(this.bottom);
+
+ var info = new Element('div').inject(this.bottom);
+ this.prevLink = new Element('a', {'id': 'lbPrevLink', 'href': '#', 'styles': {'display': 'none'}}).setHTML("slimbox.previous".localize()).inject(info);
+ this.number = new Element('span', {'id': 'lbNumber'}).inject(info);
+ this.nextLink = this.prevLink.clone().setProperties({'id': 'lbNextLink' }).setHTML("slimbox.next".localize()).inject(info);
+ this.prevLink.onclick = this.previous.bind(this);
+ this.nextLink.onclick = this.next.bind(this);
+
+ this.error = new Element('div').setProperty('id', 'lbError').setHTML(this.options.errorMessage);
+ new Element('div', {'styles': {'clear': 'both'}}).inject(this.bottom);
+
+ var nextEffect = this.nextEffect.bind(this);
+ this.fx = {
+ overlay: this.overlay.effect('opacity', {duration: 500}).hide(),
+ resize: this.center.effects($extend({duration: this.options.resizeDuration, onComplete: nextEffect}, this.options.resizeTransition ? {transition: this.options.resizeTransition} : {})),
+ image: this.image.effect('opacity', {duration: 500, onComplete: nextEffect}),
+ bottom: this.bottom.effect('margin-top', {duration: 400, onComplete: nextEffect})
+ };
+
+ this.fxs = new Fx.Elements([this.center, this.image], $extend({duration: this.options.resizeDuration, onComplete: nextEffect}, this.options.resizeTransition ? {transition: this.options.resizeTransition} : {}));
+
+ this.preloadPrev = new Image();
+ this.preloadNext = new Image();
+ },
+
+ click: function(link){
+ var rel = link.rel.split(' ');
+ if (rel[0].length == 8) return this.open([[url, title, rel[1]]], 0);
+
+ var imageNum=0, images = [];
+ this.anchors.each(function(el){
+ var elRel = el.rel.split(' ');
+ if (elRel[0]!=rel[0]) return;
+ if((el.href==link.href) && (el.title==link.title)) imageNum = images.length;
+ images.push([el.href, el.title, elRel[1]]);
+ });
+ return this.open(images, imageNum);
+ },
+
+ open: function(images, imageNum){
+ this.images = images;
+ this.position();
+ this.setup(true);
+ this.top = window.getScrollTop() + (window.getHeight() / 15);
+ this.center.setStyles({top: this.top, display: ''});
+ this.fx.overlay.start(0.7);
+ return this.changeImage(imageNum);
+ },
+
+ position: function(){
+ this.overlay.setStyles({top: window.getScrollTop(), height: window.getHeight()});
+ },
+
+ setup: function(open){
+ var elements = $A(document.getElementsByTagName('object'));
+ elements.extend(document.getElementsByTagName(window.ie ? 'select' : 'embed'));
+ elements.each(function(el){
+ if (open) el.lbBackupStyle = el.style.visibility;
+ el.style.visibility = open ? 'hidden' : el.lbBackupStyle;
+ });
+ var fn = open ? 'addEvent' : 'removeEvent';
+ window[fn]('scroll', this.eventPosition)[fn]('resize', this.eventPosition);
+ document[fn]('keydown', this.eventKeyDown);
+ this.step = 0;
+ },
+
+ keyboardListener: function(event){
+ switch (event.keyCode){
+ case 27: case 88: case 67: this.close(); break;
+ case 37: case 38: case 80: this.previous(); break;
+ case 13: case 32: case 39: case 40: case 78: this.next(); break;
+ default: return;
+ }
+ new Event(event).stop();
+ },
+
+ previous: function(){
+ return this.changeImage(this.activeImage-1);
+ },
+
+ next: function(){
+ return this.changeImage(this.activeImage+1);
+ },
+
+ changeImage: function(imageNum){
+ if (this.step || (imageNum < 0) || (imageNum >= this.images.length)) return false;
+ this.step = 1;
+ this.activeImage = imageNum;
+
+ this.center.style.backgroundColor = '';
+ this.bottomContainer.style.display = this.prevLink.style.display = this.nextLink.style.display = 'none';
+ this.fx.image.hide();
+ this.center.className = 'lbLoading';
+
+ this.preload = new Image();
+ this.image.empty().setStyle('overflow','hidden');
+ if( this.images[imageNum][2] == 'img' ){
+ this.preload.onload = this.nextEffect.bind(this);
+ this.preload.src = this.images[imageNum][0];
+ } else {
+ this.iframeId = "lbFrame_"+new Date().getTime(); // Safari would not update iframe content that has static id.
+ this.so = new Element('iframe').setProperties({
+ id: this.iframeId,
+// width: this.contentsWidth,
+// height: this.contentsHeight,
+ frameBorder:0,
+ scrolling:'auto',
+ src:this.images[imageNum][0]
+ }).inject(this.image);
+ this.nextEffect(); //asynchronous loading?
+
+ }
+ return false;
+ },
+
+ ajaxFailure: function (){
+ this.ajaxFailed = true;
+ this.image.setHTML('').adopt(this.error.clone());
+ this.nextEffect();
+ },
+
+ nextEffect: function(){
+ switch (this.step++){
+ case 1:
+ this.center.className = '';
+ this.caption.empty().adopt(new Element('a', {
+ 'href':this.images[this.activeImage][0],
+ 'title':"slimbox.directLink".localize()
+ }).setHTML(this.images[this.activeImage][1] || ''));
+
+ var type = (this.images[this.activeImage][2]=='img') ? "slimbox.info" : "slimbox.remoteRequest";
+ this.number.setHTML((this.images.length == 1) ? '' : type.localize(this.activeImage+1, this.images.length));
+ this.image.style.backgroundImage = 'none';
+
+ var w = Math.max(this.options.initialWidth,this.preload.width),
+ h = Math.max(this.options.initialHeight,this.preload.height),
+ ww = Window.getWidth()-10,
+ wh = Window.getHeight()-120;
+ if(this.images[this.activeImage][2]!='img' &&!this.ajaxFailed){ w = 6000; h = 3000; }
+ if(w > ww) { h = Math.round(h * ww/w); w = ww; }
+ if(h > wh) { w = Math.round(w * wh/h); h = wh; }
+
+ this.image.style.width = this.bottom.style.width = w+'px';
+ this.image.style.height = /*this.prevLink.style.height = this.nextLink.style.height = */ h+'px';
+
+ if( this.images[this.activeImage][2]=='img') {
+ this.image.style.backgroundImage = 'url('+this.images[this.activeImage][0]+')';
+
+ if (this.activeImage) this.preloadPrev.src = this.images[this.activeImage-1][0];
+ if (this.activeImage != (this.images.length - 1)) this.preloadNext.src = this.images[this.activeImage+1][0];
+
+ this.number.setHTML(this.number.innerHTML+' ['+this.preload.width+'×'+this.preload.height+']');
+ } else {
+ this.so.style.width=w+'px';
+ this.so.style.height=h+'px';
+ }
+
+ if (this.options.animateCaption) this.bottomContainer.setStyles({height: '0px', display: ''});
+
+ this.fxs.start({
+ '0': { height: [this.image.offsetHeight], width: [this.image.offsetWidth], marginLeft: [-this.image.offsetWidth/2] },
+ '1': { opacity: [1] }
+ });
+
+ break;
+ case 2:
+ //this.center.style.backgroundColor = '#000';
+ this.image.setStyle('overflow','auto');
+ this.bottomContainer.setStyles({ top: (this.top + this.center.clientHeight)+'px', marginLeft: this.center.style.marginLeft });
+ if (this.options.animateCaption){
+ this.fx.bottom.set(-this.bottom.offsetHeight);
+ this.bottomContainer.style.height = '';
+ this.fx.bottom.start(0);
+ break;
+ }
+ this.bottomContainer.style.height = '';
+ case 3:
+ if (this.activeImage) this.prevLink.style.display = '';
+ if (this.activeImage != (this.images.length - 1)) this.nextLink.style.display = '';
+ this.step = 0;
+ }
+ },
+
+ close: function(){
+ if (this.step < 0) return;
+ this.step = -1;
+ if (this.preload){
+ this.preload.onload = Class.empty;
+ this.preload = null;
+ }
+ for (var f in this.fx) this.fx[f].stop();
+ this.center.style.display = this.bottomContainer.style.display = 'none';
+ this.fx.overlay.chain(this.setup.pass(false, this)).start(0);
+ return false;
+ }
+};
+
+
+/** 114 Reflection
+ ** Inspired by Reflection.js at http://cow.neondragon.net/stuff/reflection/
+ ** Freely distributable under MIT-style license.
+ ** Adapted for JSPWiki/BrushedTemplate, D.Frederickx, Sep 06
+ ** Use:
+ ** %%reflection-height-opacity [some-image.jpg] %%
+ **/
+var WikiReflection = {
+
+ onPageLoad: function(){
+ $$('*[class^=reflection]').each( function(w){
+ var parms = w.className.split('-');
+ $ES('img',w).each(function(img){
+ Reflection.add(img, parms[1], parms[2]);
+ });
+ });
+ }
+}
+/* FIXME : add delayed loading of reflection library */
+var Reflection = {
+
+ options: { height: 0.33, opacity: 0.5 },
+
+ add: function(img, height, opacity) {
+ //TODO Reflection.remove(image); --is this still needed?
+ height = (height ) ? height/100 : this.options.height;
+ opacity = (opacity) ? opacity/100: this.options.opacity;
+
+ var div = new Element('div').injectAfter(img).adopt(img),
+ imgW = img.width,
+ imgH = img.height,
+ rH = Math.floor(imgH * height); //reflection height
+
+ div.className = img.className.replace(/\breflection\b/, "");
+ div.style.cssText = img.backupStyle = img.style.cssText;
+ //div.setStyles({'width':img.width, 'height':imgH +rH, "maxWidth": imgW });
+ div.setStyles({'width':img.width, 'height':imgH +rH });
+ img.style.cssText = 'vertical-align: bottom';
+ //img.className = 'inline reflected'; //FIXME: is this still needed ??
+
+ if( window.ie ){
+ new Element('img', {'src': img.src, 'styles': {
+ 'width': imgW,
+ 'marginBottom': "-" + (imgH - rH) + 'px',
+ 'filter': 'flipv progid:DXImageTransform.Microsoft.Alpha(opacity='+(opacity*100)+', style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy='+(height*100)+')'
+ }}).inject(div);
+ } else {
+ var r = new Element('canvas', {'width':imgW, 'height':rH, 'styles': {'width':imgW, 'height': rH}}).inject(div);
+ if( !r.getContext ) return;
+
+ var ctx = r.getContext("2d");
+ ctx.save();
+ ctx.translate(0, imgH-1);
+ ctx.scale(1, -1);
+ ctx.drawImage(img, 0, 0, imgW, imgH);
+ ctx.restore();
+ ctx.globalCompositeOperation = "destination-out";
+
+ var g = ctx.createLinearGradient(0, 0, 0, rH);
+ g.addColorStop( 0, "rgba(255, 255, 255, " + (1 - opacity) + ")" );
+ g.addColorStop( 1, "rgba(255, 255, 255, 1.0)" );
+ ctx.fillStyle = g;
+ ctx.rect( 0, 0, imgW, rH );
+ ctx.fill();
+ }
+ }
+}
+
+/** 120 brushed quick links **/
+var QuickLinks = {
+
+ onPageLoad: function(){
+ if( $("previewcontent") || !Wiki.PrefShowQuickLinks ) return;
+
+ var sections = $$('#pagecontent *[id^=section]');
+ if(sections.length==0) return;
+
+ var ql,qlPrev,qlEdit,qlNext;
+ function qql(clazz,title,text,href){
+ var a = new Element('a',{'title':title.localize()}).setHTML(text);
+ if(href) a.setProperty('href',href);
+ ql.adopt(new Element('span',{'class':clazz}).adopt(a));
+ return(a);
+ }
+ ql = new Element('div',{'class':'quicklinks'});
+ qql('quick2Top','quick.top','«','#wikibody');
+ qlPrev = qql('quick2Prev','quick.previous','‹');
+
+ if(Wiki.PermissioneEdit) qlEdit = qql('quick2Edit','quick.edit','•');
+
+ qlNext = qql('quick2Next','quick.next','›');
+ qql('quick2Bottom','quick.bottom','»','#footer');
+
+ var last = sections.length-1;
+ sections.each(function(s,i){
+ qlNext.setProperty('href',(i==last) ? '#footer' : '#'+sections[i+1].id);
+ qlPrev.setProperty('href',(i==0) ? '#wikibody' : '#'+sections[i-1].id);
+ if(qlEdit) qlEdit.setProperty('href','Edit.jsp?page='+Wiki.PageName+'§ion='+(i+1));
+ s.adopt(ql.clone());
+ });
+ }
+}
+
+/** Class: Tabbed Section (130)
+ Creates tabs, based on some css-class information
+ Use in jspwiki: %%tabbedSection %%tab-FirstTab .. %% %%
+
+ Following markup:
+ <div class="tabbedSection">
+ <div class="tab-FirstTab">..<div>
+ <div class="tab-SecondTab">..<div>
+ </div>
+
+ is changed into
+ <div class="tabmenu"><span><a activetab>..</a></span>..</div>
+ <div class="tabbedSection tabs">
+ <div class="tab-firstTab ">
+ <div class="tab-SecondTab hidetab">
+ </div>
+ **/
+var TabbedSection = {
+
+ onPageLoad: function(){
+
+ // charge existing tabmenu's with click handlers
+ $$('.tabmenu a').each(function(el){
+ if(el.href) return;
+ var tab = $(el.id.substr(5)); //drop 'menu-' prefix
+ el.addEvent('click', this.clickTab.bind(tab) );
+ },this);
+
+ // convert tabbedSections into tabmenu's with click handlers
+ $$('.tabbedSection').each( function(tt){
+ tt.addClass('tabs'); //css styling is on tabs
+ var tabmenu = new Element('div',{'class':'tabmenu'}).injectBefore(tt);
+
+ tt.getChildren().each(function(tab,i) {
+ if( !tab.className.test('^tab-') ) return;
+
+ if( !tab.id || (tab.id=="") ) tab.id = tab.className;
+ var title = tab.className.substr(4).deCamelize(); //drop 'tab-' prefix
+
+ (i==0) ? tab.removeClass('hidetab') : tab.addClass('hidetab');
+
+ new Element('div',{'styles':{'clear':'both'}}).inject(tab);
+
+ var menu = new Element('a', {
+ 'id':'menu-'+tab.id,
+ 'events':{ 'click': this.clickTab.bind(tab) }
+ }).appendText(title).inject(tabmenu);
+ if( i==0 ) menu.addClass('activetab');
+ },this);
+ }, this);
+ },
+
+ clickTab: function(){
+ var menu = $('menu-'+this.id);
+ this.getParent().getChildren().some( function(el){
+ if(el.id){
+ var m = $('menu-'+el.id);
+ if( m && m.hasClass('activetab') ) {
+ if( el.id != this.id ) {
+ m.removeClass('activetab');
+ menu.addClass('activetab');
+ el.addClass('hidetab');
+ this.removeClass('hidetab'); //.show();
+ }
+ return true;
+ }
+ }
+ return false;
+ },this);
+ }
+
+}
+
+/** 132 Accordion for Tabs, Accordeons, CollapseBoxes
+ **
+ ** Following markup:
+ ** <div class="accordion">
+ ** <div class="tab-FirstTab">...<div>
+ ** <div class="tab-SecondTab">...<div>
+ ** </div>
+ **
+ ** is changed into
+ ** <div class="accordion">
+ ** <div class="toggle active">First Tab</div>
+ ** <div class="tab-FirstTab tab active">...</div>
+ ** <div class="toggle">Second Tab</div>
+ ** <div class="tab-SecondTab">...</div>
+ ** </div>
+ **/
+var WikiAccordion = {
+
+ onPageLoad: function(){
+ $$('.accordion, .tabbedAccordion').each( function(tt){
+
+ var toggles=[], contents=[], togglemenu=false;
+ if(tt.hasClass('tabbedAccordion')) togglemenu = new Element('div',{'class':'togglemenu'}).injectBefore(tt);
+
+ tt.getChildren().each(function(tab) {
+ if( !tab.className.test('^tab-') ) return;
+
+ //FIXME use class to make tabs visible during printing
+ //(i==0) ? tab.removeClass('hidetab'): tab.addClass('hidetab');
+
+ var title = tab.className.substr(4).deCamelize();
+ if(togglemenu) {
+ toggles.push(new Element('div',{'class':'toggle'}).inject(togglemenu).appendText(title));
+ } else {
+ toggles.push(new Element('div',{'class':'toggle'}).injectBefore(tab).appendText(title));
+ }
+ contents.push(tab.addClass('tab'));
+ });
+ new Accordion(toggles, contents, {
+ alwaysHide: !togglemenu,
+ onComplete: function(){
+ var el = $(this.elements[this.previous]);
+ if (el.offsetHeight > 0) el.setStyle('height', 'auto');
+ },
+ onActive: function(toggle,content){
+ toggle.addClass('active'); content.addClass('active');
+ },
+ onBackground: function(toggle,content){
+ content.setStyle('height', content['offsetHeight']);
+ toggle.removeClass('active'); content.removeClass('active');
+ }
+ });
+ });
+ }
+}
+
+
+/* 140 SearchBox
+ * FIXME: remember 10 most recent search topics (cookie based)
+ * Extended with quick links for view, edit and clone (ref. idea of Ron Howard - Nov 05)
+ * Refactored for mootools, April 07
+ */
+var SearchBox = {
+
+ onPageLoad: function(){
+ this.onPageLoadQuickSearch();
+ this.onPageLoadFullSearch();
+ },
+
+ onPageLoadQuickSearch : function(){
+ var q = $('query'); if( !q ) return;
+ this.query = q;
+ q.observe(this.ajaxQuickSearch.bind(this) );
+
+ this.hover = $('searchboxMenu').setProperty('visibility','visible')
+ .effect('opacity', {wait:false}).set(0);
+
+ $(q.form).addEvent('submit',this.submit.bind(this))
+ //FIXME .addEvent('blur',function(){ this.hasfocus=false; this.hover.start(0) }.bind(this))
+ //FIXME .addEvent('focus',function(){ this.hasfocus=true; this.hover.start(0.9) }.bind(this))
+ .addEvent('mouseout',function(){ this.hover.start(0) }.bind(this))
+ .addEvent('mouseover',function(){ Wiki.locatemenu(this.query, $('searchboxMenu') ); this.hover.start(0.9) }.bind(this));
+
+ /* use advanced search-input on safari - experimental */
+ if(window.xwebkit){
+ q.setProperties({type:"search",autosave:q.form.action,results:"9",placeholder:q.defaultValue});
+ } else {
+ $('recentClear').addEvent('click', this.clear.bind(this));
+
+ this.recent = Wiki.prefs.get('RecentSearch'); if(!this.recent) return;
+
+ var ul = new Element('ul',{'id':'recentItems'}).inject($('recentSearches').show());
+ this.recent.each(function(el){
+ new Element('a',{
+ 'href':'#',
+ 'events': {'click':function(){ q.value = el; q.form.submit(); }}
+ }).setHTML(el).inject( new Element('li').inject(ul) );
+ });
+ }
+ },
+
+ onPageLoadFullSearch : function(){
+ var q2 = $("query2"); if( !q2 ) return;
+ this.query2 = q2;
+
+ var changescope = function(){
+ var qq = this.query2.value.replace(/^(?:author:|name:|contents:|attachment:)/,'');
+ this.query2.value = $('scope').getValue() + qq;
+ this.runfullsearch();
+ }.bind(this);
+
+ q2.observe( this.runfullsearch.bind(this) );
+
+ $('scope').addEvent('change', changescope);
+ $('details').addEvent('click', this.runfullsearch.bind(this));
+ },
+
+ runfullsearch : function(){
+ var q2 = this.query2.value;
+ if( !q2 || (q2.trim()=='')) {
+ $('searchResult2').empty();
+ return;
+ }
+ $('spin').show();
+
+ var scope = $('scope'),
+ match= q2.match(/^(?:author:|name:|contents:|attachment:)/) ||"";
+ $each(scope.options, function(option){
+ if (option.value == match) option.selected = true;
+ });
+
+ new Ajax(Wiki.TemplateDir+'AJAXSearch.jsp', {
+ postBody: $('searchform2').toQueryString(),
+ update: 'searchResult2',
+ method: 'post',
+ onComplete: function() { $('spin').hide(); GraphBar.onPageLoad(); Wiki.prefs.set('PrevQuery', q2); }
+ }).request();
+ },
+
+ submit: function(){
+ var v = this.query.value;
+ if( v == this.query.defaultValue) this.query.value = '';
+ if( !this.recent ) this.recent=[];
+ if( !this.recent.test(v) ){
+ if(this.recent.length > 9) this.recent.pop();
+ this.recent.unshift(v);
+ Wiki.prefs.set('RecentSearch', this.recent);
+ }
+ },
+
+ clear: function(){
+ this.recent = [];
+ Wiki.prefs.remove('RecentSearch');
+ $('recentSearches','recentClear').hide();
+ },
+
+ ajaxQuickSearch: function(){
+ var qv = this.query.value ;
+ if( (qv==null) || (qv.trim()=="") || (qv==this.query.defaultValue) ) {
+ $('searchOutput').empty();
+ return;
+ }
+ $('searchTarget').setHTML('('+qv+') :');
+ $('searchSpin').show();
+
+ 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.getUrl(el.map.page) }).setHTML(el.map.page),
+ new Element('span',{'class':'small'}).setHTML(" ("+el.map.score+")")
+ ).inject(frag);
+ });
+ $('searchOutput').empty().adopt(frag);
+ Wiki.locatemenu( $('query'), $('searchboxMenu') );
+ });
+ } ,
+
+
+ /* navigate to url, after smart pagename handling */
+ navigate: function(url, promptText, clone, search){
+ var p = Wiki.PageName, s = this.query.value;
+ if(s == this.query.defaultValue) s = '';
+
+ if(s == ''){
+ s = prompt(promptText, (clone) ? p+'sbox.clone.suffix'.localize() : p);
+ if( !s || (s == '') ) return false;
+ }
+ //if(!search) s = s.replace(/[^A-Za-z0-9._\/]/g, ''); //valid pagename FIXME
+ if( clone && (s != p) ) s += '&clone=' + p;
+
+ if(s == '') return false; //dont exec the click
+ location.href = url.replace('__PAGEHERE__', s);
+ }
+}
+
+
+/**
+ ** 150 GraphBar Object: also used on the findpage
+ ** %%graphBars ... %%
+ ** convert numbers inside %%gBar ... %% tags to graphic horizontal bars
+ ** no img needed.
+ ** supported parameters: bar-color and bar-maxsize
+ ** e.g. %%graphBars-e0e0e0 ... %% use color #e0e0e0, default size 120
+ ** e.g. %%graphBars-blue-red ... %% blend colors from blue to red
+ ** e.g. %%graphBars-red-40 ... %% use color red, maxsize 40 chars
+ ** e.g. %%graphBars-vertical ... %% vertical bars
+ ** e.g. %%graphBars-progress ... %% progress bars in 2 colors
+ ** e.g. %%graphBars-gauge ... %% gauge bars in gradient colors
+ **/
+
+/* minimal variant of the Color class, inspired by mootools */
+var Color = new Class({
+
+ _HTMLColors: {
+ black :"000000", green :"008000", silver :"c0c0c0", lime :"00ff00",
+ gray :"808080", olive :"808000", white :"ffffff", yellow:"ffff00",
+ maroon :"800000", navy :"000080", red :"ff0000", blue :"0000ff",
+ purple :"800080", teal :"008080", fuchsia:"ff00ff", aqua :"00ffff"
+ },
+
+ initialize: function(color, type){
+ type = type || (color.push ? 'rgb' : 'hex');
+ if(this._HTMLColors[color]) color = this._HTMLColors[color];
+ var rgb = (type=='rgb') ? color : color.toString().hexToRgb(true);
+ if(!rgb) return false;
+ rgb.hex = rgb.rgbToHex();
+ return $extend(rgb, Color.prototype);
+ },
+
+ mix: function(){
+ var colors = $A(arguments),
+ rgb = this.copy(),
+ alpha = (($type(colors[colors.length - 1]) == 'number') ? colors.pop() : 50)/100,
+ alphaI = 1-alpha;
+
+ colors.each(function(color){
+ color = new Color(color);
+ for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] * alphaI) + (color[i] * alpha));
+ });
+ return new Color(rgb, 'rgb');
+ },
+
+ invert: function(){
+ return new Color(this.map(function(value){
+ return 255 - value;
+ }));
+ }
+
+});
+
+var GraphBar =
+{
+ onPageLoad : function(){
+ $$('*[class^=graphBars]').each( function(g){
+ var lbound = 20, //max - lowerbound size of bar
+ ubound = 320, //min - upperbound size of bar
+ vwidth = 20, //vertical bar width
+ color1 = null, // bar color
+ color2 = null, // 2nd bar color used depending on bar-type
+ isGauge = false, // gauge bar
+ isProgress = false, // progress bar
+ isHorizontal = true,// horizontal or vertical orientation
+ parms = g.className.substr(9).split('-'),
+ barName = parms.shift(); //first param is optional barName
+
+ parms.each(function(p){
+ p = p.toLowerCase();
+ if(p == "vertical") { isHorizontal = false; }
+ else if(p == "progress") { isProgress = true; }
+ else if(p == "gauge") { isGauge = true; }
+ else if(p.indexOf("min") == 0) { lbound = p.substr(3).toInt(); }
+ else if(p.indexOf("max") == 0) { ubound = p.substr(3).toInt(); }
+ else if(p != "") {
+ p = new Color(p,'hex'); if(!p.hex) return;
+ if(!color1) color1 = p;
+ else if(!color2) color2 = p;
+ }
+ });
+ if( !color2 && color1) color2 = (isGauge || isProgress) ? color1.invert() : color1;
+
+ if( lbound > ubound ) { var m = ubound; ubound=lbound; ubound=m; }
+ var size = ubound-lbound;
+
+ var bars = $ES('.gBar'+barName, g); //collect all gBar elements
+ if( (bars.length==0) && barName && (barName!="")){ // check table data
+ bars = this.getTableValues(g, barName);
+ }
+ if( !bars ) return;
+
+ var barData = this.parseBarData( bars, lbound, size ),
+ border = (isHorizontal ? 'borderLeft' : 'borderBottom');
+
+ bars.each(function(b,j){
+ var bar1 = $H().set(border+'Width',barData[j]),
+ bar2 = $H(), // 2nd bar only valid ico 'progress'
+ barEL = new Element('span',{'class':'graphBar'}),
+ pb = b.getParent(); // parent of gBar element
+
+ if(isHorizontal){
+ barEL.setHTML('x');
+ if(isProgress){
+ bar2.extend(bar1.obj);
+ bar1.set(border+'Width',ubound-barData[j]).set('marginLeft','-1ex');
+ }
+ } else { // isVertical
+ if(pb.getTag()=='td') { pb = new Element('div').injectWrapper(pb); }
+
+ pb.setStyles({'height':ubound+b.getStyle('lineHeight').toInt(), 'position':'relative'});
+ b.setStyle('position', 'relative'); //needed for inserted spans ;-)) hehe
+ if( !isProgress ) { b.setStyle('top', (ubound-barData[j])); }
+
+ bar1.extend({'position':'absolute', 'width':vwidth, 'bottom':'0'});
+ if(isProgress){
+ bar2.extend(bar1.obj).set(border+'Width', ubound);
+ }
+ }
+ if(isProgress){
+ if(color1){ bar1.set('borderColor', color1.hex); }
+ if(color2){
+ bar2.set('borderColor', color2.hex);
+ } else {
+ bar1.set('borderColor', 'transparent');
+ }
+ } else if(color1){
+ var percent = isGauge ? (barData[j]-lbound)/size : j/(bars.length-1);
+ bar1.set('borderColor', color1.mix(color2, 100 * percent).hex);
+ }
+
+ if(bar2.length > 0){ barEL.clone().setStyles(bar2.obj).injectBefore(b); };
+ if(bar1.length > 0){ barEL.setStyles(bar1.obj).injectBefore(b); };
+ },this);
+
+ },this);
+ },
+
+ // parse bar data types and scale according to lbound and size
+ parseBarData: function(nodes, lbound, size){
+ var barData=[],
+ maxValue=Number.MIN_VALUE,
+ minValue=Number.MAX_VALUE,
+ num=date=true;
+
+ nodes.each(function(n,i){
+ var s = n.getText();
+ barData.push(s);
+ if(num) num = !isNaN(parseFloat( s.match(Number.REparsefloat) ) );
+ if(date) date = !isNaN(Date.parse(s));
+ });
+ barData = barData.map(function(b){
+ if(date) { b = new Date(Date.parse(b) ).valueOf(); }
+ else if(num) { b = parseFloat( b.match(Number.REparsefloat) ); }
+
+ maxValue = Math.max(maxValue, b);
+ minValue = Math.min(minValue, b);
+ return b;
+ });
+
+ if(maxValue==minValue) maxValue=minValue+1; /* avoid div by 0 */
+ size = size/(maxValue-minValue);
+ return barData.map(function(b){
+ return ( (size*(b-minValue)) + lbound).toInt();
+ });
+ },
+
+ /* Fetch set of gBar values from a table
+ * Check first-row to match field-name: return array with col values
+ * Check first-column to match field-name: return array with row values
+ * insert SPANs as place-holder of the missing gBars
+ */
+ getTableValues: function(node, fieldName){
+ var table = $E('table', node); if(!table) return false;
+ var tlen = table.rows.length;
+
+ if( tlen > 1 ){ /* check for COLUMN based table */
+ var r = table.rows[0];
+ for( var h=0; h < r.cells.length; h++ ){
+ if( $getText( r.cells[h] ).trim() == fieldName ){
+ var result = [];
+ for( var i=1; i< tlen; i++)
+ result.push( new Element('span').injectWrapper(table.rows[i].cells[h]) );
+ return result;
+ }
+ }
+ }
+ for( var h=0; h < tlen; h++ ){ /* check for ROW based table */
+ var r = table.rows[h];
+ if( $getText( r.cells[0] ).trim() == fieldName ){
+ var result = [];
+ for( var i=1; i< r.cells.length; i++)
+ result.push( new Element('span').injectWrapper(r.cells[i]) );
+ return result;
+ }
+ }
+ return false;
+ }
+}
+
+/** 200 Collapsible list and boxes
+ ** See also David Lindquist <first name><at><last name><dot><net>
+ ** See: http://www.gazingus.org/html/DOM-Scripted_Lists_Revisited.html
+ **
+ ** Add support for collabsable boxes, Nov 05, D.Frederickx
+ ** Refactored on mootools, including effects, May 07, D.Frederickx
+ **/
+var Collapsible =
+{
+ pims : [], // all me cookies
+
+ onPageLoad: function(){
+ this.bullet = new Element('div',{'class':'collapseBullet'}).setHTML('•');
+ this.initialise( "favorites", "JSPWikiCollapseFavorites" );
+ this.initialise( "pagecontent", "JSPWikiCollapse" + Wiki.PageName );
+ this.initialise( "previewcontent", "JSPWikiCollapse" + Wiki.PageName );
+ this.initialise( "info" );
+ },
+
+ initialise: function( page, cookie){
+ page = $(page); if(!page) return;
+
+ this.pims.push({
+ 'name':cookie,
+ 'value':'',
+ 'initial': (cookie ? Cookie.get(cookie) : "")
+ });
+ $ES('.collapse', page).each(function(el){
+ if( $E('.collapseBullet',el) ) return; /* no nesting */
+ this.collapseNode(el);
+ }, this);
+ $ES('.collapsebox,.collapsebox-closed', page).each(function(el){
+ this.collapseBox(el);
+ }, this);
+ },
+
+ collapseBox: function(el){
+ var title = el.getFirst(); if( !title ) return;
+ var body = new Element('div', {'class':'collapsebody'}),
+ bullet = this.bullet.clone(),
+ isclosed = el.hasClass('collapsebox-closed');
+
+ while(title.nextSibling) body.appendChild(title.nextSibling); // wrap other siblings
+ el.appendChild(body);
+
+ if(isclosed) el.removeClass('collapsebox-closed').addClass('collapsebox');
+ bullet.injectTop( title.addClass('collapsetitle') );
+ this.newBullet(bullet, body, !isclosed, title );
+ },
+
+ // Modifies the list such that sublists can be hidden/shown by clicking the listitem bullet
+ // The listitem bullet is a node inserted into the DOM tree as the first child of the
+ // listitem containing the sublist.
+ collapseNode: function(node){
+ $ES('li',node).each(function(li){
+ var ulol = $E('ul',li) || $E('ol',li);
+
+ //dont insert bullet when LI is 'empty': no text or no non-ul/ol tags
+ var emptyLI = true;
+ for( var n = li.firstChild; n ; n = n.nextSibling ) {
+ if((n.nodeType == 3 ) && ( n.nodeValue.trim() == "" ) ) continue; //keep searching
+ if((n.nodeName == "UL") || (n.nodeName == "OL")) break; //seems like an empty li
+ emptyLI = false;
+ break;
+ }
+ if( emptyLI ) return;
+
+ new Element('div',{'class':'collapsebody'}).injectWrapper(li);
+ var bullet = this.bullet.clone().injectTop(li);
+ if(ulol) this.newBullet(bullet, ulol, (ulol.getTag()=='ul'));
+ },this);
+ },
+
+ newBullet: function(bullet, body, isopen, clicktarget){
+ var ck = this.pims.getLast(); //read cookie
+ isopen = this.parseCookie(isopen);
+ if(!clicktarget) clicktarget = bullet;
+
+ var bodyfx = body.setStyle('overflow','hidden')
+ .effect('height', {
+ wait:false,
+ onStart:this.renderBullet.bind(bullet),
+ onComplete:function(){ if(bullet.hasClass('collapseOpen')) body.setStyle('height','auto'); }
+ });
+
+ bullet.className = (isopen ? 'collapseClose' : 'collapseOpen'); //ready for rendering
+ clicktarget.addEvent('click', this.clickBullet.bind(bullet, [ck, ck.value.length-1, bodyfx]))
+ .addEvent('mouseenter', function(){ clicktarget.addClass('collapseHover')} )
+ .addEvent('mouseleave', function(){ clicktarget.removeClass('collapseHover')} );
+
+ bodyfx.fireEvent('onStart');
+ if(!isopen) bodyfx.set(0); //.set( isopen ? body.scrollHeight : 0 );
+ },
+
+ renderBullet: function(){
+ if(this.hasClass('collapseClose')){
+ this.setProperties({'title':'collapse'.localize(), 'class':'collapseOpen'}).setHTML('-'); /* » */
+ } else {
+ this.setProperties({'title':'expand'.localize(), 'class':'collapseClose'}).setHTML('+'); /* « */
+ }
+ },
+
+ clickBullet: function( ck, bulletidx, bodyfx){
+ var collapse = this.hasClass('collapseOpen'),
+ bodyHeight = bodyfx.element.scrollHeight;
+
+ if(collapse) bodyfx.start(bodyHeight, 0); else bodyfx.start(bodyHeight);
+
+ ck.value = ck.value.substring(0,bulletidx) + (collapse ? 'c' : 'o') + ck.value.substring(bulletidx+1) ;
+ if(ck.name) Cookie.set(ck.name, ck.value, {path:Wiki.BasePath, duration:20});
+ },
+
+ // parse initial cookie versus actual document
+ // returns true if collapse status is open
+ parseCookie: function( isopen ){
+ var ck = this.pims.getLast(),
+ cursor = ck.value.length,
+ token = (isopen ? 'o' : 'c');
+
+ if(ck.initial && (ck.initial.length > cursor)){
+ var cookieToken = ck.initial.charAt( cursor );
+
+ if( ( isopen && (cookieToken == 'c') )
+ || ( !isopen && (cookieToken == 'o') ) ) token = cookieToken ;
+
+ if(token != cookieToken) ck.initial = null; //mismatch with initial cookie
+ }
+ ck.value += token; //append and save currentcookie
+
+ return(token == 'o');
+ }
+}
+
+
+/** 220 RoundedCorners --experimental
+ ** based on Nifty corners by Allesandro Fulciniti
+ ** www.pro.html.it
+ ** Refactored for JSPWiki
+ **
+ ** JSPWiki syntax:
+ **
+ ** %%roundedCorners-<corners>-<color>-<borderColor>
+ ** %%
+ **
+ ** roundedCorners-yyyy-ffc5ff-c0c0c0
+ **
+ ** corners: "yyyy" where first y: top-left, 2nd y: top-right,
+ ** 3rd y: bottom-left; 4th y: bottom-right
+ ** value can be: "y": Normal rounded corner (lowercase y)
+ ** "s": Small rounded corner (lowercase s)
+ ** "n": Normal square corner
+ **
+ **/
+var RoundedCorners =
+{
+ /** Definition of CORNER dimensions
+ ** Normal Normal+Border Small Small+Border
+ ** .....+++ .....BBB ..+++ ..BBB
+ ** ...+++++ ...BB+++ .++++ .B+++
+ ** ..++++++ ..B+++++ +++++ B++++
+ ** .+++++++ .B++++++
+ ** .+++++++ .B++++++
+ ** ++++++++ B+++++++
+ **
+ ** legend: . background, B border, + forground color
+ **/
+ NormalTop :
+ [ { margin: "5px", height: "1px", borderSide: "0", borderTop: "1px" }
+ , { margin: "3px", height: "1px", borderSide: "2px" }
+ , { margin: "2px", height: "1px", borderSide: "1px" }
+ , { margin: "1px", height: "2px", borderSide: "1px" }
+ ] ,
+ SmallTop :
+ [ { margin: "2px", height: "1px", borderSide: "0", borderTop: "1px" }
+ , { margin: "1px", height: "1px", borderSide: "1px" }
+ ] ,
+ //NormalBottom: see onPageLoad()
+ //SmallBottom: see onPageLoad()
+
+ /**
+ ** Usage:
+ ** RoundedCorners.register( "#header", ['yyyy', '00f000', '32cd32'] );
+ **/
+ registry: {},
+ register: function( selector, parameters )
+ {
+ this.registry[selector] = parameters;
+ return this;
+ },
+
+ onPageLoad: function()
+ {
+ /* make reverse copies for bottom definitions */
+ this.NormalBottom = this.NormalTop.slice(0).reverse();
+ this.SmallBottom = this.SmallTop.slice(0).reverse();
+
+ for( selector in this.registry ) // CHECK NEEDED
+ {
+ var n = $$(selector);
+ var parms = this.registry[selector];
+ this.exec( n, parms[0], parms[1], parms[2], parms[3] );
+ }
+
+ $$('#pagecontent *[class^=roundedCorners]').each(function(el){
+ var parms = el.className.split('-');
+ if( parms.length < 2 ) return;
+ this.exec( [el], parms[1], parms[2], parms[3], parms[4] );
+ },this);
+ },
+
+ exec: function( nodes, corners, color, borderColor, background )
+ {
+ corners = ( corners ? corners+"nnnn": "yyyy" );
+ color = new Color(color,'hex') || 'transparent';
+ if(borderColor) borderColor = new Color(borderColor);
+ if(background) background = new Color(background);
+
+ var c = corners.split('');
+ /* [0]=top-left; [1]=top-right; [2]=bottom-left; [3]=bottom-right; */
+
+ var nodeTop = null;
+ var nodeBottom = null;
+
+ if( c[0]+c[1] != "nn" ) //add top rounded corners
+ {
+ nodeTop = document.createElement("b") ;
+ nodeTop.className = "roundedCorners" ;
+
+ if( (c[0] == "y") || (c[1] == "y") )
+ {
+ this.addCorner( nodeTop, this.NormalTop, c[0], c[1], color, borderColor );
+ }
+ else if( (c[0] == "s") || (c[1] == "s") )
+ {
+ this.addCorner( nodeTop, this.SmallTop, c[0], c[1], color, borderColor );
+ }
+ }
+
+ if( c[2]+c[3] != "nn" ) //add bottom rounded corners
+ {
+ nodeBottom = document.createElement("b");
+ nodeBottom.className = "roundedCorners";
+
+ if( (c[2] == "y") || (c[3] == "y") )
+ {
+ this.addCorner( nodeBottom, this.NormalBottom, c[2], c[3], color, borderColor );
+ }
+ else if( (c[2] == "s") || (c[3] == "s") )
+ {
+ this.addCorner( nodeBottom, this.SmallBottom, c[2], c[3], color, borderColor );
+ }
+ }
+
+ if( (!nodeTop) && (!borderColor) && (!nodeBottom) ) return;
+
+ for( var i=0; i<nodes.length; i++)
+ {
+ if( !nodes[i] ) continue;
+ this.addBody( nodes[i], color, borderColor );
+ if( nodeTop ) nodes[i].insertBefore( nodeTop.cloneNode(true), nodes[i].firstChild );
+ if( nodeBottom ) nodes[i].appendChild( nodeBottom.cloneNode(true) );
+ }
+ },
+
+ addCorner: function( node, arr, left, right, color, borderColor )
+ {
+ for( var i=0; i< arr.length; i++ )
+ {
+ var n = document.createElement("div");
+ n.style.height = arr[i].height;
+ n.style.overflow = "hidden";
+ n.style.borderWidth = "0";
+ n.style.backgroundColor = color.hex;
+
+ if( borderColor )
+ {
+ n.style.borderColor = borderColor.hex;
+ n.style.borderStyle = "solid";
+ if(arr[i].borderTop)
+ {
+ n.style.borderTopWidth = arr[i].borderTop;
+ n.style.height = "0";
+ }
+ }
+
+ if( left != 'n' ) n.style.marginLeft = arr[i].margin;
+ if( right != 'n' ) n.style.marginRight = arr[i].margin;
+ if( borderColor )
+ {
+ n.style.borderLeftWidth = ( left == 'n' ) ? "1px": arr[i].borderSide;
+ n.style.borderRightWidth = ( right == 'n' ) ? "1px": arr[i].borderSide;
+ }
+ node.appendChild( n );
+ }
+ },
+
+ // move all children of the node inside a DIV and set color and bordercolor
+ addBody: function( node, color, borderColor)
+ {
+ if( node.passed ) return;
+
+ var container = new Element('div').injectWrapper(node);
+
+ container.style.padding = "0 4px";
+ container.style.backgroundColor = color.hex;
+ if( borderColor )
+ {
+ container.style.borderLeft = "1px solid " + borderColor.hex;
+ container.style.borderRight = "1px solid " + borderColor.hex;
+ }
+
+ node.passed=true;
+ }
+}
+
+
+/** 230 Sortable -- Sort tables **/
+//TODO cache table ok, cache datatype for each column
+var Sortable =
+{
+ onPageLoad: function(){
+ this.DefaultTitle = "sort.click".localize();
+ this.AscendingTitle = "sort.ascending".localize();
+ this.DescendingTitle = "sort.descending".localize();
+
+ $$('.sortable table').each(function(table){
+ if( table.rows.length < 2 ) return;
+
+ $A(table.rows[0].cells).each(function(th){
+ th=$(th);
+ if( th.getTag() != 'th' ) return;
+ th.addEvent('click', function(){ Sortable.sort(th); })
+ .addClass('sort')
+ .title=Sortable.DefaultTitle;
+ });
+ },this);
+ },
+
+ sort: function(th){
+ var table = getAncestorByTagName(th, "table" ),
+ filter = (table.filterStack),
+ rows = (table.sortCache || []),
+ colidx = 0, //target column to sort
+ body = $T(table);
+ th = $(th);
+
+ //todo add spinner while sorting
+ //validate header row
+ $A(body.rows[0].cells).each(function(thi, i){
+ if(thi.getTag() != 'th') return;
+ if(th == thi) { colidx=i; return; }
+ thi.removeClass('sortAscending').removeClass('sortDescending')
+ .addClass('sort').title = Sortable.DefaultTitle;
+ });
+
+ if(rows.length == 0){ //if data not yet cached
+ $A(body.rows).each(function(r,i){
+ if((i==0) || ((i==1) && (filter))) return;
+ rows.push( r );
+ });
+ };
+ var datatype = Sortable.guessDataType(rows,colidx);
+
+ //do the actual sorting
+ if(th.hasClass('sort')){
+ rows.sort( Sortable.createCompare(colidx, datatype) )
+ }
+ else rows.reverse();
+
+ var fl=th.hasClass('sortDescending');
+ th.removeClass('sort').removeClass('sortAscending').removeClass('sortDescending');
+ th.addClass(fl ? 'sortAscending': 'sortDescending')
+ .title= fl ? Sortable.DescendingTitle: Sortable.AscendingTitle;
+
+ var frag = document.createDocumentFragment();
+ rows.each( function(r,i){ frag.appendChild(r); });
+ body.appendChild(frag);
+ table.sortCache = rows;
+ if(table.zebra) table.zebra();
+ },
+
+ guessDataType: function(rows, colidx){
+ var num=date=ip4=euro=true;
+ rows.each(function(r,i){
+ var v = $getText(r.cells[colidx]).clean().toLowerCase();
+ if(num) num = !isNaN(parseFloat(v));
+ if(date) date = !isNaN(Date.parse(v));
+ if(ip4) ip4 = v.test("(?:\\d{1,3}\\.){3}\\d{1,3}");
+ if(euro) euro = v.test("^[£$â¬][0-9.,]+");
+ });
+ return (euro) ? 'euro': (ip4) ? 'ip4': (date) ? 'date': (num) ? 'num': 'string';
+ },
+
+ convert: function(val, datatype){
+ switch(datatype){
+ case "num" : return parseFloat( val.match( Number.REparsefloat ) );
+ case "euro": return parseFloat( val.replace(/[^0-9.,]/g,'') );
+ case "date": return new Date( Date.parse( val ) );
+ case "ip4" :
+ var octet = val.split( "." );
+ return parseInt(octet[0]) * 1000000000 + parseInt(octet[1]) * 1000000 + parseInt(octet[2]) * 1000 + parseInt(octet[3]);
+ default : return val.toString().toLowerCase();
+ }
+ },
+
+ createCompare: function(i, datatype) {
+ return function(row1, row2) {
+ var val1 = Sortable.convert( $getText(row1.cells[i]), datatype );
+ var val2 = Sortable.convert( $getText(row2.cells[i]), datatype );
+
+ if(val1<val2){ return -1 } else if(val1>val2){ return 1 } else return 0;
+ }
+ }
+}
+
+/** 240 table-filters
+ ** inspired by http://www.codeproject.com/jscript/filter.asp
+ **/
+var TableFilter =
+{
+ onPageLoad: function(){
+ this.All = "filter.all".localize();
+ this.FilterRow = 1; //row number of filter dropdowns
+
+ $$('.table-filter table').each( function(table){
+ if( table.rows.length < 2 ) return;
+
+ /*
+ $A(table.rows[0].cells).each(function(e,i){
+ var s = new Element('select',{
+ 'events': {
+ 'click':function(event){ event.stop(); }.bindWithEvent(),
+ 'change':TableFilter.filter
+ }
+ });
+ s.fcol = i; //store index
+ e.adopt(s);
+ },this);
+ */
+
+ var r = $(table.insertRow(TableFilter.FilterRow)).addClass('filterrow');
+ for(var j=0; j < table.rows[0].cells.length; j++ ){
+ var s = new Element('select',{
+ 'events': {
+ 'change':TableFilter.filter
+ }
+ });
+ s.fcol = j; //store index
+
+ new Element('th').adopt(s).inject(r);
+ }
+ table.filterStack = [];
+ TableFilter.buildEmptyFilters(table);
+ });
+ },
+
+ buildEmptyFilters: function(table){
+ for(var i=0; i < table.rows[0].cells.length; i++){
+ var ff = table.filterStack.some(function(f){ return f.fcol==i });
+ if(!ff) TableFilter.buildFilter(table, i);
+ }
+ if(table.zebra) table.zebra();
+ },
+
+ // this function initialises a column dropdown filter
+ buildFilter: function(table, col, selectedValue){
+ // Get a reference to the dropdownbox.
+ var select = table.rows[TableFilter.FilterRow].cells[col].firstChild;
+ //var select = $(table.rows[0].cells[col]).getLast();
+ if(!select) return; //empty dropdown
+ select.options.length = 0;
+
+ var rows=[];
+ $A(table.rows).each(function(r,i){
+ if((i==0) || (i==TableFilter.FilterRow)) return;
+ if(r.style.display == 'none') return;
+ rows.push( r );
+ });
+ rows.sort(Sortable.createCompare(col, Sortable.guessDataType(rows,col)));
+
+ //add only unique strings to the dropdownbox
+ select.options[0]= new Option(this.All, this.All);
+ var value;
+ rows.each(function(r,i){
+ var v = $getText(r.cells[col]).clean().toLowerCase();
+ if(v == value) return;
+ value = v;
+ if(v.length > 32) v = v.substr(0,32)+ "...";
+ select.options[select.options.length] = new Option(v, value);
+ });
+ (select.options.length <= 2) ? select.hide() : select.show();
+ if(selectedValue != undefined) {
+ select.value = selectedValue;
+ } else {
+ select.options[0].selected = true;
+ }
+ },
+
+ filter: function(){ //onchange handler of filter dropdowns
+ var col = this.fcol,
+ value = this.value,
+ table = getAncestorByTagName(this, 'table');
+ if( !table || table.style.display == 'none') return;
+
+ // First check if the column is allready in the filter.
+ if(table.filterStack.every(function(f,i){
+ if(f.fcol != col) return true;
+ if(value == TableFilter.All) table.filterStack.splice(i, 1);
+ else f.fValue = value;
+ return false;
+ }) ) table.filterStack.push( {fValue:value, fcol:col} );
+
+ $A(table.rows).each(function(r,i){ //show all
+ r.style.display='';
+ });
+
+ table.filterStack.each(function(f){ //now filter the right rows
+ var v = f.fValue, c = f.fcol;
+ TableFilter.buildFilter(table, c, v);
+
+ var j=0;
+ $A(table.rows).each(function(r,i){
+ if((i==0) || (i==TableFilter.FilterRow)) return;
+ if(v != $getText(r.cells[c]).clean().toLowerCase()) r.style.display = 'none';
+ });
+ });
+ TableFilter.buildEmptyFilters(table); //fill remaining dropdowns
+ }
+}
+
+
+/** 250 Categories: turn wikipage link into AJAXed popup **/
+var Categories =
+{
+ onPageLoad: function (){
+ this.jsp = Wiki.TemplateDir + '/AJAXCategories.jsp';
+
+ $$('.category a.wikipage').each(function(link){
+ var page = Wiki.getPageName(link.href); if(!page) return;
+ var wrap = new Element('span').injectBefore(link).adopt(link),
+ popup = new Element('div',{'class':'categoryPopup'}).inject(wrap),
+ popfx = popup.effect('opacity',{wait:false}).set(0);
+
+ link.addClass('categoryLink')
+ .setProperties({ href:'#', title: "category.title".localize(page) })
+ .addEvent('click', function(e){
+ new Event(e).stop(); //dont jump to top of page ;-)
+ new Ajax( Categories.jsp, {
+ postBody: '&page=' + page,
+ update: popup,
+ onComplete: function(){
+ link.setProperty('title', '').removeEvent('click');
+ wrap.addEvent('mouseover', function(e){ popfx.start(0.9); })
+ .addEvent('mouseout', function(e){ popfx.start(0); });
+ popup.setStyle('left', link.getPosition().x);
+ popfx.start(0.9);
+ }
+ }).request();
+ });
+ });
+ }
+}
+
+/**
+ ** 260 Wiki Tips:
+ **/
+var WikiTips =
+{
+ onPageLoad: function() {
+ var tips = [];
+ $$('*[class^=tip]').each( function(t){
+ var parms = t.className.split('-');
+ if( parms.length<=0 || parms[0] != 'tip' ) return;
+ t.className = "tip";
+
+ var body = new Element('span').injectWrapper(t).hide(),
+ caption = (parms[1]) ? parms[1].deCamelize(): "tip.default.title".localize();
+
+ tips.push(
+ new Element('span',{
+ 'class': 'tip-anchor',
+ 'title': caption + '::' + body.innerHTML
+ }).setHTML(caption).inject(t)
+ );
+ });
+ if( tips.length>0 ) new Tips( tips , {'className':'tip'} );
+ }
+}
+
+
+/**
+ ** 270 Wiki Columns
+ ** Dirk Frederickx, Mar 07
+ **/
+var WikiColumns =
+{
+ onPageLoad: function() {
+ var tips = [];
+ $$('*[class^=columns]').each( function(t){
+ var parms = t.className.split('-');
+ t.className='columns';
+ WikiColumns.buildColumns(t, parms[1] || 'auto');
+ });
+ },
+
+ buildColumns: function( el, width){
+ var breaks = $ES('hr',el);
+ if(!breaks || breaks.length==0) return;
+
+ var colCount = breaks.length+1;
+ width = (width=='auto') ? 98/colCount+'%' : width/colCount+'px';
+
+ var colDef = new Element('div',{'class':'col','styles':{'width':width}}),
+ col = colDef.clone().injectBefore(el.getFirst()),
+ n;
+ while(n = col.nextSibling){
+ if(n.tagName && n.tagName.toLowerCase() == 'hr'){
+ col = colDef.clone();
+ $(n).replaceWith(col);
+ continue;
+ }
+ col.appendChild(n);
+ }
+ new Element('div',{'styles':{'clear':'both'}}).inject(el);
+ }
+}
+
+/** 280 ZebraTable
+ ** Color odd/even rows of table differently
+ ** 1) odd rows get css class odd (ref. jspwiki.css )
+ ** %%zebra-table ... %%
+ **
+ ** 2) odd rows get css style='background=<color>'
+ ** %%zebra-<odd-color> ... %%
+ **
+ ** 3) odd rows get odd-color, even rows get even-color
+ ** %%zebra-<odd-color>-<even-color> ... %%
+ **
+ ** colors are specified in HEX (without #) format or html color names (red, lime, ...)
+ **/
+var ZebraTable = {
+
+ onPageLoad: function(){
+ $$('*[class^=zebra]').each(function(z){
+ var parms = z.className.split('-'),
+ isDefault = parms[1].test('table'),
+ c1 = '',
+ c2 = '';
+ if(parms[1]) c1= new Color(parms[1],'hex');
+ if(parms[2]) c2= new Color(parms[2],'hex');
+ $ES('table',z).each(function(t){
+ t.zebra = this.zebrafy.pass([isDefault, c1,c2],t);
+ t.zebra();
+ },this);
+ },this);
+ },
+ zebrafy: function(isDefault, c1,c2){
+ var j=0;
+ $A($T(this).rows).each(function(r,i){
+ if(i==0 || (r.style.display=='none')) return;
+ if(isDefault) (j++ % 2 == 0) ? $(r).addClass('odd') : $(r).removeClass('odd');
+ else $(r).setStyle('background-color', (j++ % 2 == 0) ? c1 : c2 );
+ });
+ }
+}
+
+
+/** Highlight Word
+ ** Inspired by http://www.kryogenix.org/code/browser/searchhi/
+ ** Modified 21006 to fix query string parsing and add case insensitivity
+ ** Modified 20030227 by sgala@hisitech.com to skip words
+ ** with "-" and cut %2B (+) preceding pages
+ ** Refactored for JSPWiki -- now based on regexp, by D.Frederickx. Nov 2005
+ **/
+var HighlightWord =
+{
+ ReQuery: new RegExp( "(?:\\?|&)(?:q|query)=([^&]*)", "g" ),
+
+ onPageLoad: function (){
+ var q = Wiki.prefs.get('PrevQuery'); Wiki.prefs.set('PrevQuery', '');
+ if( !q && this.ReQuery.test(document.referrer)) q = RegExp.$1;
+ if( !q ) return;
+
+ var words = decodeURIComponent(q);
+ words = words.replace( /\+/g, " " );
+ words = words.replace( /\s+-\S+/g, "" );
+ words = words.replace( /([\(\[\{\\\^\$\|\)\?\*\.\+])/g, "\\$1" ); //escape metachars
+ words = words.trim().split(/\s+/).join("|");
+ this.reMatch = new RegExp( "(" + words + ")" , "gi");
+
+ this.walkDomTree( $("pagecontent") );
+ },
+
+ // recursive tree walk matching all text nodes
+ walkDomTree: function( node )
+ {
+ if( !node ) return; /* bugfix */
+ var nn = null;
+ for( var n = node.firstChild; n ; n = nn ) {
+ nn = n. nextSibling; /* prefetch nextSibling cause the tree will be modified */
+ this.walkDomTree( n );
+ }
+ // 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 || '';
+ if( !this.reMatch.test( s ) ) return;
+ var tmp = new Element('span').setHTML(s.replace(this.reMatch,"<span class='searchword'>$1</span>"));
+
+ var f = document.createDocumentFragment();
+ while( tmp.firstChild ) f.appendChild( tmp.firstChild );
+
+ node.parentNode.replaceChild( f, node );
+ }
+}
+
+
+/* 300 Javascript Code Prettifier
+ * based on http://google-code-prettify.googlecode.com/svn/trunk/README.html
+ */
+var WikiPrettify = {
+ onPageLoad: function(){
+ var els = $$('.prettify pre, .prettify code'); if(!els) return;
+
+ //TODO: load assets .css and .js
+ els.addClass('prettyprint');
+ prettyPrint();
+ }
+}
+
+
+window.addEvent('load', function(){
+
+ Wiki.onPageLoad();
+ WikiReflection.onPageLoad(); //before accordion cause impacts height!
+ WikiAccordion.onPageLoad();
+
+ TabbedSection.onPageLoad(); //after coordion or safari
+ QuickLinks.onPageLoad();
+
+ //console.profile();
+ Collapsible.onPageLoad();
+ //console.profileEnd();
+
+ SearchBox.onPageLoad();
+ Sortable.onPageLoad();
+ TableFilter.onPageLoad();
+ RoundedCorners.onPageLoad();
+ ZebraTable.onPageLoad();
+ HighlightWord.onPageLoad();
+ GraphBar.onPageLoad();
+ Categories.onPageLoad();
+
+ WikiSlimbox.onPageLoad();
+ WikiTips.onPageLoad();
+ WikiColumns.onPageLoad();
+ WikiPrettify.onPageLoad();
+});
\ No newline at end of file
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs/scripts/jspwiki-edit.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs/scripts/jspwiki-edit.js?rev=627259&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs/scripts/jspwiki-edit.js (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs/scripts/jspwiki-edit.js Tue Feb 12 22:11:16 2008
@@ -0,0 +1,496 @@
+/**
+ ** jspwiki edit support routines
+ ** Based on brushed template
+ ** Needs jspwiki-common.js and mootools.js
+ **
+ ** EditTools object (main object)
+ ** - find&replace functionality : with regexp support
+ **
+ ** - included popup-pagelinks routine from Janne //
+ **
+ ** TextArea object
+ ** Supports selections inside textarea, in ie and other browsers
+ **/
+
+var EditTools =
+{
+ onPageLoad: function(){
+
+ this.textarea = $('editorarea');
+ if(!this.textarea || !this.textarea.visible) return;
+
+ window.onbeforeunload = (function(){
+ if(this.textarea.value != this.textarea.defaultValue) {
+ return "edit.areyousure".localize();
+ };
+ }).bind(this);
+
+ /* make textarea more intelligent */
+ this.wikisnippets = this.getWikiSnippets();
+ this.wikismartpairs = this.getWikiSmartPairs();
+ this.onPageLoadPostEditor();
+
+ /* activate editassist toolbar */
+ var toolbar = $('toolbar');
+ var fxToolbar = new Fx.Slide(toolbar,{
+ onStart:function(){
+ $('editassist').toggleClass('closed');
+ }
+ });
+ var e = $('editassist').addEvent('click', function(e){
+ e = new Event(e);
+ fxToolbar.toggle();
+ e.stop();
+ }).getParent().show();
+
+
+ //FIXME: stop-event not yet working properly on eg UNDO
+ $('tbREDO').addEvent('click', function(e) { EditTools.redoTextarea(); new Event(e).stop(); });
+ $('tbUNDO').addEvent('click', function(e) { new Event(e).stop(); EditTools.undoTextarea(); });
+ $('replace').addEvent('click', function(e) { EditTools.doReplace(); new Event(e).stop(); })
+ .getParent().getParent().show();
+
+ toolbar.getElements('a.tool').each(function(el){
+ el.addEvent('click', this.insertTextArea.pass(el,this));
+ },this);
+
+ /* add textarea resize drag bar */
+ var hh=Wiki.prefs.get('EditorSize');
+ if(hh) this.textarea.setStyle('height',hh);
+ var h = new Element('div',{'class':'textarea-resizer', 'title':'edit.resize'.localize()})
+ .injectAfter(this.textarea);
+ this.textarea.makeResizable({
+ handle:h,
+ modifiers: {x:false, y:'height'},
+ onComplete: function(){ Wiki.prefs.set('EditorSize',this.value.now.y); }
+ });
+
+ },
+
+ onPageLoadPostEditor: function(){
+ if(window.ie) return;
+ this.posteditor = new postEditor.create(this.textarea,'changenote');
+
+ /* patch posteditor DF Jul 07 */
+ /* righ-arrow nok on FF, nop on Safari */
+ this.posteditor.onKeyRight = Class.empty;
+ /* make posteditor changes undoable */
+ this.posteditor.value = function(value) {
+ EditTools.storeTextarea();
+ this.element.value = value.join("");
+ };
+
+ ['smartpairs', 'tabcompletion'].each( function(el){
+ $(el).setProperty('checked', Wiki.prefs.get(el) || false)
+ .addEvent('click',function(e) {
+ Wiki.prefs.set(el,this.checked);
+ EditTools.initPostEditor();
+ });
+ },this);
+ $('smartpairs').getParent().show();
+
+ this.initPostEditor();
+ },
+ initPostEditor: function(){
+ if(! this.posteditor) return;
+ this.posteditor.changeSmartTypingPairs( $('smartpairs').checked ? this.wikismartpairs : {} );
+ this.posteditor.changeSnippets( $('tabcompletion').checked ? this.wikisnippets : {} );
+ },
+
+ getWikiSnippets: function(){
+ return {
+ "toc" : {
+ snippet:["","[{TableOfContents }]", "\n"],
+ tab:['[{TableOfContents }]', '']
+ },
+ "link" : {
+ snippet:["[","link text|pagename", "]"],
+ tab:['link text','pagename','']
+ },
+ "code" : {
+ snippet:["%%prettify \n{{{\n","some code block", "\n}}}\n/%\n"],
+ tab:['some code block','']
+ },
+ "pre" : {
+ snippet:["{{{\n","some preformatted block", "\n}}}\n"],
+ tab:['some preformatted block','']
+ },
+ "br" : {
+ snippet:['\\\\\n','',''],
+ tab:['']
+ },
+ "bold" : {
+ snippet:["__","some bold text", "__"],
+ tab:['some bold text','']
+ },
+ "italic" : {
+ snippet:["''","some italic text", "''"],
+ tab:['some italic text','']
+ },
+ "h1" : {
+ snippet:["!!! ","Heading 1 title", "\n"],
+ tab:["Heading 1 title", ""]
+ },
+ "h2" : {
+ snippet:["!! ","Heading 2 title", "\n"],
+ tab:["Heading 2 title", ""]
+ },
+ "h3" : {
+ snippet:["! ","Heading 3 title", "\n"],
+ tab:["Heading 3 title", ""]
+ },
+ "dl" : {
+ snippet:["\n",";term:definition text", "\n"],
+ tab:["term","definition text", ""]
+ },
+ "mono" : {
+ snippet:["{{","some monospaced text", "}}"],
+ tab:["some monospaced text", ""]
+ },
+ "hr" : {
+ snippet:['----\n','',''],
+ tab:['']
+ },
+ "sub" : {
+ snippet:["%%sub ","subscript text", "/%"],
+ tab:['subscript text','']
+ },
+ "sup" : {
+ snippet:["%%sup ","superscript text", "/%"],
+ tab:['superscript text','']
+ },
+ "strike" : {
+ snippet:["%%strike ","strikethrough text", "/%"],
+ tab:['strikethrough text','']
+ },
+ "tab" : {
+ snippet:["%%tabbedSection \n","%%tab-tabTitle1\ntab content 1\n/%\n%%tab-tabTitle2\ntab content 2", "\n/%\n/%\n"],
+ tab:['tabTitle1','tab content 1','tabTitle2','tab content 2','']
+ },
+ "table" : {
+ snippet:["\n","||heading 1||heading 2\n| cell 1 | cell 2", "\n"],
+ tab:['heading 1','heading 2','cell 1','cell 2','']
+ },
+ "img" : {
+ snippet:["","[{Image src='img.jpg' width='..' height='..' align='left|center|right' style='..' class='..' }]", "\n"],
+ tab:['img.jpg', '']
+ },
+ "quote" : {
+ snippet:["%%quote \n","quoted text", "\n/%\n"],
+ tab:['quoted text','']
+ },
+ "%%" : {
+ snippet:["%%","wikistyle\nsome text", "\n/%"],
+ tab:['wikistyle','some text','']
+ },
+ //dynamic snippets
+ "sign" : {
+ snippet:["\\\\\n--",Wiki.UserName+", "+"25 Sep 07","\n"],
+ tab:[Wiki.UserName,'25 Sep 07','']
+ },
+ /* TODO: how to insert the proper current date/timestamp, inline with the preferred time format */
+ "date" : {
+ //return new object snippet
+ command: function(k) {
+ var dayNames = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
+ monthNames = ["January","February","March","April","May","June","July","August","September","October","November","December"],
+ dt = new Date(),
+ y = dt.getYear();
+ if (y < 1000) y +=1900;
+ var date = dayNames[dt.getDay()] + ", " + monthNames[dt.getMonth()] + " " + dt.getDate() + ", " + y;
+ return {
+ //key:"date", optional
+ snippet:['',date,' '],
+ tab:[date,'']
+ };
+ }
+ }
+} /* return */
+
+ },
+ getWikiSmartPairs: function(){
+ return {
+ '"' : '"',
+ '(' : ')',
+ '{' : '}',
+ '[' : ']',
+ '<' : '>',
+ "'" : { scope:{ "{{{":"}}}" }, pair:"'" }
+ }
+ },
+ insertTextArea: function(el) {
+ var snippy = this.wikisnippets[el.getText()]; if(!snippy) return
+
+ var s = TextArea.getSelection(this.textarea),
+ t = snippy.snippet.join('');
+ EditTools.storeTextarea();
+
+ if((el.rel=='break') && (!TextArea.isSelectionAtStartOfLine(this.textarea))) {
+ t = "\n" + t;
+ }
+ if(s) t = t.replace( snippy.tab[0], s)
+ TextArea.replaceSelection(this.textarea, t);
+ return false; /*don't propagate*/
+ } ,
+
+ /* TOOLBAR: find&replace */
+ doReplace: function( ){
+ var findText = $('tbFIND').value,
+ isRegExp = $('tbREGEXP').checked,
+ reGlobal = $('tbGLOBAL').checked ? 'g' : '',
+ replaceText = $('tbREPLACE').value,
+ reMatchCase = $('tbMatchCASE').checked ? '' : 'i';
+
+ if( findText == "") return;
+
+ var sel = TextArea.getSelection(this.textarea);
+ var data = ( !sel || (sel=="") ) ? this.textarea.value : sel;
+
+ if(!isRegExp){ /* escape all special re characters */
+ var re = new RegExp( "([\.\*\\\?\+\[\^\$])", "gi");
+ findText = findText.replace( re,"\\$1" );
+ }
+
+ var re = new RegExp(findText, reGlobal+reMatchCase+"m" ); //multiline
+ if(!re.exec(data)){
+ alert( "edit.findandreplace.nomatch".localize() );
+ return true;
+ }
+
+ data = data.replace(re, replaceText);
+
+ this.storeTextarea();
+ if(!sel || (sel=="")){
+ this.textarea.value = data;
+ } else {
+ TextArea.replaceSelection( this.textarea, data );
+ }
+ if(this.textarea.onchange) this.textarea.onchange();
+ } ,
+
+ /* TOOLBAR: cut/copy/paste clipboard functionality */
+ CLIPBOARD : null,
+ clipboard : function( format ){
+ var s = TextArea.getSelection(this.textarea);
+ if( !s || s == "") return;
+
+ this.CLIPBOARD = s ;
+ $( 'tbPASTE' ).className = this.ToolbarMarker;
+ var ss = format.replace( /\$/, s);
+ if( s == ss ) return; //copy
+
+ this.storeTextarea(); //cut
+ TextArea.replaceSelection( this.textarea, ss );
+ } ,
+
+ paste : function()
+ {
+ if( !this.CLIPBOARD ) return;
+ this.storeTextarea();
+ TextArea.replaceSelection( this.textarea, this.CLIPBOARD );
+ } ,
+
+ /* UNDO functionality: use by all toolbar and find&replace functions */
+ UNDOstack : [],
+ REDOstack : [],
+ UNDOdepth : 20,
+
+ storeTextarea : function() {
+ this.UNDOstack.push( this.textarea.value );
+ $('tbUNDO').disabled = '';
+ this.REDOstack = [];
+ $('tbREDO').disabled = 'true';
+ if(this.UNDOstack.length > this.UNDOdepth) this.UNDOstack.shift();
+ },
+ undoTextarea : function(){
+ if(this.UNDOstack.length > 0){
+ $('tbREDO').disabled = '';
+ this.REDOstack.push(this.textarea.value);
+ this.textarea.value = this.UNDOstack.pop();
+ }
+ if(this.UNDOstack.length == 0) $('tbUNDO').disabled = 'true';
+ if(!this.selector) return;
+ this.onSelectorLoad();
+ this.onSelectorChanged();
+
+ this.textarea.focus();
+ },
+ redoTextarea : function(){
+ if(this.REDOstack.length > 0){
+ $('tbUNDO').disabled = '';
+ this.UNDOstack.push(this.textarea.value);
+ this.textarea.value = this.REDOstack.pop();
+ }
+ if(this.REDOstack.length == 0) $('tbREDO').disabled = 'true';
+ if(!this.selector) return;
+ this.onSelectorLoad();
+ this.onSelectorChanged();
+ this.textarea.focus();
+ },
+ getSuggestionMenu: function(){
+ var mID = 'findSuggestionMenu',
+ m = $(mID);
+ if( !m ) {
+ m = new Element('div',{'id':mID}).injectTop( $('favorites') );
+ }
+ return m;
+ }
+}
+
+
+/** TextArea support routines
+ **/
+var TextArea =
+{
+ getSelection: function(id){
+ var f = $(id); if(!f) return '';
+
+ if(window.ie) return document.selection.createRange().text;
+ return f.getValue().substring(f.selectionStart, f.selectionEnd);
+ },
+
+ /* replaces the selection with aValue, and returns with aValue selected */
+ replaceSelection: function(id, newText){
+ var f = $(id); if(!f) return;
+ var scrollTop = this.scrollTop;
+
+ if(window.ie){
+ f.focus();
+ var r = document.selection.createRange();
+ r.text = newText;
+ r.moveStart('character',-newText.length); /***/
+ r.select();
+ f.range.select();
+ }
+ else {
+ var start = f.selectionStart, end = f.selectionEnd;
+ f.value = f.value.substring(0, start) + newText + f.value.substring(end);
+ f.replaceSelectionRange(start + newText.length, start + newText.length);
+ }
+ f.focus();
+ f.scrollTop = scrollTop;
+ if(f.onchange) f.onchange();
+ },
+
+ /* check whether selection is preceeded by a \n (peek-ahead) */
+ isSelectionAtStartOfLine: function(id){
+ var f = $(id); if(!f) return false;
+
+ if(window.ie){
+ f.focus();
+ var r1 = document.selection.createRange(),
+ r2 = document.selection.createRange();
+ r2.moveStart( "character", -1);
+ if(r2.text=="") r2.moveEnd( "character", 1);
+ if(r1.compareEndPoints("StartToStart", r2) == 0) return true;
+ if(r2.text.charAt(0).match( /[\n\r]/ )) return true;
+ }
+ else {
+ if(f.selectionStart == 0) return true;
+ if(f.value.charAt(f.selectionStart-1) == '\n') return true;
+ }
+ return false;
+ }
+};
+
+//*************************
+// TODO
+// copied from default -- to be incorporated in EditTools
+var globalCursorPos; // global variabe to keep track of where the cursor was
+
+//sets the global variable to keep track of the cursor position
+function setCursorPos(id)
+{
+ if(window.ie) return;
+ globalCursorPos = getCursorPos( $(id) );
+}
+
+function getCursorPos(textElement)
+{
+ //save off the current value to restore it later,
+ var sOldText = textElement.value;
+
+ if(window.ie){
+ var objRange = document.selection.createRange(),
+ sOldRange = objRange.text,
+ sWeirdString = '#%~'; //small string that will not normally be encountered
+
+ //insert the weirdstring where the cursor is at
+ objRange.text = sOldRange + sWeirdString;
+ objRange.moveStart('character', (0 - sOldRange.length - sWeirdString.length));
+
+ //save off the new string with the weirdstring in it
+ var sNewText = textElement.value;
+
+ //set the actual text value back to how it was
+ objRange.text = sOldRange;
+
+ //look through the new string we saved off and find the location of
+ //the weirdstring that was inserted and return that value
+ for (i=0; i <= sNewText.length; i++) {
+ var sTemp = sNewText.substring(i, i + sWeirdString.length);
+ if (sTemp == sWeirdString) {
+ var cursorPos = (i - sOldRange.length);
+ return cursorPos;
+ }
+ }
+ }
+ // Mozilla and the rest
+ else if( textElement.selectionStart || textElement.selectionStart == '0')
+ {
+ return textElement.selectionStart;
+ }
+ else
+ {
+ return sOldText.length;
+ }
+}
+
+//this function inserts the input string into the textarea
+//where the cursor was at
+function insertString(stringToInsert) {
+ var firstPart = myForm.myTextArea.value.substring(0, globalCursorPos);
+ var secondPart = myForm.myTextArea.value.substring(globalCursorPos,
+ myForm.myTextArea.value.length);
+ myForm.myTextArea.value = firstPart + stringToInsert + secondPart;
+}
+
+/**
+ ** JSON-RPC
+ ** POST is
+ ** {"id": 2, "method": "search.getSuggestions", "params": ["p", 10]}
+ ** Response is
+ ** {"result":{"list":["Pic\/ruby.jpg","Pic\/telenet-smile.gif","Pic\/spin-greyblocks.gif","Pic\/shadow_transparent2.png","Pic\/monkey-mam-child.jpg","Pic\/brushed-button.jpg","Pic\/resizecursorv.png","Pic\/UserKeychainIcon.tiff","PrototypeJavascriptLibrary","Pizza Margerita"],"javaClass":"java.util.ArrayList"},"id":2}
+ **/
+function getSuggestions(id)
+{
+ if(window.ie) return;
+ var textNode = $(id),
+ val = textNode.value,
+ searchword;
+
+ var pos = getCursorPos(textNode);
+ for( i = pos-1; i > 0; i-- ){
+ if( val.charAt(i) == ']' ) break;
+ if( val.charAt(i) == '[' && i < val.length-1 ) { searchword = val.substring(i+1,pos); break; }
+ }
+
+ if(searchword){
+ jsonrpc.search.getSuggestions(callback, searchword, 10);
+ } else {
+ EditTools.getSuggestionMenu().hide();
+ }
+}
+function callback(result, exception)
+{
+ if(exception) { alert(exception.message); return; }
+
+ var menuNode = EditTools.getSuggestionMenu(),
+ html = [];
+
+ result.list.each(function(el) { html.push('<li>'+el+'</li>'); });
+
+ menuNode.setHTML('<ul>',html.join(''),'</ul>').show();
+}
+
+window.addEvent('load', EditTools.onPageLoad.bind(EditTools) ); //edit only
\ No newline at end of file
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs/scripts/jspwiki-prefs.js
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs/scripts/jspwiki-prefs.js?rev=627259&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs/scripts/jspwiki-prefs.js (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/webdocs/scripts/jspwiki-prefs.js Tue Feb 12 22:11:16 2008
@@ -0,0 +1,117 @@
+/**
+ ** jspwiki preferences support routines
+ ** based on brushedGroup.js
+ ** Dirk Frederickx Jun 07
+ ** Uses mootools library
+ **/
+
+var WikiGroup =
+{
+ MembersID : "membersfield",
+ GroupTltID : "grouptemplate",
+ GroupID : "groupfield",
+ NewGroupID : "newgroup",
+ GroupInfoID : "groupinfo",
+ CancelBtnID : "cancelButton",
+ SaveBtnID : "saveButton",
+ CreateBtnID : "createButton",
+ DeleteBtnID : "deleteButton",
+ groups : { "(new)": { members:"", groupInfo:"" } },
+ cursor : null,
+ isEditOn : false,
+ isCreateOn : false,
+
+ putGroup: function(group, members, groupInfo, isSelected){
+ this.groups[group] = { members: members, groupInfo: groupInfo };
+
+ var g = $(this.GroupTltID);
+ var gg = g.clone().setHTML(group);
+ gg.id = '';
+ g.parentNode.appendChild(gg);
+ $(gg).show();
+
+ if(isSelected || !this.cursor) this.onMouseOverGroup(gg);
+ } ,
+
+ onMouseOverGroup: function(node){
+ if(this.isEditOn) return;
+ this.setCursor(node);
+
+ var g = this.groups[ (node.id == this.GroupID) ? "(new)": node.innerHTML ];
+ $(this.MembersID).value = g.members;
+ $(this.GroupInfoID).innerHTML = g.groupInfo;
+ } ,
+
+ setCursor: function(node){
+ if(this.cursor) $(this.cursor).removeClass('cursor');
+ this.cursor = $(node).addClass('cursor');
+ } ,
+
+ //create new group: focus on input field
+ onClickNew: function(){
+ if(this.isEditOn) return;
+
+ this.isCreateOn = true;
+ $(this.MembersID).value = "";
+ this.toggle();
+ } ,
+
+ //toggle edit status of Group Editor
+ toggle: function(){
+ this.isEditOn = !this.isEditOn; //toggle
+
+ $(this.MembersID ).disabled =
+ $(this.SaveBtnID ).disabled =
+ $(this.CreateBtnID).disabled =
+ $(this.CancelBtnID).disabled = !this.isEditOn;
+ var del = $(this.DeleteBtnID);
+ if(del) del.disabled = this.isCreateOn || !this.isEditOn;
+
+ if(this.isCreateOn) { $(this.CreateBtnID).toggle(); $(this.SaveBtnID).toggle() };
+
+ var newGrp = $(this.NewGroupID),
+ members = $(this.MembersID);
+
+ if(this.isEditOn){
+ members.getParent().addClass("cursor");
+
+ newGrp.disabled = !this.isCreateOn;
+ if(this.isCreateOn) { newGrp.focus(); } else { members.focus(); }
+ } else {
+ members.getParent().removeClass("cursor");
+
+ if(this.isCreateOn){
+ this.isCreateOn = false;
+ newGrp.value = newGrp.defaultValue;
+ members.value = "";
+ }
+ newGrp.blur();
+ members.blur();
+ newGrp.disabled = false;
+ }
+ } ,
+
+ // submit form to create new group
+ onSubmitNew: function(form, actionURL){
+ var newGrp = $(this.NewGroupID);
+ if(newGrp.value == newGrp.defaultValue){
+ alert("group.validName".localize());
+ newGrp.focus();
+ } else this.onSubmit(form, actionURL);
+ } ,
+
+ // submit form: fill out actual group and members info
+ onSubmit: function(form, actionURL){
+ if(! this.cursor) return false;
+ var g = (this.cursor.id == this.GroupID) ? $(this.NewGroupID).value: this.cursor.innerHTML;
+
+ /* form.action = actionURL; -- doesn't work in IE */
+ form.setAttribute("action", actionURL) ;
+ form.group.value = g;
+ form.members.value = $(this.MembersID).value;
+ form.action.value = "save";
+
+ Wiki.submitOnce(form);
+ form.submit();
+ }
+}
\ No newline at end of file