rails-bootstrap-markdown 1.0.0 → 2.6.0

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 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);