From: Jean-Philippe Lang <%= submit_tag l(:button_create) %>
- <%= preview_link(preview_board_message_path(@board), 'message-form') %> |
<%= link_to l(:button_cancel), "#", :onclick => '$("#add-message").hide(); return false;' %> <%= l(:label_nothing_to_preview) %>
<%= submit_tag l(:button_save) %> - <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @journal.issue), - "journal-#{@journal.id}-form", - "journal_#{@journal.id}_preview" %> | <%= link_to l(:button_cancel), '#', :onclick => "$('#journal-#{@journal.id}-form').remove(); $('#journal-#{@journal.id}-notes').show(); return false;" %>
- - <% end %> -<%= wikitoolbar_for "journal_#{@journal.id}_notes" %> +<%= wikitoolbar_for "journal_#{@journal.id}_notes", preview_issue_path(:project_id => @project, :issue_id => @journal.issue) %> diff --git a/app/views/messages/_form.html.erb b/app/views/messages/_form.html.erb index d9f48af5c..adba05224 100644 --- a/app/views/messages/_form.html.erb +++ b/app/views/messages/_form.html.erb @@ -24,7 +24,7 @@<%= label_tag "message_content", l(:description_message_content), :class => "hidden-for-sighted" %> <%= f.text_area :content, :cols => 80, :rows => 15, :class => 'wiki-edit', :id => 'message_content' %>
-<%= wikitoolbar_for 'message_content' %> +<%= wikitoolbar_for 'message_content', preview_board_message_path(:board_id => @board, :id => @message) %><%= l(:label_attachment_plural) %>
diff --git a/app/views/messages/edit.html.erb b/app/views/messages/edit.html.erb
index 53948e4da..063676197 100644
--- a/app/views/messages/edit.html.erb
+++ b/app/views/messages/edit.html.erb
@@ -12,6 +12,4 @@
<%= render :partial => 'form',
:locals => {:f => f, :replying => !@message.parent.nil?} %>
<%= submit_tag l(:button_save) %>
- <%= preview_link({:controller => 'messages', :action => 'preview', :board_id => @board, :id => @message}, 'message-form') %>
<% end %>
-
<%= render :partial => 'attachments/form', :locals => {:container => @news} %>
-<%= wikitoolbar_for 'news_description' %> +<%= wikitoolbar_for 'news_description', preview_news_path(:project_id => @project, :id => @news) %> \ No newline at end of file diff --git a/app/views/news/edit.html.erb b/app/views/news/edit.html.erb index 4e4cf2e2b..50b897324 100644 --- a/app/views/news/edit.html.erb +++ b/app/views/news/edit.html.erb @@ -3,9 +3,7 @@ <%= labelled_form_for @news, :html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <%= submit_tag l(:button_save) %> -<%= preview_link preview_news_path(:project_id => @project, :id => @news), 'news-form' %> <% end %> - <% content_for :header_tags do %> <%= stylesheet_link_tag 'scm' %> diff --git a/app/views/news/index.html.erb b/app/views/news/index.html.erb index 2ff5a1c8c..cd51aa950 100644 --- a/app/views/news/index.html.erb +++ b/app/views/news/index.html.erb @@ -12,10 +12,8 @@ :html => { :id => 'news-form', :multipart => true } do |f| %> <%= render :partial => 'news/form', :locals => { :f => f } %> <%= submit_tag l(:button_create) %> -<%= preview_link preview_news_path(:project_id => @project), 'news-form' %> | <%= link_to l(:button_cancel), "#", :onclick => '$("#add-news").hide()' %> <% end if @project %> -<%= submit_tag l(:button_add) %>
<% end %> diff --git a/app/views/previews/issue.html.erb b/app/views/previews/issue.html.erb deleted file mode 100644 index a88bec6fc..000000000 --- a/app/views/previews/issue.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -<% if @notes %> - -<% end %> - -<% if @description %> - -<% end %> diff --git a/app/views/wiki/edit.html.erb b/app/views/wiki/edit.html.erb index 6692cfa6e..60a2f6596 100644 --- a/app/views/wiki/edit.html.erb +++ b/app/views/wiki/edit.html.erb @@ -57,14 +57,11 @@<%= submit_tag l(:button_save) %> - <%= preview_link({:controller => 'wiki', :action => 'preview', :project_id => @project, :id => @page.title }, 'wiki_form') %> - | <%= link_to l(:button_cancel), wiki_page_edit_cancel_path(@page) %> + <%= link_to l(:button_cancel), wiki_page_edit_cancel_path(@page) %>
-<%= wikitoolbar_for 'content_text' %> +<%= wikitoolbar_for 'content_text', preview_project_wiki_page_path(:project_id => @project, :id => @page.title) %> <% end %> - - <% content_for :header_tags do %> <%= robot_exclusion_tag %> <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 6bf07ff34..6e2cd75c5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1030,6 +1030,7 @@ en: label_font_monospace: Monospaced font label_font_proportional: Proportional font label_last_notes: Last notes + label_nothing_to_preview: Nothing to preview button_login: Login button_submit: Submit diff --git a/config/routes.rb b/config/routes.rb index f61f2e067..034498208 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -26,9 +26,8 @@ Rails.application.routes.draw do get 'account/activation_email', :to => 'account#activation_email', :as => 'activation_email' match '/news/preview', :controller => 'previews', :action => 'news', :as => 'preview_news', :via => [:get, :post, :put, :patch] - match '/issues/preview/new/:project_id', :to => 'previews#issue', :as => 'preview_new_issue', :via => [:get, :post, :put, :patch] - match '/issues/preview/edit/:id', :to => 'previews#issue', :as => 'preview_edit_issue', :via => [:get, :post, :put, :patch] match '/issues/preview', :to => 'previews#issue', :as => 'preview_issue', :via => [:get, :post, :put, :patch] + match '/preview/text', :to => 'previews#text', :as => 'preview_text', :via => [:get, :post, :put, :patch] match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post] diff --git a/lib/redmine/wiki_formatting.rb b/lib/redmine/wiki_formatting.rb index f4496d98f..66764551d 100644 --- a/lib/redmine/wiki_formatting.rb +++ b/lib/redmine/wiki_formatting.rb @@ -214,7 +214,7 @@ module Redmine end module Helper - def wikitoolbar_for(field_id) + def wikitoolbar_for(field_id, preview_url = preview_text_path) end def heads_for_wiki_formatter diff --git a/lib/redmine/wiki_formatting/markdown/helper.rb b/lib/redmine/wiki_formatting/markdown/helper.rb index f41fee61c..fac2f8bf3 100644 --- a/lib/redmine/wiki_formatting/markdown/helper.rb +++ b/lib/redmine/wiki_formatting/markdown/helper.rb @@ -19,10 +19,10 @@ module Redmine module WikiFormatting module Markdown module Helper - def wikitoolbar_for(field_id) + def wikitoolbar_for(field_id, preview_url = preview_text_path) heads_for_wiki_formatter url = "#{Redmine::Utils.relative_url_root}/help/#{current_language.to_s.downcase}/wiki_syntax_markdown.html" - javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.draw();") + javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.setPreviewUrl('#{preview_url}'); wikiToolbar.draw();") end def initial_page_content(page) diff --git a/lib/redmine/wiki_formatting/textile/helper.rb b/lib/redmine/wiki_formatting/textile/helper.rb index 92c7c880f..6e8ba9812 100644 --- a/lib/redmine/wiki_formatting/textile/helper.rb +++ b/lib/redmine/wiki_formatting/textile/helper.rb @@ -19,11 +19,11 @@ module Redmine module WikiFormatting module Textile module Helper - def wikitoolbar_for(field_id) + def wikitoolbar_for(field_id, preview_url = preview_text_path) heads_for_wiki_formatter # Is there a simple way to link to a public resource? url = "#{Redmine::Utils.relative_url_root}/help/#{current_language.to_s.downcase}/wiki_syntax_textile.html" - javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.draw();") + javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.setPreviewUrl('#{preview_url}'); wikiToolbar.draw();") end def initial_page_content(page) @@ -33,7 +33,8 @@ module Redmine def heads_for_wiki_formatter unless @heads_for_wiki_formatter_included content_for :header_tags do - javascript_include_tag('jstoolbar/jstoolbar-textile.min') + + javascript_include_tag('jstoolbar/jstoolbar') + + javascript_include_tag('jstoolbar/textile') + javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language.to_s.downcase}") + javascript_tag("var wikiImageMimeTypes = #{Redmine::MimeType.by_type('image').to_json};") + stylesheet_link_tag('jstoolbar') diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 0239e0343..dbe4a32b9 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -449,17 +449,6 @@ function hideModal(el) { modal.dialog("close"); } -function submitPreview(url, form, target) { - $.ajax({ - url: url, - type: 'post', - data: $('#'+form).serialize(), - success: function(data){ - $('#'+target).html(data); - } - }); -} - function collapseScmEntry(id) { $('.'+id).each(function() { if ($(this).hasClass('open')) { @@ -846,6 +835,28 @@ $(document).ready(function(){ toggleDisabledInit(); }); +$(document).ready(function(){ + $('#content').on('click', 'div.jstTabs a.tab-preview', function(event){ + var tab = $(event.target); + + var url = tab.data('url'); + var form = tab.parents('form'); + var jstBlock = tab.parents('.jstBlock'); + + var element = encodeURIComponent(jstBlock.find('.wiki-edit').val()); + var attachments = form.find('.attachments_fields input').serialize(); + + $.ajax({ + url: url, + type: 'post', + data: "text=" + element + '&' + attachments, + success: function(data){ + jstBlock.find('.wiki-preview').html(data); + } + }); + }); +}); + function keepAnchorOnSignIn(form){ var hash = decodeURIComponent(self.document.location.hash); if (hash) { diff --git a/public/javascripts/attachments.js b/public/javascripts/attachments.js index 1baafc0bd..753855f91 100644 --- a/public/javascripts/attachments.js +++ b/public/javascripts/attachments.js @@ -237,8 +237,7 @@ function addInlineAttachmentMarkup(file) { 'selectionStart': cursorPosition + newLineBefore, 'selectionEnd': cursorPosition + inlineFilename.length + newLineBefore }); - $textarea.closest('.jstEditor') - .siblings('.jstElements') + $textarea.parents('.jstBlock') .find('.jstb_img').click(); // move cursor into next line diff --git a/public/javascripts/jstoolbar/jstoolbar-textile.min.js b/public/javascripts/jstoolbar/jstoolbar-textile.min.js deleted file mode 100644 index e087d1cf2..000000000 --- a/public/javascripts/jstoolbar/jstoolbar-textile.min.js +++ /dev/null @@ -1 +0,0 @@ -function jsToolBar(e){if(document.createElement&&e&&("undefined"!=typeof document.selection||"undefined"!=typeof e.setSelectionRange)){if(this.textarea=e,this.editor=document.createElement("div"),this.editor.className="jstEditor",this.textarea.parentNode.insertBefore(this.editor,this.textarea),this.editor.appendChild(this.textarea),this.toolbar=document.createElement("div"),this.toolbar.className="jstElements",this.editor.parentNode.insertBefore(this.toolbar,this.editor),this.editor.addEventListener&&navigator.appVersion.match(/\bMSIE\b/)){this.handle=document.createElement("div"),this.handle.className="jstHandle";var t=this.resizeDragStart,n=this;this.handle.addEventListener("mousedown",function(e){t.call(n,e)},!1),window.addEventListener("unload",function(){n.handle.parentNode.removeChild(n.handle);delete n.handle},!1),this.editor.parentNode.insertBefore(this.handle,this.editor.nextSibling)}this.context=null,this.toolNodes={}}}function jsButton(e,t,n,o){"undefined"==typeof jsToolBar.strings?this.title=e||null:this.title=jsToolBar.strings[e]||e||null,this.fn=t||function(){},this.scope=n||null,this.className=o||null}function jsSpace(e){this.id=e||null,this.width=null}function jsCombo(e,t,n,o,i){this.title=e||null,this.options=t||null,this.scope=n||null,this.fn=o||function(){},this.className=i||null}jsButton.prototype.draw=function(){if(!this.scope)return null;var e=document.createElement("button");e.setAttribute("type","button"),e.tabIndex=200,this.className&&(e.className=this.className),e.title=this.title;var t=document.createElement("span");if(t.appendChild(document.createTextNode(this.title)),e.appendChild(t),void 0!=this.icon&&(e.style.backgroundImage="url("+this.icon+")"),"function"==typeof this.fn){var n=this;e.onclick=function(){try{n.fn.apply(n.scope,arguments)}catch(e){}return!1}}return e},jsSpace.prototype.draw=function(){var e=document.createElement("span");return this.id&&(e.id=this.id),e.appendChild(document.createTextNode(String.fromCharCode(160))),e.className="jstSpacer",this.width&&(e.style.marginRight=this.width+"px"),e},jsCombo.prototype.draw=function(){if(!this.scope||!this.options)return null;var e=document.createElement("select");this.className&&(e.className=className),e.title=this.title;for(var t in this.options){var n=document.createElement("option");n.value=t,n.appendChild(document.createTextNode(this.options[t])),e.appendChild(n)}var o=this;return e.onchange=function(){try{o.fn.call(o.scope,this.value)}catch(e){alert(e)}return!1},e},jsToolBar.prototype={base_url:"",mode:"wiki",elements:{},help_link:"",getMode:function(){return this.mode},setMode:function(e){this.mode=e||"wiki"},switchMode:function(e){e=e||"wiki",this.draw(e)},setHelpLink:function(e){this.help_link=e},button:function(e){var t=this.elements[e];if("function"!=typeof t.fn[this.mode])return null;var n=new jsButton(t.title,t.fn[this.mode],this,"jstb_"+e);return void 0!=t.icon&&(n.icon=t.icon),n},space:function(e){var t=new jsSpace(e);return void 0!==this.elements[e].width&&(t.width=this.elements[e].width),t},combo:function(e){var t=this.elements[e],n=t[this.mode].list.length;if("function"!=typeof t[this.mode].fn||0==n)return null;for(var o={},i=0;n>i;i++){var s=t[this.mode].list[i];o[s]=t.options[s]}return new jsCombo(t.title,o,this,t[this.mode].fn)},draw:function(e){for(this.setMode(e);this.toolbar.hasChildNodes();)this.toolbar.removeChild(this.toolbar.firstChild);this.toolNodes={};var t,n,o;for(var i in this.elements){t=this.elements[i];var s=void 0==t.type||""==t.type||void 0!=t.disabled&&t.disabled||void 0!=t.context&&null!=t.context&&t.context!=this.context;s||"function"!=typeof this[t.type]||(n=this[t.type](i),n&&(o=n.draw()),o&&(this.toolNodes[i]=o,this.toolbar.appendChild(o)))}},singleTag:function(e,t){e=e||null,t=t||e,e&&t&&this.encloseSelection(e,t)},encloseLineSelection:function(e,t,n){this.textarea.focus(),e=e||"",t=t||"";var o,i,s,l,a,r;if("undefined"!=typeof document.selection?s=document.selection.createRange().text:"undefined"!=typeof this.textarea.setSelectionRange&&(o=this.textarea.selectionStart,i=this.textarea.selectionEnd,l=this.textarea.scrollTop,o=this.textarea.value.substring(0,o).replace(/[^\r\n]*$/g,"").length,i=this.textarea.value.length-this.textarea.value.substring(i,this.textarea.value.length).replace(/^[^\r\n]*/,"").length,s=this.textarea.value.substring(o,i)),s.match(/ $/)&&(s=s.substring(0,s.length-1),t+=" "),r="function"==typeof n?s?n.call(this,s):n(""):s?s:"",a=e+r+t,"undefined"!=typeof document.selection){document.selection.createRange().text=a;var c=this.textarea.createTextRange();c.collapse(!1),c.move("character",-t.length),c.select()}else"undefined"!=typeof this.textarea.setSelectionRange&&(this.textarea.value=this.textarea.value.substring(0,o)+a+this.textarea.value.substring(i),s?this.textarea.setSelectionRange(o+a.length,o+a.length):this.textarea.setSelectionRange(o+e.length,o+e.length),this.textarea.scrollTop=l)},encloseSelection:function(e,t,n){this.textarea.focus(),e=e||"",t=t||"";var o,i,s,l,a,r;if("undefined"!=typeof document.selection?s=document.selection.createRange().text:"undefined"!=typeof this.textarea.setSelectionRange&&(o=this.textarea.selectionStart,i=this.textarea.selectionEnd,l=this.textarea.scrollTop,s=this.textarea.value.substring(o,i),o>0&&this.textarea.value.substr(o-1, 1).match(/\S/)&&(e=" "+e),this.textarea.value.substr(i, 1).match(/\S/)&&(t=t+" ")),s.match(/ $/)&&(s=s.substring(0,s.length-1),t+=" "),r="function"==typeof n?s?n.call(this,s):n(""):s?s:"",a=e+r+t,"undefined"!=typeof document.selection){document.selection.createRange().text=a;var c=this.textarea.createTextRange();c.collapse(!1),c.move("character",-t.length),c.select()}else"undefined"!=typeof this.textarea.setSelectionRange&&(this.textarea.value=this.textarea.value.substring(0,o)+a+this.textarea.value.substring(i),s?this.textarea.setSelectionRange(o+a.length,o+a.length):this.textarea.setSelectionRange(o+e.length,o+e.length),this.textarea.scrollTop=l)},stripBaseURL:function(e){if(""!=this.base_url){var t=e.indexOf(this.base_url);0==t&&(e=e.substr(this.base_url.length))}return e}},jsToolBar.prototype.resizeSetStartH=function(){this.dragStartH=this.textarea.offsetHeight+0},jsToolBar.prototype.resizeDragStart=function(e){var t=this;this.dragStartY=e.clientY,this.resizeSetStartH(),document.addEventListener("mousemove",this.dragMoveHdlr=function(e){t.resizeDragMove(e)},!1),document.addEventListener("mouseup",this.dragStopHdlr=function(e){t.resizeDragStop(e)},!1)},jsToolBar.prototype.resizeDragMove=function(e){this.textarea.style.height=this.dragStartH+e.clientY-this.dragStartY+"px"},jsToolBar.prototype.resizeDragStop=function(e){document.removeEventListener("mousemove",this.dragMoveHdlr,!1),document.removeEventListener("mouseup",this.dragStopHdlr,!1)},jsToolBar.prototype.precodeMenu=function(e){for(var t=["c","clojure","cpp","css","delphi","diff","erb","go","groovy","haml","html","java","javascript","json","lua","php","python","ruby","sass","sql","taskpaper","text","xml","yaml"],n=$("\n","\n")}}},jsToolBar.prototype.elements.precode={type:"button",title:"Highlighted code",fn:{wiki:function(){var e=this;this.precodeMenu(function(t){e.encloseLineSelection('
\n',"\n
\n")})}}},jsToolBar.prototype.elements.space4={type:"space"},jsToolBar.prototype.elements.link={type:"button",title:"Wiki link",fn:{wiki:function(){this.encloseSelection("[[","]]")}}},jsToolBar.prototype.elements.img={type:"button",title:"Image",fn:{wiki:function(){this.encloseSelection("!","!")}}},jsToolBar.prototype.elements.space5={type:"space"},jsToolBar.prototype.elements.help={type:"button",title:"Help",fn:{wiki:function(){window.open(this.help_link,"","resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes")}}};
diff --git a/public/javascripts/jstoolbar/jstoolbar.js b/public/javascripts/jstoolbar/jstoolbar.js
index bbda5f8b8..98dae6ff2 100644
--- a/public/javascripts/jstoolbar/jstoolbar.js
+++ b/public/javascripts/jstoolbar/jstoolbar.js
@@ -7,12 +7,12 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
+ *
* DotClear is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with DotClear; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
@@ -34,15 +34,45 @@ function jsToolBar(textarea) {
this.textarea = textarea;
+ this.toolbarBlock = document.createElement('div');
+ this.toolbarBlock.className = 'jstBlock';
+ this.textarea.parentNode.insertBefore(this.toolbarBlock, this.textarea);
+
this.editor = document.createElement('div');
this.editor.className = 'jstEditor';
- this.textarea.parentNode.insertBefore(this.editor,this.textarea);
+ this.preview = document.createElement('div');
+ this.preview.className = 'wiki wiki-preview hidden';
+ this.preview.setAttribute('id', 'preview_' + textarea.getAttribute('id'));
+
this.editor.appendChild(this.textarea);
+ this.editor.appendChild(this.preview);
+
+ this.tabsBlock = document.createElement('div');
+ this.tabsBlock.className = 'jstTabs tabs';
+
+ var This = this;
+ this.writeTab = new jsTab('Write', true);
+ this.writeTab.onclick = function(event) { This.hidePreview.call(This, event); return false; };
+
+ this.previewTab = new jsTab('Preview');
+ this.previewTab.onclick = function(event) { This.showPreview.call(This, event); return false; };
+
+ var elementsTab = document.createElement('li');
+ elementsTab.classList = 'tab-elements';
+
+ var tabs = document.createElement('ul');
+ tabs.appendChild(this.writeTab);
+ tabs.appendChild(this.previewTab);
+ tabs.appendChild(elementsTab);
+ this.tabsBlock.appendChild(tabs);
this.toolbar = document.createElement("div");
this.toolbar.className = 'jstElements';
- this.editor.parentNode.insertBefore(this.toolbar,this.editor);
+ elementsTab.appendChild(this.toolbar);
+
+ this.toolbarBlock.appendChild(this.tabsBlock);
+ this.toolbarBlock.appendChild(this.editor);
// Dragable resizing
if (this.editor.addEventListener && navigator.appVersion.match(/\bMSIE\b/))
@@ -53,19 +83,40 @@ function jsToolBar(textarea) {
var This = this;
this.handle.addEventListener('mousedown',function(event) { dragStart.call(This,event); },false);
// fix memory leak in Firefox (bug #241518)
- window.addEventListener('unload',function() {
+ window.addEventListener('unload',function() {
var del = This.handle.parentNode.removeChild(This.handle);
delete(This.handle);
},false);
-
+
this.editor.parentNode.insertBefore(this.handle,this.editor.nextSibling);
}
-
+
this.context = null;
- this.toolNodes = {}; // lorsque la toolbar est dessinée , cet objet est garni
+ this.toolNodes = {}; // lorsque la toolbar est dessinée , cet objet est garni
// de raccourcis vers les éléments DOM correspondants aux outils.
}
+function jsTab(name, selected) {
+ selected = selected || false;
+ if(typeof jsToolBar.strings == 'undefined') {
+ var tabName = name || null;
+ } else {
+ var tabName = jsToolBar.strings[name] || name || null;
+ }
+
+ var tab = document.createElement('li');
+ var link = document.createElement('a');
+ link.setAttribute('href', '#');
+ link.innerText = tabName;
+ link.className = 'tab-' + name.toLowerCase();
+
+ if (selected == true) {
+ link.classList.add('selected');
+ }
+ tab.appendChild(link)
+
+ return tab;
+}
function jsButton(title, fn, scope, className) {
if(typeof jsToolBar.strings == 'undefined') {
this.title = title || null;
@@ -91,6 +142,7 @@ jsButton.prototype.draw = function() {
if (this.icon != undefined) {
button.style.backgroundImage = 'url('+this.icon+')';
}
+
if (typeof(this.fn) == 'function') {
var This = this;
button.onclick = function() { try { This.fn.apply(This.scope, arguments) } catch (e) {} return false; };
@@ -110,7 +162,7 @@ jsSpace.prototype.draw = function() {
if (this.width) span.style.marginRight = this.width+'px';
return span;
-}
+}
function jsCombo(title, options, scope, fn, className) {
this.title = title || null;
@@ -136,7 +188,7 @@ jsCombo.prototype.draw = function() {
var This = this;
select.onchange = function() {
- try {
+ try {
This.fn.call(This.scope, this.value);
} catch (e) { alert(e); }
@@ -152,7 +204,7 @@ jsToolBar.prototype = {
mode: 'wiki',
elements: {},
help_link: '',
-
+
getMode: function() {
return this.mode;
},
@@ -170,6 +222,10 @@ jsToolBar.prototype = {
this.help_link = link;
},
+ setPreviewUrl: function(url) {
+ this.previewTab.firstChild.setAttribute('data-url', url);
+ },
+
button: function(toolName) {
var tool = this.elements[toolName];
if (typeof tool.fn[this.mode] != 'function') return null;
@@ -292,7 +348,6 @@ jsToolBar.prototype = {
encloseSelection: function(prefix, suffix, fn) {
this.textarea.focus();
-
prefix = prefix || '';
suffix = suffix || '';
@@ -343,7 +398,24 @@ jsToolBar.prototype = {
this.textarea.scrollTop = scrollPos;
}
},
+ showPreview: function(event) {
+ if (event.target.classList.contains('selected')) { return; }
+ this.preview.setAttribute('style', 'min-height: ' + this.textarea.clientHeight + 'px;')
+ this.toolbar.classList.add('hidden');
+ this.textarea.classList.add('hidden');
+ this.preview.classList.remove('hidden');
+ this.tabsBlock.getElementsByClassName('tab-write')[0].classList.remove('selected');
+ event.target.classList.add('selected');
+ },
+ hidePreview: function(event) {
+ if (event.target.classList.contains('selected')) { return; }
+ this.toolbar.classList.remove('hidden');
+ this.textarea.classList.remove('hidden');
+ this.preview.classList.add('hidden');
+ this.tabsBlock.getElementsByClassName('tab-preview')[0].classList.remove('selected');
+ event.target.classList.add('selected');
+ },
stripBaseURL: function(url) {
if (this.base_url != '') {
var pos = url.indexOf(this.base_url);
diff --git a/public/javascripts/jstoolbar/lang/jstoolbar-en.js b/public/javascripts/jstoolbar/lang/jstoolbar-en.js
index 4f72f087e..c52e85508 100644
--- a/public/javascripts/jstoolbar/lang/jstoolbar-en.js
+++ b/public/javascripts/jstoolbar/lang/jstoolbar-en.js
@@ -15,3 +15,5 @@ jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';
+jsToolBar.strings['Write'] = 'Write';
+jsToolBar.strings['Preview'] = 'Preview';
\ No newline at end of file
diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css
index c680fac92..e98350679 100644
--- a/public/stylesheets/application.css
+++ b/public/stylesheets/application.css
@@ -274,7 +274,6 @@ tr.issue.idnt-8 td.subject {padding-left: 136px; background-position: 120px 50%;
tr.issue.idnt-9 td.subject {padding-left: 152px; background-position: 136px 50%;}
table.issue-report {table-layout:fixed;}
-table.issue-report th {white-space: normal;}
tr.entry { border: 1px solid #f8f8f8; }
tr.entry td { white-space: nowrap; }
@@ -678,7 +677,6 @@ span.pagination>span {white-space:nowrap;}
min-height: 2em;
clear:left;
}
-
html>body .tabular p {overflow:hidden;}
.tabular input, .tabular select {max-width:95%}
@@ -730,11 +728,25 @@ label.no-css {
input#time_entry_comments { width: 90%;}
input#months { width: 30px; }
-fieldset.preview {margin-top: 1em; min-width: inherit; background: url(../images/draft.png)}
+.jstBlock .jstTabs, .jstBlock .wiki-preview { width: 99%; }
+
+.jstBlock .jstTabs { padding-right: 6px; }
+.jstBlock .wiki-preview { padding: 2px; }
+.jstBlock .wiki-preview p:first-child { padding-top: 0 !important;}
+.jstBlock .wiki-preview p:last-child { padding-bottom: 0 !important;}
+#content .box .jstBlock .jstTabs li { background-color: #f6f6f6; }
+
+.tabular .wiki-preview, .tabular .jstTabs {width: 95%;}
+.tabular.settings .wiki-preview, .tabular.settings .jstTabs { width: 99%; }
+.tabular .wiki-preview p {
+ min-height: initial;
+ padding: 1em 0 1em 0 !important;
+ overflow: initial;
+}
.tabular.settings p{ padding-left: 300px; }
.tabular.settings label{ margin-left: -300px; width: 295px; }
-.tabular.settings textarea { width: 99%; }
+.tabular.settings textarea, .tabular.settings .wiki-preview, .tabular.settings .jstTabs { width: 99%; }
.settings.enabled_scm table {width:100%}
.settings.enabled_scm td.scm_name{ font-weight: bold; }
diff --git a/public/stylesheets/jstoolbar.css b/public/stylesheets/jstoolbar.css
index 4bf674556..dc195b279 100644
--- a/public/stylesheets/jstoolbar.css
+++ b/public/stylesheets/jstoolbar.css
@@ -1,19 +1,45 @@
+.jstBlock .hidden {
+ display: none;
+}
.jstEditor {
padding-left: 0px;
}
.jstEditor textarea, .jstEditor iframe {
margin: 0;
}
-
.jstHandle {
height: 10px;
font-size: 0.1em;
cursor: s-resize;
/*background: transparent url(img/resizer.png) no-repeat 45% 50%;*/
}
-
+#content .jstTabs.tabs {
+ margin-bottom: 10px;
+}
+#content .jstTabs.tabs li {
+ height: 42px;
+}
+#content .jstTabs.tabs li:before{
+ content: '';
+ display: inline-block;
+ vertical-align: middle;
+ height: 100%;
+}
+#content .jstTabs.tabs li a {
+ display: inline-block;
+ vertical-align: bottom;
+ line-height: 19px;
+}
.jstElements {
- padding: 3px 3px 3px 0;
+ display: inline-block;
+ vertical-align: bottom;
+ border-bottom: 1px solid #bbbbbb;
+ padding-left: 6px;
+ height: 26px;
+}
+.wiki-preview {
+ background-color: #ffffff;
+ border: 1px solid #bbbbbb;
}
.jstElements button {
diff --git a/test/functional/messages_controller_test.rb b/test/functional/messages_controller_test.rb
index 353100a84..0f3d937db 100644
--- a/test/functional/messages_controller_test.rb
+++ b/test/functional/messages_controller_test.rb
@@ -33,7 +33,7 @@ class MessagesControllerTest < Redmine::ControllerTest
assert_select 'h2', :text => 'First post'
end
-
+
def test_show_should_contain_reply_field_tags_for_quoting
@request.session[:user_id] = 2
get :show, :params => {
@@ -214,7 +214,7 @@ class MessagesControllerTest < Redmine::ControllerTest
:id => 1,
:reply => {
:content => 'This is a test reply',
- :subject => 'Test reply'
+ :subject => 'Test reply'
}
}
reply = Message.order('id DESC').first
@@ -265,9 +265,9 @@ class MessagesControllerTest < Redmine::ControllerTest
post :preview, :params => {
:board_id => 1,
:message => {
- :subject => "",
- :content => "Previewed text"
- }
+ :subject => ""
+ },
+ :text => "Previewed text"
}
assert_response :success
assert_include 'Previewed text', response.body
@@ -280,8 +280,8 @@ class MessagesControllerTest < Redmine::ControllerTest
:board_id => 1,
:message => {
:subject => "",
- :content => "Previewed text"
- }
+ },
+ :text => "Previewed text"
}
assert_response :success
assert_include 'Previewed text', response.body
diff --git a/test/functional/previews_controller_test.rb b/test/functional/previews_controller_test.rb
index 65fb9ae77..5512cff2d 100644
--- a/test/functional/previews_controller_test.rb
+++ b/test/functional/previews_controller_test.rb
@@ -28,62 +28,36 @@ class PreviewsControllerTest < Redmine::ControllerTest
:journals, :journal_details,
:news
- def test_preview_new_issue
+ def test_preview_new_issue_description
@request.session[:user_id] = 2
post :issue, :params => {
:project_id => '1',
- :issue => {
- :description => 'Foo'
- }
+ :text => 'Foo'
}
assert_response :success
- assert_select 'fieldset' do
- assert_select 'legend', :text => 'Description'
- assert_select 'p', :text => 'Foo'
- end
+ assert_select 'p', :text => 'Foo'
end
- def test_preview_issue_notes_with_no_change_to_description
+ def test_preview_issue_description
@request.session[:user_id] = 2
post :issue, :params => {
:project_id => '1',
- :id => 1,
- :issue => {
- :description => Issue.find(1).description,
- :notes => 'Foo'
- }
+ :issue_id => 1,
+ :text => 'Unable to print recipes'
}
assert_response :success
- assert_select 'legend', :text => 'Description', :count => 0
- assert_select 'legend', :text => 'Notes'
- end
- def test_preview_issue_notes_with_change_to_description
- @request.session[:user_id] = 2
- post :issue, :params => {
- :project_id => '1',
- :id => 1,
- :issue => {
- :description => 'Changed description',
- :notes => 'Foo'
- }
- }
- assert_response :success
- assert_select 'legend', :text => 'Description'
- assert_select 'legend', :text => 'Notes'
+ assert_select 'p', :text => 'Unable to print recipes'
end
- def test_preview_journal_notes_for_update
+ def test_preview_issue_notes
@request.session[:user_id] = 2
post :issue, :params => {
:project_id => '1',
:id => 1,
- :journal => {
- :notes => 'Foo'
- }
+ :text => 'Foo'
}
assert_response :success
- assert_select 'legend', :text => 'Notes'
assert_select 'p', :text => 'Foo'
end
@@ -92,53 +66,30 @@ class PreviewsControllerTest < Redmine::ControllerTest
@request.session[:user_id] = 2
post :issue, :params => {
:project_id => '1',
- :id => 1,
- :issue => {
- :notes => 'attachment:foo.bar'
- }
+ :issue_id => 1,
+ :field => 'notes',
+ :text => 'attachment:foo.bar'
}
assert_response :success
assert_select 'a.attachment', :text => 'foo.bar'
end
- def test_preview_issue_with_project_changed
- @request.session[:user_id] = 2
- post :issue, :params => {
- :project_id => '1',
- :id => 1,
- :issue => {
- :notes => 'notes',
- :project_id => 2
- }
- }
- assert_response :success
- assert_select 'legend', :text => 'Notes'
- end
-
def test_preview_new_news
get :news, :params => {
:project_id => 1,
- :news => {
- :title => '',
- :description => 'News description',
- :summary => ''
- }
+ :text => 'News description',
}
assert_response :success
- assert_select 'fieldset.preview', :text => /News description/
+ assert_select 'p', :text => /News description/
end
def test_preview_existing_news
get :news, :params => {
:project_id => 1,
:id => 2,
- :news => {
- :title => '',
- :description => 'News description',
- :summary => ''
- }
+ :text => 'News description'
}
assert_response :success
- assert_select 'fieldset.preview', :text => /News description/
+ assert_select 'p', :text => /News description/
end
end
diff --git a/test/integration/attachments_test.rb b/test/integration/attachments_test.rb
index 6fd58ed5f..0b3bf1862 100644
--- a/test/integration/attachments_test.rb
+++ b/test/integration/attachments_test.rb
@@ -77,8 +77,9 @@ class AttachmentsTest < Redmine::IntegrationTest
token = ajax_upload('myupload.jpg', 'JPEG content')
- post '/issues/preview/new/ecookbook', :params => {
- :issue => {:tracker_id => 1, :description => 'Inline upload: !myupload.jpg!'},
+ post '/issues/preview', :params => {
+ :issue => {:tracker_id => 1, :project_id => 'ecookbook'},
+ :text => 'Inline upload: !myupload.jpg!',
:attachments => {'1' => {:filename => 'myupload.jpg', :description => 'My uploaded file', :token => token}}
}
assert_response :success
diff --git a/test/integration/layout_test.rb b/test/integration/layout_test.rb
index a52943254..08e7214ce 100644
--- a/test/integration/layout_test.rb
+++ b/test/integration/layout_test.rb
@@ -63,7 +63,7 @@ class LayoutTest < Redmine::IntegrationTest
Role.anonymous.add_permission! :add_issues
get '/projects/ecookbook/issues/new'
- assert_select 'head script[src^=?]', '/javascripts/jstoolbar/jstoolbar-textile.min.js?'
+ assert_select 'head script[src^=?]', '/javascripts/jstoolbar/jstoolbar.js?'
end
def test_calendar_header_tags
diff --git a/test/integration/routing/previews_test.rb b/test/integration/routing/previews_test.rb
index c6a78758d..c5b4eeaee 100644
--- a/test/integration/routing/previews_test.rb
+++ b/test/integration/routing/previews_test.rb
@@ -19,14 +19,14 @@ require File.expand_path('../../../test_helper', __FILE__)
class RoutingPreviewsTest < Redmine::RoutingTest
def test_previews
- should_route 'GET /issues/preview/new/foo' => 'previews#issue', :project_id => 'foo'
- should_route 'PUT /issues/preview/new/foo' => 'previews#issue', :project_id => 'foo'
- should_route 'POST /issues/preview/new/foo' => 'previews#issue', :project_id => 'foo'
-
- should_route 'GET /issues/preview/edit/321' => 'previews#issue', :id => '321'
- should_route 'PUT /issues/preview/edit/321' => 'previews#issue', :id => '321'
- should_route 'POST /issues/preview/edit/321' => 'previews#issue', :id => '321'
+ should_route 'GET /issues/preview' => 'previews#issue'
+ should_route 'PUT /issues/preview' => 'previews#issue'
+ should_route 'POST /issues/preview' => 'previews#issue'
should_route 'GET /news/preview' => 'previews#news'
+
+ should_route 'GET /preview/text' => 'previews#text'
+ should_route 'PUT /preview/text' => 'previews#text'
+ should_route 'POST /preview/text' => 'previews#text'
end
end
diff --git a/test/system/issues_test.rb b/test/system/issues_test.rb
index 0ffc1c235..f4c52996d 100644
--- a/test/system/issues_test.rb
+++ b/test/system/issues_test.rb
@@ -179,10 +179,10 @@ class IssuesTest < ApplicationSystemTestCase
fill_in 'Subject', :with => 'new issue subject'
fill_in 'Description', :with => 'new issue description'
click_link 'Preview'
+ find 'div.wiki-preview', :visible => true, :text => 'new issue description'
end
- find 'div#preview fieldset', :visible => true, :text => 'new issue description'
assert_difference 'Issue.count' do
- find('input[name=commit]').click
+ click_button('Create')
end
issue = Issue.order('id desc').first
@@ -314,9 +314,9 @@ class IssuesTest < ApplicationSystemTestCase
# Update the notes
fill_in 'Notes', :with => 'Updated notes'
# Preview the change
- click_on 'Preview'
- assert page.has_css?('#journal_2_preview')
- assert page.first('#journal_2_preview').has_content?('Updated notes')
+ page.first('#change-2 a.tab-preview').click
+ assert page.has_css?('#preview_journal_2_notes')
+ assert page.first('#preview_journal_2_notes').has_content?('Updated notes')
# Save
click_on 'Save'