mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-02 08:13:34 +03:00
2151d8a49f
closes #2426, closes #2781, closes #2913 - Concatenate vendor files on change of js in core/shared/ - Add all the markerManager stuff to its own mixin - make markers a shared object for all that mix it in. makes it easier to use helper functions in different modules - add getMarkdown method, returns object with two keys holding the markdown: one with markers, the other without - Clear markers when codemirror is destroyed - make Editor subcomponents communicate through the Editor Controller - Set Codemirror and html preview shared scrolling - Set CodeMirror, html preview css scroll class with util - Create 'scratch' property in Editor controller; prevents a model save wiping image markers due to markdown bindings - Add editor and html preview actions to handle img upload start/finish - disable codemirror when an image is being uploaded, enables on success or failure - Fix editor wordcount when there are 0 words - Add modal dialog when transitioning out of the editor with an unsaved post - Add window.onbeforeunload handling with `.unloadDirtyMessage()` on editor controller - and various other things
220 lines
7.0 KiB
JavaScript
220 lines
7.0 KiB
JavaScript
var MarkerManager = Ember.Mixin.create({
|
|
imageMarkdownRegex: /^(?:\{<(.*?)>\})?!(?:\[([^\n\]]*)\])(?:\(([^\n\]]*)\))?$/gim,
|
|
markerRegex: /\{<([\w\W]*?)>\}/,
|
|
|
|
uploadId: 1,
|
|
|
|
// create an object that will be shared amongst instances.
|
|
// makes it easier to use helper functions in different modules
|
|
markers: {},
|
|
|
|
// Add markers to the line if it needs one
|
|
initMarkers: function (line) {
|
|
var imageMarkdownRegex = this.get('imageMarkdownRegex'),
|
|
markerRegex = this.get('markerRegex'),
|
|
editor = this.get('codemirror'),
|
|
isImage = line.text.match(imageMarkdownRegex),
|
|
hasMarker = line.text.match(markerRegex);
|
|
|
|
if (isImage && !hasMarker) {
|
|
this.addMarker(line, editor.getLineNumber(line));
|
|
}
|
|
},
|
|
|
|
// Get the markdown with all the markers stripped
|
|
getMarkdown: function (value) {
|
|
var marker, id,
|
|
editor = this.get('codemirror'),
|
|
markers = this.get('markers'),
|
|
markerRegexForId = this.get('markerRegexForId'),
|
|
oldValue = value || editor.getValue(),
|
|
newValue = oldValue;
|
|
|
|
for (id in markers) {
|
|
if (markers.hasOwnProperty(id)) {
|
|
marker = markers[id];
|
|
newValue = newValue.replace(markerRegexForId(id), '');
|
|
}
|
|
}
|
|
|
|
return {
|
|
withMarkers: oldValue,
|
|
withoutMarkers: newValue
|
|
};
|
|
},
|
|
|
|
// check the given line to see if it has an image, and if it correctly has a marker
|
|
// in the special case of lines which were just pasted in, any markers are removed to prevent duplication
|
|
checkLine: function (ln, mode) {
|
|
var editor = this.get('codemirror'),
|
|
line = editor.getLineHandle(ln),
|
|
imageMarkdownRegex = this.get('imageMarkdownRegex'),
|
|
markerRegex = this.get('markerRegex'),
|
|
isImage = line.text.match(imageMarkdownRegex),
|
|
hasMarker;
|
|
|
|
// We care if it is an image
|
|
if (isImage) {
|
|
hasMarker = line.text.match(markerRegex);
|
|
|
|
if (hasMarker && (mode === 'paste' || mode === 'undo')) {
|
|
// this could be a duplicate, and won't be a real marker
|
|
this.stripMarkerFromLine(line);
|
|
}
|
|
|
|
if (!hasMarker) {
|
|
this.addMarker(line, ln);
|
|
}
|
|
}
|
|
// TODO: hasMarker but no image?
|
|
},
|
|
|
|
// Add a marker to the given line
|
|
// Params:
|
|
// line - CodeMirror LineHandle
|
|
// ln - line number
|
|
addMarker: function (line, ln) {
|
|
var marker,
|
|
markers = this.get('markers'),
|
|
editor = this.get('codemirror'),
|
|
uploadPrefix = 'image_upload',
|
|
uploadId = this.get('uploadId'),
|
|
magicId = '{<' + uploadId + '>}',
|
|
newText = magicId + line.text;
|
|
|
|
editor.replaceRange(
|
|
newText,
|
|
{line: ln, ch: 0},
|
|
{line: ln, ch: newText.length}
|
|
);
|
|
|
|
marker = editor.markText(
|
|
{line: ln, ch: 0},
|
|
{line: ln, ch: (magicId.length)},
|
|
{collapsed: true}
|
|
);
|
|
|
|
markers[uploadPrefix + '_' + uploadId] = marker;
|
|
this.set('uploadId', uploadId += 1);
|
|
},
|
|
|
|
// Check each marker to see if it is still present in the editor and if it still corresponds to image markdown
|
|
// If it is no longer a valid image, remove it
|
|
checkMarkers: function () {
|
|
var id, marker, line,
|
|
editor = this.get('codemirror'),
|
|
markers = this.get('markers'),
|
|
imageMarkdownRegex = this.get('imageMarkdownRegex');
|
|
|
|
for (id in markers) {
|
|
if (markers.hasOwnProperty(id)) {
|
|
marker = markers[id];
|
|
|
|
if (marker.find()) {
|
|
line = editor.getLineHandle(marker.find().from.line);
|
|
if (!line.text.match(imageMarkdownRegex)) {
|
|
this.removeMarker(id, marker, line);
|
|
}
|
|
} else {
|
|
this.removeMarker(id, marker);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// this is needed for when we transition out of the editor.
|
|
// since the markers object is persistent and shared between classes that
|
|
// mix in this mixin, we need to make sure markers don't carry over between edits.
|
|
clearMarkers: function () {
|
|
var markers = this.get('markers'),
|
|
id,
|
|
marker;
|
|
|
|
// can't just `this.set('markers', {})`,
|
|
// since it wouldn't apply to this mixin,
|
|
// but only to the class that mixed this mixin in
|
|
for (id in markers) {
|
|
if (markers.hasOwnProperty(id)) {
|
|
marker = markers[id];
|
|
delete markers[id];
|
|
marker.clear();
|
|
}
|
|
}
|
|
},
|
|
|
|
// Remove a marker
|
|
// Will be passed a LineHandle if we already know which line the marker is on
|
|
removeMarker: function (id, marker, line) {
|
|
var markers = this.get('markers');
|
|
|
|
delete markers[id];
|
|
marker.clear();
|
|
|
|
if (line) {
|
|
this.stripMarkerFromLine(line);
|
|
} else {
|
|
this.findAndStripMarker(id);
|
|
}
|
|
},
|
|
|
|
// Removes the marker on the given line if there is one
|
|
stripMarkerFromLine: function (line) {
|
|
var ln,
|
|
editor = this.get('codemirror'),
|
|
markerRegex = /\{<([\w\W]*?)>\}/,
|
|
markerText = line.text.match(markerRegex);
|
|
|
|
ln = editor.getLineNumber(line);
|
|
|
|
if (markerText) {
|
|
editor.replaceRange(
|
|
'',
|
|
{line: ln, ch: markerText.index},
|
|
{line: ln, ch: markerText.index + markerText[0].length}
|
|
);
|
|
}
|
|
},
|
|
|
|
// the regex
|
|
markerRegexForId: function (id) {
|
|
id = id.replace('image_upload_', '');
|
|
return new RegExp('\\{<' + id + '>\\}', 'gmi');
|
|
},
|
|
|
|
// Find a marker in the editor by id & remove it
|
|
// Goes line by line to find the marker by it's text if we've lost track of the TextMarker
|
|
findAndStripMarker: function (id) {
|
|
var self = this,
|
|
editor = this.get('codemirror');
|
|
|
|
editor.eachLine(function (line) {
|
|
var markerText = self.markerRegexForId(id).exec(line.text),
|
|
ln;
|
|
|
|
if (markerText) {
|
|
ln = editor.getLineNumber(line);
|
|
editor.replaceRange(
|
|
'',
|
|
{line: ln, ch: markerText.index},
|
|
{line: ln, ch: markerText.index + markerText[0].length}
|
|
);
|
|
}
|
|
});
|
|
},
|
|
|
|
// Find the line with the marker which matches
|
|
findLine: function (result_id) {
|
|
var editor = this.get('codemirror'),
|
|
markers = this.get('markers');
|
|
|
|
// try to find the right line to replace
|
|
if (markers.hasOwnProperty(result_id) && markers[result_id].find()) {
|
|
return editor.getLineHandle(markers[result_id].find().from.line);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
});
|
|
|
|
export default MarkerManager;
|