github.com/bitcubate/cryptojournal@v1.2.5-0.20171102134152-f578b3d788ab/src/lib/editable/assets/scripts/editable.js (about)

     1  // Package Editable provides an active toolbar for content-editable textareas
     2  // Version 1.0
     3  
     4  // TODO - show formatting selected on toolbar when selection changes in our contenteditable
     5  // TODO - perhaps intercept return key to be sure we get a para, and to be sure we insert newline within code sections, not new code tags + br or similar
     6  // TODO - Clean out more HTML cruft from programs like word/textedit
     7  
     8  // On document ready, scan for and activate toolbars associated with contenteditable
     9  DOM.Ready(function() {
    10      if (!DOM.Exists('.content-editable-toolbar')) {
    11          return
    12      }
    13  
    14      // Activate editable content
    15      Editable.Activate('.content-editable-toolbar');
    16  });
    17  
    18  var Editable = (function() {
    19      return {
    20          // Activate editable elements with selector s
    21          Activate: function(s) {
    22              if (!DOM.Exists(s)) {
    23                  return;
    24              }
    25  
    26              DOM.Each('.content-editable-toolbar', function(toolbar) {
    27                  // Store associated elements for access later
    28                  toolbar.buttons = toolbar.querySelectorAll('a');
    29                  var dataEditable = toolbar.getAttribute('data-editable');
    30                  toolbar.editable = DOM.First(DOM.Format("#{0}-editable", dataEditable));
    31                  toolbar.textarea = DOM.First(DOM.Format("#{0}-textarea", dataEditable));
    32  
    33                  if (toolbar.editable === undefined) {
    34                      toolbar.editable = DOM.Nearest(toolbar, '.content-editable')[0];
    35                  }
    36                  if (toolbar.textarea === undefined) {
    37                      toolbar.textarea = DOM.Nearest(toolbar, '.content-textarea')[0];
    38                  }
    39  
    40                  // Set textarea to hidden initially
    41                  toolbar.textarea.style.display = 'none';
    42  
    43                  // Listen to a form submit, and call updateContent to make sure 
    44                  // our textarea in the form is up to date with the latest content
    45                  toolbar.textarea.form.addEventListener('submit', function(e) {
    46                      Editable.updateContent(toolbar.editable, toolbar.textarea, false);
    47                      return false;
    48                  });
    49  
    50                  // Intercept paste on editable and remove complex html before it is pasted in
    51                  toolbar.editable.addEventListener('input', function(e) {
    52                      Editable.cleanHTMLElements(this);
    53                  });
    54  
    55  
    56                  // Listen to button clicks within the toolbar
    57                  DOM.ForEach(toolbar.buttons, function(el, i) {
    58                      el.addEventListener('click', function(e) {
    59                          var cmd = this.id;
    60                          var insert = "";
    61  
    62                          switch (cmd) {
    63                              case "showCode":
    64                                  Editable.updateContent(toolbar.editable, toolbar.textarea, true);
    65                                  break;
    66                              case "createLink":
    67                                  insert = prompt("Supply the web URL to link to");
    68                                  // Prefix url with http:// if no scheme supplied
    69                                  if (insert.indexOf('http') !== 0) {
    70                                      insert = "http://" + insert;
    71                                  }
    72                                  break;
    73                              case "formatblock":
    74                                  insert = this.getAttribute('data-format');
    75                                  break;
    76                              default:
    77                                  break;
    78                          }
    79  
    80                          if (cmd.length > 0) {
    81                              document.execCommand(cmd, false, insert);
    82                          }
    83  
    84                          // Find and remove evil html created by browsers 
    85                          var sel = Editable.getSelectionParentElement();
    86  
    87                          if (sel !== null) {
    88                              // Clean align stuff
    89                              Editable.cleanAlign(cmd, sel);
    90                              Editable.cleanHTMLElements(sel);
    91                              sel.removeAttribute('style');
    92                          }
    93  
    94                          e.preventDefault();
    95                          return false;
    96                      });
    97                  });
    98              });
    99          }, // End activate
   100  
   101          // cleanAlign
   102          cleanAlign: function(cmd, el) {
   103  
   104              switch (cmd) {
   105                  case "justifyCenter":
   106  
   107                      if (sel.hasClass('align-center')) {
   108                          sel.removeClass('align-center');
   109                      } else {
   110                          sel.addClass('align-center');
   111                      }
   112  
   113                      sel.removeClass('align-left').removeClass('align-right');
   114                      sel.removeAttr('style');
   115                      break;
   116                  case "justifyLeft":
   117                      if (sel.hasClass('align-left')) {
   118                          sel.removeClass('align-left');
   119                      } else {
   120                          sel.addClass('align-left');
   121                      }
   122  
   123                      sel.removeClass('align-center').removeClass('align-right');
   124                      sel.removeAttribute('style');
   125  
   126  
   127                      break;
   128                  case "justifyRight":
   129  
   130                      if (sel.hasClass('align-right')) {
   131                          sel.removeClass('align-right');
   132                      } else {
   133                          sel.addClass('align-right');
   134                      }
   135  
   136                      sel.removeClass('align-center').removeClass('align-left');
   137                      sel.removeAttribute('style');
   138                      break;
   139              }
   140          },
   141  
   142  
   143  
   144          // CleanHTML is used to clean the html content from the contenteditable before it is assigned to the textarea
   145          cleanHTML: function(html) {
   146              html = html.replace(/<\/?span>/gi, ''); // Remove all empty span tags
   147              html = html.replace(/<\/?font [^>]*>/gi, ''); // Remove ALL font tags
   148              html = html.replace(/&nbsp;</gi, ' <'); // this is sometimes required, be careful
   149              html = html.replace(/<p><\/p>/gi, '\n'); // pretty format but remove empty paras
   150              html = html.replace(/<br><\/li>/gi, '<\/li>');
   151  
   152              // Remove comments and other MS cruft
   153              html = html.replace(/<!--[\w\d\[\]\s<\/>:.!="*{};-]*-->/gi, '');
   154              html = html.replace(/ class\=\"MsoNormal\"/gi, '');
   155              html = html.replace(/<p><o:p> <\/o:p><\/p>/gi, '');
   156  
   157              // Pretty printing elements which follow on from one another
   158              html = html.replace(/><(li|ul|ol|p|h\d|\/ul|\/ol)>/gi, '>\n<$1>');
   159  
   160              return html;
   161          },
   162  
   163          // cleanHTMLElements removes certain attributes which are usually full of junk (style, color etc)
   164          cleanHTMLElements: function(el) {
   165              // Browsers tend to use style attributes to add all sorts of awful stuff to the html
   166              // No inline styles allowed
   167              DOM.ForEach(el.querySelectorAll('p, div, b, i, h1, h2, h3, h4, h5, h6'), function(e) { e.removeAttribute('style'); });
   168              DOM.ForEach(el.querySelectorAll('span'), function(e) {
   169                  e.removeAttribute('style');
   170                  e.removeAttribute('lang');
   171              });
   172              DOM.ForEach(el.querySelectorAll('font'), function(e) { e.removeAttribute('color'); });
   173          },
   174  
   175          // If textarea visible update the content editable with new html
   176          // else update the textarea with new content editable html
   177          updateContent: function(editable, textarea, toggle) {
   178              var html = '';
   179              if (textarea.style.display !== 'none') {
   180                  html = textarea.value;
   181                  editable.innerHTML = html;
   182                  if (toggle) {
   183                      editable.style.display = '';
   184                      textarea.style.display = 'none';
   185                  }
   186              } else {
   187                  html = editable.innerHTML;
   188                  // Cleanup the html by removing plain spans
   189                  html = Editable.cleanHTML(html);
   190                  textarea.value = html;
   191                  if (toggle) {
   192                      editable.style.display = 'none';
   193                      textarea.style.display = '';
   194                  }
   195              }
   196  
   197          },
   198  
   199          // The closest parent element which encloses the entire text selection
   200          getSelectionParentElement: function() {
   201              var p = null,
   202                  sel;
   203              if (window.getSelection) {
   204                  sel = window.getSelection();
   205                  if (sel.rangeCount) {
   206                      p = sel.getRangeAt(0).commonAncestorContainer;
   207                      if (p.nodeType != 1) {
   208                          p = p.parentNode;
   209                      }
   210                  }
   211              } else if ((sel = document.selection) && sel.type != "Control") {
   212                  p = sel.createRange().parentElement();
   213              }
   214              return p;
   215          }
   216  
   217      };
   218  }());