forked from code-dot-org/code-dot-org
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinitializeCodeMirror.js
More file actions
148 lines (135 loc) · 4.73 KB
/
Copy pathinitializeCodeMirror.js
File metadata and controls
148 lines (135 loc) · 4.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/**
* @file Function that initializes a CodeMirror editor in place of a textarea.
*/
/* global inlineAttach */
import $ from 'jquery';
import CodeMirror from 'codemirror';
import CodeMirrorSpellChecker from 'codemirror-spell-checker';
import 'codemirror/mode/markdown/markdown';
import 'codemirror/addon/edit/closetag';
import 'codemirror/addon/edit/matchtags';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/edit/trailingspace';
import 'codemirror/addon/mode/overlay';
import 'codemirror/addon/fold/xml-fold';
import 'codemirror/addon/lint/lint';
import 'codemirror/addon/lint/javascript-lint';
import 'codemirror/mode/xml/xml';
import 'codemirror/mode/javascript/javascript';
import './vendor/codemirror.inline-attach';
import {JSHINT} from 'jshint';
import React from 'react';
import ReactDOM from 'react-dom';
import SafeMarkdown from '../templates/SafeMarkdown';
window.JSHINT = JSHINT;
CodeMirrorSpellChecker({
codeMirrorInstance: CodeMirror,
});
/**
* initializeCodeMirror replaces a textarea on the page with a full-featured
* CodeMirror editor.
* @param {!string|!Element} target - element or id of element to replace.
* @param {!string} mode - editor syntax mode
* @param {Object} options - misc optional arguments
* @param {function} [options.callback] - onChange callback for editor
* @param {boolean} [options.attachments] - whether to enable attachment
* uploading in this editor.
* @param {function} [options.onUpdateLinting] - callback called when linting is finished
* @param {function} [options.getAnnotations] - optional function to override linting behavior
* @param {(string|Element)} [options.preview] - element or id of element to
* populate with a preview. If none specified, will look for an element
* by appending "_preview" to the id of the target element.
*/
function initializeCodeMirror(target, mode, options = {}) {
let {callback, attachments, onUpdateLinting, additionalAnnotations, preview} =
options;
let updatePreview;
// Code mirror parses html using xml mode
var htmlMode = false;
if (mode === 'html') {
mode = 'xml';
htmlMode = true;
}
var node = target.nodeType ? target : document.getElementById(target);
var backdrop = undefined;
if (mode === 'markdown') {
backdrop = mode;
mode = 'spell-checker';
// In markdown mode, look for a preview element (found by just appending
// _preview to the target id), if it exists extend our callback to update
// the preview element with the markdown contents
preview = preview || `#${node.id}_preview`;
const previewElement = preview.nodeType ? preview : $(preview).get(0);
if (previewElement) {
const originalCallback = callback;
updatePreview = editor => {
ReactDOM.render(
React.createElement(SafeMarkdown, {
markdown: editor.getValue(),
}),
previewElement
);
};
callback = (editor, ...rest) => {
if (originalCallback) {
originalCallback(editor, ...rest);
}
updatePreview(editor);
};
}
}
const getAnnotations = (text, options, cm) => {
const cmValidation = cm.getHelper(CodeMirror.Pos(0, 0), 'lint')(
text,
{},
cm
);
const additionalValidation = additionalAnnotations
? additionalAnnotations(text, options, cm)
: [];
return [...cmValidation, ...additionalValidation];
};
var editor = CodeMirror.fromTextArea(node, {
mode: mode,
backdrop: backdrop,
htmlMode: htmlMode,
viewportMargin: Infinity,
matchTags: {bothTags: true},
autoCloseTags: true,
showTrailingSpace: true,
lineWrapping: true,
gutters: ['CodeMirror-lint-markers'],
lint: {
getAnnotations: additionalAnnotations ? getAnnotations : undefined,
onUpdateLinting,
},
});
if (callback) {
editor.on('change', callback);
}
if (updatePreview) {
updatePreview(editor);
}
if (attachments) {
// default options are for markdown mode
var attachOptions = {
uploadUrl: '/level_assets/upload',
uploadFieldName: 'file',
downloadFieldName: 'newAssetUrl',
allowedTypes: ['image/jpeg', 'image/png', 'image/jpg', 'image/gif'],
progressText: '![Uploading file...]()',
urlText: '', // `{filename}` tag gets replaced with URL
errorText: 'Error uploading file; images must be no larger than 2MB',
extraHeaders: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
},
};
if (mode === 'javascript') {
attachOptions.progressText = '"Uploading file..."';
attachOptions.urlText = '"{filename}"';
}
inlineAttach.attachToCodeMirror(editor, attachOptions);
}
return editor;
}
module.exports = initializeCodeMirror;