Project

General

Profile

Feature #27758 » 0002-replace-preview-link-with-write-priview-tabs.patch

Marius BĂLTEANU, 2018-09-09 14:03

View differences:

app/controllers/messages_controller.rb
118 118

  
119 119
  def preview
120 120
    message = @board.messages.find_by_id(params[:id])
121
    @text = (params[:message] || params[:reply])[:content]
121
    @text = params[:text] ? params[:text] : nil
122 122
    @previewed = message
123 123
    render :partial => 'common/preview'
124 124
  end
app/controllers/previews_controller.rb
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 17

  
18 18
class PreviewsController < ApplicationController
19
  before_action :find_project, :find_attachments
19
  before_action :find_project, :except => :text 
20
  before_action :find_attachments
20 21

  
21 22
  def issue
22
    @issue = Issue.visible.find_by_id(params[:id]) unless params[:id].blank?
23
    @issue = Issue.visible.find_by_id(params[:issue_id]) unless params[:issue_id].blank?
23 24
    if @issue
24
      @description = params[:issue] && params[:issue][:description]
25
      if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n")
26
        @description = nil
27
      end
28
      @notes = params[:journal] ? params[:journal][:notes] : nil
29
      @notes ||= params[:issue] ? params[:issue][:notes] : nil
30
    else
31
      @description = (params[:issue] ? params[:issue][:description] : nil)
25
      @previewed = @issue
32 26
    end
33
    render :layout => false
27
    @text = params[:text] ? params[:text] : nil
28
    render :partial => 'common/preview'
34 29
  end
35 30

  
36 31
  def news
37 32
    if params[:id].present? && news = News.visible.find_by_id(params[:id])
38 33
      @previewed = news
39 34
    end
40
    @text = (params[:news] ? params[:news][:description] : nil)
35
    @text = params[:text] ? params[:text] : nil
41 36
    render :partial => 'common/preview'
42 37
  end
43 38

  
39
  def text
40
    @text = params[:text] ? params[:text] : nil
41
    render :partial => 'common/preview'
42
  end
44 43
  private
45 44

  
46 45
  def find_project
app/controllers/wiki_controller.rb
321 321
      @attachments += page.attachments
322 322
      @previewed = page.content
323 323
    end
324
    @text = params[:content][:text]
324
    @text = params[:content].present? ? params[:content][:text] : params[:text]
325 325
    render :partial => 'common/preview'
326 326
  end
327 327

  
app/views/boards/show.html.erb
14 14
<%= form_for @message, :url => new_board_message_path(@board), :html => {:multipart => true, :id => 'message-form'} do |f| %>
15 15
  <%= render :partial => 'messages/form', :locals => {:f => f} %>
16 16
  <p><%= submit_tag l(:button_create) %>
17
  <%= preview_link(preview_board_message_path(@board), 'message-form') %> |
18 17
  <%= link_to l(:button_cancel), "#", :onclick => '$("#add-message").hide(); return false;' %></p>
19 18
<% end %>
20
<div id="preview" class="wiki"></div>
21 19
<% end %>
22 20
</div>
23 21

  
......
60 58
<% end %>
61 59

  
62 60
<% html_title @board.name %>
63

  
64 61
<% content_for :header_tags do %>
65 62
    <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@project}: #{@board}") %>
66 63
<% end %>
app/views/common/_preview.html.erb
1
<fieldset class="preview"><legend><%= l(:label_preview) %></legend>
2
<%= textilizable @text, :attachments => @attachments, :object => @previewed %>
3
</fieldset>
1
<% unless @text.blank? %>
2
  <%= textilizable @text, :attachments => @attachments, :object => @previewed %>
3
<% else	%>
4
  <p><%= l(:label_nothing_to_preview) %></p>
5
<% end %>
app/views/issues/_edit.html.erb
30 30
    <% if @issue.notes_addable? %>
31 31
      <fieldset><legend><%= l(:field_notes) %></legend>
32 32
      <%= f.text_area :notes, :cols => 60, :rows => 10, :class => 'wiki-edit', :no_label => true %>
33
      <%= wikitoolbar_for 'issue_notes' %>
33
      <%= wikitoolbar_for 'issue_notes', preview_issue_path(:project_id => @project, :issue_id => @issue) %>
34 34

  
35 35
      <% if @issue.safe_attribute? 'private_notes' %>
36 36
      <%= f.check_box :private_notes, :no_label => true %> <label for="issue_private_notes"><%= l(:field_private_notes) %></label>
......
68 68
    <%= f.hidden_field :lock_version %>
69 69
    <%= hidden_field_tag 'last_journal_id', params[:last_journal_id] || @issue.last_journal_id %>
70 70
    <%= submit_tag l(:button_submit) %>
71
    <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @issue), 'issue-form' %>
72
    | <%= link_to l(:button_cancel), issue_path(id: @issue.id), :onclick => params[:action] == 'show' ? "$('#update').hide(); return false;" : '' %>
71
    <%= link_to l(:button_cancel), issue_path(id: @issue.id), :onclick => params[:action] == 'show' ? "$('#update').hide(); return false;" : '' %>
73 72

  
74 73
    <%= hidden_field_tag 'prev_issue_id', @prev_issue_id if @prev_issue_id %>
75 74
    <%= hidden_field_tag 'next_issue_id', @next_issue_id if @next_issue_id %>
76 75
    <%= hidden_field_tag 'issue_position', @issue_position if @issue_position %>
77 76
    <%= hidden_field_tag 'issue_count', @issue_count if @issue_count %>
77

  
78 78
<% end %>
79 79

  
80
<div id="preview" class="wiki"></div>
app/views/issues/_form.html.erb
37 37
                   :no_label => true %>
38 38
  <% end %>
39 39
</p>
40
<%= wikitoolbar_for 'issue_description' %>
40
<%= wikitoolbar_for 'issue_description', preview_issue_path(:project_id => @issue.project, :issue_id => @issue.id) %>
41 41
<% end %>
42 42

  
43 43
<div id="attributes" class="attributes">
app/views/issues/new.html.erb
39 39

  
40 40
  <%= submit_tag l(:button_create) %>
41 41
  <%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
42
  <%= preview_link preview_new_issue_path(:project_id => @issue.project), 'issue-form' %>
43 42
<% end %>
44 43

  
45
<div id="preview" class="wiki"></div>
46

  
47 44
<% content_for :header_tags do %>
48 45
    <%= robot_exclusion_tag %>
49 46
<% end %>
app/views/journals/_notes_form.html.erb
14 14
    <% end %>
15 15
    <%= call_hook(:view_journals_notes_form_after_notes, { :journal => @journal}) %>
16 16
    <p><%= submit_tag l(:button_save) %>
17
    <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @journal.issue), 
18
                     "journal-#{@journal.id}-form",
19
                     "journal_#{@journal.id}_preview" %> |
20 17
    <%= link_to l(:button_cancel), '#', :onclick => "$('#journal-#{@journal.id}-form').remove(); $('#journal-#{@journal.id}-notes').show(); return false;" %></p>
21

  
22
    <div id="journal_<%= @journal.id %>_preview" class="wiki"></div>
23 18
<% end %>
24
<%= wikitoolbar_for "journal_#{@journal.id}_notes" %>
19
<%= wikitoolbar_for "journal_#{@journal.id}_notes", preview_issue_path(:project_id => @project, :issue_id => @journal.issue) %>
app/views/messages/_form.html.erb
24 24
<p>
25 25
<%= label_tag "message_content", l(:description_message_content), :class => "hidden-for-sighted" %>
26 26
<%= f.text_area :content, :cols => 80, :rows => 15, :class => 'wiki-edit', :id => 'message_content' %></p>
27
<%= wikitoolbar_for 'message_content' %>
27
<%= wikitoolbar_for 'message_content', preview_board_message_path(:board_id => @board, :id => @message) %>
28 28
<!--[eoform:message]-->
29 29

  
30 30
<p><%= l(:label_attachment_plural) %><br />
app/views/messages/edit.html.erb
12 12
  <%= render :partial => 'form',
13 13
             :locals => {:f => f, :replying => !@message.parent.nil?} %>
14 14
  <%= submit_tag l(:button_save) %>
15
  <%= preview_link({:controller => 'messages', :action => 'preview', :board_id => @board, :id => @message}, 'message-form') %>
16 15
<% end %>
17
<div id="preview" class="wiki"></div>
app/views/messages/new.html.erb
3 3
<%= form_for @message, :url => {:action => 'new'}, :html => {:multipart => true, :id => 'message-form'} do |f| %>
4 4
  <%= render :partial => 'form', :locals => {:f => f} %>
5 5
  <%= submit_tag l(:button_create) %>
6
  <%= preview_link({:controller => 'messages', :action => 'preview', :board_id => @board}, 'message-form') %>
7 6
<% end %>
8

  
9
<div id="preview" class="wiki"></div>
app/views/messages/show.html.erb
85 85
<%= form_for @reply, :as => :reply, :url => {:action => 'reply', :id => @topic}, :html => {:multipart => true, :id => 'message-form'} do |f| %>
86 86
  <%= render :partial => 'form', :locals => {:f => f, :replying => true} %>
87 87
  <%= submit_tag l(:button_submit) %>
88
  <%= preview_link({:controller => 'messages', :action => 'preview', :board_id => @board}, 'message-form') %>
89 88
<% end %>
90
<div id="preview" class="wiki"></div>
91 89
</div>
92 90
<% end %>
93 91

  
app/views/news/_form.html.erb
7 7
<p id="attachments_form"><label><%= l(:label_attachment_plural) %></label><%= render :partial => 'attachments/form', :locals => {:container => @news} %></p>
8 8
</div>
9 9

  
10
<%= wikitoolbar_for 'news_description' %>
10
<%= wikitoolbar_for 'news_description', preview_news_path(:project_id => @project, :id => @news) %>
app/views/news/edit.html.erb
3 3
<%= labelled_form_for @news, :html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %>
4 4
<%= render :partial => 'form', :locals => { :f => f } %>
5 5
<%= submit_tag l(:button_save) %>
6
<%= preview_link preview_news_path(:project_id => @project, :id => @news), 'news-form' %>
7 6
<% end %>
8
<div id="preview" class="wiki"></div>
9 7

  
10 8
<% content_for :header_tags do %>
11 9
  <%= stylesheet_link_tag 'scm' %>
app/views/news/index.html.erb
12 12
                                           :html => { :id => 'news-form', :multipart => true } do |f| %>
13 13
<%= render :partial => 'news/form', :locals => { :f => f } %>
14 14
<%= submit_tag l(:button_create) %>
15
<%= preview_link preview_news_path(:project_id => @project), 'news-form' %> |
16 15
<%= link_to l(:button_cancel), "#", :onclick => '$("#add-news").hide()' %>
17 16
<% end if @project %>
18
<div id="preview" class="wiki"></div>
19 17
</div>
20 18

  
21 19
<h2><%=l(:label_news_plural)%></h2>
app/views/news/new.html.erb
4 4
                                           :html => { :id => 'news-form', :multipart => true } do |f| %>
5 5
  <%= render :partial => 'news/form', :locals => { :f => f } %>
6 6
  <%= submit_tag l(:button_create) %>
7
  <%= preview_link preview_news_path(:project_id => @project), 'news-form' %>
8 7
<% end %>
9
<div id="preview" class="wiki"></div>
app/views/news/show.html.erb
16 16
                                           :html => { :id => 'news-form', :multipart => true, :method => :put } do |f| %>
17 17
<%= render :partial => 'form', :locals => { :f => f } %>
18 18
<%= submit_tag l(:button_save) %>
19
<%= preview_link preview_news_path(:project_id => @project, :id => @news), 'news-form' %> |
20 19
<%= link_to l(:button_cancel), "#", :onclick => '$("#edit-news").hide(); return false;' %>
21 20
<% end %>
22
<div id="preview" class="wiki"></div>
23 21
</div>
24 22
<% end %>
25 23

  
......
56 54
<%= form_tag({:controller => 'comments', :action => 'create', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %>
57 55
<div class="box">
58 56
    <%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit' %>
59
    <%= wikitoolbar_for 'comment_comments' %>
57
    <%= wikitoolbar_for 'comment_comments', preview_news_path(:project_id => @project, :id => @news) %>
60 58
</div>
61 59
<p><%= submit_tag l(:button_add) %></p>
62 60
<% end %>
app/views/previews/issue.html.erb
1
<% if @notes %>
2
  <fieldset class="preview"><legend><%= l(:field_notes) %></legend>
3
    <%= textilizable @notes, :attachments => @attachments, :object => @issue %>
4
  </fieldset>
5
<% end %>
6

  
7
<% if @description %>
8
  <fieldset class="preview"><legend><%= l(:field_description) %></legend>
9
    <%= textilizable @description, :attachments => @attachments, :object => @issue %>
10
  </fieldset>
11
<% end %>
app/views/wiki/edit.html.erb
57 57

  
58 58
<p>
59 59
  <%= submit_tag l(:button_save) %>
60
   <%= preview_link({:controller => 'wiki', :action => 'preview', :project_id => @project, :id => @page.title }, 'wiki_form') %>
61
   | <%= link_to l(:button_cancel), wiki_page_edit_cancel_path(@page) %>
60
  <%= link_to l(:button_cancel), wiki_page_edit_cancel_path(@page) %>
62 61
 </p>
63
<%= wikitoolbar_for 'content_text' %>
62
<%= wikitoolbar_for 'content_text', preview_project_wiki_page_path(:project_id => @project, :id => @page.title) %>
64 63
<% end %>
65 64

  
66
<div id="preview" class="wiki"></div>
67

  
68 65
<% content_for :header_tags do %>
69 66
  <%= robot_exclusion_tag %>
70 67
<% end %>
config/locales/en.yml
1026 1026
  label_font_monospace: Monospaced font
1027 1027
  label_font_proportional: Proportional font
1028 1028
  label_last_notes: Last notes
1029
  label_nothing_to_preview: Nothing to preview
1029 1030

  
1030 1031
  button_login: Login
1031 1032
  button_submit: Submit
config/routes.rb
26 26
  get 'account/activation_email', :to => 'account#activation_email', :as => 'activation_email'
27 27

  
28 28
  match '/news/preview', :controller => 'previews', :action => 'news', :as => 'preview_news', :via => [:get, :post, :put, :patch]
29
  match '/issues/preview/new/:project_id', :to => 'previews#issue', :as => 'preview_new_issue', :via => [:get, :post, :put, :patch]
30
  match '/issues/preview/edit/:id', :to => 'previews#issue', :as => 'preview_edit_issue', :via => [:get, :post, :put, :patch]
31 29
  match '/issues/preview', :to => 'previews#issue', :as => 'preview_issue', :via => [:get, :post, :put, :patch]
30
  match '/preview/text', :to => 'previews#text', :as => 'preview_text', :via => [:get, :post, :put, :patch]
32 31

  
33 32
  match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post]
34 33

  
......
156 155
        end
157 156
      end
158 157
    end
159
  
158

  
160 159
    match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get
161 160
    resources :wiki, :except => [:index, :create], :as => 'wiki_page' do
162 161
      member do
lib/redmine/wiki_formatting.rb
214 214
      end
215 215

  
216 216
      module Helper
217
        def wikitoolbar_for(field_id)
217
        def wikitoolbar_for(field_id, preview_url = preview_text_path)
218 218
        end
219 219

  
220 220
        def heads_for_wiki_formatter
lib/redmine/wiki_formatting/markdown/helper.rb
19 19
  module WikiFormatting
20 20
    module Markdown
21 21
      module Helper
22
        def wikitoolbar_for(field_id)
22
        def wikitoolbar_for(field_id, preview_url = preview_text_path)
23 23
          heads_for_wiki_formatter
24 24
          url = "#{Redmine::Utils.relative_url_root}/help/#{current_language.to_s.downcase}/wiki_syntax_markdown.html"
25
          javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.draw();")
25
          javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.setPreviewUrl('#{preview_url}'); wikiToolbar.draw();")
26 26
        end
27 27

  
28 28
        def initial_page_content(page)
lib/redmine/wiki_formatting/textile/helper.rb
19 19
  module WikiFormatting
20 20
    module Textile
21 21
      module Helper
22
        def wikitoolbar_for(field_id)
22
        def wikitoolbar_for(field_id, preview_url = preview_text_path)
23 23
          heads_for_wiki_formatter
24 24
          # Is there a simple way to link to a public resource?
25 25
          url = "#{Redmine::Utils.relative_url_root}/help/#{current_language.to_s.downcase}/wiki_syntax_textile.html"
26
          javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.draw();")
26
          javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.setPreviewUrl('#{preview_url}'); wikiToolbar.draw();")
27 27
        end
28 28

  
29 29
        def initial_page_content(page)
public/javascripts/application.js
449 449
  modal.dialog("close");
450 450
}
451 451

  
452
function submitPreview(url, form, target) {
453
  $.ajax({
454
    url: url,
455
    type: 'post',
456
    data: $('#'+form).serialize(),
457
    success: function(data){
458
      $('#'+target).html(data);
459
    }
460
  });
461
}
462

  
463 452
function collapseScmEntry(id) {
464 453
  $('.'+id).each(function() {
465 454
    if ($(this).hasClass('open')) {
......
846 835
  toggleDisabledInit();
847 836
});
848 837

  
838
$(document).ready(function(){
839
  $('#content').on('click', 'div.jstTabs a.tab-preview', function(event){
840
    var tab = $(event.target);
841

  
842
    var url = tab.data('url');
843
    var form = tab.parents('form');
844
    var jstBlock = tab.parents('.jstBlock');
845

  
846
    var element = encodeURIComponent(jstBlock.find('.wiki-edit').val());
847
    var attachments = form.find('.attachments_fields input').serialize();
848

  
849
    $.ajax({
850
      url: url,
851
      type: 'post',
852
      data: "text=" + element + '&' + attachments,
853
      success: function(data){
854
        jstBlock.find('.wiki-preview').html(data);
855
      }
856
    });
857
  });
858
});
859

  
849 860
function keepAnchorOnSignIn(form){
850 861
  var hash = decodeURIComponent(self.document.location.hash);
851 862
  if (hash) {
public/javascripts/attachments.js
237 237
      'selectionStart': cursorPosition + newLineBefore,
238 238
      'selectionEnd': cursorPosition + inlineFilename.length + newLineBefore
239 239
    });
240
    $textarea.closest('.jstEditor')
241
      .siblings('.jstElements')
240
    $textarea.parents('.jstBlock')
242 241
      .find('.jstb_img').click();
243 242

  
244 243
    // move cursor into next line
public/javascripts/jstoolbar/jstoolbar.js
7 7
 * it under the terms of the GNU General Public License as published by
8 8
 * the Free Software Foundation; either version 2 of the License, or
9 9
 * (at your option) any later version.
10
 * 
10
 *
11 11
 * DotClear is distributed in the hope that it will be useful,
12 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 14
 * GNU General Public License for more details.
15
 * 
15
 *
16 16
 * You should have received a copy of the GNU General Public License
17 17
 * along with DotClear; if not, write to the Free Software
18 18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
......
34 34

  
35 35
  this.textarea = textarea;
36 36

  
37
  this.toolbarBlock = document.createElement('div');
38
  this.toolbarBlock.className = 'jstBlock';
39
  this.textarea.parentNode.insertBefore(this.toolbarBlock, this.textarea);
40

  
37 41
  this.editor = document.createElement('div');
38 42
  this.editor.className = 'jstEditor';
39 43

  
40
  this.textarea.parentNode.insertBefore(this.editor,this.textarea);
44
  this.preview = document.createElement('div');
45
  this.preview.className = 'wiki wiki-preview hidden';
46
  this.preview.setAttribute('id', 'preview_' + textarea.getAttribute('id'));
47

  
41 48
  this.editor.appendChild(this.textarea);
49
  this.editor.appendChild(this.preview);
50

  
51
  this.tabsBlock = document.createElement('div');
52
  this.tabsBlock.className = 'jstTabs tabs';
53

  
54
  var This = this;
55
  this.writeTab = new jsTab('Write', true);
56
  this.writeTab.onclick = function(event) { This.hidePreview.call(This, event); return false; };
57

  
58
  this.previewTab = new jsTab('Preview');
59
  this.previewTab.onclick = function(event) { This.showPreview.call(This, event); return false; };
60

  
61
  var elementsTab = document.createElement('li');
62
  elementsTab.classList = 'tab-elements';
63

  
64
  var tabs = document.createElement('ul');
65
  tabs.appendChild(this.writeTab);
66
  tabs.appendChild(this.previewTab);
67
  tabs.appendChild(elementsTab);
68
  this.tabsBlock.appendChild(tabs);
42 69

  
43 70
  this.toolbar = document.createElement("div");
44 71
  this.toolbar.className = 'jstElements';
45
  this.editor.parentNode.insertBefore(this.toolbar,this.editor);
72
  elementsTab.appendChild(this.toolbar);
73

  
74
  this.toolbarBlock.appendChild(this.tabsBlock);
75
  this.toolbarBlock.appendChild(this.editor);
46 76

  
47 77
  // Dragable resizing
48 78
  if (this.editor.addEventListener && navigator.appVersion.match(/\bMSIE\b/))
......
53 83
    var This = this;
54 84
    this.handle.addEventListener('mousedown',function(event) { dragStart.call(This,event); },false);
55 85
    // fix memory leak in Firefox (bug #241518)
56
    window.addEventListener('unload',function() { 
86
    window.addEventListener('unload',function() {
57 87
      var del = This.handle.parentNode.removeChild(This.handle);
58 88
      delete(This.handle);
59 89
    },false);
60
    
90

  
61 91
    this.editor.parentNode.insertBefore(this.handle,this.editor.nextSibling);
62 92
  }
63
  
93

  
64 94
  this.context = null;
65
  this.toolNodes = {}; // lorsque la toolbar est dessinée , cet objet est garni 
95
  this.toolNodes = {}; // lorsque la toolbar est dessinée , cet objet est garni
66 96
                       // de raccourcis vers les éléments DOM correspondants aux outils.
67 97
}
68 98

  
99
function jsTab(name, selected) {
100
  selected = selected || false;
101
  if(typeof jsToolBar.strings == 'undefined') {
102
    var tabName = name || null;
103
  } else {
104
    var tabName = jsToolBar.strings[name] || name || null;
105
  }
106

  
107
  var tab = document.createElement('li');
108
  var link = document.createElement('a');
109
  link.setAttribute('href', '#');
110
  link.innerText = tabName;
111
  link.className = 'tab-' + name.toLowerCase();
112

  
113
  if (selected == true) {
114
    link.classList.add('selected');
115
  }
116
  tab.appendChild(link)
117

  
118
  return tab;
119
}
69 120
function jsButton(title, fn, scope, className) {
70 121
  if(typeof jsToolBar.strings == 'undefined') {
71 122
    this.title = title || null;
......
91 142
  if (this.icon != undefined) {
92 143
    button.style.backgroundImage = 'url('+this.icon+')';
93 144
  }
145

  
94 146
  if (typeof(this.fn) == 'function') {
95 147
    var This = this;
96 148
    button.onclick = function() { try { This.fn.apply(This.scope, arguments) } catch (e) {} return false; };
......
110 162
  if (this.width) span.style.marginRight = this.width+'px';
111 163

  
112 164
  return span;
113
} 
165
}
114 166

  
115 167
function jsCombo(title, options, scope, fn, className) {
116 168
  this.title = title || null;
......
136 188

  
137 189
  var This = this;
138 190
  select.onchange = function() {
139
    try { 
191
    try {
140 192
      This.fn.call(This.scope, this.value);
141 193
    } catch (e) { alert(e); }
142 194

  
......
152 204
  mode: 'wiki',
153 205
  elements: {},
154 206
  help_link: '',
155
  
207

  
156 208
  getMode: function() {
157 209
    return this.mode;
158 210
  },
......
170 222
    this.help_link = link;
171 223
  },
172 224

  
225
  setPreviewUrl: function(url) {
226
    this.previewTab.firstChild.setAttribute('data-url', url);
227
  },
228

  
173 229
  button: function(toolName) {
174 230
    var tool = this.elements[toolName];
175 231
    if (typeof tool.fn[this.mode] != 'function') return null;
......
292 348

  
293 349
  encloseSelection: function(prefix, suffix, fn) {
294 350
    this.textarea.focus();
295

  
296 351
    prefix = prefix || '';
297 352
    suffix = suffix || '';
298 353

  
......
343 398
      this.textarea.scrollTop = scrollPos;
344 399
    }
345 400
  },
401
  showPreview: function(event) {
402
    if (event.target.classList.contains('selected')) { return; }
403
    this.preview.setAttribute('style', 'min-height: ' + this.textarea.clientHeight + 'px;')
404
    this.toolbar.classList.add('hidden');
405
    this.textarea.classList.add('hidden');
406
    this.preview.classList.remove('hidden');
407
    this.tabsBlock.getElementsByClassName('tab-write')[0].classList.remove('selected');
408
    event.target.classList.add('selected');
346 409

  
410
  },
411
  hidePreview: function(event) {
412
    if (event.target.classList.contains('selected')) { return; }
413
    this.toolbar.classList.remove('hidden');
414
    this.textarea.classList.remove('hidden');
415
    this.preview.classList.add('hidden');
416
    this.tabsBlock.getElementsByClassName('tab-preview')[0].classList.remove('selected');
417
    event.target.classList.add('selected');
418
  },
347 419
  stripBaseURL: function(url) {
348 420
    if (this.base_url != '') {
349 421
      var pos = url.indexOf(this.base_url);
public/javascripts/jstoolbar/lang/jstoolbar-en.js
15 15
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
16 16
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
17 17
jsToolBar.strings['Image'] = 'Image';
18
jsToolBar.strings['Write'] = 'Write';
19
jsToolBar.strings['Preview'] = 'Preview';
public/stylesheets/application.css
273 273
tr.issue.idnt-9 td.subject {padding-left: 152px; background-position: 136px 50%;}
274 274

  
275 275
table.issue-report {table-layout:fixed;}
276
table.issue-report th {white-space: normal;}
277 276

  
278 277
tr.entry { border: 1px solid #f8f8f8; }
279 278
tr.entry td { white-space: nowrap; }
......
679 678
  min-height: 2em;
680 679
  clear:left;
681 680
}
682

  
683 681
html>body .tabular p {overflow:hidden;}
684 682

  
685 683
.tabular input, .tabular select {max-width:95%}
......
731 729
input#time_entry_comments { width: 90%;}
732 730
input#months { width: 30px; }
733 731

  
734
fieldset.preview {margin-top: 1em; min-width: inherit; background: url(../images/draft.png)}
732
.jstBlock .jstTabs, .jstBlock .wiki-preview { width: 99%; }
733

  
734
.jstBlock .jstTabs { padding-right: 6px; }
735
.jstBlock .wiki-preview { padding: 2px; }
736
.jstBlock .wiki-preview p:first-child { padding-top: 0 !important;}
737
.jstBlock .wiki-preview p:last-child { padding-bottom: 0 !important;}
738
#content .box .jstBlock .jstTabs li { background-color: #f6f6f6; }
739

  
740
.tabular .wiki-preview, .tabular .jstTabs {width: 95%;}
741
.tabular.settings .wiki-preview, .tabular.settings .jstTabs { width: 99%; }
742
.tabular .wiki-preview p {
743
  min-height: initial;
744
  padding: 1em 0 1em 0 !important;
745
  overflow: initial;
746
}
735 747

  
736 748
.tabular.settings p{ padding-left: 300px; }
737 749
.tabular.settings label{ margin-left: -300px; width: 295px; }
738
.tabular.settings textarea { width: 99%; }
750
.tabular.settings textarea, .tabular.settings .wiki-preview, .tabular.settings .jstTabs { width: 99%; }
739 751

  
740 752
.settings.enabled_scm table {width:100%}
741 753
.settings.enabled_scm td.scm_name{ font-weight: bold; }
public/stylesheets/jstoolbar.css
1
.jstBlock .hidden {
2
    display: none;
3
}
1 4
.jstEditor {
2 5
    padding-left: 0px;
3 6
}
4 7
.jstEditor textarea, .jstEditor iframe {
5 8
    margin: 0;
6 9
}
7

  
8 10
.jstHandle {
9 11
    height: 10px;
10 12
    font-size: 0.1em;
11 13
    cursor: s-resize;
12 14
    /*background: transparent url(img/resizer.png) no-repeat 45% 50%;*/
13 15
}
14

  
16
#content .jstTabs.tabs {
17
    margin-bottom: 10px;
18
}
19
#content .jstTabs.tabs li {
20
    height: 42px;
21
}
22
#content .jstTabs.tabs li:before{
23
    content: '';
24
    display: inline-block;
25
    vertical-align: middle;
26
    height: 100%;
27
}
28
#content .jstTabs.tabs li a {
29
    display: inline-block;
30
    vertical-align: bottom;
31
    line-height: 19px;
32
}
15 33
.jstElements {
16
    padding: 3px 3px 3px 0;
34
    display: inline-block;
35
    vertical-align: bottom;
36
    border-bottom: 1px solid #bbbbbb;
37
    padding-left: 6px;
38
    height: 26px;
39
}
40
.wiki-preview {
41
    background-color: #ffffff;
42
    border: 1px solid #bbbbbb;
17 43
}
18 44

  
19 45
.jstElements button {
test/functional/messages_controller_test.rb
33 33

  
34 34
    assert_select 'h2', :text => 'First post'
35 35
  end
36
  
36

  
37 37
  def test_show_should_contain_reply_field_tags_for_quoting
38 38
    @request.session[:user_id] = 2
39 39
    get :show, :params => {
......
214 214
        :id => 1,
215 215
        :reply => {
216 216
          :content => 'This is a test reply',
217
          :subject => 'Test reply' 
217
          :subject => 'Test reply'
218 218
        }
219 219
      }
220 220
    reply = Message.order('id DESC').first
......
265 265
    post :preview, :params => {
266 266
        :board_id => 1,
267 267
        :message => {
268
          :subject => "",
269
          :content => "Previewed text"
270
        }
268
          :subject => ""
269
        },
270
        :text => "Previewed text"
271 271
      }
272 272
    assert_response :success
273 273
    assert_include 'Previewed text', response.body
......
280 280
        :board_id => 1,
281 281
        :message => {
282 282
          :subject => "",
283
          :content => "Previewed text"
284
        }
283
        },
284
        :text => "Previewed text"
285 285
      }
286 286
    assert_response :success
287 287
    assert_include 'Previewed text', response.body
test/functional/previews_controller_test.rb
28 28
           :journals, :journal_details,
29 29
           :news
30 30

  
31
  def test_preview_new_issue
31
  def test_preview_new_issue_description
32 32
    @request.session[:user_id] = 2
33 33
    post :issue, :params => {
34 34
        :project_id => '1',
35
        :issue => {
36
          :description => 'Foo'
37
        }
35
        :text => 'Foo'
38 36
      }
39 37
    assert_response :success
40
    assert_select 'fieldset' do
41
      assert_select 'legend', :text => 'Description'
42
      assert_select 'p', :text => 'Foo'
43
    end
38
    assert_select 'p', :text => 'Foo'
44 39
  end
45 40

  
46
  def test_preview_issue_notes_with_no_change_to_description
41
  def test_preview_issue_description
47 42
    @request.session[:user_id] = 2
48 43
    post :issue, :params => {
49 44
        :project_id => '1',
50
        :id => 1,
51
        :issue => {
52
          :description => Issue.find(1).description,
53
          :notes => 'Foo'
54
        }
45
        :issue_id => 1,
46
        :text => 'Unable to print recipes'
55 47
      }
56 48
    assert_response :success
57
    assert_select 'legend', :text => 'Description', :count => 0
58
    assert_select 'legend', :text => 'Notes'
59
  end
60 49

  
61
  def test_preview_issue_notes_with_change_to_description
62
    @request.session[:user_id] = 2
63
    post :issue, :params => {
64
        :project_id => '1',
65
        :id => 1,
66
        :issue => {
67
          :description => 'Changed description',
68
          :notes => 'Foo'
69
        }
70
      }
71
    assert_response :success
72
    assert_select 'legend', :text => 'Description'
73
    assert_select 'legend', :text => 'Notes'
50
    assert_select 'p', :text => 'Unable to print recipes'
74 51
  end
75 52

  
76
  def test_preview_journal_notes_for_update
53
  def test_preview_issue_notes
77 54
    @request.session[:user_id] = 2
78 55
    post :issue, :params => {
79 56
        :project_id => '1',
80 57
        :id => 1,
81
        :journal => {
82
          :notes => 'Foo'
83
        }
58
        :text => 'Foo'
84 59
      }
85 60
    assert_response :success
86
    assert_select 'legend', :text => 'Notes'
87 61
    assert_select 'p', :text => 'Foo'
88 62
  end
89 63

  
......
92 66
    @request.session[:user_id] = 2
93 67
    post :issue, :params => {
94 68
        :project_id => '1',
95
        :id => 1,
96
        :issue => {
97
          :notes => 'attachment:foo.bar'
98
        }
69
        :issue_id => 1,
70
        :field => 'notes',
71
        :text => 'attachment:foo.bar'
99 72
      }
100 73
    assert_response :success
101 74
    assert_select 'a.attachment', :text => 'foo.bar'
102 75
  end
103 76

  
104
  def test_preview_issue_with_project_changed
105
    @request.session[:user_id] = 2
106
    post :issue, :params => {
107
        :project_id => '1',
108
        :id => 1,
109
        :issue => {
110
          :notes => 'notes',
111
          :project_id => 2
112
        }
113
      }
114
    assert_response :success
115
    assert_select 'legend', :text => 'Notes'
116
  end
117

  
118 77
  def test_preview_new_news
119 78
    get :news, :params => {
120 79
        :project_id => 1,
121
        :news => {
122
          :title => '',
123
          :description => 'News description',
124
          :summary => ''
125
        }
80
        :text => 'News description',
126 81
      }
127 82
    assert_response :success
128
    assert_select 'fieldset.preview', :text => /News description/
83
    assert_select 'p', :text => /News description/
129 84
  end
130 85

  
131 86
  def test_preview_existing_news
132 87
    get :news, :params => {
133 88
        :project_id => 1,
134 89
        :id => 2,
135
        :news => {
136
          :title => '',
137
          :description => 'News description',
138
          :summary => ''
139
        }
90
        :text => 'News description'
140 91
      }
141 92
    assert_response :success
142
    assert_select 'fieldset.preview', :text => /News description/
93
    assert_select 'p', :text => /News description/
143 94
  end
144 95
end
test/integration/attachments_test.rb
77 77

  
78 78
    token = ajax_upload('myupload.jpg', 'JPEG content')
79 79

  
80
    post '/issues/preview/new/ecookbook', :params => {
81
        :issue => {:tracker_id => 1, :description => 'Inline upload: !myupload.jpg!'},
80
    post '/issues/preview', :params => {
81
        :issue => {:tracker_id => 1, :project_id => 'ecookbook'},
82
        :text => 'Inline upload: !myupload.jpg!',
82 83
        :attachments => {'1' => {:filename => 'myupload.jpg', :description => 'My uploaded file', :token => token}}
83 84
      }
84 85
    assert_response :success
test/integration/layout_test.rb
63 63
    Role.anonymous.add_permission! :add_issues
64 64

  
65 65
    get '/projects/ecookbook/issues/new'
66
    assert_select 'head script[src^=?]', '/javascripts/jstoolbar/jstoolbar-textile.min.js?'
66
    assert_select 'head script[src^=?]', '/javascripts/jstoolbar/jstoolbar.js?'
67 67
  end
68 68

  
69 69
  def test_calendar_header_tags
test/integration/routing/previews_test.rb
19 19

  
20 20
class RoutingPreviewsTest < Redmine::RoutingTest
21 21
  def test_previews
22
    should_route 'GET /issues/preview/new/foo' => 'previews#issue', :project_id => 'foo'
23
    should_route 'PUT /issues/preview/new/foo' => 'previews#issue', :project_id => 'foo'
24
    should_route 'POST /issues/preview/new/foo' => 'previews#issue', :project_id => 'foo'
25

  
26
    should_route 'GET /issues/preview/edit/321' => 'previews#issue', :id => '321'
27
    should_route 'PUT /issues/preview/edit/321' => 'previews#issue', :id => '321'
28
    should_route 'POST /issues/preview/edit/321' => 'previews#issue', :id => '321'
22
    should_route 'GET /issues/preview' => 'previews#issue'
23
    should_route 'PUT /issues/preview' => 'previews#issue'
24
    should_route 'POST /issues/preview' => 'previews#issue'
29 25

  
30 26
    should_route 'GET /news/preview' => 'previews#news'
27

  
28
    should_route 'GET /preview/text' => 'previews#text'
29
    should_route 'PUT /preview/text' => 'previews#text'
30
    should_route 'POST /preview/text' => 'previews#text'
31 31
  end
32 32
end
test/system/issues_test.rb
179 179
      fill_in 'Subject', :with => 'new issue subject'
180 180
      fill_in 'Description', :with => 'new issue description'
181 181
      click_link 'Preview'
182
      find 'div.wiki-preview', :visible => true, :text => 'new issue description'
182 183
    end
183
    find 'div#preview fieldset', :visible => true, :text => 'new issue description'
184 184
    assert_difference 'Issue.count' do
185
      find('input[name=commit]').click
185
      click_button('Create')
186 186
    end
187 187

  
188 188
    issue = Issue.order('id desc').first
......
314 314
    # Update the notes
315 315
    fill_in 'Notes', :with => 'Updated notes'
316 316
    # Preview the change
317
    click_on 'Preview'
318
    assert page.has_css?('#journal_2_preview')
319
    assert page.first('#journal_2_preview').has_content?('Updated notes')
317
    page.first('#change-2 a.tab-preview').click
318
    assert page.has_css?('#preview_journal_2_notes')
319
    assert page.first('#preview_journal_2_notes').has_content?('Updated notes')
320 320
    # Save
321 321
    click_on 'Save'
322 322

  
(14-14/17)