bootstrap_markdown_rails 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5ee0ee461d6f2f895ec3193e44491e9c270b8b9c
4
+ data.tar.gz: 87d8526218e4756df505685dbc7a8bab5dc2d560
5
+ SHA512:
6
+ metadata.gz: cd735d79320fca6eca1d559944a3a05fec672eaa7bf25426c5e5eb20ecfd50922053684e5cb3314a643c5121e7a5987cb2c343069f4d4800ce8ccebb7b8e6bbe
7
+ data.tar.gz: 8f473af618fd370c9c05b6d731e6ed6ae7c7a0b9ae4292922b74e5af0ccdcf8fd2fbd85a12cd4ec26cdf6f02f44f725ff24e2432d195593154d05d5796d92514
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bootstrap_markdown_rails.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Phuong 'J' Le H.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # BootstrapMarkdownRails
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'bootstrap_markdown_rails'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install bootstrap_markdown_rails
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/yeuem1vannam/bootstrap_markdown_rails/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,1336 @@
1
+ /* ===================================================
2
+ * bootstrap-markdown.js v2.7.0
3
+ * http://github.com/toopay/bootstrap-markdown
4
+ * ===================================================
5
+ * Copyright 2013-2014 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, this.$element.data(), this.$element.data('options'))
34
+ this.$oldContent = null
35
+ this.$isPreview = false
36
+ this.$isFullscreen = false
37
+ this.$editor = null
38
+ this.$textarea = null
39
+ this.$handler = []
40
+ this.$callback = []
41
+ this.$nextTab = []
42
+
43
+ this.showEditor()
44
+ }
45
+
46
+ Markdown.prototype = {
47
+
48
+ constructor: Markdown
49
+
50
+ , __alterButtons: function(name,alter) {
51
+ var handler = this.$handler, isAll = (name == 'all'),that = this
52
+
53
+ $.each(handler,function(k,v) {
54
+ var halt = true
55
+ if (isAll) {
56
+ halt = false
57
+ } else {
58
+ halt = v.indexOf(name) < 0
59
+ }
60
+
61
+ if (halt == false) {
62
+ alter(that.$editor.find('button[data-handler="'+v+'"]'))
63
+ }
64
+ })
65
+ }
66
+
67
+ , __buildButtons: function(buttonsArray, container) {
68
+ var i,
69
+ ns = this.$ns,
70
+ handler = this.$handler,
71
+ callback = this.$callback
72
+
73
+ for (i=0;i<buttonsArray.length;i++) {
74
+ // Build each group container
75
+ var y, btnGroups = buttonsArray[i]
76
+ for (y=0;y<btnGroups.length;y++) {
77
+ // Build each button group
78
+ var z,
79
+ buttons = btnGroups[y].data,
80
+ btnGroupContainer = $('<div/>', {
81
+ 'class': 'btn-group'
82
+ })
83
+
84
+ for (z=0;z<buttons.length;z++) {
85
+ var button = buttons[z],
86
+ buttonContainer, buttonIconContainer,
87
+ buttonHandler = ns+'-'+button.name,
88
+ buttonIcon = this.__getIcon(button.icon),
89
+ btnText = button.btnText ? button.btnText : '',
90
+ btnClass = button.btnClass ? button.btnClass : 'btn',
91
+ tabIndex = button.tabIndex ? button.tabIndex : '-1',
92
+ hotkey = typeof button.hotkey !== 'undefined' ? button.hotkey : '',
93
+ hotkeyCaption = typeof jQuery.hotkeys !== 'undefined' && hotkey !== '' ? ' ('+hotkey+')' : ''
94
+
95
+ // Construct the button object
96
+ buttonContainer = $('<button></button>');
97
+ buttonContainer.text(' ' + this.__localize(btnText)).addClass('btn-default btn-sm').addClass(btnClass);
98
+ if(btnClass.match(/btn\-(primary|success|info|warning|danger|link)/)){
99
+ buttonContainer.removeClass('btn-default');
100
+ }
101
+ buttonContainer.attr({
102
+ 'type': 'button',
103
+ 'title': this.__localize(button.title) + hotkeyCaption,
104
+ 'tabindex': tabIndex,
105
+ 'data-provider': ns,
106
+ 'data-handler': buttonHandler,
107
+ 'data-hotkey': hotkey
108
+ });
109
+ if (button.toggle == true){
110
+ buttonContainer.attr('data-toggle', 'button');
111
+ }
112
+ buttonIconContainer = $('<span/>');
113
+ buttonIconContainer.addClass(buttonIcon);
114
+ buttonIconContainer.prependTo(buttonContainer);
115
+
116
+ // Attach the button object
117
+ btnGroupContainer.append(buttonContainer);
118
+
119
+ // Register handler and callback
120
+ handler.push(buttonHandler);
121
+ callback.push(button.callback);
122
+ }
123
+
124
+ // Attach the button group into container dom
125
+ container.append(btnGroupContainer);
126
+ }
127
+ }
128
+
129
+ return container;
130
+ }
131
+ , __setListener: function() {
132
+ // Set size and resizable Properties
133
+ var hasRows = typeof this.$textarea.attr('rows') != 'undefined',
134
+ maxRows = this.$textarea.val().split("\n").length > 5 ? this.$textarea.val().split("\n").length : '5',
135
+ rowsVal = hasRows ? this.$textarea.attr('rows') : maxRows
136
+
137
+ this.$textarea.attr('rows',rowsVal)
138
+ if (this.$options.resize) {
139
+ this.$textarea.css('resize',this.$options.resize)
140
+ }
141
+
142
+ this.$textarea
143
+ .on('focus', $.proxy(this.focus, this))
144
+ .on('keypress', $.proxy(this.keypress, this))
145
+ .on('keyup', $.proxy(this.keyup, this))
146
+ .on('change', $.proxy(this.change, this))
147
+
148
+ if (this.eventSupported('keydown')) {
149
+ this.$textarea.on('keydown', $.proxy(this.keydown, this))
150
+ }
151
+
152
+ // Re-attach markdown data
153
+ this.$textarea.data('markdown',this)
154
+ }
155
+
156
+ , __handle: function(e) {
157
+ var target = $(e.currentTarget),
158
+ handler = this.$handler,
159
+ callback = this.$callback,
160
+ handlerName = target.attr('data-handler'),
161
+ callbackIndex = handler.indexOf(handlerName),
162
+ callbackHandler = callback[callbackIndex]
163
+
164
+ // Trigger the focusin
165
+ $(e.currentTarget).focus()
166
+
167
+ callbackHandler(this)
168
+
169
+ // Trigger onChange for each button handle
170
+ this.change(this);
171
+
172
+ // Unless it was the save handler,
173
+ // focusin the textarea
174
+ if (handlerName.indexOf('cmdSave') < 0) {
175
+ this.$textarea.focus()
176
+ }
177
+
178
+ e.preventDefault()
179
+ }
180
+
181
+ , __localize: function(string) {
182
+ var messages = $.fn.markdown.messages,
183
+ language = this.$options.language
184
+ if (
185
+ typeof messages !== 'undefined' &&
186
+ typeof messages[language] !== 'undefined' &&
187
+ typeof messages[language][string] !== 'undefined'
188
+ ) {
189
+ return messages[language][string];
190
+ }
191
+ return string;
192
+ }
193
+
194
+ , __getIcon: function(src) {
195
+ return typeof src == 'object' ? src[this.$options.iconlibrary] : src;
196
+ }
197
+
198
+ , setFullscreen: function(mode) {
199
+ var $editor = this.$editor,
200
+ $textarea = this.$textarea
201
+
202
+ if (mode === true) {
203
+ $editor.addClass('md-fullscreen-mode')
204
+ $('body').addClass('md-nooverflow')
205
+ this.$options.onFullscreen(this)
206
+ } else {
207
+ $editor.removeClass('md-fullscreen-mode')
208
+ $('body').removeClass('md-nooverflow')
209
+ }
210
+
211
+ this.$isFullscreen = mode;
212
+ $textarea.focus()
213
+ }
214
+
215
+ , showEditor: function() {
216
+ var instance = this,
217
+ textarea,
218
+ ns = this.$ns,
219
+ container = this.$element,
220
+ originalHeigth = container.css('height'),
221
+ originalWidth = container.css('width'),
222
+ editable = this.$editable,
223
+ handler = this.$handler,
224
+ callback = this.$callback,
225
+ options = this.$options,
226
+ editor = $( '<div/>', {
227
+ 'class': 'md-editor',
228
+ click: function() {
229
+ instance.focus()
230
+ }
231
+ })
232
+
233
+ // Prepare the editor
234
+ if (this.$editor == null) {
235
+ // Create the panel
236
+ var editorHeader = $('<div/>', {
237
+ 'class': 'md-header btn-toolbar'
238
+ })
239
+
240
+ // Merge the main & additional button groups together
241
+ var allBtnGroups = []
242
+ if (options.buttons.length > 0) allBtnGroups = allBtnGroups.concat(options.buttons[0])
243
+ if (options.additionalButtons.length > 0) allBtnGroups = allBtnGroups.concat(options.additionalButtons[0])
244
+
245
+ // Reduce and/or reorder the button groups
246
+ if (options.reorderButtonGroups.length > 0) {
247
+ allBtnGroups = allBtnGroups
248
+ .filter(function(btnGroup) {
249
+ return options.reorderButtonGroups.indexOf(btnGroup.name) > -1
250
+ })
251
+ .sort(function(a, b) {
252
+ if (options.reorderButtonGroups.indexOf(a.name) < options.reorderButtonGroups.indexOf(b.name)) return -1
253
+ if (options.reorderButtonGroups.indexOf(a.name) > options.reorderButtonGroups.indexOf(b.name)) return 1
254
+ return 0
255
+ })
256
+ }
257
+
258
+ // Build the buttons
259
+ if (allBtnGroups.length > 0) {
260
+ editorHeader = this.__buildButtons([allBtnGroups], editorHeader)
261
+ }
262
+
263
+ if (options.fullscreen.enable) {
264
+ editorHeader.append('<div class="md-controls"><a class="md-control md-control-fullscreen" href="#"><span class="'+this.__getIcon(options.fullscreen.icons.fullscreenOn)+'"></span></a></div>').on('click', '.md-control-fullscreen', function(e) {
265
+ e.preventDefault();
266
+ instance.setFullscreen(true)
267
+ })
268
+ }
269
+
270
+ editor.append(editorHeader)
271
+
272
+ // Wrap the textarea
273
+ if (container.is('textarea')) {
274
+ container.before(editor)
275
+ textarea = container
276
+ textarea.addClass('md-input')
277
+ editor.append(textarea)
278
+ } else {
279
+ var rawContent = (typeof toMarkdown == 'function') ? toMarkdown(container.html()) : container.html(),
280
+ currentContent = $.trim(rawContent)
281
+
282
+ // This is some arbitrary content that could be edited
283
+ textarea = $('<textarea/>', {
284
+ 'class': 'md-input',
285
+ 'val' : currentContent
286
+ })
287
+
288
+ editor.append(textarea)
289
+
290
+ // Save the editable
291
+ editable.el = container
292
+ editable.type = container.prop('tagName').toLowerCase()
293
+ editable.content = container.html()
294
+
295
+ $(container[0].attributes).each(function(){
296
+ editable.attrKeys.push(this.nodeName)
297
+ editable.attrValues.push(this.nodeValue)
298
+ })
299
+
300
+ // Set editor to blocked the original container
301
+ container.replaceWith(editor)
302
+ }
303
+
304
+ var editorFooter = $('<div/>', {
305
+ 'class': 'md-footer'
306
+ }),
307
+ createFooter = false,
308
+ footer = ''
309
+ // Create the footer if savable
310
+ if (options.savable) {
311
+ createFooter = true;
312
+ var saveHandler = 'cmdSave'
313
+
314
+ // Register handler and callback
315
+ handler.push(saveHandler)
316
+ callback.push(options.onSave)
317
+
318
+ editorFooter.append('<button class="btn btn-success" data-provider="'
319
+ +ns
320
+ +'" data-handler="'
321
+ +saveHandler
322
+ +'"><i class="icon icon-white icon-ok"></i> '
323
+ +this.__localize('Save')
324
+ +'</button>')
325
+
326
+
327
+ }
328
+
329
+ footer = typeof options.footer === 'function' ? options.footer(this) : options.footer
330
+
331
+ if ($.trim(footer) !== '') {
332
+ createFooter = true;
333
+ editorFooter.append(footer);
334
+ }
335
+
336
+ if (createFooter) editor.append(editorFooter)
337
+
338
+ // Set width
339
+ if (options.width && options.width !== 'inherit') {
340
+ if (jQuery.isNumeric(options.width)) {
341
+ editor.css('display', 'table')
342
+ textarea.css('width', options.width + 'px')
343
+ } else {
344
+ editor.addClass(options.width)
345
+ }
346
+ }
347
+
348
+ // Set height
349
+ if (options.height && options.height !== 'inherit') {
350
+ if (jQuery.isNumeric(options.height)) {
351
+ var height = options.height
352
+ if (editorHeader) height = Math.max(0, height - editorHeader.outerHeight())
353
+ if (editorFooter) height = Math.max(0, height - editorFooter.outerHeight())
354
+ textarea.css('height', height + 'px')
355
+ } else {
356
+ editor.addClass(options.height)
357
+ }
358
+ }
359
+
360
+ // Reference
361
+ this.$editor = editor
362
+ this.$textarea = textarea
363
+ this.$editable = editable
364
+ this.$oldContent = this.getContent()
365
+
366
+ this.__setListener()
367
+
368
+ // Set editor attributes, data short-hand API and listener
369
+ this.$editor.attr('id',(new Date).getTime())
370
+ this.$editor.on('click', '[data-provider="bootstrap-markdown"]', $.proxy(this.__handle, this))
371
+
372
+ if (this.$element.is(':disabled') || this.$element.is('[readonly]')) {
373
+ this.$editor.addClass('md-editor-disabled');
374
+ this.disableButtons('all');
375
+ }
376
+
377
+ if (this.eventSupported('keydown') && typeof jQuery.hotkeys === 'object') {
378
+ editorHeader.find('[data-provider="bootstrap-markdown"]').each(function() {
379
+ var $button = $(this),
380
+ hotkey = $button.attr('data-hotkey')
381
+ if (hotkey.toLowerCase() !== '') {
382
+ textarea.bind('keydown', hotkey, function() {
383
+ $button.trigger('click')
384
+ return false;
385
+ })
386
+ }
387
+ })
388
+ }
389
+
390
+ if (options.initialstate === 'preview') {
391
+ this.showPreview();
392
+ } else if (options.initialstate === 'fullscreen' && options.fullscreen.enable) {
393
+ this.setFullscreen(true)
394
+ }
395
+
396
+ } else {
397
+ this.$editor.show()
398
+ }
399
+
400
+ if (options.autofocus) {
401
+ this.$textarea.focus()
402
+ this.$editor.addClass('active')
403
+ }
404
+
405
+ if (options.fullscreen.enable && options.fullscreen !== false) {
406
+ this.$editor.append('\
407
+ <div class="md-fullscreen-controls">\
408
+ <a href="#" class="exit-fullscreen" title="Exit fullscreen"><span class="'+this.__getIcon(options.fullscreen.icons.fullscreenOff)+'"></span></a>\
409
+ </div>')
410
+
411
+ this.$editor.on('click', '.exit-fullscreen', function(e) {
412
+ e.preventDefault()
413
+ instance.setFullscreen(false)
414
+ })
415
+ }
416
+
417
+ // hide hidden buttons from options
418
+ this.hideButtons(options.hiddenButtons)
419
+
420
+ // disable disabled buttons from options
421
+ this.disableButtons(options.disabledButtons)
422
+
423
+ // Trigger the onShow hook
424
+ options.onShow(this)
425
+
426
+ return this
427
+ }
428
+
429
+ , parseContent: function() {
430
+ var content,
431
+ callbackContent = this.$options.onPreview(this) // Try to get the content from callback
432
+
433
+ if (typeof callbackContent == 'string') {
434
+ // Set the content based by callback content
435
+ content = callbackContent
436
+ } else {
437
+ // Set the content
438
+ var val = this.$textarea.val();
439
+ if(typeof markdown == 'object') {
440
+ content = markdown.toHTML(val);
441
+ }else if(typeof marked == 'function') {
442
+ content = marked(val);
443
+ } else {
444
+ content = val;
445
+ }
446
+ }
447
+
448
+ return content;
449
+ }
450
+
451
+ , showPreview: function() {
452
+ var options = this.$options,
453
+ container = this.$textarea,
454
+ afterContainer = container.next(),
455
+ replacementContainer = $('<div/>',{'class':'md-preview','data-provider':'markdown-preview'}),
456
+ content
457
+
458
+ // Give flag that tell the editor enter preview mode
459
+ this.$isPreview = true
460
+ // Disable all buttons
461
+ this.disableButtons('all').enableButtons('cmdPreview')
462
+
463
+ content = this.parseContent()
464
+
465
+ // Build preview element
466
+ replacementContainer.html(content)
467
+
468
+ if (afterContainer && afterContainer.attr('class') == 'md-footer') {
469
+ // If there is footer element, insert the preview container before it
470
+ replacementContainer.insertBefore(afterContainer)
471
+ } else {
472
+ // Otherwise, just append it after textarea
473
+ container.parent().append(replacementContainer)
474
+ }
475
+
476
+ // Set the preview element dimensions
477
+ replacementContainer.css({
478
+ width: container.outerWidth() + 'px',
479
+ height: container.outerHeight() + 'px'
480
+ })
481
+
482
+ if (this.$options.resize) {
483
+ replacementContainer.css('resize',this.$options.resize)
484
+ }
485
+
486
+ // Hide the last-active textarea
487
+ container.hide()
488
+
489
+ // Attach the editor instances
490
+ replacementContainer.data('markdown',this)
491
+
492
+ if (this.$element.is(':disabled') || this.$element.is('[readonly]')) {
493
+ this.$editor.addClass('md-editor-disabled');
494
+ this.disableButtons('all');
495
+ }
496
+
497
+ return this
498
+ }
499
+
500
+ , hidePreview: function() {
501
+ // Give flag that tell the editor quit preview mode
502
+ this.$isPreview = false
503
+
504
+ // Obtain the preview container
505
+ var container = this.$editor.find('div[data-provider="markdown-preview"]')
506
+
507
+ // Remove the preview container
508
+ container.remove()
509
+
510
+ // Enable all buttons
511
+ this.enableButtons('all')
512
+ // Disable configured disabled buttons
513
+ this.disableButtons(this.$options.disabledButtons)
514
+
515
+ // Back to the editor
516
+ this.$textarea.show()
517
+ this.__setListener()
518
+
519
+ return this
520
+ }
521
+
522
+ , isDirty: function() {
523
+ return this.$oldContent != this.getContent()
524
+ }
525
+
526
+ , getContent: function() {
527
+ return this.$textarea.val()
528
+ }
529
+
530
+ , setContent: function(content) {
531
+ this.$textarea.val(content)
532
+
533
+ return this
534
+ }
535
+
536
+ , findSelection: function(chunk) {
537
+ var content = this.getContent(), startChunkPosition
538
+
539
+ if (startChunkPosition = content.indexOf(chunk), startChunkPosition >= 0 && chunk.length > 0) {
540
+ var oldSelection = this.getSelection(), selection
541
+
542
+ this.setSelection(startChunkPosition,startChunkPosition+chunk.length)
543
+ selection = this.getSelection()
544
+
545
+ this.setSelection(oldSelection.start,oldSelection.end)
546
+
547
+ return selection
548
+ } else {
549
+ return null
550
+ }
551
+ }
552
+
553
+ , getSelection: function() {
554
+
555
+ var e = this.$textarea[0]
556
+
557
+ return (
558
+
559
+ ('selectionStart' in e && function() {
560
+ var l = e.selectionEnd - e.selectionStart
561
+ return { start: e.selectionStart, end: e.selectionEnd, length: l, text: e.value.substr(e.selectionStart, l) }
562
+ }) ||
563
+
564
+ /* browser not supported */
565
+ function() {
566
+ return null
567
+ }
568
+
569
+ )()
570
+
571
+ }
572
+
573
+ , setSelection: function(start,end) {
574
+
575
+ var e = this.$textarea[0]
576
+
577
+ return (
578
+
579
+ ('selectionStart' in e && function() {
580
+ e.selectionStart = start
581
+ e.selectionEnd = end
582
+ return
583
+ }) ||
584
+
585
+ /* browser not supported */
586
+ function() {
587
+ return null
588
+ }
589
+
590
+ )()
591
+
592
+ }
593
+
594
+ , replaceSelection: function(text) {
595
+
596
+ var e = this.$textarea[0]
597
+
598
+ return (
599
+
600
+ ('selectionStart' in e && function() {
601
+ e.value = e.value.substr(0, e.selectionStart) + text + e.value.substr(e.selectionEnd, e.value.length)
602
+ // Set cursor to the last replacement end
603
+ e.selectionStart = e.value.length
604
+ return this
605
+ }) ||
606
+
607
+ /* browser not supported */
608
+ function() {
609
+ e.value += text
610
+ return jQuery(e)
611
+ }
612
+
613
+ )()
614
+
615
+ }
616
+
617
+ , getNextTab: function() {
618
+ // Shift the nextTab
619
+ if (this.$nextTab.length == 0) {
620
+ return null
621
+ } else {
622
+ var nextTab, tab = this.$nextTab.shift()
623
+
624
+ if (typeof tab == 'function') {
625
+ nextTab = tab()
626
+ } else if (typeof tab == 'object' && tab.length > 0) {
627
+ nextTab = tab
628
+ }
629
+
630
+ return nextTab
631
+ }
632
+ }
633
+
634
+ , setNextTab: function(start,end) {
635
+ // Push new selection into nextTab collections
636
+ if (typeof start == 'string') {
637
+ var that = this
638
+ this.$nextTab.push(function(){
639
+ return that.findSelection(start)
640
+ })
641
+ } else if (typeof start == 'number' && typeof end == 'number') {
642
+ var oldSelection = this.getSelection()
643
+
644
+ this.setSelection(start,end)
645
+ this.$nextTab.push(this.getSelection())
646
+
647
+ this.setSelection(oldSelection.start,oldSelection.end)
648
+ }
649
+
650
+ return
651
+ }
652
+
653
+ , __parseButtonNameParam: function(nameParam) {
654
+ var buttons = []
655
+
656
+ if (typeof nameParam == 'string') {
657
+ buttons.push(nameParam)
658
+ } else {
659
+ buttons = nameParam
660
+ }
661
+
662
+ return buttons
663
+ }
664
+
665
+ , enableButtons: function(name) {
666
+ var buttons = this.__parseButtonNameParam(name),
667
+ that = this
668
+
669
+ $.each(buttons, function(i, v) {
670
+ that.__alterButtons(buttons[i], function (el) {
671
+ el.removeAttr('disabled')
672
+ });
673
+ })
674
+
675
+ return this;
676
+ }
677
+
678
+ , disableButtons: function(name) {
679
+ var buttons = this.__parseButtonNameParam(name),
680
+ that = this
681
+
682
+ $.each(buttons, function(i, v) {
683
+ that.__alterButtons(buttons[i], function (el) {
684
+ el.attr('disabled','disabled')
685
+ });
686
+ })
687
+
688
+ return this;
689
+ }
690
+
691
+ , hideButtons: function(name) {
692
+ var buttons = this.__parseButtonNameParam(name),
693
+ that = this
694
+
695
+ $.each(buttons, function(i, v) {
696
+ that.__alterButtons(buttons[i], function (el) {
697
+ el.addClass('hidden');
698
+ });
699
+ })
700
+
701
+ return this;
702
+
703
+ }
704
+
705
+ , showButtons: function(name) {
706
+ var buttons = this.__parseButtonNameParam(name),
707
+ that = this
708
+
709
+ $.each(buttons, function(i, v) {
710
+ that.__alterButtons(buttons[i], function (el) {
711
+ el.removeClass('hidden');
712
+ });
713
+ })
714
+
715
+ return this;
716
+
717
+ }
718
+
719
+ , eventSupported: function(eventName) {
720
+ var isSupported = eventName in this.$element
721
+ if (!isSupported) {
722
+ this.$element.setAttribute(eventName, 'return;')
723
+ isSupported = typeof this.$element[eventName] === 'function'
724
+ }
725
+ return isSupported
726
+ }
727
+
728
+ , keyup: function (e) {
729
+ var blocked = false
730
+ switch(e.keyCode) {
731
+ case 40: // down arrow
732
+ case 38: // up arrow
733
+ case 16: // shift
734
+ case 17: // ctrl
735
+ case 18: // alt
736
+ break
737
+
738
+ case 9: // tab
739
+ var nextTab
740
+ if (nextTab = this.getNextTab(),nextTab != null) {
741
+ // Get the nextTab if exists
742
+ var that = this
743
+ setTimeout(function(){
744
+ that.setSelection(nextTab.start,nextTab.end)
745
+ },500)
746
+
747
+ blocked = true
748
+ } else {
749
+ // The next tab memory contains nothing...
750
+ // check the cursor position to determine tab action
751
+ var cursor = this.getSelection()
752
+
753
+ if (cursor.start == cursor.end && cursor.end == this.getContent().length) {
754
+ // The cursor already reach the end of the content
755
+ blocked = false
756
+
757
+ } else {
758
+ // Put the cursor to the end
759
+ this.setSelection(this.getContent().length,this.getContent().length)
760
+
761
+ blocked = true
762
+ }
763
+ }
764
+
765
+ break
766
+
767
+ case 13: // enter
768
+ blocked = false
769
+ break
770
+ case 27: // escape
771
+ if (this.$isFullscreen) this.setFullscreen(false)
772
+ blocked = false
773
+ break
774
+
775
+ default:
776
+ blocked = false
777
+ }
778
+
779
+ if (blocked) {
780
+ e.stopPropagation()
781
+ e.preventDefault()
782
+ }
783
+
784
+ this.$options.onChange(this)
785
+ }
786
+
787
+ , change: function(e) {
788
+ this.$options.onChange(this);
789
+ return this;
790
+ }
791
+
792
+ , focus: function (e) {
793
+ var options = this.$options,
794
+ isHideable = options.hideable,
795
+ editor = this.$editor
796
+
797
+ editor.addClass('active')
798
+
799
+ // Blur other markdown(s)
800
+ $(document).find('.md-editor').each(function(){
801
+ if ($(this).attr('id') != editor.attr('id')) {
802
+ var attachedMarkdown
803
+
804
+ if (attachedMarkdown = $(this).find('textarea').data('markdown'),
805
+ attachedMarkdown == null) {
806
+ attachedMarkdown = $(this).find('div[data-provider="markdown-preview"]').data('markdown')
807
+ }
808
+
809
+ if (attachedMarkdown) {
810
+ attachedMarkdown.blur()
811
+ }
812
+ }
813
+ })
814
+
815
+ // Trigger the onFocus hook
816
+ options.onFocus(this);
817
+
818
+ return this
819
+ }
820
+
821
+ , blur: function (e) {
822
+ var options = this.$options,
823
+ isHideable = options.hideable,
824
+ editor = this.$editor,
825
+ editable = this.$editable
826
+
827
+ if (editor.hasClass('active') || this.$element.parent().length == 0) {
828
+ editor.removeClass('active')
829
+
830
+ if (isHideable) {
831
+
832
+ // Check for editable elements
833
+ if (editable.el != null) {
834
+ // Build the original element
835
+ var oldElement = $('<'+editable.type+'/>'),
836
+ content = this.getContent(),
837
+ currentContent = (typeof markdown == 'object') ? markdown.toHTML(content) : content
838
+
839
+ $(editable.attrKeys).each(function(k,v) {
840
+ oldElement.attr(editable.attrKeys[k],editable.attrValues[k])
841
+ })
842
+
843
+ // Get the editor content
844
+ oldElement.html(currentContent)
845
+
846
+ editor.replaceWith(oldElement)
847
+ } else {
848
+ editor.hide()
849
+
850
+ }
851
+ }
852
+
853
+ // Trigger the onBlur hook
854
+ options.onBlur(this)
855
+ }
856
+
857
+ return this
858
+ }
859
+
860
+ }
861
+
862
+ /* MARKDOWN PLUGIN DEFINITION
863
+ * ========================== */
864
+
865
+ var old = $.fn.markdown
866
+
867
+ $.fn.markdown = function (option) {
868
+ return this.each(function () {
869
+ var $this = $(this)
870
+ , data = $this.data('markdown')
871
+ , options = typeof option == 'object' && option
872
+ if (!data) $this.data('markdown', (data = new Markdown(this, options)))
873
+ })
874
+ }
875
+
876
+ $.fn.markdown.messages = {}
877
+
878
+ $.fn.markdown.defaults = {
879
+ /* Editor Properties */
880
+ autofocus: false,
881
+ hideable: false,
882
+ savable:false,
883
+ width: 'inherit',
884
+ height: 'inherit',
885
+ resize: 'none',
886
+ iconlibrary: 'glyph',
887
+ language: 'en',
888
+ initialstate: 'editor',
889
+
890
+ /* Buttons Properties */
891
+ buttons: [
892
+ [{
893
+ name: 'groupFont',
894
+ data: [{
895
+ name: 'cmdBold',
896
+ hotkey: 'Ctrl+B',
897
+ title: 'Bold',
898
+ icon: { glyph: 'glyphicon glyphicon-bold', fa: 'fa fa-bold', 'fa-3': 'icon-bold' },
899
+ callback: function(e){
900
+ // Give/remove ** surround the selection
901
+ var chunk, cursor, selected = e.getSelection(), content = e.getContent()
902
+
903
+ if (selected.length == 0) {
904
+ // Give extra word
905
+ chunk = e.__localize('strong text')
906
+ } else {
907
+ chunk = selected.text
908
+ }
909
+
910
+ // transform selection and set the cursor into chunked text
911
+ if (content.substr(selected.start-2,2) == '**'
912
+ && content.substr(selected.end,2) == '**' ) {
913
+ e.setSelection(selected.start-2,selected.end+2)
914
+ e.replaceSelection(chunk)
915
+ cursor = selected.start-2
916
+ } else {
917
+ e.replaceSelection('**'+chunk+'**')
918
+ cursor = selected.start+2
919
+ }
920
+
921
+ // Set the cursor
922
+ e.setSelection(cursor,cursor+chunk.length)
923
+ }
924
+ },{
925
+ name: 'cmdItalic',
926
+ title: 'Italic',
927
+ hotkey: 'Ctrl+I',
928
+ icon: { glyph: 'glyphicon glyphicon-italic', fa: 'fa fa-italic', 'fa-3': 'icon-italic' },
929
+ callback: function(e){
930
+ // Give/remove * surround the selection
931
+ var chunk, cursor, selected = e.getSelection(), content = e.getContent()
932
+
933
+ if (selected.length == 0) {
934
+ // Give extra word
935
+ chunk = e.__localize('emphasized text')
936
+ } else {
937
+ chunk = selected.text
938
+ }
939
+
940
+ // transform selection and set the cursor into chunked text
941
+ if (content.substr(selected.start-1,1) == '_'
942
+ && content.substr(selected.end,1) == '_' ) {
943
+ e.setSelection(selected.start-1,selected.end+1)
944
+ e.replaceSelection(chunk)
945
+ cursor = selected.start-1
946
+ } else {
947
+ e.replaceSelection('_'+chunk+'_')
948
+ cursor = selected.start+1
949
+ }
950
+
951
+ // Set the cursor
952
+ e.setSelection(cursor,cursor+chunk.length)
953
+ }
954
+ },{
955
+ name: 'cmdHeading',
956
+ title: 'Heading',
957
+ hotkey: 'Ctrl+H',
958
+ icon: { glyph: 'glyphicon glyphicon-header', fa: 'fa fa-font', 'fa-3': 'icon-font' },
959
+ callback: function(e){
960
+ // Append/remove ### surround the selection
961
+ var chunk, cursor, selected = e.getSelection(), content = e.getContent(), pointer, prevChar
962
+
963
+ if (selected.length == 0) {
964
+ // Give extra word
965
+ chunk = e.__localize('heading text')
966
+ } else {
967
+ chunk = selected.text + '\n';
968
+ }
969
+
970
+ // transform selection and set the cursor into chunked text
971
+ if ((pointer = 4, content.substr(selected.start-pointer,pointer) == '### ')
972
+ || (pointer = 3, content.substr(selected.start-pointer,pointer) == '###')) {
973
+ e.setSelection(selected.start-pointer,selected.end)
974
+ e.replaceSelection(chunk)
975
+ cursor = selected.start-pointer
976
+ } else if (selected.start > 0 && (prevChar = content.substr(selected.start-1,1), !!prevChar && prevChar != '\n')) {
977
+ e.replaceSelection('\n\n### '+chunk)
978
+ cursor = selected.start+6
979
+ } else {
980
+ // Empty string before element
981
+ e.replaceSelection('### '+chunk)
982
+ cursor = selected.start+4
983
+ }
984
+
985
+ // Set the cursor
986
+ e.setSelection(cursor,cursor+chunk.length)
987
+ }
988
+ }]
989
+ },{
990
+ name: 'groupLink',
991
+ data: [{
992
+ name: 'cmdUrl',
993
+ title: 'URL/Link',
994
+ hotkey: 'Ctrl+L',
995
+ icon: { glyph: 'glyphicon glyphicon-link', fa: 'fa fa-link', 'fa-3': 'icon-link' },
996
+ callback: function(e){
997
+ // Give [] surround the selection and prepend the link
998
+ var chunk, cursor, selected = e.getSelection(), content = e.getContent(), link
999
+
1000
+ if (selected.length == 0) {
1001
+ // Give extra word
1002
+ chunk = e.__localize('enter link description here')
1003
+ } else {
1004
+ chunk = selected.text
1005
+ }
1006
+
1007
+ link = prompt(e.__localize('Insert Hyperlink'),'http://')
1008
+
1009
+ if (link != null && link != '' && link != 'http://' && link.substr(0,4) == 'http') {
1010
+ var sanitizedLink = $('<div>'+link+'</div>').text()
1011
+
1012
+ // transform selection and set the cursor into chunked text
1013
+ e.replaceSelection('['+chunk+']('+sanitizedLink+')')
1014
+ cursor = selected.start+1
1015
+
1016
+ // Set the cursor
1017
+ e.setSelection(cursor,cursor+chunk.length)
1018
+ }
1019
+ }
1020
+ },{
1021
+ name: 'cmdImage',
1022
+ title: 'Image',
1023
+ hotkey: 'Ctrl+G',
1024
+ icon: { glyph: 'glyphicon glyphicon-picture', fa: 'fa fa-picture-o', 'fa-3': 'icon-picture' },
1025
+ callback: function(e){
1026
+ // Give ![] surround the selection and prepend the image link
1027
+ var chunk, cursor, selected = e.getSelection(), content = e.getContent(), link
1028
+
1029
+ if (selected.length == 0) {
1030
+ // Give extra word
1031
+ chunk = e.__localize('enter image description here')
1032
+ } else {
1033
+ chunk = selected.text
1034
+ }
1035
+
1036
+ link = prompt(e.__localize('Insert Image Hyperlink'),'http://')
1037
+
1038
+ if (link != null && link != '' && link != 'http://' && link.substr(0,4) == 'http') {
1039
+ var sanitizedLink = $('<div>'+link+'</div>').text()
1040
+
1041
+ // transform selection and set the cursor into chunked text
1042
+ e.replaceSelection('!['+chunk+']('+sanitizedLink+' "'+e.__localize('enter image title here')+'")')
1043
+ cursor = selected.start+2
1044
+
1045
+ // Set the next tab
1046
+ e.setNextTab(e.__localize('enter image title here'))
1047
+
1048
+ // Set the cursor
1049
+ e.setSelection(cursor,cursor+chunk.length)
1050
+ }
1051
+ }
1052
+ }]
1053
+ },{
1054
+ name: 'groupMisc',
1055
+ data: [{
1056
+ name: 'cmdList',
1057
+ hotkey: 'Ctrl+U',
1058
+ title: 'Unordered List',
1059
+ icon: { glyph: 'glyphicon glyphicon-list', fa: 'fa fa-list', 'fa-3': 'icon-list-ul' },
1060
+ callback: function(e){
1061
+ // Prepend/Give - surround the selection
1062
+ var chunk, cursor, selected = e.getSelection(), content = e.getContent()
1063
+
1064
+ // transform selection and set the cursor into chunked text
1065
+ if (selected.length == 0) {
1066
+ // Give extra word
1067
+ chunk = e.__localize('list text here')
1068
+
1069
+ e.replaceSelection('- '+chunk)
1070
+ // Set the cursor
1071
+ cursor = selected.start+2
1072
+
1073
+ } else {
1074
+ if (selected.text.indexOf('\n') < 0) {
1075
+ chunk = selected.text
1076
+
1077
+ e.replaceSelection('- '+chunk)
1078
+
1079
+ // Set the cursor
1080
+ cursor = selected.start+2
1081
+ } else {
1082
+ var list = []
1083
+
1084
+ list = selected.text.split('\n')
1085
+ chunk = list[0]
1086
+
1087
+ $.each(list,function(k,v) {
1088
+ list[k] = '- '+v
1089
+ })
1090
+
1091
+ e.replaceSelection('\n\n'+list.join('\n'))
1092
+
1093
+ // Set the cursor
1094
+ cursor = selected.start+4
1095
+ }
1096
+ }
1097
+
1098
+ // Set the cursor
1099
+ e.setSelection(cursor,cursor+chunk.length)
1100
+ }
1101
+ },
1102
+ {
1103
+ name: 'cmdListO',
1104
+ hotkey: 'Ctrl+O',
1105
+ title: 'Ordered List',
1106
+ icon: { glyph: 'glyphicon glyphicon-th-list', fa: 'fa fa-list-ol', 'fa-3': 'icon-list-ol' },
1107
+ callback: function(e) {
1108
+
1109
+ // Prepend/Give - surround the selection
1110
+ var chunk, cursor, selected = e.getSelection(), content = e.getContent()
1111
+
1112
+ // transform selection and set the cursor into chunked text
1113
+ if (selected.length == 0) {
1114
+ // Give extra word
1115
+ chunk = e.__localize('list text here')
1116
+ e.replaceSelection('1. '+chunk)
1117
+ // Set the cursor
1118
+ cursor = selected.start+3
1119
+
1120
+ } else {
1121
+ if (selected.text.indexOf('\n') < 0) {
1122
+ chunk = selected.text
1123
+
1124
+ e.replaceSelection('1. '+chunk)
1125
+
1126
+ // Set the cursor
1127
+ cursor = selected.start+3
1128
+ } else {
1129
+ var list = []
1130
+
1131
+ list = selected.text.split('\n')
1132
+ chunk = list[0]
1133
+
1134
+ $.each(list,function(k,v) {
1135
+ list[k] = '1. '+v
1136
+ })
1137
+
1138
+ e.replaceSelection('\n\n'+list.join('\n'))
1139
+
1140
+ // Set the cursor
1141
+ cursor = selected.start+5
1142
+ }
1143
+ }
1144
+
1145
+ // Set the cursor
1146
+ e.setSelection(cursor,cursor+chunk.length)
1147
+ }
1148
+ },
1149
+ {
1150
+ name: 'cmdCode',
1151
+ hotkey: 'Ctrl+K',
1152
+ title: 'Code',
1153
+ icon: { glyph: 'glyphicon glyphicon-asterisk', fa: 'fa fa-code', 'fa-3': 'icon-code' },
1154
+ callback: function(e) {
1155
+
1156
+ // Give/remove ** surround the selection
1157
+ var chunk, cursor, selected = e.getSelection(), content = e.getContent()
1158
+
1159
+ if (selected.length == 0) {
1160
+ // Give extra word
1161
+ chunk = e.__localize('code text here')
1162
+ } else {
1163
+ chunk = selected.text
1164
+ }
1165
+
1166
+ // transform selection and set the cursor into chunked text
1167
+ if (content.substr(selected.start-1,1) == '`'
1168
+ && content.substr(selected.end,1) == '`' ) {
1169
+ e.setSelection(selected.start-1,selected.end+1)
1170
+ e.replaceSelection(chunk)
1171
+ cursor = selected.start-1
1172
+ } else {
1173
+ e.replaceSelection('`'+chunk+'`')
1174
+ cursor = selected.start+1
1175
+ }
1176
+
1177
+ // Set the cursor
1178
+ e.setSelection(cursor,cursor+chunk.length)
1179
+ }
1180
+ },
1181
+ {
1182
+ name: 'cmdQuote',
1183
+ hotkey: 'Ctrl+Q',
1184
+ title: 'Quote',
1185
+ icon: { glyph: 'glyphicon glyphicon-comment', fa: 'fa fa-quote-left', 'fa-3': 'icon-quote-left' },
1186
+ callback: function(e) {
1187
+ // Prepend/Give - surround the selection
1188
+ var chunk, cursor, selected = e.getSelection(), content = e.getContent()
1189
+
1190
+ // transform selection and set the cursor into chunked text
1191
+ if (selected.length == 0) {
1192
+ // Give extra word
1193
+ chunk = e.__localize('quote here')
1194
+ e.replaceSelection('> '+chunk)
1195
+ // Set the cursor
1196
+ cursor = selected.start+2
1197
+
1198
+ } else {
1199
+ if (selected.text.indexOf('\n') < 0) {
1200
+ chunk = selected.text
1201
+
1202
+ e.replaceSelection('> '+chunk)
1203
+
1204
+ // Set the cursor
1205
+ cursor = selected.start+2
1206
+ } else {
1207
+ var list = []
1208
+
1209
+ list = selected.text.split('\n')
1210
+ chunk = list[0]
1211
+
1212
+ $.each(list,function(k,v) {
1213
+ list[k] = '> '+v
1214
+ })
1215
+
1216
+ e.replaceSelection('\n\n'+list.join('\n'))
1217
+
1218
+ // Set the cursor
1219
+ cursor = selected.start+4
1220
+ }
1221
+ }
1222
+
1223
+ // Set the cursor
1224
+ e.setSelection(cursor,cursor+chunk.length)
1225
+ }
1226
+ }]
1227
+ },{
1228
+ name: 'groupUtil',
1229
+ data: [{
1230
+ name: 'cmdPreview',
1231
+ toggle: true,
1232
+ hotkey: 'Ctrl+P',
1233
+ title: 'Preview',
1234
+ btnText: 'Preview',
1235
+ btnClass: 'btn btn-primary btn-sm',
1236
+ icon: { glyph: 'glyphicon glyphicon-search', fa: 'fa fa-search', 'fa-3': 'icon-search' },
1237
+ callback: function(e){
1238
+ // Check the preview mode and toggle based on this flag
1239
+ var isPreview = e.$isPreview,content
1240
+
1241
+ if (isPreview == false) {
1242
+ // Give flag that tell the editor enter preview mode
1243
+ e.showPreview()
1244
+ } else {
1245
+ e.hidePreview()
1246
+ }
1247
+ }
1248
+ }]
1249
+ }]
1250
+ ],
1251
+ additionalButtons:[], // Place to hook more buttons by code
1252
+ reorderButtonGroups:[],
1253
+ hiddenButtons:[], // Default hidden buttons
1254
+ disabledButtons:[], // Default disabled buttons
1255
+ footer: '',
1256
+ fullscreen: {
1257
+ enable: true,
1258
+ icons: {
1259
+ fullscreenOn: {
1260
+ fa: 'fa fa-expand',
1261
+ glyph: 'glyphicon glyphicon-fullscreen',
1262
+ 'fa-3': 'icon-resize-full'
1263
+ },
1264
+ fullscreenOff: {
1265
+ fa: 'fa fa-compress',
1266
+ glyph: 'glyphicon glyphicon-fullscreen',
1267
+ 'fa-3': 'icon-resize-small'
1268
+ }
1269
+ }
1270
+ },
1271
+
1272
+ /* Events hook */
1273
+ onShow: function (e) {},
1274
+ onPreview: function (e) {},
1275
+ onSave: function (e) {},
1276
+ onBlur: function (e) {},
1277
+ onFocus: function (e) {},
1278
+ onChange: function(e) {},
1279
+ onFullscreen: function(e) {}
1280
+ }
1281
+
1282
+ $.fn.markdown.Constructor = Markdown
1283
+
1284
+
1285
+ /* MARKDOWN NO CONFLICT
1286
+ * ==================== */
1287
+
1288
+ $.fn.markdown.noConflict = function () {
1289
+ $.fn.markdown = old
1290
+ return this
1291
+ }
1292
+
1293
+ /* MARKDOWN GLOBAL FUNCTION & DATA-API
1294
+ * ==================================== */
1295
+ var initMarkdown = function(el) {
1296
+ var $this = el
1297
+
1298
+ if ($this.data('markdown')) {
1299
+ $this.data('markdown').showEditor()
1300
+ return
1301
+ }
1302
+
1303
+ $this.markdown()
1304
+ }
1305
+
1306
+ var blurNonFocused = function(e) {
1307
+ var $activeElement = $(document.activeElement)
1308
+
1309
+ // Blur event
1310
+ $(document).find('.md-editor').each(function(){
1311
+ var $this = $(this),
1312
+ focused = $activeElement.closest('.md-editor')[0] === this,
1313
+ attachedMarkdown = $this.find('textarea').data('markdown') ||
1314
+ $this.find('div[data-provider="markdown-preview"]').data('markdown')
1315
+
1316
+ if (attachedMarkdown && !focused) {
1317
+ attachedMarkdown.blur()
1318
+ }
1319
+ })
1320
+ }
1321
+
1322
+ $(document)
1323
+ .on('click.markdown.data-api', '[data-provide="markdown-editable"]', function (e) {
1324
+ initMarkdown($(this))
1325
+ e.preventDefault()
1326
+ })
1327
+ .on('click focusin', function (e) {
1328
+ blurNonFocused(e)
1329
+ })
1330
+ .ready(function(){
1331
+ $('textarea[data-provide="markdown"]').each(function(){
1332
+ initMarkdown($(this))
1333
+ })
1334
+ })
1335
+
1336
+ }(window.jQuery);