#tinymce and #xeditable

headlineI am working on a project with some fairly complex editable fields in popups requirements.  I started looking around for a library that would package up the popups so that I can use them without having to start from scratch.  I came across the x-editable library which works very smoothly and handles most of what I need it to.  One of the requirements is that the textarea in the popup need to be a tinymce editor.  Here is my solution to hooking them up.  As usual the code is fairly simple and elegant once it works, but getting it to work took far longer than I anticipated.

Html:

<div>
  <span>Headline:</span>
  <a href="#" id="puHeadline"><i class="fa fa-edit"></i></a>
  <div id="textGoesHere"></div>
</div>

JavaScript:

$(function () {
  //----------------------------------------------------------------
  // Demo popover edit with tinymce
  $.fn.editable.defaults.mode = 'popup'; //toggle `popup` / `inline` mode

  $('#puHeadline').editable({
    pk: 1,
    type: 'textarea',
    title: 'Headline:',
    placeholder: 'Enter your headline...',
    placement: 'right',
    showbuttons: 'bottom',
    inputclass: 'tinymce',
    validate: function(value) {
      if($.trim(value) == '') {
        return 'This field is required';
      }
    },
    display: function (value) {
      $('#textGoesHere').html(value);
    },
  });

  $('#puHeadline').click(function() {
    setupTinyMCE();
  });

  function setupTinyMCE() {
    // TinyMCE functionality
    $('.tinymce').tinymce({
      // Location of TinyMCE script
      script_url: '/scripts/tinymce/4.0.25/tinymce.min.js',
      // General options
      theme: "modern",
      plugins: "link code fullscreen fmgwordpaste",
      toolbar: "bold italic | alignleft aligncenter alignright | fullscreen",
      menubar: false,
      statusbar: false,
      content_css: "/areas/c2c/Content/css/editor.css",
      setup: function(editor) {
      },
      init_instance_callback: function(editor) {
        $('.mce-tinymce').show();
        $(editor.getContainer()).find(".mce-path").css("display", "none");
      },
    });
  }
});

XML Date, Convert to TimeZone

Dates are complicated no matter which language you use.  To solve some of our problems we save all dates as Universal time, typically using (DateTime.UtcNow).  Then when we display it we convert to a specific timezone.

For this specific project we save the Utc date as an XML string.  When we parse it out for display we read the date, convert to the proper timezone, then put it back in the XML be parsed for display. In this system we show all our dates as Central Standard Time.   We were doing a TryParse but got an error stating that the date was the wrong “Kind”.  Finally I hit on the right syntax and, as with so many other little tidbits, don’t want to lose it.


.......

XElement cElement = data.Element("CreatedDate");

cElement.ReplaceWith(GetDateFormattedCst(cElement, "{0:g} CST"));

.......
 private XElement GetDateFormattedCst(XElement element, string format)
    {
      if(element == null || element.IsEmpty)
         return null;
      XmlReader reader = element.CreateReader();
      reader.MoveToContent();
      string dateValue = reader.ReadInnerXml();

      DateTime dateTime;
      CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
      DateTimeStyles styles = DateTimeStyles.AdjustToUniversal;

      if (DateTime.TryParse(dateValue, culture, styles, out dateTime))
      {
        dateTime = dateTime.ToCentralStandardTime();
      }
      else
      {
        return null;
      }

      dateValue = String.Format(format, dateTime);
      dateValue = string.Format("<{0}>{1}</{0}>", element.Name, dateValue);

      return XElement.Parse(dateValue);
    }

#WebEssentials & Zen Coding

One of the plug-ins that I use the most in Visual Studio is Web Essentials.  It has a large set of features designed to make life easier for developers to get their jobs done.   Today I read the article by Visual Studio Time Saver – Faster HTML Coding by Susan Ibach where she talks about the Zen Coding feature in Web Essentials.  For as many years I have used Web Essentials this is not a feature that I am familiar with.  It’s incredible!  Here is a list of links to different places to get additional information on Web Essentials and tips on how to use Zen Coding.

As a bonus check out vstips in Twitter.  There some great suggestions in there for making Visual Studio easier to use.

Here is a link to the video Susan referenced that shows how to use the Zen Coding functionality.

MVC: Check if Is Dirty via #JavaScript

I had a need to do some fancy work on a MVC Razor page enabling and disabling buttons based on various criteria.  To do this I needed to check to see if the user had changed any data on the page I ended up using the following structure to do it.

First set up your page with a form and all your input fields.

@using(Html.BeginForm("actionname", "controllername", new { area = "areaname" }, 
       FormMethod.Post, new { id = "saveForm", @class = "disable-submit-form" }))
{
     /* lots of page code */
}

This method serializes the data into a Name, Value array of objects  and hashes it.

     // Grab all the data fields in the code and serialize them, then hash them
     // @Html.Checkbox and CheckBoxFor need to be handled as a separate case 
     function GetSerializedItems() {
        var serializedArray = $(".disable-submit-form").serializeArray()
                                 .filter(function (item) { 
                                           return item.name != 'CheckboxItem1' 
                                               && item.name != 'CheckboxItem2'; 
                                         });
        var serializedCheckboxItems = $('input:checkbox').map(
                                        function () { 
                                           return { name: this.name, 
                                                    value: this.checked ? this.value 
                                                                        : "false" }; 
                                        });
        for (var index = 0; index < serializedCheckboxItems.length; ++index) {
           serializedArray.push(serializedCheckboxItems[index]);
        }
        return convertSerializedArrayToHash(serializedArray);
      }

      $(document).ready(function () {
        // Get the initial state of the data on the page.
        var startItems = GetSerializedItems(); 
       
        // On each field change recheck the current button state
        $('.disable-submit-form').on('change', function () {
           showhidebuttons();
        });

      function showhidebuttons() {
        var $form = $(".disable-submit-form");
        var currentItems = GetSerializedItems();
        var dirty = hashDiff(startItems, currentItems);
        var isDirty = countProperties(dirty) > 0;
        var isValid = $form.valid();
        var isDraft = '@Model.IsDraft' === 'True';

        $form.find(".submitform").attr('disabled', 'disabled');

        if (isValid && isDirty) {
          $form.find(".submitform").removeAttr('disabled');
        } else if (isDraft && isValid) {
          $form.find("#PreviewId").removeAttr('disabled');
          $form.find("#PublishId").removeAttr('disabled');
        }
      }
     }
     
     //Call ShowHideButtons to set the default state
     showhidebuttons();

Here are the javascript helpers I used.

 function convertSerializedArrayToHash(a) {
 var r = {};
 for (var i = 0; i < a.length; i++) {
 r[a[i].name] = a[i].value;
 }
 return r;
 }

 function hashDiff(h1, h2) {
 var d = {};
 for (k in h2) {
 if (h1[k] !== h2[k]) d[k] = h2[k];
 }
 return d;
 }

 function countProperties(obj) {
 var count = 0;

 for (var prop in obj) {
 if (obj.hasOwnProperty(prop))
 ++count;
 }

 return count;
 }

#TinyMCE: Fixing Pageload Flickering

When my TinyMCE boxes load I find that there is brief moment when it flickers, the users don’t like that.   Looking closely I found that the flicker is a brief glimpse of the underlying HTML that is rendered before the TinyMCE box finishes loading and is displayed.  To fix it I do some fancy hide/show work.  Initially the page is loaded with the control hidden, then when it is done loading it shows it. Since I had the code already put together, I left in an example of a read-only version of the editor.

When the page is loading mark the textarea as hidden:

     <div class="form-group">
        @Html.Label("Content:") @Html.ValidationMessageFor(m => m.ContentHtml)
        @if (Model.IsReadOnly)
        {
          @Html.TextAreaFor(m => m.ContentHtml, 
                            new { @class = "tinymce-ro", @readonly = "readonly", 
                            @style = "height:300px; width:100%; display:none" })
        }
        else
        {
          @Html.TextAreaFor(m => m.ContentHtml, 
                            new { @class = "tinymce", maxlength = 5000, 
                            @style = "height:300px; width:100%; display:none" })
        }
      </div>

Then show it again once loading is complete:

    $('textarea.tinymce').tinymce({
      script_url: '/scripts/tinymce/4.0.25/tinymce.min.js',
      theme: "modern",
      toolbar: "bold italic ",
      paste_auto_cleanup_on_paste: true,
      init_instance_callback: function (editor) {
        $('.mce-tinymce').show('fast');
      }
    });

    $('textarea.tinymce-ro').tinymce({
      readonly: true,
      script_url: '/scripts/tinymce/4.0.25/tinymce.min.js',
      theme: "modern",
      toolbar: false,
      menubar: false,
      statusbar: false,
      init_instance_callback: function (editor) {
        if (document.getElementById(editor.id).style.display === 'none') {
          editor.getBody().style.backgroundColor = "#EBEBE4";
        }
        $('.mce-tinymce').show('fast');
      }
    });

UPDATE: I discovered that my custom plugins were not loading properly when I used this method. I came up with another version posted here – Pageload Flickering V2.

#TinyMCE: Plugin DDL with Popup

Recently I had a story asking for some advanced functionality for a new TinyMCE button. I broke down the requirements to the following.  I created a JSFiddle to show it working.

  • Custom TinyMCE drop down menu
  • When a menu item is selected it should insert the menu value into the editor at the cursor
  • Last item on the menu will show a popup
  • On the popup have a text box for entering custom text
  • When Insert is clicked insert the text into the editor at the cursor
Core plugin code – Javascript
$('#Insert').click(function () {
    var text = $('#added-token').val();
    tinyMCE.activeEditor.execCommand('mceInsertContent', false, &quot;{Added Text:&quot; + text + &quot;}&quot;);
    $('#token-modal').modal('hide');
    return false;
});
tinymce.PluginManager.add('tokens', function (editor, url) {
    editor.addButton('inserttoken', {
        title: &quot;Insert Token&quot;,
        type: 'menubutton',
        text: 'Tokens',
        icon: false,
        onselect: function (e) {
          if (e.control.settings.value != '{MenuItemPopup}') {
            editor.insertContent(e.control.settings.value);
          } else {
            $('#token-modal').modal({ show: true });
          }
        },
        menu: [
            { text: 'Menu Item 1', value: '{MenuItem1}' },
            { text: 'Menu Item 2', value: '{MenuItem2}' },
            { text: 'Menu Item 3', value: '{MenuItem3}' },
            { text: 'Menu Item 4', value: '{MenuItem4}' },                     
            { text: 'Menu Item with Popup', value: '{MenuItemPopup}' }                        
        ],
        onPostRender: function () {
            this.value('{MenuItem1}');
        }
    });    
});
tinymce.init({
    selector: &quot;textarea&quot;,
	width:      '100%',
	height:     270,
    plugins:    [ &quot;anchor link tokens&quot; ],
    statusbar:  false,
	menubar:    false,
    toolbar:    &quot;inserttoken anchor | alignleft aligncenter alignright alignjustify&quot;,
	rel_list:   [ { title: 'Lightbox', value: 'lightbox' } ]
});
Core plugin code – HTML
&lt;div id=&quot;token-modal&quot; class=&quot;modal hide fade&quot; &gt;
    &lt;div class=&quot;modal-header&quot;&gt;
        &lt;button type=&quot;button&quot; class=&quot;close&quot; data-dismiss=&quot;modal&quot; aria-hidden=&quot;true&quot;&gt;×&lt;/button&gt;
        &lt;h3 id=&quot;myModalLabel&quot;&gt;Modal header&lt;/h3&gt;
    &lt;/div&gt;
    &lt;div class=&quot;modal-body&quot;&gt;
        &lt;input type=&quot;text&quot; name=&quot;group&quot; id=&quot;added-token&quot;  /&gt;
    &lt;/div&gt;
    &lt;div class=&quot;modal-footer&quot;&gt;
        &lt;button data-dismiss=&quot;modal&quot;&gt;Close&lt;/button&gt;
        &lt;button id=&quot;Insert&quot;&gt;Insert&lt;/button&gt;
    &lt;/div&gt;