sync live edit js from panikdb
authorFrédéric Péters <fpeters@0d.be>
Sat, 20 Jun 2020 12:21:43 +0000 (14:21 +0200)
committerFrédéric Péters <fpeters@0d.be>
Sat, 20 Jun 2020 12:21:43 +0000 (14:21 +0200)
chloro/phyll/static/js/chloro.js

index e7b3f1d..6b831ac 100644 (file)
@@ -2,17 +2,58 @@
   var Phylly = {
     BLOCKS: [
           {name: 'code', tag: 'PRE', klass: 'screen'},
-          {name: 'figure', tag: 'DIV', subtag: true, klass: 'figure'},
+          {name: 'figure', special: 'img', tag: 'DIV', subtag: true, klass: 'figure'},
           {name: 'note', tag: 'DIV', subtag: true, klass: 'note'},
     ],
     input_event: function(event) {
-      if (event.originalEvent.inputType != "insertParagraph") return true;
       var sel = document.getSelection();
       var anchorNode = sel.anchorNode;
+      if (sel.anchorNode.contentEditable === 'true' && (
+              sel.anchorNode.innerHTML == '<br>' || !sel.anchorNode.innerHTML)) {
+        // when everything has been removed, add back <p><br></p>
+        var empty_p = document.createElement('P');
+        empty_p.appendChild(document.createElement('BR'));
+        if (anchorNode.childNodes.length) { // lone <br>
+          anchorNode.removeChild(anchorNode.childNodes[0]);
+        }
+        anchorNode.appendChild(empty_p);
+        var range = document.createRange();
+        range.setStart(empty_p, 0);
+        sel.removeAllRanges();
+        sel.addRange(range);
+        return;
+      }
+      if (event.originalEvent.inputType != "insertParagraph") return true;
+      if (sel.anchorNode.tagName == "DIV" && sel.anchorNode.innerHTML == "<br>") {
+        // new empty div got inserted, replace it with a <p>
+        var empty_p = document.createElement('P');
+        empty_p.appendChild(document.createElement('BR'));
+        var empty_div = sel.anchorNode;
+        empty_div.replaceWith(empty_p);
+        var range = document.createRange();
+        range.setStart(empty_p, 0);
+        sel.removeAllRanges();
+        sel.addRange(range);
+      }
+      if (sel.anchorNode.tagName == "LI" && sel.anchorNode.innerHTML == "<br>") {
+        // new empty li got inserted, insert a <p> within
+        var empty_p = document.createElement('P');
+        empty_p.appendChild(document.createElement('BR'));
+        var empty_li = anchorNode;
+        if (empty_li.childNodes.length) { // lone <br>
+          empty_li.removeChild(empty_li.childNodes[0]);
+        }
+        empty_li.appendChild(empty_p);
+        var range = document.createRange();
+        range.setStart(empty_p, 0);
+        sel.removeAllRanges();
+        sel.addRange(range);
+      }
       var prev_p = sel.anchorNode.previousSibling;
       if (! prev_p) return;
       if (prev_p.tagName != 'P') {
         prev_p = $(prev_p).parents('p')[0];
+        if (! prev_p || prev_p.tagName != 'P') return;
       }
       var title_match = prev_p.innerText.match(/^(h[1-6]). /);
       if (title_match) {
           $(inline_style_toolbar).hide();
         }
       });
+      var $image_upload = $('<input type="file" nam="image" id="image-upload">');
+      $image_upload.on('change', upload_image);
+      $image_upload.appendTo(document.body);
+
+      $(document).on('click', 'div.figure span.empty', function() {
+        window.active_figure = this.parentNode;
+        $('#image-upload').trigger('click');
+        return true;
+      });
+    },
+
+    off: function() {
+      $('#image-upload').remove();
+      if (block_style_toolbar) { block_style_toolbar.hide(); }
+      if (inline_style_toolbar) { inline_style_toolbar.hide(); }
+      $(document).off('selectionchange');
     },
 
     bind_events: function(elem) {
       $(elem).on('keyup click', update_block_style_toolbar);
     },
 
+    unbind_events: function(elem) {
+      $(elem).off('input');
+      $(elem).off('keyup click');
+    },
+
   }
   window.Phylly = Phylly;
 
+  function upload_image() {
+    if ($(this).prop('files').length > 0) {
+      var file = $(this).prop('files')[0];
+      var params = new FormData();
+      params.append('image', file);
+      $.post({url: '/wiki/ajax/image/', processData: false, data: params, contentType: false}).success(function(data) {
+        var img = document.createElement('IMG');
+        img.src = data.url;
+        if (data.orig_url) {
+          img.setAttribute('data-orig-url', data.orig_url);
+        }
+        $(window.active_figure).empty().append(img);
+      });
+    }
+  }
+
   function get_contenteditable_subnode(node) {
     if (node === null) return null;
+    if (node.contentEditable === 'true') return node;  // but we shouldn't arrive at root
     if (node.parentNode.contentEditable === 'true') return node;
     return get_contenteditable_subnode(node.parentNode);
   }
+  function get_parent(node, type) {
+   if (node === null) return null;
+   if (node.tagName == type) return node;
+   return get_parent(node.parentNode, type);
+  }
   function get_active_block(node) {
     var main_node = get_contenteditable_subnode(node);
     if (main_node === null) return null;
   function block_style() {
     var sel = window.getSelection();
     var current_anchor = sel.anchorNode;
+    if (this.action_block.special == 'img') {
+      action = 'insertHTML';
+      param = '<div class="figure"><span class="empty"></span></div><p id="new-p"></p>';
+      document.execCommand(action, false, param);
+      current_anchor = $('#new-p')[0];
+      $(current_anchor).attr('id', null);
+      var range = document.createRange();
+      range.setStart(current_anchor, 0);
+      sel.removeAllRanges();
+      sel.addRange(range);
+      update_block_style_toolbar();
+
+      return;
+    }
+    if (this.action_block.special == 'list') {
+      if (this.classList.contains('on')) { // toggle off
+        var main_node = get_contenteditable_subnode(sel.anchorNode);
+        var li = get_parent(sel.anchorNode, 'LI');
+        for (var i=li.childNodes.length; i>0; i--) {
+          var child = li.childNodes[i-1];
+          main_node.insertAdjacentElement('afterend', child);
+          var range = document.createRange();
+          range.setStart(child, 0);
+          sel.removeAllRanges();
+          sel.addRange(range);
+        }
+        li.remove();
+        update_block_style_toolbar();
+      } else {
+        var current_node = sel.anchorNode;
+        var ul = document.createElement('UL');
+        ul.className = 'list';
+        var li = document.createElement('LI');
+        ul.appendChild(li);
+        sel.anchorNode.parentNode.insertBefore(ul, current_node);
+        li.appendChild(current_node);
+        var range = document.createRange();
+        range.setStart(current_node, 0);
+        sel.removeAllRanges();
+        sel.addRange(range);
+      }
+      return;
+    }
     if (this.classList.contains('on')) { // toggle off
       if (this.action_block.subtag) {
         // unwrap
       action = 'insertHTML';
       param = $('<code></code>', {text: window.getSelection().toString()})[0].outerHTML;
     }
+    if (action == 'wiki') {
+      action = 'insertHTML';
+      var text = window.getSelection().toString();
+      var $new_link = $('<a></a>', {text: text, href: '#tbd'});
+      var request_id = Math.floor(Math.random() * 10000);
+      $new_link.attr('data-request-id', request_id);
+      var params = {};
+      params.title = text;
+      params.request_id = request_id;
+      $.post('/wiki/ajax/newpage/', params).success(function(data) {
+        $('a[data-request-id=' + data.request_id + ']').attr('href', data.url).removeAttr('data-request-id');
+      });
+      param = $new_link[0].outerHTML;
+    }
     if (action == 'createLink') {
       var sel = window.getSelection();
       var $input = $('input[name=link-target]');