rails-bootstrap-markdown 1.0.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b27bac04a913814ecb58b4abbb151d3e8aac8acb
4
- data.tar.gz: 1f18da19cced94651de9f54d38a46e40ae18ae6d
3
+ metadata.gz: 3d90f4705ea4630c7e176d8d07efcb18e4faf4ce
4
+ data.tar.gz: 84096cdc58399fc311942bc9a0483bfe59fe4d83
5
5
  SHA512:
6
- metadata.gz: 6ef374aaeba24702f1797416d7f1dc329842d934921edf829527f152f4b4236e2566a43ad8737646f8b4f780dfb95844e8b7c2588acf97bf446fb63da91de205
7
- data.tar.gz: 265fc5fb77cf588995e50a95980822f1f6e798cce7c7a7a50726f9a0cb4d210bdde2baf7d76926f7d2e1c4bf973ed68b367b94804032d636a91b1b07a0ac52d1
6
+ metadata.gz: 86a34516bbadc8ec866f3663b24f7796d07c37d6738487dd5447c9c2f43b859f1f15480c1cc542cac3733ca47896d227f619fb5b21bac05b3143811b2043748d
7
+ data.tar.gz: dd2221974b223e8a2a9b74c07a18564bf18900dd6ecf189626a6b47bde0354e960dc98b75fe160e0ddb966493b6deae84163c4c4e561bb011c2499021b751f6f
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .idea
data/README.md CHANGED
@@ -7,6 +7,7 @@ A rails gem for [Bootstrap Markdown](http://toopay.github.io/bootstrap-markdown/
7
7
  - http://toopay.github.io/bootstrap-markdown/
8
8
  - https://github.com/domchristie/to-markdown
9
9
  - https://github.com/evilstreak/markdown-js
10
+ - https://github.com/mathiasbynens/he
10
11
 
11
12
  ## Installation
12
13
 
@@ -32,8 +33,10 @@ Or install it yourself as:
32
33
 
33
34
  ### Javascript
34
35
 
36
+ To require all 3rd party libraries at once, use:
37
+
35
38
  ```
36
- //= require bootstrap-markdown
39
+ //= require bootstrap-markdown-bundle
37
40
  ```
38
41
 
39
42
  ## Contributing
@@ -0,0 +1,5 @@
1
+ //= require he
2
+ //= require markdown
3
+ //= require to-markdown
4
+
5
+ //= require bootstrap-markdown
@@ -1,8 +1,8 @@
1
1
  /* ===================================================
2
- * bootstrap-markdown.js v2.1.0
2
+ * bootstrap-markdown.js v2.6.0
3
3
  * http://github.com/toopay/bootstrap-markdown
4
4
  * ===================================================
5
- * Copyright 2013 Taufan Aditya
5
+ * Copyright 2013-2014 Taufan Aditya
6
6
  *
7
7
  * Licensed under the Apache License, Version 2.0 (the "License");
8
8
  * you may not use this file except in compliance with the License.
@@ -17,9 +17,6 @@
17
17
  * limitations under the License.
18
18
  * ========================================================== */
19
19
 
20
- //= require markdown
21
- //= require to-markdown
22
-
23
20
  !function ($) {
24
21
 
25
22
  "use strict"; // jshint ;_;
@@ -30,17 +27,18 @@
30
27
 
31
28
  var Markdown = function (element, options) {
32
29
  // Class Properties
33
- this.$ns = 'bootstrap-markdown'
34
- this.$element = $(element)
35
- this.$editable = {el:null, type:null,attrKeys:[], attrValues:[], content:null}
36
- this.$options = $.extend(true, {}, $.fn.markdown.defaults, options)
37
- this.$oldContent = null
38
- this.$isPreview = false
39
- this.$editor = null
40
- this.$textarea = null
41
- this.$handler = []
42
- this.$callback = []
43
- this.$nextTab = []
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 = []
44
42
 
45
43
  this.showEditor()
46
44
  }
@@ -87,31 +85,37 @@
87
85
  var button = buttons[z],
88
86
  buttonToggle = '',
89
87
  buttonHandler = ns+'-'+button.name,
88
+ buttonIcon = this.__getIcon(button.icon),
90
89
  btnText = button.btnText ? button.btnText : '',
91
90
  btnClass = button.btnClass ? button.btnClass : 'btn',
92
- tabIndex = button.tabIndex ? button.tabIndex : '-1'
91
+ tabIndex = button.tabIndex ? button.tabIndex : '-1',
92
+ hotkey = typeof button.hotkey !== 'undefined' ? button.hotkey : '',
93
+ hotkeyCaption = typeof jQuery.hotkeys !== 'undefined' && hotkey !== '' ? ' ('+hotkey+')' : ''
93
94
 
94
95
  if (button.toggle == true) {
95
96
  buttonToggle = ' data-toggle="button"'
96
97
  }
97
98
 
98
99
  // Attach the button object
99
- btnGroupContainer.append('<button class="'
100
+ btnGroupContainer.append('<button type="button" class="'
100
101
  +btnClass
101
102
  +' btn-default btn-sm" title="'
102
- +button.title
103
+ +this.__localize(button.title)
104
+ +hotkeyCaption
103
105
  +'" tabindex="'
104
106
  +tabIndex
105
107
  +'" data-provider="'
106
108
  +ns
107
109
  +'" data-handler="'
108
110
  +buttonHandler
111
+ +'" data-hotkey="'
112
+ +hotkey
109
113
  +'"'
110
114
  +buttonToggle
111
115
  +'><span class="'
112
- +button.icon
116
+ +buttonIcon
113
117
  +'"></span> '
114
- +btnText
118
+ +this.__localize(btnText)
115
119
  +'</button>')
116
120
 
117
121
  // Register handler and callback
@@ -133,12 +137,15 @@
133
137
  rowsVal = hasRows ? this.$textarea.attr('rows') : maxRows
134
138
 
135
139
  this.$textarea.attr('rows',rowsVal)
136
- this.$textarea.css('resize','none')
140
+ if (this.$options.resize) {
141
+ this.$textarea.css('resize',this.$options.resize)
142
+ }
137
143
 
138
144
  this.$textarea
139
145
  .on('focus', $.proxy(this.focus, this))
140
146
  .on('keypress', $.proxy(this.keypress, this))
141
147
  .on('keyup', $.proxy(this.keyup, this))
148
+ .on('change', $.proxy(this.change, this))
142
149
 
143
150
  if (this.eventSupported('keydown')) {
144
151
  this.$textarea.on('keydown', $.proxy(this.keydown, this))
@@ -161,6 +168,9 @@
161
168
 
162
169
  callbackHandler(this)
163
170
 
171
+ // Trigger onChange for each button handle
172
+ this.change(this);
173
+
164
174
  // Unless it was the save handler,
165
175
  // focusin the textarea
166
176
  if (handlerName.indexOf('cmdSave') < 0) {
@@ -170,6 +180,40 @@
170
180
  e.preventDefault()
171
181
  }
172
182
 
183
+ , __localize: function(string) {
184
+ var messages = $.fn.markdown.messages,
185
+ language = this.$options.language
186
+ if (
187
+ typeof messages !== 'undefined' &&
188
+ typeof messages[language] !== 'undefined' &&
189
+ typeof messages[language][string] !== 'undefined'
190
+ ) {
191
+ return messages[language][string];
192
+ }
193
+ return string;
194
+ }
195
+
196
+ , __getIcon: function(src) {
197
+ return typeof src == 'object' ? src[this.$options.iconlibrary] : src;
198
+ }
199
+
200
+ , setFullscreen: function(mode) {
201
+ var $editor = this.$editor,
202
+ $textarea = this.$textarea
203
+
204
+ if (mode === true) {
205
+ $editor.addClass('md-fullscreen-mode')
206
+ $('body').addClass('md-nooverflow')
207
+ this.$options.onFullscreen(this)
208
+ } else {
209
+ $editor.removeClass('md-fullscreen-mode')
210
+ $('body').removeClass('md-nooverflow')
211
+ }
212
+
213
+ this.$isFullscreen = mode;
214
+ $textarea.focus()
215
+ }
216
+
173
217
  , showEditor: function() {
174
218
  var instance = this,
175
219
  textarea,
@@ -195,14 +239,34 @@
195
239
  'class': 'md-header btn-toolbar'
196
240
  })
197
241
 
198
- // Build the main buttons
199
- if (options.buttons.length > 0) {
200
- editorHeader = this.__buildButtons(options.buttons, editorHeader)
242
+ // Merge the main & additional button groups together
243
+ var allBtnGroups = []
244
+ if (options.buttons.length > 0) allBtnGroups = allBtnGroups.concat(options.buttons[0])
245
+ if (options.additionalButtons.length > 0) allBtnGroups = allBtnGroups.concat(options.additionalButtons[0])
246
+
247
+ // Reduce and/or reorder the button groups
248
+ if (options.reorderButtonGroups.length > 0) {
249
+ allBtnGroups = allBtnGroups
250
+ .filter(function(btnGroup) {
251
+ return options.reorderButtonGroups.indexOf(btnGroup.name) > -1
252
+ })
253
+ .sort(function(a, b) {
254
+ if (options.reorderButtonGroups.indexOf(a.name) < options.reorderButtonGroups.indexOf(b.name)) return -1
255
+ if (options.reorderButtonGroups.indexOf(a.name) > options.reorderButtonGroups.indexOf(b.name)) return 1
256
+ return 0
257
+ })
258
+ }
259
+
260
+ // Build the buttons
261
+ if (allBtnGroups.length > 0) {
262
+ editorHeader = this.__buildButtons([allBtnGroups], editorHeader)
201
263
  }
202
264
 
203
- // Build the additional buttons
204
- if (options.additionalButtons.length > 0) {
205
- editorHeader = this.__buildButtons(options.additionalButtons, editorHeader)
265
+ if (options.fullscreen.enable) {
266
+ 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) {
267
+ e.preventDefault();
268
+ instance.setFullscreen(true)
269
+ })
206
270
  }
207
271
 
208
272
  editor.append(editorHeader)
@@ -239,12 +303,15 @@
239
303
  container.replaceWith(editor)
240
304
  }
241
305
 
242
- // Create the footer if savable
243
- if (options.savable) {
244
- var editorFooter = $('<div/>', {
306
+ var editorFooter = $('<div/>', {
245
307
  'class': 'md-footer'
246
308
  }),
247
- saveHandler = 'cmdSave'
309
+ createFooter = false,
310
+ footer = ''
311
+ // Create the footer if savable
312
+ if (options.savable) {
313
+ createFooter = true;
314
+ var saveHandler = 'cmdSave'
248
315
 
249
316
  // Register handler and callback
250
317
  handler.push(saveHandler)
@@ -254,21 +321,43 @@
254
321
  +ns
255
322
  +'" data-handler="'
256
323
  +saveHandler
257
- +'"><i class="icon icon-white icon-ok"></i> Save</button>')
324
+ +'"><i class="icon icon-white icon-ok"></i> '
325
+ +this.__localize('Save')
326
+ +'</button>')
327
+
258
328
 
259
- editor.append(editorFooter)
260
329
  }
261
330
 
262
- // Set width/height
263
- $.each(['height','width'],function(k,attr){
264
- if (options[attr] != 'inherit') {
265
- if (jQuery.isNumeric(options[attr])) {
266
- editor.css(attr,options[attr]+'px')
267
- } else {
268
- editor.addClass(options[attr])
269
- }
331
+ footer = typeof options.footer === 'function' ? options.footer(this) : options.footer
332
+
333
+ if ($.trim(footer) !== '') {
334
+ createFooter = true;
335
+ editorFooter.append(footer);
336
+ }
337
+
338
+ if (createFooter) editor.append(editorFooter)
339
+
340
+ // Set width
341
+ if (options.width && options.width !== 'inherit') {
342
+ if (jQuery.isNumeric(options.width)) {
343
+ editor.css('display', 'table')
344
+ textarea.css('width', options.width + 'px')
345
+ } else {
346
+ editor.addClass(options.width)
270
347
  }
271
- })
348
+ }
349
+
350
+ // Set height
351
+ if (options.height && options.height !== 'inherit') {
352
+ if (jQuery.isNumeric(options.height)) {
353
+ var height = options.height
354
+ if (editorHeader) height = Math.max(0, height - editorHeader.outerHeight())
355
+ if (editorFooter) height = Math.max(0, height - editorFooter.outerHeight())
356
+ textarea.css('height', height + 'px')
357
+ } else {
358
+ editor.addClass(options.height)
359
+ }
360
+ }
272
361
 
273
362
  // Reference
274
363
  this.$editor = editor
@@ -282,6 +371,30 @@
282
371
  this.$editor.attr('id',(new Date).getTime())
283
372
  this.$editor.on('click', '[data-provider="bootstrap-markdown"]', $.proxy(this.__handle, this))
284
373
 
374
+ if (this.$element.is(':disabled') || this.$element.is('[readonly]')) {
375
+ this.$editor.addClass('md-editor-disabled');
376
+ this.disableButtons('all');
377
+ }
378
+
379
+ if (this.eventSupported('keydown') && typeof jQuery.hotkeys === 'object') {
380
+ editorHeader.find('[data-provider="bootstrap-markdown"]').each(function() {
381
+ var $button = $(this),
382
+ hotkey = $button.attr('data-hotkey')
383
+ if (hotkey.toLowerCase() !== '') {
384
+ textarea.bind('keydown', hotkey, function() {
385
+ $button.trigger('click')
386
+ return false;
387
+ })
388
+ }
389
+ })
390
+ }
391
+
392
+ if (options.initialstate === 'preview') {
393
+ this.showPreview();
394
+ } else if (options.initialstate === 'fullscreen' && options.fullscreen.enable) {
395
+ this.setFullscreen(true)
396
+ }
397
+
285
398
  } else {
286
399
  this.$editor.show()
287
400
  }
@@ -291,15 +404,60 @@
291
404
  this.$editor.addClass('active')
292
405
  }
293
406
 
407
+ if (options.fullscreen.enable && options.fullscreen !== false) {
408
+ this.$editor.append('\
409
+ <div class="md-fullscreen-controls">\
410
+ <a href="#" class="switch-theme" title="Switch themes"><span class="'+this.__getIcon(options.fullscreen.icons.switchTheme)+'"></span></a>\
411
+ <a href="#" class="exit-fullscreen" title="Exit fullscreen"><span class="'+this.__getIcon(options.fullscreen.icons.fullscreenOff)+'"></span></a>\
412
+ </div>')
413
+
414
+ this.$editor.on('click', '.exit-fullscreen', function(e) {
415
+ e.preventDefault()
416
+ instance.setFullscreen(false)
417
+ })
418
+
419
+ this.$editor.on('click', '.switch-theme', function(e) {
420
+ e.preventDefault()
421
+ instance.$editor.toggleClass('theme-dark')
422
+ })
423
+ }
424
+
425
+ // hide hidden buttons from options
426
+ this.hideButtons(options.hiddenButtons)
427
+
428
+ // disable disabled buttons from options
429
+ this.disableButtons(options.disabledButtons)
430
+
294
431
  // Trigger the onShow hook
295
432
  options.onShow(this)
296
433
 
297
434
  return this
298
435
  }
299
436
 
437
+ , parseContent: function() {
438
+ var content,
439
+ callbackContent = this.$options.onPreview(this) // Try to get the content from callback
440
+
441
+ if (typeof callbackContent == 'string') {
442
+ // Set the content based by callback content
443
+ content = callbackContent
444
+ } else {
445
+ // Set the content
446
+ var val = this.$textarea.val();
447
+ if(typeof markdown == 'object') {
448
+ content = markdown.toHTML(val);
449
+ }else if(typeof marked == 'function') {
450
+ content = marked(val);
451
+ } else {
452
+ content = val;
453
+ }
454
+ }
455
+
456
+ return content;
457
+ }
458
+
300
459
  , showPreview: function() {
301
460
  var options = this.$options,
302
- callbackContent = options.onPreview(this), // Try to get the content from callback
303
461
  container = this.$textarea,
304
462
  afterContainer = container.next(),
305
463
  replacementContainer = $('<div/>',{'class':'md-preview','data-provider':'markdown-preview'}),
@@ -310,13 +468,7 @@
310
468
  // Disable all buttons
311
469
  this.disableButtons('all').enableButtons('cmdPreview')
312
470
 
313
- if (typeof callbackContent == 'string') {
314
- // Set the content based by callback content
315
- content = callbackContent
316
- } else {
317
- // Set the content
318
- content = (typeof markdown == 'object') ? markdown.toHTML(container.val()) : container.val()
319
- }
471
+ content = this.parseContent()
320
472
 
321
473
  // Build preview element
322
474
  replacementContainer.html(content)
@@ -329,12 +481,27 @@
329
481
  container.parent().append(replacementContainer)
330
482
  }
331
483
 
484
+ // Set the preview element dimensions
485
+ replacementContainer.css({
486
+ width: container.outerWidth() + 'px',
487
+ height: container.outerHeight() + 'px'
488
+ })
489
+
490
+ if (this.$options.resize) {
491
+ replacementContainer.css('resize',this.$options.resize)
492
+ }
493
+
332
494
  // Hide the last-active textarea
333
495
  container.hide()
334
496
 
335
497
  // Attach the editor instances
336
498
  replacementContainer.data('markdown',this)
337
499
 
500
+ if (this.$element.is(':disabled') || this.$element.is('[readonly]')) {
501
+ this.$editor.addClass('md-editor-disabled');
502
+ this.disableButtons('all');
503
+ }
504
+
338
505
  return this
339
506
  }
340
507
 
@@ -350,6 +517,8 @@
350
517
 
351
518
  // Enable all buttons
352
519
  this.enableButtons('all')
520
+ // Disable configured disabled buttons
521
+ this.disableButtons(this.$options.disabledButtons)
353
522
 
354
523
  // Back to the editor
355
524
  this.$textarea.show()
@@ -477,7 +646,7 @@
477
646
  this.$nextTab.push(function(){
478
647
  return that.findSelection(start)
479
648
  })
480
- } else if (typeof start == 'numeric' && typeof end == 'numeric') {
649
+ } else if (typeof start == 'number' && typeof end == 'number') {
481
650
  var oldSelection = this.getSelection()
482
651
 
483
652
  this.setSelection(start,end)
@@ -489,24 +658,70 @@
489
658
  return
490
659
  }
491
660
 
492
- , enableButtons: function(name) {
493
- var alter = function (el) {
494
- el.removeAttr('disabled')
661
+ , __parseButtonNameParam: function(nameParam) {
662
+ var buttons = []
663
+
664
+ if (typeof nameParam == 'string') {
665
+ buttons.push(nameParam)
666
+ } else {
667
+ buttons = nameParam
495
668
  }
496
669
 
497
- this.__alterButtons(name,alter)
670
+ return buttons
671
+ }
498
672
 
499
- return this
673
+ , enableButtons: function(name) {
674
+ var buttons = this.__parseButtonNameParam(name),
675
+ that = this
676
+
677
+ $.each(buttons, function(i, v) {
678
+ that.__alterButtons(buttons[i], function (el) {
679
+ el.removeAttr('disabled')
680
+ });
681
+ })
682
+
683
+ return this;
500
684
  }
501
685
 
502
686
  , disableButtons: function(name) {
503
- var alter = function (el) {
504
- el.attr('disabled','disabled')
505
- }
687
+ var buttons = this.__parseButtonNameParam(name),
688
+ that = this
506
689
 
507
- this.__alterButtons(name,alter)
690
+ $.each(buttons, function(i, v) {
691
+ that.__alterButtons(buttons[i], function (el) {
692
+ el.attr('disabled','disabled')
693
+ });
694
+ })
695
+
696
+ return this;
697
+ }
698
+
699
+ , hideButtons: function(name) {
700
+ var buttons = this.__parseButtonNameParam(name),
701
+ that = this
702
+
703
+ $.each(buttons, function(i, v) {
704
+ that.__alterButtons(buttons[i], function (el) {
705
+ el.addClass('hidden');
706
+ });
707
+ })
708
+
709
+ return this;
710
+
711
+ }
712
+
713
+ , showButtons: function(name) {
714
+ var buttons = this.__parseButtonNameParam(name),
715
+ that = this
716
+
717
+ $.each(buttons, function(i, v) {
718
+ that.__alterButtons(buttons[i], function (el) {
719
+ el.removeClass('hidden');
720
+ });
721
+ })
722
+
723
+ return this;
508
724
 
509
- return this
510
725
  }
511
726
 
512
727
  , eventSupported: function(eventName) {
@@ -518,16 +733,6 @@
518
733
  return isSupported
519
734
  }
520
735
 
521
- , keydown: function (e) {
522
- this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
523
- this.keyup(e)
524
- }
525
-
526
- , keypress: function (e) {
527
- if (this.suppressKeyPressRepeat) return
528
- this.keyup(e)
529
- }
530
-
531
736
  , keyup: function (e) {
532
737
  var blocked = false
533
738
  switch(e.keyCode) {
@@ -568,7 +773,10 @@
568
773
  break
569
774
 
570
775
  case 13: // enter
776
+ blocked = false
777
+ break
571
778
  case 27: // escape
779
+ if (this.$isFullscreen) this.setFullscreen(false)
572
780
  blocked = false
573
781
  break
574
782
 
@@ -580,7 +788,14 @@
580
788
  e.stopPropagation()
581
789
  e.preventDefault()
582
790
  }
583
- }
791
+
792
+ this.$options.onChange(this)
793
+ }
794
+
795
+ , change: function(e) {
796
+ this.$options.onChange(this);
797
+ return this;
798
+ }
584
799
 
585
800
  , focus: function (e) {
586
801
  var options = this.$options,
@@ -605,6 +820,9 @@
605
820
  }
606
821
  })
607
822
 
823
+ // Trigger the onFocus hook
824
+ options.onFocus(this);
825
+
608
826
  return this
609
827
  }
610
828
 
@@ -663,6 +881,8 @@
663
881
  })
664
882
  }
665
883
 
884
+ $.fn.markdown.messages = {}
885
+
666
886
  $.fn.markdown.defaults = {
667
887
  /* Editor Properties */
668
888
  autofocus: false,
@@ -670,6 +890,10 @@
670
890
  savable:false,
671
891
  width: 'inherit',
672
892
  height: 'inherit',
893
+ resize: 'none',
894
+ iconlibrary: 'glyph',
895
+ language: 'en',
896
+ initialstate: 'editor',
673
897
 
674
898
  /* Buttons Properties */
675
899
  buttons: [
@@ -677,15 +901,16 @@
677
901
  name: 'groupFont',
678
902
  data: [{
679
903
  name: 'cmdBold',
904
+ hotkey: 'Ctrl+B',
680
905
  title: 'Bold',
681
- icon: 'glyphicon glyphicon-bold',
906
+ icon: { glyph: 'glyphicon glyphicon-bold', fa: 'fa fa-bold', 'fa-3': 'icon-bold' },
682
907
  callback: function(e){
683
908
  // Give/remove ** surround the selection
684
909
  var chunk, cursor, selected = e.getSelection(), content = e.getContent()
685
910
 
686
911
  if (selected.length == 0) {
687
912
  // Give extra word
688
- chunk = 'strong text'
913
+ chunk = e.__localize('strong text')
689
914
  } else {
690
915
  chunk = selected.text
691
916
  }
@@ -707,14 +932,15 @@
707
932
  },{
708
933
  name: 'cmdItalic',
709
934
  title: 'Italic',
710
- icon: 'glyphicon glyphicon-italic',
935
+ hotkey: 'Ctrl+I',
936
+ icon: { glyph: 'glyphicon glyphicon-italic', fa: 'fa fa-italic', 'fa-3': 'icon-italic' },
711
937
  callback: function(e){
712
938
  // Give/remove * surround the selection
713
939
  var chunk, cursor, selected = e.getSelection(), content = e.getContent()
714
940
 
715
941
  if (selected.length == 0) {
716
942
  // Give extra word
717
- chunk = 'emphasized text'
943
+ chunk = e.__localize('emphasized text')
718
944
  } else {
719
945
  chunk = selected.text
720
946
  }
@@ -736,16 +962,17 @@
736
962
  },{
737
963
  name: 'cmdHeading',
738
964
  title: 'Heading',
739
- icon: 'glyphicon glyphicon-font',
965
+ hotkey: 'Ctrl+H',
966
+ icon: { glyph: 'glyphicon glyphicon-header', fa: 'fa fa-font', 'fa-3': 'icon-font' },
740
967
  callback: function(e){
741
968
  // Append/remove ### surround the selection
742
969
  var chunk, cursor, selected = e.getSelection(), content = e.getContent(), pointer, prevChar
743
970
 
744
971
  if (selected.length == 0) {
745
972
  // Give extra word
746
- chunk = 'heading text'
973
+ chunk = e.__localize('heading text')
747
974
  } else {
748
- chunk = selected.text
975
+ chunk = selected.text + '\n';
749
976
  }
750
977
 
751
978
  // transform selection and set the cursor into chunked text
@@ -754,12 +981,12 @@
754
981
  e.setSelection(selected.start-pointer,selected.end)
755
982
  e.replaceSelection(chunk)
756
983
  cursor = selected.start-pointer
757
- } else if (prevChar = content.substr(selected.start-1,1), !!prevChar && prevChar != '\n') {
758
- e.replaceSelection('\n\n### '+chunk+'\n')
984
+ } else if (selected.start > 0 && (prevChar = content.substr(selected.start-1,1), !!prevChar && prevChar != '\n')) {
985
+ e.replaceSelection('\n\n### '+chunk)
759
986
  cursor = selected.start+6
760
987
  } else {
761
988
  // Empty string before element
762
- e.replaceSelection('### '+chunk+'\n')
989
+ e.replaceSelection('### '+chunk)
763
990
  cursor = selected.start+4
764
991
  }
765
992
 
@@ -772,21 +999,22 @@
772
999
  data: [{
773
1000
  name: 'cmdUrl',
774
1001
  title: 'URL/Link',
775
- icon: 'glyphicon glyphicon-globe',
1002
+ hotkey: 'Ctrl+L',
1003
+ icon: { glyph: 'glyphicon glyphicon-link', fa: 'fa fa-link', 'fa-3': 'icon-link' },
776
1004
  callback: function(e){
777
1005
  // Give [] surround the selection and prepend the link
778
1006
  var chunk, cursor, selected = e.getSelection(), content = e.getContent(), link
779
1007
 
780
1008
  if (selected.length == 0) {
781
1009
  // Give extra word
782
- chunk = 'enter link description here'
1010
+ chunk = e.__localize('enter link description here')
783
1011
  } else {
784
1012
  chunk = selected.text
785
1013
  }
786
1014
 
787
- link = prompt('Insert Hyperlink','http://')
1015
+ link = prompt(e.__localize('Insert Hyperlink'),'http://')
788
1016
 
789
- if (link != null) {
1017
+ if (link != null && link != '' && link != 'http://') {
790
1018
  // transform selection and set the cursor into chunked text
791
1019
  e.replaceSelection('['+chunk+']('+link+')')
792
1020
  cursor = selected.start+1
@@ -798,27 +1026,28 @@
798
1026
  },{
799
1027
  name: 'cmdImage',
800
1028
  title: 'Image',
801
- icon: 'glyphicon glyphicon-picture',
1029
+ hotkey: 'Ctrl+G',
1030
+ icon: { glyph: 'glyphicon glyphicon-picture', fa: 'fa fa-picture-o', 'fa-3': 'icon-picture' },
802
1031
  callback: function(e){
803
1032
  // Give ![] surround the selection and prepend the image link
804
1033
  var chunk, cursor, selected = e.getSelection(), content = e.getContent(), link
805
1034
 
806
1035
  if (selected.length == 0) {
807
1036
  // Give extra word
808
- chunk = 'enter image description here'
1037
+ chunk = e.__localize('enter image description here')
809
1038
  } else {
810
1039
  chunk = selected.text
811
1040
  }
812
1041
 
813
- link = prompt('Insert Image Hyperlink','http://')
1042
+ link = prompt(e.__localize('Insert Image Hyperlink'),'http://')
814
1043
 
815
1044
  if (link != null) {
816
1045
  // transform selection and set the cursor into chunked text
817
- e.replaceSelection('!['+chunk+']('+link+' "enter image title here")')
1046
+ e.replaceSelection('!['+chunk+']('+link+' "'+e.__localize('enter image title here')+'")')
818
1047
  cursor = selected.start+2
819
1048
 
820
1049
  // Set the next tab
821
- e.setNextTab('enter image title here')
1050
+ e.setNextTab(e.__localize('enter image title here'))
822
1051
 
823
1052
  // Set the cursor
824
1053
  e.setSelection(cursor,cursor+chunk.length)
@@ -829,8 +1058,9 @@
829
1058
  name: 'groupMisc',
830
1059
  data: [{
831
1060
  name: 'cmdList',
832
- title: 'List',
833
- icon: 'glyphicon glyphicon-list',
1061
+ hotkey: 'Ctrl+U',
1062
+ title: 'Unordered List',
1063
+ icon: { glyph: 'glyphicon glyphicon-list', fa: 'fa fa-list', 'fa-3': 'icon-list-ul' },
834
1064
  callback: function(e){
835
1065
  // Prepend/Give - surround the selection
836
1066
  var chunk, cursor, selected = e.getSelection(), content = e.getContent()
@@ -838,12 +1068,12 @@
838
1068
  // transform selection and set the cursor into chunked text
839
1069
  if (selected.length == 0) {
840
1070
  // Give extra word
841
- chunk = 'list text here'
1071
+ chunk = e.__localize('list text here')
842
1072
 
843
1073
  e.replaceSelection('- '+chunk)
844
-
845
1074
  // Set the cursor
846
1075
  cursor = selected.start+2
1076
+
847
1077
  } else {
848
1078
  if (selected.text.indexOf('\n') < 0) {
849
1079
  chunk = selected.text
@@ -869,7 +1099,130 @@
869
1099
  }
870
1100
  }
871
1101
 
1102
+ // Set the cursor
1103
+ e.setSelection(cursor,cursor+chunk.length)
1104
+ }
1105
+ },
1106
+ {
1107
+ name: 'cmdListO',
1108
+ hotkey: 'Ctrl+O',
1109
+ title: 'Ordered List',
1110
+ icon: { glyph: 'glyphicon glyphicon-th-list', fa: 'fa fa-list-ol', 'fa-3': 'icon-list-ol' },
1111
+ callback: function(e) {
1112
+
1113
+ // Prepend/Give - surround the selection
1114
+ var chunk, cursor, selected = e.getSelection(), content = e.getContent()
1115
+
1116
+ // transform selection and set the cursor into chunked text
1117
+ if (selected.length == 0) {
1118
+ // Give extra word
1119
+ chunk = e.__localize('list text here')
1120
+ e.replaceSelection('1. '+chunk)
1121
+ // Set the cursor
1122
+ cursor = selected.start+3
1123
+
1124
+ } else {
1125
+ if (selected.text.indexOf('\n') < 0) {
1126
+ chunk = selected.text
1127
+
1128
+ e.replaceSelection('1. '+chunk)
1129
+
1130
+ // Set the cursor
1131
+ cursor = selected.start+3
1132
+ } else {
1133
+ var list = []
1134
+
1135
+ list = selected.text.split('\n')
1136
+ chunk = list[0]
1137
+
1138
+ $.each(list,function(k,v) {
1139
+ list[k] = '1. '+v
1140
+ })
1141
+
1142
+ e.replaceSelection('\n\n'+list.join('\n'))
872
1143
 
1144
+ // Set the cursor
1145
+ cursor = selected.start+5
1146
+ }
1147
+ }
1148
+
1149
+ // Set the cursor
1150
+ e.setSelection(cursor,cursor+chunk.length)
1151
+ }
1152
+ },
1153
+ {
1154
+ name: 'cmdCode',
1155
+ hotkey: 'Ctrl+K',
1156
+ title: 'Code',
1157
+ icon: { glyph: 'glyphicon glyphicon-asterisk', fa: 'fa fa-code', 'fa-3': 'icon-code' },
1158
+ callback: function(e) {
1159
+
1160
+ // Give/remove ** surround the selection
1161
+ var chunk, cursor, selected = e.getSelection(), content = e.getContent()
1162
+
1163
+ if (selected.length == 0) {
1164
+ // Give extra word
1165
+ chunk = e.__localize('code text here')
1166
+ } else {
1167
+ chunk = selected.text
1168
+ }
1169
+
1170
+ // transform selection and set the cursor into chunked text
1171
+ if (content.substr(selected.start-1,1) == '`'
1172
+ && content.substr(selected.end,1) == '`' ) {
1173
+ e.setSelection(selected.start-1,selected.end+1)
1174
+ e.replaceSelection(chunk)
1175
+ cursor = selected.start-1
1176
+ } else {
1177
+ e.replaceSelection('`'+chunk+'`')
1178
+ cursor = selected.start+1
1179
+ }
1180
+
1181
+ // Set the cursor
1182
+ e.setSelection(cursor,cursor+chunk.length)
1183
+ }
1184
+ },
1185
+ {
1186
+ name: 'cmdQuote',
1187
+ hotkey: 'Ctrl+Q',
1188
+ title: 'Quote',
1189
+ icon: { glyph: 'glyphicon glyphicon-comment', fa: 'fa fa-quote-left', 'fa-3': 'icon-quote-left' },
1190
+ callback: function(e) {
1191
+ // Prepend/Give - surround the selection
1192
+ var chunk, cursor, selected = e.getSelection(), content = e.getContent()
1193
+
1194
+ // transform selection and set the cursor into chunked text
1195
+ if (selected.length == 0) {
1196
+ // Give extra word
1197
+ chunk = e.__localize('quote here')
1198
+ e.replaceSelection('> '+chunk)
1199
+ // Set the cursor
1200
+ cursor = selected.start+2
1201
+
1202
+ } else {
1203
+ if (selected.text.indexOf('\n') < 0) {
1204
+ chunk = selected.text
1205
+
1206
+ e.replaceSelection('> '+chunk)
1207
+
1208
+ // Set the cursor
1209
+ cursor = selected.start+2
1210
+ } else {
1211
+ var list = []
1212
+
1213
+ list = selected.text.split('\n')
1214
+ chunk = list[0]
1215
+
1216
+ $.each(list,function(k,v) {
1217
+ list[k] = '> '+v
1218
+ })
1219
+
1220
+ e.replaceSelection('\n\n'+list.join('\n'))
1221
+
1222
+ // Set the cursor
1223
+ cursor = selected.start+4
1224
+ }
1225
+ }
873
1226
 
874
1227
  // Set the cursor
875
1228
  e.setSelection(cursor,cursor+chunk.length)
@@ -880,10 +1233,11 @@
880
1233
  data: [{
881
1234
  name: 'cmdPreview',
882
1235
  toggle: true,
1236
+ hotkey: 'Ctrl+P',
883
1237
  title: 'Preview',
884
1238
  btnText: 'Preview',
885
1239
  btnClass: 'btn btn-primary btn-sm',
886
- icon: 'glyphicon glyphicon-search',
1240
+ icon: { glyph: 'glyphicon glyphicon-search', fa: 'fa fa-search', 'fa-3': 'icon-search' },
887
1241
  callback: function(e){
888
1242
  // Check the preview mode and toggle based on this flag
889
1243
  var isPreview = e.$isPreview,content
@@ -899,12 +1253,39 @@
899
1253
  }]
900
1254
  ],
901
1255
  additionalButtons:[], // Place to hook more buttons by code
1256
+ reorderButtonGroups:[],
1257
+ hiddenButtons:[], // Default hidden buttons
1258
+ disabledButtons:[], // Default disabled buttons
1259
+ footer: '',
1260
+ fullscreen: {
1261
+ enable: true,
1262
+ icons: {
1263
+ fullscreenOn: {
1264
+ fa: 'fa fa-expand',
1265
+ glyph: 'glyphicon glyphicon-fullscreen',
1266
+ 'fa-3': 'icon-resize-full'
1267
+ },
1268
+ fullscreenOff: {
1269
+ fa: 'fa fa-compress',
1270
+ glyph: 'glyphicon glyphicon-fullscreen',
1271
+ 'fa-3': 'icon-resize-small'
1272
+ },
1273
+ switchTheme: {
1274
+ fa: 'fa fa-adjust',
1275
+ glyph: 'glyphicon glyphicon-adjust',
1276
+ 'fa-3': 'icon-adjust'
1277
+ }
1278
+ }
1279
+ },
902
1280
 
903
1281
  /* Events hook */
904
1282
  onShow: function (e) {},
905
1283
  onPreview: function (e) {},
906
1284
  onSave: function (e) {},
907
- onBlur: function (e) {}
1285
+ onBlur: function (e) {},
1286
+ onFocus: function (e) {},
1287
+ onChange: function(e) {},
1288
+ onFullscreen: function(e) {}
908
1289
  }
909
1290
 
910
1291
  $.fn.markdown.Constructor = Markdown
@@ -927,7 +1308,8 @@
927
1308
  $this.data('markdown').showEditor()
928
1309
  return
929
1310
  }
930
- $this.markdown($this.data())
1311
+
1312
+ $this.markdown()
931
1313
  }
932
1314
 
933
1315
  var analyzeMarkdown = function(e) {
@@ -993,4 +1375,4 @@
993
1375
  })
994
1376
  })
995
1377
 
996
- }(window.jQuery);
1378
+ }(window.jQuery);