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 2015/06/22 22:11:44 UTC

svn commit: r1686927 [5/11] - in /jspwiki/trunk: ./ jspwiki-war/src/main/config/wro/ jspwiki-war/src/main/java/org/apache/wiki/ jspwiki-war/src/main/resources/templates/ jspwiki-war/src/main/scripts/behaviors/ jspwiki-war/src/main/scripts/dialog/ jspwi...

Added: jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Function.Extend.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Function.Extend.js?rev=1686927&view=auto
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Function.Extend.js (added)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Function.Extend.js Mon Jun 22 20:11:42 2015
@@ -0,0 +1,61 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); fyou may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+Function.implement({
+
+/*
+Event: debounce
+    Returns a function, that, as long as it continues to be invoked, will not
+    be triggered. The function will be called after it stops being called for
+    N milliseconds. If `immediate` is passed, trigger the function on the
+    leading edge, instead of the trailing.
+    I.e. collapse a number of events into a single event.
+
+Credits:
+    http://davidwalsh.name/function-debounce
+
+Example:
+    el.addEvent('resize', resizePage.debounce(250, true).bind(this) );
+*/
+    debounce: function(wait, immediate){
+
+        var func = this, timer;
+
+        return function(){
+
+            var args = arguments,
+                context = this,
+                callNow = immediate && !timer;
+
+            function later(){
+                timer = null;
+                if( !immediate ){ func.apply(context, args); }
+            }
+
+            clearTimeout(timer);
+            timer = setTimeout(later, wait || 250);
+
+            if( callNow ){ func.apply(context, args); }
+
+        };
+
+    }
+
+});

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/HighlightQuery.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/HighlightQuery.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/HighlightQuery.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/HighlightQuery.js Mon Jun 22 20:11:42 2015
@@ -31,7 +31,7 @@ Credit:
 Arguments
     node - (DOM-element)
     query - (optional) query string, default is document referrer query string
-    template - (string) html template replacement string, default <span class='highlight'>$1</span>
+    template - (string) html template replacement string, default <mark>$&</mark>
 */
 function HighlightQuery( node, query, template ){
 
@@ -56,7 +56,7 @@ function HighlightQuery( node, query, te
                 if( regexp.test( s ) ){
 
                     n = new Element('span',{
-                        html: s.replace(regexp, template || "<span class='highlight'>$1</span>")
+                        html: s.replace(regexp, template || "<mark>$&</mark>")
                     });
                     frag = document.createDocumentFragment();
                     while( n.firstChild ) frag.appendChild( n.firstChild );

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Request.File.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Request.File.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Request.File.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Request.File.js Mon Jun 22 20:11:42 2015
@@ -1,24 +1,4 @@
 /*
-    JSPWiki - a JSP-based WikiWiki clone.
-
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); fyou may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing,
-    software distributed under the License is distributed on an
-    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-    KIND, either express or implied.  See the License for the
-    specific language governing permissions and limitations
-    under the License.
-*/
-/*
 ---
 
 name: Request.File
@@ -63,7 +43,7 @@ Request.File = new Class({
 
     send: function(options){
         if (!this.check(options)) return this;
-//console.log(this.formData);
+        //console.log(this.formData, this.options.url);
         this.options.isSuccess = this.options.isSuccess || this.isSuccess;
         this.running = true;
 

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/String.Extend.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/String.Extend.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/String.Extend.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/String.Extend.js Mon Jun 22 20:11:42 2015
@@ -20,7 +20,7 @@
 */
 /*
 Mootools Extension: String.Extend.js
-	String-extensions: capitalize,() deCamelize(), trunc(), xsubs()
+    String-extensions: capitalize,() deCamelize(), trunc(), xsubs()
 */
 String.implement({
 
@@ -34,7 +34,7 @@ String.implement({
     */
     capitalize: function(){
         //fix mootools implementation , supporting i18n chars such as éàè, not matched by \b
-		//return String(this).replace(/\b[a-z]/g, function(match){
+        //return String(this).replace(/\b[a-z]/g, function(match){
         return this.replace(/(\s|^)\S/g, function(match){
             return match.toUpperCase();
         });
@@ -59,7 +59,7 @@ String.implement({
 
     Arguments:
         size - maximum length of the string, excluding the length of the elips
-        elips - (optional) replaces the truncated part (defaults to '...')
+        ellipsis - (optional) replaces the truncated part (defaults to '...')
 
     Alternative
         Use css propoerty
@@ -71,9 +71,9 @@ String.implement({
     > "this is a long string".trunc(7) === "this is..."
     > "this is a long string".trunc(7,'__') === "this is__"
     */
-    trunc: function (size, elips){
+    trunc: function (size, ellipsis){
 
-        return this.slice(0, size-1) + ((this.length<size) ? '' : (elips||'…'));
+        return this.slice(0, size-1) + ((this.length<size) ? '' : (ellipsis||'…'));
 
     },
 
@@ -92,20 +92,19 @@ String.implement({
         (start code)
         //initialize the translation strings
         String.I18N = {
-            "javascript.moreInfo":"More",
-            "javascript.imageInfo":"Image {0} of {1}",  //indexed parms
-            "javascript.imageInfo2":Image {imgCount} of {totalCount}"   //named parms
-            "javascript.curlyBraces":"Show \{Curly Braces}",  //escaped curly braces
+            "javascript.moreInfo": "More",
+            "javascript.imageInfo": "Image {0} of {1}",  //indexed parms
+            "javascript.imageInfo2": "Image {imgCount} of {totalCount}"   //named parms
+            "javascript.curlyBraces": "Show \{Curly Braces}",  //escaped curly braces
         }
         String.I18N.PREFIX="javascript.";
 
         "moreInfo".localize() === "More";
-        "imageInfo".localize(2,4) ===  "Image 2 of 4"
-        "imageInfo2".localize({totalCount:4, imgCount:2}) === "Image 2 of 4"
+        "imageInfo".localize(2, 4) ===  "Image 2 of 4"
+        "imageInfo2".localize({totalCount: 4, imgCount: 2}) === "Image 2 of 4"
         "curlyBraces".localize() === "Show {Curly Braces}"
 
         (end)
-
     */
     localize: function( params ){
 
@@ -137,18 +136,18 @@ String.implement({
         return this.substitute(object, regexp);
     },
 
-	/*
-	Function:slick(props)
-		Convert css selector into a new DOM Element
-
-	Example:
-	>	"input#someID.someClass1.someClass2[disabled=true]".slick({text:"Hi"});
-	*/
-	slick:function(props){
+    /*
+    Function:slick(props)
+        Fancy DOM Element builder
 
-		return new Element(this+"", props);
+    Example:
+    >    "input#someID.someClass1.someClass2[disabled=true]".slick({text:"Hi"});
+    */
+    slick: function(props){
 
-	},
+        return new Element(this + "", props);
+
+    },
 
     /*
     Function: sliceArgs
@@ -159,7 +158,7 @@ String.implement({
         > <command>.sliceArgs( args (, regexp) );
 
     Arguments:
-        args : (string) or dom-element with classname 
+        args : (string) or dom-element with classname
         regexp : (optional string) pattern match for the arguments, defaults (-\w+)*
 
     Example
@@ -170,35 +169,35 @@ String.implement({
     */
     sliceArgs: function(element, regexp){
 
-    	var args = element.grab /*isElement*/ ? element.className : element;
+        var args = element.grab /*isElement*/ ? element.className : element;
 
-        if( !regexp) regexp = "(^|\\s)"+this+"(-\\w+)*(?:\\s|$)"; //default '-' separated arguments
+        if( !regexp) regexp = "(?:^|\\s)("+this+"(?:-\\w+)*)(?:\\s|$)"; //default '-' separated arguments
 
         args = args.match( regexp );
-        return args && args[0].split('-').slice(1);
+        return args && args[1].split('-').slice(1);
 
     },
 
-	/*
-	Function: fetchContext    
-		Match an elements classname or string against one of the bootstrap contextual patterns.
-		Supported contexts: default, primary, success, info, warning, danger
-		
-		Return a (string) classname to invoke the contextual colors.
-		
-	Example
-	>	'panel'.fetchContext( 'accordion-danger') => 'panel panel-danger'
-	>	'panel'.fetchContext( 'commentbox-success') => 'panel panel-success'
-
-	*/
-	fetchContext : function(element){
-	
-	    var name = element.grab /*isElement*/ ? element.className : element;
+    /*
+    Function: fetchContext
+        Match an elements classname or string against one of the bootstrap contextual patterns.
+        Supported contexts: default, primary, success, info, warning, danger
+
+        Return a (string) classname to invoke the contextual colors.
+
+    Example
+    >    'panel'.fetchContext( 'accordion-danger') => 'panel panel-danger'
+    >    'panel'.fetchContext( 'commentbox-success') => 'panel panel-success'
+
+    */
+    fetchContext : function(element){
+
+        var name = element.grab /*isElement*/ ? element.className : element;
 
         name = (name.match( /\b(default|primary|success|info|warning|danger)(\b|$)/ )||[,'default'])[1];
 
-		return this+' '+this+'-'+name ;
-		
-	}
+        return this + ' ' + this + '-' + name ;
+
+    }
 
-});
+});
\ No newline at end of file

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Textarea.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Textarea.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Textarea.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Textarea.js Mon Jun 22 20:11:42 2015
@@ -28,44 +28,28 @@ Class: Textarea
 Example:
     (start code)
     <script>
-        var txta = new Textarea( "mainTextarea" );
+        var ta = new Textarea( "mainTextarea" );
     </script>
     (end)
 */
 var Textarea = new Class({
 
-    Implements: [Options,Events],
+    Implements: [Options, Events],
 
     //options: { onChange:function(e){} );
 
-    initialize: function(el,options){
+    initialize: function(el, options){
 
         var self = this,
-            txta = self.ta = $(el),
-
-            lastValue,
-            lastLength = -1,
-            changeFn = function(e){
-                var v = txta.value;
-                if( v.length != lastLength || v !== lastValue ){
-                    self.fireEvent('change', e);
-                    lastLength = v.length;
-                    lastValue = v;
-                }
-            };
+            ta = self.ta = document.id(el);
 
         self.setOptions(options);
 
-        txta.addEvents({ change:changeFn, keyup:changeFn });
-
-        //Create shadow div to support pixel measurement of the caret in the textarea
-        //self.taShadow = new Element('div',{
-        //    styles: { position:'absolute', visibility:'hidden', overflow:'auto'/*,top:0, left:0, zIndex:1, white-space:pre-wrap*/ }
-        //})
-        self.taShadow = new Element('div[style=position:absolute;visibility:hidden;overflow:auto]')
-          .inject(txta,'before')
-          .setStyles( txta.getStyles(
-            'font-family0font-size0line-height0text-indent0padding-top0padding-right0padding-bottom0padding-left0border-left-width0border-right-width0border-left-style0border-right-style0white-space0word-wrap'
+        //Create a shadow div to support getCoordinates() of any character in the textarea
+        //This only works if the textarea font is monospace (?)
+        self.taShadow = "div[style=position:absolute;visibility:hidden;overflow:auto]".slick()
+          .setStyles( ta.getStyles(
+            "font-family0font-size0line-height0text-indent0padding-top0padding-right0padding-bottom0padding-left0border-left-width0border-right-width0border-left-style0border-right-style0white-space0word-wrap"
             .split(0)
         ));
 
@@ -79,9 +63,9 @@ var Textarea = new Class({
         the element when passed an instance of the class. (mootools 1.2.x)
 
     Example:
-    >    var txta = new Textarea('textarea-element');
-    >    $('textarea-element') == txta.toElement();
-    >    $('textarea-element') == $(txta); //mootools 1.2.x
+    >    var ta = new Textarea("textarea-element");
+    >    $("textarea-element") == ta.toElement();
+    >    $("textarea-element") == $(ta); //mootools 1.2.x
     */
     toElement: function(){
         return this.ta;
@@ -96,18 +80,22 @@ var Textarea = new Class({
     },
     /*
     Function: slice
-        Mimics the string slice function on the value (text content) of the textarea.
-    Arguments:
-        Ref. javascript slice function
+        Invokes slice(..) on the value of the textarea
     */
-    slice: function(start,end){
-        return this.ta.value.slice(start,end);
+    slice: function(start, end){
+        return this.ta.value.slice(start, end);
+    },
+    /*
+    Function: indexOf
+        Invokes indexOf(..) on the value of the textarea
+    */
+    indexOf: function(searchValue, fromIndex){
+        return this.ta.value.indexOf(searchValue, fromIndex);
     },
-
 
     /*
     Function: getFromStart
-        Returns the first not selected part of the textarea, till the start of the selection.
+        Returns the start of the textarea, upto the start of the selection.
     */
     getFromStart: function(){
         return this.slice( 0, this.getSelectionRange().start );
@@ -115,7 +103,7 @@ var Textarea = new Class({
 
     /*
     Function: getTillEnd
-        Returns the last not selected part of the textarea, starting from the end of the selection.
+        Returns the end of the textarea, starting from the end of the selection.
     */
     getTillEnd: function(){
         return this.slice( this.getSelectionRange().end );
@@ -127,7 +115,7 @@ var Textarea = new Class({
 
     Note:
         IE fixme: this may return any selection, not only selected text in this textarea
-            //if(Browser.Engine.trident) return document.selection.createRange().text;
+        //if(Browser.Engine.trident) return document.selection.createRange().text;
     */
     getSelection: function(){
 
@@ -138,41 +126,42 @@ var Textarea = new Class({
 
     /*
     Function: setSelectionRange
-        Selects the selection range of the textarea from start to end
+        Set a new selection range of the textarea
 
     Arguments:
-        start - start position of the selection
-        end - (optional) end position of the seletion (default == start)
+        start - start position of the new selection
+        end - (optional) end position of the new seletion (default is start)
 
     Returns:
         Textarea object
     */
     setSelectionRange: function(start, end){
 
-        var txta = this.ta,
-            value,diff,range;
+        var ta = this.ta,
+            value, diff, range;
 
-        if(!end){ end = start; }
+        if( !end ){ end = start; }
 
-        if( txta.setSelectionRange ){
+        if( ta.setSelectionRange ){
 
-            txta.setSelectionRange(start, end);
+            ta.setSelectionRange(start, end);
 
         } else {
 
-            value = txta.value;
-            diff = value.slice(start, end - start).replace(/\r/g, '').length;
-            start = value.slice(0, start).replace(/\r/g, '').length;
-
-            range = txta.createTextRange();
-            range.collapse(1 /*true*/);
-            range.moveEnd('character', start + diff);
-            range.moveStart('character', start);
+            value = ta.value;
+            diff = value.slice(start, end - start).replace(/\r/g, "").length;
+            start = value.slice(0, start).replace(/\r/g, "").length;
+
+            range = ta.createTextRange();
+            range.collapse( true );
+            range.moveEnd("character", start + diff);
+            range.moveStart("character", start);
             range.select();
             //textarea.scrollTop = scrollPosition;
             //textarea.focus();
 
         }
+        ta.fireEvent("change");
         return this;
     },
 
@@ -180,60 +169,61 @@ var Textarea = new Class({
     Function: getSelectionRange
         Returns an object describing the textarea selection range.
 
-    Returns:
-        {{ { 'start':number, 'end':number, 'thin':boolean } }}
-        start - coordinate of the selection
-        end - coordinate of the selection
-        thin - boolean indicates whether selection is empty (start==end)
+    Returns: Object:
+        start - (number) start position of the selection
+        end - (number) end position of the selection
+        thin - (boolean) indicates whether selection is empty (start==end)
     */
 
-/* ffs
+    /* ffs
     getIERanges: function(){
         this.ta.focus();
-        var txta = this.ta,
+        var ta = this.ta,
             range = document.selection.createRange(),
             re = this.createTextRange(),
             dupe = re.duplicate();
         re.moveToBookmark(range.getBookmark());
-        dupe.setEndPoint('EndToStart', re);
+        dupe.setEndPoint("EndToStart", re);
         return { start: dupe.text.length, end: dupe.text.length + range.text.length, length: range.text.length, text: range.text };
     },
-*/
+    */
     getSelectionRange: function(){
 
-        var txta = this.ta,
-            caret = { start: 0, end: 0 /*, thin: true*/ },
+        var ta = this.ta,
+            start = 0,
+            end = 0,
             range, dup, value, offset;
 
-        if( txta.selectionStart!=null ){
+        if( ta.selectionStart != null ){
 
-            caret = { start: txta.selectionStart, end: txta.selectionEnd };
+            //caret = { start: ta.selectionStart, end: ta.selectionEnd };
+            start = ta.selectionStart;
+            end = ta.selectionEnd;
 
         } else {
 
             range = document.selection.createRange();
-            //if (!range || range.parentElement() != txta){ return caret; }
-            if ( range && range.parentElement() == txta ){
+
+            if ( range && range.parentElement() == ta ){
                 dup = range.duplicate();
-                value = txta.value;
+                value = ta.value;
                 offset = value.length - value.match(/[\n\r]*$/)[0].length;
 
-                dup.moveToElementText(txta);
-                dup.setEndPoint('StartToEnd', range);
-                caret.end = offset - dup.text.length;
-                dup.setEndPoint('StartToStart', range);
-                caret.start = offset - dup.text.length;
+                dup.moveToElementText(ta);
+                dup.setEndPoint("StartToEnd", range);
+                end = offset - dup.text.length;
+
+                dup.setEndPoint("StartToStart", range);
+                start = offset - dup.text.length;
             }
         }
 
-        caret.thin = (caret.start==caret.end);
-
-        return caret;
+        return { start: start, end: end, thin: start == end };
     },
 
     /*
     Function: setSelection
-        Replaces the selection with a new value (concatenation of arguments).
+        Replaces the selection with a new string (concatenation of arguments).
         On return, the selection is set to the replaced text string.
 
     Arguments:
@@ -244,28 +234,28 @@ var Textarea = new Class({
         Textarea object, with a new selection
 
     Example:
-        > txta.setSelection("new", " value"); //replace selection by 'new value'
+        > ta.setSelection("new", " value"); //replace selection by "new value"
     */
     setSelection: function(){
 
-        var value = Array.from(arguments).join('').replace(/\r/g, ''),
-            txta = this.ta,
-            scrollTop = txta.scrollTop, //cache top
-            start,end,v,range;
-
-        if( txta.selectionStart!=null ){
-
-            start = txta.selectionStart;
-            end = txta.selectionEnd;
-            v = txta.value;
-            //txta.value = v.substr(0, start) + value + v.substr(end);
-            txta.value = v.slice(0, start) + value + v.substr(end);
-            txta.selectionStart = start;
-            txta.selectionEnd = start + value.length;
+        var value = Array.from(arguments).join("").replace(/\r/g, ""),
+            ta = this.ta,
+            scrollTop = ta.scrollTop, //cache top
+            start, end, v, range;
+
+        if( ta.selectionStart != null ){
+
+            start = ta.selectionStart;
+            end = ta.selectionEnd;
+            v = ta.value;
+            //ta.value = v.substr(0, start) + value + v.substr(end);
+            ta.value = v.slice(0, start) + value + v.substr(end);
+            ta.selectionStart = start;
+            ta.selectionEnd = start + value.length;
 
         } else {
 
-            txta.focus();
+            ta.focus();
             range = document.selection.createRange();
             range.text = value;
             range.collapse(1 /*true*/);
@@ -273,9 +263,9 @@ var Textarea = new Class({
             range.select();
 
         }
-        txta.focus();
-        txta.scrollTop = scrollTop;
-        txta.fireEvent('change');
+        ta.focus();
+        ta.scrollTop = scrollTop;
+        ta.fireEvent("change");
         return this;
 
     },
@@ -292,7 +282,7 @@ var Textarea = new Class({
     */
     insertAfter: function(){
 
-        var value = Array.from(arguments).join('');
+        var value = Array.from(arguments).join("");
 
         return this.setSelection( value )
             .setSelectionRange( this.getSelectionRange().start + value.length );
@@ -301,12 +291,26 @@ var Textarea = new Class({
 
     /*
     Function: isCaretAtStartOfLine
-        Returns boolean indicating whether caret is at the start of a line.
+        Returns boolean indicating whether caret (or start of the selection)
+        is at the start of a line.
+        (previous char is \n)
     */
     isCaretAtStartOfLine: function(){
 
-        var i = this.getSelectionRange().start;
-        return( (i<1) || ( this.ta.value.charAt( i-1 ).test( /[\n\r]/ ) ) );
+        var start = this.getSelectionRange().start;
+        return ( (start < 1) || ( this.ta.value.charAt(start - 1).test( /[\n\r]/ ) ) );
+
+    },
+    /*
+    Function: isCaretAtEndOfLine
+        Returns boolean indicating whether the caret or the end of the selection
+        is at the end of a line.
+        (last char is \n)
+    */
+    isCaretAtEndOfLine: function(){
+
+        var end = this.getSelectionRange().end;
+        return ( (end == this.ta.value.length) || ( this.slice(end - 1, end + 1).test( /[\n\r]/ ) ) );
 
     },
 
@@ -327,34 +331,29 @@ var Textarea = new Class({
      */
     getCoordinates: function( offset ){
 
-        var txta = this.ta,
-            taShadow = this.taShadow,
-            delta = 0,
-            el,css,style,t,l,w,h;
-
-        //prepare taShadow
-        css = txta.getStyles(['padding-left','padding-right','border-left-width','border-right-width']);
-        for(style in css){ delta +=css[style].toInt() }
+        var ta = this.ta,
+            //make sure the shadow element is always just before of the textarea
+            taShadow = this.taShadow.inject(ta, "before"),
+            value = ta.value,
+            el, t, l, w, h;
 
-        //default offset is the position of the caret
+        //default character offset is the caret (cursor or begin of the selection)
         if( !offset ){ offset = this.getSelectionRange().end; }
 
         el = taShadow.set({
             styles: {
-                width: txta.offsetWidth - delta,
-                height: txta.getStyle('height')  //ensure proper handling of scrollbars - if any
+                width: ta.offsetWidth,
+                height: ta.getStyle("height")  //ensure proper handling of scrollbars - if any
             },
-            //FIXME: should we put the full selection inside the <i></i> bracket ? (iso a single char)
-            html: txta.value.slice(0, offset) + '<i>A</i>' + txta.value.slice(offset+1)
-        }).getElement('i');
+            html: value.slice(0, offset) + "<i>A</i>" + value.slice(offset + 1)
+        }).getElement("i");
 
-        t = txta.offsetTop + el.offsetTop - txta.scrollTop;
-        l = txta.offsetLeft + el.offsetLeft - txta.scrollLeft;
+        t = ta.offsetTop + el.offsetTop - ta.scrollTop;
+        l = ta.offsetLeft + el.offsetLeft - ta.scrollLeft;
         w = el.offsetWidth;
         h = el.offsetHeight;
-        return {top:t, left:l, width:w, height:h, right:l+w, bottom:t+h}
+        return { top: t, left: l, width: w, height: h, right: l + w, bottom: t + h };
 
     }
 
 });
-

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Tips.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Tips.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Tips.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/moo-extend/Tips.js Mon Jun 22 20:11:42 2015
@@ -27,16 +27,16 @@ Bootstrap
     //tip anchors
     <element> Caption
         <body> ...body... </body>
-    </element>        
+    </element>
 
     //layout of the tip, with absolute position
     div.tooltip(.active)(.top|.left|.right|.bottom)
         div.tooltip-inner
-            <body> ... </body>                        
+            <body> ... </body>
         div.tooltip-arrow
 (end)
 */
-var Tips = function Tips(elements,options){
+var Tips = function Tips(elements){
 
         var tt = 'div.tooltip',
             TheTip = [tt,[tt+'-inner'/*,tt+'-arrow'*/]].slick().inject(document.body),
@@ -44,50 +44,49 @@ var Tips = function Tips(elements,option
 
         $$(elements).addEvents({
 
-        	mousemove: function(e){
-		        TheTip.setStyles({ top:e.page.y +10, left:e.page.x + 10 });
-        	},
-
-        	mouseenter: function(e){
-		        inner.adopt( this.getFirst() ) ;
-	    	    TheTip.addClass('in'); //.fade('in');
-    	    },
-			
-        	mouseleave: function(e){
-		        TheTip.removeClass('in'); //.fade('out');
-        	    this.adopt( inner.getFirst() );
-        	}
+            mousemove: function(e){
+                TheTip.setStyles({ top:e.page.y +10, left:e.page.x + 10 });
+            },
+
+            mouseenter: function(){
+                inner.adopt( this.getFirst() ) ;
+                TheTip.addClass('in'); //.fade('in');
+            },
+
+            mouseleave: function(){
+                TheTip.removeClass('in'); //.fade('out');
+                this.adopt( inner.getFirst() );
+            }
         });
-}
-
+};
 
 
 /*TIP position logic
-	position: function(event){
-		
-		var windowPadding={x:0, y:0};
+    position: function(event){
 
-		var size = window.getSize(), 
-		    scroll = window.getScroll(),
-			tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight},
-			props = {x: 'left', y: 'top'},
-			bounds = {y: false, x2: false, y2: false, x: false},
-			obj = {};
+        var windowPadding={x:0, y:0};
 
-		for (var z in props){
+        var size = window.getSize(),
+            scroll = window.getScroll(),
+            tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight},
+            props = {x: 'left', y: 'top'},
+            bounds = {y: false, x2: false, y2: false, x: false},
+            obj = {};
 
-			obj[props[z]] = event.page[z] + this.options.offset[z];
+        for (var z in props){
 
-			if (obj[props[z]] < 0) bounds[z] = true;
+            obj[props[z]] = event.page[z] + this.options.offset[z];
 
-			if ((obj[props[z]] + tip[z] - scroll[z]) > size[z] - windowPadding[z]){
+            if (obj[props[z]] < 0) bounds[z] = true;
 
-				obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z];
-				bounds[z+'2'] = true;
-			}
-		}
+            if ((obj[props[z]] + tip[z] - scroll[z]) > size[z] - windowPadding[z]){
 
-		this.tip.setStyles(obj);
-	},
+                obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z];
+                bounds[z+'2'] = true;
+            }
+        }
 
-*/
+        this.tip.setStyles(obj);
+    },
+
+*/
\ No newline at end of file

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Snipe.Commands.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Snipe.Commands.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Snipe.Commands.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Snipe.Commands.js Mon Jun 22 20:11:42 2015
@@ -1,4 +1,4 @@
-/*!
+/*
     JSPWiki - a JSP-based WikiWiki clone.
 
     Licensed to the Apache Software Foundation (ASF) under one
@@ -32,7 +32,7 @@ Class: SnipEditor.Commands
     - click events on command buttons (.cmd.pop)
     - suggestion trigger (exec ???)
 
-    FYI - all DIALOGs are created as descendants of the Dialog class.
+    DIALOGs are created as descendants of the Dialog class.
     - Dialog : floating dialog panel
         - FormDialog : predef content with open/close handlers ???
         - Dialog.Selection : selectable list of items
@@ -44,16 +44,16 @@ Class: SnipEditor.Commands
 
 Options:
     container: DOM element  => contains commands([data-cmd])
-    dialogs - predefined set of dialog initialisators
+    dialogs - predefined set of dialog definitions
     // **event handlers**
-    onOpen - invoked after opening a DIALOG
-    onClose - invoked after closing a DIALOG
+    onOpen - invoked after opening any DIALOG
+    onClose - invoked after closing any DIALOG
     onAction - action call-back action(cmd,arguments)
 
 Properties
     - buttons : collection of DOM-elements with click handlers to either
         action() or toggle()
-    - dialogs : collection of dialog definitions [Dialog-class, {dialog parameters}]
+    - dialogs : collection of dialog definitions [Dialog-class, {options}]
 
 DOM structure:
     <div class="cmd tICON"><i>action command</i></div>
@@ -66,105 +66,111 @@ Snipe.Commands = new Class({
     Implements: [Events, Options],
 
     options: {
-        //onAction:function()...s
-        cmds:'cmd' //toolbar button data attribute
+        //onAction:function(command,value){ .. },
+        cmds: "data-cmd" //toolbar button data attribute
+        //relativeTo: document.body //default position of a dialog
         //dialogs:{ cmd1:dialog1, cmd2:dialog2, ...}
     },
-    btns: {},     //all cmd:buttons (dom element)
-    dlgs: {},     //all cmd:instantiated dialogs  (lazy activation)
-    dialogs: {},  //all cmd:dialogs
+    dlgs: {},  //all cmd:instantiated dialogs  (lazy activation)
+    btns: {},  //all buttons
+    dialogs: {},  //all cmd:dialogs  definitions
 
     initialize: function( container, options ){
 
-        var self = this.setOptions(options), 
-            attr = 'data-' + self.options.cmds,
-            command, 
+        var self = this.setOptions(options),
+            dataCmd = self.options.cmds,
+            command,
             dialog,
-            dialogs = options.dialogs||{};
+            dialogs = options.dialogs || {};
+
 
-        container.getElements('['+attr+']').each( function(el){
+        //add click buttons and dialogs
+        container.addEvent("click:relay([" + dataCmd + "])", function(event){
 
-            command = el.get(attr);
+            var cmd = this.get( dataCmd ),
+                dlg = self.dlgs[ cmd ];
 
-            self.btns[command] = el.addEvent('click', self.click.pass(command,self));
+            dlg ? dlg.toggle() : self.action( cmd );
 
-            if( dialog = container.getElement('.dialog.' + command) ){
-            
-                if( dialogs[command] ){
+            // input fields (eg checkboxes) keep the default behaviour; other click events are disabled
+            if( !this.match("input") ){ event.stop(); }
+
+        });
 
-                    dialogs[command][1].dialog = dialog;  
+        //see if there are any dialogs linked to a button. Eg: "div.dialog.<command>"
+        container.getElements("[" + dataCmd + "]").each( function(button){
 
-                } else {
+            command = button.get(dataCmd);
+            self.btns[command] = button;
 
-                    dialogs[command] = dialog;
+            if( dialog = container.getElement(".dialog." + command) ){
+
+                if( !dialogs[command] ){ dialogs[command] = [Dialog, {}]; }
+
+                options = dialogs[command][1];
+                //register the DOM dialog element, and move to top of DOM for proper absolute positioning
+                options.dialog = dialog.inject(document.body);
+                options.relativeTo = button;  //register the dialog positioning info
 
-                }
             }
         });
 
         self.addDialogs( dialogs );
+
     },
 
     /*
-    Funciton: addDialog
-        Add a new dialog.
-        The dialog is only created when invoking the command.
-        This happens through a button click or through the action() method.
+    Function: addDialog
+        Add a new dialogs.
 
     Arguments:
-        dialogs: {cmd1:dialog, cmd2:dialog-def...}
-        (dialog-def : array[Dialog-Class, {dialog parameters}]
-        relativeTo: create a dialog relative to a positioned object (eg. button, textarea-location)
+        newdialogs: {cmd:[Dialog-Class, {options}]..}
     */
-    addDialogs: function(newdialogs, relativeTo){
+    addDialogs: function( newdialogs ){
+
+        var dialog,
+            command,
+            dialogs = this.dialogs;
 
-        var self = this,
-            dialog,
-            command
-            dialogs = self.dialogs;
-        
         for( command in newdialogs ){
 
-            if( dialogs && dialogs[command] ){
-                console.log("AddDialogs - warning: double registration of => " + command);
+            if( dialogs[command] ){
+                console.log("Snipe.Commands addDialogs() - warning: double registration of => " + command);
             }
 
-            dialog = dialogs[command] = newdialogs[command];  //array of all dialogs
-            if( instanceOf( dialog, Dialog ) ){ self.attach(command,dialog); }
+            dialog = dialogs[command] = newdialogs[command];
 
-            //checkme ...
-            if( relativeTo ){ self.btns[ command ] = relativeTo; }
+            //note: make sure to initialize this.dialogs[command] prior to calling show()
+            if( instanceOf( dialog, Dialog ) ){ this.attach(dialog, command); }
 
-        };
-        //console.log('allDialogs: '+Object.keys(self.dialogs) );
+        }
+        //console.log("allDialogs: " + Object.keys(this.dialogs) );
     },
 
-    attach: function(command, dialog){
+    /*
+    Function: attach
+        Attach event-handlers to a dialogs
+    */
+    attach: function(dialog, command){
 
         var self = this,
-            actionHdl = function(v){ self.fireEvent('action', [command,v]); };
+            //fire ACTION event back to the invoker of the Snipe.Commands
+            actionHdl = function(value){ self.fireEvent("action", [command, value]); };
 
-        //console.log('attachDialog: '+command);
+        console.log("Snipe.Commands: attachDialog() ", command, dialog);
 
         return self.dlgs[command] = dialog.addEvents({
-            onOpen: self.openDialog.bind(self, command),
-            onClose: self.closeDialog.bind(self, command),
-            onAction: actionHdl,
-            onDrag: actionHdl
+            open: self.openDialog.bind(self, command),
+            close: self.closeDialog.bind(self, command),
+            action: actionHdl,
+            drag: actionHdl
         });
     },
 
-    click: function( command ){
-
-        var dialog = this.dlgs[ command ];
-        dialog ? dialog.toggle() : this.action( command );
-
-    },
-
     /*
     Function: action
-        Action handler for a simple command. Pass the 'action' event
-        up to the Snipe Editor.
+        Action handler for a simple command.
+        Send the "action" event back to the Snipe Editor.
 
     Arguments:
         command : command name
@@ -172,28 +178,28 @@ Snipe.Commands = new Class({
     */
     action: function( command, value ){
 
-        var self = this, 
-            active = 'active',
-            button = self.btns[command], 
-            dialog = self.dlgs[command];
+        var self = this,
+            active = "active",
+            button = self.btns[command],
+            dialog;
 
-        //console.log("Commands.action "+command+" value:"+value+" btn="+button+ " dlg="+dialog);
-        if( button ) button = document.id( button);
+        //console.log("Commands.action ", command, " value:", value, " btn=", button, " dlg=", dialog);
+        //if( button ) button = document.id( button);
 
-        if( button && button.match('.disabled') ){
+        if( button && button.match(".disabled") ){
 
-            //nothing to do here
+            //nothing to be done here
 
         } else if( self.dialogs[command] ){
 
-            if( !dialog ){ dialog = self.createDialog(command) }
-            if( value ){ dialog.setValue( value ); }
+            dialog = self.dlgs[command] || self.createDialog(command);
+            if( value != null ){ dialog.setValue( value ); }
             dialog.show();
 
         } else {
 
             if( button ){ button.addClass(active); }
-            self.fireEvent('action', [command, value] );
+            self.fireEvent("action", [command, value] );
             if( button ){ button.removeClass(active); }
 
         }
@@ -202,115 +208,97 @@ Snipe.Commands = new Class({
 
     /*
     Function: createDialog
-        Create a new dialog.
-        The name of the cmd determines the type (or class) of Dialog to be created
-        - cmd: Dialog[cmd] (eg cmd='font' : Dialog.Font)
-        - the name of the dialog equals the DOM ID of a predefined HTML dialog
-
-        - DOM Element: predefined DOM dialog
-        - [ Dialog-class, { dialog parameters } ]
-        - { dialog parameters } : the cmd determines the type of Dialog to create
-        - "string" : create a Dialog.Selection dialog
+        Create a new dialog, based on dialog creation parameters in this.dlgs :
+        - [ Dialog-class, { options } ]
+        - otherwise convert to Dialog.Selection dialog
 
 
     Arguments
-        cmd - (string) command
+        command - (string) command
 
         The open/close handlers will make sure only one dialog is open at the
-        same time. The open dialog is stored in {{this.activeCmd}}.
+        same time. The open dialog is stored in {{this.activecommand}}.
 
-        The key events 'action', 'drag', 'open' and 'close' are propagated upwards.
+        The key events "action", "drag", "open" and "close" are propagated upwards.
 
     Returns:
         The created dialog, which is also stored in this.dlgs[] repository.
 
     */
-    createDialog: function( cmd ){
+    createDialog: function( command ){
 
-        var self = this,
-            dlg,
-            btn = self.btns[cmd],
-            factory = Function.from( self.dialogs[cmd] )(),
-            type = typeOf(factory);
+        var dialog = Function.from( this.dialogs[command] )();
 
-        //console.log('Commands.createDialog() '+cmd+' '+ ' btn='+btn +" "+type);
+        //console.log("Snipe.Commands: createDialog() " + command + " ",dialog );
 
-        //expect factory to be [Dialog class,  {dialog options object}]
-        if( type != 'array' || factory.length != 2 ){
+        if( typeOf(dialog) != "array" ){ dialog = [ Dialog.Selection, { body: dialog } ]; }
 
-            factory = ( type == 'element' ) ?
-                [Dialog, {dialog:factory}] : [Dialog.Selection, {body:factory}];
-        }
+        if( !dialog[1].relativeTo ){ dialog[1].relativeTo = this.options.relativeTo || document.body; }
 
-        dlg = new factory[0]( Object.append( factory[1],{
-            //cssClass: 'dialog float',
-            autoClose: false, //fixme: suggestion dialog should not be autoclosed
-            relativeTo: btn   //button or textareaa
-            //draggable: true
-        }) );
+        dialog[1].autoClose = false; //checkme: suggest-dialogs should not be autoclose?
 
-        //Make sure that this.dlgs[cmd] gets initialized prior to calling show()
-        return self.attach(cmd, dlg);
+        //note: make sure to initialize this.dialogs[command] prior to calling show()
+        return this.attach( new dialog[0]( dialog[1] ), command);
     },
 
     /*
     Function: openDialog
         Opens a dialog. If already another dialog was open, that one will first be closed.
-        When a toolbar button exists, it will get the css class '.active'.
+        When a toolbar button exists, it will get the css class ".active".
 
         Note: make sure that this.dlgs[cmd] is initialized prior to calling show() !
 
     Argument:
         command - dialog to be opened
-        preOpen - ...
     */
-    openDialog: function(command, dialog){
+    openDialog: function(command){
 
-        var self = this, 
-            current = self.activeCmd, 
-            tmp;
+        var self = this,
+            activeDlg = self.activeDlg,
+            newDlg = self.dlgs[command],
+            button = self.btns[command];
 
-        //console.log('Commands.openDialog() ' + command + ' ' + self.activeCmd );
+        //console.log("Snipe.Commands: openDialog() " + command + " " + activeDlg);
 
-        if( ( current!=command ) && ( tmp = self.dlgs[current] ) ){ tmp.hide(); }
-        //toobar button will be deactivated by closeDialog()
+        if( activeDlg && (activeDlg != newDlg) ){ activeDlg.hide(); }
+        self.activeDlg = self.dlgs[command];
 
-        self.activeCmd = command;
-        if( tmp = self.btns[command] ){ $(tmp).addClass('active'); }
+        if( button ){ button.addClass("active"); }
+
+        self.fireEvent("open", command);
 
-        self.fireEvent('open', command, dialog);
     },
 
     /*
     Function: closeDialog
 
     Arguments:
-        cmd - (mandatory) dialog to be closed
+        command - (mandatory) dialog to be closed
     */
-    closeDialog: function(cmd, dialog){
+    closeDialog: function(command, dialog){
+
+        var self = this,
+            button = self.btns[command];
 
-        var self = this, 
-            btn = self.btns[cmd];
+        //console.log("Snipe.Commands: closeDialog() " + command )
 
-        //console.log('Commands.closeDialog() ' + cmd + ' ' + self.activeCmd )
+        if( self.dlgs[command] == self.activeDlg ){ self.activeDlg = null; }
 
-        if( cmd == self.activeCmd ){ self.activeCmd = null; }
-        if( btn ){ $(btn).removeClass('active'); }
+        if( button ){ button.removeClass("active"); }
+
+        self.fireEvent("close", command);
 
-        self.fireEvent('close', cmd, dialog);
     },
 
     /*
     Function: close
-        Close the active dialog, if any.
+        Close any active dialog.
     */
     close: function(){
 
-        var activeCmd = this.activeCmd;
-        if( activeCmd ){ this.dlgs[activeCmd].hide(); }
+        var activeDlg = this.activeDlg;
+        if( activeDlg ){ activeDlg.hide(); }
 
     }
 
-});
-
-
+});
\ No newline at end of file

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Snipe.Sections.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Snipe.Sections.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Snipe.Sections.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Snipe.Sections.js Mon Jun 22 20:11:42 2015
@@ -1,6 +1,5 @@
-/*!
+/*
     JSPWiki - a JSP-based WikiWiki clone.
-
     Licensed to the Apache Software Foundation (ASF) under one
     or more contributor license agreements.  See the NOTICE file
     distributed with this work for additional information
@@ -8,9 +7,7 @@
     to you under the Apache License, Version 2.0 (the
     "License"); fyou may not use this file except in compliance
     with the License.  You may obtain a copy of the License at
-
        http://www.apache.org/licenses/LICENSE-2.0
-
     Unless required by applicable law or agreed to in writing,
     software distributed under the License is distributed on an
     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -21,14 +18,13 @@
 /*
 Class: SnipEditor.Sections
     This dialog displays the list of page sections.
-    A page section includes the header
 
     (all) - allows to select all sections (auto generated)
     start-of-page - only present when first section starts on an offset > 0
     section1..n - section titles, with indentation level depending on their weight
 
     The set of sections is generated by the parseSections() callback handler.
-    This parser returns an array of section 'descriptors':
+    This parser returns an array of section "descriptors":
 >    [ {title:text, start:char-offset, indent:indentation-level}, ... ]
 
     Clicking an entry triggers the updateSections() callback handler.
@@ -37,39 +33,58 @@ Class: SnipEditor.Sections
 Depends:
     Snipe
 
+Example:
+(start code)
+    div.cage
+      div.btn.btn-link
+        span.icon-bookmark
+            span.caret
+      ul.dropdown-menu [data-sections="div"][data-hover-parent=".cage"]
+        li  a first
+        li  a ..
+        li  a.dropdown-divider
+        li  a ..
+
+
+    new Snipe.Sections( sectionDropDown, {
+        snipe: snipe,
+        parser: function(text){ .. return [[{title, start, depth}],..]; }
+    });
+(end)
 */
 
 Snipe.Sections = new Class({
 
     Implements: [Events],
-    Binds: ['show','update','action'],
-    
+    Binds: ["show","update","action"],
+
     options: {
         //snipe:snip-editor
-        //parser: function
+        //parser: function(text){ returns [[title,start,depth]..] }
         all: "( all )".localize(),
         startOfPage: "Start of Page".localize()
     },
 
     initialize: function(element, options){
 
-        var self = this;
+        var self = this,
+            snipe = options.snipe;
 
-        self.element = element  //dropdown menu
-            .onHover( self.container = element.get('data-sections'), self.show )
-            .addEvent( 'click:relay(a)', self.action );
-        self.container =  element.getParent( element.get('data-sections') );
-        
+        self.container = element;
         self.parser = options.parser;
-        self.main = options.snipe.get('mainarea');
-        self.work = options.snipe.toElement().addEvents({ 
-            keyup: self.update, 
-            change: self.update 
+
+        self.list = element.getElement("ul").addEvent("click:relay(a)", self.action);
+
+        self.main = snipe.get("mainarea");
+        self.work = $(snipe.get("textarea")).addEvents({
+            keyup: self.update,
+            change: self.update
         });
 
         self.parse();
-        self.action( location.search );  //url?section=0..n 
+        self.action( location.search );  //url?section=0..n
         self.show();
+
     },
 
     /*
@@ -88,9 +103,10 @@ Snipe.Sections = new Class({
     },
 
     /*
-    Function: onOpen
-        UPDATE/RFEFRESH the textarea section dialog.
-        Build the DOM list-items 
+    Function: show
+        UPDATE/RFEFRESH the section dropdown-menu.
+        Highlight the current item.
+
     (start code)
         ul.dropdown-menu
             li
@@ -104,96 +120,95 @@ Snipe.Sections = new Class({
                 a.indent-0.section1 Title-Section-1
             ...
             li
-                a.indent-0.section99 Title-Section-2                
-    (end)        
+                a.indent-0.section99 Title-Section-2
+    (end)
     */
-    //onOpen: function( dialog ){
     show: function( ){
 
-        var options = this.options,
-            data = [],
+        //console.log("Sections show",this.current, this.sections.length, this.sections[3]);
+
+        var data = [],
+            options = this.options,
+            current = this.current,
             sections = this.sections,
-            
+
             addItem = function(indent,name,offset){
-                data.push('li',['a.indent-'+indent+'.section'+offset,{ html:name }]);
-            }
+                data.push("li" + (offset==current ? ".active" : ""),[
+                    "a.indent-" + indent + ".section" + offset, { html:name }
+                ]);
+            };
 
         addItem(0, options.all ,-2);
-        
+
         if( sections[0] ){
-        
+
             if( sections[0].start > 0 ){ addItem(0, options.startOfPage, -1); }
 
-            data.push('li.divider');
-            
-            sections.each( function(item, idx){ 
-                addItem( item.depth, item.title.trunc(36), idx );
+            data.push( "li.divider" );
+
+            sections.each( function(item, idx){
+
+                addItem( item.depth, item.title/*.trunc(36)*/, idx );
+
             });
 
         }
-        
-        this.element.empty().adopt( data.slick() );
+
+        this.list.empty().adopt( data.slick() );
 
     },
 
     /*
     Function: update
         Make sure that changes to the work textarea are propagated to the main textarea.
-        This functions handles the correct insertion of the changed section into the main
-        textarea.
+        This function handles the propagation of changes into the main textarea.
     */
     update: function(){
 
-        //console.log("Sections: update");
         var self = this,
             main = self.main,
             work = self.work.value,
-            sections = self.sections,
-            s = main.value,
-            //insert \n to ensure the next section always starts on a new line.
-            linefeed = (work.slice(-1) != '\n')  ? '\n' : '';
+            s = main.value;
+
+        //insert \n to ensure the next section always starts on a new line.
+        if( work.slice(-1) != "\n" ){ work +="\n"; }
+
+        //console.log("change txta: from="+self.begin+ " end="+self.end);
+        main.value = s.slice(0, self.begin) + work  + s.slice(self.end);
 
-        //console.log('change txta: from='+sections.begin+ ' end='+sections.end);
-        main.value = s.slice(0, self.begin) + work + linefeed + s.slice(self.end);
         self.end = self.begin + work.length;
 
         self.parse();
+
     },
 
     /*
-    Function: onAction
+    Function: action
         This function copies the selected section from the main to the work textarea.
         It is invoked at initialization and through the dialog onAction click handler.
 
     Arguments:
         item - index of selected section: all, -1, 0..n
     */
-    //onAction:function( item ){
-
-    /*
-    setValue: function(value){
-    },
-    action: function(item){
-        var value = item.get('title');
-        this.setValue(value).parent(value);
-    },
-    */
     action:function( item ){
 
+        //console.log("Sections: action",item);
         var self = this,
             main = self.main.value,
+            work = self.work,
             sections = self.sections,
             begin = 0,
             end = main.length;
 
         if( item ){
-        
+
             //item.target => event.target; this is an onclick invocation
-            if( item.target ) item = item.target.className;
-            
+            if( item.target ){ item = item.target.className; }
+
             //section-2=All, section-1=StartOfPage, section0..section99=rest
             item = ( item.match( /section=?(-?\d+)/ )||[,-2])[1].toInt();
-            
+
+
             if( item == -1 ){
 
                 //show the Start Of Page, prior to the first real section
@@ -202,18 +217,29 @@ Snipe.Sections = new Class({
             } else if(item >= 0  && sections[item] ){
 
                 begin = sections[item].start;
-                //if( item+1 < sections.length ){ end = sections[item+1].start; }
                 if( sections[item+1] ){ end = sections[item+1].start; }
 
             }
+
+            self.current = item;
+
         }
 
-        self.work.value = main.slice(begin, end);
+        work.value = "";  //FIXME google chrome  43.0.2357.65 bug -- if omitted, the textarea is displayed EMPTY!! ??
+        work.value = main.slice(begin, end);
         self.begin = begin;
         self.end = end;
 
-        //now close the hover menu and focus the text-area...
-        self.container.removeClass('open');
-        self.container.ifClass( item >= -1, 'section-selected');
+        //section-selected class : turn bookmark icon red or blue
+        self.container.ifClass( item >= -1, "section-selected");
+
+        //update the dropdown menu, and highlight the current item
+        self.show();
+
+        work.fireEvent("change");  //needed to rerun page preview
+
+        return false; //stop click event propagation
+
     }
-});
+
+});
\ No newline at end of file

Added: jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Snipe.Snips.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Snipe.Snips.js?rev=1686927&view=auto
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Snipe.Snips.js (added)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Snipe.Snips.js Mon Jun 22 20:11:42 2015
@@ -0,0 +1,394 @@
+/*
+    JSPWiki - a JSP-based WikiWiki clone.
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); fyou may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+       http://www.apache.org/licenses/LICENSE-2.0
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+/*
+Snippet:
+    init - initialize snippets; detect shortcut-keys, and suggest dialogs
+    get - retrieve and validate the snippet. Return false when not found.
+    inScope - check whether a snippet is inScope at the current cursor position
+    toggle - ...
+
+
+    suggest - match suggestion dialog => fireEvent(action,cmd)
+    shortcut -  match key => fireEvent(action,cmd)
+
+Example
+    var sn = new Snipe.Snips(textarea, { snippets... } });
+    sn.dialogs => snippet dialogs
+    sn.suggest => suggest snippets
+    sn.keys => shortcut keys
+
+*/
+
+Snipe.Snips = new Class({
+
+    Implements: Events,
+
+    initialize: function( workarea, snips ){
+
+        var self = this,
+            cmd, snip, key, suggest,
+            //shortcut = ((navigator.userAgent.indexOf("Mac OS X")!=-1) ? "meta+" : "control+");
+            //control = (Browser.platform == "mac" ? "meta+" : "control+");
+            control = ( navigator.platform.match( /mac/i ) ? "meta+" : "control+");
+
+
+
+        self.workarea = workarea;  //Textarea class
+        self.snips = snips;
+
+        self.keys = {};
+        self.dialogs = {};
+        self.suggestions = {};
+
+        for( cmd in snips ){
+
+            snip = Function.from( snips[cmd] )( workarea, cmd );
+
+            // short format of snip
+            if( typeOf(snip)=="string" ){ snip = { snippet:snip }; }
+
+            //Not needed sofar: Function.from( snip.initialize )(cmd, snip);
+
+            if( key = snip.key ){
+
+                // key:"f" => key:"control+f" ;  key:"shift+enter" (no change)
+                if( !key.contains("+") ){ key = control + key; }
+                self.keys[ key.toLowerCase() ] = cmd;
+                snip.key = key;
+
+            }
+
+            if( suggest = snip.suggest ){
+
+                if( typeOf(suggest)== "string" ){
+
+                    snip.suggest = {
+                        pfx: RegExp( suggest + "$" ),
+                        match: RegExp( "^" + suggest )
+                    }
+                    //console.log( snip.suggest );
+                }
+                self.suggestions[cmd] = snip;
+
+            }
+
+            //check for snip dialogs -- they have the same name as the command
+            //EG:  find: { find:<this is a snip dialog> }
+            if( snip[cmd] ){ self.dialogs[cmd] = snip[cmd]; }
+
+            snips[cmd] = snip;
+
+        }
+        //console.log(this.keys);
+
+    },
+
+    /*
+    Function: match
+        Lookup a cmd entered just in front of the caret/cursor of the workarea.
+    */
+    match: function(){
+
+        var cmd, fromStart = this.workarea.getFromStart();
+
+        for( cmd in this.snips ){
+            if( fromStart.test( cmd+"$" ) ) return cmd;
+        }
+
+        return false;
+    },
+
+    /*
+    Function: matchSuggest
+        Lookup a cmd enter just in front of the caret/cursor of the workarea..
+
+        snip.suggest => {
+            start: <pos>,
+            match: <match-string-before-caret>,
+            tail:  <length-match-after-caret>
+        }
+    */
+    matchSuggest: function(){
+
+        var cmd, snip, pfx, result, suggest,
+            snips = this.suggestions,
+            workarea = this.workarea,
+            caret = workarea.getSelectionRange(),
+            fromStart = workarea.getFromStart();
+
+        for( cmd in snips ){
+
+            snip = snips[cmd];
+            suggest = snip.suggest;
+
+            if( this.inScope(snip, fromStart) ){
+
+                if( suggest.pfx ){
+
+                    pfx = fromStart.match( suggest.pfx );
+
+                    if( pfx ){
+
+                        console.log("SUGGEST Prefix ", cmd, suggest.pfx, pfx.getLast() );
+                        pfx = pfx.getLast(); //match last (x)
+                        result = workarea.slice( caret.start - pfx.length )
+                                        .match( suggest.match );
+
+                        console.log("SUGGEST Match ", suggest.match, result );
+
+                        if( result ){
+                            result = { pfx:pfx, match:result.getLast() } ;
+                        }
+
+                    }
+
+                } else {
+
+                    result = Function.from(suggest)(workarea, caret, fromStart);
+
+                }
+
+                if( result ){
+
+                    result.cmd = cmd;
+                    return result;
+
+                }
+            }
+        }
+        return false;
+
+    },
+
+    /*
+    Function: get
+        Retrieve and validate the snippet.
+        Returns false when the snippet is not found or not in scope.
+
+
+    Arguments:
+        snips - snippet collection object for lookup of the cmd
+        cmd - snippet key. If not present, retrieve the cmd from
+            the textarea just to the left of the caret. (i.e. tab-completion)
+
+    Returns:
+        Return a snippet object or false.
+        - false
+        - snippet object
+
+    Example:
+        (start code)
+        sn.get( "bold" );
+
+        returned_object = false || {
+                key: "snippet-key",
+                snippet: " snippet-string ",
+                text: " converted snippet-string, no-parameter braces, auto-indented ",
+                parms: [parm1, parm2, "last-snippet-string" ]
+            }
+        (end)
+    */
+    get: function( cmd ){
+
+        var self = this,
+            txta = self.workarea,
+            fromStart = txta.getFromStart(),
+            snip = self.snips[cmd],
+            parms = [],
+            s,last;
+
+        if( snip && snip.synonym ){ snip = self.snips[snip.synonym]; }
+
+        if( !snip || !self.inScope(snip, fromStart) ){ return false; }
+
+        s = snip.snippet || "";
+
+        //parse snippet {parameters}
+        s = s.replace( /\\?\{([^{}]+)\}/g, function(match, name){
+
+            if( match.charAt(0) == "{" ){
+                parms.push(name);
+                return name;
+            } else {
+                return match.slice(1)
+            }
+
+        }).replace( /\\\{/g, "{" );
+        //and end by replacing all escaped "\{" by real "{" chars
+
+        //also push the last piece of the snippet onto the parms[] array
+        last = parms.getLast();
+        if( last ){ parms.push( s.slice(s.lastIndexOf(last) + last.length) ); }
+
+        //collapse \n of previous line if the snippet starts with \n
+        if( s.test(/^\n/) && ( fromStart.test( /(^|[\n\r]\s*)$/ ) ) ) {
+            s = s.slice(1);
+            //console.log("remove leading \\n");
+        }
+
+        //collapse \n of subsequent line when the snippet ends with a \n
+        if( s.test(/\n$/) && ( txta.getTillEnd().test( /^\s*[\n\r]/ ) ) ) {
+            s = s.slice(0,-1);
+            //console.log("remove trailing \\n");
+        }
+
+        //finally auto-indent the snippet"s internal newlines \n
+        var prevline = fromStart.split(/\r?\n/).pop(),
+            indent = prevline.match(/^\s+/);
+        if( indent ){ s = s.replace( /\n/g, "\n" + indent[0] ); }
+
+        //complete the snip object
+        snip.text = s;
+        snip.parms = parms;
+
+        //console.log("Snipe.Snips:get() ",snip.text, JSON.encode(snip),"***" );
+        return snip;
+    },
+
+    /*
+    Function: inScope
+        Sometimes it is useful to restrict the scope of a snippet, and only allow
+        the snippet expansion in specific parts of the text. The scope parameter allows
+        you to do that by defining start and end delimiting strings.
+        For example, the following "fn" snippet will only expands when it appears
+        inside the scope of a script tag.
+
+        (start code)
+        "fn": {
+            snippet: "function( {args} )\{ \n    {body}\n\}\n",
+            scope: {"<script":"</script"} //should be inside this bracket
+        }
+        (end)
+
+        The opposite is possible too. Use the "nScope" or not-in-scope parameter
+        to make sure the snippet is only inserted when not in scope.
+
+        (start code)
+        "special": {
+            snippet: "{special}",
+            nScope: { "%%(":")" } //should not be inside this bracket
+        },
+        (end)
+
+    Arguments:
+        snip - Snippet Object
+        text - (string) used to check for open scope items
+
+    Returns:
+        True when the snippet is in scope, false otherwise.
+    */
+    inScope: function(snip, text){
+
+        var pattern, pos, scope=snip.scope, nscope=snip.nscope;
+
+        if( scope ){
+
+            if( typeOf(scope)=="function" ){
+
+                return scope( this.textarea );
+
+            } else {
+
+                for( pattern in scope ){
+
+                    pos = text.lastIndexOf(pattern);
+                    if( (pos > -1) && (text.indexOf( scope[pattern], pos ) == -1) ){
+
+                        return true;
+
+                    }
+
+                }
+                return false;
+            }
+        }
+
+        if( nscope ){
+
+            for( pattern in nscope ){
+
+                pos = text.lastIndexOf(pattern);
+                if( (pos > -1) && (text.indexOf( nscope[pattern], pos ) == -1) ){
+
+                    return false;
+
+                }
+
+            }
+
+        }
+        return 1 /*true*/;
+    },
+
+
+    /*
+    Function: toggle
+        Toggle the prefix and suffix of a snippet.
+        Eg. toggle between {{__text__}} and {{text}}.
+        The selection will be matched against the snippet.
+
+    Precondition:
+        - the selection is not empty (caret.thin = false)
+        - the snippet has exatly one {parameter}
+
+    Arguments:
+        txta - Textarea object
+        snip - Snippet object
+        caret - Caret object {start, end, thin}
+
+    Returns:
+        - (string) replacement string for the selection.
+            By default, returns snip.text
+        - the snip.parms will be set to [] is toggle was executed successfully
+        Eventually the selection will be extended if the
+        prefix and suffix were just outside the selection.
+    */
+    toggle: function(txta, snip, caret){
+
+        var s = snip.text,
+            //get the first and last textual parts of the snippet
+            arr = s.trim().split( snip.parms[0] ),
+            fst = arr[0],
+            lst = arr[1],
+            re = new RegExp( "^\\s*" + fst.trim().escapeRegExp() + "\\s*(.*)\\s*" + lst.trim().escapeRegExp() + "\\s*$" );
+
+        if( (fst + lst)!="" ){
+
+            s = txta.getSelection();
+            snip.parms = [];
+
+            // if pfx & sfx (with optional whitespace) are matched: remove them
+            if( s.test(re) ){
+
+                s = s.replace( re, "$1" );
+
+            // if pfx/sfx are matched just outside the selection: extend selection
+            } else if( txta.getFromStart().test(fst.escapeRegExp()+"$") && txta.getTillEnd().test("^"+lst.escapeRegExp()) ){
+
+                txta.setSelectionRange(caret.start-fst.length, caret.end+lst.length);
+
+            // otherwise, insert snippet and set caret between pfx and sfx
+            } else {
+
+                txta.setSelection( fst+lst ).setSelectionRange( caret.start + fst.length );
+            }
+        }
+
+        return s;
+    }
+});
\ No newline at end of file