Anonymous avatar 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))
             )
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.