diff --git a/src/js/easymde.js b/src/js/easymde.js index 8e73d6e..5eb37c8 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -1124,6 +1124,13 @@ function _toggleHeading(cm, direction, size) { var startPoint = cm.getCursor('start'); var endPoint = cm.getCursor('end'); + var sharpLevels = cm.options.backdrop.headingLevels || [], + minLevel = sharpLevels.length ? sharpLevels[0] : 1, + maxLevel = sharpLevels.length ? sharpLevels[sharpLevels.length-1] : 6; + if (size && sharpLevels.length && sharpLevels.indexOf(size) === -1) { + cm.focus(); + return false; + } for (var i = startPoint.line; i <= endPoint.line; i++) { (function (i) { var text = cm.getLine(i); @@ -1132,14 +1139,20 @@ function _toggleHeading(cm, direction, size) { if (direction !== undefined) { if (currHeadingLevel <= 0) { if (direction == 'bigger') { - text = '###### ' + text; + text = '#'.repeat(maxLevel) + ' ' + text; } else { - text = '# ' + text; + text = '#'.repeat(minLevel) + ' ' + text; + } + } else if (currHeadingLevel == maxLevel && direction == 'smaller') { + text = text.substr(maxLevel + 1); + } else if (currHeadingLevel == minLevel && direction == 'bigger') { + text = text.substr(minLevel + 1); + } else if (sharpLevels.length) { + if (direction == 'bigger') { + text = '#'.repeat(sharpLevels[sharpLevels.indexOf(currHeadingLevel)-1]) + text.replace(/^[#]+/, '' ); + } else { + text = '#'.repeat(sharpLevels[sharpLevels.indexOf(currHeadingLevel)+1]) + text.replace(/^[#]+/, '' ); } - } else if (currHeadingLevel == 6 && direction == 'smaller') { - text = text.substr(7); - } else if (currHeadingLevel == 1 && direction == 'bigger') { - text = text.substr(2); } else { if (direction == 'bigger') { text = text.substr(1); @@ -2205,108 +2218,138 @@ EasyMDE.prototype.render = function (el) { } if (options.parsingConfig.headingLevels) { // If the *headingLevels* argument is present, set our custom modifiers - var makeBiggerHeadline = function(headline, from, to) { - headline = headline || ''; - if (!from || !to) { + var headingMakeBigger = function(heading, from, to) { + heading = heading || ''; + if (!from || !to || from >= to) { return ''; } var level = ''; - while (from < to) { - level += '#'; - from++; + if (!heading.length) { + while (from < to) { + level += '#'; + from++; + } + level += ' '; + return level; + } else { + while (to > 0) { + level += '#'; + to--; + } + level += ' '; + return /#/.test(heading) ? heading.replace(/^[#]+\s*/, level) : level; } - level += ' '; - return /#/.test(headline) ? headline.replace(/#\s*/, level) : level; }; - var headlineNeedUpdate = function(myCurrHeadline, allowedHeadlingLevels, levelSearchDir) { - if (!myCurrHeadline || !allowedHeadlingLevels) { + var headingMakeSmaller = function(heading, from, to) { + heading = heading || ''; + if (!from || !to || from <= to) { + return ''; + } + var level = ''; + if (!heading.length) { + while (from > to) { + level += '#'; + from--; + } + level += ' '; + return level; + } else { + while (to > 0) { + level += '#'; + to--; + } + level += ' '; + return /#/.test(heading) ? heading.replace(/^[#]+\s*/, level) : level; + } + }; + var headingNeedUpdate = function(currHeading, allowedHeadingLevels, levelSearchDir) { + if (!currHeading || !allowedHeadingLevels) { return false; } - myCurrHeadline = myCurrHeadline.trim(); - if (!/^#+/.test(myCurrHeadline)){ + currHeading = currHeading.trim(); + if (!/^#+/.test(currHeading)){ return false; } - var currHeadlingLevel = (myCurrHeadline.match(/^([#]+)/g) || [''])[0].length; - if (allowedHeadlingLevels.indexOf(currHeadlingLevel) !== -1) { + var currHeadingLevel = (currHeading.match(/^([#]+)/g) || [''])[0].length; + if (allowedHeadingLevels.indexOf(currHeadingLevel) !== -1) { return false; } levelSearchDir = levelSearchDir || 'asc'; var newHeadingLevel = -1, n = 0; if (levelSearchDir === 'asc') { - while (n < allowedHeadlingLevels.length) { - if (allowedHeadlingLevels[n] > currHeadlingLevel) { - newHeadingLevel = allowedHeadlingLevels[n]; + while (n < allowedHeadingLevels.length) { + if (allowedHeadingLevels[n] > currHeadingLevel) { + newHeadingLevel = allowedHeadingLevels[n]; break; } n++; } } if (newHeadingLevel < 0) { - n = allowedHeadlingLevels.length - 1; + n = allowedHeadingLevels.length - 1; while (n > -1) { - if (allowedHeadlingLevels[n] < currHeadlingLevel) { - newHeadingLevel = allowedHeadlingLevels[n]; + if (allowedHeadingLevels[n] < currHeadingLevel) { + newHeadingLevel = allowedHeadingLevels[n]; break; } n--; } if (levelSearchDir === 'dsc') { - currHeadlingLevel += 1; + currHeadingLevel += 1; if (newHeadingLevel < 0) { newHeadingLevel = 0; } } } - if (newHeadingLevel < 0 || newHeadingLevel === currHeadlingLevel) { + if (newHeadingLevel < 0 || newHeadingLevel === currHeadingLevel) { return false; } return { - from: currHeadlingLevel, + from: currHeadingLevel, to: newHeadingLevel, - diff: Math.abs(newHeadingLevel - currHeadlingLevel), + diff: Math.abs(newHeadingLevel - currHeadingLevel), }; }; - var checkNewHeadline = function(cm, obj) { - var myHeadline = cm.getRange({ + var headingCheckNew = function(cm, obj) { + var currHeading = 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) { + var myLevels = headingNeedUpdate(currHeading, cm.options.backdrop.headingLevels); + if (!myLevels || !myLevels.from || !myLevels.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) { + if (myLevels.to > myLevels.from) { // Current level is forbidden so we jump to the closest upper level allowed // We only need to reset the sharp numbers with the appropriate value before the modification is applied - obj.text[0] = makeBiggerHeadline('', levels.from, levels.to); - } - else { + obj.text[0] = headingMakeBigger('', myLevels.from, myLevels.to); + } else { // The current level is forbidden and we jump to the closest lower level available // A bit more is needed: we have to cancel the requested update and trigger a replacement with the existing sharp signs obj.cancel(); - var newHeadline = ''; - while (levels.to > 0) { - newHeadline += '#'; - levels.to--; + var newHeading = ''; + while (myLevels.to > 0) { + newHeading += '#'; + myLevels.to--; } - newHeadline += ' '; - cm.doc.replaceRange(newHeadline, { + newHeading += ' '; + cm.doc.replaceRange(newHeading, { line: obj.from.line, ch: 0, }, { line: obj.to.line, ch: obj.to.ch, - }, myHeadline ); // 4th arguments to keep a trace in the history when possible + }, currHeading ); // 4th arguments to keep a trace in the history when possible } } return true; }; - var checkExistingHeadline = function(cm, obj) { + var headingCheckExisting = function(cm, obj) { var myChar = cm.getRange({ line: obj.from.line, ch: obj.from.ch, @@ -2314,9 +2357,8 @@ EasyMDE.prototype.render = function (el) { line: obj.to.line, ch: obj.to.ch + 1, }); - // console.log( '==' + myChar + '==' ); if (!/\s|#/.test(myChar || '')) { - // Don't bother to go further if no headlines were detected + // Don't bother to go further if no headling were detected return false; } if ((obj.from.line === obj.to.line) && obj.text.length === 1) { @@ -2327,12 +2369,11 @@ EasyMDE.prototype.render = function (el) { return false; } if (!/#/.test(myText)) { - myText = '# ' + myText.trim(); // Wasn't headline - } - else { + myText = '# ' + myText.trim(); // Wasn't heading + } else { myText = myText.replace(/#/, '##'); // Increment one sharp sign - }; - myLevels = headlineNeedUpdate(myText, cm.options.backdrop.headingLevels); + } + myLevels = headingNeedUpdate(myText, cm.options.backdrop.headingLevels); if (!myLevels) { return false; } @@ -2348,7 +2389,7 @@ EasyMDE.prototype.render = function (el) { return true; } else if (/delete/.test(obj.origin) && obj.text[0] === '') { - myLevels = headlineNeedUpdate(myText.replace(/#/, ''), cm.options.backdrop.headingLevels, 'dsc'); + myLevels = headingNeedUpdate(myText.replace(/#/, ''), cm.options.backdrop.headingLevels, 'dsc'); if (!myLevels) { return false; } @@ -2378,50 +2419,95 @@ EasyMDE.prototype.render = function (el) { } } }; + var headingCheckRow = function(row, cm) { + if (!row || !/^#/.test(row.trim())) { + return row; + } + row = row.replace(/^(\s*)#/, '#'); + var myLevels = headingNeedUpdate(row, cm.options.backdrop.headingLevels); + if (!myLevels || !myLevels.from || !myLevels.to) { + return row; + } else if (myLevels.from < myLevels.to) { + return headingMakeBigger(row, myLevels.from, myLevels.to); + } else if (myLevels.to < myLevels.from) { + return headingMakeSmaller(row, myLevels.from, myLevels.to); + } + return row; + }; this.codemirror.on('beforeChange', function (cm, obj) { - console.log(obj); - if (!obj || !obj.from || !obj.to || !obj.text) { + // console.log(obj); + if (!obj || !obj.from || !obj.to || !obj.text || !obj.text.length) { // Don't go further 'cause a required argument is missing... return false; } - if ((obj.from.line === obj.to.line) ) { + if ((obj.from.line === obj.to.line) && obj.text.length < 2) { if (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 range containing a sharp sign + // As we are sure the cursor was not inside a range containing a sharp sign return false; } - if (obj.from.ch === 0 && obj.to.ch === 0 && /\s|\b/.test(obj.text[0] || '')) { - // Prevent space at the beginning of the line + if (obj.from.ch === 0 && obj.to.ch === 0 && /\s/.test(obj.text[0] || '')) { + // (Force) Prevent space at the beginning of the line obj.cancel(); return false; } } - if (!obj.text.length) { - // Just in case - return false; - } if (!obj.origin) { - // Just in case + // If a modification was triggered by a code and not an human + // The origin can be "undefined", so just in case set one. obj.origin = '+none'; } if (/input/.test(obj.origin)) { // Something was added if (obj.text.length === 1 && obj.text[0].length === 1) { // Only one character on 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; + return headingCheckNew(cm, obj); + } else if (obj.text[0] === '#') { + return headingCheckExisting(cm, obj); } } } else if (/delete/.test(obj.origin)) { // Something was removed if (obj.text.length === 1 && !obj.text[0].length) { // Only one character on one line has been removed - return checkExistingHeadline(cm, obj); + return headingCheckExisting(cm, obj); + } + } + if (obj.text.length < 2) { + return false; + } + // Multilines modification like a paste + var r; + if (obj.from.ch === 0) { + // Loop each row and check for headers + for (r=0; r 7) { + // We are sure an existing header is not involved + // So exclude the first row from the loop + for (r=1; r