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

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Wiki.Snips.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Wiki.Snips.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Wiki.Snips.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/wiki-edit/Wiki.Snips.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
@@ -18,11 +15,6 @@
     specific language governing permissions and limitations
     under the License.
 */
-/*
-
-
-
-*/
 
 /*
 DirectSnippet definitions for JSPWiki, aka ''smartpairs''.
@@ -44,10 +36,13 @@ Wiki.DirectSnips = {
 };
 
 /*
-    Function: tabSnippets
-    
+Function: snippets
+
         Definitions for the JSPWiki editor commands.
 
+
+        A command consists of triggers, attributes, snippets, events and dialogs.
+
         Following commands are predefined by the snipe editor:
         - find : toggles the find and replace dialog
         - sections : toggle sections dropdown dialog, which allows to switch
@@ -55,55 +50,57 @@ Wiki.DirectSnips = {
         - undo : undo last command, set the editor to the previous stable state
         - redo : revert last undo command
 
-        A command consists of triggers, attributes, snippets, events and dialogs.
 
-        Triggers : 
-        ========
-        click events, suggestion dialogs, TAB-completion and Ctrl-keys.
+Snippet Triggers :
 
-        Click events are attached to DOM elements with a .cmd css-class.
-        If the DOM element also contains a .pop css-class, a dialog will be opened.
+        Triggers can be click events, TAB-key, suggestion dialogs and shortcut-keys (ctrl+/meta+).
 
+        CLICK event:
+        Click events are attached to DOM elements with a {{data-cmd="cmd"}} attribute.
+
+        TAB key:
+        Enter a command followed by the TAB key.
         TAB-completion can be turned on/off via the 'tabcompletion' flag.
 
-        The 'keyup' event can trigger a suggestion dialog:
+        KEYUP event, may trigger a suggestion dialog:
+        Suggestion dialogs are opened when the cursor is located 'inside' a command keyword.
+        Match function determines a valid suggestion command.
+
         - the suggest(txta,caret) function validates the suggestion context
           It returns true/false and can modify the snippet with
              - snip.start : begin offset of the matched prefix
              - snip.match : matched prefix (string)
              - snip.tail: (optional) replaceable tail
 
-        Attributes :
-        ==========
-        - initialize: function(cmd, snip) called once at initialization
-        - key: shortcut key  (ctrl-key)
-        - scope: set to TRUE when the cmd is valid
-        - nscope: set to TRUE when the cmd is not valid
-        - cmdId: wysiwyg mode only (commandIdentifier)
-        
-        Snippet :
-        =======
-        The snippet contains the inserted or replaced text.
-        - static snippet: "some string"
-        - snippet with parameters in {} brackets: "some {dialog1} string"
-          A {.} will be replaced by the selected text.
-          A {dialog-1} opens a dialog, and inserts the returned info (eg color, selection...)
-        - dynamic snippet: javascript function.
-          Example:
-              snippet: function(){
-                  this.dialogs.exec( dialog-name, ...
-                      onChange: function(value){  }
-                  )
-              }
 
-        Event :
-        =====
+Snippet Attributes :
+        - initialize: function(cmd, snip){ return snippet } called once during initialisation
+        - key: shortcut key  (ctrl-key or meta-key)
+        - scope: returns TRUE when the command appears inside certain start and end pattern
+        - nscope: set to TRUE when the command is not inside certain start and end pattern
+        - cmdId: (wysiwyg mode only) corresponding commandIdentifier
+        - synonym:
+
+Snippet actions
+        Text, Event, Dialog (will be opened prior to insertion of the text)
+
+Snippet Text:
+        The snippet text contains the text to be inserted or replaced.
+        Add '\n' at the start or end of the snippet, if it need to appear on a new line.
+        - a {.description} (start with dot) will be replaced by the selected text, if any
+        - a {parameter} (not dot) will become the selected text AFTER insertion of the snippet
+        The snippet text can also be replace by a javascript function, so any manipulation
+        of the textarea is possible.
+
+Snippet Event :
+
         Fires an event back to the invoking Object (Wiki.Edit in our case)
         Example:
             smartpairs: { event: 'config' }
 
-        Dialogs :
-        =======
+Snippet dialog:
+
+        Snippet dialog carry the name of the command.
         (btw -- you do use unique names, do you?)
         - <dialog-name>: [ Dialog.SubClass, {dialog-parameters, event-handlers} ]
         - <dialog-name>: "dialog initialization string"
@@ -113,330 +110,476 @@ Wiki.DirectSnips = {
         The Dialog Classes are subclass of Dialog. (eg. Dialog.Selection)
 
 
-    Examples:
+Examples:
+
+    bold: '__{.bold}__'
+    bold: { snippet: '__{.bold}__' }
 
-     acl: {
-         nscope: { "[{" : "}]" },
-         snippet: "[\\{ ALLOW {permission} {principal(s)}  }]"
+    toc: { snippet: '\n[{TableOfContents}]\n' }
 
-         permission: "view|edit|delete",
-         "principals(s)": [Dialog.Selection, {
+    newline: {
+        key:'shift+enter',
+        snippet: '\\\\\n'
+    }
+    br: { synonym: 'newline' }
+
+
+    acl: {
+        nscope: { "[{" : "}]" },
+        snippet: "[{ALLOW {permission} {principal}  }]"
+     },
+     permission: {
+        scope: { "[{ALLOW" : "}]" },
+        suggest: 'ALLOW\s+(\w+)',
+        permission: "view|edit|delete"  //selection dialog
+     },
+     principal: {
+        scope: { "[{ALLOW" : "}]" },
+        suggest: 'ALLOW\s+\w+\s+(\w+)',
+        principals: [Dialog.Selection, {
              onOpen: function(){ this.setBody( AJAX-request list of principals ); }
-         ]
-     }
+        ]
+     },
 
-     link: {
-         suggest: function(){
-             //match [, but not [[ or [{
-             //defines .start, .selection, .trail ??
-         }
-         snippet: "{wikiLink}",
-         //or snippet: function(){ this.dialogs.exec('wikiLink'); },
-         wikiLink: [Dialog.Link, {
+    link: {
+        key:'l',
+        scope: { '[':']' , '[':'\n' },
+        nscope: { '[{':'\n', '[[','\n' },
+        snippet: '[{.description} | {pagenameOrUrl} | linkAttributes ] ",
+        commandIdentifier:'createlink'
+    },
+    linkDlg: {
+        scope: { '[':']' , '[':'\n' },
+        //match [link] or [link,  do not match [{, [[
+        //match '[' + 'any char except \n, [, { or ]' at end of the string
+        suggest: '|?([^\\n\\|\\]\\[\\{]+)',
+        linkDlg: [Dialog.Link, {
             onOpen: function(){
                 AJAX-retrieval of link suggestions
             }
          }]
-     }
-
-     color: {
-        nscope: {"%%(":")"},
-        action: "%%(color:#000000; background:#ffffff;) {.} \%",
-     }
-     colorsuggestion: {
-        scope: {"%%(":")"},
-        suggest: function(){
-            //match #cccccc
-        }
-        snippet: "{color}",
-        color: [ dialog.Color, {
-            //parms
-        }]
-     }
+        ****
+            suggest: function(txta, caret){
+                //match [link] or [link,  do not match [{, [[
+                //match '[' + 'any char except \n, [, { or ]' at end of the string
+                var result = txta.getFromStart().match( /\[([^\[\{\]\n\r]*)$/ ),
+                    link;
 
+                if( result ){
+                    link = result[1].split('|').getLast(); //exclude "text|" prefix
+                    result = {
+                        start: caret.start - link.length ,
+                        //if no input yet, then get list attachments of this wikipage
+                        match: link,
+                        tail: txta.slice( caret.start ).search( /[\n\r\]]/ )
+                    };
+                }
+                return result;
+            },
+        ****
+    }
+    linkAttributes: {
+        scope: { '|':']'},
+        suggest: ...,
+        linkAttributes: 'class='', newpage ....'
+    }
 
 */
 
 Wiki.Snips = {
 
-        find: {
-            key: "f"
-            //predefined find dialog triggered via Ctrl-f or a toolbar 'find' button
-        },
-
-        //sections:
-        //predefined section dialog triggered via a toolbar 'sections' button
-        //TODO: turn it into a suggestion menu for header lines
-
-        undo: {
-            //event: "undo", //predefined snipe event
-            action: function(){ this.undoredo.onUndo(); },
-            key: "z"
-        },
-
-        redo: {
-            //event: "redo", //predefined snipe event
-            action: function(){ this.undoredo.onRedo(); },
-            key: "y"
-        },
+        // Snipe predefined commands
+        find: { key: "f" },
+        undo: { key: "z", event: "undo" },
+        redo: { key: "y", event: "redo" },
 
+        // Configuration commands
         smartpairs: { event: 'config' },
         livepreview: { event: 'config' },
         autosuggest: { event: 'config' },
         tabcompletion: { event: 'config' },
         previewcolumn: { event: 'config' },
 
+
+        // Simple shortcuts
         br: {
             key: "shift+enter",
-            snippet: "\\\\\n"
+            snippet: "\\\\ "
         },
-
         hr: "\n----\n",
+        lorem: "This is is just some sample. Don’t even bother reading it; you will just waste your time. Why do you keep reading? Do I have to use Lorem Ipsum to stop you? OK, here goes: Lorem ipsum dolor sit amet, consectetur adipi sicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Still reading? Gosh, you’re impossible. I’ll stop here to spare you.",
+        Lorem: { synonym: "lorem" },
 
 
-        h: {
-            xxxsuggest: function(txta,caret){
-                var c,result=txta.slice(0,caret.start).match( /(?:^|[\n\r])(!{1,3}[^\n\r]*)$/ );
+        // simple inline tab completion commands
+        bold:   { key:'b', snippet: "__{bold}__ " },
+        italic: { key:'i', snippet: "''{italic}'' " },
 
-                if( result ){
-                    c = result[1];
-                    result = {
-                        start: caret.start - c.length,
-                        match: c + txta.slice(caret.start).match( /[^\n\r]*/ )||''  //entire line
-                    };
-                }
-                return result;
-            },
-            h: [Dialog.Selection, {
-                onOpen: function(){
-                    var value = (this.getValue().replace(/^!+\s?/,'')||'Title'), //remove !markup
-                        val = value.trim().trunc(20),
-                        k = ['!!! '+value,'!! '+value,'! '+value],
-                        v = ['<h2>'+val+'</h2>','<h3>'+val+'</h3>','<h4>'+val+'</h4>'];
+        mono:   "\\{\\{{monospaced text}}} ",
+        sub:    "%%sub {subscript text}/% ",
+        sup:    "%%sup {superscript text}/% ",
+        strike: "%%strike {strikethrough text}/% ",
 
-                    this.setBody( v.associate( k ) );
-                }
-            }]
+        // simple block tab completion commands
+        quote:  "\n%%quote \n{.}\n/%\n",
+        dl:     "\n;{term}:{definition-text} ",
+        pre:    "\n\\{\\{\\{\n{some preformatted block}\n}}}\n",
+        code:   "\n%%prettify \n\\{\\{\\{\n{/* some code block */}\n}}}\n/%\n",
+        table:  "\n||heading-1||heading-2\n| cell11   | cell12\n| cell21   | cell22\n",
+        //xflow:  "\n%%xflow\n{wide content}\n/%\n ",
+
+        me: { alias: 'sign'},
+        sign: function(){
+            var name = Wiki.UserName || 'UserName';
+            return "\n\\\\ &mdash;" + name + ", "+ new Date().toISOString() + "\\\\ \n";
         },
-    
-        font: {
+
+        date: function(k) {
+            return new Date().toISOString()+' ';
+            //return "[{Date value='" + d.toISOString() + "' }]"
+            //return "[{Date " + d.toISOString() + " }]"
+        },
+
+        tabs: {
             nScope: {
                 "%%(":")",
-                "font-family:":";"
+                "%%tabbedSection":"/%"
             },
-            /*
-            suggest: function(txta,caret){
-                //match /%%(:?.*)font-family:([^;\)]+)/
-
-            },*/
-            snippet: "%%(font-family:{font};) {.}/% ",
-            font: [Dialog.Font, {}]
+            snippet:"%%tabbedSection \n%%tab-{tabTitle1}\n{tab content 1}\n/%\n%%tab-{tabTitle2}\n{tab content 2}\n/%\n/%\n "
         },
 
-        color: {
-            nscope: { '%%(': ')' },
-            snippet: "%%(color:{#000000}; background:{#ffffff};) {.} ",
-            suggest: function(txta, caret){
-                //match "#cccccc;" pattern
-                var c,d, result = txta.slice(0,caret.end).match( /#[0-9a-f]{0,6}$/i );
 
-                if( result ){
-                    c = result[0];
-                    d = txta.slice( caret.end ).match( /^[0-9a-f]+/i )||'';
-                    result = {
-                        start: caret.end - c.length, //position of # char
-                        match: (c+d).slice(0,7)
-                    };
+
+        img: "\n[{Image src='img.jpg' width='400px' height='300px' align='{left}' }]\n ",
+
+        imgSrcDlg:{
+            scope: { "[{Image":"}]" },
+            suggest: { pfx:"src='([^']*)'?$", match: "^([^']*)" },
+            imgSrcDlg: [ Dialog.Selection, {
+
+                caption: "Image Source",
+                onOpen: function( dialog ){
+
+                    var //dialog = this,
+                        key = dialog.getValue();
+
+                    if( !key || (key.trim()=='') ){ key = Wiki.PageName + '/'; }
+
+                    //console.log('json lookup for '+key);
+             	   	//Wiki.ajaxJsonCall("/search/suggestions",[key,30], function(result) {
+                    Wiki.jsonrpc("/search/suggestions", [key, 30], function( result ){
+
+                        console.log('jsonrpc result', result );
+                        if( result[1] /*length>1*/ ){
+
+                            dialog.setBody( result );
+
+                        } else {
+
+                            dialog.hide();
+
+                        }
+                    });
                 }
-                return result;
-            },
-            color: [ Dialog.Color, {
-                //colorImage:'./test-dialog-assets/circle-256.png'
             }]
+        },
+
+/*
+        imgAlignDlg: {
+            scope: { "[{Image":"}]" },
+            suggest: "align='\\w+'",
+            imgAlignDlg: "left|center|right"
+        },
+*/
+
+        font: {
+            nScope: { "%%(":")", },
+            snippet: "%%(font-family:{font};) body /% ",
+        },
+        fontDlg: {
+            scope: { "%%(":")" },
+            suggest: { pfx: "font-family:([^;\\)\\n\\r]*)$", match:"^([^;\\)\\n\\r]*)" },
+            fontDlg: [Dialog.Font, {}]
+        },
+
+        color: "%%(color:{#000000}; background:#ffffff;) {some text} /%",
+
+        colorDlg: {
+            scope: { '%%(': ')' },
+            suggest:"#[0-9a-fA-F]{0,6}",
+            colorDlg: [ Dialog.Color , {} ]
          },
 
-        symbol: { synonym:"chars" },
-        chars: {
-            nScope: { "%%(":")" },
-            snippet: "{&entity;}",
-            suggest: function(txta, caret){
-                //match &xxx;
-                var c,result = txta.slice(0,caret.end).match( /&[\da-zA-Z]*;?$/ );
+        symbol: { synonym: "chars" },
+        chars: "&entity;",
 
-                if( result ){
-                    c = result[0];
-                    result = {
-                        start: caret.end - c.length,
-                        match: c,
-                        tail: txta.slice( caret.end ).match( /^[\da-zA-Z]*;?/ )||''
-                    }
+        charsDlg: {
+            suggest: '&\\w+;?',
+            charsDlg: [ Dialog.Chars, { caption:"Special Chars".localize() }]
+        },
+
+
+        icon: "%%icon-{}search /%",
+        iconDlg: {
+            scope: { "%%":"/%" },
+            suggest: "icon-\\S*",
+            iconDlg: [Dialog.Selection, {
+                cssClass:".dialog-horizontal",
+                body:{
+                    "icon-search":"<div class='icon-search'></div>",
+                    "icon-user":"<div class='icon-user'></div>",
+                    "icon-home":"<div class='icon-home'></div>",
+                    "icon-refresh":"<div class='icon-refresh'></div>",
+                    "icon-repeat":"<div class='icon-repeat'></div>",
+                    "icon-bookmark":"<div class='icon-bookmark'></div>",
+                    "icon-font":"<div class='icon-font'></div>",
+                    "icon-bold":"<div class='icon-bold'></div>",
+                    "icon-italic":"<div class='icon-italic'></div>",
+                    "icon-tint":"<div class='icon-tint'></div>",
+                    "icon-plus":"<div class='icon-plus'></div>",
+                    "icon-external-link":"<div class='icon-external-link'></div>",
+
+                    "icon-sign-in":"<div class='icon-sign-in'></div>",
+                    "icon-sign-out":"<div class='icon-sign-out'></div>",
+                    "icon-rss":"<div class='icon-rss'></div>",
+                    "icon-wrench":"<div class='icon-wrench'></div>",
+                    "icon-filter":"<div class='icon-filter'></div>",
+                    "icon-link":"<div class='icon-link'></div>",
+                    "icon-paperclip":"<div class='icon-paperclip'></div>",
+                    "icon-undo":"<div class='icon-undo'></div>",
+                    "icon-euro":"<div class='icon-euro'></div>",
+                    "icon-slimbox":"<div class='icon-slimbox'></div>",
+                    "icon-picture":"<div class='icon-picture'></div>",
+                    "icon-columns":"<div class='icon-columns'></div>"
                 }
-                return result;
+            }]
+        },
 
-            },
-            chars: [Dialog.Chars, {caption:"Special Chars".localize()}]
+        contextText: {
+            scope: { "%%":"/%" },
+            suggest: {pfx: "%%text-(\\w*)$", match: "^default|success|info|warning|danger" },
+            contextText: [Dialog.Selection, {
+                cssClass:".dialog-horizontal",
+                body:{
+                    primary:"<span class='text-primary'>primary</span>",
+                    success:"<span class='text-success'>success</span>",
+                    info:"<span class='text-info'>info</span>",
+                    warning:"<span class='text-warning'>warning</span>",
+                    danger:"<span class='text-danger'>danger</span>"
+                }
+            }]
         },
 
-        style: { synonym:"css"},
-        css: {
-            nScope: { "%%(":")" },
-            snippet: "%%{css} {.} /% ",
-            suggest: function(txta, caret){
-                //match %%(.w+)
-                var c, result = txta.slice(0,caret.end).match(/%%[\da-zA-Z(\-\_:#;)]*$/);
+        contextBG: {
+            scope: { "%%":"/%" },
+            suggest: {pfx:"%%(default|success|info|warning|error)$", match:"^default|success|info|warning|error"},
+            contextBG: [Dialog.Selection, {
+                cssClass:".dialog-horizontal",
+                body:{
+                    "default":"<span class='default'>default</span>",
+                    success:"<span class='success'>success</span>",
+                    info:"<span class='info'>info</span>",
+                    warning:"<span class='warning'>warning</span>",
+                    error:"<span class='error'>error</span>",
+                }
+            }]
+        },
 
-                if(result){
-                    c = result[0].slice(2); //drop the %% prefix
-                    result = {
-                        start: caret.end - c.length,
-                        match: c + txta.slice( caret.end ).match( /^[\da-zA-Z(:#;)]*/ )||''
-                    };
+        labelDlg: {
+            scope: { "%%":"/%" },
+            suggest: {pfx: "%%label-(\\w*)$", match: "^default|success|info|warning|danger" },
+            labelDlg: [Dialog.Selection, {
+                cssClass:".dialog-horizontal",
+                body:{
+                    "default":"<span class='label label-default'>default</span>",
+                    primary:"<span class='label label-primary'>primary</span>",
+                    success:"<span class='label label-success'>success</span>",
+                    info:"<span class='label label-info'>info</span>",
+                    warning:"<span class='label label-warning'>warning</span>",
+                    danger:"<span class='label label-danger'>danger</span>"
                 }
-                return result;
-            },
-            css: {
+            }]
+        },
+
+        listDlg: {
+            scope: { "%%list-":"/%" },
+            suggest: {pfx: "list-(?:[\\w-]+-)?(\\w*)$", match: "^\\w*" },
+            listDlg: [Dialog.Selection, {
+                cssClass:".dialog-horizontal",
+                body: "nostyle|unstyled|hover|group"
+            }]
+        },
+
+        tableDlg: {
+            scope: { "%%table-":"/%" },
+            suggest: {pfx: "table-(?:[\\w-]+-)?(\\w*)$", match: "^\\w*" },
+            tableDlg: [Dialog.Selection, {
+                cssClass:".dialog-horizontal",
+                body: "sort|filter|striped|bordered|hover|condensed|fit"
+            }]
+        },
+
+
+        cssDlg: {
+            scope: { "%%":"/%" },
+            suggest: {pfx:"%%([\\da-zA-Z-_]*)$", match:"^[\\da-zA-Z-_]*"},
+            cssDlg: {
                 "(css:value;)":"any css definitions",
-                "text-success":"text-success",
-                "text-information":"text-information",
-                "text-warning":"text-warning",
-                "text-error":"text-error",
-                success:"success",
-                information:"information",
-                warning:"warning",
-                error:"error",
-                commentbox:"commentbox",
-                quote:"quoted paragraph",
-                sub:"sub-script<span class='sub'>2</span>",
-                sup:"super-script<span class='sup'>2</span>",
-                strike:"<span class='strike'>strikethrough</span>",
-                pretify:"prettify code block",
-                reflection:"image reflection"
+                "default":"contextual backgrounds",
+                "text-default":"contextual text color",
+                "label-default":"<span class='label label-default'>contextual labels</span>",
+                "badge":"badges <span class='badge'>13</span>",
+                //"btn-default":"<span class='btn btn-xs btn-default'>Buttons</span>",
+                "collapse":"collapsable lists",
+                "list-nostyle":"list styles",
+                //progress:"Progress Bars",
+                "table-fit":"table styles",
+                "":"",
+                "add-css":"Add CSS",
+                alert: "Alert Box",
+                accordion: "Accordion",  //leftAccordion, rightAccordion, pillsAccordion, accordion-primary...
+                category: "Category Links",
+                carousel: "Carousel",
+                columns: "Multi-column layout",
+                commentbox: "Comment Box",
+                //graphBar
+                pills:"Pills",
+                prettify: "Prettify syntax highlighter",
+                scrollable: "Scrollable Preformatted block",
+                //reflection: "Image with reflection",
+                slimbox: "Slimbox Viewer <span class='icon-slimbox'></span>",
+                //"under-construction": "<div class='under-construction'> </div>",
+                tabs:"Tabs",
+                viewer: "Media Viewer"
+
+//inline styles
+                //bold
+                //italic
+//                small:"<span class='small'>Smaller</span> text",
+//                sub:"2<span class='sub'>8</span> Sub-Script",
+//                sup:"2<span class='sup'>3</span> Super-Script",
+//                strike:"<span class='strike'>strikethrough</span>",
+//block styles
+//                quote:"<div class='quote'>Quoted paragraph</div>",
+//                lead:"<span class='lead'>LEAD text</span>",
+//                "drop-caps":"Drop Caps",
                 //xflow:"wide content with scroll bars"
             }
         },
 
-        //simple tab completion commands
-        sub: "%%sub {subscript text}/% ",
-        sup: "%%sup {superscript text}/% ",
-        strike: "%%strike {strikethrough text}/% ",
-        xflow: "\n%%xflow\n{wide content}\n/%\n ",
-        quote: "\n%%quote \n{quoted text}\n/%\n",
-        dl: "\n;{term}:{definition-text} ",
-        pre: "\n\\{\\{\\{\n{some preformatted block}\n}}}\n",
-        code: "\n%%prettify \n\\{\\{\\{\n{/* some code block */}\n}}}\n/%\n",
-        mono: "\\{\\{{monospaced text}}} ",
-
         link: {
             key:'l',
             commandIdentifier:'createlink',
-            suggest: function(txta, caret){
+            //snippet: "[{description|}{pagename or url}|{attributes}] ",
+            snippet: "[{pagename or url}] "
+        },
 
-                //match [link] or [link,  do not match [{, [[
-                //match '[' + 'any char except \n, [, { or ]' at end of the string
-                var result = txta.getFromStart().match( /\[([^\[\{\]\n\r]*)$/ ),
-                    link;
 
-                if( result ){
-                    link = result[1].split('|').getLast(); //exclude "text|" prefix
-                    result = {
-                        start: caret.start - link.length ,
-                        //if no input yet, then get list attachments of this wikipage
-                        match: link,
-                        tail: txta.slice( caret.start ).search( /[\n\r\]]/ )
-                    };
-                }
-                return result;
+        linkPart3:{
+            suggest: {
+                pfx: "\\[(?:[^\\|\\]]+\\|[^\\|\\]]+\\|)([^\\|\\[\\]\\n\\r]*)$",
+                match: "^[^\\|\\]\\n\\r]*"
             },
+            linkPart3: {
+                //"class='category'": "Category link",
+                "class='viewer'": "View Linked content",
+                "class='slimbox'": "Add Slimbox link <span class='icon-slimbox'/> ",
+                "class='slimbox-link'": "Replace by Slimbox Link <span class='icon-slimbox'/> ",
+                "divide1": "",
+                "class='btn btn-primary'": "Button Style",
+                "class='btn btn-xs btn-primary'": "Small Button Style",
+                "divide2": "",
+                "target='_blank'": "Open link in new tab"
+            }
 
-            //snippet: "[{display text}|{pagename or url}|{attributes}] ",
-            snippet: "[{link}] ",
-            //attributes: "accesskey='X'|title='description'|target='_blank'
-            //    'accesskey', 'charset', 'class', 'hreflang', 'id', 'lang', 'dir',
-            //  'rel', 'rev', 'style', 'tabindex', 'target', 'title', 'type'
-            //    display-text
-            //    wiki-page or url -- allow to validate the url ; preview the page/url
-            //    title: descriptive text
-            //- target: _blank --new-- window yes or no
+        },
+
+        linkDlg: {
+
+            //match [description|link], [link] or [link,  do not match [{, [[
+            //match '[' + 'any char except \n, [, { or ]' at end of the string
+            //note: do not include the [ in the matched string
+            suggest: {
+                pfx: "\\[(?:[^\\|\\]]+\\|)?([^\\|\\[\\{\\]\\n\\r]*)$",
+                match: "^([^\\|\\[\\{\\]\\n\\r]*)(?:\\]|\\|)"
+            },
 
-            //link: [ Dialog.Link, { ...
-            link: [ Dialog.Selection, {
+            linkDlg: [ Dialog.Selection, {
 
-                onOpen: function(dialog){
+                caption: "Wiki Link",
+                onOpen: function( dialog ){
 
 
-//console.log("****"+dialog.getValue()+"****", Wiki.PageName)
-                    var dialog = this, 
+                    var //dialog = this,
                         key = dialog.getValue();
-                        
-                    if( !key || (key.trim()=='')) key = Wiki.PageName + '/';
+
+                    if( !key || (key.trim()=='') ){ key = Wiki.PageName + '/'; }
 
                     //console.log('json lookup for '+key);
-             	   	Wiki.ajaxJsonCall("/search/suggestions",[key,30], function(result) {
-                       if( result && result.size()>0 ){
-                           dialog.setBody( result );
-                       } else {
-                           dialog.hide();
-                       }
-             	   	});
-                }
-            }]
+                    Wiki.jsonrpc("/search/suggestions", [key, 30], function( result ){
 
+                        console.log("jsonrpc result", result );
+                        if( result[1] /*length>1*/ ){
 
-        },
+                            dialog.setBody( result );
+
+                        } else {
+
+                            dialog.hide();
 
-        bold: {
-            key:'b',
-            snippet:"__{bold text}__ "
+                        }
+                    });
+                }
+            }]
         },
 
-        italic: {
-            key:'i',
-            snippet: "''{italic text}'' "
+        variableDlg: {
+            scope:{ '[{$':'}]'},
+            suggest: "\\w+",
+            variableDlg: "applicationname|baseurl|encoding|inlinedimages|interwikilinks|jspwikiversion|loginstatus|uptime|pagename|pageprovider|pageproviderdescription|page-styles|requestcontext|totalpages|username"
         },
 
+
+        // Page access rights
         allow: { synonym: "acl" },
-        acl: {
-            snippet: "\n[\\{ALLOW {permission} {principal(,principal)} \\}]\n",
-            permission: "view|edit|modify|comment|rename|upload|delete",
-            //permission:[Dialog.Selection, {body:"view|edit|modify|comment|rename|upload|delete"}]
-            "principal(,principal)": function(){
-                return "Anonymous|Asserted|Authenticated|All";
-                //FIXME: retrieve list of available wiki user groups through ajax call
-            }
-        },
+        acl: "\n[{ALLOW {permission} {principal} }]\n",
 
-        img: {
-            snippet:"\n[\\{Image src='{img.jpg}' width='{400px}' height='{300px}' align='{text-align}' style='{css-style}' class='{css-class}' }]\n ",
-            'text-align':'left|center|right'
+        permission: {
+            scope:{ '[{ALLOW':'}]'},
+            suggest: { pfx:"ALLOW (\\w+)$", match:"^\\w+" },
+            permission: [Dialog.Selection, {
+                cssClass:".dialog-horizontal",
+                body:"view|edit|modify|comment|rename|upload|delete"
+            }]
         },
+        principal: {
+            scope:{ '[{ALLOW':'}]'},
+            suggest: { pfx:"ALLOW \\w+ (?:[\\w,]+,)?(\\w*)$", match:"^\\w*" },
+
+            principal: [ Dialog.Selection, {
+
+                caption: "Roles, Groups or Users",
+                onOpen: function( dialog ){
+
+                    new Request({
+                        url: Wiki.XHRPreview,
+                        data: { page: Wiki.PageName, wikimarkup: "[{Groups}]" },
+                        onSuccess: function(responseText){
+
+                            var body = "Anonymous|Asserted|Authenticated|All";
+                            responseText = responseText.replace( /<[^>]+>/g,'').replace(/\s*,\s*/g,'|' ).trim();
+                            if(responseText != ""){ body = body + '||' + responseText; }
+
+                            dialog.setBody(body);
+
+                        }
+                    }).send();
+
+                }
+            }]
+
 
-        plugin: {
-            snippet: "\n[\\{{plugin}}]\n",
-            suggest: function(txta, xcaret){
-                //match [{
-            },
-            plugin: {
-                "TableOfContents title='Page contents' numbered='true' prefix='Chap. '":"Table Of Contents (toc)",
-                "If name='value' page='pagename' exists='true' contains='regexp'\n\nbody\n":"Test a page variable",
-                "SET alias='{pagename}'":"Make a Page Alias",
-                "SET name='value'":"Set a page variable",
-                "$varname":"Get a page variable",
-                "InsertPage page='pagename'":"Insert Page",
-                "CurrentTimePlugin format='yyyy mmm-dd'":"Current Time",
-                "Search query='Janne' max='10'":"Search query",
-                "ReferredPagesPlugin page='pagename' type='local|external|attachment' depth='1..8' include='regexp' exclude='regexp'":"Incoming Links (aka referred pages)",
-                "ReferringPagesPlugin page='pagename' separator=',' include='regexp' exclude='regexp'":"Outgoing Links (aka referring pages)",
-                "WeblogPlugin page='pagename' startDate='300604' days='30' maxEntries='30' allowComments='false'":"Display weblog posts",
-                "WeblogEntryPlugin":"New weblog entry"
-            }
-        },
 
-        tab: {
-            nScope: {
-                "%%(":")",
-                "%%tabbedSection":"/%"
-            },
-            snippet:"%%tabbedSection \n%%tab-{tabTitle1}\n{tab content 1}\n/%\n%%tab-{tabTitle2}\n{tab content 2}\n/%\n/%\n "
         },
 
         toc: {
@@ -444,31 +587,90 @@ Wiki.Snips = {
             snippet:"\n[\\{TableOfContents }]\n"
         },
 
-        table: "\n||heading-1||heading-2\n| cell11   | cell12\n| cell21   | cell22\n",
-
-        me: { alias: 'sign'},
-        sign: function(){
-            var name = Wiki.UserName || 'UserName';
-            return "\\\\\n--" + name + ", "+ new Date().toISOString() + "\\\\\n";
+        tocParams: {
+            scope:{ '[{TableOfContents':'}]'},
+            suggest: "\\s",
+            tocParams: [Dialog.Selection, {
+                caption: "TOC additional parameters",
+                body:{
+                " title='Page contents' ":"title",
+                " numbered='true' ":"numbered",
+                " prefix='Chap. ' ":"chapter prefix"
+                }
+            }]
         },
 
-        date: function(k) {
-            return new Date().toISOString()+' ';
-            //return "[{Date value='" + d.toISOString() + "' }]"
-            //return "[{Date " + d.toISOString() + " }]"
-        },
 
-        abra: {
-            suggest:"abra",
-            snippet:"cadabra"
-        },
-        abrar: {
-            suggest:"abrar",
-            snippet:"acurix"
+//        plugin: "\n[{{plugin}}]\n",
+
+        plugin: {
+            //match [{plugin}]  do not match [[{
+            //match '[{' + 'any char except \n, or }]' at end of the string
+            //note: do not include the [ in the matched string
+            //snippet: "\n[{{plugin}}]\n",
+            suggest: {
+                pfx: "(^|[^\\[])\\[\\{([^\\[\\]\\n\\r]*)(?:\\|\\])?$",
+                match: "^([^\\[\\]\\n\\r]*)\\}\\]"
+            },
+            plugin: [ Dialog.Selection, {
+                caption: "Plugin",
+                body: {
+                "ALLOW permission principal ": "Page Access Rights",
+                "SET name='value'":"Set a Wiki variable",
+                "$varname":"Get a Wiki variable",
+                "If name='value' page='pagename' exists='true' contains='regexp'\n\nbody\n":"IF plugin",
+                "SET alias='${pagename}'":"Page Alias",
+                "SET sidebar='off'":"Collapse Sidebar",
+                //"Table":"Advanced Tables",
+                //"Groups":"View all Wiki Groups",
+                "":"",
+                "Counter":"Insert a simple counter",
+                "CurrentTimePlugin format='yyyy mmm-dd'":"Insert Current Time",
+                "Denounce":"Denounce a link",
+                "Image src='${image.jpg}'":"Insert an Image",
+                "IndexPlugin":"Index of all pages",
+
+                "InsertPage page='${pagename}'":"Insert another Page",
+                "SET page-styles='prettify-nonum table-condensed-fit'":"Insert Page Styles",
+                "ListLocksPlugin":"List page locks",
+                "RecentChangesPlugin":"Displays the recent changed pages",
+                "ReferredPagesPlugin page='{pagename}' type='local|external|attachment' depth='1..8' include='regexp' exclude='regexp'":"Incoming Links (referred pages)",
+                "ReferringPagesPlugin page='{pagename}' separator=',' include='regexp' exclude='regexp'":"Outgoing Links (referring pages)",
+                "Search query='Janne' max='10'":"Insert a Search query",
+                "TableOfContents ":"Table Of Contents ",
+                "UndefinedPagesPlugin":"List pages that are missing",
+                "UnusedPagesPlugin":"List pages that have been orphaned",
+                "WeblogArchivePlugin":"Displays a list of older weblog entries",
+                "WeblogEntryPlugin":"Makes a new weblog entry",
+                "WeblogPlugin page='{pagename}' startDate='300604' days='30' maxEntries='30' allowComments='false'":"Builds a weblog"
+                }
+            }]
+
         },
-        lorem: "This is is just some sample. Don’t even bother reading it; you will just waste your time. Why do you keep reading? Do I have to use Lorem Ipsum to stop you? OK, here goes: Lorem ipsum dolor sit amet, consectetur adipi sicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Still reading? Gosh, you’re impossible. I’ll stop here to spare you.",
-        Lorem: { synonym: "lorem" }
 
+        //FIXME
+        //Commands triggered by the selection of substrings:
+        //    lowest priority vs. other snippets
+        selectInline: {
+            suggest: function(workarea,caret,fromStart){
+                if(!caret.thin){
+                     console.log("got selection", caret);
+                     return { pfx:"xx", match:workarea.getSelection() }
+                }
+            },
+
+            selectInline: [Dialog.Selection, {
+                cssClass: ".dialog-horizontal",
+                body:{
+                    "__bold__":"bold",
+                    "''italic''":"italic",
+                    "{{mono}}":"mono",
+                    "[link]":"link"
+                }
+            }]
+        },
+        selectBlock:  "code|prettify",
+        selectStartOfLine: "!!!h1|!!h2|!h3|bold|italic|mono|link|plugin"
 
 }
 

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Category.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Category.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Category.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Category.js Mon Jun 22 20:11:42 2015
@@ -51,26 +51,26 @@ DOM structure after:
 Wiki.Category = function(element, pagename, xhrURL){
 
     function poppy(event){
-    
+
         var popup = this.getNext();
         event.stop();
-        popup.swapClass('hide', 'loading');
-        element.set('title','').removeEvents();
+        popup.swapClass("hide", "loading");
+        element.set("title", "").removeEvents();
 
         new Request.HTML({
-            url: xhrURL,
-            data: { page:pagename },
+            url: xhrURL, //+"?page="+pagename,
+            data: { page: decodeURIComponent(pagename) },
             update: popup,
-            onSuccess: function(){ popup.swapClass('loading', 'active'); }
-        }).send();    
+            onSuccess: function(){ popup.swapClass("loading", "active"); }
+        }).send();
     }
-    
-    ['span',['div.popup.hide']].slick().wraps(element,'top');
-    
+
+    ["span", ["div.popup.hide"]].slick().wraps(element, "top");
+
     element.set({
-        'class': 'category-link',
-        title: 'category.title'.localize( pagename ),
+        "class": "category-link",
+        title: "category.title".localize( pagename ),
         events: { click: poppy }
     });
 
-}
+};

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Findpages.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Findpages.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Findpages.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Findpages.js Mon Jun 22 20:11:42 2015
@@ -21,27 +21,51 @@
 /*
 Class: Wiki.SearchBox
     ...
-    Adds/Removes 'li.findpages' elements to the dropdown menu.
+    Adds/Removes "li.findpages" elements to the dropdown menu.
     These elements are the results of the wiki rpc call, based on the query.
 
 
 Example:
->    wiki.add('query', Wiki.SearchBox, {
->        rpc: function(value, callback){ Wiki.ajaxJsonCall('/search/findPages',[value,'20'], callback },
+>    wiki.add("query", Wiki.Findpages, {
+>        rpc: function(value, callback){ Wiki.ajaxJsonCall("/search/findPages",[value,"20"], callback },
+>        rpc: function(value, callback){ wiki.jsonrpc("search.findPages", [value,20], callback },
 >        toUrl: wiki.toUrl
 >    });
+
+DOM Structure:
+(start code)
+
+      //only ico no full match with query : options to create/clone new page
+      li.findpages
+          a.createpage[href="/wiki/B"] [Create] B
+      li.findpages
+          a.createpage[href="/wiki/B&clone=Main]  [Create] B [based on] Main
+      //show all rpc results
+      li.findpages
+          a[href]
+
+(end)
 */
 Wiki.Findpages = new Class({
 
-    Binds:['search','action'],
+    Binds: ["search", "action"],
     Implements: Events,
 
     initialize: function(element, options){
 
-        this.rpc = options.rpc;
-        this.toUrl = options.toUrl;
-        this.query = element.getParent('form').query.observe( this.search );
-        this.element = element; //ul.dropdown menu
+        var self = this;
+
+        self.rpc = options.rpc;
+        self.toUrl = options.toUrl;
+        self.allowClone = options.allowClone;
+        self.query = element.getParent("form").query.observe( self.search );
+        self.element = element; //ul.dropdown menu
+
+        self.element.addEvent("click:relay([name=cloney])", function(){
+
+            this.getParent("a").href = self.toUrl(self.getValue(), true, this.checked);
+
+        });
 
     },
 
@@ -50,20 +74,25 @@ Wiki.Findpages = new Class({
     },
 
     empty: function(){
-        this.element.getElements('li.findpages').destroy();
+        this.element.getElements("li.findpages").destroy();
     },
 
     search: function(){
 
         var value = this.getValue();
 
-        if( (value == null) || (value.trim()=="") ){
+        if( (value == null) || (value.trim() == "") ){
 
             this.empty();
 
         } else {
 
-            this.rpc( 'name:'+value, this.action );
+            //this.rpc( "name:" + value, this.action );
+            this.rpc( value, this.action );
+            //for testing ...
+            //this.action([{"score":91,"page":"Collapsible List"},{"score":78,"page":"BrushedTemplateEditor"},{"score":78,"page":"BrushedTemplate"},{"score":78,"page":"BrushedTemplateScreenshots"},{"score":76,"page":"BrushedWikiPlugin"},{"score":76,"page":"BrushedTemplateTypography"},{"score":76,"page":"BrushedTemplateDiscussion"},{"score":74,"page":"BrushedTemplateIdeas"},{"score":71,"page":"BrushedTemplateTOC"},{"score":70,"page":"BrushedTemplateDiscussion2006"},{"score":68,"page":"BrushedTemplateMetadataEditor"},{"score":66,"page":"BrushedTemplateEval"},{"score":66,"page":"BrushedTemplateForms"},{"score":66,"page":"BrushedTemplateJSP"},{"score":66,"page":"BrushedTemplateToolbar"},{"score":65,"page":"BrushedTemplateListTable"}]);
+
+            //obsolete:
             //for testing ...
             //this.action({"id":10000,"result":{"javaClass":"java.util.ArrayList","list":[{"map":{"page":"BrushedTemplateCollapse","score":99},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplate","score":95},"javaClass":"java.util.HashMap"},{"map":{"page":"CollapsibleList","score":61},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateInGerman","score":55},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateDiscussion","score":50},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateScreenshots","score":50},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateTypography","score":50},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedConditionalPlugin","score":48},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateDiscussion2006","score":45},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateMetadataEditor","score":44},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTablePlugin","score"
 :43},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateColumns","score":43},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateCategories","score":43},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateToolbar","score":43},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateTOC","score":43},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateAccordion","score":43},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateTip","score":43},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateSlimbox","score":43},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedTemplateRoundedCorners","score":40},"javaClass":"java.util.HashMap"},{"map":{"page":"BrushedEditPageHelp","score":37},"javaClass":"java.util.HashMap"}]}});
 
@@ -75,43 +104,42 @@ Wiki.Findpages = new Class({
 
         var self = this,
             value = self.getValue(),
-            elements = [], content, btn = 'span.btn.btn-xs.btn-danger';
+            elements = [], item;
 
-        //helper function
-        function addLI( page, score, isEdit, isClone ){
+        if( result ){
 
-            content = [];
-            if( isClone ){
-                content.push(btn, {text: "Clone this page and Create".localize() });
-            } else if( isEdit ){
-                content.push(btn, {text: "Create".localize() });
-            }
-            content.push( 'span', {text:page});
-            if( score ) content.push('span.badge.pull-right', {text:score});
+            item = result[0];
 
-            elements.push( "li.findpages", [
-                ( isEdit ? "a.createpage" : "a" ), { href: self.toUrl(page, isEdit, isClone) }, 
-                content     
-            ]);
+            if( !item || item.page != value ){
 
-        }
+                elements.push( "li.findpages", [
+                    "a", { href: self.toUrl(value, true), title: "sbox.create".localize(value) }, [
+                        "span.createpage", { text: value }, [
+                            "div.btn.btn-danger.btn-xs.pull-right", [
+                               "input[type=checkbox][name=cloney]",
+                               "span", { text: "sbox.clone".localize() }
+                            ]
+                        ]
+                    ]
+                ]);
 
-        if( result.list ){
-
-            item = result.list[0];
-            if( !item || item.map.page!=value ){
-                addLI(value, 0, true);  //create new page
-                addLI(value, 0, true, true);  //clone current page into a new page
             }
 
-            while(result.list[0]){
-                item = result.list.shift().map; 
-                addLI(item.page, item.score);                
+            while( result[0] ){
+
+                item = result.shift();
+
+                elements.push( "li.findpages", [
+                    "a", { href: self.toUrl( item.page ) }, [
+                        "span", { text: item.page },
+                        "span.badge.pull-right", { text: item.score }
+                    ]
+                ]);
             }
 
 			self.empty();
-			if(elements[0]) elements.slick().inject( self.element.getFirst('.divider'), 'before' );
-            //self.fireEvent('complete');
+			if( elements[0] ){ elements.slick().inject( self.element.getFirst(".divider"), "before" ); }
+            //self.fireEvent("complete");
 
         }
     }

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Group.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Group.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Group.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Group.js Mon Jun 22 20:11:42 2015
@@ -138,4 +138,4 @@ var WikiGroup =
         Wiki.submitOnce(form);
         form.submit();
     }
-}
+}
\ No newline at end of file

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Prefs.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Prefs.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Prefs.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Prefs.js Mon Jun 22 20:11:42 2015
@@ -23,43 +23,56 @@ Javascript routines to support JSPWiki U
     PreferencesContent.jsp
     PreferencesTab.jsp
 
-    *  prefSkin:'SkinName',
-    *  prefTimeZone:'TimeZone',
-    *  prefTimeFormat:'DateFormat',
-    *  prefOrientation:'Orientation',
-    *  editor:'editor',
-    *  prefLanguage:'Language',
-    *  prefSectionEditing:'SectionEditing' =>checkbox 'on'
+    *  prefSkin:"SkinName",
+    *  prefTimeZone:"TimeZone",
+    *  prefTimeFormat:"DateFormat",
+    *  prefOrientation:"Orientation",
+    *  editor:"editor",
+    *  prefLanguage:"Language",
+    *  prefSectionEditing:"SectionEditing" =>checkbox "on"
 */
-Wiki.once('#setCookie', function(form){
+!function(wiki){
 
-        window.onbeforeunload = function(){
+    var datapref = "[data-pref]"; //data preference elements
 
-            if( form.getElements('[data-pref]').some(function(el){
+    function windowUnload( onbeforeunload ){
+        window.onbeforeunload = onbeforeunload || function(){};
+    }
 
-                //a checkbox.get('value') returns 'on' when checked; 
-                //so getDefaultValue() should also return 'on'
-                return (el.get('value') != el.getDefaultValue());
+    wiki.once("#setCookie", function(form){
 
-            }) ) return 'prefs.areyousure'.localize();      
-        };
+        windowUnload( function(){
+
+            if( form.getElements( datapref ).some(function(el){
+
+                //a checkbox.get("value") returns "on" when checked;
+                //so getDefaultValue() should also return "on"
+                return (el.get("value") != el.getDefaultValue());
+
+            }) ){ return "prefs.areyousure".localize(); }
+        } );
+
+        form.addEvent("submit", function(){
+
+            this.getElements( datapref ).each( function(el){
+
+                wiki.prefs.set( el.get( datapref ), el.get("value") );
 
-        form.addEvent('submit', function(e){
-    
-            this.getElements('[data-pref]').each( function(el){
-                Wiki.set( el.get('data-pref'), el.get('value') ); 
             });
-            window.onbeforeunload = function(){};
+            windowUnload();
 
         });
     })
 
-    .once('#clearCookie', function(form){
+    .once("#clearCookie", function(form){
 
-        form.addEvent('submit', function(){ 
+        form.addEvent("submit", function(){
 
-            window.onbeforeunload = function(){}; 
-            Wiki.erase(); 
+            windowUnload();
+            wiki.erase();
 
         });
-});
+
+    });
+
+}(Wiki);

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Recents.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Recents.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Recents.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Recents.js Mon Jun 22 20:11:42 2015
@@ -42,10 +42,10 @@ DOM-structure:
 (end code)
 
 Examples:
->    wiki.add('.searchbox-recents', function(element){
+>    wiki.add(".searchbox-recents", function(element){
 >       new Wiki.Recents(element, {
->           items: wiki.prefs.get('RecentSearch'),
->           onChange: function(recents){ wiki.set('RecentSearch',recents); }
+>           items: wiki.prefs.get("RecentSearch"),
+>           onChange: function(recents){ wiki.set("RecentSearch",recents); }
 >       });
 >   });
 
@@ -54,29 +54,28 @@ Wiki.Recents = new Class({
 
     Implements: [Events, Options],
 
-    initialize:function(dropdown,options){
+    initialize: function(dropdown, options){
 
-        var self = this, 
-            items, i=0, len, 
-            list=[], li='li.recents';
+        var self = this,
+            items, i = 0,
+            list = [], li = "li.recents";
 
 		self.setOptions( options );
-        //self.options.items = ['foo','bar']; //test
+        //self.options.items = ["foo","bar"]; //test
 
         self.items = items = self.options.items || list;
-        self.form = dropdown.getParent('form').addEvent('submit', self.submit.bind(self) );
+        self.form = dropdown.getParent("form").addEvent("submit", self.submit.bind(self) );
 
         //build li.recents dropdown items
         if( items[0] ){
 
             while(items[i]){
-                list.push(li, ['a', { html:items[i++].stripScripts() }] );
+                list.push(li, ["a", { html: items[i++].stripScripts() }] );
             }
-            //list.push(li+'.clear',['a',{html:'[Clear Recent Searches]' }]);
-            list.push(li+'.clear',['a', [ 'span.btn.btn-xs.btn-default',{text:'Clear Recent Searches' }]]);
+            list.push(li + ".clear", ["a", [ "span.btn.btn-xs.btn-default", {text: "sbox.clearrecent".localize() }]]);
             dropdown.adopt( list.slick() );
         }
-        dropdown.addEvent('click:relay('+li+')', function(ev){ ev.stop(); self.action(this); });
+        dropdown.addEvent( "click:relay(" + li + ")", function(ev){ ev.stop(); self.action(this); });
 
     },
 
@@ -84,24 +83,24 @@ Wiki.Recents = new Class({
 
         var self = this, form = self.form;
 
-        if( element.match('.clear') ){
+        if( element.match(".clear") ){
 
-            //element.getSiblings('li.recents').destroy();
+            //element.getSiblings("li.recents").destroy();
             //element.destroy();
-            element.getElements('!> > li.recents').destroy(); //!> == direct parent
+            element.getElements("!> > li.recents").destroy(); //!> == direct parent
             self.items = [];
-            self.fireEvent('change'/*,null*/);
+            self.fireEvent("change"/*,null*/);
 
         } else {
 
-            form.query.value = element.get('text');
+            form.query.value = element.get("text");
             form.submit();
 
         }
 
     },
 
-    submit:function(){
+    submit: function(){
 
         var self = this,
             items = self.items,
@@ -110,8 +109,8 @@ Wiki.Recents = new Class({
         if( items.indexOf( value ) < 0 ){
 
             //insert new item at the start of the list, and cap list on max 10 items
-            if( items.unshift(value) > 9){ items = items.slice(0,9); }
-            self.fireEvent('change', [self.items = items] );
+            if( items.unshift(value) > 9){ items = items.slice(0, 9); }
+            self.fireEvent("change", [self.items = items] );
 
         }
 

Modified: jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Search.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Search.js?rev=1686927&r1=1686926&r2=1686927&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Search.js (original)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Search.js Mon Jun 22 20:11:42 2015
@@ -33,50 +33,55 @@ Example:
     (start code)
     new wiki.Search( form, {
         url: wiki.XHRSearch,
-        onComplete: function(){ this.wiki.set('PrevQuery', form.query); }
+        onComplete: function(){ this.wiki.set("PrevQuery", form.query); }
     });
     (end)
 
 */
 Wiki.Search = new Class({
 
-    Binds: ['action','setQuery','setStart'],
+    Binds: ["action", "setQuery", "setStart"],
     Implements: [Events, Options],
 
-    initialize:function(form,options){
+    initialize: function(form, options){
 
-        var self = this, s;
+        var self = this, s, hash = location.hash;
 
-        self.setOptions(options);
+        self.setOptions( options );
         self.form = form;
-        form.getElements('input[name=scope]').addEvent('click', self.setQuery);
-        form.details.addEvent('click', self.action);
+        form.getElements( "input[name=scope]").addEvent("click", self.setQuery );
+        form.details.addEvent( "click", self.action );
 
-        self.result = form.getNext().addEvent('click:relay(.pagination a)', self.setStart);
+        self.result = form.getNext().addEvent("click:relay(.pagination a)", self.setStart);
 
         self.query = form.query.observe( function(){
-            form.start.value='0';   // reset the start page before re-running new ajax searches
+
+            form.start.value = "0";   // reset the start page before running new ajax searches
             self.action();
+
         });
 
-        // hash may contain a query pagination parameter: (-1=all,0,1,2...)
-        if( location.hash ){
-            s = decodeURIComponent(location.hash.slice(1)).match(/(.*):(-?\d+)$/);
-            if(s && s[2] /*s.length==3*/ ){
+        // hash may contain a query pagination parameter: (-1 =all, 0, 1, 2 ...)
+        if( hash ){
+
+            s = decodeURIComponent( hash.slice(1) ).match(/(.*):(-?\d+)$/);
+
+            if( s && s[2] /*s.length==3*/ ){
                 //console.log(s);
-                query.value = s[1];
+                self.query.value = s[1];
                 form.start.value = s[2];
                 self.setQuery();
             }
+
         }
     },
 
-    scopeRE : /^(?:author:|name:|contents:|attachment:)/,
+    scopeRE: /^(?:author:|name:|contents:|attachment:)/,
     setQuery: function( ){
 
         this.query.value =
-            this.form.getElement("input[name=scope]:checked").value +
-            this.query.get('value').replace(this.scopeRE, '');
+            this.form.getElement( "input[name=scope]:checked" ).value +
+            this.query.get( "value" ).replace( this.scopeRE, "" );
 
         this.action();
 
@@ -84,17 +89,19 @@ Wiki.Search = new Class({
 
     setScope: function( query ){
 
-        var scope = query.match( this.scopeRE ) ||"";
-        scope = this.form.getElement("input[name=scope][value="+scope+"]");
-        if( scope ){ scope.checked=true; }
+        var scope = query.match( this.scopeRE ) || "";
+        scope = this.form.getElement( "input[name=scope][value=" + scope + "]" );
+        if( scope ){ scope.checked = true; }
 
     },
 
-    setStart: function(event){    
+    setStart: function(event){
+
         event.stop();
-        //this.form.start.value = event.target.get('data-start');
+        //this.form.start.value = event.target.get( "data-start" );
         this.action();
-    },    
+
+    },
 
     action: function(){
 
@@ -103,7 +110,7 @@ Wiki.Search = new Class({
             query = self.query.value,
             result = self.result;
 
-        if( !query || (query.trim()=='') ){
+        if( !query || ( query.trim() == "" ) ){
             return result.empty();
         }
 
@@ -112,27 +119,28 @@ Wiki.Search = new Class({
         new Request.HTML({
             url: self.options.xhrURL,
             data: form,
-            //method: 'post',  //is default
+            //method: "post",  //is default
             update: result,
             onComplete: function(){
-                spin.hide();
-                new GraphBar( result.getElement('.graphBars' ));
-                self.fireEvent('onComplete');
+
+                new GraphBar( result.getElement( ".graphBars" ));
+                self.fireEvent( "onComplete" );
+
             }
         }).send();
 
-        location.hash = '#'+ query+':'+form.start.value;  // push the query into the url history
+        location.hash = "#" + query + ":" + form.start.value;  // push the query into the url history
 
     }
 
 });
 
 /*
-FIXME : hack the old runfullsearch() 
-    <a title="Show items from 21 to 40" 
-        onclick="$('start').value=20; SearchBox.runfullsearch();">2</a>
-    
+FIXME : hack the old runfullsearch()
+    <a title="Show items from 21 to 40"
+        onclick="$("start").value=20; SearchBox.runfullsearch();">2</a>
+
 Better:
     <a title="Show items from 21 to 40" data-start="20" >2</a>
 */
-var SearchBox = { runfullsearch : function(){} }
+var SearchBox = { runfullsearch: function(){} };
\ No newline at end of file

Added: jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Wiki.Behaviors.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Wiki.Behaviors.js?rev=1686927&view=auto
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Wiki.Behaviors.js (added)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/wiki/Wiki.Behaviors.js Mon Jun 22 20:11:42 2015
@@ -0,0 +1,645 @@
+/*
+    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.
+*/
+/*
+Wiki.Behaviours
+    Contains all behaviours added by default to JSPWiki.
+
+    Uses global var "Wiki", and a number of Classes.
+
+Depend on :
+    moo-extend/Behavior.js
+    moo-extend/Color.js
+    moo-extend/Element.Extend.js
+    moo-extend/HighlightQuery.js
+    moo-extend/String.Extend.js
+    moo-extend/Tips.js
+    moo-extend/Array.NaturalSort.js
+
+    wiki/Wiki.js
+    wiki/Category.js
+
+    behaviors/Collapsible.js
+    behaviors/Columns.js
+    behaviors/CommentBox.js
+    behaviors/GraphBar.js
+    behaviors/Element.Reflect.js
+
+    behaviors/Tabs.js
+    behaviors/Accordion.js
+
+    behaviors/Viewer.js
+    behaviors/Viewer.Slimbox.js
+    behaviors/Viewer.Carousel.js
+
+    behaviors/TableX.js
+    behaviors/TableX.Sort.js
+    behaviors/TableX.Filter.js
+    behaviors/TableX.Zebra.js
+*/
+
+!function( wiki ){
+
+var TheSlimbox, T = TableX;
+
+
+/*
+Behavior: GraphBars, Progress-bars
+
+%%progress 50 /%
+%%progress 50/75 /%
+%%progress-inside 50 /%
+%%progress-after 50 /%
+%%progress-red 50 /%
+%%progress-red-striped 50 /%
+*/
+wiki.add("*[class^=progress]", function(element){
+
+    var maxv = 100,
+        value = element.innerHTML.trim(),
+        clazz = ".graphBars-" + element.className;
+
+    //also support  "value / max-value"  format
+    if( /^(\d+)\s*\/\s*(\d+)$/.test(value) ){
+        element.innerHTML = RegExp.$1;
+        maxv = RegExp.$2;
+    }
+
+    ( element.get("tag") + clazz + "-maxv" + maxv ).slick().wraps(element);
+    element.className = "gBar";
+
+
+/*
+%%progress-red-striped 50/%  =>  %%graphBars-progress-red-striped-maxv100 %%gBar 50/% /%
+%%progress-red-striped 50/75 /%  =>  %%graphBars-progress-red-striped-maxv75 %%gBar 50/% /%
+
+*/
+
+})
+
+/*
+Behavior:%%graphBar .. /%
+*/
+    .add("*[class^=graphBars]", GraphBar )
+
+
+    //FIXME -- OBSOLETE ?? top level TAB of the page
+    //.add(".page > .tabmenu a:not([href])", Tab )
+
+/*
+Behavior:tabs & pills
+>   %%tabbedSection .. /%
+>   %%tabs .. /%
+>   %%pills .. /%
+*/
+    .add(".tabbedSection,.tabs", Tab )
+    .add(".pills", Tab, { nav: "ul.nav.nav-pills" } )
+
+/*
+Behavior:Accordion
+>   %%accordion .. /%
+>   %%leftAccordion .. /%
+>   %%rightAccordion .. /%
+>   %%pillsAccordion .. /%
+>   %%tabbedAccordion .. /%
+*/
+    .add("[class^=accordion]", Accordion)
+    .add("[class^=leftAccordion]", Accordion, { type: "pills", position: "pull-left" })
+    .add("[class^=rightAccordion]", Accordion, { type: "pills", position: "pull-right" })
+    .add(".tabbedAccordion", Accordion, { type: "tabs" })
+    .add(".pillsAccordion", Accordion, { type: "pills" })
+
+
+/*
+Behavior:JSPWiki Categories
+>   %%category .. /%
+*/
+    .add( ".category a.wikipage", function(element) {
+
+        new wiki.Category(element, wiki.toPageName(element.href), wiki.XHRCategories);
+
+    })
+
+/*
+Behavior:Alert (based on Bootstrap)
+>   %%alert .. /%
+*/
+    .add(".alert", function(element){
+
+        element.addClass("alert-warning alert-dismissable").grab(
+
+            "button.close[type=button][html=&times;]".slick()
+                .addEvent("click", function(){ element.dispose(); }),
+            "top"
+
+        );
+
+    })
+
+/*
+Behavior: Quote (based on Bootstrap)
+>   %%quote .. /%
+*/
+    .add(".quote", function(element){
+
+        "blockquote".slick().wraps( "p".slick().wraps(element) );
+
+    })
+
+
+/*
+Behavior: Viewer
+>     %%viewer [link to youtube, vimeo, some-wiki-page, http://some-external-site ..] /%
+>     [description | url to youtube... | class="viewer"]
+*/
+    .add("a.viewer, div.viewer a", function( a ){
+
+        Viewer.preload(a.href, { width: 800, height: 600 }, function( element ){
+
+            var next = a.getNext();
+            if( next && next.match("img.outlink") ){ next.dispose(); }
+
+            element.addClass("viewport").replaces(a);
+
+        });
+
+    });
+
+
+/*
+Behavior: Viewer.Slimbox
+    Injects slimbox button, after each link inside the %%slimbox container.
+    The slimbox button opens a modal overlay box with a rich media viewer.
+    When the %%slimbox container encloses multiple links, "next" and "previous" buttons
+    are added to navigate between all media.
+
+Example:
+>    %%slimbox [any supported link] /%
+>    [link-description | link-url | class="slimbox"]
+
+DOM structure:
+
+JSPWiki support attachment links (with paperclip), inline images and external links.
+Notice how inline images are converted to attachement links.
+(start code)
+    div.slimbox
+        a.attachment[href="url.(png|bmp|tiff|jpg|jpeg|gif)"] Image link
+        a.infolink[href="url]
+            img[src=attachment_small.png]   (small jspwiki paperclip)
+
+        img.inline[src="url2"]
+
+        a.external[href="url3"] External link
+        img.outlink[src=out.png]
+(end)
+becomes
+(start code)
+    div.slimbox
+        a.attachment[href="url1"] Image link
+        a.slimboxbtn[href="url1"][title=Image link] &raquo;
+        a.infolink[href="url]
+            img[src=attachment_small.png]   (small paperclip)
+
+        a.attachment[href="url2"] url2
+        a.slimboxbtn[href="url2"][title=url2] &raquo;
+
+        a.external[href="url3"] External link
+        a.slimboxbtn[href="url3"][title=External link]
+        img.outlink[src=out.png]
+(end)
+
+Example of the short notation with the .slimbox class
+>    a.slimbox[href="url"] Link
+becomes
+>    a.slimbox[href="url"] Link
+
+*/
+
+//helper function, to collect the links to be converted
+function filterJSPWikiLinks(element){
+
+    return element.match("a") ?
+        element :
+        element.getElements( element.match(".slimbox-attachments") ?
+            "a[href].attachment" :
+            // otherwise,  catch several different cases in one go
+            //    img:not([src$=/attachment_small.png]):not(.outlink)  ::jspwiki small icons
+            //    a[href].attachment,
+            //    a[href].external,
+            //    a[href].wikipage
+            "img:not([src$=/attachment_small.png]):not(.outlink),a[href].attachment,a[href].external,a[href].wikipage"
+        );
+}
+
+wiki.once("body", function( /*elements*/ ){
+
+        //create singleton TheSlimbox
+        TheSlimbox = new Viewer.Slimbox({
+            hints: {
+                //use defaults as much as possible
+                btn: "slimbox.btn".localize(),
+                caption: " Go to: {0}" //FIXME: "slimbox.caption".localize()
+            }
+        });
+    })
+
+    // [ link-description | link-url | class="slimbox-link" ]
+    // replaces the link by a slimbox-link
+    .add("a.slimbox-link", function( element ){
+
+        TheSlimbox.watch(element);
+
+    })
+
+    .add(".slimbox-attachments,*[class~=slimbox],*[class~=lightbox]", function( element ){
+
+        var arr = filterJSPWikiLinks( element );
+
+        TheSlimbox.watch(arr, "button.slimbox-btn");
+
+        //jspwiki -- replace inlined images by attachment links
+        $$(arr).filter("img[src]").each( function( img ){
+
+            "a.attachment".slick({
+                href: img.src,
+                html: img.title || img.alt
+            }).replaces( img );
+        });
+
+        /*FFS: replacing img[src], should also add the info paperclip
+              .grab( [
+                    "a.infolink",{href:element.src},[
+                        "img[alt="(info)"]",{src:".../attachment_small.png"}
+                        ]
+                    ].slick()
+                )
+        */
+
+    })
+
+/*
+Behavior: Viewer.Carousel (embed auto-rotating media viewer into a wiki page)
+> %%carousel [link-1] [link-2] .. [link-n]/%  =>  carousel viewer next,previous
+> %%carousel-auto [link-1] [link-2] .. [link-n]/%  =>  with auto-rotation
+*/
+    .add( ".carousel", function( element ){
+
+        new Viewer.Carousel( filterJSPWikiLinks( element ), {
+            container: element
+        });
+
+
+    });
+
+
+/*
+Behavior: Collapsible.Box, Collapsible.List
+    Create collabsible boxes and (un)ordered lists.
+    The collapse status (open/close) is persisted in a cookie.
+
+Depends on:
+    Wiki, Cookie, Cookie.Flag, Collapsible, Collapsible.Box, Collapsible.List
+
+>    %%collapse
+>    %%collapsebox
+>    %%collapsebox-closed
+*/
+
+//helper function
+function collapseFn(element, cookie){
+
+    var TCollapsible = Collapsible,
+        clazz = element.className,
+        list = "collapse",
+        box = list + "box";
+
+    cookie = new Cookie.Flags("JSPWikiCollapse" + cookie, {path: wiki.BasePath, duration: 20});
+
+    if( clazz == list ){
+
+        new TCollapsible.List(element, { cookie: cookie });
+
+    } else if( clazz.indexOf(box) == 0 ){
+
+        new TCollapsible.Box(element, {
+            cookie: cookie,
+            collapsed: clazz.indexOf(box + "-closed") == 0
+        });
+    }
+}
+
+wiki
+    .add(".page div[class^=collapse]", collapseFn, wiki.PageName )
+    .add(".sidebar div[class^=collapse]", collapseFn, "Sidebar")
+
+/*
+Behavior:Comment Box
+
+Wiki Markup:
+(start code)
+    %%commentbox .. /%
+    %%commentbox-Caption .... /%
+    %%commentbox
+        !Caption
+        ..
+    /%
+(end)
+*/
+    .add("div[class^=commentbox]", CommentBox, { prefix: "commentbox" } )
+
+
+/*
+Behavior:Columns
+
+>    %%columns(-width) .. /%
+*/
+    .add( "div[class*=columns]", Columns, { prefix: "columns" } )
+
+/*
+Dynamic Style: Code-Prettifier
+    JSPWiki wrapper around http://google-code-prettify.googlecode.com/svn/trunk/README.html
+
+    TODO: add option to overrule the choice of language:
+    >    "bsh", "c", "cc", "cpp", "cs", "csh", "cyc", "cv", "htm", "html",
+    >    "java", "js", "m", "mxml", "perl", "pl", "pm", "py", "rb", "sh",
+    >    "xhtml", "xml", "xsl"
+
+Example:
+>    %%prettify {{{
+>        some code snippet here ...
+>    }}} /%
+
+*/
+    .add("div.prettify pre, div.prettify code", function(element){
+
+        element.addClass("prettyprint");
+
+        //brute-force line-number injection
+        "pre.prettylines".slick({
+
+            html: element.innerHTML.trim().split("\n").map( function(line, i){
+                return i + 1; }
+            ).join("\n")
+
+        }).inject(element, "before");
+
+    })
+    .add("[class*=prettify-nonum] pre, [class*=prettify-nonum] code", function(element){
+
+        element.addClass("prettyprint");
+
+    })
+
+    .once(".prettyprint", prettyPrint)  //after element.prettyPrint decoration, prettify them
+
+
+/*
+Behavior:Reflection for images
+>    %%reflection-30-50    //size of reflection images is 30% height by 50% wide
+*/
+    .add( "[class^=reflection]", function(element){
+
+        var args = "reflection".sliceArgs( element );
+
+        if( args ){
+            element.getElements("img").reflect({
+                height: args[0] / 100,
+                width: args[1] / 100
+            });
+        }
+
+    })
+
+/*
+Behavior: Table behaviors
+
+(start code)
+    %%zebra ... /%              => default odd row colors (light grey)
+    %%zebra-table ... /%     => default odd row colors (light grey)
+    %%zebra-eee ... /%      => odd rows get backgroundcolor #eee
+    %%zebra-pink ... /%      => odd rows get backgroundcolor red
+    %%zebra-eee-red ... /%     => odd rows: #eee, even rows: red
+
+    %%table-striped-bordered-hover-condensed-fit-filter-sort
+    %%sortable .. /%
+    %%table-filter .. /%
+
+    FFS %%table-scrollable  (keep head fixed, max height for the table)
+(end)
+
+*/
+    .add(".zebra,div[class*=zebra]", function(element){
+
+        var args = "zebra".sliceArgs(element);
+
+        element.getElements("table").each(function(table){
+            new T.Zebra(table, { colors: args });
+        });
+
+    })
+
+    .add(".sortable,div[class*=table-]", function(element){
+
+        element.ifClass(element.hasClass("sortable"), "table-sort");
+
+        var args = "table".sliceArgs(element),
+            arg,
+            tables = element.getElements("table"),
+            hints = Object.map({
+                sort: "sort.click",
+                atoz: "sort.ascending",
+                ztoa: "sort.descending"
+            }, String.localize);
+
+        while( args[0] ){
+
+            arg = args.shift();
+
+            if( arg.test("striped|bordered|hover|condensed|fit")){
+
+                element.addClass("table-"+arg);
+
+            }
+
+            else if( arg == "filter" ){
+
+                tables.each( function(t){ new T.Filter(t, {hint: "filter.hint".localize()}); });
+                //new T.Filter(element,{ list:["one$","James"], hint:hints.filter});
+
+            }
+
+            else if( arg == "sort" ){
+
+                element.addClass("sortable");
+                tables.each( function(t){ new T.Sort(t, {hints: hints}); });
+
+            }
+
+        }
+
+    })
+
+
+/*
+Behavior: Scrollable pre area with maximum size (based on BOOTSTRAP)
+
+>   %%scrollable {{{ ... }}}        //max-height=240px (default)
+>   %%scrollable-150 {{{ ... }}}    //max-height=150px
+
+*/
+    .add("[class*=scrollable]", function(element){
+
+        var maxHeight = "scrollable".sliceArgs(element)[0] || "240";
+
+        element.getElements("pre")
+            .addClass("pre-scrollable")
+            .setStyle("maxHeight", maxHeight + "px");
+
+    })
+
+/*
+Behavior: Font Icon style (based on BOOTSTRAP)
+    Convert .icon-<icon-name> into appropriate class-name depending on the font family
+
+    //Glyphicon :  .glyphicon.glyphicon-<icon-name>
+    //Font-Awesome: .fa.fa-<icon-name>
+    FontJspwiki (via icomoon) : .icon-<icon-name>
+*/
+    .add("[class^=icon-]", function(element){
+
+        //element.className="glyphicon glyph"+element.className;
+        //element.className = "fa fa-"+element.className.slice(5);
+
+    })
+
+/*
+Behavior: List (based on BOOTSTRAP)
+
+>   %%list-unstyled-hover-group-nostyle
+
+*/
+    .add("[class*=list]", function(element){
+
+        var args = "list".sliceArgs(element),
+            lists = element.getElements("ul|ol");
+
+        args.each( function( arg ){
+
+            if( arg.test("unstyled|hover|group|nostyle") ){
+
+                lists.addClass( "list-"+arg );
+
+            }
+
+            if( arg.test("group") ){
+
+                lists.each( function(item){
+                    item.getElements("li").addClass("list-group-item");
+                });
+
+            }
+        });
+    })
+
+/*
+Behavior: Labels (based on Bootstrap)
+    Support %%label, %%label-default, %%label-primary, %%label-info, %%label-success; %%label-warning, %%label-danger
+*/
+    .add("[class^=label]", function(element){
+
+        element.addClass( "label".fetchContext(element) );
+
+    })
+
+/*
+Behavior: Tips
+    Add mouse-hover Tips to your pages. Depends on Mootools Tips plugin.
+
+Wiki-markup:
+    > %%tip ... /%
+    > %%tip-Caption ... /%
+
+DOM Structure:
+(start code)
+    //before
+    div.tip-TipCaption ...tip-body...
+
+    //after
+    a.tip-link  Tip Caption
+        div.tip-TipCaption ...tip-body...
+
+(end)
+*/
+    .once("*[class^=tip]", function(tips){
+
+        var caption, more = "tip.default.title".localize();
+
+        tips = tips.map( function(tip){
+
+            caption = (tip.className.split("-")[1]||more).deCamelize();
+            return "a.tip-link".slick({ text: caption }).wraps(tip);
+
+        });
+
+        Tips( tips ); //activate tips behavior
+
+    })
+
+/*
+Behavior: DropCaps
+    Convert the first character of a paragraph to a large "DropCap"
+
+>    %%dropcaps .. /%
+
+*/
+    .add("div.dropcaps", function(element){
+
+        var content, node = element.firstChild;
+
+        if( node.nodeType == 3 ){   // this is a text-node
+
+            content = node.textContent.trim();
+            node.textContent = content.slice(1);  //remove first character
+            //and inject the dropcap character in front of the content
+            "span.dropcaps".slick({text: content.slice(0, 1)}).inject(element, "top");
+
+        }
+
+    })
+
+/*
+Behavior: Add-CSS
+
+>   %%add-css ... /%
+>   %%add-css [some-remote-wiki-page] /%
+*/
+    .add(".add-css", AddCSS)
+
+/*
+Behavior:Flip, Flop
+
+>    %%flip(-w<idth>-h<eight>-primary-info-success-warning-danger-error) .. /%
+>    %%flop(-w<idth>-h<eight>-primary-info-success-warning-danger-error) .. /%
+*/
+    .add( "div[class*=flip]", Flip, { prefix: "flip" } )
+    .add( "div[class*=flop]", Flip, { prefix: "flop" } );
+
+
+}( Wiki );