Project

General

Profile

Feature #41294 » 0004-Separate-the-quote-reply-implementation-into-quote_reply.js.patch

Katsuya HIDAKA, 2024-09-22 13:01

View differences:

app/assets/javascripts/application.js
1262 1262
    tribute.attach(element);
1263 1263
}
1264 1264

  
1265
function quoteReply(path, selectorForContentElement) {
1266
  const contentElement = $(selectorForContentElement).get(0);
1267
  const quote = QuoteExtractor.extract(contentElement);
1268

  
1269
  $.ajax({
1270
    url: path,
1271
    type: 'post',
1272
    data: { quote: quote }
1273
  });
1274
}
1275

  
1276
class QuoteExtractor {
1277
  static extract(targetElement) {
1278
    return new QuoteExtractor(targetElement).extract();
1279
  }
1280

  
1281
  constructor(targetElement) {
1282
    this.targetElement = targetElement;
1283
    this.selection = window.getSelection();
1284
  }
1285

  
1286
  extract() {
1287
    const range = this.selectedRange;
1288

  
1289
    if (!range) {
1290
      return null;
1291
    }
1292

  
1293
    if (!this.targetElement.contains(range.startContainer)) {
1294
      range.setStartBefore(this.targetElement);
1295
    }
1296
    if (!this.targetElement.contains(range.endContainer)) {
1297
      range.setEndAfter(this.targetElement);
1298
    }
1299

  
1300
    return this.formatRange(range);
1301
  }
1302

  
1303
  formatRange(range) {
1304
    return range.toString().trim();
1305
  }
1306

  
1307
  get selectedRange() {
1308
    if (!this.isSelected) {
1309
      return null;
1310
    }
1311

  
1312
    // Retrive the first range that intersects with the target element.
1313
    // NOTE: Firefox allows to select multiple ranges in the document.
1314
    for (let i = 0; i < this.selection.rangeCount; i++) {
1315
      let range = this.selection.getRangeAt(i);
1316
      if (range.intersectsNode(this.targetElement)) {
1317
        return range;
1318
      }
1319
    }
1320
    return null;
1321
  }
1322

  
1323
  get isSelected() {
1324
    return this.selection.containsNode(this.targetElement, true);
1325
  }
1326
}
1327

  
1328 1265
$(document).ready(setupAjaxIndicator);
1329 1266
$(document).ready(hideOnLoad);
1330 1267
$(document).ready(addFormObserversForDoubleSubmit);
app/assets/javascripts/quote_reply.js
1
function quoteReply(path, selectorForContentElement) {
2
  const contentElement = $(selectorForContentElement).get(0);
3
  const quote = QuoteExtractor.extract(contentElement);
4

  
5
  $.ajax({
6
    url: path,
7
    type: 'post',
8
    data: { quote: quote }
9
  });
10
}
11

  
12
class QuoteExtractor {
13
  static extract(targetElement) {
14
    return new QuoteExtractor(targetElement).extract();
15
  }
16

  
17
  constructor(targetElement) {
18
    this.targetElement = targetElement;
19
    this.selection = window.getSelection();
20
  }
21

  
22
  extract() {
23
    const range = this.selectedRange;
24

  
25
    if (!range) {
26
      return null;
27
    }
28

  
29
    if (!this.targetElement.contains(range.startContainer)) {
30
      range.setStartBefore(this.targetElement);
31
    }
32
    if (!this.targetElement.contains(range.endContainer)) {
33
      range.setEndAfter(this.targetElement);
34
    }
35

  
36
    return this.formatRange(range);
37
  }
38

  
39
  formatRange(range) {
40
    return range.toString().trim();
41
  }
42

  
43
  get selectedRange() {
44
    if (!this.isSelected) {
45
      return null;
46
    }
47

  
48
    // Retrive the first range that intersects with the target element.
49
    // NOTE: Firefox allows to select multiple ranges in the document.
50
    for (let i = 0; i < this.selection.rangeCount; i++) {
51
      let range = this.selection.getRangeAt(i);
52
      if (range.intersectsNode(this.targetElement)) {
53
        return range;
54
      }
55
    }
56
    return null;
57
  }
58

  
59
  get isSelected() {
60
    return this.selection.containsNode(this.targetElement, true);
61
  }
62
}
app/views/issues/show.html.erb
1
<% content_for :header_tags do %>
2
  <%= javascript_include_tag 'quote_reply' %>
3
<% end %>
4

  
1 5
<%= render :partial => 'action_menu' %>
2 6

  
3 7
<h2 class="inline-block"><%= issue_heading(@issue) %></h2><%= issue_status_type_badge(@issue.status) %>
app/views/messages/show.html.erb
1
<% content_for :header_tags do %>
2
  <%= javascript_include_tag 'quote_reply' %>
3
<% end %>
4

  
1 5
<%= board_breadcrumb(@message) %>
2 6

  
3 7
<div class="contextual">
(10-10/15)