Commits

Anonymous committed a1e3e72

jQuery plugin for CodeMirror initialization and preview updating is now working for FeinCMS and model fields. Model inlines need work.

Comments (0)

Files changed (8)

markupmirror/fields.py

         """Adds attributes necessary for CodeMirror initialization to the
         field's widget.
 
-        The class "item-markupmirror" is used to identify textareas that should
-        be enhanced with the editor.
+        The class "markupmirror-editor" is used to identify textareas that
+        should be enhanced with the editor.
 
         The ``data-mode`` and ``data-markuptype`` attributes depend on a
         selected ``default_markup_type``. If a field does not have a default
 
         """
         widget_attrs = {
-            'class': 'item-markupmirror',
+            'class': 'markupmirror-editor',
         }
         if (self.default_markup_type and
             self.default_markup_type in markup_pool):

markupmirror/settings.py

 # Minified JS and CSS files
 MARKUPMIRROR_JS = (
     'markupmirror/jquery-1.7.2.min.js',
+    'markupmirror/jquery.cookie.min.js',
     'markupmirror/codemirror.min.js',
     'markupmirror/markupmirror.js',
 )

markupmirror/static/markupmirror/jquery.cookie.min.js

+/*!
+ * jQuery Cookie Plugin
+ * https://github.com/carhartl/jquery-cookie
+ *
+ * Copyright 2011, Klaus Hartl
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.opensource.org/licenses/GPL-2.0
+ */
+(function(g){g.cookie=function(h,b,a){if(1<arguments.length&&(!/Object/.test(Object.prototype.toString.call(b))||null===b||void 0===b)){a=g.extend({},a);if(null===b||void 0===b)a.expires=-1;if("number"===typeof a.expires){var d=a.expires,c=a.expires=new Date;c.setDate(c.getDate()+d)}b=""+b;return document.cookie=[encodeURIComponent(h),"=",a.raw?b:encodeURIComponent(b),a.expires?"; expires="+a.expires.toUTCString():"",a.path?"; path="+a.path:"",a.domain?"; domain="+a.domain:"",a.secure?"; secure":
+""].join("")}for(var a=b||{},d=a.raw?function(a){return a}:decodeURIComponent,c=document.cookie.split("; "),e=0,f;f=c[e]&&c[e].split("=");e++)if(d(f[0])===h)return d(f[1]||"");return null}})(jQuery);

markupmirror/static/markupmirror/markupmirror.js

     "jQuery": jQuery
 };
 
-/* document ready and jQuery closure */
+/* jQuery closure and document ready */
 (function($) { $(function() {
 
 
 
+/* make AJAX requests work with Django's CSRF protection */
+$.ajaxSetup({
+    crossDomain: false
+});
+$(document).ajaxSend(function(event, xhr, settings) {
+    if (!(/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.type))) {
+        xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
+    }
+});
+
+
+/* Plugin that handles the initialization of CodeMirror editors
+   and preview iframes for each textarea.markupmirror-editor */
 var MarkupMirrorEditor = function( element, options ) {
     var _this = this,
         _public = {
 
             /* add the Codemirror to a new element */
             add: function( el, options ) {
+
+                /* skip if no element passed */
                 if( !el || el.length === 0 ) {
                     return _this;
                 }
 
                 var editor;
                 $.each( el, function() {
-                    if( this.nodeName.toLowerCase() === 'textarea' ) {
-                        if( options === undefined ) {
-                            if( _private.getOption( '_init' ) === true &&
-                                _private.getOption( 'inherit' ) === true ) {
-                                editor = CodeMirror.fromTextArea(
-                                    this,
-                                    _private.getOption( 'initOptions' ) );
-                                _public.configure( { 'editor': editor } );
-                            } else {
-                                editor = CodeMirror.fromTextArea( this );
-                                _public.configure( { 'editor': editor } );
-                            }
+                    if( this.nodeName.toLowerCase() !== 'textarea' ) {
+                        return true;
+                    }
+
+                    if( options === undefined ) {
+                        if( _private.getOption( '_init' ) === true &&
+                            _private.getOption( 'inherit' ) === true ) {
+                            editor = CodeMirror.fromTextArea(
+                                this,
+                                _private.getOption( 'initOptions' ) );
+                            _public.configure( { 'editor': editor } );
                         } else {
-
-                            /* so we are able to overwrite just a few of the
-                             * options */
-                            if( _private.getOption( '_init' ) === true &&
-                                _private.getOption( 'extend' ) ) {
-                                options = $.extend(
-                                    _private.getOption( 'initOptions' ),
-                                    options );
-                            }
-
-                            editor = CodeMirror.fromTextArea( this, options );
+                            editor = CodeMirror.fromTextArea( this );
                             _public.configure( { 'editor': editor } );
                         }
+                    } else {
+                        /* so we are able to overwrite just a few of the
+                         * options */
+                        if( _private.getOption( '_init' ) === true &&
+                            _private.getOption( 'extend' ) ) {
+                            options = $.extend(
+                                _private.getOption( 'initOptions' ),
+                                options );
+                        }
 
-                        if( typeof(
-                            _private.getOption( 'onInit' ) ) === 'function' ) {
-                            _private.getOption( 'onInit' )( this );
-                        }
+                        editor = CodeMirror.fromTextArea( this, options );
+                        _public.configure( { 'editor': editor } );
+                    }
+
+                    if( typeof( _private.getOption( 'onInit' ) ) ===
+                            'function' ) {
+                        _private.getOption( 'onInit' )( this );
                     }
                 });
 
                 /* initial options passed in */
                 initOptions: undefined,
 
-                /* new added elements inherit from the inital
-                 * options */
+                /* new added elements inherit from the inital options */
                 inherit: true,
 
-                /* extends the original options object by passing a
-                 * new element in */
+                /* extends the original options object by passing
+                   a new element in */
                 extend: true,
 
                 /* initial init was done */
     /* add init element */
     _public.add( element, options );
 
-    return _this = _public;
+    _this = _public;
+    return _this;
 };
 
 
-/* initialize plugin for all textareas, class item-markupmirror */
-var $textarea = $('.item-markupmirror'),
+/* initialize plugin for all textarea.markupmirror-editor elements */
+var preview_delay,
+    $textarea = $( '.markupmirror-editor' ),
     CM = new MarkupMirrorEditor( undefined,
-                                 $textarea.data('mmSettings') )
-        .configure({'onInit': createIframe})
-        .configure({'onChange': updateIframe})
+            $.extend( {
+                'onChange': function( editor ) {
+                    clearTimeout( preview_delay );
+                    preview_delay = setTimeout(function() {
+                        updatePreview(editor);
+                    }, 500);
+                }
+            },
+            $textarea.data('mmSettings') ) )
+        .configure({
+            'onInit': createIframe
+        })
         .add( $textarea );
 
 
-/* when init a textarea with codemirror we also create an iframe */
-function createIframe( el ) {
-    var $el = $(el),
-        $iframe = $('<iframe />').attr(
-            'src', $el.data('mmSettings')['base_url']);
+/* when changing the textarea we replace the iframe content with the
+   new coming from the server */
+function updatePreview( editor ) {
+    var $textarea = $( editor.getTextArea() ),
+        $codemirror = $textarea.next( '.CodeMirror' ),
+        $iframe = $codemirror.children( 'iframe' ),
+        mm_settings = $textarea.data( 'mmSettings' ),
+        url = mm_settings[ 'preview_url' ],
+        markup_type = mm_settings[ 'markup_type' ];
 
-    $el.on('change', updateIframe);
-    $iframe.insertAfter( $el );
+    $.post(url, {
+            /* csrfmiddlewaretoken: '{{ csrf_token }}', */
+            markup_type: markup_type,
+            text: editor.getValue()
+        },
+        function(data, textStatus, jqXHR) {
+            $iframe.trigger('_resize');
+
+            if( $iframe.data('load') === true ) {
+                $iframe.trigger('_update',
+                                {html: data});
+            } else {
+                $iframe.data('replace', data);
+            }
+        }
+    );
 }
 
 
-/* when changing the textarea we replace the iframe content with the
- * new coming from the server */
-function updateIframe( e ) {
-    console.log('update');
+/* when init a textarea with codemirror we also create an iframe */
+function createIframe( textarea ) {
+    var $textarea = $( textarea ),
+        $iframe = $( '<iframe />' ).attr(
+            'src', $textarea.data( 'mmSettings' )[ 'base_url' ] ),
+        $codemirror = $textarea.next( '.CodeMirror' );
+
+    $iframe
+        .addClass( 'CodeMirror-preview' )
+        .on({
+            'load': function() {
+                var $this = $(this);
+                $this.data('load', true);
+
+                if( $this.data('replace') !== undefined ) {
+                    $this.trigger(
+                        '_update',
+                        {html: $this.data('replace')});
+                }
+            },
+            '_resize': function() {
+                $(this).css({'height':
+                    $(this).prev().outerHeight()});
+            },
+            '_update': function( e, data ) {
+                $(this)
+                    .contents()
+                    .find('body')
+                    .html(data.html);
+            }
+        })
+        .trigger('_resize')
+        .appendTo( $codemirror );
+
+    /* update iframe contents for the first time */
+    updatePreview( $codemirror.get(0).CodeMirror );
 }
 
-console.log( CM.get('editor') );
 
 
-
-/* end document ready and jQuery closure */
+/* end jQuery closure and document ready */
 }); })(markupmirror.jQuery);

markupmirror/templates/admin/markupmirror/feincms/init_codemirror.html

             }
 
             var markdown_init_fn = function(){
-                $('{% block selectors %}.order-machine textarea.item-markupmirror, #frontend_editor textarea.item-markupmirror{% endblock %}').each(function(){
+                $('{% block selectors %}.order-machine textarea.markupmirror-editor, #frontend_editor textarea.markupmirror-editor{% endblock %}').each(function(){
                     feincms_markdown_add_codemirror(this);
                 });
             }
             {% block enable %}
                 contentblock_init_handlers.push(markdown_init_fn);
                 contentblock_move_handlers.poorify.push(function(item) {
-                    item.find('textarea.item-markupmirror').each(
+                    item.find('textarea.markupmirror-editor').each(
                         feincms_markdown_remove_codemirror);
                 });
                 contentblock_move_handlers.richify.push(function(item) {
-                    item.find('textarea.item-markupmirror').each(
+                    item.find('textarea.markupmirror-editor').each(
                         feincms_markdown_add_codemirror);
                 });
 

markupmirror/widgets.py

 
 class MarkupMirrorTextarea(forms.Textarea):
 
-    css_classes = ('item-markupmirror',)
+    css_classes = ('markupmirror-editor',)
 
     def __init__(self, attrs=None):
-        """Adds the ``item-markupmirror`` class to the textarea to make sure
+        """Adds the ``markupmirror-editor`` class to the textarea to make sure
         it can be identified through JS.
 
         """
 class AdminMarkupMirrorTextareaWidget(
     MarkupMirrorTextarea, AdminTextareaWidget):
 
-    css_classes = ('vLargeTextField', 'item-markupmirror')
+    css_classes = ('vLargeTextField', 'markupmirror-editor')
 
 
 __all__ = ('MarkupMirrorTextarea', 'AdminMarkupMirrorTextareaWidget')

tests/tests/fields.py

         """Form fields for ``MarkupMirrorFields`` always have two additional
         attributes:
 
-        * ``class=item-markupmirror``.
+        * ``class=markupmirror-editor``.
         * ``data-mm-settings``: a JSON dictionary containing the init settings
           for CodeMirror and the URLs and parameters required for the preview
           view.
         self.assertEqual(
             sorted(comment.widget.attrs.keys()),
             ['class', 'cols', 'data-mm-settings', 'rows'])
-        self.assertTrue('item-markupmirror' in comment.widget.attrs['class'])
+        self.assertTrue('markupmirror-editor' in comment.widget.attrs['class'])
         self.assertEqual(
             comment.widget.attrs['data-mm-settings'],
             '{{"markup_type": "{0}", "mode": "{1}"}}'.format(

tests/tests/widgets.py

             comment.widget.render('comment', self.mp.comment),
             textwrap.dedent(u"""\
                 <textarea rows="10" cols="40" name="comment"
-                          class="item-markupmirror"
+                          class="markupmirror-editor"
                           data-mm-settings='{0}'></textarea>""").format(
                     json.dumps(self.mm_settings, sort_keys=True))
             )
                 }),
             textwrap.dedent(u"""\
                 <textarea rows="10" cols="40" name="comment"
-                          class="item-markupmirror"
+                          class="markupmirror-editor"
                           data-mm-settings='{0}'
                           data-something="else"></textarea>""").format(
                     json.dumps(self.mm_settings, sort_keys=True))
             comment.widget.render('comment', u""),
             textwrap.dedent(u"""\
                 <textarea rows="10" cols="40" name="comment"
-                          class="item-markupmirror"
+                          class="markupmirror-editor"
                           data-mm-settings='{0}'></textarea>""").format(
                 json.dumps(attrs, sort_keys=True))
             )
             admin_widget.render('comment', self.mp.comment),
             textwrap.dedent(u"""\
                 <textarea rows="10" cols="40" name="comment"
-                          class="vLargeTextField item-markupmirror"
+                          class="vLargeTextField markupmirror-editor"
                           data-mm-settings='{0}' />""").format(
                 json.dumps(attrs, sort_keys=True))
             )