diff --git a/app/assets/javascripts/application.coffee b/app/assets/javascripts/application.coffee
deleted file mode 100644
index 4e724e8bc..000000000
--- a/app/assets/javascripts/application.coffee
+++ /dev/null
@@ -1,148 +0,0 @@
-#= require jquery2
-#= require jquery_ujs
-#= require jquery.autocomplete
-#= require jquery.caret-range
-#= require jquery.cookie
-#= require jquery.hotkeys
-#= require jquery.notice
-#= require jquery.markitup
-#= require markitup-markdown
-#= require_tree .
-
-$ = window.jQuery
-
-$("body").on "ajax:success", "form[data-remote]", (e, data) ->
- $.noticeAdd text: data.notice if data and data.notice
- $("#nb_votes").text data.nb_votes if data and data.nb_votes
- $(@).parent().hide() unless $(@).data("hidden")
-
-$(".markItUp").markItUp window.markItUpSettings
-
-$("a.hit_counter[data-hit]").each ->
- @href = "/redirect/" + $(@).data("hit")
-
-# Ready to moule
-$("input[autofocus=autofocus]").focus()
-$(".board").chat()
-$("#news_revisions").redaction()
-
-# Force people to preview their modified contents
-$("textarea, #form_answers input").keypress (event) ->
- $(@).parents("form").find("input[value=Prévisualiser]").next("input[type=submit]").hide()
- $(@).off event
-
-# Add/Remove dynamically links in the news form
-langs =
- xx: "!? hmmm ?!"
- fr: "Français"
- de: "Allemand"
- en: "Anglais"
- eu: "Basque"
- ct: "Catalan"
- cn: "Chinois"
- wq: "Code/binaire"
- ko: "Coréen"
- da: "Danois"
- es: "Espagnol"
- ee: "Estonien"
- fi: "Finnois"
- el: "Grec"
- it: "Italien"
- ja: "Japonais"
- nl: "Néerlandais"
- no: "Norvégien"
- pl: "Polonais"
- pt: "Portugais"
- ru: "Russe"
- sv: "Suédois"
-
-$("#form_links").nested_fields "news", "link", "lien", "fieldset", title: "text", url: "url", lang: langs
-$("#form_answers").nested_fields "poll", "answer", "choix", "p", answer: "text"
-
-# Mask the contributors if they are too many
-$("article.news .edited_by").each ->
- field = $(@)
- nb = field.find("a").length
- if nb > 3
- was = field.html()
- field.html "#{nb} personnes"
- field.one "click", -> field.html was
-
-# Toolbar preferences
-$("#account_visible_toolbar")
- .prop("checked", Toolbar.storage.visible != "false")
- .click ->
- Toolbar.storage.visible = $(@).is(":checked")
- true
-
-# Show the toolbar
-$.fn.reverse = [].reverse
-if $("body").hasClass("logged")
- if $("#comments").length
- $("#comments .new-comment")
- .toolbar("Nouveaux commentaires", folding: "#comments .comment")
- .additional $("#comments .comment").sort((a,b) -> a.id.localeCompare(b.id)), "Commentaires par ordre chronologique"
- else if $("main .node").length
- $("#phare .new-node, main .new-node:not(.ppp)")
- .toolbar("Contenus jamais visités")
- .additional $("#phare .new_comments, main .node:not(.ppp) .new_comments").parents("article").reverse(), "Contenus lus avec + de commentaires"
-
-# Redaction
-$(".edition_in_place").editionInPlace()
-$("#redaction .new_link").editionInPlace()
-$("#redaction .new_paragraph").on "ajax:success", false
-$("#redaction .link, #redaction .paragraph").lockableEditionInPlace()
-
-# Tags
-$.fn.autocompleter = () ->
- @each ->
- input = $(@)
- input.autocomplete input.data("url"),
- multiple: true
- multipleSeparator: " "
- dataType: "text"
- matchSubset: false
- @
-$("input#tags").autocompleter()
-$(".tag_in_place").on("in_place:form", ->
- $("input.autocomplete").autocompleter().focus()
-).on("in_place:success", ->
- $.noticeAdd text: "Étiquettes ajoutées"
-).editionInPlace()
-$(".add_tag, .remove_tag").click( ->
- $(@).blur().parents("form").data hidden: "true"
-).parents("form").on "ajax:success", ->
- $(@).find("input").attr disabled: "disabled"
-
-# Hotkeys
-$(document).bind("keypress", "g", ->
- $("html,body").animate scrollTop: 0, 500
- false
-).bind("keypress", "shift+g", ->
- $("html,body").animate scrollTop: $("body").attr("scrollHeight"), 500
- false
-).bind "keypress", "shift+?", ->
- $.noticeAdd
- text: """
- Raccourcis clavier :
")).find("#les-totoz")
- @board.on("mouseenter", ".totoz", @createTotoz)
- .on("mouseleave", ".totoz", @destroyTotoz)
- $.push(@chan).on("chat", @onChat).start()
-
- onChat: (msg) =>
- existing = $("#board_" + msg.id)
- return if existing.length > 0
- if @isInboxLarge
- @inbox.append(msg.large).find(".board-left:last .norloge").click @norloge
- @inboxContainer.scrollTop(@inbox.height())
- @norlogize right for right in @inbox.find(".board-right:last")
- @norlogize_left left for left in @inbox.find(".board-left time:last")
- else
- @inbox.prepend(msg.message).find(".board-left:first .norloge").click @norloge
- @norlogize right for right in @inbox.find(".board-right:first")
- @norlogize_left left for left in @inbox.find(".board-left time:first")
-
- postMessage: (event) =>
- form = $(event.target)
- data = form.serialize()
- @input.val("").select()
- $.ajax url: form.attr("action"), data: data, type: "POST", dataType: "script"
- false
-
- norloge: (event) =>
- string = $(event.target).attr("norloge")
- index = $(event.target).data("clockIndex")
- if index > 1 || (index == 1 && @board.find(".board-left time[data-clock-time=\"" + $(event.target).data("clockTime") + "\"]").length > 1)
- switch index
- when 1 then string += "¹"
- when 2 then string += "²"
- when 3 then string += "³"
- else string += ":" + index
- value = @input.val()
- range = @input.caret()
- unless range.start?
- range.start = 0
- range.end = 0
- @input.val value.substr(0, range.start) + string + " " + value.substr(range.end, value.length)
- @input.caret range.start + string.length + 1
- @input.focus()
-
- norlogize: (x) ->
- tmp = $('
')
- escape = (txt) -> tmp.text(txt).html()
- $(x).contents().filter(-> @nodeType == 3).each ->
- r = /(\d{4}-\d{2}-\d{2} )?(\d{2}:\d{2}(:\d{2})?)([⁰¹²³⁴⁵⁶⁷⁸⁹]+|[:\^]\d+)?/g
- orig = escape @data
- html = ""
- while matches = r.exec orig
- [match, datematch, timematch, minutes, index] = matches
- if index
- switch index.substr(0, 1)
- when ":", "^" then index = index.substr(1)
- when "¹" then index = 1
- when "²" then index = 2
- when "³" then index = 3
- when "⁴" then index = 4
- when "⁵" then index = 5
- when "⁶" then index = 6
- when "⁷" then index = 7
- when "⁸" then index = 8
- when "⁹" then index = 9
- else index = 1
- else
- index = 1
- stop = matches.index
- html = html + orig.slice(idx, stop) + "
"
- idx = r.lastIndex
- $(@).replaceWith html+orig.slice(idx) if html
- if @totoz_type == "popup" || @totoz_type == "inline"
- cfg = @
- $(x).contents().filter(-> @nodeType == 3).each ->
- totoz = /\[:([0-9a-zA-Z \*\$@':_-]+)\]/g
- orig = escape @data
- html = ""
- while matches = totoz.exec orig
- [title, name] = matches
- stop = matches.index
- html = html + orig.slice(idx, stop) +
- if cfg.totoz_type == "popup"
- "
#{title}"
- else if cfg.totoz_type == "inline"
- "

"
- idx = totoz.lastIndex
- $(@).replaceWith html+orig.slice(idx) if html
-
- norlogize_left: (x) ->
- norlogeDatetime = $(x).attr("datetime")
- date = /\d{4}-\d{2}-\d{2}/.exec(norlogeDatetime)
- time = /\d{2}:\d{2}:\d{2}/.exec(norlogeDatetime)
- index = @board.find(".board-left time[data-clock-date=\"" + date + "\"][data-clock-time=\"" + time + "\"]").length + 1
- $(x).attr("data-clock-date", date)
- $(x).attr("data-clock-time", time)
- $(x).attr("data-clock-index", index)
-
- left_highlitizer: (event) =>
- time = $(event.target).data("clockTime")
- index = $(event.target).data("clockIndex")
- @inbox.find("time[data-clock-time=\"" + time + "\"][data-clock-index=\"" + index + "\"]").addClass "highlighted"
- @inbox.find("time[data-clock-time=\"" + time.substr(0, 5) + "\"][data-clock-index=\"" + index + "\"]").addClass "highlighted"
-
- right_highlitizer: (event) =>
- time = $(event.target).data("clockTime")
- index = $(event.target).data("clockIndex")
- if time.length = 5
- @inbox.find("time[data-clock-time*=\"" + time + "\"]").addClass "highlighted"
- else
- @inbox.find("time[data-clock-time=\"" + time + "\"][data-clock-index=\"" + index + "\"]").addClass "highlighted"
-
- deshighlitizer: =>
- @inbox.find("time.highlighted").removeClass "highlighted"
-
- createTotoz: (event) =>
- totozName = event.target.getAttribute("data-totoz-name")
- totozId = encodeURIComponent(totozName).replace(/[%']/g, "")
- totoz = @totoz.find("#totoz-" + totozId).first()
- if totoz.size() == 0
- totoz = $("
")
- .css(display: "none", position: "absolute")
- .append("

")
- @totoz.append totoz
- position = $(event.target).position()
- [x, y] = [position.left, position.top + event.target.offsetHeight]
- totoz.css "z-index": "15", display: "block", top: y + 5, left: x + 5
-
- destroyTotoz: (event) =>
- totozId = encodeURIComponent(event.target.getAttribute("data-totoz-name")).replace(/[%']/g, "")
- totoz = @totoz.find("#totoz-" + totozId).first()
- totoz.css display: "none"
-
-$.fn.chat = ->
- @each ->
- new Chat($(@))
diff --git a/app/assets/javascripts/chat.js b/app/assets/javascripts/chat.js
new file mode 100644
index 000000000..458751281
--- /dev/null
+++ b/app/assets/javascripts/chat.js
@@ -0,0 +1,344 @@
+//= require push
+
+$ = window.jQuery;
+
+class Chat {
+ constructor(board) {
+ this.onChat = this.onChat.bind(this);
+ this.postMessage = this.postMessage.bind(this);
+ this.norloge = this.norloge.bind(this);
+ this.left_highlitizer = this.left_highlitizer.bind(this);
+ this.right_highlitizer = this.right_highlitizer.bind(this);
+ this.deshighlitizer = this.deshighlitizer.bind(this);
+ this.createTotoz = this.createTotoz.bind(this);
+ this.destroyTotoz = this.destroyTotoz.bind(this);
+ this.board = board;
+ this.input = this.board.find("input[type=text]");
+ this.inbox = this.board.find(".inbox");
+ this.isInboxLarge = this.inbox.hasClass("large");
+ this.inboxContainer = this.board.find(".inbox-container");
+ this.inboxContainer.scrollTop(this.inbox.height());
+ this.chan = this.board.data("chan");
+ this.board.find(".board-left .norloge").click(this.norloge);
+ this.board.find("form").submit(this.postMessage);
+ this.totoz_type = $.cookie("totoz-type");
+ this.totoz_url = $.cookie("totoz-url") || "https://totoz.eu/img/";
+ for (var right of this.board.find(".board-right")) {
+ this.norlogize(right);
+ }
+ for (var left of this.board
+ .find(".board-left time")
+ .get()
+ .reverse()) {
+ this.norlogize_left(left);
+ }
+ this.board
+ .on("mouseenter", ".board-left time", this.left_highlitizer)
+ .on("mouseleave", "time", this.deshighlitizer);
+ this.board
+ .on("mouseenter", ".board-right time", this.right_highlitizer)
+ .on("mouseleave", "time", this.deshighlitizer);
+ if (this.totoz_type === "popup") {
+ this.totoz = this.board
+ .append($('
'))
+ .find("#les-totoz");
+ this.board
+ .on("mouseenter", ".totoz", this.createTotoz)
+ .on("mouseleave", ".totoz", this.destroyTotoz);
+ }
+ $.push(this.chan)
+ .on("chat", this.onChat)
+ .start();
+ }
+
+ onChat(msg) {
+ let right;
+ const existing = $("#board_" + msg.id);
+ if (existing.length > 0) {
+ return;
+ }
+ if (this.isInboxLarge) {
+ this.inbox
+ .append(msg.large)
+ .find(".board-left:last .norloge")
+ .click(this.norloge);
+ this.inboxContainer.scrollTop(this.inbox.height());
+ for (right of this.inbox.find(".board-right:last")) {
+ this.norlogize(right);
+ }
+ for (var left of this.inbox.find(".board-left time:last")) {
+ this.norlogize_left(left);
+ }
+ } else {
+ this.inbox
+ .prepend(msg.message)
+ .find(".board-left:first .norloge")
+ .click(this.norloge);
+ for (right of this.inbox.find(".board-right:first")) {
+ this.norlogize(right);
+ }
+ for (var left of this.inbox.find(".board-left time:first")) {
+ this.norlogize_left(left);
+ }
+ }
+ }
+
+ postMessage(event) {
+ const form = $(event.target);
+ const data = form.serialize();
+ this.input.val("").select();
+ $.ajax({
+ url: form.attr("action"),
+ data,
+ type: "POST",
+ dataType: "script"
+ });
+ return false;
+ }
+
+ norloge(event) {
+ let string = $(event.target).attr("norloge");
+ const index = $(event.target).data("clockIndex");
+ if (
+ index > 1 ||
+ (index === 1 &&
+ this.board.find(
+ '.board-left time[data-clock-time="' +
+ $(event.target).data("clockTime") +
+ '"]'
+ ).length > 1)
+ ) {
+ switch (index) {
+ case 1:
+ string += "¹";
+ break;
+ case 2:
+ string += "²";
+ break;
+ case 3:
+ string += "³";
+ break;
+ default:
+ string += ":" + index;
+ }
+ }
+ const value = this.input.val();
+ const range = this.input.caret();
+ if (!range.start) {
+ range.start = 0;
+ range.end = 0;
+ }
+ this.input.val(
+ value.substr(0, range.start) +
+ string +
+ " " +
+ value.substr(range.end, value.length)
+ );
+ this.input.caret(range.start + string.length + 1);
+ return this.input.focus();
+ }
+
+ norlogize(x) {
+ const tmp = $("
");
+ const escape = txt => tmp.text(txt).html();
+ $(x)
+ .contents()
+ .filter(function() {
+ return this.nodeType === 3;
+ })
+ .each(function() {
+ let matches;
+ let idx;
+ const r = /(\d{4}-\d{2}-\d{2} )?(\d{2}:\d{2}(:\d{2})?)([⁰¹²³⁴⁵⁶⁷⁸⁹]+|[:\^]\d+)?/g;
+ const orig = escape(this.data);
+ let html = "";
+ while ((matches = r.exec(orig))) {
+ var [match, datematch, timematch, minutes, index] = matches;
+ if (index) {
+ switch (index.substr(0, 1)) {
+ case ":":
+ case "^":
+ index = index.substr(1);
+ break;
+ case "¹":
+ index = 1;
+ break;
+ case "²":
+ index = 2;
+ break;
+ case "³":
+ index = 3;
+ break;
+ case "⁴":
+ index = 4;
+ break;
+ case "⁵":
+ index = 5;
+ break;
+ case "⁶":
+ index = 6;
+ break;
+ case "⁷":
+ index = 7;
+ break;
+ case "⁸":
+ index = 8;
+ break;
+ case "⁹":
+ index = 9;
+ break;
+ default:
+ index = 1;
+ }
+ } else {
+ index = 1;
+ }
+ var stop = matches.index;
+ html =
+ html +
+ orig.slice(idx, stop) +
+ '
";
+ idx = r.lastIndex;
+ }
+ if (html) {
+ return $(this).replaceWith(html + orig.slice(idx));
+ }
+ });
+ if (this.totoz_type === "popup" || this.totoz_type === "inline") {
+ const cfg = this;
+ return $(x)
+ .contents()
+ .filter(function() {
+ return this.nodeType === 3;
+ })
+ .each(function() {
+ let matches;
+ let idx;
+ const totoz = /\[:([0-9a-zA-Z \*\$@':_-]+)\]/g;
+ const orig = escape(this.data);
+ let html = "";
+ while ((matches = totoz.exec(orig))) {
+ var [title, name] = matches;
+ var stop = matches.index;
+ html =
+ html +
+ orig.slice(idx, stop) +
+ (() => {
+ if (cfg.totoz_type === "popup") {
+ return `
${title}`;
+ } else if (cfg.totoz_type === "inline") {
+ return `

`;
+ }
+ })();
+ idx = totoz.lastIndex;
+ }
+ if (html) {
+ return $(this).replaceWith(html + orig.slice(idx));
+ }
+ });
+ }
+ }
+
+ norlogize_left(x) {
+ const norlogeDatetime = $(x).attr("datetime");
+ const date = /\d{4}-\d{2}-\d{2}/.exec(norlogeDatetime);
+ const time = /\d{2}:\d{2}:\d{2}/.exec(norlogeDatetime);
+ const index =
+ this.board.find(
+ '.board-left time[data-clock-date="' +
+ date +
+ '"][data-clock-time="' +
+ time +
+ '"]'
+ ).length + 1;
+ $(x).attr("data-clock-date", date);
+ $(x).attr("data-clock-time", time);
+ return $(x).attr("data-clock-index", index);
+ }
+
+ left_highlitizer(event) {
+ const time = $(event.target).data("clockTime");
+ const index = $(event.target).data("clockIndex");
+ this.inbox
+ .find(
+ 'time[data-clock-time="' + time + '"][data-clock-index="' + index + '"]'
+ )
+ .addClass("highlighted");
+ return this.inbox
+ .find(
+ 'time[data-clock-time="' +
+ time.substr(0, 5) +
+ '"][data-clock-index="' +
+ index +
+ '"]'
+ )
+ .addClass("highlighted");
+ }
+
+ right_highlitizer(event) {
+ const time = $(event.target).data("clockTime");
+ const index = $(event.target).data("clockIndex");
+ if ((time.length = 5)) {
+ return this.inbox
+ .find('time[data-clock-time*="' + time + '"]')
+ .addClass("highlighted");
+ } else {
+ return this.inbox
+ .find(
+ 'time[data-clock-time="' +
+ time +
+ '"][data-clock-index="' +
+ index +
+ '"]'
+ )
+ .addClass("highlighted");
+ }
+ }
+
+ deshighlitizer() {
+ return this.inbox.find("time.highlighted").removeClass("highlighted");
+ }
+
+ createTotoz(event) {
+ const totozName = event.target.getAttribute("data-totoz-name");
+ const totozId = encodeURIComponent(totozName).replace(/[%']/g, "");
+ let totoz = this.totoz.find("#totoz-" + totozId).first();
+ if (totoz.size() === 0) {
+ totoz = $(`
`)
+ .css({ display: "none", position: "absolute" })
+ .append(`

`);
+ this.totoz.append(totoz);
+ }
+ const position = $(event.target).position();
+ const x = position.left;
+ const y = position.top + event.target.offsetHeight;
+ return totoz.css({
+ "z-index": "15",
+ display: "block",
+ top: y + 5,
+ left: x + 5
+ });
+ }
+
+ destroyTotoz(event) {
+ const totozId = encodeURIComponent(
+ event.target.getAttribute("data-totoz-name")
+ ).replace(/[%']/g, "");
+ const totoz = this.totoz.find("#totoz-" + totozId).first();
+ return totoz.css({ display: "none" });
+ }
+}
+
+$.fn.chat = function() {
+ return this.each(function() {
+ return new Chat($(this));
+ });
+};
diff --git a/app/assets/javascripts/edition_in_place.coffee b/app/assets/javascripts/edition_in_place.coffee
deleted file mode 100644
index 9436b2843..000000000
--- a/app/assets/javascripts/edition_in_place.coffee
+++ /dev/null
@@ -1,70 +0,0 @@
-$ = window.jQuery
-
-class EditionInPlace
- constructor: (@el, @edit) ->
- @url = @el.data("url") or (document.location.pathname + "/modifier")
- @button().click @loadForm
-
- button: ->
- if @edit then @el.find(@edit) else @el
-
- loadForm: =>
- @button().off "click"
- @old = @el.html()
- @xhr = $.ajax(url: @url, type: "get", dataType: "html").fail(@cantEdit).done(@showForm)
- false
-
- cantEdit: =>
- @el.trigger "in_place:cant_edit", @xhr
- @button().click @loadForm
- @xhr = null
-
- showForm: =>
- form = @el.html(@xhr.responseText).find("form")
- form.find(".cancel").click @reset
- form.find("textarea, input, select")[0].select()
- form.find(".markItUp").markItUp window.markItUpSettings
- form.submit @submitForm
- @el.trigger "in_place:form", @xhr
- @xhr = null
-
- reset: (event) =>
- @el.html @old
- @button().click @loadForm
- @el.trigger "in_place:reset", event
- false
-
- submitForm: =>
- form = @el.find("form")
- url = form.attr("action")
- data = form.serialize()
- @xhr = $.ajax(url: url, type: "post", data: data, dataType: "html").fail(@error).done(@success)
- false
-
- error: =>
- try
- error = @el.find("ul.error")
- response = $.parseJSON(@xhr.responseText)
- messages = []
- for attribute, errors of response.errors
- for message in errors
- messages.push(message)
- if messages.length == 1
- error.text("Erreur : " + messages[0])
- else
- error.text("Erreurs :")
- for message in messages
- error.append($("
").append(message))
- error.show()
- @el.trigger "in_place:error", @xhr
- @xhr = null
-
- success: =>
- @el = $(@xhr.responseText).replaceAll @el
- @button().click @loadForm
- @el.trigger "in_place:success", @xhr
- @xhr = null
-
-$.fn.editionInPlace = (edit_selector) ->
- @each ->
- new EditionInPlace($(@), edit_selector)
diff --git a/app/assets/javascripts/edition_in_place.js b/app/assets/javascripts/edition_in_place.js
new file mode 100644
index 000000000..58b6dda8e
--- /dev/null
+++ b/app/assets/javascripts/edition_in_place.js
@@ -0,0 +1,106 @@
+$ = window.jQuery;
+
+class EditionInPlace {
+ constructor(el, edit) {
+ this.loadForm = this.loadForm.bind(this);
+ this.cantEdit = this.cantEdit.bind(this);
+ this.showForm = this.showForm.bind(this);
+ this.reset = this.reset.bind(this);
+ this.submitForm = this.submitForm.bind(this);
+ this.error = this.error.bind(this);
+ this.success = this.success.bind(this);
+ this.el = el;
+ this.edit = edit;
+ this.url = this.el.data("url") || document.location.pathname + "/modifier";
+ this.button().click(this.loadForm);
+ }
+
+ button() {
+ if (this.edit) {
+ return this.el.find(this.edit);
+ } else {
+ return this.el;
+ }
+ }
+
+ loadForm() {
+ this.button().off("click");
+ this.old = this.el.html();
+ this.xhr = $.ajax({ url: this.url, type: "get", dataType: "html" })
+ .fail(this.cantEdit)
+ .done(this.showForm);
+ return false;
+ }
+
+ cantEdit() {
+ this.el.trigger("in_place:cant_edit", this.xhr);
+ this.button().click(this.loadForm);
+ this.xhr = null;
+ }
+
+ showForm() {
+ const form = this.el.html(this.xhr.responseText).find("form");
+ form.find(".cancel").click(this.reset);
+ form.find("textarea, input, select")[0].select();
+ form.find(".markItUp").markItUp(window.markItUpSettings);
+ form.submit(this.submitForm);
+ this.el.trigger("in_place:form", this.xhr);
+ this.xhr = null;
+ }
+
+ reset(event) {
+ this.el.html(this.old);
+ this.button().click(this.loadForm);
+ this.el.trigger("in_place:reset", event);
+ return false;
+ }
+
+ submitForm() {
+ const form = this.el.find("form");
+ const url = form.attr("action");
+ const data = form.serialize();
+ this.xhr = $.ajax({ url, type: "post", data, dataType: "html" })
+ .fail(this.error)
+ .done(this.success);
+ return false;
+ }
+
+ error() {
+ try {
+ let message;
+ const error = this.el.find("ul.error");
+ const response = $.parseJSON(this.xhr.responseText);
+ const messages = [];
+ for (var attribute in response.errors) {
+ var errors = response.errors[attribute];
+ for (message of errors) {
+ messages.push(message);
+ }
+ }
+ if (messages.length === 1) {
+ error.text("Erreur : " + messages[0]);
+ } else {
+ error.text("Erreurs :");
+ for (message of messages) {
+ error.append($("").append(message));
+ }
+ }
+ error.show();
+ } catch (error1) {}
+ this.el.trigger("in_place:error", this.xhr);
+ this.xhr = null;
+ }
+
+ success() {
+ this.el = $(this.xhr.responseText).replaceAll(this.el);
+ this.button().click(this.loadForm);
+ this.el.trigger("in_place:success", this.xhr);
+ this.xhr = null;
+ }
+}
+
+$.fn.editionInPlace = function(edit_selector) {
+ return this.each(function() {
+ return new EditionInPlace($(this), edit_selector);
+ });
+};
diff --git a/app/assets/javascripts/lockable_edition_in_place.coffee b/app/assets/javascripts/lockable_edition_in_place.coffee
deleted file mode 100644
index 9213b45a4..000000000
--- a/app/assets/javascripts/lockable_edition_in_place.coffee
+++ /dev/null
@@ -1,13 +0,0 @@
-$ = window.jQuery
-
-$.fn.lockableEditionInPlace = ->
- $(@).on("in_place:form", ->
- self = $(@)
- self.data cancel: self.find(".cancel").data("url")
- self.data token: self.find('input[name="authenticity_token"]').serialize()
- ).on("in_place:reset", ->
- self = $(@)
- $.ajax url: self.data("cancel"), type: "post", data: self.data("token")
- ).on("in_place:cant_edit", (event, xhr) ->
- $.noticeAdd text: xhr.responseText
- ).editionInPlace("button.edit")
diff --git a/app/assets/javascripts/lockable_edition_in_place.js b/app/assets/javascripts/lockable_edition_in_place.js
new file mode 100644
index 000000000..82ae1dc17
--- /dev/null
+++ b/app/assets/javascripts/lockable_edition_in_place.js
@@ -0,0 +1,24 @@
+$ = window.jQuery;
+
+$.fn.lockableEditionInPlace = function() {
+ return $(this)
+ .on("in_place:form", function() {
+ const self = $(this);
+ self.data({ cancel: self.find(".cancel").data("url") });
+ self.data({
+ token: self.find('input[name="authenticity_token"]').serialize()
+ });
+ })
+ .on("in_place:reset", function() {
+ const self = $(this);
+ $.ajax({
+ url: self.data("cancel"),
+ type: "post",
+ data: self.data("token")
+ });
+ })
+ .on("in_place:cant_edit", (event, xhr) =>
+ $.noticeAdd({ text: xhr.responseText })
+ )
+ .editionInPlace("button.edit");
+};
diff --git a/app/assets/javascripts/nested_fields.coffee b/app/assets/javascripts/nested_fields.coffee
deleted file mode 100644
index 4f4f29ca0..000000000
--- a/app/assets/javascripts/nested_fields.coffee
+++ /dev/null
@@ -1,48 +0,0 @@
-$ = window.jQuery
-
-class NestedFields
- constructor: (@el, @parent, @nested, @text, @tag, @attributes) ->
- @create()
-
- create: ->
- items = @el.children(".#{@nested}")
- @counter = items.length
- @bind_item item, i for item, i in items
- @el.append $("<#{@tag}/>", html: $("",
- type: "button"
- id: "add_#{@nested}"
- text: "Ajouter un #{@text}"
- ))
- $("#add_#{@nested}").click @add_item
-
- bind_item: (item, counter) ->
- it = $(item)
- it.append """"""
- it.children(".remove").click =>
- if counter
- name = "#{@parent}[#{@nested}s_attributes][#{counter}][_destroy]"
- it.replaceWith $("", name: name, type: "hidden", value: 1)
- else
- it.remove()
- false
-
- add_item: =>
- last = @el.children(".#{@nested}:last")
- last = @el.children("#{@tag}:first") if last.length == 0
- fset = $("<#{@tag}/>", class: @nested)
- last.after fset
- for i,type of @attributes
- name = "#{@parent}[#{@nested}s_attributes][#{@counter}][#{i}]"
- if typeof (type) == "string"
- elem = $("", name: name, type: type, size: 30, autocomplete: "off")
- else
- elem = $("", name: name)
- $("", value: j, text: txt).appendTo elem for j,txt of type
- fset.append(elem).append " "
- @bind_item last.next()
- @counter += 1
- false
-
-$.fn.nested_fields = (parent, nested, text, tag, attributes) ->
- @each ->
- new NestedFields($(@), parent, nested, text, tag, attributes)
diff --git a/app/assets/javascripts/nested_fields.js b/app/assets/javascripts/nested_fields.js
new file mode 100644
index 000000000..b3432b95e
--- /dev/null
+++ b/app/assets/javascripts/nested_fields.js
@@ -0,0 +1,86 @@
+$ = window.jQuery;
+
+class NestedFields {
+ constructor(el, parent, nested, text, tag, attributes) {
+ this.add_item = this.add_item.bind(this);
+ this.el = el;
+ this.parent = parent;
+ this.nested = nested;
+ this.text = text;
+ this.tag = tag;
+ this.attributes = attributes;
+ this.create();
+ }
+
+ create() {
+ const items = this.el.children(`.${this.nested}`);
+ this.counter = items.length;
+ for (let i = 0; i < items.length; i++) {
+ var item = items[i];
+ this.bind_item(item, i);
+ }
+ this.el.append(
+ $(`<${this.tag}/>`, {
+ html: $("", {
+ type: "button",
+ id: `add_${this.nested}`,
+ text: `Ajouter un ${this.text}`
+ })
+ })
+ );
+ $(`#add_${this.nested}`).click(this.add_item);
+ }
+
+ bind_item(item, counter) {
+ const it = $(item);
+ it.append(
+ ``
+ );
+ it.children(".remove").click(() => {
+ if (counter) {
+ const name = `${this.parent}[${
+ this.nested
+ }s_attributes][${counter}][_destroy]`;
+ it.replaceWith($("", { name, type: "hidden", value: 1 }));
+ } else {
+ it.remove();
+ }
+ return false;
+ });
+ }
+
+ add_item() {
+ let last = this.el.children(`.${this.nested}:last`);
+ if (last.length === 0) {
+ last = this.el.children(`${this.tag}:first`);
+ }
+ const fset = $(`<${this.tag}/>`, { class: this.nested });
+ last.after(fset);
+ for (var i in this.attributes) {
+ var elem;
+ var type = this.attributes[i];
+ var name = `${this.parent}[${this.nested}s_attributes][${
+ this.counter
+ }][${i}]`;
+ if (typeof type === "string") {
+ elem = $("", { name, type, size: 30, autocomplete: "off" });
+ } else {
+ elem = $("", { name });
+ for (var j in type) {
+ var txt = type[j];
+ $("", { value: j, text: txt }).appendTo(elem);
+ }
+ }
+ fset.append(elem).append(" ");
+ }
+ this.bind_item(last.next());
+ this.counter += 1;
+ return false;
+ }
+}
+
+$.fn.nested_fields = function(parent, nested, text, tag, attributes) {
+ return this.each(function() {
+ return new NestedFields($(this), parent, nested, text, tag, attributes);
+ });
+};
diff --git a/app/assets/javascripts/popup.coffee b/app/assets/javascripts/popup.coffee
deleted file mode 100644
index d8c838c5e..000000000
--- a/app/assets/javascripts/popup.coffee
+++ /dev/null
@@ -1,25 +0,0 @@
-$ = window.jQuery
-
-# Popup management: on click on event element, hide / show popup
-$(".popup-event").click ->
- # Update active event to the one which were clicked
- event = $(@)
- popupId = event.data("popup-id")
- allEvents = $(".popup-event[data-popup-id=#{popupId}]")
- popup = $("##{popupId}")
- showPopup = (typeof popup.attr("data-popup-show") is 'undefined')
- # Update popup status and advertise all popup event togglers
- if showPopup
- popup.attr("data-popup-show", "")
- allEvents.attr("data-popup-shown", "")
- else
- popup.removeAttr("data-popup-show")
- allEvents.removeAttr("data-popup-shown")
- # Give new popup display status to all listeners
- listners = popup.data("popup-listner-ids")
- for listner in listners.split(" ")
- listnerElement = $("##{listner}")
- if showPopup
- listnerElement.attr("data-popup-#{popupId}-shown", "")
- else
- listnerElement.removeAttr("data-popup-#{popupId}-shown", "")
diff --git a/app/assets/javascripts/popup.js b/app/assets/javascripts/popup.js
new file mode 100644
index 000000000..d68799f6c
--- /dev/null
+++ b/app/assets/javascripts/popup.js
@@ -0,0 +1,30 @@
+$ = window.jQuery;
+
+// Popup management: on click on event element, hide / show popup
+$(".popup-event").click(function() {
+ // Update active event to the one which were clicked
+ const event = $(this);
+ const popupId = event.data("popup-id");
+ const allEvents = $(`.popup-event[data-popup-id=${popupId}]`);
+ const popup = $(`#${popupId}`);
+ const showPopup = typeof popup.attr("data-popup-show") === "undefined";
+ // Update popup status and advertise all popup event togglers
+ if (showPopup) {
+ popup.attr("data-popup-show", "");
+ allEvents.attr("data-popup-shown", "");
+ } else {
+ popup.removeAttr("data-popup-show");
+ allEvents.removeAttr("data-popup-shown");
+ }
+ // Give new popup display status to all listeners
+ const listners = popup.data("popup-listner-ids");
+
+ for (var listner of listners.split(" ")) {
+ var listnerElement = $(`#${listner}`);
+ if (showPopup) {
+ listnerElement.attr(`data-popup-${popupId}-shown`, "");
+ } else {
+ listnerElement.removeAttr(`data-popup-${popupId}-shown`, "");
+ }
+ }
+});
diff --git a/app/assets/javascripts/push.coffee b/app/assets/javascripts/push.coffee
deleted file mode 100644
index d9289fce6..000000000
--- a/app/assets/javascripts/push.coffee
+++ /dev/null
@@ -1,34 +0,0 @@
-$ = window.jQuery
-
-class Push
- constructor: (@chan) ->
- @callbacks = {}
- @started = false
-
- on: (kind, callback) ->
- @callbacks[kind] = callback
- @
-
- start: ->
- if not @started
- @started = true
- $(window).load =>
- source = new EventSource("/b/#{@chan}")
- source.addEventListener "message", @onMessage
- source.addEventListener "error", @onError
- $(window).unload -> source.close()
-
- onMessage: (e) =>
- try
- msg = $.parseJSON e.data
- fn = @callbacks[msg.kind]
- fn msg if fn?
- catch err
- console.log err if window.console
-
- onError: (e) =>
- console.log "onError", e if window.console
-
-pushs = []
-$.push = (chan) ->
- pushs[chan] ||= new Push(chan)
diff --git a/app/assets/javascripts/push.js b/app/assets/javascripts/push.js
new file mode 100644
index 000000000..89ad3dce6
--- /dev/null
+++ b/app/assets/javascripts/push.js
@@ -0,0 +1,51 @@
+$ = window.jQuery;
+
+class Push {
+ constructor(chan) {
+ this.onMessage = this.onMessage.bind(this);
+ this.onError = this.onError.bind(this);
+ this.chan = chan;
+ this.callbacks = {};
+ this.started = false;
+ }
+
+ on(kind, callback) {
+ this.callbacks[kind] = callback;
+ return this;
+ }
+
+ start() {
+ if (!this.started) {
+ this.started = true;
+ $(window).load(() => {
+ const source = new EventSource(`/b/${this.chan}`);
+ source.addEventListener("message", this.onMessage);
+ source.addEventListener("error", this.onError);
+ $(window).unload(() => source.close());
+ });
+ }
+ }
+
+ onMessage(e) {
+ try {
+ const msg = $.parseJSON(e.data);
+ const fn = this.callbacks[msg.kind];
+ if (fn) {
+ fn(msg);
+ }
+ } catch (err) {
+ if (window.console) {
+ console.log(err);
+ }
+ }
+ }
+
+ onError(e) {
+ if (window.console) {
+ console.log("onError", e);
+ }
+ }
+}
+
+const pushs = [];
+$.push = chan => pushs[chan] || (pushs[chan] = new Push(chan));
diff --git a/app/assets/javascripts/redaction.coffee b/app/assets/javascripts/redaction.coffee
deleted file mode 100644
index 006949845..000000000
--- a/app/assets/javascripts/redaction.coffee
+++ /dev/null
@@ -1,117 +0,0 @@
-#= require push
-
-$ = window.jQuery
-
-class Redaction
- constructor: (chan) ->
- push = $.push chan
- for name, fn of @ when name.slice(0, 2) == "on"
- kind = name.replace(/[A-Z]/g, "_$&").toLowerCase().slice(3)
- push.on kind, fn
- push.start()
-
- onSubmit: (msg) ->
- $.noticeAdd text: "#{msg.username} a soumis la dépêche", stay: true
-
- onPublish: (msg) ->
- $.noticeAdd text: "La dépêche a été acceptée par #{msg.username}", stay: true
-
- onRefuse: (msg) ->
- $.noticeAdd text: "La dépêche a été refusée par #{msg.username}", stay: true
-
- onRewrite: (msg) ->
- $.noticeAdd text: "La dépêche a été renvoyée dans l’espace de rédaction par #{msg.username}", stay: true
-
- onVote: (msg) ->
- $.noticeAdd text: "#{msg.username} a voté #{msg.word}"
- $("#news_vote").load "/moderation/news/#{msg.news_id}/vote"
-
- onUpdate: (msg) ->
- $("#news_header .title").text msg.title
- $("#news_header .topic").text msg.section.title
- $("#edition figure.image img").attr src: "/images/sections/#{msg.section.id}.png"
-
- liForRevision: (msg) ->
- parts = window.location.pathname.split("/")
- slug = parts[parts.length - 1]
- """
-
- #{msg.username} : #{msg.message} - #{msg.creationdate}
-
- """
-
- onRevision: (msg) =>
- $("#news_revisions ul").prepend @liForRevision(msg)
- $("#topbar .revision-cell").text msg.version
- atPosition = msg.creationdate.indexOf(':') - 2
- finalDate = [msg.creationdate.slice(0, atPosition), "à ", msg.creationdate.slice(atPosition)].join("")
- $("#topbar .revision-date").text "le " + finalDate
-
- innerHtmlForLink: (msg) ->
- """
-
#{msg.title} (#{msg.nb_clicks} clic#{if msg.nb_clicks > 1 then 's' else ''})
- """
-
- htmlForLink: (msg) ->
- """
-
- #{@innerHtmlForLink msg}
-
-
-
-
- """
-
- onAddLink: (msg) =>
- $("#links").append @htmlForLink(msg)
- $("#link_#{msg.id}").lockableEditionInPlace()
-
- onUpdateLink: (msg) =>
- $("#link_#{msg.id}").html(@innerHtmlForLink msg)
- .attr(lang: msg.lang)
-
- onRemoveLink: (msg) ->
- $("#link_#{msg.id}").remove()
-
- htmlForPara: (msg) ->
- """
-
- #{msg.body}
-
-
-
-
- """
-
- onAddParagraph: (msg) =>
- if msg.after
- $("#paragraph_#{msg.after}").after @htmlForPara(msg)
- else
- $("##{msg.part}").append @htmlForPara(msg)
- $("#paragraph_#{msg.id}").lockableEditionInPlace()
-
- onUpdateParagraph: (msg) =>
- $("#paragraph_#{msg.id}").html(msg.body)
-
- onRemoveParagraph: (msg) ->
- $("#paragraph_#{msg.id}").remove()
-
- onSecondPartToc: (msg) ->
- $(".second_part_toc").html(msg.toc)
-
- onLockParagraph: (msg) ->
- editing = $ """
-

- """
- $("#paragraph_#{msg.id} .actions").prepend editing
-
- onUnlockParagraph: (msg) ->
- $("#editing_#{msg.id}").remove()
-
-$.fn.redaction = ->
- @each ->
- new Redaction($(@).data("chan"))
diff --git a/app/assets/javascripts/redaction.js b/app/assets/javascripts/redaction.js
new file mode 100644
index 000000000..c483ae6ec
--- /dev/null
+++ b/app/assets/javascripts/redaction.js
@@ -0,0 +1,181 @@
+//= require push
+
+$ = window.jQuery;
+
+class Redaction {
+ constructor(chan) {
+ this.onRevision = this.onRevision.bind(this);
+ this.onAddLink = this.onAddLink.bind(this);
+ this.onUpdateLink = this.onUpdateLink.bind(this);
+ this.onAddParagraph = this.onAddParagraph.bind(this);
+ this.onUpdateParagraph = this.onUpdateParagraph.bind(this);
+ const push = $.push(chan);
+ for (var name in this) {
+ var fn = this[name];
+ if (name.slice(0, 2) === "on") {
+ var kind = name
+ .replace(/[A-Z]/g, "_$&")
+ .toLowerCase()
+ .slice(3);
+ push.on(kind, fn);
+ }
+ }
+ push.start();
+ }
+
+ onSubmit(msg) {
+ $.noticeAdd({
+ text: `${msg.username} a soumis la dépêche`,
+ stay: true
+ });
+ }
+
+ onPublish(msg) {
+ $.noticeAdd({
+ text: `La dépêche a été acceptée par ${msg.username}`,
+ stay: true
+ });
+ }
+
+ onRefuse(msg) {
+ $.noticeAdd({
+ text: `La dépêche a été refusée par ${msg.username}`,
+ stay: true
+ });
+ }
+
+ onRewrite(msg) {
+ $.noticeAdd({
+ text: `La dépêche a été renvoyée dans l’espace de rédaction par ${
+ msg.username
+ }`,
+ stay: true
+ });
+ }
+
+ onVote(msg) {
+ $.noticeAdd({ text: `${msg.username} a voté ${msg.word}` });
+ $("#news_vote").load(`/moderation/news/${msg.news_id}/vote`);
+ }
+
+ onUpdate(msg) {
+ $("#news_header .title").text(msg.title);
+ $("#news_header .topic").text(msg.section.title);
+ $("#edition figure.image img").attr({
+ src: `/images/sections/${msg.section.id}.png`
+ });
+ }
+
+ liForRevision(msg) {
+ const parts = window.location.pathname.split("/");
+ const slug = parts[parts.length - 1];
+ return `\
+
+ ${msg.username} : ${msg.message} - ${msg.creationdate}
+\
+`;
+ }
+
+ onRevision(msg) {
+ $("#news_revisions ul").prepend(this.liForRevision(msg));
+ $("#topbar .revision-cell").text(msg.version);
+ const atPosition = msg.creationdate.indexOf(":") - 2;
+ const finalDate = [
+ msg.creationdate.slice(0, atPosition),
+ "à ",
+ msg.creationdate.slice(atPosition)
+ ].join("");
+ $("#topbar .revision-date").text("le " + finalDate);
+ }
+
+ innerHtmlForLink(msg) {
+ return `\
+
${msg.title} (${
+ msg.nb_clicks
+ } clic${msg.nb_clicks > 1 ? "s" : ""})\
+`;
+ }
+
+ htmlForLink(msg) {
+ return `\
+
+ ${this.innerHtmlForLink(msg)}
+
+
+
+\
+`;
+ }
+
+ onAddLink(msg) {
+ $("#links").append(this.htmlForLink(msg));
+ $(`#link_${msg.id}`).lockableEditionInPlace();
+ }
+
+ onUpdateLink(msg) {
+ $(`#link_${msg.id}`)
+ .html(this.innerHtmlForLink(msg))
+ .attr({ lang: msg.lang });
+ }
+
+ onRemoveLink(msg) {
+ $(`#link_${msg.id}`).remove();
+ }
+
+ htmlForPara(msg) {
+ return `\
+
+ ${msg.body}
+
+
+
+
\
+`;
+ }
+
+ onAddParagraph(msg) {
+ if (msg.after) {
+ $(`#paragraph_${msg.after}`).after(this.htmlForPara(msg));
+ } else {
+ $(`#${msg.part}`).append(this.htmlForPara(msg));
+ }
+ $(`#paragraph_${msg.id}`).lockableEditionInPlace();
+ }
+
+ onUpdateParagraph(msg) {
+ $(`#paragraph_${msg.id}`).html(msg.body);
+ }
+
+ onRemoveParagraph(msg) {
+ $(`#paragraph_${msg.id}`).remove();
+ }
+
+ onSecondPartToc(msg) {
+ $(".second_part_toc").html(msg.toc);
+ }
+
+ onLockParagraph(msg) {
+ const editing = $(`\
+

\
+`);
+ $(`#paragraph_${msg.id} .actions`).prepend(editing);
+ }
+
+ onUnlockParagraph(msg) {
+ $(`#editing_${msg.id}`).remove();
+ }
+}
+
+$.fn.redaction = function() {
+ return this.each(function() {
+ return new Redaction($(this).data("chan"));
+ });
+};
diff --git a/app/assets/javascripts/tab.coffee b/app/assets/javascripts/tab.coffee
deleted file mode 100644
index 34eaf9e29..000000000
--- a/app/assets/javascripts/tab.coffee
+++ /dev/null
@@ -1,13 +0,0 @@
-$ = window.jQuery
-
-# Tabs management: on click hide data from all other sibling tabs
-$(".tab").click ->
- # Update active tab to the one which were clicked
- tab = $(@)
- tab.attr("data-show-content", true)
- tab.siblings().removeAttr("data-show-content")
- # Show active content linked to the newly activated tab
- content = $("#" + $(@).data("tab-content-id"))
- content.attr("data-show-content", true)
- content.siblings().removeAttr("data-show-content")
- false
diff --git a/app/assets/javascripts/tab.js b/app/assets/javascripts/tab.js
new file mode 100644
index 000000000..c38aaa8db
--- /dev/null
+++ b/app/assets/javascripts/tab.js
@@ -0,0 +1,14 @@
+$ = window.jQuery;
+
+// Tabs management: on click hide data from all other sibling tabs
+$(".tab").click(function() {
+ // Update active tab to the one which were clicked
+ const tab = $(this);
+ tab.attr("data-show-content", true);
+ tab.siblings().removeAttr("data-show-content");
+ // Show active content linked to the newly activated tab
+ const content = $("#" + $(this).data("tab-content-id"));
+ content.attr("data-show-content", true);
+ content.siblings().removeAttr("data-show-content");
+ return false;
+});
diff --git a/app/assets/javascripts/toolbar.coffee b/app/assets/javascripts/toolbar.coffee
deleted file mode 100644
index 78469bfbb..000000000
--- a/app/assets/javascripts/toolbar.coffee
+++ /dev/null
@@ -1,149 +0,0 @@
-$ = window.jQuery
-
-class Toolbar
- constructor: (@items, @text, options) ->
- @nb_items = @items.length
- @current = 0
- @options = $.extend({}, Toolbar.defaultOptions, options)
- @visible = (Toolbar.storage.visible or @options.visible) != "false"
- @threshold = Toolbar.storage.threshold or @options.thresholds[0]
- @hiding()
- @folding()
- @create()
-
- create: ->
- if @visible
- $("body").append """
-
- """
- $("#toolbar_items .prev").click @prev_item
- $("#toolbar_items .next").click @next_item
- $("#toolbar .change").click @change_threshold
- $(document).bind("keypress", "<", @prev_item)
- .bind("keypress", ">", @next_item)
- .bind("keypress", "k", @prev_item)
- .bind("keypress", "j", @next_item)
-
- next_item: =>
- @current += 1
- @current = 1 if @current > @nb_items
- @go_to_current()
-
- prev_item: =>
- @current -= 1
- @current = @nb_items if @current <= 0
- @go_to_current()
-
- go_to_current: ->
- return if @nb_items == 0
- item = @items[@current - 1]
- pos = $(item).offset().top
- $("html,body").stop().animate scrollTop: pos, 500
- $("#toolbar_current_item").text @current if @visible
- false
-
- additional: (@alt_items, @alt_text) ->
- @nb_alt_items = @alt_items.length
- @alt_current = 0
- if @visible
- $("#toolbar_items").after """
-
#{@alt_text} :
- #{@alt_current} /
- #{@nb_alt_items}
- [ |
- ]
-
- """
- $("#toolbar_alt_items .prev").click @alt_prev_item
- $("#toolbar_alt_items .next").click @alt_next_item
- $(document).bind("keypress", "[", @alt_prev_item)
- .bind("keypress", "]", @alt_next_item)
- .bind("keypress", "h", @alt_prev_item)
- .bind("keypress", "l", @alt_next_item)
-
- alt_next_item: =>
- @alt_current += 1
- @alt_current = 1 if @alt_current > @nb_alt_items
- @go_to_alt_current()
-
- alt_prev_item: =>
- @alt_current -= 1
- @alt_current = @nb_alt_items if @alt_current <= 0
- @go_to_alt_current()
-
- go_to_alt_current: ->
- return if @nb_alt_items == 0
- item = @alt_items[@alt_current - 1]
- pos = $(item).offset().top
- $("html,body").stop().animate scrollTop: pos, 500
- $("#toolbar_current_alt_item").text @alt_current if @visible
- false
-
- change_threshold: (event) =>
- el = $(event.target)
- ths = @options.thresholds
- index = $.inArray(parseInt(el.text(), 10), ths) + 1
- Toolbar.storage.threshold = @threshold = ths[index % ths.length]
- el.text @threshold
- @folding()
- false
-
- hiding: ->
- return unless @options.folding?
- items = $(@options.folding)
- for i in items
- do (i) =>
- item = $(i)
- score = parseInt(item.find(".score:first").text(), 10)
- where = item.children("h2").children(".anchor")
- close = $('
[-]').insertBefore(where)
- close.after(' ')
- hide = (b) ->
- if b
- item.addClass "fold"
- close.text("[+]").attr "title", "Réafficher le fil de discussion"
- item.children("ul").hide()
- else
- item.removeClass "fold"
- close.text("[-]").attr "title", "Cacher le fil de discussion"
- item.children("ul").show()
- close.click ->
- hide close.text() == "[-]"
- false
-
- folding: ->
- return unless @options.folding?
- items = $(@options.folding)
- items.find(".folding").click()
- for i in items
- do (i) =>
- item = $(i)
- score = parseInt(item.find(".score:first").text(), 10)
- if score < @threshold
- item.addClass "fold"
- where = item.children("h2").children(".anchor")
- link = $('
[+]').insertBefore(where)
- link.after(' ').click ->
- item.removeClass "fold"
- link.remove()
- false
-
-Toolbar.storage = window["localStorage"] or {}
-
-Toolbar.defaultOptions =
- visible: true
- folding: null
- thresholds: [ 1, 2, 5, -42, 0 ]
-
-$.fn.toolbar = (text, options) ->
- new Toolbar($(@), text, options)
-
-# Exports the Toolbar class
-window.Toolbar = Toolbar
diff --git a/app/assets/javascripts/toolbar.js b/app/assets/javascripts/toolbar.js
new file mode 100644
index 000000000..22faf6b23
--- /dev/null
+++ b/app/assets/javascripts/toolbar.js
@@ -0,0 +1,211 @@
+$ = window.jQuery;
+
+class Toolbar {
+ constructor(items, text, options) {
+ this.next_item = this.next_item.bind(this);
+ this.prev_item = this.prev_item.bind(this);
+ this.alt_next_item = this.alt_next_item.bind(this);
+ this.alt_prev_item = this.alt_prev_item.bind(this);
+ this.change_threshold = this.change_threshold.bind(this);
+ this.items = items;
+ this.text = text;
+ this.nb_items = this.items.length;
+ this.current = 0;
+ this.options = $.extend({}, Toolbar.defaultOptions, options);
+ this.visible =
+ (Toolbar.storage.visible || this.options.visible) !== "false";
+ this.threshold = Toolbar.storage.threshold || this.options.thresholds[0];
+ this.hiding();
+ this.folding();
+ this.create();
+ }
+
+ create() {
+ if (this.visible) {
+ $("body").append(`\
+
\
+`);
+ $("#toolbar_items .prev").click(this.prev_item);
+ $("#toolbar_items .next").click(this.next_item);
+ $("#toolbar .change").click(this.change_threshold);
+ }
+ $(document)
+ .bind("keypress", "<", this.prev_item)
+ .bind("keypress", ">", this.next_item)
+ .bind("keypress", "k", this.prev_item)
+ .bind("keypress", "j", this.next_item);
+ }
+
+ next_item() {
+ this.current += 1;
+ if (this.current > this.nb_items) {
+ this.current = 1;
+ }
+ this.go_to_current();
+ return false;
+ }
+
+ prev_item() {
+ this.current -= 1;
+ if (this.current <= 0) {
+ this.current = this.nb_items;
+ }
+ this.go_to_current();
+ return false;
+ }
+
+ go_to_current() {
+ if (this.nb_items === 0) {
+ return;
+ }
+ const item = this.items[this.current - 1];
+ const pos = $(item).offset().top;
+ $("html,body")
+ .stop()
+ .animate({ scrollTop: pos }, 500);
+ if (this.visible) {
+ $("#toolbar_current_item").text(this.current);
+ }
+ }
+
+ additional(alt_items, alt_text) {
+ this.alt_items = alt_items;
+ this.alt_text = alt_text;
+ this.nb_alt_items = this.alt_items.length;
+ this.alt_current = 0;
+ if (this.visible) {
+ $("#toolbar_items").after(`\
+
${this.alt_text} :
+ ${this.alt_current} /
+ ${this.nb_alt_items}
+ [ |
+ ]
+\
+`);
+ $("#toolbar_alt_items .prev").click(this.alt_prev_item);
+ $("#toolbar_alt_items .next").click(this.alt_next_item);
+ }
+ return $(document)
+ .bind("keypress", "[", this.alt_prev_item)
+ .bind("keypress", "]", this.alt_next_item)
+ .bind("keypress", "h", this.alt_prev_item)
+ .bind("keypress", "l", this.alt_next_item);
+ }
+
+ alt_next_item() {
+ this.alt_current += 1;
+ if (this.alt_current > this.nb_alt_items) {
+ this.alt_current = 1;
+ }
+ return this.go_to_alt_current();
+ }
+
+ alt_prev_item() {
+ this.alt_current -= 1;
+ if (this.alt_current <= 0) {
+ this.alt_current = this.nb_alt_items;
+ }
+ this.go_to_alt_current();
+ return false;
+ }
+
+ go_to_alt_current() {
+ if (this.nb_alt_items === 0) {
+ return;
+ }
+ const item = this.alt_items[this.alt_current - 1];
+ const pos = $(item).offset().top;
+ $("html,body")
+ .stop()
+ .animate({ scrollTop: pos }, 500);
+ if (this.visible) {
+ $("#toolbar_current_alt_item").text(this.alt_current);
+ }
+ }
+
+ change_threshold(event) {
+ const el = $(event.target);
+ const ths = this.options.thresholds;
+ const index = $.inArray(parseInt(el.text(), 10), ths) + 1;
+ Toolbar.storage.threshold = this.threshold = ths[index % ths.length];
+ el.text(this.threshold);
+ this.folding();
+ return false;
+ }
+
+ hiding() {
+ if (this.options.folding == null) {
+ return;
+ }
+ $(this.options.folding).each(i => {
+ const item = $(i);
+ const score = parseInt(item.find(".score:first").text(), 10);
+ const where = item.children("h2").children(".anchor");
+ const close = $(
+ '
[-]'
+ ).insertBefore(where);
+ close.after(" ");
+ const hide = function(b) {
+ if (b) {
+ item.addClass("fold");
+ close.text("[+]").attr("title", "Réafficher le fil de discussion");
+ item.children("ul").hide();
+ } else {
+ item.removeClass("fold");
+ close.text("[-]").attr("title", "Cacher le fil de discussion");
+ item.children("ul").show();
+ }
+ };
+ close.click(function() {
+ hide(close.text() === "[-]");
+ return false;
+ });
+ });
+ }
+
+ folding() {
+ if (this.options.folding == null) {
+ return;
+ }
+ const items = $(this.options.folding);
+ items.find(".folding").click();
+ items.each(i => {
+ const item = $(i);
+ const score = parseInt(item.find(".score:first").text(), 10);
+ if (score < this.threshold) {
+ item.addClass("fold");
+ const where = item.children("h2").children(".anchor");
+ const link = $(
+ '
[+]'
+ ).insertBefore(where);
+ link.after(" ").click(function() {
+ item.removeClass("fold");
+ link.remove();
+ return false;
+ });
+ }
+ });
+ }
+}
+
+Toolbar.storage = window["localStorage"] || {};
+
+Toolbar.defaultOptions = {
+ visible: true,
+ folding: null,
+ thresholds: [1, 2, 5, -42, 0]
+};
+
+$.fn.toolbar = function(text, options) {
+ return new Toolbar($(this), text, options);
+};
+
+// Exports the Toolbar class
+window.Toolbar = Toolbar;
diff --git a/app/models/stylesheet.rb b/app/models/stylesheet.rb
index cb92c9b4c..d38a7adbe 100644
--- a/app/models/stylesheet.rb
+++ b/app/models/stylesheet.rb
@@ -35,7 +35,7 @@ def self.capture(url, cookies)
val = cookies[key]
Dir.chdir Rails.root.join("public") do
timeout 30 do
- cmd = ["phantomjs #{Rails.root}/lib/phantomjs/rasterize.coffee '#{url}' #{dst} '#{key}' '#{val}'",
+ cmd = ["phantomjs #{Rails.root}/lib/phantomjs/rasterize.js '#{url}' #{dst} '#{key}' '#{val}'",
# "wkhtmltoimage --crop-h 1024 --cookie '#{key}' '#{val}' '#{url}' #{dst}",
"mogrify -resize 400 -extent 400x400 #{dst} 2>&1"].join(" && ")
system cmd
diff --git a/lib/phantomjs/rasterize.coffee b/lib/phantomjs/rasterize.coffee
deleted file mode 100644
index dcadff232..000000000
--- a/lib/phantomjs/rasterize.coffee
+++ /dev/null
@@ -1,24 +0,0 @@
-page = require('webpage').create()
-system = require 'system'
-
-if system.args.length != 5
- console.log 'Usage: phantomjs rasterize.coffee URL filename cookie_name cookie_value'
- phantom.exit 1
-else
- address = system.args[1]
- output = system.args[2]
- page.viewportSize = { width: 1024, height: 768 }
- phantom.addCookie
- name: system.args[3]
- value: system.args[4]
- domain: 'dlfp.lo'
- path: '/'
- httpOnly: true
- secure: false
- expires: (new Date()).getTime() + (1000 * 60 * 60)
- page.open address, (status) ->
- if status isnt 'success'
- console.log 'Unable to load the address!'
- phantom.exit()
- else
- window.setTimeout (-> page.render output; phantom.exit()), 200
diff --git a/lib/phantomjs/rasterize.js b/lib/phantomjs/rasterize.js
new file mode 100644
index 000000000..1d9296ce6
--- /dev/null
+++ b/lib/phantomjs/rasterize.js
@@ -0,0 +1,31 @@
+const page = require('webpage').create();
+const system = require('system');
+
+if (system.args.length !== 5) {
+ console.log('Usage: phantomjs rasterize.js URL filename cookie_name cookie_value');
+ phantom.exit(1);
+} else {
+ const address = system.args[1];
+ const output = system.args[2];
+ page.viewportSize = { width: 1024, height: 768 };
+ phantom.addCookie({
+ name: system.args[3],
+ value: system.args[4],
+ domain: 'dlfp.lo',
+ path: '/',
+ httpOnly: true,
+ secure: false,
+ expires: (new Date()).getTime() + (1000 * 60 * 60)
+ });
+ page.open(address, function(status) {
+ if (status !== 'success') {
+ console.log('Unable to load the address!');
+ phantom.exit();
+ } else {
+ window.setTimeout(function() {
+ page.render(output);
+ phantom.exit();
+ }, 200);
+ }
+ });
+}
diff --git a/package.json b/package.json
index 3115c41c3..bb120d2d9 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,7 @@
},
"scripts": {
"lint": "stylelint app/*/*/{,common/,parts/,responsive/}*.scss",
- "pretty": "prettier --write app/*/*/{,common/,parts/,responsive/}*.scss"
+ "pretty": "prettier --write app/*/*/{,common/,parts/,responsive/}*.scss app/assets/*/*.js"
},
"stylelint": {
"extends": "stylelint-config-recommended-scss",