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 2013/08/02 20:37:55 UTC
svn commit: r1509803 [2/4] - in /jspwiki/trunk: ./ jspwiki-war/
jspwiki-war/src/main/config/wro/ jspwiki-war/src/main/java/org/apache/wiki/
jspwiki-war/src/main/scripts/ jspwiki-war/src/main/styles/
jspwiki-war/src/main/webapp/scripts/ jspwiki-war/src/...
Added: jspwiki/trunk/jspwiki-war/src/main/scripts/jspwiki-edit.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/jspwiki-edit.js?rev=1509803&view=auto
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/jspwiki-edit.js (added)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/jspwiki-edit.js Fri Aug 2 18:37:54 2013
@@ -0,0 +1,817 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+/**
+ ** Javascript routines to support JSPWiki Editing
+ ** since v.2.6.0
+ ** uses mootools v1.1
+ **
+ ** Needs jspwiki-common.js and mootools.js
+ ** EditTools object (main object)
+ ** - find&replace functionality : with regexp support
+ ** - included popup-pagelinks routines from Janne
+ **
+ ** TextArea object
+ ** Supports selections inside textarea, in ie and other browsers
+ **/
+var WikiSnippets =
+{
+ getSnippets: function(){
+ // FIXME: This is a kludge; should really insert a Date plugin or something.
+ var now = new Date();
+ var day = ((now.getDate() < 10) ? "0" + now.getDate() : now.getDate())
+ var month = ((now.getMonth() < 9) ? "0" + (now.getMonth()+1) : (now.getMonth()+1) )
+ var currentDate = now.getFullYear() + "-" + month + "-" + day;
+
+ return {
+ "toc" : {
+ snippet:["","[{TableOfContents }]", "\n"],
+ tab:['[{TableOfContents }]', '']
+ },
+ "link" : {
+ snippet:["[","link text|pagename", "]"],
+ tab:['link text','pagename','']
+ },
+ "code" : {
+ snippet:["%%prettify \n{{{\n","some code block", "\n}}}\n/%\n"],
+ tab:['some code block','']
+ },
+ "pre" : {
+ snippet:["{{{\n","some preformatted block", "\n}}}\n"],
+ tab:['some preformatted block','']
+ },
+ "br" : {
+ snippet:['\\\\\n','',''],
+ tab:['']
+ },
+ "bold" : {
+ snippet:["__","some bold text", "__"],
+ tab:['some bold text','']
+ },
+ "italic" : {
+ snippet:["''","some italic text", "''"],
+ tab:['some italic text','']
+ },
+ "h1" : {
+ snippet:["!!!","Heading 1 title\n", ""],
+ tab:["Heading 1 title\n", ""]
+ },
+ "h2" : {
+ snippet:["!!","Heading 2 title", ""],
+ tab:["Heading 2 title\n", ""]
+ },
+ "h3" : {
+ snippet:["!","Heading 3 title", ""],
+ tab:["Heading 3 title\n", ""]
+ },
+ "dl" : {
+ snippet:[";","term:definition text", ""],
+ tab:["term","definition text", ""]
+ },
+ "mono" : {
+ snippet:["{{","some monospaced text", "}}"],
+ tab:["some monospaced text", ""]
+ },
+ "hr" : {
+ snippet:['','----','\n'],
+ tab:['']
+ },
+ "sub" : {
+ snippet:["%%sub ","subscript text", "/%"],
+ tab:['subscript text','']
+ },
+ "sup" : {
+ snippet:["%%sup ","superscript text", "/%"],
+ tab:['superscript text','']
+ },
+ "strike" : {
+ snippet:["%%strike ","strikethrough text", "/%"],
+ tab:['strikethrough text','']
+ },
+ "tab" : {
+ snippet:["%%tabbedSection \n","%%tab-tabTitle1\ntab content 1\n/%\n%%tab-tabTitle2\ntab content 2", "\n/%\n/%\n"],
+ tab:['tabTitle1','tab content 1','tabTitle2','tab content 2','']
+ },
+ "table" : {
+ snippet:["\n","||heading 1||heading 2\n| cell 1 | cell 2", "\n"],
+ tab:['heading 1','heading 2','cell 1','cell 2','']
+ },
+ "img" : {
+ snippet:["","[{Image src='img.jpg' width='..' height='..' align='left|center|right' style='..' class='..' }]", "\n"],
+ tab:['img.jpg', '']
+ },
+ "quote" : {
+ snippet:["%%quote \n","quoted text", "\n/%\n"],
+ tab:['quoted text','']
+ },
+ "%%" : {
+ snippet:["%%","wikistyle\nsome text", "\n/%"],
+ tab:['wikistyle','some text','']
+ },
+ //dynamic snippets
+ "sign" : {
+ snippet:["\\\\\n--",Wiki.UserName+", "+currentDate,"\n"],
+ tab:[Wiki.UserName,currentDate,'']
+ },
+ /* TODO: how to insert the proper current date/timestamp, inline with the preferred time format */
+ /* TODO: Should be localized. */
+ "date" : {
+ //return new object snippet
+ command: function(k) {
+ var dayNames = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
+ monthNames = ["January","February","March","April","May","June","July","August","September","October","November","December"],
+ dt = new Date(),
+ y = dt.getYear();
+ if (y < 1000) y +=1900;
+ var date = dayNames[dt.getDay()] + ", " + monthNames[dt.getMonth()] + " " + dt.getDate() + ", " + y;
+ return {
+ //key:"date", optional
+ snippet:['',date,' '],
+ tab:[date,'']
+ };
+ }
+ }
+ } /* return */
+
+ },
+ getSmartPairs: function(){
+ return {
+ '"' : '"',
+ '(' : ')',
+ '{' : '}',
+ '[' : ']',
+ '<' : '>',
+ "'" : { scope:{ "{{{":"}}}" }, pair:"'" }
+ }
+ }
+}
+
+/*
+ *
+ */
+var EditTools =
+{
+ onPageLoad: function(){
+
+ Wiki.onPageLoad(); //Wiki.onpageload should always run first, but seems not guaranteed on ie so let's do this for sure
+
+ window.onbeforeunload = (function(){
+ var ta = $('editorarea');
+ if(ta.value != ta.defaultValue) return "edit.areyousure".localize();
+ }).bind(this);
+
+ this.wikisnippets = WikiSnippets.getSnippets();
+ this.wikismartpairs = WikiSnippets.getSmartPairs();
+
+ this.mainarea = this.textarea = $('editorarea');
+ if(!this.textarea || !this.textarea.visible) return;
+
+ /* may insert a new this.textarea */
+ this.onPageLoadSectionEdit( );
+
+ //this.ta = new TextArea( this.textarea );
+ this.ta = TextArea.initialize( this.textarea ); //FIXME
+
+ this.onPageLoadResizeTextarea();
+ this.onPageLoadToolbar();
+
+ this.onPageLoadPostEditor();
+ this.onPageLoadPreview();
+
+ this.textarea
+ .addEvent('click', this.getSuggestions.bind(this))
+ .addEvent('keyup', this.getSuggestions.bind(this))
+ .addEvent('change', this.onChangeTextarea.bind(this))
+ .focus();
+
+ /* regularly refresh section-edit toc and sneak-preview */
+ this.textarea.fireEvent.periodical(3000,this.textarea,['change']);
+ },
+
+ /* add textarea resize drag bar */
+ onPageLoadResizeTextarea: function(){
+ var hh=Wiki.prefs.get('EditorSize');
+ if(hh) this.textarea.setStyle('height',hh);
+
+ var h = new Element('div',{
+ 'class':'textarea-resizer',
+ 'title':'edit.resize'.localize()
+ }).injectAfter(this.textarea);
+
+ this.textarea.makeResizable({
+ handle:h,
+ modifiers: {x:false, y:'height'},
+ onComplete: function(){
+ Wiki.prefs.set('EditorSize',this.value.now.y);
+ }
+ });
+ },
+
+ onPageLoadToolbar: function(){
+ $('tools').addClass('collapsebox-closed');
+ Collapsible.render('editform','');
+
+ $('tbREDO').addEvent('click', this.redo.bind(this) );
+ $('tbUNDO').addEvent('click', this.undo.bind(this) );
+ $('doreplace').addEvent('click', this.doReplace.bind(this) );
+ $$('#tools a.tool').addEvent('click',this.toggleSnippet.bind(this) );
+
+ },
+
+ doReplace: function(e){
+ new Event(e).stop();
+
+ var findText = $('tbFIND').value,
+ replaceText = $('tbREPLACE').value,
+ isRegExp = $('tbREGEXP').checked,
+ reGlobal = $('tbGLOBAL').checked ? 'g' : '',
+ reMatchCase = $('tbMatchCASE').checked ? '' : 'i';
+
+ if(findText == '') return;
+
+ var sel = TextArea.getSelection(this.textarea),
+ data = (!sel || (sel=='')) ? this.textarea.value : sel;
+
+ if(!isRegExp){ /* escape all special re characters */
+ var re = new RegExp('([\.\*\\\?\+\[\^\$])','gi');
+ findText = findText.replace(re,'\\$1');
+ }
+
+ var re = new RegExp(findText, reGlobal+reMatchCase+'m'); //multiline
+ if(!re.exec(data)){
+ Wiki.alert('edit.findandreplace.nomatch'.localize());
+ return;// true;
+ }
+ data = data.replace(re, replaceText);
+
+ this.store();
+ if(!sel || (sel=="")){
+ this.textarea.value = data;
+ } else {
+ TextArea.replaceSelection( this.textarea, data );
+ }
+ this.textarea.fireEvent('change');
+ },
+
+ onPageLoadPostEditor: function(){
+ if(window.ie) return;
+
+ $('toolextra').show();
+ this.posteditor = new postEditor.create(this.textarea,'changenote');
+
+ /* patch posteditor DF Jul 07 */
+ /* righ-arrow nok on FF, nop on Safari */
+ this.posteditor.onKeyRight = Class.empty;
+ /* make posteditor changes undoable */
+ this.posteditor.value = function(value) {
+ EditTools.store();
+ this.element.value = value.join('');
+ this.element.fireEvent('change');
+ };
+
+ /* quick dirty patch: backspace should remove only one char and not 4 spaces */
+ this.posteditor.onBackspace=function(e) {
+ var ss = this.ss(), se = this.se();
+ if(ss == se && this.slice(ss - this.tabl,ss) == this.tab) {
+ return;
+ /*
+ e.preventDefault();
+ var start = this.getStart(this.tab), end = this.slice(ss,this.element.value.length);
+ if(start.match(/\n$/g) && end.match(/^\n/g)) {
+ this.value([start,this.slice(ss-1,this.element.value.length)]);
+ } else {
+ this.value([start,end]);
+ }
+ this.selectRange(ss - this.tabl,0);
+ */
+ } else if(ss == se) {
+ var charCode = this.slice(ss - 1,ss),
+ close = this.slice(ss,ss+1),
+ stpair = this.options.smartTypingPairs[charCode];
+ if($type(stpair) == 'string') stpair = { pair : stpair };
+ if(stpair && stpair.pair == close) {
+ this.value([this.getStart(stpair.pair),this.slice(ss,this.element.value.length)]);
+ this.selectRange(ss,0);
+ }
+ }
+ };
+
+
+ /* next extra fix for latest Safari 3.1 cause tabs are not catched anymore in the onkeypress handler */
+ /* TODO: this could be a great workaround for ie as well */
+ if(window.webkit){
+ this.textarea.addEvent('keydown',function(e){
+ if(e.keyCode == 9) EditTools.posteditor.onKeyPress(e);
+ });
+ };
+
+ ['smartpairs', 'tabcompletion'].each( function(el){
+ $(el).setProperty('checked', Wiki.prefs.get(el) || false)
+ .addEvent('click',function(e) {
+ Wiki.prefs.set(el,this.checked);
+ EditTools.initPostEditor();
+ });
+ },this);
+
+ this.initPostEditor();
+ },
+
+ initPostEditor: function(){
+ if(! this.posteditor) return;
+ this.posteditor.changeSmartTypingPairs( $('smartpairs').checked ? this.wikismartpairs : {} );
+ this.posteditor.changeSnippets( $('tabcompletion').checked ? this.wikisnippets : {} );
+ },
+
+ toggleSnippet: function(e) {
+ e = new Event(e).stop();
+
+ var el = e.target,
+ snippy = this.wikisnippets[el.getText()];
+
+ if(!snippy) return;
+
+ var s = TextArea.getSelection(this.textarea),
+ sn1 = snippy.snippet[0],
+ sn2 = snippy.snippet[2],
+ t = snippy.snippet.join('');
+
+ this.store();
+
+ if((el.rel=='break') && (!TextArea.isSelectionAtStartOfLine(this.textarea))) {
+ t = '\n' + t;
+ }
+ if(s) {
+ // toggle markup
+ if((s.indexOf(sn1) == 0) && (s.lastIndexOf(sn2) == (s.length - sn2.length))) {
+ t = s.substring(sn1.length, s.length-sn2.length);
+ } else {
+ t = t.replace( snippy.tab[0], s)
+ }
+ }
+ TextArea.replaceSelection(this.textarea, t);
+ } ,
+
+ // *** UNDO functionality ***
+ $undo: [],
+ $redo: [],
+ $maxundo: 20,
+
+ $get: function() {
+ var ta = this.textarea,
+ sel = TextArea.getSelectionCoordinates(ta);
+ return {
+ main:this.mainarea.value,
+ value:ta.value,
+ cursor:sel,
+ scrollTop:ta.scrollTop,
+ scrollLeft:ta.scrollLeft
+ };
+ },
+ $put: function(o){
+ var ta = this.textarea;
+ this.mainarea.value = o.main;
+ ta.value = o.value;
+ ta.scrollTop = o.scrollTop;
+ ta.scrollLeft = o.scrollLeft;
+ TextArea.setSelection(o.cursor.start,o.cursor.end);
+ ta.fireEvent('change');
+ },
+ store: function() {
+ this.$undo.push( this.$get() );
+ this.$redo = [];
+ if(this.$undo.length > this.$maxundo) this.$undo.shift();
+
+ $('tbUNDO').disabled = '';
+ $('tbREDO').disabled = 'true';
+ },
+ undo: function(e){
+ new Event(e).stop();
+ if(this.$undo.length > 0){
+ $('tbREDO').disabled = '';
+ this.$redo.push( this.$get() );
+ this.$put( this.$undo.pop() );
+ } else {
+ $('tbUNDO').disabled = 'true';
+ }
+ },
+ redo: function(e){
+ new Event(e).stop();
+ if(this.$redo.length > 0){
+ this.$undo.push( this.$get() );
+ this.$put( this.$redo.pop() );
+ $('tbUNDO').disabled = '';
+ } else {
+ $('tbREDO').disabled = 'true';
+ }
+ },
+ // *** end of UNDO functionality ***
+
+ getSuggestions: function() {
+ var textarea = this.textarea,
+ sel = TextArea.getSelectionCoordinates(textarea),
+ val = textarea.value,
+ searchword = '',
+ searchlen = 0;
+
+ var suggestID = 'findSuggestionMenu',
+ suggest = $(suggestID) || new Element('div',{
+ 'id':suggestID
+ }).injectAfter($('favorites').getFirst());
+
+ /* find a partial jspwiki-link 'searchword' */
+ /* look backwards for the start of a wiki-link bracket */
+ for( var i = sel.start-1; i >= 0; i-- ){
+ if( val.charAt(i) == ']' ) break;
+ if( val.charAt(i) == '[' && i < val.length-1 ) {
+ searchword = val.substring(i+1,sel.start);
+ if( searchword.charAt(0) == '{' ) return; // Ignore plugins.
+ if(searchword.indexOf('|') != -1) searchword = searchword.split('|')[1];
+ searchlen = searchword.length;
+
+ if(searchlen == 0) searchword=Wiki.PageName+'/'; /* by default - get list of attachments, if any */
+ break;
+ }
+ }
+ if(searchword =='') return suggest.hide();
+
+ if(sel.start == sel.end) { //when no selection, extend till next ] or end of the line
+ var ss = val.substring(sel.start),
+ end = ss.search(/[\n\r\]]/);
+ if(end!=-1) sel.end = sel.start+end;
+ }
+
+ Wiki.jsonrpc('search.getSuggestions', [searchword,30], function(result,exception){
+ if(exception) {
+ alert(exception.message);
+ } else if(!result.list || (result.list.length == 0)) {
+ suggest.hide();
+ } else {
+ var ul = new Element('ul').inject( suggest.empty().show() );
+ result.list.each( function(rslt) {
+ new Element('li',{
+ 'title':rslt,
+ 'events': {
+ 'click':function(ev){
+ new Event(ev).stop();
+ EditTools.store();
+ TextArea.setSelection(sel.start,sel.end);
+ TextArea.replaceSelection(textarea, rslt.substr(searchlen));
+ sel.end = sel.start + rslt.length - searchlen;
+ },
+ 'mouseout': function(){ this.removeClass('hover') },
+ 'mouseover':function(){ this.addClass('hover') }
+ }
+ }).setHTML(rslt.trunc(36) ).inject(ul);
+ }); /* each */
+ } /* endif */
+ });
+ },
+
+ onPageLoadPreview : function(){
+ var checkbox = $('autopreview');
+
+ if(!checkbox) return;
+
+ checkbox
+ .setProperty('checked', Wiki.prefs.get('autopreview') || false)
+ .addEvent('click', function(){
+ var ta = this.textarea,
+ isOn = checkbox.checked;
+
+ $('sneakpreview').empty();
+ ta.removeEvents('preview');
+ Wiki.prefs.set('autopreview',isOn);
+
+ if(isOn) ta.addEvent('preview', this.refreshPreview.bind(this)).fireEvent('preview');
+
+ }.bind(this)).fireEvent('click');
+ },
+
+ refreshPreview: function(){
+ var preview = $('sneakpreview');
+
+ $('previewSpin').show();
+ new Ajax( Wiki.TemplateUrl + "/AJAXPreview.jsp?page="+Wiki.PageName, {
+ postBody: 'wikimarkup=' + encodeURIComponent(this.textarea.value),
+ update: preview,
+ onComplete: function(){
+ $('previewSpin').hide();
+ Wiki.renderPage(preview, Wiki.PageName);
+ }
+ }).request();
+ },
+
+ onPageLoadSectionEdit : function( ){
+
+ /* section editing is only valid for edit context, not valid in the comment context */
+ if( (Wiki.Context!='edit')
+ ||(Wiki.prefs.get('SectionEditing') != 'on') ) return;
+
+ /* Duplicate the textarea into a main and work area.
+ The workarea is used for actual editing.
+ The mainarea reflects at all times the whole document
+ */
+ this.textarea = this.mainarea.clone()
+ .removeProperty('id')
+ .removeProperty('name')
+ .injectBefore( this.mainarea.hide() );
+
+ var tt = new Element('div',{'id':'toctoc'}).adopt(
+ new Element('label').setHTML('sectionediting.label'.localize()),
+ this.sections = new Element('ul')
+ ).injectTop($('favorites'))
+
+ /* initialise the section sections */
+ this.onSectionLoad();
+
+ var cursor = location.search.match(/[&?]section=(\d+)/);
+ cursor = (cursor && cursor[1]) ? 1+cursor[1].toInt() : 0;
+ if((cursor>0) && this.textarea.sop) cursor++;
+
+ /* initialise the selected section */
+ this.onChangeSection(cursor);
+
+ },
+
+ /*
+ * UPDATE/RFEFRESH the section dropdown
+ * This function is called at startup, and everytime the section textarea changes
+ * Postcondition: the section-edit dropdown contains following entries
+ * 0. ( all )
+ * 1. start-of-page (if applicable)
+ * 2. text==<<header 1...n>> , <<sections.offset stores start-offset in main textarea>>
+ */
+ onSectionLoad : function(){
+ var mainarea = this.mainarea.value,
+ ta = this.textarea,
+ DELIM = "\u00a4";
+
+ /* mask all headers inside a {{{ ... }}} but keep length unchanged! */
+ mainarea = mainarea.replace(/\{\{\{([\s\S]*?)\}\}\}/g, function(match){
+ return match.replace( /^!/mg, ' ' );
+ });
+
+ var tt = mainarea.replace( /^([!]{1,3})/mg, DELIM+"$1"+DELIM ).split(DELIM);
+
+ this.newSection();
+ ta.sop = (tt.length>1) && (tt[0] != ''); //start of page section has no !!!header
+ if(ta.sop) this.addSection("edit.startOfPage".localize(), 0, 0);
+
+ var pos = tt.shift().length,
+ ttlen = tt.map(function(i){ return i.length });
+
+ for(var i=0; i<ttlen.length; i=i+2){
+ var hlen = ttlen[i], //length of header markup !!!,!! or !
+ indent = (hlen==2) ? 1 : (hlen==1) ? 2 : 0,
+ title = tt[i+1].match(/.*?$/m)[0]; //title is first line only
+
+ this.addSection(title, pos, indent);
+ pos += hlen + ttlen[i+1];
+ };
+ },
+
+ setSection: function( cursor ){
+ var els = this.sections.getChildren();
+
+ if(cursor <0 || cursor >= els.length) cursor = 0;
+ els.removeClass('cursor');
+ els[cursor].addClass('cursor');
+ },
+
+ newSection: function(){
+ this.sections.empty();
+ this.sections.offsets = [];
+ this.addSection("edit.allsections".localize(),-1,0);
+ },
+
+ addSection: function(text,offset,indent){
+ text = text.replace(/~([^~])/g, '$1'); /*remove wiki-markup escape chars ~ */
+ this.sections.offsets.push(offset);
+ this.sections.adopt(
+ new Element('li').adopt(
+ new Element('a',{
+ 'class':'action',
+ 'styles': {
+ 'padding-left':(indent+0.5)+'em'
+ },
+ 'title':text,
+ 'events':{
+ 'click':this.onChangeSection.pass([this.sections.offsets.length-1],this)
+ }
+ }).setHTML(text.trunc(30))
+ )
+ );
+ },
+
+ /* the USER clicks a new item from the section dropdown
+ * copy a part of the main textarea to the section textarea
+ */
+ onChangeSection: function(cursor){
+ var se = this.sections.offsets,
+ ta = this.textarea,
+ ma = this.mainarea.value;
+
+ this.setSection(cursor);
+ ta.cursor = cursor;
+ ta.begin = (cursor==0) ? 0 : se[cursor];
+ ta.end = ((cursor==0) || (cursor+1 >= se.length)) ? ma.length : se[cursor+1];
+ ta.value = ma.substring(ta.begin,ta.end);
+ ta.focus();
+ ta.fireEvent('preview');
+ },
+
+ /*
+ * Changes in the section textarea:
+ * happens when
+ * (i) textarea is changed and deselected (click outside the textarea)
+ * (ii) user clicks a toolbar-button
+ * (iii) periodical
+ *
+ * 1) copy section textarea at the right offset of the main textarea
+ * 2) refresh the section-edit menu
+ */
+ onChangeTextarea : function(){
+ var ta = this.textarea, ma = this.mainarea;
+
+ if(ta.value == this.cacheTextarea) return;
+ this.cacheTextarea = ta.value;
+
+ if( this.sections ){
+ var s = ma.value,
+ //insert \n to ensure the next line's !!!header remains at column 0.
+ addNewLine = ((ta.value.slice(-1) != '\n') && (s.charAt(ta.end) =='!')) ? '\n' : '';
+
+ ma.value = s.substring(0, ta.begin) + ta.value + addNewLine + s.substring(ta.end);
+ ta.end = ta.begin + ta.value.length;
+ this.onSectionLoad(); //refresh section-edit menu
+ }
+ ta.fireEvent('preview');
+ }
+}
+
+/*
+ * TextArea support routines
+ */
+//var TextArea = new Class({
+var TextArea =
+{
+ initialize: function(el){
+ this.textarea = $(el);
+ return this;
+ },
+
+ getSelection: function(id){
+ var f = $(id); if(!f) return '';
+
+ // IE fixme: this returns any selection, not only selected text in the textarea
+ //if(window.ie) return document.selection.createRange().text;
+ //return f.getValue().substring(f.selectionStart, f.selectionEnd);
+
+ var cur = this.getSelectionCoordinates(id);
+ return f.getValue().substring(cur.start, cur.end);
+ },
+
+ /*
+ Function: setSelectionRange
+ Selects the selection range of the textarea from start to end
+
+ Arguments:
+ start - start position of the selection
+ end - (optional) end position of the seletion (default == start)
+
+ Returns:
+ Textarea object
+ */
+ setSelection: function(start, end){
+
+ var txta = this.textarea;
+ if(!end) end = start;
+
+ if($defined(txta.setSelectionRange)){
+
+ txta.setSelectionRange(start, end);
+
+ } else {
+
+ var value = txta.value,
+ diff = value.substr(start, end - start).replace(/\r/g, '').length;
+
+ start = value.substr(0, start).replace(/\r/g, '').length;
+
+ var range = txta.createTextRange();
+ range.collapse(true);
+ range.moveEnd('character', start + diff);
+ range.moveStart('character', start);
+ range.select();
+ //textarea.scrollTop = scrollPosition;
+ //textarea.focus();
+
+ }
+ return this;
+
+ },
+
+ // getCursor(id) : returns start offset of cursor (integer)
+ getCursor: function(id) {
+ return this.getSelectionCoordinates(id).start;
+ },
+
+ /*
+ Function: getSelectionCoordinates
+ Returns the selected textarea 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)
+ */
+ getSelectionCoordinates: function(id) {
+ var txta = $(id),
+ pos = {start: 0, end: 0, thin: true};
+
+ if( $defined(txta.selectionStart) ){
+
+ pos = { start: txta.selectionStart, end: txta.selectionEnd };
+
+ } else {
+
+ var range = document.selection.createRange();
+ if (!range || range.parentElement() != txta) return pos;
+ var dup = range.duplicate(),
+ value = txta.value,
+ offset = value.length - value.match(/[\n\r]*$/)[0].length;
+
+ dup.moveToElementText(txta);
+ dup.setEndPoint('StartToEnd', range);
+ pos.end = offset - dup.text.length;
+ dup.setEndPoint('StartToStart', range);
+ pos.start = offset - dup.text.length;
+
+ }
+
+ pos.thin = (pos.start==pos.end);
+ return pos;
+
+ },
+
+ // replaceSelection(id,newtext) replaces the selection with a newtext, an selects the replaced newtext
+ replaceSelection: function(id, newText){
+
+ var value = newText.replace(/\r/g, ''), //$A(arguments).join(''),
+ txta = $(id),
+ scrollTop = txta.scrollTop; //cache top
+
+ if( $defined(txta.selectionStart) ){
+
+ var start = txta.selectionStart,
+ end = txta.selectionEnd,
+ v = txta.value;
+ txta.value = v.substr(0, start) + value + v.substr(end);
+ txta.selectionStart = start;
+ txta.selectionEnd = start + value.length;
+
+ } else {
+
+ txta.focus();
+ var range = document.selection.createRange();
+ range.text = value;
+ range.collapse(true);
+ range.moveStart("character", -value.length);
+ range.select();
+
+ }
+ txta.focus();
+ txta.scrollTop = scrollTop;
+ txta.fireEvent('change');
+ return;
+
+ },
+
+ // isSelectionAtStartOfLine(id): returns boolean indicating whether cursor is at the start of newline
+ isSelectionAtStartOfLine: function(id){
+ var f = $(id); if(!f) return false;
+
+ var i = this.getCursor(id);
+ return( (i<=0) || ( f.value.charAt( i-1 ).match( /[\n\r]/ ) ) );
+ }
+
+};
+
+window.addEvent('load', EditTools.onPageLoad.bind(EditTools) );
\ No newline at end of file
Added: jspwiki/trunk/jspwiki-war/src/main/scripts/jspwiki-prefs.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/jspwiki-prefs.js?rev=1509803&view=auto
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/jspwiki-prefs.js (added)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/jspwiki-prefs.js Fri Aug 2 18:37:54 2013
@@ -0,0 +1,164 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+/**
+ ** Javascript routines to support JSPWiki UserPreferences
+ ** since v.2.6.0
+ ** uses mootools v1.1
+ **/
+
+var WikiPreferences =
+{
+ onPageLoad: function(){
+
+ window.onbeforeunload = (function(){
+
+ if( $('prefs').getFormElements().some(function(el){
+ return (el.getValue() != el.getDefaultValue());
+ }) ) return "prefs.areyousure".localize();
+
+ }).bind(this);
+ },
+
+ savePrefs: function(){
+ var prefs = {
+ 'prefSkin':'SkinName',
+ 'prefTimeZone':'TimeZone',
+ 'prefTimeFormat':'DateFormat',
+ 'prefOrientation':'Orientation',
+ 'editor':'editor',
+ 'prefLanguage':'Language',
+ 'prefSectionEditing':'SectionEditing'
+ };
+ for(var el in prefs){
+ if($(el)) Wiki.prefs.set(prefs[el],$(el).getValue());
+ };
+ }
+}
+
+window.addEvent('load', WikiPreferences.onPageLoad.bind(WikiPreferences) );
+
+// refactor me
+var WikiGroup =
+{
+ MembersID : "membersfield",
+ //GroupTltID : "grouptemplate",
+ GroupID : "groupfield",
+ NewGroupID : "newgroup",
+ GroupInfoID : "groupinfo",
+ CancelBtnID : "cancelButton",
+ SaveBtnID : "saveButton",
+ CreateBtnID : "createButton",
+ DeleteBtnID : "deleteButton",
+ groups : { "(new)": { members:"", groupInfo:"" } },
+ cursor : null,
+ isEditOn : false,
+ isCreateOn : false,
+
+ putGroup: function(group, members, groupInfo, isSelected){
+ this.groups[group] = { members: members, groupInfo: groupInfo };
+
+ var g = $("grouptemplate");
+ gg = g.clone().removeProperty('id').setHTML(group).inject(g.getParent()).show();
+
+ if(isSelected || !this.cursor) this.onMouseOverGroup(gg);
+ } ,
+
+ onMouseOverGroup: function(node){
+ if(this.isEditOn) return;
+ this.setCursor(node);
+
+ var g = this.groups[ (node.id == this.GroupID) ? "(new)": node.innerHTML ];
+ $(this.MembersID).value = g.members;
+ $(this.GroupInfoID).innerHTML = g.groupInfo;
+ } ,
+
+ setCursor: function(node){
+ if(this.cursor) $(this.cursor).removeClass('cursor');
+ this.cursor = $(node).addClass('cursor');
+ } ,
+
+ //create new group: focus on input field
+ onClickNew: function(){
+ if(this.isEditOn) return;
+
+ this.isCreateOn = true;
+ $(this.MembersID).value = "";
+ this.toggle();
+ } ,
+
+ //toggle edit status of Group Editor
+ toggle: function(){
+ this.isEditOn = !this.isEditOn; //toggle
+
+ $(this.MembersID ).disabled =
+ $(this.SaveBtnID ).disabled =
+ $(this.CreateBtnID).disabled =
+ $(this.CancelBtnID).disabled = !this.isEditOn;
+ var del = $(this.DeleteBtnID);
+ if(del) del.disabled = this.isCreateOn || !this.isEditOn;
+
+ if(this.isCreateOn) { $(this.CreateBtnID).toggle(); $(this.SaveBtnID).toggle() };
+
+ var newGrp = $(this.NewGroupID),
+ members = $(this.MembersID);
+
+ if(this.isEditOn){
+ members.getParent().addClass("cursor");
+
+ newGrp.disabled = !this.isCreateOn;
+ if(this.isCreateOn) { newGrp.focus(); } else { members.focus(); }
+ } else {
+ members.getParent().removeClass("cursor");
+
+ if(this.isCreateOn){
+ this.isCreateOn = false;
+ newGrp.value = newGrp.defaultValue;
+ members.value = "";
+ }
+ newGrp.blur();
+ members.blur();
+ newGrp.disabled = false;
+ }
+ } ,
+
+ // submit form to create new group
+ onSubmitNew: function(form, actionURL){
+ var newGrp = $(this.NewGroupID);
+ if(newGrp.value == newGrp.defaultValue){
+ alert("group.validName".localize());
+ newGrp.focus();
+ } else this.onSubmit(form, actionURL);
+ } ,
+
+ // submit form: fill out actual group and members info
+ onSubmit: function(form, actionURL){
+ if(! this.cursor) return false;
+ var g = (this.cursor.id == this.GroupID) ? $(this.NewGroupID).value: this.cursor.innerHTML;
+
+ /* form.action = actionURL; -- doesn't work in IE */
+ form.setAttribute("action", actionURL) ;
+ form.group.value = g;
+ form.members.value = $(this.MembersID).value;
+ form.action.value = "save";
+
+ Wiki.submitOnce(form);
+ form.submit();
+ }
+}
\ No newline at end of file
Added: jspwiki/trunk/jspwiki-war/src/main/scripts/posteditor.js
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/scripts/posteditor.js?rev=1509803&view=auto
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/scripts/posteditor.js (added)
+++ jspwiki/trunk/jspwiki-war/src/main/scripts/posteditor.js Fri Aug 2 18:37:54 2013
@@ -0,0 +1 @@
+eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('k 1K={};1K.2q=2h 2a({r:" ",22:q(G){b.G=1G.12({Z:{},X:{},1g:{}},G||{})},2n:q(1U,N,G){d(2g.2f)t;b.v=$(1U);b.N=$(N);b.22(G);b.15={1a:b.v.1u(\'1t-W\').1q()||14,2p:b.v.1u(\'2o-2m\').1q()||11,W:b.v.1u(\'W\').1q()};b.h=B;b.I=0;b.M=0;b.u=B;b.K=b.r.f;b.v.2e=b.1O.2c(b)},2b:q(Z){b.G.Z=Z||{}},29:q(X){b.G.X=X||{}},28:q(1g){b.G.1g=1g||{}},c:q(){t b.v.1D},j:q(){t b.v.1B},y:q(l,E){t b.v.o.y(l,E)},o:q(o){b.v.o=o.Y("")},F:q(O){k O=O?O.f:0;t b.y(0,b.c()-O)},J:q(O){k O=O?O.f:0;t b.v.o.y(b.j()-O)},w:q(l,E){b.v.1D=l;b.v.1B=l+E},S:q(S,P){d(P){b.10=b.v.10;b.1k=b.v.1k}z{b.v.10=b.10;b.v.1k=b.1k}d(S)b.v.S()},1Z:q(){k 1X=b.F().1j("\\n").f,W=(1X-2l.2k(b.v.10/b.15.1a))*b.15.1a;W+=b.15.1a;d
(W>=b.15.W)b.v.10+=b.15.1a;b.S(H,1)},1O:q(e){d(b.1S(e))t;d(b.1R(e))t;b.1Q(e);d(b.1P(e))t;d([13,9,8,1w].1c(e.1b))b.S(R,H);2d(e.1b){18 27:b.u=B;b.h=B;T;18 1N:b.1M(e);T;18 13:b.1L(e);T;18 9:b.1v(e);T;18 8:b.1I(e);T;18 1w:b.1H(e);T}d([13,9,8,1w].1c(e.1b))b.S(H,R)},1S:q(e){d(e.1F&&e.1b==13){d(b.N){e.U();b.N.S();t H}}t R},1R:q(e){k L=1E.1J(e.L),C=b.G.X[L];d(C){d($P(C)==\'1C\')C={16:C};d(!C.V||b.V(C.V)){k c=b.c(),j=b.j(),l=b.F();d(c==j){b.o([l,C.16,b.J()]);b.w(l.f,0)}z{e.U();b.I=c;b.M=j;b.o([l,L,b.y(c,j),C.16,b.J()]);b.w(c+1,j-c)}}C=B;t H}t R},1Q:q(e){k L=1E.1J(e.L);d(e.26&&e.1F){d([0,1,2,3,4,5,6,7,8,9].1c(L)){k 1s=b.G.1g[L];d(1s){k c=b.c(),j=b.j(),x=1s.1A(b,[b.y(c,j)]);d(x){k l=b.F();d($P(x)==\'1r\'){b.o([l,x.Y(""),b.J()]);b.w(l.f+x[0].f,x[1].f)}z{d(x.19){d(x.p){l=b.y(0,x.19[0]);k E=b.y(x.19[1],b.v.o.f);b.o([l,x.p.Y(""),E]);b.w(l.f+x.p[0].f,x.p[1].f)}z{b.w(x.19[0],x.19[1])}}z{b.o([l,x.p.Y(""),b.J()]);b.w(l.f+x.p[0].f,x.p[1].f)}}}}}}},1P:q(e){d(b.h){k c=b.c(),j=b.j(),D=b.I,E=b.M;d(![D+1,D,
D-1,E].1c(c)){b.u=B;b.h=B}d(b.h&&[25,1N].1c(e.1b)&&c==j){b.u=B;b.h=B}b.I=c;b.M=j}z{b.I=0;b.M=0}t R},V:q(1p){k c=b.c(),Q=b.F();1z(k D 1y 1p){d(!D)t H;k 1n=Q.1o(D);d(1n>-1){k 1m=b.y(1n+D.f,c).1o(1p[D]);d(1m==-1)t H}}t R},1M:q(e){k c=b.c(),j=b.j(),l=b.F();d(c!=j){e.U();b.w(j,0)}},1L:q(e){b.1Z();k c=b.c(),j=b.j(),l=b.F();d(c==j){k 1t=l.1j("\\n").23(),r=1t.1l(/^\\s+/21);d(r){e.U();r=r.Y("");b.o([l,"\\n",r,b.J()]);b.w(c+1+r.f,0)}}},1I:q(e){k c=b.c(),j=b.j();d(c==j&&b.y(c-b.K,c)==b.r){e.U();k l=b.F(b.r),E=b.y(c,b.v.o.f);d(l.1l(/\\n$/g)&&E.1l(/^\\n/g)){b.o([l,b.y(c-1,b.v.o.f)])}z{b.o([l,E])}b.w(c-b.K,0)}z d(c==j){k L=b.y(c-1,c),1m=b.y(c,c+1),C=b.G.X[L];d($P(C)==\'1C\')C={16:C};d(C&&C.16==1m){b.o([b.F(C.16),b.y(c,b.v.o.f)]);b.w(c,0)}}},1H:q(e){k c=b.c(),j=b.j();d(c==j&&b.y(c,c+b.K)==b.r){e.U();b.o([b.F(),b.y(c+b.K,b.v.o.f)]);b.w(c,0)}},1v:q(e){e.U();k c=b.c(),j=b.j(),x=b.y(c,j),Q=b.F();d(b.20(e,c,j))t;d(b.24(e,c,j))t;d(c!=j&&x.1i("\\n")!=-1){k 1Y=x.1h(/\\n/g,"\\n"+b.r);b.o([Q,b.r,1Y,b.J()]);
b.w(c+b.K,j+(b.K*x.1j("\\n").f)-c-b.K)}z{k A=B;1z(k D 1y b.G.Z){k o=b.G.Z[D];d($P(o)==\'q\')1W;d(Q.f-D.f==-1)1W;d(Q.f-D.f==Q.1o(D)){d($P(o)==\'1r\')o={p:o};A=1G.12({},o);T}}d(A&&(!A.V||b.V(A.V))){d(A.17){k 17=A.17.1A(b,[D]);d($P(17)==\'1r\')A.p=17;z A=17}k p=A.p.1f(),r=Q.1j("\\n").23().1l(/^\\s+/21),l=b.F(A.D||D);d(r){r=r.Y("");p[0]=p[0].1h(/\\n/g,"\\n"+r);p[1]=p[1].1h(/\\n/g,"\\n"+r);p[2]=p[2].1h(/\\n/g,"\\n"+r)}b.o([l,p[0],p[1],p[2],b.J()]);d(A.r){b.h={r:A.r.1f(),p:p.1f(),l:A.l};k m=b.h.r.1V();b.h.c=p[1].1i(m);d(b.h.c>-1){b.h.1e=l.f+p[0].f+b.h.c;b.I=b.h.1e;b.M=b.I+m.f;b.u=B;d(A.u){b.h.u=A.u;b.h.m=m;b.h.1d=H;d(2j A.1d==\'2i\')b.h.1d=A.1d;k u=b.h.u[m];d(u){k i=[m].12(u);k a=u.1f().12([\'\']);b.h.1x=m;b.u=a.1T(i)}}b.w(l.f+p[0].f+b.h.c,m.f)}z{b.h=B;b.w(l.f+p[0].f,p[1].f)}}z{b.w(l.f+p[0].f,p[1].f)}p=B}z{b.o([Q,b.r,b.y(c,b.v.o.f)]);d(c==j)b.w(c+b.K,0);z b.w(c+b.K,j-c)}}},24:q(e,c,j){d(b.h){k f=b.h.r.f;d(f){d(b.h.1e<=c){k m=b.h.r.1V(),N=b.y(c,c+b.h.p[1].f-b.h.c).1i(m);d(f==1&&!m){k E=b.h
.p[2].f;d($P(b.h.l)==\'2r\')E=b.h.l;z d(b.h.l)E=0;b.w(j+b.J().1i(b.h.p[2])+E,0);b.u=B;t H}z d(N>-1){b.h.c=N;b.h.1e=N+c;b.I=b.h.1e;b.M=b.I+m.f;b.h.m=m;d(b.u){k u=b.h.u[m];d(u){k i=[m].12(u);k a=u.1f().12([\'\']);b.h.1x=m;b.u=a.1T(i)}z{b.u=B}}b.w(c+N,m.f);t H}z{b.1v(e);t H}}}b.h=B}t R},20:q(e,c,j){d(b.u&&c==b.I&&j==b.M&&b.h.m.f==j-c){k m=b.u[b.h.m];d(m){b.M=b.I+m.f;b.h.m=m;b.o([b.F(),m,b.J()]);b.w(c,m.f);t H}z d(b.h.1d){m=b.h.1x;b.h.m=m;b.M=b.I+m.f;b.o([b.F(),m,b.J()]);b.w(c,m.f);t H}}b.u=B;t R}});',62,152,'|||||||||||this|ss|if||length||autoTab||se|var|start|item||value|snippet|function|tab||return|completion|element|selectRange|sel|slice|else|snippetObj|null|stpair|key|end|getStart|options|true|ssKey|getEnd|tabl|charCode|seKey|next|rest|type|text|false|focus|break|preventDefault|scope|height|smartTypingPairs|join|snippets|scrollTop||extend|||styles|pair|command|case|selection|line_height|keyCode|test|loop|ssLast|copy|selections|replace|indexOf|split|scrollLeft|match|close|open|lastI
ndexOf|scopes|toInt|array|fn|line|getStyle|onTab|46|index|in|for|apply|selectionEnd|string|selectionStart|String|shiftKey|Object|onDelete|onBackspace|fromCharCode|postEditor|onEnter|onKeyRight|39|onKeyPress|filterByTab|filterBySelect|filterByPairs|filterByNext|associate|el|shift|continue|lines|newsel|updateScroll|filterCompletion|gi|setOptions|pop|filterAutoTab|38|ctrlKey||changeSelections|changeSmartTypingPairs|Class|changeSnippets|bind|switch|onkeypress|ActiveXObject|window|new|boolean|typeof|round|Math|size|initialize|font|font_size|create|number'.split('|'),0,{}))
\ No newline at end of file