| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426 | /* =================================================== * bootstrap-markdown.js v2.7.0 * http://github.com/toopay/bootstrap-markdown * =================================================== * Copyright 2013-2014 Taufan Aditya * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ========================================================== */! function ($) {    "use strict"; // jshint ;_;    /* MARKDOWN CLASS DEFINITION     * ========================== */    var Markdown = function (element, options) {        // Class Properties        this.$ns = 'bootstrap-markdown'        this.$element = $(element)        this.$editable = {            el: null,            type: null,            attrKeys: [],            attrValues: [],            content: null        }        this.$options = $.extend(true, {}, $.fn.markdown.defaults, options, this.$element.data(), this.$element.data('options'))        this.$oldContent = null        this.$isPreview = false        this.$isFullscreen = false        this.$editor = null        this.$textarea = null        this.$handler = []        this.$callback = []        this.$nextTab = []        this.showEditor()    }    Markdown.prototype = {        constructor: Markdown        ,        __alterButtons: function (name, alter) {            var handler = this.$handler,                isAll = (name == 'all'),                that = this            $.each(handler, function (k, v) {                var halt = true                if (isAll) {                    halt = false                } else {                    halt = v.indexOf(name) < 0                }                if (halt == false) {                    alter(that.$editor.find('button[data-handler="' + v + '"]'))                }            })        }        ,        __buildButtons: function (buttonsArray, container) {            var i,                ns = this.$ns,                handler = this.$handler,                callback = this.$callback            for (i = 0; i < buttonsArray.length; i++) {                // Build each group container                var y, btnGroups = buttonsArray[i]                for (y = 0; y < btnGroups.length; y++) {                    // Build each button group                    var z,                        buttons = btnGroups[y].data,                        btnGroupContainer = $('<div/>', {                            'class': 'btn-group'                        })                    for (z = 0; z < buttons.length; z++) {                        var button = buttons[z],                            buttonContainer, buttonIconContainer,                            buttonHandler = ns + '-' + button.name,                            buttonIcon = this.__getIcon(button.icon),                            btnText = button.btnText ? button.btnText : '',                            btnClass = button.btnClass ? button.btnClass : 'btn',                            tabIndex = button.tabIndex ? button.tabIndex : '-1',                            hotkey = typeof button.hotkey !== 'undefined' ? button.hotkey : '',                            hotkeyCaption = typeof jQuery.hotkeys !== 'undefined' && hotkey !== '' ? ' (' + hotkey + ')' : ''                        // Construct the button object                        buttonContainer = $('<button></button>');                        buttonContainer.text(' ' + this.__localize(btnText)).addClass('btn-white btn-sm').addClass(btnClass);                        if (btnClass.match(/btn\-(primary|success|info|warning|danger|link)/)) {                            buttonContainer.removeClass('btn-default');                        }                        buttonContainer.attr({                            'type': 'button',                            'title': this.__localize(button.title) + hotkeyCaption,                            'tabindex': tabIndex,                            'data-provider': ns,                            'data-handler': buttonHandler,                            'data-hotkey': hotkey                        });                        if (button.toggle == true) {                            buttonContainer.attr('data-toggle', 'button');                        }                        buttonIconContainer = $('<span/>');                        buttonIconContainer.addClass(buttonIcon);                        buttonIconContainer.prependTo(buttonContainer);                        // Attach the button object                        btnGroupContainer.append(buttonContainer);                        // Register handler and callback                        handler.push(buttonHandler);                        callback.push(button.callback);                    }                    // Attach the button group into container dom                    container.append(btnGroupContainer);                }            }            return container;        },        __setListener: function () {            // Set size and resizable Properties            var hasRows = typeof this.$textarea.attr('rows') != 'undefined',                maxRows = this.$textarea.val().split("\n").length > 5 ? this.$textarea.val().split("\n").length : '5',                rowsVal = hasRows ? this.$textarea.attr('rows') : maxRows            this.$textarea.attr('rows', rowsVal)            if (this.$options.resize) {                this.$textarea.css('resize', this.$options.resize)            }            this.$textarea                .on('focus', $.proxy(this.focus, this))                .on('keypress', $.proxy(this.keypress, this))                .on('keyup', $.proxy(this.keyup, this))                .on('change', $.proxy(this.change, this))            if (this.eventSupported('keydown')) {                this.$textarea.on('keydown', $.proxy(this.keydown, this))            }            // Re-attach markdown data            this.$textarea.data('markdown', this)        }        ,        __handle: function (e) {            var target = $(e.currentTarget),                handler = this.$handler,                callback = this.$callback,                handlerName = target.attr('data-handler'),                callbackIndex = handler.indexOf(handlerName),                callbackHandler = callback[callbackIndex]            // Trigger the focusin            $(e.currentTarget).focus()            callbackHandler(this)            // Trigger onChange for each button handle            this.change(this);            // Unless it was the save handler,            // focusin the textarea            if (handlerName.indexOf('cmdSave') < 0) {                this.$textarea.focus()            }            e.preventDefault()        }        ,        __localize: function (string) {            var messages = $.fn.markdown.messages,                language = this.$options.language            if (                typeof messages !== 'undefined' &&                typeof messages[language] !== 'undefined' &&                typeof messages[language][string] !== 'undefined'            ) {                return messages[language][string];            }            return string;        }        ,        __getIcon: function (src) {            return typeof src == 'object' ? src[this.$options.iconlibrary] : src;        }        ,        setFullscreen: function (mode) {            var $editor = this.$editor,                $textarea = this.$textarea            if (mode === true) {                $editor.addClass('md-fullscreen-mode')                $('body').addClass('md-nooverflow')                this.$options.onFullscreen(this)            } else {                $editor.removeClass('md-fullscreen-mode')                $('body').removeClass('md-nooverflow')            }            this.$isFullscreen = mode;            $textarea.focus()        }        ,        showEditor: function () {            var instance = this,                textarea,                ns = this.$ns,                container = this.$element,                originalHeigth = container.css('height'),                originalWidth = container.css('width'),                editable = this.$editable,                handler = this.$handler,                callback = this.$callback,                options = this.$options,                editor = $('<div/>', {                    'class': 'md-editor',                    click: function () {                        instance.focus()                    }                })            // Prepare the editor            if (this.$editor == null) {                // Create the panel                var editorHeader = $('<div/>', {                    'class': 'md-header btn-toolbar'                })                // Merge the main & additional button groups together                var allBtnGroups = []                if (options.buttons.length > 0) allBtnGroups = allBtnGroups.concat(options.buttons[0])                if (options.additionalButtons.length > 0) allBtnGroups = allBtnGroups.concat(options.additionalButtons[0])                // Reduce and/or reorder the button groups                if (options.reorderButtonGroups.length > 0) {                    allBtnGroups = allBtnGroups                        .filter(function (btnGroup) {                            return options.reorderButtonGroups.indexOf(btnGroup.name) > -1                        })                        .sort(function (a, b) {                            if (options.reorderButtonGroups.indexOf(a.name) < options.reorderButtonGroups.indexOf(b.name)) return -1                            if (options.reorderButtonGroups.indexOf(a.name) > options.reorderButtonGroups.indexOf(b.name)) return 1                            return 0                        })                }                // Build the buttons                if (allBtnGroups.length > 0) {                    editorHeader = this.__buildButtons([allBtnGroups], editorHeader)                }                if (options.fullscreen.enable) {                    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) {                        e.preventDefault();                        instance.setFullscreen(true)                    })                }                editor.append(editorHeader)                // Wrap the textarea                if (container.is('textarea')) {                    container.before(editor)                    textarea = container                    textarea.addClass('md-input')                    editor.append(textarea)                } else {                    var rawContent = (typeof toMarkdown == 'function') ? toMarkdown(container.html()) : container.html(),                        currentContent = $.trim(rawContent)                    // This is some arbitrary content that could be edited                    textarea = $('<textarea/>', {                        'class': 'md-input',                        'val': currentContent                    })                    editor.append(textarea)                    // Save the editable                    editable.el = container                    editable.type = container.prop('tagName').toLowerCase()                    editable.content = container.html()                    $(container[0].attributes).each(function () {                        editable.attrKeys.push(this.nodeName)                        editable.attrValues.push(this.nodeValue)                    })                    // Set editor to blocked the original container                    container.replaceWith(editor)                }                var editorFooter = $('<div/>', {                        'class': 'md-footer'                    }),                    createFooter = false,                    footer = ''                    // Create the footer if savable                if (options.savable) {                    createFooter = true;                    var saveHandler = 'cmdSave'                    // Register handler and callback                    handler.push(saveHandler)                    callback.push(options.onSave)                    editorFooter.append('<button class="btn btn-success" data-provider="' + ns + '" data-handler="' + saveHandler + '"><i class="icon icon-white icon-ok"></i> ' + this.__localize('Save') + '</button>')                }                footer = typeof options.footer === 'function' ? options.footer(this) : options.footer                if ($.trim(footer) !== '') {                    createFooter = true;                    editorFooter.append(footer);                }                if (createFooter) editor.append(editorFooter)                // Set width                if (options.width && options.width !== 'inherit') {                    if (jQuery.isNumeric(options.width)) {                        editor.css('display', 'table')                        textarea.css('width', options.width + 'px')                    } else {                        editor.addClass(options.width)                    }                }                // Set height                if (options.height && options.height !== 'inherit') {                    if (jQuery.isNumeric(options.height)) {                        var height = options.height                        if (editorHeader) height = Math.max(0, height - editorHeader.outerHeight())                        if (editorFooter) height = Math.max(0, height - editorFooter.outerHeight())                        textarea.css('height', height + 'px')                    } else {                        editor.addClass(options.height)                    }                }                // Reference                this.$editor = editor                this.$textarea = textarea                this.$editable = editable                this.$oldContent = this.getContent()                this.__setListener()                // Set editor attributes, data short-hand API and listener                this.$editor.attr('id', (new Date).getTime())                this.$editor.on('click', '[data-provider="bootstrap-markdown"]', $.proxy(this.__handle, this))                if (this.$element.is(':disabled') || this.$element.is('[readonly]')) {                    this.$editor.addClass('md-editor-disabled');                    this.disableButtons('all');                }                if (this.eventSupported('keydown') && typeof jQuery.hotkeys === 'object') {                    editorHeader.find('[data-provider="bootstrap-markdown"]').each(function () {                        var $button = $(this),                            hotkey = $button.attr('data-hotkey')                        if (hotkey.toLowerCase() !== '') {                            textarea.bind('keydown', hotkey, function () {                                $button.trigger('click')                                return false;                            })                        }                    })                }                if (options.initialstate === 'preview') {                    this.showPreview();                } else if (options.initialstate === 'fullscreen' && options.fullscreen.enable) {                    this.setFullscreen(true)                }            } else {                this.$editor.show()            }            if (options.autofocus) {                this.$textarea.focus()                this.$editor.addClass('active')            }            if (options.fullscreen.enable && options.fullscreen !== false) {                this.$editor.append('\          <div class="md-fullscreen-controls">\            <a href="#" class="exit-fullscreen" title="Exit fullscreen"><span class="' + this.__getIcon(options.fullscreen.icons.fullscreenOff) + '"></span></a>\          </div>')                this.$editor.on('click', '.exit-fullscreen', function (e) {                    e.preventDefault()                    instance.setFullscreen(false)                })            }            // hide hidden buttons from options            this.hideButtons(options.hiddenButtons)            // disable disabled buttons from options            this.disableButtons(options.disabledButtons)            // Trigger the onShow hook            options.onShow(this)            return this        }        ,        parseContent: function () {            var content,                callbackContent = this.$options.onPreview(this) // Try to get the content from callback            if (typeof callbackContent == 'string') {                // Set the content based by callback content                content = callbackContent            } else {                // Set the content                var val = this.$textarea.val();                if (typeof markdown == 'object') {                    content = markdown.toHTML(val);                } else if (typeof marked == 'function') {                    content = marked(val);                } else {                    content = val;                }            }            return content;        }        ,        showPreview: function () {            var options = this.$options,                container = this.$textarea,                afterContainer = container.next(),                replacementContainer = $('<div/>', {                    'class': 'md-preview',                    'data-provider': 'markdown-preview'                }),                content            // Give flag that tell the editor enter preview mode            this.$isPreview = true            // Disable all buttons            this.disableButtons('all').enableButtons('cmdPreview')            content = this.parseContent()            // Build preview element            replacementContainer.html(content)            if (afterContainer && afterContainer.attr('class') == 'md-footer') {                // If there is footer element, insert the preview container before it                replacementContainer.insertBefore(afterContainer)            } else {                // Otherwise, just append it after textarea                container.parent().append(replacementContainer)            }            // Set the preview element dimensions            replacementContainer.css({                width: container.outerWidth() + 'px',                height: container.outerHeight() + 'px'            })            if (this.$options.resize) {                replacementContainer.css('resize', this.$options.resize)            }            // Hide the last-active textarea            container.hide()            // Attach the editor instances            replacementContainer.data('markdown', this)            if (this.$element.is(':disabled') || this.$element.is('[readonly]')) {                this.$editor.addClass('md-editor-disabled');                this.disableButtons('all');            }            return this        }        ,        hidePreview: function () {            // Give flag that tell the editor quit preview mode            this.$isPreview = false            // Obtain the preview container            var container = this.$editor.find('div[data-provider="markdown-preview"]')            // Remove the preview container            container.remove()            // Enable all buttons            this.enableButtons('all')            // Disable configured disabled buttons            this.disableButtons(this.$options.disabledButtons)            // Back to the editor            this.$textarea.show()            this.__setListener()            return this        }        ,        isDirty: function () {            return this.$oldContent != this.getContent()        }        ,        getContent: function () {            return this.$textarea.val()        }        ,        setContent: function (content) {            this.$textarea.val(content)            return this        }        ,        findSelection: function (chunk) {            var content = this.getContent(),                startChunkPosition            if (startChunkPosition = content.indexOf(chunk), startChunkPosition >= 0 && chunk.length > 0) {                var oldSelection = this.getSelection(),                    selection                this.setSelection(startChunkPosition, startChunkPosition + chunk.length)                selection = this.getSelection()                this.setSelection(oldSelection.start, oldSelection.end)                return selection            } else {                return null            }        }        ,        getSelection: function () {            var e = this.$textarea[0]            return (                ('selectionStart' in e && function () {                    var l = e.selectionEnd - e.selectionStart                    return {                        start: e.selectionStart,                        end: e.selectionEnd,                        length: l,                        text: e.value.substr(e.selectionStart, l)                    }                }) ||                /* browser not supported */                function () {                    return null                }            )()        }        ,        setSelection: function (start, end) {            var e = this.$textarea[0]            return (                ('selectionStart' in e && function () {                    e.selectionStart = start                    e.selectionEnd = end                    return                }) ||                /* browser not supported */                function () {                    return null                }            )()        }        ,        replaceSelection: function (text) {            var e = this.$textarea[0]            return (                ('selectionStart' in e && function () {                    e.value = e.value.substr(0, e.selectionStart) + text + e.value.substr(e.selectionEnd, e.value.length)                    // Set cursor to the last replacement end                    e.selectionStart = e.value.length                    return this                }) ||                /* browser not supported */                function () {                    e.value += text                    return jQuery(e)                }            )()        }        ,        getNextTab: function () {            // Shift the nextTab            if (this.$nextTab.length == 0) {                return null            } else {                var nextTab, tab = this.$nextTab.shift()                if (typeof tab == 'function') {                    nextTab = tab()                } else if (typeof tab == 'object' && tab.length > 0) {                    nextTab = tab                }                return nextTab            }        }        ,        setNextTab: function (start, end) {            // Push new selection into nextTab collections            if (typeof start == 'string') {                var that = this                this.$nextTab.push(function () {                    return that.findSelection(start)                })            } else if (typeof start == 'number' && typeof end == 'number') {                var oldSelection = this.getSelection()                this.setSelection(start, end)                this.$nextTab.push(this.getSelection())                this.setSelection(oldSelection.start, oldSelection.end)            }            return        }        ,        __parseButtonNameParam: function (nameParam) {            var buttons = []            if (typeof nameParam == 'string') {                buttons.push(nameParam)            } else {                buttons = nameParam            }            return buttons        }        ,        enableButtons: function (name) {            var buttons = this.__parseButtonNameParam(name),                that = this            $.each(buttons, function (i, v) {                that.__alterButtons(buttons[i], function (el) {                    el.removeAttr('disabled')                });            })            return this;        }        ,        disableButtons: function (name) {            var buttons = this.__parseButtonNameParam(name),                that = this            $.each(buttons, function (i, v) {                that.__alterButtons(buttons[i], function (el) {                    el.attr('disabled', 'disabled')                });            })            return this;        }        ,        hideButtons: function (name) {            var buttons = this.__parseButtonNameParam(name),                that = this            $.each(buttons, function (i, v) {                that.__alterButtons(buttons[i], function (el) {                    el.addClass('hidden');                });            })            return this;        }        ,        showButtons: function (name) {            var buttons = this.__parseButtonNameParam(name),                that = this            $.each(buttons, function (i, v) {                that.__alterButtons(buttons[i], function (el) {                    el.removeClass('hidden');                });            })            return this;        }        ,        eventSupported: function (eventName) {            var isSupported = eventName in this.$element            if (!isSupported) {                this.$element.setAttribute(eventName, 'return;')                isSupported = typeof this.$element[eventName] === 'function'            }            return isSupported        }        ,        keyup: function (e) {            var blocked = false            switch (e.keyCode) {            case 40: // down arrow            case 38: // up arrow            case 16: // shift            case 17: // ctrl            case 18: // alt                break            case 9: // tab                var nextTab                if (nextTab = this.getNextTab(), nextTab != null) {                    // Get the nextTab if exists                    var that = this                    setTimeout(function () {                        that.setSelection(nextTab.start, nextTab.end)                    }, 500)                    blocked = true                } else {                    // The next tab memory contains nothing...                    // check the cursor position to determine tab action                    var cursor = this.getSelection()                    if (cursor.start == cursor.end && cursor.end == this.getContent().length) {                        // The cursor already reach the end of the content                        blocked = false                    } else {                        // Put the cursor to the end                        this.setSelection(this.getContent().length, this.getContent().length)                        blocked = true                    }                }                break            case 13: // enter                blocked = false                break            case 27: // escape                if (this.$isFullscreen) this.setFullscreen(false)                blocked = false                break            default:                blocked = false            }            if (blocked) {                e.stopPropagation()                e.preventDefault()            }            this.$options.onChange(this)        }        ,        change: function (e) {            this.$options.onChange(this);            return this;        }        ,        focus: function (e) {            var options = this.$options,                isHideable = options.hideable,                editor = this.$editor            editor.addClass('active')            // Blur other markdown(s)            $(document).find('.md-editor').each(function () {                if ($(this).attr('id') != editor.attr('id')) {                    var attachedMarkdown                    if (attachedMarkdown = $(this).find('textarea').data('markdown'),                        attachedMarkdown == null) {                        attachedMarkdown = $(this).find('div[data-provider="markdown-preview"]').data('markdown')                    }                    if (attachedMarkdown) {                        attachedMarkdown.blur()                    }                }            })            // Trigger the onFocus hook            options.onFocus(this);            return this        }        ,        blur: function (e) {            var options = this.$options,                isHideable = options.hideable,                editor = this.$editor,                editable = this.$editable            if (editor.hasClass('active') || this.$element.parent().length == 0) {                editor.removeClass('active')                if (isHideable) {                    // Check for editable elements                    if (editable.el != null) {                        // Build the original element                        var oldElement = $('<' + editable.type + '/>'),                            content = this.getContent(),                            currentContent = (typeof markdown == 'object') ? markdown.toHTML(content) : content                        $(editable.attrKeys).each(function (k, v) {                            oldElement.attr(editable.attrKeys[k], editable.attrValues[k])                        })                        // Get the editor content                        oldElement.html(currentContent)                        editor.replaceWith(oldElement)                    } else {                        editor.hide()                    }                }                // Trigger the onBlur hook                options.onBlur(this)            }            return this        }    }    /* MARKDOWN PLUGIN DEFINITION     * ========================== */    var old = $.fn.markdown    $.fn.markdown = function (option) {        return this.each(function () {            var $this = $(this),                data = $this.data('markdown'),                options = typeof option == 'object' && option            if (!data) $this.data('markdown', (data = new Markdown(this, options)))        })    }    $.fn.markdown.messages = {}    $.fn.markdown.defaults = {        /* Editor Properties */        autofocus: false,        hideable: false,        savable: false,        width: 'inherit',        height: 'inherit',        resize: 'none',        iconlibrary: 'glyph',        language: 'zh',        initialstate: 'editor',        /* Buttons Properties */        buttons: [      [{                name: 'groupFont',                data: [{                    name: 'cmdBold',                    hotkey: 'Ctrl+B',                    title: 'Bold',                    icon: {                        glyph: 'glyphicon glyphicon-bold',                        fa: 'fa fa-bold',                        'fa-3': 'icon-bold'                    },                    callback: function (e) {                        // Give/remove ** surround the selection                        var chunk, cursor, selected = e.getSelection(),                            content = e.getContent()                        if (selected.length == 0) {                            // Give extra word                            chunk = e.__localize('strong text')                        } else {                            chunk = selected.text                        }                        // transform selection and set the cursor into chunked text                        if (content.substr(selected.start - 2, 2) == '**' && content.substr(selected.end, 2) == '**') {                            e.setSelection(selected.start - 2, selected.end + 2)                            e.replaceSelection(chunk)                            cursor = selected.start - 2                        } else {                            e.replaceSelection('**' + chunk + '**')                            cursor = selected.start + 2                        }                        // Set the cursor                        e.setSelection(cursor, cursor + chunk.length)                    }        }, {                    name: 'cmdItalic',                    title: 'Italic',                    hotkey: 'Ctrl+I',                    icon: {                        glyph: 'glyphicon glyphicon-italic',                        fa: 'fa fa-italic',                        'fa-3': 'icon-italic'                    },                    callback: function (e) {                        // Give/remove * surround the selection                        var chunk, cursor, selected = e.getSelection(),                            content = e.getContent()                        if (selected.length == 0) {                            // Give extra word                            chunk = e.__localize('emphasized text')                        } else {                            chunk = selected.text                        }                        // transform selection and set the cursor into chunked text                        if (content.substr(selected.start - 1, 1) == '_' && content.substr(selected.end, 1) == '_') {                            e.setSelection(selected.start - 1, selected.end + 1)                            e.replaceSelection(chunk)                            cursor = selected.start - 1                        } else {                            e.replaceSelection('_' + chunk + '_')                            cursor = selected.start + 1                        }                        // Set the cursor                        e.setSelection(cursor, cursor + chunk.length)                    }        }, {                    name: 'cmdHeading',                    title: 'Heading',                    hotkey: 'Ctrl+H',                    icon: {                        glyph: 'glyphicon glyphicon-header',                        fa: 'fa fa-header',                        'fa-3': 'icon-font'                    },                    callback: function (e) {                        // Append/remove ### surround the selection                        var chunk, cursor, selected = e.getSelection(),                            content = e.getContent(),                            pointer, prevChar                        if (selected.length == 0) {                            // Give extra word                            chunk = e.__localize('heading text')                        } else {                            chunk = selected.text + '\n';                        }                        // transform selection and set the cursor into chunked text                        if ((pointer = 4, content.substr(selected.start - pointer, pointer) == '### ') || (pointer = 3, content.substr(selected.start - pointer, pointer) == '###')) {                            e.setSelection(selected.start - pointer, selected.end)                            e.replaceSelection(chunk)                            cursor = selected.start - pointer                        } else if (selected.start > 0 && (prevChar = content.substr(selected.start - 1, 1), !!prevChar && prevChar != '\n')) {                            e.replaceSelection('\n\n### ' + chunk)                            cursor = selected.start + 6                        } else {                            // Empty string before element                            e.replaceSelection('### ' + chunk)                            cursor = selected.start + 4                        }                        // Set the cursor                        e.setSelection(cursor, cursor + chunk.length)                    }        }]      }, {                name: 'groupLink',                data: [{                    name: 'cmdUrl',                    title: 'URL/Link',                    hotkey: 'Ctrl+L',                    icon: {                        glyph: 'glyphicon glyphicon-link',                        fa: 'fa fa-link',                        'fa-3': 'icon-link'                    },                    callback: function (e) {                        // Give [] surround the selection and prepend the link                        var chunk, cursor, selected = e.getSelection(),                            content = e.getContent(),                            link                        if (selected.length == 0) {                            // Give extra word                            chunk = e.__localize('enter link description here')                        } else {                            chunk = selected.text                        }                        link = prompt(e.__localize('Insert Hyperlink'), 'http://')                        if (link != null && link != '' && link != 'http://' && link.substr(0, 4) == 'http') {                            var sanitizedLink = $('<div>' + link + '</div>').text()                            // transform selection and set the cursor into chunked text                            e.replaceSelection('[' + chunk + '](' + sanitizedLink + ')')                            cursor = selected.start + 1                            // Set the cursor                            e.setSelection(cursor, cursor + chunk.length)                        }                    }        }, {                    name: 'cmdImage',                    title: 'Image',                    hotkey: 'Ctrl+G',                    icon: {                        glyph: 'glyphicon glyphicon-picture',                        fa: 'fa fa-picture-o',                        'fa-3': 'icon-picture'                    },                    callback: function (e) {                        // Give ![] surround the selection and prepend the image link                        var chunk, cursor, selected = e.getSelection(),                            content = e.getContent(),                            link                        if (selected.length == 0) {                            // Give extra word                            chunk = e.__localize('enter image description here')                        } else {                            chunk = selected.text                        }                        link = prompt(e.__localize('Insert Image Hyperlink'), 'http://')                        if (link != null && link != '' && link != 'http://' && link.substr(0, 4) == 'http') {                            var sanitizedLink = $('<div>' + link + '</div>').text()                            // transform selection and set the cursor into chunked text                            e.replaceSelection(' + '")')                            cursor = selected.start + 2                            // Set the next tab                            e.setNextTab(e.__localize('enter image title here'))                            // Set the cursor                            e.setSelection(cursor, cursor + chunk.length)                        }                    }        }]      }, {                name: 'groupMisc',                data: [{                        name: 'cmdList',                        hotkey: 'Ctrl+U',                        title: 'Unordered List',                        icon: {                            glyph: 'glyphicon glyphicon-list',                            fa: 'fa fa-list',                            'fa-3': 'icon-list-ul'                        },                        callback: function (e) {                            // Prepend/Give - surround the selection                            var chunk, cursor, selected = e.getSelection(),                                content = e.getContent()                            // transform selection and set the cursor into chunked text                            if (selected.length == 0) {                                // Give extra word                                chunk = e.__localize('list text here')                                e.replaceSelection('- ' + chunk)                                // Set the cursor                                cursor = selected.start + 2                            } else {                                if (selected.text.indexOf('\n') < 0) {                                    chunk = selected.text                                    e.replaceSelection('- ' + chunk)                                    // Set the cursor                                    cursor = selected.start + 2                                } else {                                    var list = []                                    list = selected.text.split('\n')                                    chunk = list[0]                                    $.each(list, function (k, v) {                                        list[k] = '- ' + v                                    })                                    e.replaceSelection('\n\n' + list.join('\n'))                                    // Set the cursor                                    cursor = selected.start + 4                                }                            }                            // Set the cursor                            e.setSelection(cursor, cursor + chunk.length)                        }        },                    {                        name: 'cmdListO',                        hotkey: 'Ctrl+O',                        title: 'Ordered List',                        icon: {                            glyph: 'glyphicon glyphicon-th-list',                            fa: 'fa fa-list-ol',                            'fa-3': 'icon-list-ol'                        },                        callback: function (e) {                            // Prepend/Give - surround the selection                            var chunk, cursor, selected = e.getSelection(),                                content = e.getContent()                            // transform selection and set the cursor into chunked text                            if (selected.length == 0) {                                // Give extra word                                chunk = e.__localize('list text here')                                e.replaceSelection('1. ' + chunk)                                // Set the cursor                                cursor = selected.start + 3                            } else {                                if (selected.text.indexOf('\n') < 0) {                                    chunk = selected.text                                    e.replaceSelection('1. ' + chunk)                                    // Set the cursor                                    cursor = selected.start + 3                                } else {                                    var list = []                                    list = selected.text.split('\n')                                    chunk = list[0]                                    $.each(list, function (k, v) {                                        list[k] = '1. ' + v                                    })                                    e.replaceSelection('\n\n' + list.join('\n'))                                    // Set the cursor                                    cursor = selected.start + 5                                }                            }                            // Set the cursor                            e.setSelection(cursor, cursor + chunk.length)                        }        },                    {                        name: 'cmdCode',                        hotkey: 'Ctrl+K',                        title: 'Code',                        icon: {                            glyph: 'glyphicon glyphicon-asterisk',                            fa: 'fa fa-code',                            'fa-3': 'icon-code'                        },                        callback: function (e) {                            // Give/remove ** surround the selection                            var chunk, cursor, selected = e.getSelection(),                                content = e.getContent()                            if (selected.length == 0) {                                // Give extra word                                chunk = e.__localize('code text here')                            } else {                                chunk = selected.text                            }                            // transform selection and set the cursor into chunked text                            if (content.substr(selected.start - 1, 1) == '`' && content.substr(selected.end, 1) == '`') {                                e.setSelection(selected.start - 1, selected.end + 1)                                e.replaceSelection(chunk)                                cursor = selected.start - 1                            } else {                                e.replaceSelection('`' + chunk + '`')                                cursor = selected.start + 1                            }                            // Set the cursor                            e.setSelection(cursor, cursor + chunk.length)                        }        },                    {                        name: 'cmdQuote',                        hotkey: 'Ctrl+Q',                        title: 'Quote',                        icon: {                            glyph: 'glyphicon glyphicon-comment',                            fa: 'fa fa-quote-left',                            'fa-3': 'icon-quote-left'                        },                        callback: function (e) {                            // Prepend/Give - surround the selection                            var chunk, cursor, selected = e.getSelection(),                                content = e.getContent()                            // transform selection and set the cursor into chunked text                            if (selected.length == 0) {                                // Give extra word                                chunk = e.__localize('quote here')                                e.replaceSelection('> ' + chunk)                                // Set the cursor                                cursor = selected.start + 2                            } else {                                if (selected.text.indexOf('\n') < 0) {                                    chunk = selected.text                                    e.replaceSelection('> ' + chunk)                                    // Set the cursor                                    cursor = selected.start + 2                                } else {                                    var list = []                                    list = selected.text.split('\n')                                    chunk = list[0]                                    $.each(list, function (k, v) {                                        list[k] = '> ' + v                                    })                                    e.replaceSelection('\n\n' + list.join('\n'))                                    // Set the cursor                                    cursor = selected.start + 4                                }                            }                            // Set the cursor                            e.setSelection(cursor, cursor + chunk.length)                        }        }]      }, {                name: 'groupUtil',                data: [{                    name: 'cmdPreview',                    toggle: true,                    hotkey: 'Ctrl+P',                    title: 'Preview',                    btnText: 'Preview',                    btnClass: 'btn btn-sm',                    icon: {                        glyph: 'glyphicon glyphicon-search',                        fa: 'fa fa-search',                        'fa-3': 'icon-search'                    },                    callback: function (e) {                        // Check the preview mode and toggle based on this flag                        var isPreview = e.$isPreview,                            content                        if (isPreview == false) {                            // Give flag that tell the editor enter preview mode                            e.showPreview()                        } else {                            e.hidePreview()                        }                    }        }]      }]    ],        additionalButtons: [], // Place to hook more buttons by code        reorderButtonGroups: [],        hiddenButtons: [], // Default hidden buttons        disabledButtons: [], // Default disabled buttons        footer: '',        fullscreen: {            enable: true,            icons: {                fullscreenOn: {                    fa: 'fa fa-expand',                    glyph: 'glyphicon glyphicon-fullscreen',                    'fa-3': 'icon-resize-full'                },                fullscreenOff: {                    fa: 'fa fa-compress',                    glyph: 'glyphicon glyphicon-fullscreen',                    'fa-3': 'icon-resize-small'                }            }        },        /* Events hook */        onShow: function (e) {},        onPreview: function (e) {},        onSave: function (e) {},        onBlur: function (e) {},        onFocus: function (e) {},        onChange: function (e) {},        onFullscreen: function (e) {}    }    $.fn.markdown.Constructor = Markdown    /* MARKDOWN NO CONFLICT     * ==================== */    $.fn.markdown.noConflict = function () {        $.fn.markdown = old        return this    }    /* MARKDOWN GLOBAL FUNCTION & DATA-API     * ==================================== */    var initMarkdown = function (el) {        var $this = el        if ($this.data('markdown')) {            $this.data('markdown').showEditor()            return        }        $this.markdown()    }    var blurNonFocused = function (e) {        var $activeElement = $(document.activeElement)        // Blur event        $(document).find('.md-editor').each(function () {            var $this = $(this),                focused = $activeElement.closest('.md-editor')[0] === this,                attachedMarkdown = $this.find('textarea').data('markdown') ||                $this.find('div[data-provider="markdown-preview"]').data('markdown')            if (attachedMarkdown && !focused) {                attachedMarkdown.blur()            }        })    }    $(document)        .on('click.markdown.data-api', '[data-provide="markdown-editable"]', function (e) {            initMarkdown($(this))            e.preventDefault()        })        .on('click focusin', function (e) {            blurNonFocused(e)        })        .ready(function () {            $('textarea[data-provide="markdown"]').each(function () {                initMarkdown($(this))            })        })}(window.jQuery);
 |