ui_bibz 2.0.0.alpha12 → 2.0.0.alpha13

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