diff --git a/example/index.html b/example/index.html index f464984..484ad97 100644 --- a/example/index.html +++ b/example/index.html @@ -15,7 +15,7 @@ diff --git a/src/js/easymde.js b/src/js/easymde.js index f1268aa..199c1ae 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -1825,9 +1825,14 @@ function EasyMDE(options) { if ( options.parsingConfig.headingLevels ) { var headingLevels = []; for ( var l = 0, requestedLevels = options.parsingConfig.headingLevels; l < requestedLevels.length; l++ ) { - if ( ! isNaN( requestedLevels[ l ] ) && headingLevels.indexOf( requestedLevels[ l ] ) === -1 ) { - headingLevels[ l ] = parseInt( requestedLevels[ l ], 10 ); + requestedLevels[ l ] = parseInt( requestedLevels[ l ], 10 ); + if ( isNaN( requestedLevels[ l ] ) || headingLevels.indexOf( requestedLevels[ l ] ) !== -1 ) { + continue; } + if ( requestedLevels[ l ] < 1 && requestedLevels[ l ] > 6 ) { + continue; + } + headingLevels[ l ] = requestedLevels[ l ]; } options.parsingConfig.headingLevels = headingLevels.sort(); } @@ -2199,28 +2204,165 @@ EasyMDE.prototype.render = function (el) { }); } if (options.parsingConfig.headingLevels) { - this.codemirror.on('beforeChange', function (cm, obj) { - if (!obj || !obj.from || !obj.to) { + // If the *headingLevels* argument is present, set our custom modifiers + var makeBiggerHeadline = function(headline, from, to) { + headline = headline || ''; + if (!from || !to) { + return ''; + } + var level = ''; + while (from < to) { + level += '#'; + from++; + } + level += ' '; console.log(level); + return /#/.test(headline) ? headline.replace(/#\s*/, level) : level; + } + var headlineNeedUpdate = function(myCurrHeadline, allowedHeadlingLevels) { + if (!myCurrHeadline || !allowedHeadlingLevels) { return false; } - if (obj.from.line !== obj.to.line) { + if (!/^[#]+/.test(myCurrHeadline.trim())){ return false; } - if (!obj.from.ch || !obj.to.ch || obj.to.ch > 6) { - return false; - } - if (!obj.text || obj.text.length !== 1 || obj.text[0] !== ' ') { - return false; - } - var myText = cm.getRange({line: obj.from.line, ch: 0}, {line: obj.to.line, ch: obj.to.ch}); - if (!/^#+$/.test(myText.trim())){ - return false; - } - var allowedHeadlingLevels = cm.options.backdrop.headingLevels, - currHeadlingLevel = (myText.match(/#/g) || []).length; + var currHeadlingLevel = ((myCurrHeadline || '').match(/#/g) || []).length; if (allowedHeadlingLevels.indexOf(currHeadlingLevel) !== -1) { return false; } + var newHeadingLevel = 0, n = 0; + while (n < allowedHeadlingLevels.length) { + if (allowedHeadlingLevels[n] > currHeadlingLevel) { + newHeadingLevel = allowedHeadlingLevels[n]; + break; + } + n++; + } + if (!newHeadingLevel) { + n = allowedHeadlingLevels.length - 1; + while (n > -1) { + if (allowedHeadlingLevels[n] < currHeadlingLevel) { + newHeadingLevel = allowedHeadlingLevels[n]; + break; + } + n--; + } + } + if (!newHeadingLevel || newHeadingLevel === currHeadlingLevel) { + return false; + } + return { + from: currHeadlingLevel, + to: newHeadingLevel, + }; + }; + var checkNewHeadline = function(cm, obj) { + var myHeadline = cm.getRange({ + line: obj.from.line, + ch: 0, + }, { + line: obj.to.line, + ch: obj.to.ch, + }); + var levels = headlineNeedUpdate(myHeadline, cm.options.backdrop.headingLevels); + if (!levels || !levels.from || !levels.to) { + return false; + } + if (obj.from.line === obj.to.line) { + // Most simple case when a modification has occured on a single line + if (levels.to > levels.from) { + // Current level is forbidden so we jump to the closest upper level allowed + // We only need to update the text value before the modification is applied + obj.text[0] = makeBiggerHeadline('', levels.from, levels.to); + } + else { + // The current level is forbidden and we jump to the closest lower level available + // A bit more complicated: we have to cancel the update and trigger a replacement with the existing string + obj.cancel(); + var newHeadline = ''; + while (levels.to > 0) { + newHeadline += '#'; + levels.to--; + } + newHeadline += ' '; + cm.doc.replaceRange(newHeadline, { + line: obj.from.line, + ch: 0, + }, { + line: obj.to.line, + ch: obj.to.ch, + }, myHeadline ); + } + } + return true; + }; + var checkExistingHeadline = function(cm, obj) { + var myChar = cm.getRange({ + line: obj.from.line, + ch: obj.from.ch, + }, { + line: obj.to.line, + ch: obj.to.ch + 1, + }); + console.log( '==' + myChar + '==' ); + if (!/\s|#/.test(myChar || '')) { + return false; + } + var myText = cm.getRange({line: obj.from.line, ch: 0}, {line: obj.to.line, ch: 8}); + console.log( myText ); + if ((obj.from.line === obj.to.line) && obj.text.length === 1 && obj.text[0] === '#') { + if (!/[^\s#]/.test(myText)) { + // Newly created, skip the check for now + return false; + } + var levels = headlineNeedUpdate(myText.replace(/#/, '##'), cm.options.backdrop.headingLevels); + if (!levels || !levels.from || !levels.to) { + return false; + } + if (levels.to > levels.from) { + obj.text[0] = '#'; + while (levels.from < levels.to) { + obj.text[0] += '#'; + levels.from++; + } + return true; + } + } + }; + this.codemirror.on('beforeChange', function (cm, obj) { + console.log(obj); + if (!obj || !obj.from || !obj.to || !obj.text) { + // Don't go further 'cause a required argument is missing... + return false; + } + if ((obj.from.line === obj.to.line) && obj.to.ch > 6) { + // No need to trigger a check if we are on the same line at character 7 or upper + // As we are sure the cursor is not inside a markdown header + return false; + } + if (/input/.test(obj.origin || '+input')) { // Something was added + if (!obj.text.length || !obj.text[0].length) { + // Just in case + return false; + } + if (obj.text.length === 1 && obj.text[0].length === 1) { + // Only one character in one line is being updated + if (obj.text[0] === ' ') { + return checkNewHeadline(cm, obj); + } + else if (obj.text[0] === '#') { + return checkExistingHeadline(cm, obj); + } + else { + return false; + } + } + else { + + } + } + else if (/delete/.test(obj.origin)) { // Something was removed + return checkExistingHeadline(cm, obj); + } }); }