diff --git a/services/web/Gruntfile.coffee b/services/web/Gruntfile.coffee
index 2b8cc27421..4104352e3d 100644
--- a/services/web/Gruntfile.coffee
+++ b/services/web/Gruntfile.coffee
@@ -1,5 +1,6 @@
fs = require "fs"
PackageVersions = require "./app/coffee/infrastructure/PackageVersions"
+Settings = require "settings-sharelatex"
require('es6-promise').polyfill()
module.exports = (grunt) ->
@@ -108,6 +109,7 @@ module.exports = (grunt) ->
"public/coffee/ide/editor/sharejs/vendor/client/microevent.coffee"
"public/coffee/ide/editor/sharejs/vendor/client/doc.coffee"
"public/coffee/ide/editor/sharejs/vendor/client/ace.coffee"
+ "public/coffee/ide/editor/sharejs/vendor/client/cm.coffee"
]
client:
@@ -152,6 +154,7 @@ module.exports = (grunt) ->
sourceMapBasepath: "public/stylesheets"
globalVars:
'is-overleaf': false
+ 'show-rich-text': Settings.showRichText
files:
"public/stylesheets/style.css": "public/stylesheets/style.less"
ol:
@@ -161,6 +164,7 @@ module.exports = (grunt) ->
sourceMapBasepath: "public/stylesheets"
globalVars:
'is-overleaf': true
+ 'show-rich-text': Settings.showRichText
files:
"public/stylesheets/ol-style.css": "public/stylesheets/ol-style.less"
diff --git a/services/web/Makefile b/services/web/Makefile
index 919a204880..e9c796b6a0 100644
--- a/services/web/Makefile
+++ b/services/web/Makefile
@@ -21,7 +21,8 @@ SHAREJS_COFFEE_FILES := \
public/coffee/ide/editor/sharejs/vendor/types/text-api.coffee \
public/coffee/ide/editor/sharejs/vendor/client/microevent.coffee \
public/coffee/ide/editor/sharejs/vendor/client/doc.coffee \
- public/coffee/ide/editor/sharejs/vendor/client/ace.coffee
+ public/coffee/ide/editor/sharejs/vendor/client/ace.coffee \
+ public/coffee/ide/editor/sharejs/vendor/client/cm.coffee
LESS_FILES := $(shell find public/stylesheets -name '*.less')
CSS_FILES := public/stylesheets/style.css public/stylesheets/ol-style.css
diff --git a/services/web/app/coffee/infrastructure/Features.coffee b/services/web/app/coffee/infrastructure/Features.coffee
index a49e0ec124..6e39ee6370 100644
--- a/services/web/app/coffee/infrastructure/Features.coffee
+++ b/services/web/app/coffee/infrastructure/Features.coffee
@@ -14,5 +14,7 @@ module.exports = Features =
return Settings.enableGithubSync
when 'v1-return-message'
return Settings.accountMerge? and Settings.overleaf?
+ when 'rich-text'
+ return Settings.showRichText
else
throw new Error("unknown feature: #{feature}")
diff --git a/services/web/app/views/project/editor.pug b/services/web/app/views/project/editor.pug
index 13939ee5d6..88ca3364b8 100644
--- a/services/web/app/views/project/editor.pug
+++ b/services/web/app/views/project/editor.pug
@@ -158,6 +158,13 @@ block requirejs
window.pdfCMapsPath = "#{pdfCMapsPath}"
window.uiConfig = JSON.parse('!{JSON.stringify(uiConfig).replace(/\//g, "\\/")}');
+ // TODO: inject based on environment
+ if hasFeature('rich-text')
+ script(
+ src="http://localhost:3809/public/js/es/richText.js"
+ type="text/javascript"
+ )
+
script(
data-main=buildJsPath("ide.js", {hashedPath:false}),
baseurl=fullJsPath,
diff --git a/services/web/app/views/project/editor/editor.pug b/services/web/app/views/project/editor/editor.pug
index 525d1034ca..d1e2938714 100644
--- a/services/web/app/views/project/editor/editor.pug
+++ b/services/web/app/views/project/editor/editor.pug
@@ -30,9 +30,13 @@ div.full-size(
i.fa.fa-arrow-left
| #{translate("open_a_file_on_the_left")}
+ if hasFeature('rich-text')
+ .toolbar.toolbar-editor(ng-controller="EditorToolbarController")
+ button(ng-click="toggleRichText()") Rich Text
+
#editor(
ace-editor="editor",
- ng-show="!!editor.sharejs_doc && !editor.opening"
+ ng-if="!!editor.sharejs_doc && !editor.opening && !editor.richText"
theme="settings.theme",
keybindings="settings.mode",
font-size="settings.fontSize",
@@ -63,6 +67,13 @@ div.full-size(
renderer-data="reviewPanel.rendererData"
)
+ if hasFeature('rich-text')
+ #editor-rich-text(
+ cm-editor
+ ng-if="!editor.opening && editor.richText"
+ sharejs-doc="editor.sharejs_doc"
+ )
+
include ./review-panel
.ui-layout-east
diff --git a/services/web/npm-shrinkwrap.json b/services/web/npm-shrinkwrap.json
index 765fc1f03e..68f9cbd92a 100644
--- a/services/web/npm-shrinkwrap.json
+++ b/services/web/npm-shrinkwrap.json
@@ -1354,6 +1354,11 @@
"from": "code-point-at@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz"
},
+ "codemirror": {
+ "version": "5.33.0",
+ "from": "codemirror@latest",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.33.0.tgz"
+ },
"coffee-script": {
"version": "1.12.4",
"from": "coffee-script@1.12.4",
diff --git a/services/web/package.json b/services/web/package.json
index 7cb82b05d6..860d98733a 100644
--- a/services/web/package.json
+++ b/services/web/package.json
@@ -30,6 +30,7 @@
"bcrypt": "1.0.1",
"body-parser": "^1.13.1",
"bufferedstream": "1.6.0",
+ "codemirror": "^5.33.0",
"connect-redis": "^3.1.0",
"cookie": "^0.2.3",
"cookie-parser": "1.3.5",
diff --git a/services/web/public/coffee/ide/editor/Document.coffee b/services/web/public/coffee/ide/editor/Document.coffee
index b6ea177fc8..afdd924588 100644
--- a/services/web/public/coffee/ide/editor/Document.coffee
+++ b/services/web/public/coffee/ide/editor/Document.coffee
@@ -26,7 +26,8 @@ define [
@connected = @ide.socket.socket.connected
@joined = false
@wantToBeJoined = false
- @_checkConsistency = _.bind(@_checkConsistency, @)
+ @_checkAceConsistency = _.bind(@_checkConsistency, @, @ace)
+ @_checkCMConsistency = _.bind(@_checkConsistency, @, @cm)
@inconsistentCount = 0
@_bindToEditorEvents()
@_bindToSocketEvents()
@@ -34,32 +35,43 @@ define [
attachToAce: (@ace) ->
@doc?.attachToAce(@ace)
editorDoc = @ace.getSession().getDocument()
- editorDoc.on "change", @_checkConsistency
+ editorDoc.on "change", @_checkAceConsistency
@ide.$scope.$emit 'document:opened', @doc
detachFromAce: () ->
@doc?.detachFromAce()
editorDoc = @ace?.getSession().getDocument()
- editorDoc?.off "change", @_checkConsistency
+ editorDoc?.off "change", @_checkAceConsistency
@ide.$scope.$emit 'document:closed', @doc
-
+
+ attachToCM: (@cm) ->
+ @doc?.attachToCM(@cm)
+ @cm?.on "change", @_checkCMConsistency
+ @ide.$scope.$emit 'document:opened', @doc
+
+ detachFromCM: () ->
+ @doc?.detachFromCM()
+ @cm?.off "change", @_checkCMConsistency
+ @ide.$scope.$emit 'document:closed', @doc
+
submitOp: (args...) -> @doc?.submitOp(args...)
- _checkConsistency: () ->
- # We've been seeing a lot of errors when I think there shouldn't be
- # any, which may be related to this check happening before the change is
- # applied. If we use a timeout, hopefully we can reduce this.
- setTimeout () =>
- editorValue = @ace?.getValue()
- sharejsValue = @doc?.getSnapshot()
- if editorValue != sharejsValue
- @inconsistentCount++
- else
- @inconsistentCount = 0
+ _checkConsistency: (editor) ->
+ return () =>
+ # We've been seeing a lot of errors when I think there shouldn't be
+ # any, which may be related to this check happening before the change is
+ # applied. If we use a timeout, hopefully we can reduce this.
+ setTimeout () =>
+ editorValue = editor?.getValue()
+ sharejsValue = @doc?.getSnapshot()
+ if editorValue != sharejsValue
+ @inconsistentCount++
+ else
+ @inconsistentCount = 0
- if @inconsistentCount >= 3
- @_onError new Error("Editor text does not match server text")
- , 0
+ if @inconsistentCount >= 3
+ @_onError new Error("Editor text does not match server text")
+ , 0
getSnapshot: () ->
@doc?.getSnapshot()
diff --git a/services/web/public/coffee/ide/editor/EditorManager.coffee b/services/web/public/coffee/ide/editor/EditorManager.coffee
index bf752ccffb..9c4d74e76f 100644
--- a/services/web/public/coffee/ide/editor/EditorManager.coffee
+++ b/services/web/public/coffee/ide/editor/EditorManager.coffee
@@ -1,7 +1,9 @@
define [
"ide/editor/Document"
"ide/editor/directives/aceEditor"
+ "ide/editor/directives/cmEditor"
"ide/editor/controllers/SavingNotificationController"
+ "ide/editor/controllers/EditorToolbarController"
], (Document) ->
class EditorManager
constructor: (@ide, @$scope) ->
@@ -12,6 +14,7 @@ define [
opening: true
trackChanges: false
wantTrackChanges: false
+ richText: false
}
@$scope.$on "entity:selected", (event, entity) =>
@@ -186,3 +189,6 @@ define [
@$scope.editor.trackChanges = want
else
@_syncTimeout = setTimeout tryToggle, 100
+
+ toggleRichText: () ->
+ @$scope.editor.richText = !@$scope.editor.richText
diff --git a/services/web/public/coffee/ide/editor/ShareJsDoc.coffee b/services/web/public/coffee/ide/editor/ShareJsDoc.coffee
index 569a6c7a9f..63fca95856 100644
--- a/services/web/public/coffee/ide/editor/ShareJsDoc.coffee
+++ b/services/web/public/coffee/ide/editor/ShareJsDoc.coffee
@@ -120,6 +120,9 @@ define [
attachToAce: (ace) -> @_doc.attach_ace(ace, false, window.maxDocLength)
detachFromAce: () -> @_doc.detach_ace?()
+ attachToCM: (cm) -> @_doc.attach_cm(cm, false)
+ detachFromCM: () -> @_doc.detach_cm?()
+
INFLIGHT_OP_TIMEOUT: 5000 # Retry sending ops after 5 seconds without an ack
WAIT_FOR_CONNECTION_TIMEOUT: 500 # If we're waiting for the project to join, try again in 0.5 seconds
_startInflightOpTimeout: (update) ->
diff --git a/services/web/public/coffee/ide/editor/controllers/EditorToolbarController.coffee b/services/web/public/coffee/ide/editor/controllers/EditorToolbarController.coffee
new file mode 100644
index 0000000000..9649899ba8
--- /dev/null
+++ b/services/web/public/coffee/ide/editor/controllers/EditorToolbarController.coffee
@@ -0,0 +1,7 @@
+define [
+ "base"
+ "ide/editor/Document"
+], (App, Document) ->
+ App.controller "EditorToolbarController", ($scope, ide) ->
+ $scope.toggleRichText = () ->
+ ide.editorManager.toggleRichText()
diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee
index 3b43a05afd..716fd2ee9d 100644
--- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee
+++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee
@@ -97,7 +97,7 @@ define [
scope.name = attrs.aceEditor
if scope.spellCheck # only enable spellcheck when explicitly required
- spellCheckCache = $cacheFactory("spellCheck-#{scope.name}", {capacity: 1000})
+ spellCheckCache = $cacheFactory.get("spellCheck-#{scope.name}") || $cacheFactory("spellCheck-#{scope.name}", {capacity: 1000})
spellCheckManager = new SpellCheckManager(scope, editor, element, spellCheckCache, $http, $q)
undoManager = new UndoManager(scope, editor, element)
highlightsManager = new HighlightsManager(scope, editor, element)
@@ -106,7 +106,6 @@ define [
metadataManager = new MetadataManager(scope, editor, element, metadata)
autoCompleteManager = new AutoCompleteManager(scope, editor, element, metadataManager, graphics, preamble, files)
-
# Prevert Ctrl|Cmd-S from triggering save dialog
editor.commands.addCommand
name: "save",
@@ -428,6 +427,9 @@ define [
if rendererData?
rendererData.lineHeight = editor.renderer.lineHeight
+ scope.$on '$destroy', () ->
+ detachFromAce(scope.sharejsDoc)
+
template: """
+ App.directive "cmEditor", () ->
+ return {
+ scope: {
+ sharejsDoc: "="
+ }
+
+ link: (scope, element, attrs) ->
+ cm = Frontend.richText.init(element.find('.cm-editor-wrapper')[0])
+
+ scope.$watch "sharejsDoc", (sharejsDoc, oldSharejsDoc) ->
+ if oldSharejsDoc?
+ detachFromCM(oldSharejsDoc)
+ if sharejsDoc?
+ attachToCM(sharejsDoc)
+
+ attachToCM = (sharejsDoc) ->
+ cm.setValue(sharejsDoc.getSnapshot())
+ sharejsDoc.attachToCM(cm)
+
+ detachFromCM = (sharejsDoc) ->
+ sharejsDoc.detachFromCM()
+
+ scope.$on 'destroy', () ->
+ detachFromCM(scope.sharejsDoc)
+
+ template: """
+
+ """
+ }
\ No newline at end of file
diff --git a/services/web/public/coffee/ide/editor/sharejs/vendor/client/ace.coffee b/services/web/public/coffee/ide/editor/sharejs/vendor/client/ace.coffee
index ac0db65b1a..177af1317e 100644
--- a/services/web/public/coffee/ide/editor/sharejs/vendor/client/ace.coffee
+++ b/services/web/public/coffee/ide/editor/sharejs/vendor/client/ace.coffee
@@ -3,7 +3,7 @@
Range = ace.require("ace/range").Range
# Convert an ace delta into an op understood by share.js
-applyToShareJS = (editorDoc, delta, doc, fromUndo) ->
+applyAceToShareJS = (editorDoc, delta, doc, fromUndo) ->
# Get the start position of the range, in no. of characters
getStartOffsetPosition = (start) ->
# This is quite inefficient - getLines makes a copy of the entire
@@ -81,7 +81,7 @@ window.sharejs.extendDoc 'attach_ace', (editor, keepEditorContents, maxDocLength
fromUndo = !!(editor.getSession().$fromUndo or editor.getSession().$fromReject)
- applyToShareJS editorDoc, change, doc, fromUndo
+ applyAceToShareJS editorDoc, change, doc, fromUndo
check()
diff --git a/services/web/public/coffee/ide/editor/sharejs/vendor/client/cm.coffee b/services/web/public/coffee/ide/editor/sharejs/vendor/client/cm.coffee
index 3c79e4ecfb..7b32ed541d 100644
--- a/services/web/public/coffee/ide/editor/sharejs/vendor/client/cm.coffee
+++ b/services/web/public/coffee/ide/editor/sharejs/vendor/client/cm.coffee
@@ -3,7 +3,7 @@
# It is heavily inspired from the Ace editor hook.
# Convert a CodeMirror delta into an op understood by share.js
-applyToShareJS = (editorDoc, delta, doc) ->
+applyCMToShareJS = (editorDoc, delta, doc) ->
# CodeMirror deltas give a text replacement.
# I tuned this operation a little bit, for speed.
startPos = 0 # Get character position from # of chars in each line.
@@ -26,7 +26,7 @@ applyToShareJS = (editorDoc, delta, doc) ->
doc.del startPos, delLen
doc.insert startPos, delta.text.join '\n' if delta.text
- applyToShareJS editorDoc, delta.next, doc if delta.next
+ applyCMToShareJS editorDoc, delta.next, doc if delta.next
# Attach a CodeMirror editor to the document. The editor's contents are replaced
# with the document's contents unless keepEditorContents is true. (In which case
@@ -39,14 +39,14 @@ window.sharejs.extendDoc 'attach_cm', (editor, keepEditorContents) ->
check = ->
window.setTimeout ->
editorText = editor.getValue()
- otText = sharedoc.getValue()
+ otText = sharedoc.getText()
if editorText != otText
console.error "Text does not match!"
console.error "editor: #{editorText}"
console.error "ot: #{otText}"
# Replace the editor text with the doc snapshot.
- editor.setValue sharedoc.getValue()
+ editor.setValue sharedoc.getText()
, 0
if keepEditorContents
@@ -64,11 +64,11 @@ window.sharejs.extendDoc 'attach_cm', (editor, keepEditorContents) ->
# Listen for edits in CodeMirror.
editorListener = (ed, change) ->
return if suppress
- applyToShareJS editor, change, sharedoc
+ applyCMToShareJS editor, change, sharedoc
check()
- editor.setOption 'onChange', editorListener
+ editor.on 'change', editorListener
@on 'insert', (pos, text) ->
suppress = true
@@ -87,7 +87,7 @@ window.sharejs.extendDoc 'attach_cm', (editor, keepEditorContents) ->
@detach_cm = ->
# TODO: can we remove the insert and delete event callbacks?
- editor.setOption 'onChange', null
+ editor.off 'onChange', editorListener
delete @detach_cm
return
diff --git a/services/web/public/es/rich-text.js b/services/web/public/es/rich-text.js
index 0b7ef955ad..b07155829d 100644
--- a/services/web/public/es/rich-text.js
+++ b/services/web/public/es/rich-text.js
@@ -1 +1,5 @@
-console.log('hello from rich text')
+import CodeMirror from 'codemirror'
+
+export function init(rootEl) {
+ return CodeMirror(rootEl)
+}
diff --git a/services/web/public/stylesheets/_style_includes.less b/services/web/public/stylesheets/_style_includes.less
index 54539a4cb4..de6c4ff7fe 100644
--- a/services/web/public/stylesheets/_style_includes.less
+++ b/services/web/public/stylesheets/_style_includes.less
@@ -80,6 +80,12 @@
@import "app/error-pages.less";
@import "app/v1-badge.less";
+// Vendor CSS
@import "../js/libs/pdfListView/TextLayer.css";
@import "../js/libs/pdfListView/AnnotationsLayer.css";
@import "../js/libs/pdfListView/HighlightsLayer.css";
+
+// CodeMirror
+& when (@show-rich-text) {
+ @import "vendor/codemirror.css";
+}
diff --git a/services/web/public/stylesheets/app/editor.less b/services/web/public/stylesheets/app/editor.less
index 36084cd2a5..fa39552c27 100644
--- a/services/web/public/stylesheets/app/editor.less
+++ b/services/web/public/stylesheets/app/editor.less
@@ -75,10 +75,17 @@
top: 40px;
}
-#editor {
+#editor, #editor-rich-text {
.full-size;
}
+#editor when (@show-rich-text = true) {
+ top: 40px; // TODO: replace with toolbar height var
+}
+#editor-rich-text when (@show-rich-text = true) {
+ top: 40px; // TODO: replace with toolbar height var
+}
+
.loading-screen {
display: flex;
flex-direction: column;
@@ -190,6 +197,14 @@
}
}
+.cm-editor-wrapper {
+ height: 100%;
+
+ .CodeMirror {
+ height: 100%;
+ }
+}
+
// The internal components of the aceEditor directive
.ace-editor-wrapper {
.full-size;
diff --git a/services/web/public/stylesheets/vendor/codemirror.css b/services/web/public/stylesheets/vendor/codemirror.css
new file mode 100644
index 0000000000..c7a8ae7047
--- /dev/null
+++ b/services/web/public/stylesheets/vendor/codemirror.css
@@ -0,0 +1,346 @@
+/* BASICS */
+
+.CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-family: monospace;
+ height: 300px;
+ color: black;
+ direction: ltr;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+ padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+ padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+ border-right: 1px solid #ddd;
+ background-color: #f7f7f7;
+ white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+ padding: 0 3px 0 5px;
+ min-width: 20px;
+ text-align: right;
+ color: #999;
+ white-space: nowrap;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror-cursor {
+ border-left: 1px solid black;
+ border-right: none;
+ width: 0;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+ border-left: 1px solid silver;
+}
+.cm-fat-cursor .CodeMirror-cursor {
+ width: auto;
+ border: 0 !important;
+ background: #7e7;
+}
+.cm-fat-cursor div.CodeMirror-cursors {
+ z-index: 1;
+}
+.cm-fat-cursor-mark {
+ background-color: rgba(20, 255, 20, 0.5);
+ -webkit-animation: blink 1.06s steps(1) infinite;
+ -moz-animation: blink 1.06s steps(1) infinite;
+ animation: blink 1.06s steps(1) infinite;
+}
+.cm-animate-fat-cursor {
+ width: auto;
+ border: 0;
+ -webkit-animation: blink 1.06s steps(1) infinite;
+ -moz-animation: blink 1.06s steps(1) infinite;
+ animation: blink 1.06s steps(1) infinite;
+ background-color: #7e7;
+}
+@-moz-keyframes blink {
+ 0% {}
+ 50% { background-color: transparent; }
+ 100% {}
+}
+@-webkit-keyframes blink {
+ 0% {}
+ 50% { background-color: transparent; }
+ 100% {}
+}
+@keyframes blink {
+ 0% {}
+ 50% { background-color: transparent; }
+ 100% {}
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+.CodeMirror-overwrite .CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-rulers {
+ position: absolute;
+ left: 0; right: 0; top: -50px; bottom: -20px;
+ overflow: hidden;
+}
+.CodeMirror-ruler {
+ border-left: 1px solid #ccc;
+ top: 0; bottom: 0;
+ position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+.cm-strikethrough {text-decoration: line-through;}
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+.CodeMirror-composing { border-bottom: 2px solid; }
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+ the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+ position: relative;
+ overflow: hidden;
+ background: white;
+}
+
+.CodeMirror-scroll {
+ overflow: scroll !important; /* Things will break if this is overridden */
+ /* 30px is the magic margin used to hide the element's real scrollbars */
+ /* See overflow: hidden in .CodeMirror */
+ margin-bottom: -30px; margin-right: -30px;
+ padding-bottom: 30px;
+ height: 100%;
+ outline: none; /* Prevent dragging from highlighting the element */
+ position: relative;
+}
+.CodeMirror-sizer {
+ position: relative;
+ border-right: 30px solid transparent;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+ before actual scrolling happens, thus preventing shaking and
+ flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ position: absolute;
+ z-index: 6;
+ display: none;
+}
+.CodeMirror-vscrollbar {
+ right: 0; top: 0;
+ overflow-x: hidden;
+ overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+ bottom: 0; left: 0;
+ overflow-y: hidden;
+ overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+ right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+ left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+ position: absolute; left: 0; top: 0;
+ min-height: 100%;
+ z-index: 3;
+}
+.CodeMirror-gutter {
+ white-space: normal;
+ height: 100%;
+ display: inline-block;
+ vertical-align: top;
+ margin-bottom: -30px;
+}
+.CodeMirror-gutter-wrapper {
+ position: absolute;
+ z-index: 4;
+ background: none !important;
+ border: none !important;
+}
+.CodeMirror-gutter-background {
+ position: absolute;
+ top: 0; bottom: 0;
+ z-index: 4;
+}
+.CodeMirror-gutter-elt {
+ position: absolute;
+ cursor: default;
+ z-index: 4;
+}
+.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
+.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
+
+.CodeMirror-lines {
+ cursor: text;
+ min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre {
+ /* Reset some styles that the rest of the page might have set */
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+ border-width: 0;
+ background: transparent;
+ font-family: inherit;
+ font-size: inherit;
+ margin: 0;
+ white-space: pre;
+ word-wrap: normal;
+ line-height: inherit;
+ color: inherit;
+ z-index: 2;
+ position: relative;
+ overflow: visible;
+ -webkit-tap-highlight-color: transparent;
+ -webkit-font-variant-ligatures: contextual;
+ font-variant-ligatures: contextual;
+}
+.CodeMirror-wrap pre {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: normal;
+}
+
+.CodeMirror-linebackground {
+ position: absolute;
+ left: 0; right: 0; top: 0; bottom: 0;
+ z-index: 0;
+}
+
+.CodeMirror-linewidget {
+ position: relative;
+ z-index: 2;
+ padding: 0.1px; /* Force widget margins to stay inside of the container */
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-rtl pre { direction: rtl; }
+
+.CodeMirror-code {
+ outline: none;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+.CodeMirror-measure {
+ position: absolute;
+ width: 100%;
+ height: 0;
+ overflow: hidden;
+ visibility: hidden;
+}
+
+.CodeMirror-cursor {
+ position: absolute;
+ pointer-events: none;
+}
+.CodeMirror-measure pre { position: static; }
+
+div.CodeMirror-cursors {
+ visibility: hidden;
+ position: relative;
+ z-index: 3;
+}
+div.CodeMirror-dragcursors {
+ visibility: visible;
+}
+
+.CodeMirror-focused div.CodeMirror-cursors {
+ visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
+.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
+
+.cm-searching {
+ background-color: #ffa;
+ background-color: rgba(255, 255, 0, .4);
+}
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+ /* Hide the cursor when printing */
+ .CodeMirror div.CodeMirror-cursors {
+ visibility: hidden;
+ }
+}
+
+/* See issue #2901 */
+.cm-tab-wrap-hack:after { content: ''; }
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }
diff --git a/services/web/webpack.config.js b/services/web/webpack.config.js
index 78e21f4982..b9474cffac 100644
--- a/services/web/webpack.config.js
+++ b/services/web/webpack.config.js
@@ -4,7 +4,7 @@ module.exports = {
// Defines the "entry point(s)" for the application - i.e. the file which
// bootstraps the application
entry: {
- 'rich-text': './public/es/rich-text.js'
+ richText: './public/es/rich-text.js'
},
// Define where and how the bundle will be output to disk