source: pro-violet-viettel/docs/template/assets/js/uncompressed/markdown/bootstrap-markdown.js @ 400

Last change on this file since 400 was 400, checked in by dungnv, 11 years ago
  • Property svn:mime-type set to text/plain
File size: 28.3 KB
Line 
1/* ===================================================
2 * bootstrap-markdown.js v1.1.4
3 * http://github.com/toopay/bootstrap-markdown
4 * ===================================================
5 * Copyright 2013 Taufan Aditya
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ========================================================== */
19
20!function ($) {
21
22  "use strict"; // jshint ;_;
23
24
25  /* MARKDOWN CLASS DEFINITION
26   * ========================== */
27
28  var Markdown = function (element, options) {
29    // Class Properties
30    this.$ns          = 'bootstrap-markdown'
31    this.$element     = $(element)
32    this.$editable    = {el:null, type:null,attrKeys:[], attrValues:[], content:null}
33    this.$options     = $.extend(true, {}, $.fn.markdown.defaults, options)
34    this.$oldContent  = null
35    this.$isPreview   = false
36    this.$editor      = null
37    this.$textarea    = null
38    this.$handler     = []
39    this.$callback    = []
40    this.$nextTab     = []
41
42    this.showEditor()
43  }
44
45  Markdown.prototype = {
46
47    constructor: Markdown
48
49  , __alterButtons: function(name,alter) {
50      var handler = this.$handler, isAll = (name == 'all'),that = this
51
52      $.each(handler,function(k,v) {
53        var halt = true
54        if (isAll) {
55          halt = false
56        } else {
57          halt = v.indexOf(name) < 0
58        }
59
60        if (halt == false) {
61          alter(that.$editor.find('button[data-handler="'+v+'"]'))
62        }
63      })
64    }
65   
66  , __buildButtons: function(buttonsArray, container) {
67      var i,
68          ns = this.$ns,
69          handler = this.$handler,
70          callback = this.$callback
71
72      for (i=0;i<buttonsArray.length;i++) {
73        // Build each group container
74        var y, btnGroups = buttonsArray[i]
75        for (y=0;y<btnGroups.length;y++) {
76          // Build each button group
77          var z,
78              buttons = btnGroups[y].data,
79              btnGroupContainer = $('<div/>', {
80                                    'class': 'btn-group'
81                                  })
82
83          for (z=0;z<buttons.length;z++) {
84            var button = buttons[z],
85                buttonToggle = '',
86                buttonHandler = ns+'-'+button.name,
87                btnText = button.btnText ? button.btnText : '',
88                btnClass = button.btnClass ? button.btnClass : 'btn',
89                tabIndex = button.tabIndex ? button.tabIndex : '-1'
90
91            if (button.toggle == true) {
92              buttonToggle = ' data-toggle="button"'
93            }
94
95            // Attach the button object
96            btnGroupContainer.append('<button class="'
97                                    +btnClass
98                                    +' btn-small" title="'
99                                    +button.title
100                                    +'" tabindex="'
101                                    +tabIndex
102                                    +'" data-provider="'
103                                    +ns
104                                    +'" data-handler="'
105                                    +buttonHandler
106                                    +'"'
107                                    +buttonToggle
108                                    +'><i class="'
109                                    +button.icon
110                                    +'"></i> '
111                                    +btnText
112                                    +'</button>')
113
114            // Register handler and callback
115            handler.push(buttonHandler)
116            callback.push(button.callback)
117          }
118
119          // Attach the button group into container dom
120          container.append(btnGroupContainer)
121        }
122      }
123
124      return container
125    }
126  , __setListener: function() {
127      // Set size and resizable Properties
128      var hasRows = typeof this.$textarea.attr('rows') != 'undefined',
129          maxRows = this.$textarea.val().split("\n").length > 5 ? this.$textarea.val().split("\n").length : '5',
130          rowsVal = hasRows ? this.$textarea.attr('rows') : maxRows
131
132      this.$textarea.attr('rows',rowsVal)
133      this.$textarea.css('resize','none')
134
135      this.$textarea
136        .on('focus',    $.proxy(this.focus, this))
137        .on('keypress', $.proxy(this.keypress, this))
138        .on('keyup',    $.proxy(this.keyup, this))
139
140      if (this.eventSupported('keydown')) {
141        this.$textarea.on('keydown', $.proxy(this.keydown, this))
142      }
143
144      // Re-attach markdown data
145      this.$textarea.data('markdown',this)
146    }
147
148  , __handle: function(e) {
149      var target = $(e.currentTarget),
150          handler = this.$handler,
151          callback = this.$callback,
152          handlerName = target.attr('data-handler'),
153          callbackIndex = handler.indexOf(handlerName),
154          callbackHandler = callback[callbackIndex]
155
156      // Trigger the focusin
157      $(e.currentTarget).focus()
158
159      callbackHandler(this)
160
161      // Unless it was the save handler,
162      // focusin the textarea
163      if (handlerName.indexOf('cmdSave') < 0) {
164        this.$textarea.focus()
165      }
166
167      e.preventDefault()
168    }
169
170  , showEditor: function() {
171      var instance = this,
172          textarea,
173          ns = this.$ns,
174          container = this.$element,
175          originalHeigth = container.css('height'),
176          originalWidth = container.css('width'),
177          editable = this.$editable,
178          handler = this.$handler,
179          callback = this.$callback,
180          options = this.$options,
181          editor = $( '<div/>', {
182                      'class': 'md-editor',
183                      click: function() {
184                        instance.focus()
185                      }
186                    })
187
188      // Prepare the editor
189      if (this.$editor == null) {
190        // Create the panel
191        var editorHeader = $('<div/>', {
192                            'class': 'md-header'
193                            })
194
195        // Build the main buttons
196        if (options.buttons.length > 0) {
197          editorHeader = this.__buildButtons(options.buttons, editorHeader)
198        }
199
200        // Build the additional buttons
201        if (options.additionalButtons.length > 0) {
202          editorHeader = this.__buildButtons(options.additionalButtons, editorHeader)
203        }
204
205        editor.append(editorHeader)
206
207        // Wrap the textarea
208        if (container.is('textarea')) {
209          container.before(editor)
210          textarea = container
211          textarea.addClass('md-input')
212          editor.append(textarea)
213        } else {
214          var rawContent = (typeof toMarkdown == 'function') ? toMarkdown(container.html()) : container.html(),
215              currentContent = $.trim(rawContent)
216
217          // This is some arbitrary content that could be edited
218          textarea = $('<textarea/>', {
219                       'class': 'md-input',
220                       'val' : currentContent
221                      })
222
223          editor.append(textarea)
224
225          // Save the editable
226          editable.el = container
227          editable.type = container.prop('tagName').toLowerCase()
228          editable.content = container.html()
229
230          $(container[0].attributes).each(function(){
231            editable.attrKeys.push(this.nodeName)
232            editable.attrValues.push(this.nodeValue)
233          })
234
235          // Set editor to blocked the original container
236          container.replaceWith(editor)
237        }
238
239        // Create the footer if savable
240        if (options.savable) {
241          var editorFooter = $('<div/>', {
242                           'class': 'md-footer'
243                         }),
244              saveHandler = 'cmdSave'
245
246          // Register handler and callback
247          handler.push(saveHandler)
248          callback.push(options.onSave)
249
250          editorFooter.append('<button class="btn btn-success" data-provider="'
251                              +ns
252                              +'" data-handler="'
253                              +saveHandler
254                              +'"><i class="icon icon-white icon-ok"></i> Save</button>')
255
256          editor.append(editorFooter)
257        }
258
259        // Set width/height
260        $.each(['height','width'],function(k,attr){
261          if (options[attr] != 'inherit') {
262            if (jQuery.isNumeric(options[attr])) {
263              editor.css(attr,options[attr]+'px')
264            } else {
265              editor.addClass(options[attr])
266            }
267          }
268        })
269
270        // Reference
271        this.$editor     = editor
272        this.$textarea   = textarea
273        this.$editable   = editable
274        this.$oldContent = this.getContent()
275
276        this.__setListener()
277
278        // Set editor attributes, data short-hand API and listener
279        this.$editor.attr('id',(new Date).getTime())
280        this.$editor.on('click', '[data-provider="bootstrap-markdown"]', $.proxy(this.__handle, this))
281
282      } else {
283        this.$editor.show()
284      }
285
286      if (options.autofocus) {
287        this.$textarea.focus()
288        this.$editor.addClass('active')
289      }
290
291      // Trigger the onShow hook
292      options.onShow(this)
293
294      return this
295    }
296
297  , showPreview: function() {
298      var options = this.$options,
299          callbackContent = options.onPreview(this), // Try to get the content from callback
300          container = this.$textarea,
301          afterContainer = container.next(),
302          replacementContainer = $('<div/>',{'class':'md-preview','data-provider':'markdown-preview'}),
303          content
304
305      // Give flag that tell the editor enter preview mode
306      this.$isPreview = true
307      // Disable all buttons
308      this.disableButtons('all').enableButtons('cmdPreview')
309
310      if (typeof callbackContent == 'string') {
311        // Set the content based by callback content
312        content = callbackContent
313      } else {
314        // Set the content
315        content = (typeof markdown == 'object') ? markdown.toHTML(container.val()) : container.val()
316      }
317
318      // Build preview element
319      replacementContainer.html(content)
320
321      if (afterContainer && afterContainer.attr('class') == 'md-footer') {
322        // If there is footer element, insert the preview container before it
323        replacementContainer.insertBefore(afterContainer)
324      } else {
325        // Otherwise, just append it after textarea
326        container.parent().append(replacementContainer)
327      }
328
329      // Hide the last-active textarea
330      container.hide()
331
332      // Attach the editor instances
333      replacementContainer.data('markdown',this)
334
335      return this
336    }
337
338  , hidePreview: function() {
339      // Give flag that tell the editor quit preview mode
340      this.$isPreview = false
341
342      // Obtain the preview container
343      var container = this.$editor.find('div[data-provider="markdown-preview"]')
344
345      // Remove the preview container
346      container.remove()
347
348      // Enable all buttons
349      this.enableButtons('all')
350
351      // Back to the editor
352      this.$textarea.show()
353      this.__setListener()
354
355      return this
356    }
357
358  , isDirty: function() {
359      return this.$oldContent != this.getContent()
360    }
361
362  , getContent: function() {
363      return this.$textarea.val()
364    }
365
366  , setContent: function(content) {
367      this.$textarea.val(content)
368
369      return this
370    }
371
372  , findSelection: function(chunk) {
373    var content = this.getContent(), startChunkPosition
374
375    if (startChunkPosition = content.indexOf(chunk), startChunkPosition >= 0 && chunk.length > 0) {
376      var oldSelection = this.getSelection(), selection
377
378      this.setSelection(startChunkPosition,startChunkPosition+chunk.length)
379      selection = this.getSelection()
380
381      this.setSelection(oldSelection.start,oldSelection.end)
382
383      return selection
384    } else {
385      return null
386    }
387  }
388
389  , getSelection: function() {
390
391      var e = this.$textarea[0]
392
393      return (
394
395          ('selectionStart' in e && function() {
396              var l = e.selectionEnd - e.selectionStart
397              return { start: e.selectionStart, end: e.selectionEnd, length: l, text: e.value.substr(e.selectionStart, l) }
398          }) ||
399
400          /* browser not supported */
401          function() {
402            return null
403          }
404
405      )()
406
407    }
408
409  , setSelection: function(start,end) {
410
411      var e = this.$textarea[0]
412
413      return (
414
415          ('selectionStart' in e && function() {
416              e.selectionStart = start
417              e.selectionEnd = end
418              return
419          }) ||
420
421          /* browser not supported */
422          function() {
423            return null
424          }
425
426      )()
427
428    }
429
430  , replaceSelection: function(text) {
431
432      var e = this.$textarea[0]
433
434      return (
435
436          ('selectionStart' in e && function() {
437              e.value = e.value.substr(0, e.selectionStart) + text + e.value.substr(e.selectionEnd, e.value.length)
438              // Set cursor to the last replacement end
439              e.selectionStart = e.value.length
440              return this
441          }) ||
442
443          /* browser not supported */
444          function() {
445              e.value += text
446              return jQuery(e)
447          }
448
449      )()
450
451    }
452
453  , getNextTab: function() {
454      // Shift the nextTab
455      if (this.$nextTab.length == 0) {
456        return null
457      } else {
458        var nextTab, tab = this.$nextTab.shift()
459
460        if (typeof tab == 'function') {
461          nextTab = tab()
462        } else if (typeof tab == 'object' && tab.length > 0) {
463          nextTab = tab
464        }
465
466        return nextTab
467      }
468    }
469
470  , setNextTab: function(start,end) {
471      // Push new selection into nextTab collections
472      if (typeof start == 'string') {
473        var that = this
474        this.$nextTab.push(function(){
475          return that.findSelection(start)
476        })
477      } else if (typeof start == 'numeric' && typeof end == 'numeric') {
478        var oldSelection = this.getSelection()
479
480        this.setSelection(start,end)
481        this.$nextTab.push(this.getSelection())
482
483        this.setSelection(oldSelection.start,oldSelection.end)
484      }
485
486      return
487    }
488
489  , enableButtons: function(name) {
490      var alter = function (el) {
491        el.removeAttr('disabled')
492      }
493
494      this.__alterButtons(name,alter)
495
496      return this
497    }
498
499  , disableButtons: function(name) {
500      var alter = function (el) {
501        el.attr('disabled','disabled')
502      }
503
504      this.__alterButtons(name,alter)
505
506      return this
507    }
508
509  , eventSupported: function(eventName) {
510      var isSupported = eventName in this.$element
511      if (!isSupported) {
512        this.$element.setAttribute(eventName, 'return;')
513        isSupported = typeof this.$element[eventName] === 'function'
514      }
515      return isSupported
516    }
517
518  , keydown: function (e) {
519      this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
520      this.keyup(e)
521    }
522
523  , keypress: function (e) {
524      if (this.suppressKeyPressRepeat) return
525      this.keyup(e)
526    }
527
528  , keyup: function (e) {
529      var blocked = false
530      switch(e.keyCode) {
531        case 40: // down arrow
532        case 38: // up arrow
533        case 16: // shift
534        case 17: // ctrl
535        case 18: // alt
536          break
537
538        case 9: // tab
539          var nextTab
540          if (nextTab = this.getNextTab(),nextTab != null) {
541            // Get the nextTab if exists
542            var that = this
543            setTimeout(function(){
544              that.setSelection(nextTab.start,nextTab.end)
545            },500)
546
547            blocked = true
548          } else {
549            // The next tab memory contains nothing...
550            // check the cursor position to determine tab action
551            var cursor = this.getSelection()
552
553            if (cursor.start == cursor.end && cursor.end == this.getContent().length) {
554              // The cursor already reach the end of the content
555              blocked = false
556
557            } else {
558              // Put the cursor to the end
559              this.setSelection(this.getContent().length,this.getContent().length)
560             
561              blocked = true
562            }
563          }
564
565          break
566
567        case 13: // enter
568        case 27: // escape
569          blocked = false
570          break
571
572        default:
573          blocked = false
574      }
575
576      if (blocked) {
577        e.stopPropagation()
578        e.preventDefault()
579      }
580  }
581
582  , focus: function (e) {
583      var options = this.$options,
584          isHideable = options.hideable,
585          editor = this.$editor
586
587      editor.addClass('active')
588
589      // Blur other markdown(s)
590      $(document).find('.md-editor').each(function(){
591        if ($(this).attr('id') != editor.attr('id')) {
592          var attachedMarkdown
593
594          if (attachedMarkdown = $(this).find('textarea').data('markdown'),
595              attachedMarkdown == null) {
596              attachedMarkdown = $(this).find('div[data-provider="markdown-preview"]').data('markdown')
597          }
598
599          if (attachedMarkdown) {
600            attachedMarkdown.blur()
601          }
602        }
603      })
604
605      return this
606    }
607
608  , blur: function (e) {
609      var options = this.$options,
610          isHideable = options.hideable,
611          editor = this.$editor,
612          editable = this.$editable
613
614      if (editor.hasClass('active') || this.$element.parent().length == 0) {
615        editor.removeClass('active')
616       
617        if (isHideable) {
618       
619          // Check for editable elements
620          if (editable.el != null) {
621            // Build the original element
622            var oldElement = $('<'+editable.type+'/>'),
623                content = this.getContent(),
624                currentContent = (typeof markdown == 'object') ? markdown.toHTML(content) : content
625
626            $(editable.attrKeys).each(function(k,v) {
627              oldElement.attr(editable.attrKeys[k],editable.attrValues[k])
628            })
629
630            // Get the editor content
631            oldElement.html(currentContent)
632
633            editor.replaceWith(oldElement)
634          } else {
635            editor.hide()
636           
637          }
638        }
639
640        // Trigger the onBlur hook
641        options.onBlur(this)
642      }
643
644      return this
645    }
646
647  }
648
649 /* MARKDOWN PLUGIN DEFINITION
650  * ========================== */
651
652  var old = $.fn.markdown
653
654  $.fn.markdown = function (option) {
655    return this.each(function () {
656      var $this = $(this)
657        , data = $this.data('markdown')
658        , options = typeof option == 'object' && option
659      if (!data) $this.data('markdown', (data = new Markdown(this, options)))
660    })
661  }
662
663  $.fn.markdown.defaults = {
664    /* Editor Properties */
665    autofocus: false,
666    hideable: false,
667    savable:false,
668    width: 'inherit',
669    height: 'inherit',
670
671    /* Buttons Properties */
672    buttons: [
673      [{
674        name: 'groupFont',
675        data: [{
676          name: 'cmdBold',
677          title: 'Bold',
678          icon: 'icon icon-bold',
679          callback: function(e){
680            // Give/remove ** surround the selection
681            var chunk, cursor, selected = e.getSelection(), content = e.getContent()
682
683            if (selected.length == 0) {
684              // Give extra word
685              chunk = 'strong text'
686            } else {
687              chunk = selected.text
688            }
689
690            // transform selection and set the cursor into chunked text
691            if (content.substr(selected.start-2,2) == '**'
692                && content.substr(selected.end,2) == '**' ) {
693              e.setSelection(selected.start-2,selected.end+2)
694              e.replaceSelection(chunk)
695              cursor = selected.start-2
696            } else {
697              e.replaceSelection('**'+chunk+'**')
698              cursor = selected.start+2
699            }
700
701            // Set the cursor
702            e.setSelection(cursor,cursor+chunk.length)
703          }
704        },{
705          name: 'cmdItalic',
706          title: 'Italic',
707          icon: 'icon icon-italic',
708          callback: function(e){
709            // Give/remove * surround the selection
710            var chunk, cursor, selected = e.getSelection(), content = e.getContent()
711
712            if (selected.length == 0) {
713              // Give extra word
714              chunk = 'emphasized text'
715            } else {
716              chunk = selected.text
717            }
718
719            // transform selection and set the cursor into chunked text
720            if (content.substr(selected.start-1,1) == '*'
721                && content.substr(selected.end,1) == '*' ) {
722              e.setSelection(selected.start-1,selected.end+1)
723              e.replaceSelection(chunk)
724              cursor = selected.start-1
725            } else {
726              e.replaceSelection('*'+chunk+'*')
727              cursor = selected.start+1
728            }
729
730            // Set the cursor
731            e.setSelection(cursor,cursor+chunk.length)
732          }
733        },{
734          name: 'cmdHeading',
735          title: 'Heading',
736          icon: 'icon icon-font',
737          callback: function(e){
738            // Append/remove ### surround the selection
739            var chunk, cursor, selected = e.getSelection(), content = e.getContent(), pointer, prevChar
740
741            if (selected.length == 0) {
742              // Give extra word
743              chunk = 'heading text'
744            } else {
745              chunk = selected.text
746            }
747
748            // transform selection and set the cursor into chunked text
749            if ((pointer = 4, content.substr(selected.start-pointer,pointer) == '### ')
750                || (pointer = 3, content.substr(selected.start-pointer,pointer) == '###')) {
751              e.setSelection(selected.start-pointer,selected.end)
752              e.replaceSelection(chunk)
753              cursor = selected.start-pointer
754            } else if (prevChar = content.substr(selected.start-1,1), !!prevChar && prevChar != '\n') {
755              e.replaceSelection('\n\n### '+chunk+'\n')
756              cursor = selected.start+6
757            } else {
758              // Empty string before element
759              e.replaceSelection('### '+chunk+'\n')
760              cursor = selected.start+4
761            }
762
763            // Set the cursor
764            e.setSelection(cursor,cursor+chunk.length)
765          }
766        }]
767      },{
768        name: 'groupLink',
769        data: [{
770          name: 'cmdUrl',
771          title: 'URL/Link',
772          icon: 'icon icon-globe',
773          callback: function(e){
774            // Give [] surround the selection and prepend the link
775            var chunk, cursor, selected = e.getSelection(), content = e.getContent(), link
776
777            if (selected.length == 0) {
778              // Give extra word
779              chunk = 'enter link description here'
780            } else {
781              chunk = selected.text
782            }
783
784            bootbox.prompt("Insert Hyperlink", function(link) {
785                                if (link != null) {
786                                  // transform selection and set the cursor into chunked text
787                                  e.replaceSelection('['+chunk+']('+link+')')
788                                  cursor = selected.start+1
789
790                                  // Set the cursor
791                                  e.setSelection(cursor,cursor+chunk.length)
792                                }
793                        });
794          }
795        },{
796          name: 'cmdImage',
797          title: 'Image',
798          icon: 'icon icon-picture',
799          callback: function(e){
800            // Give ![] surround the selection and prepend the image link
801            var chunk, cursor, selected = e.getSelection(), content = e.getContent(), link
802
803            if (selected.length == 0) {
804              // Give extra word
805              chunk = 'enter image description here'
806            } else {
807              chunk = selected.text
808            }
809
810            bootbox.prompt("Insert Image Hyperlink", function(link) {
811                                if (link != null) {
812                                  // transform selection and set the cursor into chunked text
813                                  e.replaceSelection('!['+chunk+']('+link+' "enter image title here")')
814                                  cursor = selected.start+2
815
816                                  // Set the next tab
817                                  e.setNextTab('enter image title here')
818
819                                  // Set the cursor
820                                  e.setSelection(cursor,cursor+chunk.length)
821                                }
822                        });
823          }
824        }]
825      },{
826        name: 'groupMisc',
827        data: [{
828          name: 'cmdList',
829          title: 'List',
830          icon: 'icon icon-list',
831          callback: function(e){
832            // Prepend/Give - surround the selection
833            var chunk, cursor, selected = e.getSelection(), content = e.getContent()
834
835            // transform selection and set the cursor into chunked text
836            if (selected.length == 0) {
837              // Give extra word
838              chunk = 'list text here'
839               
840              e.replaceSelection('- '+chunk)
841
842              // Set the cursor
843              cursor = selected.start+2
844            } else {
845              if (selected.text.indexOf('\n') < 0) {
846                chunk = selected.text
847
848                e.replaceSelection('- '+chunk)
849
850                // Set the cursor
851                cursor = selected.start+2
852              } else {
853                var list = []
854
855                list = selected.text.split('\n')
856                chunk = list[0]
857
858                $.each(list,function(k,v) {
859                  list[k] = '- '+v
860                })
861
862                e.replaceSelection('\n\n'+list.join('\n'))
863
864                // Set the cursor
865                cursor = selected.start+4
866              }
867            }
868
869           
870
871            // Set the cursor
872            e.setSelection(cursor,cursor+chunk.length)
873          }
874        }]
875      },{
876        name: 'groupUtil',
877        data: [{
878          name: 'cmdPreview',
879          toggle: true,
880          title: 'Preview',
881          btnText: 'Preview',
882          btnClass: 'btn btn-inverse',
883          icon: 'icon icon-white icon-search',
884          callback: function(e){
885            // Check the preview mode and toggle based on this flag
886            var isPreview = e.$isPreview,content
887
888            if (isPreview == false) {
889              // Give flag that tell the editor enter preview mode
890              e.showPreview()
891            } else {
892              e.hidePreview()
893            }
894          }
895        }]
896      }]
897    ],
898    additionalButtons:[], // Place to hook more buttons by code
899
900    /* Events hook */
901    onShow: function (e) {},
902    onPreview: function (e) {},
903    onSave: function (e) {},
904    onBlur: function (e) {}
905  }
906
907  $.fn.markdown.Constructor = Markdown
908
909
910 /* MARKDOWN NO CONFLICT
911  * ==================== */
912
913  $.fn.markdown.noConflict = function () {
914    $.fn.markdown = old
915    return this
916  }
917
918  /* MARKDOWN GLOBAL FUNCTION & DATA-API
919  * ==================================== */
920  var initMarkdown = function(el) {
921    var $this = el
922
923    if ($this.data('markdown')) {
924      $this.data('markdown').showEditor()
925      return
926    }
927    $this.markdown($this.data())
928  }
929
930  var analyzeMarkdown = function(e) {
931    var blurred = false,
932        el,
933        $docEditor = $(e.currentTarget)
934
935    // Check whether it was editor childs or not
936    if ((e.type == 'focusin' || e.type == 'click') && $docEditor.length == 1 && typeof $docEditor[0] == 'object'){
937      el = $docEditor[0].activeElement
938      if ( ! $(el).data('markdown')) {
939        if (typeof $(el).parent().parent().parent().attr('class') == "undefined"
940              || $(el).parent().parent().parent().attr('class').indexOf('md-editor') < 0) {
941          if ( typeof $(el).parent().parent().attr('class') == "undefined"
942              || $(el).parent().parent().attr('class').indexOf('md-editor') < 0) {
943         
944                blurred = true
945          }
946        } else {
947          blurred = false
948        }
949      }
950
951
952      if (blurred) {
953        // Blur event
954        $(document).find('.md-editor').each(function(){
955          var parentMd = $(el).parent()
956
957          if ($(this).attr('id') != parentMd.attr('id')) {
958            var attachedMarkdown
959
960            if (attachedMarkdown = $(this).find('textarea').data('markdown'),
961                attachedMarkdown == null) {
962                attachedMarkdown = $(this).find('div[data-provider="markdown-preview"]').data('markdown')
963            }
964
965            if (attachedMarkdown) {
966              attachedMarkdown.blur()
967            }
968          }
969        })
970      }
971
972      e.stopPropagation()
973    }
974  }
975
976  $(document)
977    .on('click.markdown.data-api', '[data-provide="markdown-editable"]', function (e) {
978      initMarkdown($(this))
979      e.preventDefault()
980    })
981    .on('click', function (e) {
982      analyzeMarkdown(e)
983    })
984    .on('focusin', function (e) {
985      analyzeMarkdown(e)
986    })
987    .ready(function(){
988      $('textarea[data-provide="markdown"]').each(function(){
989        initMarkdown($(this))
990      })
991    })
992
993}(window.jQuery);
Note: See TracBrowser for help on using the repository browser.